From 7db6fe02fded0f9555bb506eb4c2d1ae5a364f80 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 14 Aug 2022 12:56:55 +0200 Subject: [PATCH 0001/1710] Fixed issue with new output format --- parser/raylib_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 7c2a2bff0..49b43f768 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -148,7 +148,7 @@ typedef struct FunctionInfo { } FunctionInfo; // Output format for parsed data -typedef enum { DEFAULT = 0, JSON, XML, LUA } OutputFormat; +typedef enum { DEFAULT = 0, JSON, XML, LUA, CODE } OutputFormat; //---------------------------------------------------------------------------------- // Global Variables Definition From 67f8424eb069b89e141e437a7086e9b898192293 Mon Sep 17 00:00:00 2001 From: WIITD <52134513+WIITD@users.noreply.github.com> Date: Mon, 15 Aug 2022 07:12:30 +0000 Subject: [PATCH 0002/1710] raylib-freebasic updated to 4.2 (#2639) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 115893dcf..33caebf80 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -19,7 +19,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | -| raylib-freebasic | **4.0** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | +| raylib-freebasic | **4.2** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | raylib-go | **4.0** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | From f049f9dd3a089c128ba18ec32e47e9f4458aa4da Mon Sep 17 00:00:00 2001 From: moosey Date: Mon, 15 Aug 2022 02:15:43 -0500 Subject: [PATCH 0003/1710] Tiny documentation fix in reasings.h (#2640) --- examples/others/reasings.h | 2 +- examples/shapes/reasings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/others/reasings.h b/examples/others/reasings.h index 4f32dee1f..8b14ba6aa 100644 --- a/examples/others/reasings.h +++ b/examples/others/reasings.h @@ -109,7 +109,7 @@ EASEDEF float EaseLinearInOut(float t, float b, float c, float d) { return (c*t/ // Sine Easing functions EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cosf(t/d*(PI/2.0f)) + c + b); } // Ease: Sine In EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sinf(t/d*(PI/2.0f)) + b); } // Ease: Sine Out -EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); } // Ease: Sine Out +EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); } // Ease: Sine In Out // Circular Easing functions EASEDEF float EaseCircIn(float t, float b, float c, float d) { t /= d; return (-c*(sqrtf(1.0f - t*t) - 1.0f) + b); } // Ease: Circular In diff --git a/examples/shapes/reasings.h b/examples/shapes/reasings.h index 4f32dee1f..8b14ba6aa 100644 --- a/examples/shapes/reasings.h +++ b/examples/shapes/reasings.h @@ -109,7 +109,7 @@ EASEDEF float EaseLinearInOut(float t, float b, float c, float d) { return (c*t/ // Sine Easing functions EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cosf(t/d*(PI/2.0f)) + c + b); } // Ease: Sine In EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sinf(t/d*(PI/2.0f)) + b); } // Ease: Sine Out -EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); } // Ease: Sine Out +EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); } // Ease: Sine In Out // Circular Easing functions EASEDEF float EaseCircIn(float t, float b, float c, float d) { t /= d; return (-c*(sqrtf(1.0f - t*t) - 1.0f) + b); } // Ease: Circular In From 95d3a6ac529de2da68bb4b68ea69ec12208c6aef Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Mon, 15 Aug 2022 23:55:58 +0200 Subject: [PATCH 0004/1710] update raylib.zig to 4.2 (#2642) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 33caebf80..57ff716be 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -55,7 +55,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib.v | **4.0** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | | raylib-zig | **4.0** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | -| raylib.zig | **4.1-dev** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | +| raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | | hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | From 2ad7967db80644a25ca123536cf2f6efcb869684 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 17 Aug 2022 10:19:52 +0200 Subject: [PATCH 0005/1710] REVIEW: Fix issue with external GLFW missing define #2638 --- src/rcore.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 7acedf6c2..d90acf6ce 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -237,6 +237,12 @@ //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() #endif + + // TODO: HACK: Added flag if not provided by GLFW when using external library + // Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev + #if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D + #endif #endif #if defined(PLATFORM_ANDROID) From d8ed3fb31e84aea6c7c213954779d6f640817329 Mon Sep 17 00:00:00 2001 From: irishgreencitrus <43090499+irishgreencitrus@users.noreply.github.com> Date: Wed, 17 Aug 2022 15:36:03 +0100 Subject: [PATCH 0006/1710] Update `raylib.jl` and `raylib.v` to `4.2.0` (#2644) --- BINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 57ff716be..98ad5afab 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -27,7 +27,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | | raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | -| raylib.jl | **4.0** | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | +| raylib.jl | **4.2** | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | | raylib-lua | **4.0** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | **4.0** | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | @@ -52,7 +52,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-swift | **4.0** | [Swift](https://swift.org/) | MIT | https://github.com/STREGAsGate/Raylib | | raylib-scopes | auto | [Scopes](http://scopes.rocks) | MIT | https://github.com/salotz/raylib-scopes | | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | -| raylib.v | **4.0** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | +| raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | | raylib-zig | **4.0** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | | raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | From 904c5051251ff348234de20ea2336bb17ae5f3e8 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 18 Aug 2022 15:10:40 +0200 Subject: [PATCH 0007/1710] minor tweak --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 2fa604da3..123791a02 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2712,10 +2712,10 @@ bool rlCheckRenderBatchLimit(int vCount) // Convert image data to OpenGL texture (returns OpenGL valid Id) unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) { - glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding - unsigned int id = 0; + glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding + // Check texture format support by OpenGL 1.1 (compressed textures not supported) #if defined(GRAPHICS_API_OPENGL_11) if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) From 35c777ef2ccdad0b3a94b508ec13df5f6cd9ea49 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 18 Aug 2022 15:11:23 +0200 Subject: [PATCH 0008/1710] REVIEWED: Avoid crash on bad data provided --- src/rtext.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 40f6d3b5a..ef5debd5b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -818,9 +818,12 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // Unload font glyphs info data (RAM) void UnloadFontData(GlyphInfo *glyphs, int glyphCount) { - for (int i = 0; i < glyphCount; i++) UnloadImage(glyphs[i].image); + if (glyphs != NULL) + { + for (int i = 0; i < glyphCount; i++) UnloadImage(glyphs[i].image); - RL_FREE(glyphs); + RL_FREE(glyphs); + } } // Unload Font from GPU memory (VRAM) @@ -1142,7 +1145,7 @@ void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 pos // Measure string width for default font int MeasureText(const char *text, int fontSize) { - Vector2 vec = { 0.0f, 0.0f }; + Vector2 textSize = { 0.0f, 0.0f }; // Check if default font has been loaded if (GetFontDefault().texture.id != 0) @@ -1151,15 +1154,19 @@ int MeasureText(const char *text, int fontSize) if (fontSize < defaultFontSize) fontSize = defaultFontSize; int spacing = fontSize/defaultFontSize; - vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing); + textSize = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing); } - return (int)vec.x; + return (int)textSize.x; } // Measure string size for Font Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing) { + Vector2 textSize = { 0 }; + + if ((font.texture.id == 0) || (text == NULL)) return textSize; + int size = TextLength(text); // Get size in bytes of text int tempByteCounter = 0; // Used to count longer text line num chars int byteCounter = 0; @@ -1204,11 +1211,10 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; - Vector2 vec = { 0 }; - vec.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure - vec.y = textHeight*scaleFactor; + textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure + textSize.y = textHeight*scaleFactor; - return vec; + return textSize; } // Get index position for a unicode character on font From 4ee5fdf6192166ecb3fc5f020fac9077c05add29 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 20 Aug 2022 14:01:54 +0200 Subject: [PATCH 0009/1710] ADDED: Support M3D model file format (meshes and materials) #2648 --- src/config.h | 1 + src/external/m3d.h | 6530 ++++++++++++++++++++++++++++++++++++++++++++ src/rmodels.c | 169 ++ 3 files changed, 6700 insertions(+) create mode 100644 src/external/m3d.h diff --git a/src/config.h b/src/config.h index ce7d9b04d..a7ec2523f 100644 --- a/src/config.h +++ b/src/config.h @@ -192,6 +192,7 @@ #define SUPPORT_FILEFORMAT_IQM 1 #define SUPPORT_FILEFORMAT_GLTF 1 #define SUPPORT_FILEFORMAT_VOX 1 +#define SUPPORT_FILEFORMAT_M3D 1 // 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 diff --git a/src/external/m3d.h b/src/external/m3d.h new file mode 100644 index 000000000..0dacb6e09 --- /dev/null +++ b/src/external/m3d.h @@ -0,0 +1,6530 @@ +/* + * m3d.h + * https://gitlab.com/bztsrc/model3d + * + * Copyright (C) 2020 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief ANSI C89 / C++11 single header importer / exporter SDK for the Model 3D (.M3D) format + * https://gitlab.com/bztsrc/model3d + * + * PNG decompressor included from (with minor modifications to make it C89 valid): + * stb_image - v2.13 - public domain image loader - http://nothings.org/stb_image.h + * + * @version: 1.0.0 + */ + +#ifndef _M3D_H_ +#define _M3D_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*** configuration ***/ +#ifndef M3D_MALLOC +# define M3D_MALLOC(sz) malloc(sz) +#endif +#ifndef M3D_REALLOC +# define M3D_REALLOC(p,nsz) realloc(p,nsz) +#endif +#ifndef M3D_FREE +# define M3D_FREE(p) free(p) +#endif +#ifndef M3D_LOG +# define M3D_LOG(x) +#endif +#ifndef M3D_APIVERSION +#define M3D_APIVERSION 0x0100 +#ifndef M3D_DOUBLE +typedef float M3D_FLOAT; +#ifndef M3D_EPSILON +/* carefully choosen for IEEE 754 don't change */ +#define M3D_EPSILON ((M3D_FLOAT)1e-7) +#endif +#else +typedef double M3D_FLOAT; +#ifndef M3D_EPSILON +#define M3D_EPSILON ((M3D_FLOAT)1e-14) +#endif +#endif +#if !defined(M3D_SMALLINDEX) +typedef uint32_t M3D_INDEX; +typedef uint16_t M3D_VOXEL; +#define M3D_UNDEF 0xffffffff +#define M3D_INDEXMAX 0xfffffffe +#define M3D_VOXUNDEF 0xffff +#define M3D_VOXCLEAR 0xfffe +#else +typedef uint16_t M3D_INDEX; +typedef uint8_t M3D_VOXEL; +#define M3D_UNDEF 0xffff +#define M3D_INDEXMAX 0xfffe +#define M3D_VOXUNDEF 0xff +#define M3D_VOXCLEAR 0xfe +#endif +#define M3D_NOTDEFINED 0xffffffff +#ifndef M3D_NUMBONE +#define M3D_NUMBONE 4 +#endif +#ifndef M3D_BONEMAXLEVEL +#define M3D_BONEMAXLEVEL 8 +#endif +#ifndef _MSC_VER +#ifndef _inline +#define _inline __inline__ +#endif +#define _pack __attribute__((packed)) +#define _unused __attribute__((unused)) +#else +#define _inline +#define _pack +#define _unused __pragma(warning(suppress:4100)) +#endif +#ifndef __cplusplus +#define _register register +#else +#define _register +#endif + +/*** File format structures ***/ + +/** + * M3D file format structure + * 3DMO m3dchunk_t file header chunk, may followed by compressed data + * PRVW preview chunk (optional) + * HEAD m3dhdr_t model header chunk + * n x m3dchunk_t more chunks follow + * CMAP color map chunk (optional) + * TMAP texture map chunk (optional) + * VRTS vertex data chunk (optional if it's a material library) + * BONE bind-pose skeleton, bone hierarchy chunk (optional) + * n x m3db_t contains propably more, but at least one bone + * n x m3ds_t skin group records + * MTRL* material chunk(s), can be more (optional) + * n x m3dp_t each material contains propapbly more, but at least one property + * the properties are configurable with a static array, see m3d_propertytypes + * n x m3dchunk_t at least one, but maybe more face chunks + * PROC* procedural face, or + * MESH* triangle mesh (vertex index list) or + * VOXT, VOXD* voxel image (converted to mesh) or + * SHPE* mathematical shapes like parameterized surfaces + * LBLS* annotation label chunks, can be more (optional) + * ACTN* action chunk(s), animation-pose skeletons, can be more (optional) + * n x m3dfr_t each action contains probably more, but at least one frame + * n x m3dtr_t each frame contains probably more, but at least one transformation + * ASET* inlined asset chunk(s), can be more (optional) + * OMD3 end chunk + * + * Typical chunks for a game engine: 3DMO, HEAD, CMAP, TMAP, VRTS, BONE, MTRL, MESH, ACTN, OMD3 + * Typical chunks for distibution: 3DMO, PRVW, HEAD, CMAP, TMAP, VRTS, BONE, MTRL, MESH, ACTN, ASET, OMD3 + * Typical chunks for voxel image: 3DMO, HEAD, CMAP, MTRL, VOXT, VOXD, VOXD, VOXD, OMD3 + * Typical chunks for CAD software: 3DMO, PRVW, HEAD, CMAP, TMAP, VRTS, MTRL, SHPE, LBLS, OMD3 + */ +#ifdef _MSC_VER +#pragma pack(push) +#pragma pack(1) +#endif + +typedef struct { + char magic[4]; + uint32_t length; + float scale; /* deliberately not M3D_FLOAT */ + uint32_t types; +} _pack m3dhdr_t; + +typedef struct { + char magic[4]; + uint32_t length; +} _pack m3dchunk_t; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +/*** in-memory model structure ***/ + +/* textmap entry */ +typedef struct { + M3D_FLOAT u; + M3D_FLOAT v; +} m3dti_t; +#define m3d_textureindex_t m3dti_t + +/* texture */ +typedef struct { + char *name; /* texture name */ + uint8_t *d; /* pixels data */ + uint16_t w; /* width */ + uint16_t h; /* height */ + uint8_t f; /* format, 1 = grayscale, 2 = grayscale+alpha, 3 = rgb, 4 = rgba */ +} m3dtx_t; +#define m3d_texturedata_t m3dtx_t + +typedef struct { + M3D_INDEX vertexid; + M3D_FLOAT weight; +} m3dw_t; +#define m3d_weight_t m3dw_t + +/* bone entry */ +typedef struct { + M3D_INDEX parent; /* parent bone index */ + char *name; /* name for this bone */ + M3D_INDEX pos; /* vertex index position */ + M3D_INDEX ori; /* vertex index orientation (quaternion) */ + M3D_INDEX numweight; /* number of controlled vertices */ + m3dw_t *weight; /* weights for those vertices */ + M3D_FLOAT mat4[16]; /* transformation matrix */ +} m3db_t; +#define m3d_bone_t m3db_t + +/* skin: bone per vertex entry */ +typedef struct { + M3D_INDEX boneid[M3D_NUMBONE]; + M3D_FLOAT weight[M3D_NUMBONE]; +} m3ds_t; +#define m3d_skin_t m3ds_t + +/* vertex entry */ +typedef struct { + M3D_FLOAT x; /* 3D coordinates and weight */ + M3D_FLOAT y; + M3D_FLOAT z; + M3D_FLOAT w; + uint32_t color; /* default vertex color */ + M3D_INDEX skinid; /* skin index */ +#ifdef M3D_VERTEXTYPE + uint8_t type; +#endif +} m3dv_t; +#define m3d_vertex_t m3dv_t + +/* material property formats */ +enum { + m3dpf_color, + m3dpf_uint8, + m3dpf_uint16, + m3dpf_uint32, + m3dpf_float, + m3dpf_map +}; +typedef struct { + uint8_t format; + uint8_t id; +#ifdef M3D_ASCII +#define M3D_PROPERTYDEF(f,i,n) { (f), (i), (char*)(n) } + char *key; +#else +#define M3D_PROPERTYDEF(f,i,n) { (f), (i) } +#endif +} m3dpd_t; + +/* material property types */ +/* You shouldn't change the first 8 display and first 4 physical property. Assign the rest as you like. */ +enum { + m3dp_Kd = 0, /* scalar display properties */ + m3dp_Ka, + m3dp_Ks, + m3dp_Ns, + m3dp_Ke, + m3dp_Tf, + m3dp_Km, + m3dp_d, + m3dp_il, + + m3dp_Pr = 64, /* scalar physical properties */ + m3dp_Pm, + m3dp_Ps, + m3dp_Ni, + m3dp_Nt, + + m3dp_map_Kd = 128, /* textured display map properties */ + m3dp_map_Ka, + m3dp_map_Ks, + m3dp_map_Ns, + m3dp_map_Ke, + m3dp_map_Tf, + m3dp_map_Km, /* bump map */ + m3dp_map_D, + m3dp_map_N, /* normal map */ + + m3dp_map_Pr = 192, /* textured physical map properties */ + m3dp_map_Pm, + m3dp_map_Ps, + m3dp_map_Ni, + m3dp_map_Nt +}; +enum { /* aliases */ + m3dp_bump = m3dp_map_Km, + m3dp_map_il = m3dp_map_N, + m3dp_refl = m3dp_map_Pm +}; + +/* material property */ +typedef struct { + uint8_t type; /* property type, see "m3dp_*" enumeration */ + union { + uint32_t color; /* if value is a color, m3dpf_color */ + uint32_t num; /* if value is a number, m3dpf_uint8, m3pf_uint16, m3dpf_uint32 */ + float fnum; /* if value is a floating point number, m3dpf_float */ + M3D_INDEX textureid; /* if value is a texture, m3dpf_map */ + } value; +} m3dp_t; +#define m3d_property_t m3dp_t + +/* material entry */ +typedef struct { + char *name; /* name of the material */ + uint8_t numprop; /* number of properties */ + m3dp_t *prop; /* properties array */ +} m3dm_t; +#define m3d_material_t m3dm_t + +/* face entry */ +typedef struct { + M3D_INDEX materialid; /* material index */ + M3D_INDEX vertex[3]; /* 3D points of the triangle in CCW order */ + M3D_INDEX normal[3]; /* normal vectors */ + M3D_INDEX texcoord[3]; /* UV coordinates */ +#ifdef M3D_VERTEXMAX + M3D_INDEX paramid; /* parameter index */ + M3D_INDEX vertmax[3]; /* maximum 3D points of the triangle in CCW order */ +#endif +} m3df_t; +#define m3d_face_t m3df_t + +typedef struct { + uint16_t count; + char *name; +} m3dvi_t; +#define m3d_voxelitem_t m3dvi_t +#define m3d_parameter_t m3dvi_t + +/* voxel types (voxel palette) */ +typedef struct { + char *name; /* technical name of the voxel */ + uint8_t rotation; /* rotation info */ + uint16_t voxshape; /* voxel shape */ + M3D_INDEX materialid; /* material index */ + uint32_t color; /* default voxel color */ + M3D_INDEX skinid; /* skin index */ + uint8_t numitem; /* number of sub-voxels */ + m3dvi_t *item; /* list of sub-voxels */ +} m3dvt_t; +#define m3d_voxeltype_t m3dvt_t + +/* voxel data blocks */ +typedef struct { + char *name; /* name of the block */ + int32_t x, y, z; /* position */ + uint32_t w, h, d; /* dimension */ + uint8_t uncertain; /* probability */ + uint8_t groupid; /* block group id */ + M3D_VOXEL *data; /* voxel data, indices to voxel type */ +} m3dvx_t; +#define m3d_voxel_t m3dvx_t + +/* shape command types. must match the row in m3d_commandtypes */ +enum { + /* special commands */ + m3dc_use = 0, /* use material */ + m3dc_inc, /* include another shape */ + m3dc_mesh, /* include part of polygon mesh */ + /* approximations */ + m3dc_div, /* subdivision by constant resolution for both u, v */ + m3dc_sub, /* subdivision by constant, different for u and v */ + m3dc_len, /* spacial subdivision by maxlength */ + m3dc_dist, /* subdivision by maxdistance and maxangle */ + /* modifiers */ + m3dc_degu, /* degree for both u, v */ + m3dc_deg, /* separate degree for u and v */ + m3dc_rangeu, /* range for u */ + m3dc_range, /* range for u and v */ + m3dc_paru, /* u parameters (knots) */ + m3dc_parv, /* v parameters */ + m3dc_trim, /* outer trimming curve */ + m3dc_hole, /* inner trimming curve */ + m3dc_scrv, /* spacial curve */ + m3dc_sp, /* special points */ + /* helper curves */ + m3dc_bez1, /* Bezier 1D */ + m3dc_bsp1, /* B-spline 1D */ + m3dc_bez2, /* bezier 2D */ + m3dc_bsp2, /* B-spline 2D */ + /* surfaces */ + m3dc_bezun, /* Bezier 3D with control, UV, normal */ + m3dc_bezu, /* with control and UV */ + m3dc_bezn, /* with control and normal */ + m3dc_bez, /* control points only */ + m3dc_nurbsun, /* B-spline 3D */ + m3dc_nurbsu, + m3dc_nurbsn, + m3dc_nurbs, + m3dc_conn, /* connect surfaces */ + /* geometrical */ + m3dc_line, + m3dc_polygon, + m3dc_circle, + m3dc_cylinder, + m3dc_shpere, + m3dc_torus, + m3dc_cone, + m3dc_cube +}; + +/* shape command argument types */ +enum { + m3dcp_mi_t = 1, /* material index */ + m3dcp_hi_t, /* shape index */ + m3dcp_fi_t, /* face index */ + m3dcp_ti_t, /* texture map index */ + m3dcp_vi_t, /* vertex index */ + m3dcp_qi_t, /* vertex index for quaternions */ + m3dcp_vc_t, /* coordinate or radius, float scalar */ + m3dcp_i1_t, /* int8 scalar */ + m3dcp_i2_t, /* int16 scalar */ + m3dcp_i4_t, /* int32 scalar */ + m3dcp_va_t /* variadic arguments */ +}; + +#define M3D_CMDMAXARG 8 /* if you increase this, add more arguments to the macro below */ +typedef struct { +#ifdef M3D_ASCII +#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (char*)(n), (p), { (a), (b), (c), (d), (e), (f), (g), (h) } } + char *key; +#else +#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (p), { (a), (b), (c), (d), (e), (f), (g), (h) } } +#endif + uint8_t p; + uint8_t a[M3D_CMDMAXARG]; +} m3dcd_t; + +/* shape command */ +typedef struct { + uint16_t type; /* shape type */ + uint32_t *arg; /* arguments array */ +} m3dc_t; +#define m3d_shapecommand_t m3dc_t + +/* shape entry */ +typedef struct { + char *name; /* name of the mathematical shape */ + M3D_INDEX group; /* group this shape belongs to or -1 */ + uint32_t numcmd; /* number of commands */ + m3dc_t *cmd; /* commands array */ +} m3dh_t; +#define m3d_shape_t m3dh_t + +/* label entry */ +typedef struct { + char *name; /* name of the annotation layer or NULL */ + char *lang; /* language code or NULL */ + char *text; /* the label text */ + uint32_t color; /* color */ + M3D_INDEX vertexid; /* the vertex the label refers to */ +} m3dl_t; +#define m3d_label_t m3dl_t + +/* frame transformations / working copy skeleton entry */ +typedef struct { + M3D_INDEX boneid; /* selects a node in bone hierarchy */ + M3D_INDEX pos; /* vertex index new position */ + M3D_INDEX ori; /* vertex index new orientation (quaternion) */ +} m3dtr_t; +#define m3d_transform_t m3dtr_t + +/* animation frame entry */ +typedef struct { + uint32_t msec; /* frame's position on the timeline, timestamp */ + M3D_INDEX numtransform; /* number of transformations in this frame */ + m3dtr_t *transform; /* transformations */ +} m3dfr_t; +#define m3d_frame_t m3dfr_t + +/* model action entry */ +typedef struct { + char *name; /* name of the action */ + uint32_t durationmsec; /* duration in millisec (1/1000 sec) */ + M3D_INDEX numframe; /* number of frames in this animation */ + m3dfr_t *frame; /* frames array */ +} m3da_t; +#define m3d_action_t m3da_t + +/* inlined asset */ +typedef struct { + char *name; /* asset name (same pointer as in texture[].name) */ + uint8_t *data; /* compressed asset data */ + uint32_t length; /* compressed data length */ +} m3di_t; +#define m3d_inlinedasset_t m3di_t + +/*** in-memory model structure ***/ +#define M3D_FLG_FREERAW (1<<0) +#define M3D_FLG_FREESTR (1<<1) +#define M3D_FLG_MTLLIB (1<<2) +#define M3D_FLG_GENNORM (1<<3) + +typedef struct { + m3dhdr_t *raw; /* pointer to raw data */ + char flags; /* internal flags */ + signed char errcode; /* returned error code */ + char vc_s, vi_s, si_s, ci_s, ti_s, bi_s, nb_s, sk_s, fc_s, hi_s, fi_s, vd_s, vp_s; /* decoded sizes for types */ + char *name; /* name of the model, like "Utah teapot" */ + char *license; /* usage condition or license, like "MIT", "LGPL" or "BSD-3clause" */ + char *author; /* nickname, email, homepage or github URL etc. */ + char *desc; /* comments, descriptions. May contain '\n' newline character */ + M3D_FLOAT scale; /* the model's bounding cube's size in SI meters */ + M3D_INDEX numcmap; + uint32_t *cmap; /* color map */ + M3D_INDEX numtmap; + m3dti_t *tmap; /* texture map indices */ + M3D_INDEX numtexture; + m3dtx_t *texture; /* uncompressed textures */ + M3D_INDEX numbone; + m3db_t *bone; /* bone hierarchy */ + M3D_INDEX numvertex; + m3dv_t *vertex; /* vertex data */ + M3D_INDEX numskin; + m3ds_t *skin; /* skin data */ + M3D_INDEX nummaterial; + m3dm_t *material; /* material list */ +#ifdef M3D_VERTEXMAX + M3D_INDEX numparam; + m3dvi_t *param; /* parameters and their values list */ +#endif + M3D_INDEX numface; + m3df_t *face; /* model face, polygon (triangle) mesh */ + M3D_INDEX numvoxtype; + m3dvt_t *voxtype; /* model face, voxel types */ + M3D_INDEX numvoxel; + m3dvx_t *voxel; /* model face, cubes compressed into voxels */ + M3D_INDEX numshape; + m3dh_t *shape; /* model face, shape commands */ + M3D_INDEX numlabel; + m3dl_t *label; /* annotation labels */ + M3D_INDEX numaction; + m3da_t *action; /* action animations */ + M3D_INDEX numinlined; + m3di_t *inlined; /* inlined assets */ + M3D_INDEX numextra; + m3dchunk_t **extra; /* unknown chunks, application / engine specific data probably */ + m3di_t preview; /* preview chunk */ +} m3d_t; + +/*** export parameters ***/ +#define M3D_EXP_INT8 0 +#define M3D_EXP_INT16 1 +#define M3D_EXP_FLOAT 2 +#define M3D_EXP_DOUBLE 3 + +#define M3D_EXP_NOCMAP (1<<0) +#define M3D_EXP_NOMATERIAL (1<<1) +#define M3D_EXP_NOFACE (1<<2) +#define M3D_EXP_NONORMAL (1<<3) +#define M3D_EXP_NOTXTCRD (1<<4) +#define M3D_EXP_FLIPTXTCRD (1<<5) +#define M3D_EXP_NORECALC (1<<6) +#define M3D_EXP_IDOSUCK (1<<7) +#define M3D_EXP_NOBONE (1<<8) +#define M3D_EXP_NOACTION (1<<9) +#define M3D_EXP_INLINE (1<<10) +#define M3D_EXP_EXTRA (1<<11) +#define M3D_EXP_NOZLIB (1<<14) +#define M3D_EXP_ASCII (1<<15) +#define M3D_EXP_NOVRTMAX (1<<16) + +/*** error codes ***/ +#define M3D_SUCCESS 0 +#define M3D_ERR_ALLOC -1 +#define M3D_ERR_BADFILE -2 +#define M3D_ERR_UNIMPL -65 +#define M3D_ERR_UNKPROP -66 +#define M3D_ERR_UNKMESH -67 +#define M3D_ERR_UNKIMG -68 +#define M3D_ERR_UNKFRAME -69 +#define M3D_ERR_UNKCMD -70 +#define M3D_ERR_UNKVOX -71 +#define M3D_ERR_TRUNC -72 +#define M3D_ERR_CMAP -73 +#define M3D_ERR_TMAP -74 +#define M3D_ERR_VRTS -75 +#define M3D_ERR_BONE -76 +#define M3D_ERR_MTRL -77 +#define M3D_ERR_SHPE -78 +#define M3D_ERR_VOXT -79 + +#define M3D_ERR_ISFATAL(x) ((x) < 0 && (x) > -65) + +/* callbacks */ +typedef unsigned char *(*m3dread_t)(char *filename, unsigned int *size); /* read file contents into buffer */ +typedef void (*m3dfree_t)(void *buffer); /* free file contents buffer */ +typedef int (*m3dtxsc_t)(const char *name, const void *script, uint32_t len, m3dtx_t *output); /* interpret texture script */ +typedef int (*m3dprsc_t)(const char *name, const void *script, uint32_t len, m3d_t *model); /* interpret surface script */ +#endif /* ifndef M3D_APIVERSION */ + +/*** C prototypes ***/ +/* import / export */ +m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib); +unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size); +void m3d_free(m3d_t *model); +/* generate animation pose skeleton */ +m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton); +m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec); + +/* private prototypes used by both importer and exporter */ +char *_m3d_safestr(char *in, int morelines); + +/*** C implementation ***/ +#ifdef M3D_IMPLEMENTATION +#if !defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER) +/* material property definitions */ +static m3dpd_t m3d_propertytypes[] = { + M3D_PROPERTYDEF(m3dpf_color, m3dp_Kd, "Kd"), /* diffuse color */ + M3D_PROPERTYDEF(m3dpf_color, m3dp_Ka, "Ka"), /* ambient color */ + M3D_PROPERTYDEF(m3dpf_color, m3dp_Ks, "Ks"), /* specular color */ + M3D_PROPERTYDEF(m3dpf_float, m3dp_Ns, "Ns"), /* specular exponent */ + M3D_PROPERTYDEF(m3dpf_color, m3dp_Ke, "Ke"), /* emissive (emitting light of this color) */ + M3D_PROPERTYDEF(m3dpf_color, m3dp_Tf, "Tf"), /* transmission color */ + M3D_PROPERTYDEF(m3dpf_float, m3dp_Km, "Km"), /* bump strength */ + M3D_PROPERTYDEF(m3dpf_float, m3dp_d, "d"), /* dissolve (transparency) */ + M3D_PROPERTYDEF(m3dpf_uint8, m3dp_il, "il"), /* illumination model (informational, ignored by PBR-shaders) */ + + M3D_PROPERTYDEF(m3dpf_float, m3dp_Pr, "Pr"), /* roughness */ + M3D_PROPERTYDEF(m3dpf_float, m3dp_Pm, "Pm"), /* metallic, also reflection */ + M3D_PROPERTYDEF(m3dpf_float, m3dp_Ps, "Ps"), /* sheen */ + M3D_PROPERTYDEF(m3dpf_float, m3dp_Ni, "Ni"), /* index of refraction (optical density) */ + M3D_PROPERTYDEF(m3dpf_float, m3dp_Nt, "Nt"), /* thickness of face in millimeter, for printing */ + + /* aliases, note that "map_*" aliases are handled automatically */ + M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Km, "bump"), + M3D_PROPERTYDEF(m3dpf_map, m3dp_map_N, "map_N"),/* as normal map has no scalar version, it's counterpart is 'il' */ + M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Pm, "refl") +}; +/* shape command definitions. if more commands start with the same string, the longer must come first */ +static m3dcd_t m3d_commandtypes[] = { + /* technical */ + M3D_CMDDEF(m3dc_use, "use", 1, m3dcp_mi_t, 0, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_inc, "inc", 3, m3dcp_hi_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vi_t, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_mesh, "mesh", 1, m3dcp_fi_t, m3dcp_fi_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vi_t, 0, 0, 0), + /* approximations */ + M3D_CMDDEF(m3dc_div, "div", 1, m3dcp_vc_t, 0, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_sub, "sub", 2, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_len, "len", 1, m3dcp_vc_t, 0, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_dist, "dist", 2, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), + /* modifiers */ + M3D_CMDDEF(m3dc_degu, "degu", 1, m3dcp_i1_t, 0, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_deg, "deg", 2, m3dcp_i1_t, m3dcp_i1_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_rangeu, "rangeu", 1, m3dcp_ti_t, 0, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_range, "range", 2, m3dcp_ti_t, m3dcp_ti_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_paru, "paru", 2, m3dcp_va_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_parv, "parv", 2, m3dcp_va_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_trim, "trim", 3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_hole, "hole", 3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_scrv, "scrv", 3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_sp, "sp", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + /* helper curves */ + M3D_CMDDEF(m3dc_bez1, "bez1", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_bsp1, "bsp1", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_bez2, "bez2", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_bsp2, "bsp2", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + /* surfaces */ + M3D_CMDDEF(m3dc_bezun, "bezun", 4, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, m3dcp_vi_t, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_bezu, "bezu", 3, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_bezn, "bezn", 3, m3dcp_va_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_bez, "bez", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_nurbsun, "nurbsun", 4, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, m3dcp_vi_t, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_nurbsu, "nurbsu", 3, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_nurbsn, "nurbsn", 3, m3dcp_va_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_nurbs, "nurbs", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_conn, "conn", 6, m3dcp_i2_t, m3dcp_ti_t, m3dcp_i2_t, m3dcp_i2_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0), + /* geometrical */ + M3D_CMDDEF(m3dc_line, "line", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_polygon, "polygon", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_circle, "circle", 3, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_cylinder,"cylinder",6, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, 0, 0), + M3D_CMDDEF(m3dc_shpere, "shpere", 2, m3dcp_vi_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_torus, "torus", 4, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_cone, "cone", 3, m3dcp_vi_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0), + M3D_CMDDEF(m3dc_cube, "cube", 3, m3dcp_vi_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0) +}; +#endif + +#include +#include + +#if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H) +/* PNG decompressor from + + stb_image - v2.23 - public domain image loader - http://nothings.org/stb_image.h +*/ +static const char *_m3dstbi__g_failure_reason; + +enum +{ + STBI_default = 0, + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +typedef unsigned short _m3dstbi_us; + +typedef uint16_t _m3dstbi__uint16; +typedef int16_t _m3dstbi__int16; +typedef uint32_t _m3dstbi__uint32; +typedef int32_t _m3dstbi__int32; + +typedef struct +{ + _m3dstbi__uint32 img_x, img_y; + int img_n, img_out_n; + + void *io_user_data; + + int read_from_callbacks; + int buflen; + unsigned char buffer_start[128]; + + unsigned char *img_buffer, *img_buffer_end; + unsigned char *img_buffer_original, *img_buffer_original_end; +} _m3dstbi__context; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} _m3dstbi__result_info; + +#define STBI_ASSERT(v) +#define STBI_NOTUSED(v) (void)sizeof(v) +#define STBI__BYTECAST(x) ((unsigned char) ((x) & 255)) +#define STBI_MALLOC(sz) M3D_MALLOC(sz) +#define STBI_REALLOC(p,newsz) M3D_REALLOC(p,newsz) +#define STBI_FREE(p) M3D_FREE(p) +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) + +_inline static unsigned char _m3dstbi__get8(_m3dstbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + return 0; +} + +_inline static int _m3dstbi__at_eof(_m3dstbi__context *s) +{ + return s->img_buffer >= s->img_buffer_end; +} + +static void _m3dstbi__skip(_m3dstbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + s->img_buffer += n; +} + +static int _m3dstbi__getn(_m3dstbi__context *s, unsigned char *buffer, int n) +{ + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int _m3dstbi__get16be(_m3dstbi__context *s) +{ + int z = _m3dstbi__get8(s); + return (z << 8) + _m3dstbi__get8(s); +} + +static _m3dstbi__uint32 _m3dstbi__get32be(_m3dstbi__context *s) +{ + _m3dstbi__uint32 z = _m3dstbi__get16be(s); + return (z << 16) + _m3dstbi__get16be(s); +} + +#define _m3dstbi__err(x,y) _m3dstbi__errstr(y) +static int _m3dstbi__errstr(const char *str) +{ + _m3dstbi__g_failure_reason = str; + return 0; +} + +_inline static void *_m3dstbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +static int _m3dstbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + return a <= 2147483647 - b; +} + +static int _m3dstbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; + return a <= 2147483647/b; +} + +static int _m3dstbi__mad2sizes_valid(int a, int b, int add) +{ + return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__addsizes_valid(a*b, add); +} + +static int _m3dstbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__mul2sizes_valid(a*b, c) && + _m3dstbi__addsizes_valid(a*b*c, add); +} + +static void *_m3dstbi__malloc_mad2(int a, int b, int add) +{ + if (!_m3dstbi__mad2sizes_valid(a, b, add)) return NULL; + return _m3dstbi__malloc(a*b + add); +} + +static void *_m3dstbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!_m3dstbi__mad3sizes_valid(a, b, c, add)) return NULL; + return _m3dstbi__malloc(a*b*c + add); +} + +static unsigned char _m3dstbi__compute_y(int r, int g, int b) +{ + return (unsigned char) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *_m3dstbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) _m3dstbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + _m3dstbi__err("outofmem", "Out of memory"); + return NULL; + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static _m3dstbi__uint16 _m3dstbi__compute_y_16(int r, int g, int b) +{ + return (_m3dstbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static _m3dstbi__uint16 *_m3dstbi__convert_format16(_m3dstbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + _m3dstbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (_m3dstbi__uint16 *) _m3dstbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + _m3dstbi__err("outofmem", "Out of memory"); + return NULL; + } + + for (j=0; j < (int) y; ++j) { + _m3dstbi__uint16 *src = data + j * x * img_n ; + _m3dstbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#define STBI__ZFAST_BITS 9 +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +typedef struct +{ + _m3dstbi__uint16 fast[1 << STBI__ZFAST_BITS]; + _m3dstbi__uint16 firstcode[16]; + int maxcode[17]; + _m3dstbi__uint16 firstsymbol[16]; + unsigned char size[288]; + _m3dstbi__uint16 value[288]; +} _m3dstbi__zhuffman; + +_inline static int _m3dstbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +_inline static int _m3dstbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + return _m3dstbi__bitreverse16(v) >> (16-bits); +} + +static int _m3dstbi__zbuild_huffman(_m3dstbi__zhuffman *z, unsigned char *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return _m3dstbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (_m3dstbi__uint16) code; + z->firstsymbol[i] = (_m3dstbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return _m3dstbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + _m3dstbi__uint16 fastv = (_m3dstbi__uint16) ((s << 9) | i); + z->size [c] = (unsigned char ) s; + z->value[c] = (_m3dstbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = _m3dstbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +typedef struct +{ + unsigned char *zbuffer, *zbuffer_end; + int num_bits; + _m3dstbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + _m3dstbi__zhuffman z_length, z_distance; +} _m3dstbi__zbuf; + +_inline static unsigned char _m3dstbi__zget8(_m3dstbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void _m3dstbi__fill_bits(_m3dstbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) _m3dstbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +_inline static unsigned int _m3dstbi__zreceive(_m3dstbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) _m3dstbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int _m3dstbi__zhuffman_decode_slowpath(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) +{ + int b,s,k; + k = _m3dstbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +_inline static int _m3dstbi__zhuffman_decode(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) _m3dstbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return _m3dstbi__zhuffman_decode_slowpath(a, z); +} + +static int _m3dstbi__zexpand(_m3dstbi__zbuf *z, char *zout, int n) +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return _m3dstbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return _m3dstbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int _m3dstbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int _m3dstbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int _m3dstbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int _m3dstbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int _m3dstbi__parse_huffman_block(_m3dstbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = _m3dstbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return _m3dstbi__err("bad huffman code","Corrupt PNG"); + if (zout >= a->zout_end) { + if (!_m3dstbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + unsigned char *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = _m3dstbi__zlength_base[z]; + if (_m3dstbi__zlength_extra[z]) len += _m3dstbi__zreceive(a, _m3dstbi__zlength_extra[z]); + z = _m3dstbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return _m3dstbi__err("bad huffman code","Corrupt PNG"); + dist = _m3dstbi__zdist_base[z]; + if (_m3dstbi__zdist_extra[z]) dist += _m3dstbi__zreceive(a, _m3dstbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return _m3dstbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!_m3dstbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (unsigned char *) (zout - dist); + if (dist == 1) { + unsigned char v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int _m3dstbi__compute_huffman_codes(_m3dstbi__zbuf *a) +{ + static unsigned char length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + _m3dstbi__zhuffman z_codelength; + unsigned char lencodes[286+32+137]; + unsigned char codelength_sizes[19]; + int i,n; + + int hlit = _m3dstbi__zreceive(a,5) + 257; + int hdist = _m3dstbi__zreceive(a,5) + 1; + int hclen = _m3dstbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = _m3dstbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (unsigned char) s; + } + if (!_m3dstbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = _m3dstbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (unsigned char) c; + else { + unsigned char fill = 0; + if (c == 16) { + c = _m3dstbi__zreceive(a,2)+3; + if (n == 0) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = _m3dstbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = _m3dstbi__zreceive(a,7)+11; + } + if (ntot - n < c) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return _m3dstbi__err("bad codelengths","Corrupt PNG"); + if (!_m3dstbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!_m3dstbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +_inline static int _m3dstbi__parse_uncompressed_block(_m3dstbi__zbuf *a) +{ + unsigned char header[4]; + int len,nlen,k; + if (a->num_bits & 7) + _m3dstbi__zreceive(a, a->num_bits & 7); + k = 0; + while (a->num_bits > 0) { + header[k++] = (unsigned char) (a->code_buffer & 255); + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + while (k < 4) + header[k++] = _m3dstbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return _m3dstbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return _m3dstbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!_m3dstbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int _m3dstbi__parse_zlib_header(_m3dstbi__zbuf *a) +{ + int cmf = _m3dstbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = _m3dstbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return _m3dstbi__err("bad zlib header","Corrupt PNG"); + if (flg & 32) return _m3dstbi__err("no preset dict","Corrupt PNG"); + if (cm != 8) return _m3dstbi__err("bad compression","Corrupt PNG"); + return 1; +} + +static unsigned char _m3dstbi__zdefault_length[288], _m3dstbi__zdefault_distance[32]; +static void _m3dstbi__init_zdefaults(void) +{ + int i; + for (i=0; i <= 143; ++i) _m3dstbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) _m3dstbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) _m3dstbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) _m3dstbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) _m3dstbi__zdefault_distance[i] = 5; +} + +static int _m3dstbi__parse_zlib(_m3dstbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!_m3dstbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = _m3dstbi__zreceive(a,1); + type = _m3dstbi__zreceive(a,2); + if (type == 0) { + if (!_m3dstbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + if (!_m3dstbi__zbuild_huffman(&a->z_length , _m3dstbi__zdefault_length , 288)) return 0; + if (!_m3dstbi__zbuild_huffman(&a->z_distance, _m3dstbi__zdefault_distance, 32)) return 0; + } else { + if (!_m3dstbi__compute_huffman_codes(a)) return 0; + } + if (!_m3dstbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int _m3dstbi__do_zlib(_m3dstbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + _m3dstbi__init_zdefaults(); + return _m3dstbi__parse_zlib(a, parse_header); +} + +char *_m3dstbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + _m3dstbi__zbuf a; + char *p = (char *) _m3dstbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (unsigned char *) buffer; + a.zbuffer_end = (unsigned char *) buffer + len; + if (_m3dstbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +typedef struct +{ + _m3dstbi__uint32 length; + _m3dstbi__uint32 type; +} _m3dstbi__pngchunk; + +static _m3dstbi__pngchunk _m3dstbi__get_chunk_header(_m3dstbi__context *s) +{ + _m3dstbi__pngchunk c; + c.length = _m3dstbi__get32be(s); + c.type = _m3dstbi__get32be(s); + return c; +} + +_inline static int _m3dstbi__check_png_header(_m3dstbi__context *s) +{ + static unsigned char png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (_m3dstbi__get8(s) != png_sig[i]) return _m3dstbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + _m3dstbi__context *s; + unsigned char *idata, *expanded, *out; + int depth; +} _m3dstbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static unsigned char first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int _m3dstbi__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; +} + +static unsigned char _m3dstbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +static int _m3dstbi__create_png_image_raw(_m3dstbi__png *a, unsigned char *raw, _m3dstbi__uint32 raw_len, int out_n, _m3dstbi__uint32 x, _m3dstbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + _m3dstbi__context *s = a->s; + _m3dstbi__uint32 i,j,stride = x*out_n*bytes; + _m3dstbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (unsigned char *) _m3dstbi__malloc_mad3(x, y, output_bytes, 0); + if (!a->out) return _m3dstbi__err("outofmem", "Out of memory"); + + if (!_m3dstbi__mad3sizes_valid(img_n, x, depth, 7)) return _m3dstbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return _m3dstbi__err("not enough pixels","Corrupt PNG"); + } else { + if (raw_len < img_len) return _m3dstbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + unsigned char *cur = a->out + stride*j; + unsigned char *prior = cur - stride; + int filter = *raw++; + + if (filter > 4) + return _m3dstbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; + + if (j == 0) filter = first_row_filter[filter]; + + 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] + _m3dstbi__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; + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; + cur[filter_bytes+1] = 255; + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + 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) { + 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] + _m3dstbi__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] + _m3dstbi__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] + _m3dstbi__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] + _m3dstbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + if (depth == 16) { + cur = a->out + stride*j; + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + if (depth < 8) { + for (j=0; j < y; ++j) { + unsigned char *cur = a->out + stride*j; + unsigned char *in = a->out + stride*j + x*out_n - img_width_bytes; + unsigned char scale = (color == 0) ? _m3dstbi__depth_scale_table[depth] : 1; + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + 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); + } + 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); + } + 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; + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } 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]; + } + } + } + } + } else if (depth == 16) { + unsigned char *cur = a->out; + _m3dstbi__uint16 *cur16 = (_m3dstbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int _m3dstbi__create_png_image(_m3dstbi__png *a, unsigned char *image_data, _m3dstbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + unsigned char *final; + int p; + if (!interlaced) + return _m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + final = (unsigned char *) _m3dstbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + _m3dstbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!_m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int _m3dstbi__compute_transparency(_m3dstbi__png *z, unsigned char tc[3], int out_n) +{ + _m3dstbi__context *s = z->s; + _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; + unsigned char *p = z->out; + + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int _m3dstbi__compute_transparency16(_m3dstbi__png *z, _m3dstbi__uint16 tc[3], int out_n) +{ + _m3dstbi__context *s = z->s; + _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; + _m3dstbi__uint16 *p = (_m3dstbi__uint16*) z->out; + + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int _m3dstbi__expand_png_palette(_m3dstbi__png *a, unsigned char *palette, int len, int pal_img_n) +{ + _m3dstbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + unsigned char *p, *temp_out, *orig = a->out; + + p = (unsigned char *) _m3dstbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); + + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp) +{ + unsigned char palette[1024], pal_img_n=0; + unsigned char has_trans=0, tc[3]; + _m3dstbi__uint16 tc16[3]; + _m3dstbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0; + _m3dstbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!_m3dstbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + _m3dstbi__pngchunk c = _m3dstbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + _m3dstbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return _m3dstbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return _m3dstbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = _m3dstbi__get32be(s); if (s->img_x > (1 << 24)) return _m3dstbi__err("too large","Very large image (corrupt?)"); + s->img_y = _m3dstbi__get32be(s); if (s->img_y > (1 << 24)) return _m3dstbi__err("too large","Very large image (corrupt?)"); + z->depth = _m3dstbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return _m3dstbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = _m3dstbi__get8(s); if (color > 6) return _m3dstbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return _m3dstbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return _m3dstbi__err("bad ctype","Corrupt PNG"); + comp = _m3dstbi__get8(s); if (comp) return _m3dstbi__err("bad comp method","Corrupt PNG"); + filter= _m3dstbi__get8(s); if (filter) return _m3dstbi__err("bad filter method","Corrupt PNG"); + interlace = _m3dstbi__get8(s); if (interlace>1) return _m3dstbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return _m3dstbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return _m3dstbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return _m3dstbi__err("too large","Corrupt PNG"); + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return _m3dstbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return _m3dstbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = _m3dstbi__get8(s); + palette[i*4+1] = _m3dstbi__get8(s); + palette[i*4+2] = _m3dstbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return _m3dstbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return _m3dstbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return _m3dstbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = _m3dstbi__get8(s); + } else { + if (!(s->img_n & 1)) return _m3dstbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (_m3dstbi__uint32) s->img_n*2) return _m3dstbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (_m3dstbi__uint16)_m3dstbi__get16be(s); + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (unsigned char)(_m3dstbi__get16be(s) & 255) * _m3dstbi__depth_scale_table[z->depth]; + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return _m3dstbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + _m3dstbi__uint32 idata_limit_old = idata_limit; + unsigned char *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (unsigned char *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!_m3dstbi__getn(s, z->idata+ioff,c.length)) return _m3dstbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + _m3dstbi__uint32 raw_len, bpl; + if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return _m3dstbi__err("no IDAT","Corrupt PNG"); + bpl = (s->img_x * z->depth + 7) / 8; + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (unsigned char *) _m3dstbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, 1); + if (z->expanded == NULL) return 0; + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!_m3dstbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!_m3dstbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!_m3dstbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (pal_img_n) { + s->img_n = pal_img_n; + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!_m3dstbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + return _m3dstbi__err("invalid_chunk", "PNG not supported: unknown PNG chunk type"); + } + _m3dstbi__skip(s, c.length); + break; + } + _m3dstbi__get32be(s); + } +} + +static void *_m3dstbi__do_png(_m3dstbi__png *p, int *x, int *y, int *n, int req_comp, _m3dstbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) { _m3dstbi__err("bad req_comp", "Internal error"); return NULL; } + if (_m3dstbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = _m3dstbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = _m3dstbi__convert_format16((_m3dstbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, int req_comp, _m3dstbi__result_info *ri) +{ + _m3dstbi__png p; + p.s = s; + return _m3dstbi__do_png(&p, x,y,comp,req_comp, ri); +} +#define stbi__context _m3dstbi__context +#define stbi__result_info _m3dstbi__result_info +#define stbi__png_load _m3dstbi__png_load +#define stbi_zlib_decode_malloc_guesssize_headerflag _m3dstbi_zlib_decode_malloc_guesssize_headerflag +#endif + +#if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) +/* zlib_compressor from + + stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h +*/ +typedef unsigned char _m3dstbiw__uc; +typedef unsigned short _m3dstbiw__us; + +typedef uint16_t _m3dstbiw__uint16; +typedef int16_t _m3dstbiw__int16; +typedef uint32_t _m3dstbiw__uint32; +typedef int32_t _m3dstbiw__int32; + +#define STBIW_MALLOC(s) M3D_MALLOC(s) +#define STBIW_REALLOC(p,ns) M3D_REALLOC(p,ns) +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#define STBIW_FREE M3D_FREE +#define STBIW_MEMMOVE memmove +#define STBIW_UCHAR (uint8_t) +#define STBIW_ASSERT(x) +#define _m3dstbiw___sbraw(a) ((int *) (a) - 2) +#define _m3dstbiw___sbm(a) _m3dstbiw___sbraw(a)[0] +#define _m3dstbiw___sbn(a) _m3dstbiw___sbraw(a)[1] + +#define _m3dstbiw___sbneedgrow(a,n) ((a)==0 || _m3dstbiw___sbn(a)+n >= _m3dstbiw___sbm(a)) +#define _m3dstbiw___sbmaybegrow(a,n) (_m3dstbiw___sbneedgrow(a,(n)) ? _m3dstbiw___sbgrow(a,n) : 0) +#define _m3dstbiw___sbgrow(a,n) _m3dstbiw___sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define _m3dstbiw___sbpush(a, v) (_m3dstbiw___sbmaybegrow(a,1), (a)[_m3dstbiw___sbn(a)++] = (v)) +#define _m3dstbiw___sbcount(a) ((a) ? _m3dstbiw___sbn(a) : 0) +#define _m3dstbiw___sbfree(a) ((a) ? STBIW_FREE(_m3dstbiw___sbraw(a)),0 : 0) + +static void *_m3dstbiw___sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*_m3dstbiw___sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? _m3dstbiw___sbraw(*arr) : 0, *arr ? (_m3dstbiw___sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + _m3dstbiw___sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *_m3dstbiw___zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + _m3dstbiw___sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int _m3dstbiw___zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int _m3dstbiw___zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int _m3dstbiw___zhash(unsigned char *data) +{ + _m3dstbiw__uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define _m3dstbiw___zlib_flush() (out = _m3dstbiw___zlib_flushf(out, &bitbuf, &bitcount)) +#define _m3dstbiw___zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), _m3dstbiw___zlib_flush()) +#define _m3dstbiw___zlib_huffa(b,c) _m3dstbiw___zlib_add(_m3dstbiw___zlib_bitrev(b,c),c) +#define _m3dstbiw___zlib_huff1(n) _m3dstbiw___zlib_huffa(0x30 + (n), 8) +#define _m3dstbiw___zlib_huff2(n) _m3dstbiw___zlib_huffa(0x190 + (n)-144, 9) +#define _m3dstbiw___zlib_huff3(n) _m3dstbiw___zlib_huffa(0 + (n)-256,7) +#define _m3dstbiw___zlib_huff4(n) _m3dstbiw___zlib_huffa(0xc0 + (n)-280,8) +#define _m3dstbiw___zlib_huff(n) ((n) <= 143 ? _m3dstbiw___zlib_huff1(n) : (n) <= 255 ? _m3dstbiw___zlib_huff2(n) : (n) <= 279 ? _m3dstbiw___zlib_huff3(n) : _m3dstbiw___zlib_huff4(n)) +#define _m3dstbiw___zlib_huffb(n) ((n) <= 143 ? _m3dstbiw___zlib_huff1(n) : _m3dstbiw___zlib_huff2(n)) + +#define _m3dstbiw___ZHASH 16384 + +unsigned char * _m3dstbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(_m3dstbiw___ZHASH * sizeof(char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + _m3dstbiw___sbpush(out, 0x78); + _m3dstbiw___sbpush(out, 0x5e); + _m3dstbiw___zlib_add(1,1); + _m3dstbiw___zlib_add(1,2); + + for (i=0; i < _m3dstbiw___ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + int h = _m3dstbiw___zhash(data+i)&(_m3dstbiw___ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = _m3dstbiw___sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { + int d = _m3dstbiw___zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + if (hash_table[h] && _m3dstbiw___sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + _m3dstbiw___sbn(hash_table[h]) = quality; + } + _m3dstbiw___sbpush(hash_table[h],data+i); + + if (bestloc) { + h = _m3dstbiw___zhash(data+i+1)&(_m3dstbiw___ZHASH-1); + hlist = hash_table[h]; + n = _m3dstbiw___sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = _m3dstbiw___zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + _m3dstbiw___zlib_huff(j+257); + if (lengtheb[j]) _m3dstbiw___zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + _m3dstbiw___zlib_add(_m3dstbiw___zlib_bitrev(j,5),5); + if (disteb[j]) _m3dstbiw___zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + _m3dstbiw___zlib_huffb(data[i]); + ++i; + } + } + for (;i < data_len; ++i) + _m3dstbiw___zlib_huffb(data[i]); + _m3dstbiw___zlib_huff(256); + while (bitcount) + _m3dstbiw___zlib_add(0,1); + + for (i=0; i < _m3dstbiw___ZHASH; ++i) + (void) _m3dstbiw___sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + _m3dstbiw___sbpush(out, STBIW_UCHAR(s2 >> 8)); + _m3dstbiw___sbpush(out, STBIW_UCHAR(s2)); + _m3dstbiw___sbpush(out, STBIW_UCHAR(s1 >> 8)); + _m3dstbiw___sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = _m3dstbiw___sbn(out); + STBIW_MEMMOVE(_m3dstbiw___sbraw(out), out, *out_len); + return (unsigned char *) _m3dstbiw___sbraw(out); +} +#define stbi_zlib_compress _m3dstbi_zlib_compress +#else +unsigned char * _m3dstbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality); +#endif + +#define M3D_CHUNKMAGIC(m, a,b,c,d) ((m)[0]==(a) && (m)[1]==(b) && (m)[2]==(c) && (m)[3]==(d)) + +#ifdef M3D_ASCII +#include /* get sprintf */ +#include /* sprintf and strtod cares about number locale */ +#endif +#ifdef M3D_PROFILING +#include +#endif + +#if !defined(M3D_NOIMPORTER) && defined(M3D_ASCII) +/* helper functions for the ASCII parser */ +static char *_m3d_findarg(char *s) { + while(s && *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') s++; + while(s && *s && (*s == ' ' || *s == '\t')) s++; + return s; +} +static char *_m3d_findnl(char *s) { + while(s && *s && *s != '\r' && *s != '\n') s++; + if(*s == '\r') s++; + if(*s == '\n') s++; + return s; +} +static char *_m3d_gethex(char *s, uint32_t *ret) +{ + if(*s == '#') s++; + *ret = 0; + for(; *s; s++) { + if(*s >= '0' && *s <= '9') { *ret <<= 4; *ret += (uint32_t)(*s-'0'); } + else if(*s >= 'a' && *s <= 'f') { *ret <<= 4; *ret += (uint32_t)(*s-'a'+10); } + else if(*s >= 'A' && *s <= 'F') { *ret <<= 4; *ret += (uint32_t)(*s-'A'+10); } + else break; + } + return _m3d_findarg(s); +} +static char *_m3d_getint(char *s, uint32_t *ret) +{ + char *e = s; + if(!s || !*s || *s == '\r' || *s == '\n') return s; + for(; *e >= '0' && *e <= '9'; e++); + *ret = atoi(s); + return e; +} +static char *_m3d_getfloat(char *s, M3D_FLOAT *ret) +{ + char *e = s; + if(!s || !*s || *s == '\r' || *s == '\n') return s; + for(; *e == '-' || *e == '+' || *e == '.' || (*e >= '0' && *e <= '9') || *e == 'e' || *e == 'E'; e++); + *ret = (M3D_FLOAT)strtod(s, NULL); + return _m3d_findarg(e); +} +#endif +#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_ASCII) || defined(M3D_EXPORTER)) +/* helper function to create safe strings */ +char *_m3d_safestr(char *in, int morelines) +{ + char *out, *o, *i = in; + int l; + if(!in || !*in) { + out = (char*)M3D_MALLOC(1); + if(!out) return NULL; + out[0] =0; + } else { + for(o = in, l = 0; *o && ((morelines & 1) || (*o != '\r' && *o != '\n')) && l < 256; o++, l++); + out = o = (char*)M3D_MALLOC(l+1); + if(!out) return NULL; + while(*i == ' ' || *i == '\t' || *i == '\r' || (morelines && *i == '\n')) i++; + for(; *i && (morelines || (*i != '\r' && *i != '\n')); i++) { + if(*i == '\r') continue; + if(*i == '\n') { + if(morelines >= 3 && o > out && *(o-1) == '\n') break; + if(i > in && *(i-1) == '\n') continue; + if(morelines & 1) { + if(morelines == 1) *o++ = '\r'; + *o++ = '\n'; + } else + break; + } else + if(*i == ' ' || *i == '\t') { + *o++ = morelines? ' ' : '_'; + } else + *o++ = !morelines && (*i == '/' || *i == '\\') ? '_' : *i; + } + for(; o > out && (*(o-1) == ' ' || *(o-1) == '\t' || *(o-1) == '\r' || *(o-1) == '\n'); o--); + *o = 0; + out = (char*)M3D_REALLOC(out, (uintptr_t)o - (uintptr_t)out + 1); + } + return out; +} +#endif +#ifndef M3D_NOIMPORTER +/* helper function to load and decode/generate a texture */ +M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char *fn) +{ + unsigned int i, len = 0; + unsigned char *buff = NULL; + char *fn2; +#ifdef STBI__PNG_TYPE + unsigned int w, h; + stbi__context s; + stbi__result_info ri; +#endif + + /* do we have loaded this texture already? */ + for(i = 0; i < model->numtexture; i++) + if(!strcmp(fn, model->texture[i].name)) return i; + /* see if it's inlined in the model */ + if(model->inlined) { + for(i = 0; i < model->numinlined; i++) + if(!strcmp(fn, model->inlined[i].name)) { + buff = model->inlined[i].data; + len = model->inlined[i].length; + freecb = NULL; + break; + } + } + /* try to load from external source */ + if(!buff && readfilecb) { + i = (unsigned int)strlen(fn); + if(i < 5 || fn[i - 4] != '.') { + fn2 = (char*)M3D_MALLOC(i + 5); + if(!fn2) { model->errcode = M3D_ERR_ALLOC; return M3D_UNDEF; } + memcpy(fn2, fn, i); + memcpy(fn2+i, ".png", 5); + buff = (*readfilecb)(fn2, &len); + M3D_FREE(fn2); + } + if(!buff) { + buff = (*readfilecb)(fn, &len); + if(!buff) return M3D_UNDEF; + } + } + /* add to textures array */ + i = model->numtexture++; + model->texture = (m3dtx_t*)M3D_REALLOC(model->texture, model->numtexture * sizeof(m3dtx_t)); + if(!model->texture) { + if(buff && freecb) (*freecb)(buff); + model->errcode = M3D_ERR_ALLOC; + return M3D_UNDEF; + } + model->texture[i].name = fn; + model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL; + if(buff) { + if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { +#ifdef STBI__PNG_TYPE + s.read_from_callbacks = 0; + s.img_buffer = s.img_buffer_original = (unsigned char *) buff; + s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; + /* don't use model->texture[i].w directly, it's a uint16_t */ + w = h = len = 0; + ri.bits_per_channel = 8; + model->texture[i].d = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&len, 0, &ri); + model->texture[i].w = w; + model->texture[i].h = h; + model->texture[i].f = (uint8_t)len; +#endif + } else { +#ifdef M3D_TX_INTERP + if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { + M3D_LOG("Unable to generate texture"); + M3D_LOG(fn); + } +#else + M3D_LOG("Unimplemented interpreter"); + M3D_LOG(fn); +#endif + } + if(freecb) (*freecb)(buff); + } + if(!model->texture[i].d) + model->errcode = M3D_ERR_UNKIMG; + return i; +} + +/* helper function to load and generate a procedural surface */ +void _m3d_getpr(m3d_t *model, _unused m3dread_t readfilecb, _unused m3dfree_t freecb, _unused char *fn) +{ +#ifdef M3D_PR_INTERP + unsigned int i, len = 0; + unsigned char *buff = readfilecb ? (*readfilecb)(fn, &len) : NULL; + + if(!buff && model->inlined) { + for(i = 0; i < model->numinlined; i++) + if(!strcmp(fn, model->inlined[i].name)) { + buff = model->inlined[i].data; + len = model->inlined[i].length; + freecb = NULL; + break; + } + } + if(!buff || !len || (model->errcode = M3D_PR_INTERP(fn, buff, len, model)) != M3D_SUCCESS) { + M3D_LOG("Unable to generate procedural surface"); + M3D_LOG(fn); + model->errcode = M3D_ERR_UNKIMG; + } + if(freecb && buff) (*freecb)(buff); +#else + (void)readfilecb; + (void)freecb; + (void)fn; + M3D_LOG("Unimplemented interpreter"); + M3D_LOG(fn); + model->errcode = M3D_ERR_UNIMPL; +#endif +} +/* helpers to read indices from data stream */ +#define M3D_GETSTR(x) do{offs=0;data=_m3d_getidx(data,model->si_s,&offs);x=offs?((char*)model->raw+16+offs):NULL;}while(0) +_inline static unsigned char *_m3d_getidx(unsigned char *data, char type, M3D_INDEX *idx) +{ + switch(type) { + case 1: *idx = data[0] > 253 ? (int8_t)data[0] : data[0]; data++; break; + case 2: *idx = *((uint16_t*)data) > 65533 ? *((int16_t*)data) : *((uint16_t*)data); data += 2; break; + case 4: *idx = *((int32_t*)data); data += 4; break; + } + return data; +} + +#ifndef M3D_NOANIMATION +/* multiply 4 x 4 matrices. Do not use float *r[16] as argument, because some compilers misinterpret that as + * 16 pointers each pointing to a float, but we need a single pointer to 16 floats. */ +void _m3d_mul(M3D_FLOAT *r, M3D_FLOAT *a, M3D_FLOAT *b) +{ + r[ 0] = b[ 0] * a[ 0] + b[ 4] * a[ 1] + b[ 8] * a[ 2] + b[12] * a[ 3]; + r[ 1] = b[ 1] * a[ 0] + b[ 5] * a[ 1] + b[ 9] * a[ 2] + b[13] * a[ 3]; + r[ 2] = b[ 2] * a[ 0] + b[ 6] * a[ 1] + b[10] * a[ 2] + b[14] * a[ 3]; + r[ 3] = b[ 3] * a[ 0] + b[ 7] * a[ 1] + b[11] * a[ 2] + b[15] * a[ 3]; + r[ 4] = b[ 0] * a[ 4] + b[ 4] * a[ 5] + b[ 8] * a[ 6] + b[12] * a[ 7]; + r[ 5] = b[ 1] * a[ 4] + b[ 5] * a[ 5] + b[ 9] * a[ 6] + b[13] * a[ 7]; + r[ 6] = b[ 2] * a[ 4] + b[ 6] * a[ 5] + b[10] * a[ 6] + b[14] * a[ 7]; + r[ 7] = b[ 3] * a[ 4] + b[ 7] * a[ 5] + b[11] * a[ 6] + b[15] * a[ 7]; + r[ 8] = b[ 0] * a[ 8] + b[ 4] * a[ 9] + b[ 8] * a[10] + b[12] * a[11]; + r[ 9] = b[ 1] * a[ 8] + b[ 5] * a[ 9] + b[ 9] * a[10] + b[13] * a[11]; + r[10] = b[ 2] * a[ 8] + b[ 6] * a[ 9] + b[10] * a[10] + b[14] * a[11]; + r[11] = b[ 3] * a[ 8] + b[ 7] * a[ 9] + b[11] * a[10] + b[15] * a[11]; + r[12] = b[ 0] * a[12] + b[ 4] * a[13] + b[ 8] * a[14] + b[12] * a[15]; + r[13] = b[ 1] * a[12] + b[ 5] * a[13] + b[ 9] * a[14] + b[13] * a[15]; + r[14] = b[ 2] * a[12] + b[ 6] * a[13] + b[10] * a[14] + b[14] * a[15]; + r[15] = b[ 3] * a[12] + b[ 7] * a[13] + b[11] * a[14] + b[15] * a[15]; +} +/* calculate 4 x 4 matrix inverse */ +void _m3d_inv(M3D_FLOAT *m) +{ + M3D_FLOAT r[16]; + M3D_FLOAT det = + m[ 0]*m[ 5]*m[10]*m[15] - m[ 0]*m[ 5]*m[11]*m[14] + m[ 0]*m[ 6]*m[11]*m[13] - m[ 0]*m[ 6]*m[ 9]*m[15] + + m[ 0]*m[ 7]*m[ 9]*m[14] - m[ 0]*m[ 7]*m[10]*m[13] - m[ 1]*m[ 6]*m[11]*m[12] + m[ 1]*m[ 6]*m[ 8]*m[15] + - m[ 1]*m[ 7]*m[ 8]*m[14] + m[ 1]*m[ 7]*m[10]*m[12] - m[ 1]*m[ 4]*m[10]*m[15] + m[ 1]*m[ 4]*m[11]*m[14] + + m[ 2]*m[ 7]*m[ 8]*m[13] - m[ 2]*m[ 7]*m[ 9]*m[12] + m[ 2]*m[ 4]*m[ 9]*m[15] - m[ 2]*m[ 4]*m[11]*m[13] + + m[ 2]*m[ 5]*m[11]*m[12] - m[ 2]*m[ 5]*m[ 8]*m[15] - m[ 3]*m[ 4]*m[ 9]*m[14] + m[ 3]*m[ 4]*m[10]*m[13] + - m[ 3]*m[ 5]*m[10]*m[12] + m[ 3]*m[ 5]*m[ 8]*m[14] - m[ 3]*m[ 6]*m[ 8]*m[13] + m[ 3]*m[ 6]*m[ 9]*m[12]; + if(det == (M3D_FLOAT)0.0 || det == (M3D_FLOAT)-0.0) det = (M3D_FLOAT)1.0; else det = (M3D_FLOAT)1.0 / det; + r[ 0] = det *(m[ 5]*(m[10]*m[15] - m[11]*m[14]) + m[ 6]*(m[11]*m[13] - m[ 9]*m[15]) + m[ 7]*(m[ 9]*m[14] - m[10]*m[13])); + r[ 1] = -det*(m[ 1]*(m[10]*m[15] - m[11]*m[14]) + m[ 2]*(m[11]*m[13] - m[ 9]*m[15]) + m[ 3]*(m[ 9]*m[14] - m[10]*m[13])); + r[ 2] = det *(m[ 1]*(m[ 6]*m[15] - m[ 7]*m[14]) + m[ 2]*(m[ 7]*m[13] - m[ 5]*m[15]) + m[ 3]*(m[ 5]*m[14] - m[ 6]*m[13])); + r[ 3] = -det*(m[ 1]*(m[ 6]*m[11] - m[ 7]*m[10]) + m[ 2]*(m[ 7]*m[ 9] - m[ 5]*m[11]) + m[ 3]*(m[ 5]*m[10] - m[ 6]*m[ 9])); + r[ 4] = -det*(m[ 4]*(m[10]*m[15] - m[11]*m[14]) + m[ 6]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 7]*(m[ 8]*m[14] - m[10]*m[12])); + r[ 5] = det *(m[ 0]*(m[10]*m[15] - m[11]*m[14]) + m[ 2]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 3]*(m[ 8]*m[14] - m[10]*m[12])); + r[ 6] = -det*(m[ 0]*(m[ 6]*m[15] - m[ 7]*m[14]) + m[ 2]*(m[ 7]*m[12] - m[ 4]*m[15]) + m[ 3]*(m[ 4]*m[14] - m[ 6]*m[12])); + r[ 7] = det *(m[ 0]*(m[ 6]*m[11] - m[ 7]*m[10]) + m[ 2]*(m[ 7]*m[ 8] - m[ 4]*m[11]) + m[ 3]*(m[ 4]*m[10] - m[ 6]*m[ 8])); + r[ 8] = det *(m[ 4]*(m[ 9]*m[15] - m[11]*m[13]) + m[ 5]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 7]*(m[ 8]*m[13] - m[ 9]*m[12])); + r[ 9] = -det*(m[ 0]*(m[ 9]*m[15] - m[11]*m[13]) + m[ 1]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 3]*(m[ 8]*m[13] - m[ 9]*m[12])); + r[10] = det *(m[ 0]*(m[ 5]*m[15] - m[ 7]*m[13]) + m[ 1]*(m[ 7]*m[12] - m[ 4]*m[15]) + m[ 3]*(m[ 4]*m[13] - m[ 5]*m[12])); + r[11] = -det*(m[ 0]*(m[ 5]*m[11] - m[ 7]*m[ 9]) + m[ 1]*(m[ 7]*m[ 8] - m[ 4]*m[11]) + m[ 3]*(m[ 4]*m[ 9] - m[ 5]*m[ 8])); + r[12] = -det*(m[ 4]*(m[ 9]*m[14] - m[10]*m[13]) + m[ 5]*(m[10]*m[12] - m[ 8]*m[14]) + m[ 6]*(m[ 8]*m[13] - m[ 9]*m[12])); + r[13] = det *(m[ 0]*(m[ 9]*m[14] - m[10]*m[13]) + m[ 1]*(m[10]*m[12] - m[ 8]*m[14]) + m[ 2]*(m[ 8]*m[13] - m[ 9]*m[12])); + r[14] = -det*(m[ 0]*(m[ 5]*m[14] - m[ 6]*m[13]) + m[ 1]*(m[ 6]*m[12] - m[ 4]*m[14]) + m[ 2]*(m[ 4]*m[13] - m[ 5]*m[12])); + r[15] = det *(m[ 0]*(m[ 5]*m[10] - m[ 6]*m[ 9]) + m[ 1]*(m[ 6]*m[ 8] - m[ 4]*m[10]) + m[ 2]*(m[ 4]*m[ 9] - m[ 5]*m[ 8])); + memcpy(m, &r, sizeof(r)); +} +/* compose a coloumn major 4 x 4 matrix from vec3 position and vec4 orientation/rotation quaternion */ +void _m3d_mat(M3D_FLOAT *r, m3dv_t *p, m3dv_t *q) +{ + if(q->x == (M3D_FLOAT)0.0 && q->y == (M3D_FLOAT)0.0 && q->z >=(M3D_FLOAT) 0.7071065 && q->z <= (M3D_FLOAT)0.7071075 && + q->w == (M3D_FLOAT)0.0) { + r[ 1] = r[ 2] = r[ 4] = r[ 6] = r[ 8] = r[ 9] = (M3D_FLOAT)0.0; + r[ 0] = r[ 5] = r[10] = (M3D_FLOAT)-1.0; + } else { + r[ 0] = 1 - 2 * (q->y * q->y + q->z * q->z); if(r[ 0]>-M3D_EPSILON && r[ 0]x * q->y - q->z * q->w); if(r[ 1]>-M3D_EPSILON && r[ 1]x * q->z + q->y * q->w); if(r[ 2]>-M3D_EPSILON && r[ 2]x * q->y + q->z * q->w); if(r[ 4]>-M3D_EPSILON && r[ 4]x * q->x + q->z * q->z); if(r[ 5]>-M3D_EPSILON && r[ 5]y * q->z - q->x * q->w); if(r[ 6]>-M3D_EPSILON && r[ 6]x * q->z - q->y * q->w); if(r[ 8]>-M3D_EPSILON && r[ 8]y * q->z + q->x * q->w); if(r[ 9]>-M3D_EPSILON && r[ 9]x * q->x + q->y * q->y); if(r[10]>-M3D_EPSILON && r[10]x; r[ 7] = p->y; r[11] = p->z; + r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 1; +} +#endif +#if !defined(M3D_NOANIMATION) || !defined(M3D_NONORMALS) +/* portable fast inverse square root calculation. returns 1/sqrt(x) */ +static M3D_FLOAT _m3d_rsq(M3D_FLOAT x) +{ +#ifdef M3D_DOUBLE + return ((M3D_FLOAT)15.0/(M3D_FLOAT)8.0) + ((M3D_FLOAT)-5.0/(M3D_FLOAT)4.0)*x + ((M3D_FLOAT)3.0/(M3D_FLOAT)8.0)*x*x; +#else + /* John Carmack's */ + float x2 = x * 0.5f; + uint32_t *i = (uint32_t*)&x; + *i = (0x5f3759df - (*i >> 1)); + return x * (1.5f - (x2 * x * x)); +#endif +} +#endif + +/** + * Function to decode a Model 3D into in-memory format + */ +m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib) +{ + unsigned char *end, *chunk, *buff, weights[8]; + unsigned int i, j, k, l, n, am, len = 0, reclen, offs; +#ifndef M3D_NOVOXELS + int32_t min_x, min_y, min_z, max_x, max_y, max_z, sx, sy, sz, x, y, z; + M3D_INDEX edge[8], enorm; +#endif + char *name, *lang; + float f; + m3d_t *model; + M3D_INDEX mi; +#ifdef M3D_VERTEXMAX + M3D_INDEX pi; +#endif + M3D_FLOAT w; + m3dcd_t *cd; + m3dtx_t *tx; + m3dh_t *h; + m3dm_t *m; + m3da_t *a; + m3di_t *t; +#ifndef M3D_NONORMALS + char neednorm = 0; + m3dv_t *norm = NULL, *v0, *v1, *v2, va, vb; +#endif +#ifndef M3D_NOANIMATION + M3D_FLOAT r[16]; +#endif +#if !defined(M3D_NOWEIGHTS) || !defined(M3D_NOANIMATION) + m3db_t *b; +#endif +#ifndef M3D_NOWEIGHTS + m3ds_t *sk; +#endif +#ifdef M3D_ASCII + m3ds_t s; + M3D_INDEX bi[M3D_BONEMAXLEVEL+1], level; + const char *ol; + char *ptr, *pe, *fn; +#endif +#ifdef M3D_PROFILING + struct timeval tv0, tv1, tvd; + gettimeofday(&tv0, NULL); +#endif + + if(!data || (!M3D_CHUNKMAGIC(data, '3','D','M','O') +#ifdef M3D_ASCII + && !M3D_CHUNKMAGIC(data, '3','d','m','o') +#endif + )) return NULL; + model = (m3d_t*)M3D_MALLOC(sizeof(m3d_t)); + if(!model) { + M3D_LOG("Out of memory"); + return NULL; + } + memset(model, 0, sizeof(m3d_t)); + + if(mtllib) { + model->nummaterial = mtllib->nummaterial; + model->material = mtllib->material; + model->numtexture = mtllib->numtexture; + model->texture = mtllib->texture; + model->flags |= M3D_FLG_MTLLIB; + } +#ifdef M3D_ASCII + /* ASCII variant? */ + if(M3D_CHUNKMAGIC(data, '3','d','m','o')) { + model->errcode = M3D_ERR_BADFILE; + model->flags |= M3D_FLG_FREESTR; + model->raw = (m3dhdr_t*)data; + ptr = (char*)data; + ol = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + /* parse header. Don't use sscanf, that's incredibly slow */ + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + pe = _m3d_findnl(ptr); + model->scale = (float)strtod(ptr, NULL); ptr = pe; + if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0; + model->name = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr); + if(!*ptr) goto asciiend; + model->license = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr); + if(!*ptr) goto asciiend; + model->author = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr); + if(!*ptr) goto asciiend; + if(*ptr != '\r' && *ptr != '\n') + model->desc = _m3d_safestr(ptr, 3); + while(*ptr) { + while(*ptr && *ptr!='\n') ptr++; + ptr++; if(*ptr=='\r') ptr++; + if(*ptr == '\n') break; + } + + /* the main chunk reader loop */ + while(*ptr) { + while(*ptr && (*ptr == '\r' || *ptr == '\n')) ptr++; + if(!*ptr || (ptr[0]=='E' && ptr[1]=='n' && ptr[2]=='d')) break; + /* make sure there's at least one data row */ + pe = ptr; ptr = _m3d_findnl(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + /* Preview chunk */ + if(!memcmp(pe, "Preview", 7)) { + if(readfilecb) { + pe = _m3d_safestr(ptr, 0); + if(!pe || !*pe) goto asciiend; + model->preview.data = (*readfilecb)(pe, &model->preview.length); + M3D_FREE(pe); + } + while(*ptr && *ptr != '\r' && *ptr != '\n') + ptr = _m3d_findnl(ptr); + } else + /* texture map chunk */ + if(!memcmp(pe, "Textmap", 7)) { + if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); goto asciiend; } + while(*ptr && *ptr != '\r' && *ptr != '\n') { + i = model->numtmap++; + model->tmap = (m3dti_t*)M3D_REALLOC(model->tmap, model->numtmap * sizeof(m3dti_t)); + if(!model->tmap) goto memerr; + ptr = _m3d_getfloat(ptr, &model->tmap[i].u); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + _m3d_getfloat(ptr, &model->tmap[i].v); + ptr = _m3d_findnl(ptr); + } + } else + /* vertex chunk */ + if(!memcmp(pe, "Vertex", 6)) { + if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); goto asciiend; } + while(*ptr && *ptr != '\r' && *ptr != '\n') { + i = model->numvertex++; + model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); + if(!model->vertex) goto memerr; + memset(&model->vertex[i], 0, sizeof(m3dv_t)); + model->vertex[i].skinid = M3D_UNDEF; + model->vertex[i].color = 0; + model->vertex[i].w = (M3D_FLOAT)1.0; + ptr = _m3d_getfloat(ptr, &model->vertex[i].x); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getfloat(ptr, &model->vertex[i].y); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getfloat(ptr, &model->vertex[i].z); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getfloat(ptr, &model->vertex[i].w); + if(!*ptr) goto asciiend; + if(*ptr == '#') { + ptr = _m3d_gethex(ptr, &model->vertex[i].color); + if(!*ptr) goto asciiend; + } + /* parse skin */ + memset(&s, 0, sizeof(m3ds_t)); + for(j = 0, w = (M3D_FLOAT)0.0; j < M3D_NUMBONE && *ptr && *ptr != '\r' && *ptr != '\n'; j++) { + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getint(ptr, &k); + s.boneid[j] = (M3D_INDEX)k; + if(*ptr == ':') { + ptr++; + ptr = _m3d_getfloat(ptr, &s.weight[j]); + w += s.weight[j]; + } else if(!j) + s.weight[j] = (M3D_FLOAT)1.0; + if(!*ptr) goto asciiend; + } + if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) { + if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) + for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++) + s.weight[j] /= w; + k = M3D_NOTDEFINED; + if(model->skin) { + for(j = 0; j < model->numskin; j++) + if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; } + } + if(k == M3D_NOTDEFINED) { + k = model->numskin++; + model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t)); + if(!model->skin) goto memerr; + memcpy(&model->skin[k], &s, sizeof(m3ds_t)); + } + model->vertex[i].skinid = (M3D_INDEX)k; + } + ptr = _m3d_findnl(ptr); + } + } else + /* Skeleton, bone hierarchy */ + if(!memcmp(pe, "Bones", 5)) { + if(model->bone) { M3D_LOG("More bones chunks, should be unique"); goto asciiend; } + bi[0] = M3D_UNDEF; + while(*ptr && *ptr != '\r' && *ptr != '\n') { + i = model->numbone++; + model->bone = (m3db_t*)M3D_REALLOC(model->bone, model->numbone * sizeof(m3db_t)); + if(!model->bone) goto memerr; + for(level = 0; *ptr == '/'; ptr++, level++); + if(level > M3D_BONEMAXLEVEL || !*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + bi[level+1] = i; + model->bone[i].numweight = 0; + model->bone[i].weight = NULL; + model->bone[i].parent = bi[level]; + ptr = _m3d_getint(ptr, &k); + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + model->bone[i].pos = (M3D_INDEX)k; + ptr = _m3d_getint(ptr, &k); + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + model->bone[i].ori = (M3D_INDEX)k; + model->vertex[k].skinid = M3D_INDEXMAX; + pe = _m3d_safestr(ptr, 0); + if(!pe || !*pe) goto asciiend; + model->bone[i].name = pe; + ptr = _m3d_findnl(ptr); + } + } else + /* material chunk */ + if(!memcmp(pe, "Material", 8)) { + pe = _m3d_findarg(pe); + if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; + pe = _m3d_safestr(pe, 0); + if(!pe || !*pe) goto asciiend; + for(i = 0; i < model->nummaterial; i++) + if(!strcmp(pe, model->material[i].name)) { + M3D_LOG("Multiple definitions for material"); + M3D_LOG(pe); + M3D_FREE(pe); + pe = NULL; + while(*ptr && *ptr != '\r' && *ptr != '\n') ptr = _m3d_findnl(ptr); + break; + } + if(!pe) continue; + i = model->nummaterial++; + if(model->flags & M3D_FLG_MTLLIB) { + m = model->material; + model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t)); + if(!model->material) goto memerr; + memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t)); + if(model->texture) { + tx = model->texture; + model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t)); + if(!model->texture) goto memerr; + memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t)); + } + model->flags &= ~M3D_FLG_MTLLIB; + } else { + model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t)); + if(!model->material) goto memerr; + } + m = &model->material[i]; + m->name = pe; + m->numprop = 0; + m->prop = NULL; + while(*ptr && *ptr != '\r' && *ptr != '\n') { + k = n = 256; + if(*ptr == 'm' && *(ptr+1) == 'a' && *(ptr+2) == 'p' && *(ptr+3) == '_') { + k = m3dpf_map; + ptr += 4; + } + for(j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++) + if(!memcmp(ptr, m3d_propertytypes[j].key, strlen(m3d_propertytypes[j].key))) { + n = m3d_propertytypes[j].id; + if(k != m3dpf_map) k = m3d_propertytypes[j].format; + break; + } + if(n != 256 && k != 256) { + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + j = m->numprop++; + m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t)); + if(!m->prop) goto memerr; + m->prop[j].type = n + (k == m3dpf_map && n < 128 ? 128 : 0); + switch(k) { + case m3dpf_color: ptr = _m3d_gethex(ptr, &m->prop[j].value.color); break; + case m3dpf_uint8: + case m3dpf_uint16: + case m3dpf_uint32: ptr = _m3d_getint(ptr, &m->prop[j].value.num); break; + case m3dpf_float: ptr = _m3d_getfloat(ptr, &m->prop[j].value.fnum); break; + case m3dpf_map: + pe = _m3d_safestr(ptr, 0); + if(!pe || !*pe) goto asciiend; + m->prop[j].value.textureid = _m3d_gettx(model, readfilecb, freecb, pe); + if(model->errcode == M3D_ERR_ALLOC) { M3D_FREE(pe); goto memerr; } + /* this error code only returned if readfilecb was specified */ + if(m->prop[j].value.textureid == M3D_UNDEF) { + M3D_LOG("Texture not found"); + M3D_LOG(pe); + m->numprop--; + } + M3D_FREE(pe); + break; + } + } else { + M3D_LOG("Unknown material property in"); + M3D_LOG(m->name); + model->errcode = M3D_ERR_UNKPROP; + } + ptr = _m3d_findnl(ptr); + } + if(!m->numprop) model->nummaterial--; + } else + /* procedural */ + if(!memcmp(pe, "Procedural", 10)) { + pe = _m3d_safestr(ptr, 0); + _m3d_getpr(model, readfilecb, freecb, pe); + M3D_FREE(pe); + while(*ptr && *ptr != '\r' && *ptr != '\n') ptr = _m3d_findnl(ptr); + } else + /* mesh */ + if(!memcmp(pe, "Mesh", 4)) { + mi = M3D_UNDEF; +#ifdef M3D_VERTEXMAX + pi = M3D_UNDEF; +#endif + while(*ptr && *ptr != '\r' && *ptr != '\n') { + if(*ptr == 'u') { + ptr = _m3d_findarg(ptr); + if(!*ptr) goto asciiend; + mi = M3D_UNDEF; + if(*ptr != '\r' && *ptr != '\n') { + pe = _m3d_safestr(ptr, 0); + if(!pe || !*pe) goto asciiend; + for(j = 0; j < model->nummaterial; j++) + if(!strcmp(pe, model->material[j].name)) { mi = (M3D_INDEX)j; break; } + if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) { + mi = model->nummaterial++; + model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t)); + if(!model->material) goto memerr; + model->material[mi].name = pe; + model->material[mi].numprop = 1; + model->material[mi].prop = NULL; + } else + M3D_FREE(pe); + } + } else + if(*ptr == 'p') { + ptr = _m3d_findarg(ptr); + if(!*ptr) goto asciiend; +#ifdef M3D_VERTEXMAX + pi = M3D_UNDEF; + if(*ptr != '\r' && *ptr != '\n') { + pe = _m3d_safestr(ptr, 0); + if(!pe || !*pe) goto asciiend; + for(j = 0; j < model->numparam; j++) + if(!strcmp(pe, model->param[j].name)) { pi = (M3D_INDEX)j; break; } + if(pi == M3D_UNDEF) { + pi = model->numparam++; + model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t)); + if(!model->param) goto memerr; + model->param[pi].name = pe; + model->param[pi].count = 0; + } else + M3D_FREE(pe); + } +#endif + } else { + i = model->numface++; + model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t)); + if(!model->face) goto memerr; + memset(&model->face[i], 255, sizeof(m3df_t)); /* set all index to -1 by default */ + model->face[i].materialid = mi; +#ifdef M3D_VERTEXMAX + model->face[i].paramid = pi; +#endif + /* hardcoded triangles. */ + for(j = 0; j < 3; j++) { + /* vertex */ + ptr = _m3d_getint(ptr, &k); + model->face[i].vertex[j] = (M3D_INDEX)k; + if(!*ptr) goto asciiend; + if(*ptr == '/') { + ptr++; + if(*ptr != '/') { + /* texcoord */ + ptr = _m3d_getint(ptr, &k); + model->face[i].texcoord[j] = (M3D_INDEX)k; + if(!*ptr) goto asciiend; + } + if(*ptr == '/') { + ptr++; + /* normal */ + ptr = _m3d_getint(ptr, &k); + model->face[i].normal[j] = (M3D_INDEX)k; + if(!*ptr) goto asciiend; + } + if(*ptr == '/') { + ptr++; + /* maximum */ + ptr = _m3d_getint(ptr, &k); +#ifdef M3D_VERTEXMAX + model->face[i].vertmax[j] = (M3D_INDEX)k; +#endif + if(!*ptr) goto asciiend; + } + } +#ifndef M3D_NONORMALS + if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1; +#endif + ptr = _m3d_findarg(ptr); + } + } + ptr = _m3d_findnl(ptr); + } + } else + /* voxel types chunk */ + if(!memcmp(pe, "VoxTypes", 8) || !memcmp(pe, "Voxtypes", 8)) { + if(model->voxtype) { M3D_LOG("More voxel types chunks, should be unique"); goto asciiend; } + while(*ptr && *ptr != '\r' && *ptr != '\n') { + i = model->numvoxtype++; + model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t)); + if(!model->voxtype) goto memerr; + memset(&model->voxtype[i], 0, sizeof(m3dvt_t)); + model->voxtype[i].materialid = M3D_UNDEF; + model->voxtype[i].skinid = M3D_UNDEF; + ptr = _m3d_gethex(ptr, &model->voxtype[i].color); + if(!*ptr) goto asciiend; + if(*ptr == '/') { + ptr = _m3d_gethex(ptr, &k); + model->voxtype[i].rotation = k; + if(!*ptr) goto asciiend; + if(*ptr == '/') { + ptr = _m3d_gethex(ptr, &k); + model->voxtype[i].voxshape = k; + if(!*ptr) goto asciiend; + } + } + while(*ptr == ' ' || *ptr == '\t') ptr++; + if(*ptr == '\r' || *ptr == '\n') { ptr = _m3d_findnl(ptr); continue; } + /* name */ + if(*ptr != '-') { + pe = _m3d_safestr(ptr, 0); + if(!pe || !*pe) goto asciiend; + model->voxtype[i].name = pe; + for(j = 0; j < model->nummaterial; j++) + if(!strcmp(pe, model->material[j].name)) { model->voxtype[i].materialid = (M3D_INDEX)j; break; } + } + ptr = _m3d_findarg(ptr); + /* parse skin */ + memset(&s, 0, sizeof(m3ds_t)); + for(j = 0, w = (M3D_FLOAT)0.0; j < M3D_NUMBONE && *ptr && *ptr != '{' && *ptr != '\r' && *ptr != '\n'; j++) { + ptr = _m3d_getint(ptr, &k); + s.boneid[j] = (M3D_INDEX)k; + if(*ptr == ':') { + ptr++; + ptr = _m3d_getfloat(ptr, &s.weight[j]); + w += s.weight[j]; + } else if(!j) + s.weight[j] = (M3D_FLOAT)1.0; + if(!*ptr) goto asciiend; + ptr = _m3d_findarg(ptr); + } + if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) { + if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) + for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++) + s.weight[j] /= w; + k = M3D_NOTDEFINED; + if(model->skin) { + for(j = 0; j < model->numskin; j++) + if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; } + } + if(k == M3D_NOTDEFINED) { + k = model->numskin++; + model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t)); + if(!model->skin) goto memerr; + memcpy(&model->skin[k], &s, sizeof(m3ds_t)); + } + model->voxtype[i].skinid = (M3D_INDEX)k; + } + /* parse item list */ + if(*ptr == '{') { + while(*ptr == '{' || *ptr == ' ' || *ptr == '\t') ptr++; + while(*ptr && *ptr != '}' && *ptr != '\r' && *ptr != '\n') { + ptr = _m3d_getint(ptr, &k); + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '}' || *ptr == '\r' || *ptr == '\n') goto asciiend; + pe = _m3d_safestr(ptr, 0); + if(!pe || !*pe) goto asciiend; + ptr = _m3d_findarg(ptr); + j = model->voxtype[i].numitem++; + model->voxtype[i].item = (m3dvi_t*)M3D_REALLOC(model->voxtype[i].item, + model->voxtype[i].numitem * sizeof(m3dvi_t)); + if(!model->voxtype[i].item) goto memerr; + model->voxtype[i].item[j].count = k; + model->voxtype[i].item[j].name = pe; + } + if(*ptr != '}') goto asciiend; + } + ptr = _m3d_findnl(ptr); + } + } else + /* voxel data */ + if(!memcmp(pe, "Voxel", 5)) { + if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); goto asciiend; } + pe = _m3d_findarg(pe); + if(!*pe) goto asciiend; + if(*pe == '\r' || *pe == '\n') pe = NULL; + else pe = _m3d_safestr(pe, 0); + i = model->numvoxel++; + model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t)); + if(!model->voxel) goto memerr; + memset(&model->voxel[i], 0, sizeof(m3dvx_t)); + model->voxel[i].name = pe; + k = l = 0; + while(*ptr && *ptr != '\r' && *ptr != '\n') { + switch(*ptr) { + case 'u': + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getint(ptr, &n); + model->voxel[i].uncertain = ((n > 0 && n < 256 ? n : 0) * 255) / 100; + ptr = _m3d_findarg(ptr); + if(*ptr && *ptr != '\r' && *ptr != '\n') { + ptr = _m3d_getint(ptr, &n); + model->voxel[i].groupid = n > 0 && n < 256 ? n : 0; + } + break; + case 'p': + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getint(ptr, &n); + model->voxel[i].x = n; + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getint(ptr, &n); + model->voxel[i].y = n; + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getint(ptr, &n); + model->voxel[i].z = n; + break; + case 'd': + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getint(ptr, &n); + model->voxel[i].w = n; + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getint(ptr, &n); + model->voxel[i].h = n; + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + ptr = _m3d_getint(ptr, &n); + model->voxel[i].d = n; + break; + case 'l': + if(model->voxel[i].data) { l++; k = 0; } + else { + if(!model->voxel[i].w || !model->voxel[i].h || !model->voxel[i].d) { + M3D_LOG("No voxel dimension before layer data"); + goto asciiend; + } + model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC( + model->voxel[i].w * model->voxel[i].h * model->voxel[i].d * sizeof(M3D_VOXEL)); + if(!model->voxel[i].data) goto memerr; + } + break; + default: + if(!model->voxel[i].data || l >= model->voxel[i].h || k >= model->voxel[i].d) { + M3D_LOG("Missing voxel attributes or out of bound data"); + goto asciiend; + } + for(n = l * model->voxel[i].w * model->voxel[i].d + k * model->voxel[i].w; + j < model->voxel[i].w && *ptr && *ptr != '\r' && *ptr != '\n'; j++) { + ptr = _m3d_getint(ptr, &am); + if(am >= model->numvoxtype) goto asciiend; + model->voxel[i].data[n + j] = am; + } + k++; + break; + } + ptr = _m3d_findnl(ptr); + } + } else + /* mathematical shape */ + if(!memcmp(pe, "Shape", 5)) { + pe = _m3d_findarg(pe); + if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; + pe = _m3d_safestr(pe, 0); + if(!pe || !*pe) goto asciiend; + i = model->numshape++; + model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3ds_t)); + if(!model->shape) goto memerr; + h = &model->shape[i]; + h->name = pe; + h->group = M3D_UNDEF; + h->numcmd = 0; + h->cmd = NULL; + while(*ptr && *ptr != '\r' && *ptr != '\n') { + if(!memcmp(ptr, "group", 5)) { + ptr = _m3d_findarg(ptr); + ptr = _m3d_getint(ptr, &h->group); + ptr = _m3d_findnl(ptr); + if(h->group != M3D_UNDEF && h->group >= model->numbone) { + M3D_LOG("Unknown bone id as shape group in shape"); + M3D_LOG(pe); + h->group = M3D_UNDEF; + model->errcode = M3D_ERR_SHPE; + } + continue; + } + for(cd = NULL, k = 0; k < (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])); k++) { + j = (unsigned int)strlen(m3d_commandtypes[k].key); + if(!memcmp(ptr, m3d_commandtypes[k].key, j) && (ptr[j] == ' ' || ptr[j] == '\r' || ptr[j] == '\n')) + { cd = &m3d_commandtypes[k]; break; } + } + if(cd) { + j = h->numcmd++; + h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t)); + if(!h->cmd) goto memerr; + h->cmd[j].type = k; + h->cmd[j].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t)); + if(!h->cmd[j].arg) goto memerr; + memset(h->cmd[j].arg, 0, cd->p * sizeof(uint32_t)); + for(k = n = 0, l = cd->p; k < l; k++) { + ptr = _m3d_findarg(ptr); + if(!*ptr) goto asciiend; + if(*ptr == '[') { + ptr = _m3d_findarg(ptr + 1); + if(!*ptr) goto asciiend; + } + if(*ptr == ']' || *ptr == '\r' || *ptr == '\n') break; + switch(cd->a[((k - n) % (cd->p - n)) + n]) { + case m3dcp_mi_t: + mi = M3D_UNDEF; + if(*ptr != '\r' && *ptr != '\n') { + pe = _m3d_safestr(ptr, 0); + if(!pe || !*pe) goto asciiend; + for(n = 0; n < model->nummaterial; n++) + if(!strcmp(pe, model->material[n].name)) { mi = (M3D_INDEX)n; break; } + if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) { + mi = model->nummaterial++; + model->material = (m3dm_t*)M3D_REALLOC(model->material, + model->nummaterial * sizeof(m3dm_t)); + if(!model->material) goto memerr; + model->material[mi].name = pe; + model->material[mi].numprop = 1; + model->material[mi].prop = NULL; + } else + M3D_FREE(pe); + } + h->cmd[j].arg[k] = mi; + break; + case m3dcp_vc_t: +#ifdef M3D_DOUBLE + _m3d_getfloat(ptr, &w); f = w; + memcpy(&h->cmd[j].arg[k], &f, 4); +#else + _m3d_getfloat(ptr, (float*)&h->cmd[j].arg[k]); +#endif + break; + case m3dcp_va_t: + ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]); + n = k + 1; l += (h->cmd[j].arg[k] - 1) * (cd->p - k - 1); + h->cmd[j].arg = (uint32_t*)M3D_REALLOC(h->cmd[j].arg, l * sizeof(uint32_t)); + if(!h->cmd[j].arg) goto memerr; + memset(&h->cmd[j].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t)); + break; + case m3dcp_qi_t: + ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]); + model->vertex[h->cmd[i].arg[k]].skinid = M3D_INDEXMAX; + break; + default: + ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]); + break; + } + } + } else { + M3D_LOG("Unknown shape command in"); + M3D_LOG(h->name); + model->errcode = M3D_ERR_UNKCMD; + } + ptr = _m3d_findnl(ptr); + } + if(!h->numcmd) model->numshape--; + } else + /* annotation labels */ + if(!memcmp(pe, "Labels", 6)) { + pe = _m3d_findarg(pe); + if(!*pe) goto asciiend; + if(*pe == '\r' || *pe == '\n') pe = NULL; + else pe = _m3d_safestr(pe, 0); + k = 0; fn = NULL; + while(*ptr && *ptr != '\r' && *ptr != '\n') { + if(*ptr == 'c') { + ptr = _m3d_findarg(ptr); + if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; + ptr = _m3d_gethex(ptr, &k); + } else + if(*ptr == 'l') { + ptr = _m3d_findarg(ptr); + if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; + fn = _m3d_safestr(ptr, 2); + } else { + i = model->numlabel++; + model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t)); + if(!model->label) goto memerr; + model->label[i].name = pe; + model->label[i].lang = fn; + model->label[i].color = k; + ptr = _m3d_getint(ptr, &j); + model->label[i].vertexid = (M3D_INDEX)j; + ptr = _m3d_findarg(ptr); + if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; + model->label[i].text = _m3d_safestr(ptr, 2); + } + ptr = _m3d_findnl(ptr); + } + } else + /* action */ + if(!memcmp(pe, "Action", 6)) { + pe = _m3d_findarg(pe); + if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; + pe = _m3d_getint(pe, &k); + pe = _m3d_findarg(pe); + if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; + pe = _m3d_safestr(pe, 0); + if(!pe || !*pe) goto asciiend; + i = model->numaction++; + model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t)); + if(!model->action) goto memerr; + a = &model->action[i]; + a->name = pe; + a->durationmsec = k; + /* skip the first frame marker as there's always at least one frame */ + a->numframe = 1; + a->frame = (m3dfr_t*)M3D_MALLOC(sizeof(m3dfr_t)); + if(!a->frame) goto memerr; + a->frame[0].msec = 0; + a->frame[0].numtransform = 0; + a->frame[0].transform = NULL; + i = 0; + if(*ptr == 'f') + ptr = _m3d_findnl(ptr); + while(*ptr && *ptr != '\r' && *ptr != '\n') { + if(*ptr == 'f') { + i = a->numframe++; + a->frame = (m3dfr_t*)M3D_REALLOC(a->frame, a->numframe * sizeof(m3dfr_t)); + if(!a->frame) goto memerr; + ptr = _m3d_findarg(ptr); + ptr = _m3d_getint(ptr, &a->frame[i].msec); + a->frame[i].numtransform = 0; + a->frame[i].transform = NULL; + } else { + j = a->frame[i].numtransform++; + a->frame[i].transform = (m3dtr_t*)M3D_REALLOC(a->frame[i].transform, + a->frame[i].numtransform * sizeof(m3dtr_t)); + if(!a->frame[i].transform) goto memerr; + ptr = _m3d_getint(ptr, &k); + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + a->frame[i].transform[j].boneid = (M3D_INDEX)k; + ptr = _m3d_getint(ptr, &k); + ptr = _m3d_findarg(ptr); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + a->frame[i].transform[j].pos = (M3D_INDEX)k; + ptr = _m3d_getint(ptr, &k); + if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; + a->frame[i].transform[j].ori = (M3D_INDEX)k; + model->vertex[k].skinid = M3D_INDEXMAX; + } + ptr = _m3d_findnl(ptr); + } + } else + /* inlined assets chunk */ + if(!memcmp(pe, "Assets", 6)) { + while(*ptr && *ptr != '\r' && *ptr != '\n') { + if(readfilecb) { + pe = _m3d_safestr(ptr, 2); + if(!pe || !*pe) goto asciiend; + i = model->numinlined++; + model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t)); + if(!model->inlined) goto memerr; + t = &model->inlined[i]; + model->inlined[i].data = (*readfilecb)(pe, &model->inlined[i].length); + if(model->inlined[i].data) { + fn = strrchr(pe, '.'); + if(fn && (fn[1] == 'p' || fn[1] == 'P') && (fn[2] == 'n' || fn[2] == 'N') && + (fn[3] == 'g' || fn[3] == 'G')) *fn = 0; + fn = strrchr(pe, '/'); + if(!fn) fn = strrchr(pe, '\\'); + if(!fn) fn = pe; else fn++; + model->inlined[i].name = _m3d_safestr(fn, 0); + } else + model->numinlined--; + M3D_FREE(pe); + } + ptr = _m3d_findnl(ptr); + } + } else + /* extra chunks */ + if(!memcmp(pe, "Extra", 5)) { + pe = _m3d_findarg(pe); + if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; + buff = (unsigned char*)_m3d_findnl(ptr); + k = ((uint32_t)((uintptr_t)buff - (uintptr_t)ptr) / 3) + 1; + i = model->numextra++; + model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*)); + if(!model->extra) goto memerr; + model->extra[i] = (m3dchunk_t*)M3D_MALLOC(k + sizeof(m3dchunk_t)); + if(!model->extra[i]) goto memerr; + memcpy(&model->extra[i]->magic, pe, 4); + model->extra[i]->length = sizeof(m3dchunk_t); + pe = (char*)model->extra[i] + sizeof(m3dchunk_t); + while(*ptr && *ptr != '\r' && *ptr != '\n') { + ptr = _m3d_gethex(ptr, &k); + *pe++ = (uint8_t)k; + model->extra[i]->length++; + } + } else + goto asciiend; + } + model->errcode = M3D_SUCCESS; +asciiend: + setlocale(LC_NUMERIC, ol); + goto postprocess; + } +#endif + /* Binary variant */ + len = ((m3dhdr_t*)data)->length - 8; + data += 8; + if(M3D_CHUNKMAGIC(data, 'P','R','V','W')) { + /* optional preview chunk */ + model->preview.length = ((m3dchunk_t*)data)->length; + model->preview.data = data + sizeof(m3dchunk_t); + data += model->preview.length; + len -= model->preview.length; + } + if(!M3D_CHUNKMAGIC(data, 'H','E','A','D')) { + buff = (unsigned char *)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)data, len, 4096, (int*)&len, 1); + if(!buff || !len || !M3D_CHUNKMAGIC(buff, 'H','E','A','D')) { + if(buff) M3D_FREE(buff); + M3D_FREE(model); + return NULL; + } + buff = (unsigned char*)M3D_REALLOC(buff, len); + model->flags |= M3D_FLG_FREERAW; /* mark that we have to free the raw buffer */ + data = buff; +#ifdef M3D_PROFILING + gettimeofday(&tv1, NULL); + tvd.tv_sec = tv1.tv_sec - tv0.tv_sec; + tvd.tv_usec = tv1.tv_usec - tv0.tv_usec; + if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; } + printf(" Deflate model %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec); + memcpy(&tv0, &tv1, sizeof(struct timeval)); +#endif + } + model->raw = (m3dhdr_t*)data; + end = data + len; + + /* parse header */ + data += sizeof(m3dhdr_t); + M3D_LOG(data); + model->name = (char*)data; + for(; data < end && *data; data++) {}; data++; + model->license = (char*)data; + for(; data < end && *data; data++) {}; data++; + model->author = (char*)data; + for(; data < end && *data; data++) {}; data++; + model->desc = (char*)data; + chunk = (unsigned char*)model->raw + model->raw->length; + model->scale = (M3D_FLOAT)model->raw->scale; + if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0; + model->vc_s = 1 << ((model->raw->types >> 0) & 3); /* vertex coordinate size */ + model->vi_s = 1 << ((model->raw->types >> 2) & 3); /* vertex index size */ + model->si_s = 1 << ((model->raw->types >> 4) & 3); /* string offset size */ + model->ci_s = 1 << ((model->raw->types >> 6) & 3); /* color index size */ + model->ti_s = 1 << ((model->raw->types >> 8) & 3); /* tmap index size */ + model->bi_s = 1 << ((model->raw->types >>10) & 3); /* bone index size */ + model->nb_s = 1 << ((model->raw->types >>12) & 3); /* number of bones per vertex */ + model->sk_s = 1 << ((model->raw->types >>14) & 3); /* skin index size */ + model->fc_s = 1 << ((model->raw->types >>16) & 3); /* frame counter size */ + model->hi_s = 1 << ((model->raw->types >>18) & 3); /* shape index size */ + model->fi_s = 1 << ((model->raw->types >>20) & 3); /* face index size */ + model->vd_s = 1 << ((model->raw->types >>22) & 3); /* voxel dimension size */ + model->vp_s = 1 << ((model->raw->types >>24) & 3); /* voxel pixel size */ + if(model->ci_s == 8) model->ci_s = 0; /* optional indices */ + if(model->ti_s == 8) model->ti_s = 0; + if(model->bi_s == 8) model->bi_s = 0; + if(model->sk_s == 8) model->sk_s = 0; + if(model->fc_s == 8) model->fc_s = 0; + if(model->hi_s == 8) model->hi_s = 0; + if(model->fi_s == 8) model->fi_s = 0; + + /* variable limit checks */ + if(sizeof(M3D_FLOAT) == 4 && model->vc_s > 4) { + M3D_LOG("Double precision coordinates not supported, truncating to float..."); + model->errcode = M3D_ERR_TRUNC; + } + if((sizeof(M3D_INDEX) == 2 && (model->vi_s > 2 || model->si_s > 2 || model->ci_s > 2 || model->ti_s > 2 || + model->bi_s > 2 || model->sk_s > 2 || model->fc_s > 2 || model->hi_s > 2 || model->fi_s > 2)) || + (sizeof(M3D_VOXEL) == 2 && model->vp_s > 2)) { + M3D_LOG("32 bit indices not supported, unable to load model"); + M3D_FREE(model); + return NULL; + } + if(model->vi_s > 4 || model->si_s > 4) { + M3D_LOG("Invalid index size, unable to load model"); + M3D_FREE(model); + return NULL; + } + if(!M3D_CHUNKMAGIC(end - 4, 'O','M','D','3')) { + M3D_LOG("Missing end chunk"); + M3D_FREE(model); + return NULL; + } + if(model->nb_s > M3D_NUMBONE) { + M3D_LOG("Model has more bones per vertex than what importer was configured to support"); + model->errcode = M3D_ERR_TRUNC; + } + + /* look for inlined assets in advance, material and procedural chunks may need them */ + buff = chunk; + while(buff < end && !M3D_CHUNKMAGIC(buff, 'O','M','D','3')) { + data = buff; + len = ((m3dchunk_t*)data)->length; + buff += len; + if(len < sizeof(m3dchunk_t) || buff >= end) { + M3D_LOG("Invalid chunk size"); + break; + } + len -= sizeof(m3dchunk_t) + model->si_s; + + /* inlined assets */ + if(M3D_CHUNKMAGIC(data, 'A','S','E','T') && len > 0) { + M3D_LOG("Inlined asset"); + i = model->numinlined++; + model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t)); + if(!model->inlined) { +memerr: M3D_LOG("Out of memory"); + model->errcode = M3D_ERR_ALLOC; + return model; + } + data += sizeof(m3dchunk_t); + t = &model->inlined[i]; + M3D_GETSTR(t->name); + M3D_LOG(t->name); + t->data = (uint8_t*)data; + t->length = len; + } + } + + /* parse chunks */ + while(chunk < end && !M3D_CHUNKMAGIC(chunk, 'O','M','D','3')) { + data = chunk; + len = ((m3dchunk_t*)chunk)->length; + chunk += len; + if(len < sizeof(m3dchunk_t) || chunk >= end) { + M3D_LOG("Invalid chunk size"); + break; + } + len -= sizeof(m3dchunk_t); + + /* color map */ + if(M3D_CHUNKMAGIC(data, 'C','M','A','P')) { + M3D_LOG("Color map"); + if(model->cmap) { M3D_LOG("More color map chunks, should be unique"); model->errcode = M3D_ERR_CMAP; continue; } + if(!model->ci_s) { M3D_LOG("Color map chunk, shouldn't be any"); model->errcode = M3D_ERR_CMAP; continue; } + model->numcmap = len / sizeof(uint32_t); + model->cmap = (uint32_t*)(data + sizeof(m3dchunk_t)); + } else + /* texture map */ + if(M3D_CHUNKMAGIC(data, 'T','M','A','P')) { + M3D_LOG("Texture map"); + if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); model->errcode = M3D_ERR_TMAP; continue; } + if(!model->ti_s) { M3D_LOG("Texture map chunk, shouldn't be any"); model->errcode = M3D_ERR_TMAP; continue; } + reclen = model->vc_s + model->vc_s; + model->numtmap = len / reclen; + model->tmap = (m3dti_t*)M3D_MALLOC(model->numtmap * sizeof(m3dti_t)); + if(!model->tmap) goto memerr; + for(i = 0, data += sizeof(m3dchunk_t); data < chunk; i++) { + switch(model->vc_s) { + case 1: + model->tmap[i].u = (M3D_FLOAT)(data[0]) / (M3D_FLOAT)255.0; + model->tmap[i].v = (M3D_FLOAT)(data[1]) / (M3D_FLOAT)255.0; + break; + case 2: + model->tmap[i].u = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)65535.0; + model->tmap[i].v = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)65535.0; + break; + case 4: + model->tmap[i].u = (M3D_FLOAT)(*((float*)(data+0))); + model->tmap[i].v = (M3D_FLOAT)(*((float*)(data+4))); + break; + case 8: + model->tmap[i].u = (M3D_FLOAT)(*((double*)(data+0))); + model->tmap[i].v = (M3D_FLOAT)(*((double*)(data+8))); + break; + } + data += reclen; + } + } else + /* vertex list */ + if(M3D_CHUNKMAGIC(data, 'V','R','T','S')) { + M3D_LOG("Vertex list"); + if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); model->errcode = M3D_ERR_VRTS; continue; } + if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP; + reclen = model->ci_s + model->sk_s + 4 * model->vc_s; + model->numvertex = len / reclen; + model->vertex = (m3dv_t*)M3D_MALLOC(model->numvertex * sizeof(m3dv_t)); + if(!model->vertex) goto memerr; + memset(model->vertex, 0, model->numvertex * sizeof(m3dv_t)); + for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < model->numvertex; i++) { + switch(model->vc_s) { + case 1: + model->vertex[i].x = (M3D_FLOAT)((int8_t)data[0]) / (M3D_FLOAT)127.0; + model->vertex[i].y = (M3D_FLOAT)((int8_t)data[1]) / (M3D_FLOAT)127.0; + model->vertex[i].z = (M3D_FLOAT)((int8_t)data[2]) / (M3D_FLOAT)127.0; + model->vertex[i].w = (M3D_FLOAT)((int8_t)data[3]) / (M3D_FLOAT)127.0; + data += 4; + break; + case 2: + model->vertex[i].x = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)32767.0; + model->vertex[i].y = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)32767.0; + model->vertex[i].z = (M3D_FLOAT)(*((int16_t*)(data+4))) / (M3D_FLOAT)32767.0; + model->vertex[i].w = (M3D_FLOAT)(*((int16_t*)(data+6))) / (M3D_FLOAT)32767.0; + data += 8; + break; + case 4: + model->vertex[i].x = (M3D_FLOAT)(*((float*)(data+0))); + model->vertex[i].y = (M3D_FLOAT)(*((float*)(data+4))); + model->vertex[i].z = (M3D_FLOAT)(*((float*)(data+8))); + model->vertex[i].w = (M3D_FLOAT)(*((float*)(data+12))); + data += 16; + break; + case 8: + model->vertex[i].x = (M3D_FLOAT)(*((double*)(data+0))); + model->vertex[i].y = (M3D_FLOAT)(*((double*)(data+8))); + model->vertex[i].z = (M3D_FLOAT)(*((double*)(data+16))); + model->vertex[i].w = (M3D_FLOAT)(*((double*)(data+24))); + data += 32; + break; + } + switch(model->ci_s) { + case 1: model->vertex[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break; + case 2: model->vertex[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break; + case 4: model->vertex[i].color = *((uint32_t*)data); data += 4; break; + /* case 8: break; */ + } + model->vertex[i].skinid = M3D_UNDEF; + data = _m3d_getidx(data, model->sk_s, &model->vertex[i].skinid); + } + } else + /* skeleton: bone hierarchy and skin */ + if(M3D_CHUNKMAGIC(data, 'B','O','N','E')) { + M3D_LOG("Skeleton"); + if(model->bone) { M3D_LOG("More bone chunks, should be unique"); model->errcode = M3D_ERR_BONE; continue; } + if(!model->bi_s) { M3D_LOG("Bone chunk, shouldn't be any"); model->errcode=M3D_ERR_BONE; continue; } + if(!model->vertex) { M3D_LOG("No vertex chunk before bones"); model->errcode = M3D_ERR_VRTS; break; } + data += sizeof(m3dchunk_t); + model->numbone = 0; + data = _m3d_getidx(data, model->bi_s, &model->numbone); + if(model->numbone) { + model->bone = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t)); + if(!model->bone) goto memerr; + } + model->numskin = 0; + data = _m3d_getidx(data, model->sk_s, &model->numskin); + /* read bone hierarchy */ + for(i = 0; data < chunk && i < model->numbone; i++) { + data = _m3d_getidx(data, model->bi_s, &model->bone[i].parent); + M3D_GETSTR(model->bone[i].name); + data = _m3d_getidx(data, model->vi_s, &model->bone[i].pos); + data = _m3d_getidx(data, model->vi_s, &model->bone[i].ori); + model->bone[i].numweight = 0; + model->bone[i].weight = NULL; + } + /* read skin definitions */ + if(model->numskin) { + model->skin = (m3ds_t*)M3D_MALLOC(model->numskin * sizeof(m3ds_t)); + if(!model->skin) goto memerr; + for(i = 0; data < chunk && i < model->numskin; i++) { + for(j = 0; j < M3D_NUMBONE; j++) { + model->skin[i].boneid[j] = M3D_UNDEF; + model->skin[i].weight[j] = (M3D_FLOAT)0.0; + } + memset(&weights, 0, sizeof(weights)); + if(model->nb_s == 1) weights[0] = 255; + else { + memcpy(&weights, data, model->nb_s); + data += model->nb_s; + } + for(j = 0, w = (M3D_FLOAT)0.0; j < (unsigned int)model->nb_s; j++) { + if(weights[j]) { + if(j >= M3D_NUMBONE) + data += model->bi_s; + else { + model->skin[i].weight[j] = (M3D_FLOAT)(weights[j]) / (M3D_FLOAT)255.0; + w += model->skin[i].weight[j]; + data = _m3d_getidx(data, model->bi_s, &model->skin[i].boneid[j]); + } + } + } + /* this can occur if model has more bones than what the importer is configured to handle */ + if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) { + for(j = 0; j < M3D_NUMBONE; j++) + model->skin[i].weight[j] /= w; + } + } + } + } else + /* material */ + if(M3D_CHUNKMAGIC(data, 'M','T','R','L')) { + data += sizeof(m3dchunk_t); + M3D_GETSTR(name); + M3D_LOG("Material"); + M3D_LOG(name); + if(model->ci_s < 4 && !model->numcmap) model->errcode = M3D_ERR_CMAP; + for(i = 0; i < model->nummaterial; i++) + if(!strcmp(name, model->material[i].name)) { + model->errcode = M3D_ERR_MTRL; + M3D_LOG("Multiple definitions for material"); + M3D_LOG(name); + name = NULL; + break; + } + if(name) { + i = model->nummaterial++; + if(model->flags & M3D_FLG_MTLLIB) { + m = model->material; + model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t)); + if(!model->material) goto memerr; + memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t)); + if(model->texture) { + tx = model->texture; + model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t)); + if(!model->texture) goto memerr; + memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t)); + } + model->flags &= ~M3D_FLG_MTLLIB; + } else { + model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t)); + if(!model->material) goto memerr; + } + m = &model->material[i]; + m->numprop = 0; + m->name = name; + m->prop = (m3dp_t*)M3D_MALLOC((len / 2) * sizeof(m3dp_t)); + if(!m->prop) goto memerr; + while(data < chunk) { + i = m->numprop++; + m->prop[i].type = *data++; + m->prop[i].value.num = 0; + if(m->prop[i].type >= 128) + k = m3dpf_map; + else { + for(k = 256, j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++) + if(m->prop[i].type == m3d_propertytypes[j].id) { k = m3d_propertytypes[j].format; break; } + } + switch(k) { + case m3dpf_color: + switch(model->ci_s) { + case 1: m->prop[i].value.color = model->cmap ? model->cmap[data[0]] : 0; data++; break; + case 2: m->prop[i].value.color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break; + case 4: m->prop[i].value.color = *((uint32_t*)data); data += 4; break; + } + break; + + case m3dpf_uint8: m->prop[i].value.num = *data++; break; + case m3dpf_uint16:m->prop[i].value.num = *((uint16_t*)data); data += 2; break; + case m3dpf_uint32:m->prop[i].value.num = *((uint32_t*)data); data += 4; break; + case m3dpf_float: m->prop[i].value.fnum = *((float*)data); data += 4; break; + + case m3dpf_map: + M3D_GETSTR(name); + m->prop[i].value.textureid = _m3d_gettx(model, readfilecb, freecb, name); + if(model->errcode == M3D_ERR_ALLOC) goto memerr; + /* this error code only returned if readfilecb was specified */ + if(m->prop[i].value.textureid == M3D_UNDEF) { + M3D_LOG("Texture not found"); + M3D_LOG(m->name); + m->numprop--; + } + break; + + default: + M3D_LOG("Unknown material property in"); + M3D_LOG(m->name); + model->errcode = M3D_ERR_UNKPROP; + data = chunk; + break; + } + } + m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t)); + if(!m->prop) goto memerr; + } + } else + /* face */ + if(M3D_CHUNKMAGIC(data, 'P','R','O','C')) { + /* procedural surface */ + M3D_GETSTR(name); + M3D_LOG("Procedural surface"); + M3D_LOG(name); + _m3d_getpr(model, readfilecb, freecb, name); + } else + if(M3D_CHUNKMAGIC(data, 'M','E','S','H')) { + M3D_LOG("Mesh data"); + if(!model->vertex) { M3D_LOG("No vertex chunk before mesh"); model->errcode = M3D_ERR_VRTS; } + /* mesh */ + data += sizeof(m3dchunk_t); + mi = M3D_UNDEF; +#ifdef M3D_VERTEXMAX + pi = M3D_UNDEF; +#endif + am = model->numface; + while(data < chunk) { + k = *data++; + n = k >> 4; + k &= 15; + if(!n) { + if(!k) { + /* use material */ + mi = M3D_UNDEF; + M3D_GETSTR(name); + if(name) { + for(j = 0; j < model->nummaterial; j++) + if(!strcmp(name, model->material[j].name)) { + mi = (M3D_INDEX)j; + break; + } + if(mi == M3D_UNDEF) model->errcode = M3D_ERR_MTRL; + } + } else { + /* use parameter */ + M3D_GETSTR(name); +#ifdef M3D_VERTEXMAX + pi = M3D_UNDEF; + if(name) { + for(j = 0; j < model->numparam; j++) + if(!strcmp(name, model->param[j].name)) { + pi = (M3D_INDEX)j; + break; + } + if(pi == M3D_UNDEF) { + pi = model->numparam++; + model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t)); + if(!model->param) goto memerr; + model->param[pi].name = name; + model->param[pi].count = 0; + } + } +#endif + } + continue; + } + if(n != 3) { M3D_LOG("Only triangle mesh supported for now"); model->errcode = M3D_ERR_UNKMESH; return model; } + i = model->numface++; + if(model->numface > am) { + am = model->numface + 4095; + model->face = (m3df_t*)M3D_REALLOC(model->face, am * sizeof(m3df_t)); + if(!model->face) goto memerr; + } + memset(&model->face[i], 255, sizeof(m3df_t)); /* set all index to -1 by default */ + model->face[i].materialid = mi; +#ifdef M3D_VERTEXMAX + model->face[i].paramid = pi; +#endif + for(j = 0; data < chunk && j < n; j++) { + /* vertex */ + data = _m3d_getidx(data, model->vi_s, &model->face[i].vertex[j]); + /* texcoord */ + if(k & 1) + data = _m3d_getidx(data, model->ti_s, &model->face[i].texcoord[j]); + /* normal */ + if(k & 2) + data = _m3d_getidx(data, model->vi_s, &model->face[i].normal[j]); +#ifndef M3D_NONORMALS + if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1; +#endif + /* maximum */ + if(k & 4) +#ifdef M3D_VERTEXMAX + data = _m3d_getidx(data, model->vi_s, &model->face[i].vertmax[j]); +#else + data += model->vi_s; +#endif + } + if(j != n) { M3D_LOG("Invalid mesh"); model->numface = 0; model->errcode = M3D_ERR_UNKMESH; return model; } + } + model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t)); + } else + if(M3D_CHUNKMAGIC(data, 'V','O','X','T')) { + /* voxel types */ + M3D_LOG("Voxel types list"); + if(model->voxtype) { M3D_LOG("More voxel type chunks, should be unique"); model->errcode = M3D_ERR_VOXT; continue; } + if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP; + reclen = model->ci_s + model->si_s + 3 + model->sk_s; + k = len / reclen; + model->voxtype = (m3dvt_t*)M3D_MALLOC(k * sizeof(m3dvt_t)); + if(!model->voxtype) goto memerr; + memset(model->voxtype, 0, k * sizeof(m3dvt_t)); + model->numvoxtype = 0; + for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < k; i++) { + switch(model->ci_s) { + case 1: model->voxtype[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break; + case 2: model->voxtype[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break; + case 4: model->voxtype[i].color = *((uint32_t*)data); data += 4; break; + /* case 8: break; */ + } + M3D_GETSTR(name); + model->voxtype[i].materialid = M3D_UNDEF; + if(name) { + model->voxtype[i].name = name; +/* + for(j = 0; j < model->nummaterial; j++) + if(!strcmp(name, model->material[j].name)) { + model->voxtype[i].materialid = (M3D_INDEX)j; + break; + } +*/ + } + j = *data++; + model->voxtype[i].rotation = j & 0xBF; + model->voxtype[i].voxshape = ((j & 0x40) << 2) | *data++; + model->voxtype[i].numitem = *data++; + model->voxtype[i].skinid = M3D_UNDEF; + data = _m3d_getidx(data, model->sk_s, &model->voxtype[i].skinid); + if(model->voxtype[i].numitem) { + model->voxtype[i].item = (m3dvi_t*)M3D_MALLOC(model->voxtype[i].numitem * sizeof(m3dvi_t)); + if(!model->voxtype[i].item) goto memerr; + memset(model->voxtype[i].item, 0, model->voxtype[i].numitem * sizeof(m3dvi_t)); + for(j = 0; j < model->voxtype[i].numitem; j++) { + model->voxtype[i].item[j].count = *data++; + model->voxtype[i].item[j].count |= (*data++) << 8; + M3D_GETSTR(model->voxtype[i].item[j].name); + } + } + } + model->numvoxtype = i; + if(k != model->numvoxtype) { + model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t)); + if(!model->voxtype) goto memerr; + } + } else + if(M3D_CHUNKMAGIC(data, 'V','O','X','D')) { + /* voxel data */ + data += sizeof(m3dchunk_t); + M3D_GETSTR(name); + M3D_LOG("Voxel Data Layer"); + M3D_LOG(name); + if(model->vd_s > 4 || model->vp_s > 2) { M3D_LOG("No voxel index size"); model->errcode = M3D_ERR_UNKVOX; continue; } + if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); model->errcode = M3D_ERR_VOXT; } + i = model->numvoxel++; + model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t)); + if(!model->voxel) goto memerr; + memset(&model->voxel[i], 0, sizeof(m3dvx_t)); + model->voxel[i].name = name; + switch(model->vd_s) { + case 1: + model->voxel[i].x = (int32_t)((int8_t)data[0]); + model->voxel[i].y = (int32_t)((int8_t)data[1]); + model->voxel[i].z = (int32_t)((int8_t)data[2]); + model->voxel[i].w = (uint32_t)(data[3]); + model->voxel[i].h = (uint32_t)(data[4]); + model->voxel[i].d = (uint32_t)(data[5]); + data += 6; + break; + case 2: + model->voxel[i].x = (int32_t)(*((int16_t*)(data+0))); + model->voxel[i].y = (int32_t)(*((int16_t*)(data+2))); + model->voxel[i].z = (int32_t)(*((int16_t*)(data+4))); + model->voxel[i].w = (uint32_t)(*((uint16_t*)(data+6))); + model->voxel[i].h = (uint32_t)(*((uint16_t*)(data+8))); + model->voxel[i].d = (uint32_t)(*((uint16_t*)(data+10))); + data += 12; + break; + case 4: + model->voxel[i].x = *((int32_t*)(data+0)); + model->voxel[i].y = *((int32_t*)(data+4)); + model->voxel[i].z = *((int32_t*)(data+8)); + model->voxel[i].w = *((uint32_t*)(data+12)); + model->voxel[i].h = *((uint32_t*)(data+16)); + model->voxel[i].d = *((uint32_t*)(data+20)); + data += 24; + break; + } + model->voxel[i].uncertain = *data++; + model->voxel[i].groupid = *data++; + k = model->voxel[i].w * model->voxel[i].h * model->voxel[i].d; + model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC(k * sizeof(M3D_VOXEL)); + if(!model->voxel[i].data) goto memerr; + memset(model->voxel[i].data, 0xff, k * sizeof(M3D_VOXEL)); + for(j = 0; data < chunk && j < k;) { + l = ((*data++) & 0x7F) + 1; + if(data[-1] & 0x80) { + data = _m3d_getidx(data, model->vp_s, &mi); + while(l-- && j < k) model->voxel[i].data[j++] = (M3D_VOXEL)mi; + } else + while(l-- && j < k) { + data = _m3d_getidx(data, model->vp_s, &mi); + model->voxel[i].data[j++] = (M3D_VOXEL)mi; + } + } + } else + if(M3D_CHUNKMAGIC(data, 'S','H','P','E')) { + /* mathematical shape */ + data += sizeof(m3dchunk_t); + M3D_GETSTR(name); + M3D_LOG("Mathematical Shape"); + M3D_LOG(name); + i = model->numshape++; + model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3dh_t)); + if(!model->shape) goto memerr; + h = &model->shape[i]; + h->numcmd = 0; + h->cmd = NULL; + h->name = name; + h->group = M3D_UNDEF; + data = _m3d_getidx(data, model->bi_s, &h->group); + if(h->group != M3D_UNDEF && h->group >= model->numbone) { + M3D_LOG("Unknown bone id as shape group in shape"); + M3D_LOG(name); + h->group = M3D_UNDEF; + model->errcode = M3D_ERR_SHPE; + } + while(data < chunk) { + i = h->numcmd++; + h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t)); + if(!h->cmd) goto memerr; + h->cmd[i].type = *data++; + if(h->cmd[i].type & 0x80) { + h->cmd[i].type &= 0x7F; + h->cmd[i].type |= (*data++ << 7); + } + if(h->cmd[i].type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0]))) { + M3D_LOG("Unknown shape command in"); + M3D_LOG(h->name); + model->errcode = M3D_ERR_UNKCMD; + break; + } + cd = &m3d_commandtypes[h->cmd[i].type]; + h->cmd[i].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t)); + if(!h->cmd[i].arg) goto memerr; + memset(h->cmd[i].arg, 0, cd->p * sizeof(uint32_t)); + for(k = n = 0, l = cd->p; k < l; k++) + switch(cd->a[((k - n) % (cd->p - n)) + n]) { + case m3dcp_mi_t: + h->cmd[i].arg[k] = M3D_NOTDEFINED; + M3D_GETSTR(name); + if(name) { + for(n = 0; n < model->nummaterial; n++) + if(!strcmp(name, model->material[n].name)) { + h->cmd[i].arg[k] = n; + break; + } + if(h->cmd[i].arg[k] == M3D_NOTDEFINED) model->errcode = M3D_ERR_MTRL; + } + break; + case m3dcp_vc_t: + f = 0.0f; + switch(model->vc_s) { + case 1: f = (float)((int8_t)data[0]) / 127; break; + case 2: f = (float)(*((int16_t*)(data+0))) / 32767; break; + case 4: f = (float)(*((float*)(data+0))); break; + case 8: f = (float)(*((double*)(data+0))); break; + } + memcpy(&h->cmd[i].arg[k], &f, 4); + data += model->vc_s; + break; + case m3dcp_hi_t: data = _m3d_getidx(data, model->hi_s, &h->cmd[i].arg[k]); break; + case m3dcp_fi_t: data = _m3d_getidx(data, model->fi_s, &h->cmd[i].arg[k]); break; + case m3dcp_ti_t: data = _m3d_getidx(data, model->ti_s, &h->cmd[i].arg[k]); break; + case m3dcp_qi_t: + case m3dcp_vi_t: data = _m3d_getidx(data, model->vi_s, &h->cmd[i].arg[k]); break; + case m3dcp_i1_t: data = _m3d_getidx(data, 1, &h->cmd[i].arg[k]); break; + case m3dcp_i2_t: data = _m3d_getidx(data, 2, &h->cmd[i].arg[k]); break; + case m3dcp_i4_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]); break; + case m3dcp_va_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]); + n = k + 1; l += (h->cmd[i].arg[k] - 1) * (cd->p - k - 1); + h->cmd[i].arg = (uint32_t*)M3D_REALLOC(h->cmd[i].arg, l * sizeof(uint32_t)); + if(!h->cmd[i].arg) goto memerr; + memset(&h->cmd[i].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t)); + break; + } + } + } else + /* annotation label list */ + if(M3D_CHUNKMAGIC(data, 'L','B','L','S')) { + data += sizeof(m3dchunk_t); + M3D_GETSTR(name); + M3D_GETSTR(lang); + M3D_LOG("Label list"); + if(name) { M3D_LOG(name); } + if(lang) { M3D_LOG(lang); } + if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP; + k = 0; + switch(model->ci_s) { + case 1: k = model->cmap ? model->cmap[data[0]] : 0; data++; break; + case 2: k = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break; + case 4: k = *((uint32_t*)data); data += 4; break; + /* case 8: break; */ + } + reclen = model->vi_s + model->si_s; + i = model->numlabel; model->numlabel += len / reclen; + model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t)); + if(!model->label) goto memerr; + memset(&model->label[i], 0, (model->numlabel - i) * sizeof(m3dl_t)); + for(; data < chunk && i < model->numlabel; i++) { + model->label[i].name = name; + model->label[i].lang = lang; + model->label[i].color = k; + data = _m3d_getidx(data, model->vi_s, &model->label[i].vertexid); + M3D_GETSTR(model->label[i].text); + } + } else + /* action */ + if(M3D_CHUNKMAGIC(data, 'A','C','T','N')) { + M3D_LOG("Action"); + i = model->numaction++; + model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t)); + if(!model->action) goto memerr; + a = &model->action[i]; + data += sizeof(m3dchunk_t); + M3D_GETSTR(a->name); + M3D_LOG(a->name); + a->numframe = *((uint16_t*)data); data += 2; + if(a->numframe < 1) { + model->numaction--; + } else { + a->durationmsec = *((uint32_t*)data); data += 4; + a->frame = (m3dfr_t*)M3D_MALLOC(a->numframe * sizeof(m3dfr_t)); + if(!a->frame) goto memerr; + for(i = 0; data < chunk && i < a->numframe; i++) { + a->frame[i].msec = *((uint32_t*)data); data += 4; + a->frame[i].numtransform = 0; a->frame[i].transform = NULL; + data = _m3d_getidx(data, model->fc_s, &a->frame[i].numtransform); + if(a->frame[i].numtransform > 0) { + a->frame[i].transform = (m3dtr_t*)M3D_MALLOC(a->frame[i].numtransform * sizeof(m3dtr_t)); + for(j = 0; j < a->frame[i].numtransform; j++) { + data = _m3d_getidx(data, model->bi_s, &a->frame[i].transform[j].boneid); + data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].pos); + data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].ori); + } + } + } + } + } else { + i = model->numextra++; + model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*)); + if(!model->extra) goto memerr; + model->extra[i] = (m3dchunk_t*)data; + } + } + /* calculate normals, normalize skin weights, create bone/vertex cross-references and calculate transform matrices */ +#ifdef M3D_ASCII +postprocess: +#endif + if(model) { + M3D_LOG("Post-process"); +#ifdef M3D_PROFILING + gettimeofday(&tv1, NULL); + tvd.tv_sec = tv1.tv_sec - tv0.tv_sec; + tvd.tv_usec = tv1.tv_usec - tv0.tv_usec; + if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; } + printf(" Parsing chunks %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec); +#endif +#ifndef M3D_NOVOXELS + if(model->numvoxel && model->voxel) { + M3D_LOG("Converting voxels into vertices and mesh"); + /* add normals */ + enorm = model->numvertex; model->numvertex += 6; + model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); + if(!model->vertex) goto memerr; + memset(&model->vertex[enorm], 0, 6 * sizeof(m3dv_t)); + for(l = 0; l < 6; l++) + model->vertex[enorm+l].skinid = M3D_UNDEF; + model->vertex[enorm+0].y = (M3D_FLOAT)-1.0; + model->vertex[enorm+1].z = (M3D_FLOAT)-1.0; + model->vertex[enorm+2].x = (M3D_FLOAT)-1.0; + model->vertex[enorm+3].y = (M3D_FLOAT)1.0; + model->vertex[enorm+4].z = (M3D_FLOAT)1.0; + model->vertex[enorm+5].x = (M3D_FLOAT)1.0; + /* this is a fast, not so memory efficient version, only basic face culling used */ + min_x = min_y = min_z = 2147483647L; + max_x = max_y = max_z = -2147483647L; + for(i = 0; i < model->numvoxel; i++) { + if(model->voxel[i].x + (int32_t)model->voxel[i].w > max_x) max_x = model->voxel[i].x + (int32_t)model->voxel[i].w; + if(model->voxel[i].x < min_x) min_x = model->voxel[i].x; + if(model->voxel[i].y + (int32_t)model->voxel[i].h > max_y) max_y = model->voxel[i].y + (int32_t)model->voxel[i].h; + if(model->voxel[i].y < min_y) min_y = model->voxel[i].y; + if(model->voxel[i].z + (int32_t)model->voxel[i].d > max_z) max_z = model->voxel[i].z + (int32_t)model->voxel[i].d; + if(model->voxel[i].z < min_z) min_z = model->voxel[i].z; + } + i = (-min_x > max_x ? -min_x : max_x); + j = (-min_y > max_y ? -min_y : max_y); + k = (-min_z > max_z ? -min_z : max_z); + if(j > i) i = j; + if(k > i) i = k; + if(i <= 1) i = 1; + w = (M3D_FLOAT)1.0 / (M3D_FLOAT)i; + if(i >= 254) model->vc_s = 2; + if(i >= 65534) model->vc_s = 4; + for(i = 0; i < model->numvoxel; i++) { + sx = model->voxel[i].w; sz = model->voxel[i].d; sy = model->voxel[i].h; + for(y = 0, j = 0; y < sy; y++) + for(z = 0; z < sz; z++) + for(x = 0; x < sx; x++, j++) + if(model->voxel[i].data[j] < model->numvoxtype) { + k = 0; + /* 16__32 ____ + * /| /| /|2 /| + *64_128 | /_8_/ 32 + * | 1_|_2 |4|_|_| + * |/ |/ |/ 1|/ + * 4___8 |16_| */ + k = n = am = 0; + if(!y || model->voxel[i].data[j - sx*sz] >= model->numvoxtype) { n++; am |= 1; k |= 1|2|4|8; } + if(!z || model->voxel[i].data[j - sx] >= model->numvoxtype) { n++; am |= 2; k |= 1|2|16|32; } + if(!x || model->voxel[i].data[j - 1] >= model->numvoxtype) { n++; am |= 4; k |= 1|4|16|64; } + if(y == sy-1 || model->voxel[i].data[j + sx*sz] >= model->numvoxtype) { n++; am |= 8; k |= 16|32|64|128; } + if(z == sz-1 || model->voxel[i].data[j + sx] >= model->numvoxtype) { n++; am |= 16; k |= 4|8|64|128; } + if(x == sx-1 || model->voxel[i].data[j + 1] >= model->numvoxtype) { n++; am |= 32; k |= 2|8|32|128; } + if(k) { + memset(edge, 255, sizeof(edge)); + for(l = 0, len = 1, reclen = model->numvertex; l < 8; l++, len <<= 1) + if(k & len) edge[l] = model->numvertex++; + model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); + if(!model->vertex) goto memerr; + memset(&model->vertex[reclen], 0, (model->numvertex-reclen) * sizeof(m3dv_t)); + for(l = reclen; l < model->numvertex; l++) { + model->vertex[l].skinid = model->voxtype[model->voxel[i].data[j]].skinid; + model->vertex[l].color = model->voxtype[model->voxel[i].data[j]].color; + } + l = reclen; + if(k & 1) { + model->vertex[l].x = (model->voxel[i].x + x) * w; + model->vertex[l].y = (model->voxel[i].y + y) * w; + model->vertex[l].z = (model->voxel[i].z + z) * w; + l++; + } + if(k & 2) { + model->vertex[l].x = (model->voxel[i].x + x + 1) * w; + model->vertex[l].y = (model->voxel[i].y + y) * w; + model->vertex[l].z = (model->voxel[i].z + z) * w; + l++; + } + if(k & 4) { + model->vertex[l].x = (model->voxel[i].x + x) * w; + model->vertex[l].y = (model->voxel[i].y + y) * w; + model->vertex[l].z = (model->voxel[i].z + z + 1) * w; + l++; + } + if(k & 8) { + model->vertex[l].x = (model->voxel[i].x + x + 1) * w; + model->vertex[l].y = (model->voxel[i].y + y) * w; + model->vertex[l].z = (model->voxel[i].z + z + 1) * w; + l++; + } + if(k & 16) { + model->vertex[l].x = (model->voxel[i].x + x) * w; + model->vertex[l].y = (model->voxel[i].y + y + 1) * w; + model->vertex[l].z = (model->voxel[i].z + z) * w; + l++; + } + if(k & 32) { + model->vertex[l].x = (model->voxel[i].x + x + 1) * w; + model->vertex[l].y = (model->voxel[i].y + y + 1) * w; + model->vertex[l].z = (model->voxel[i].z + z) * w; + l++; + } + if(k & 64) { + model->vertex[l].x = (model->voxel[i].x + x) * w; + model->vertex[l].y = (model->voxel[i].y + y + 1) * w; + model->vertex[l].z = (model->voxel[i].z + z + 1) * w; + l++; + } + if(k & 128) { + model->vertex[l].x = (model->voxel[i].x + x + 1) * w; + model->vertex[l].y = (model->voxel[i].y + y + 1) * w; + model->vertex[l].z = (model->voxel[i].z + z + 1) * w; + l++; + } + n <<= 1; + l = model->numface; model->numface += n; + model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t)); + if(!model->face) goto memerr; + memset(&model->face[l], 255, n * sizeof(m3df_t)); + for(reclen = l; reclen < model->numface; reclen++) + model->face[reclen].materialid = model->voxtype[model->voxel[i].data[j]].materialid; + if(am & 1) { /* bottom */ + model->face[l].vertex[0] = edge[0]; model->face[l].vertex[1] = edge[1]; model->face[l].vertex[2] = edge[2]; + model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[1]; model->face[l+1].vertex[2] = edge[3]; + model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = + model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm; + l += 2; + } + if(am & 2) { /* north */ + model->face[l].vertex[0] = edge[0]; model->face[l].vertex[1] = edge[4]; model->face[l].vertex[2] = edge[1]; + model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[4]; model->face[l+1].vertex[2] = edge[5]; + model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = + model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+1; + l += 2; + } + if(am & 4) { /* west */ + model->face[l].vertex[0] = edge[0]; model->face[l].vertex[1] = edge[2]; model->face[l].vertex[2] = edge[4]; + model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[4]; + model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = + model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+2; + l += 2; + } + if(am & 8) { /* top */ + model->face[l].vertex[0] = edge[4]; model->face[l].vertex[1] = edge[6]; model->face[l].vertex[2] = edge[5]; + model->face[l+1].vertex[0] = edge[5]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[7]; + model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = + model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+3; + l += 2; + } + if(am & 16) { /* south */ + model->face[l].vertex[0] = edge[2]; model->face[l].vertex[1] = edge[7]; model->face[l].vertex[2] = edge[6]; + model->face[l+1].vertex[0] = edge[7]; model->face[l+1].vertex[1] = edge[2]; model->face[l+1].vertex[2] = edge[3]; + model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = + model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+4; + l += 2; + } + if(am & 32) { /* east */ + model->face[l].vertex[0] = edge[1]; model->face[l].vertex[1] = edge[5]; model->face[l].vertex[2] = edge[7]; + model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[7]; model->face[l+1].vertex[2] = edge[3]; + model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = + model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+5; + l += 2; + } + } + } + } + } +#endif +#ifndef M3D_NONORMALS + if(model->numface && model->face && neednorm) { + /* if they are missing, calculate triangle normals into a temporary buffer */ + norm = (m3dv_t*)M3D_MALLOC(model->numface * sizeof(m3dv_t)); + if(!norm) goto memerr; + for(i = 0, n = model->numvertex; i < model->numface; i++) + if(model->face[i].normal[0] == M3D_UNDEF) { + v0 = &model->vertex[model->face[i].vertex[0]]; + v1 = &model->vertex[model->face[i].vertex[1]]; + v2 = &model->vertex[model->face[i].vertex[2]]; + va.x = v1->x - v0->x; va.y = v1->y - v0->y; va.z = v1->z - v0->z; + vb.x = v2->x - v0->x; vb.y = v2->y - v0->y; vb.z = v2->z - v0->z; + v0 = &norm[i]; + v0->x = (va.y * vb.z) - (va.z * vb.y); + v0->y = (va.z * vb.x) - (va.x * vb.z); + v0->z = (va.x * vb.y) - (va.y * vb.x); + w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z)); + v0->x *= w; v0->y *= w; v0->z *= w; + model->face[i].normal[0] = model->face[i].vertex[0] + n; + model->face[i].normal[1] = model->face[i].vertex[1] + n; + model->face[i].normal[2] = model->face[i].vertex[2] + n; + } + /* this is the fast way, we don't care if a normal is repeated in model->vertex */ + M3D_LOG("Generating normals"); + model->flags |= M3D_FLG_GENNORM; + model->numvertex <<= 1; + model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); + if(!model->vertex) goto memerr; + memset(&model->vertex[n], 0, n * sizeof(m3dv_t)); + for(i = 0; i < model->numface; i++) + for(j = 0; j < 3; j++) { + v0 = &model->vertex[model->face[i].vertex[j] + n]; + v0->x += norm[i].x; + v0->y += norm[i].y; + v0->z += norm[i].z; + } + /* for each vertex, take the average of the temporary normals and use that */ + for(i = 0, v0 = &model->vertex[n]; i < n; i++, v0++) { + w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z)); + v0->x *= w; v0->y *= w; v0->z *= w; + v0->skinid = M3D_UNDEF; + } + M3D_FREE(norm); + } +#endif + if(model->numbone && model->bone && model->numskin && model->skin && model->numvertex && model->vertex) { +#ifndef M3D_NOWEIGHTS + M3D_LOG("Generating weight cross-reference"); + for(i = 0; i < model->numvertex; i++) { + if(model->vertex[i].skinid < model->numskin) { + sk = &model->skin[model->vertex[i].skinid]; + w = (M3D_FLOAT)0.0; + for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) + w += sk->weight[j]; + for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) { + sk->weight[j] /= w; + b = &model->bone[sk->boneid[j]]; + k = b->numweight++; + b->weight = (m3dw_t*)M3D_REALLOC(b->weight, b->numweight * sizeof(m3da_t)); + if(!b->weight) goto memerr; + b->weight[k].vertexid = i; + b->weight[k].weight = sk->weight[j]; + } + } + } +#endif +#ifndef M3D_NOANIMATION + M3D_LOG("Calculating bone transformation matrices"); + for(i = 0; i < model->numbone; i++) { + b = &model->bone[i]; + if(model->bone[i].parent == M3D_UNDEF) { + _m3d_mat((M3D_FLOAT*)&b->mat4, &model->vertex[b->pos], &model->vertex[b->ori]); + } else { + _m3d_mat((M3D_FLOAT*)&r, &model->vertex[b->pos], &model->vertex[b->ori]); + _m3d_mul((M3D_FLOAT*)&b->mat4, (M3D_FLOAT*)&model->bone[b->parent].mat4, (M3D_FLOAT*)&r); + } + } + for(i = 0; i < model->numbone; i++) + _m3d_inv((M3D_FLOAT*)&model->bone[i].mat4); +#endif + } +#ifdef M3D_PROFILING + gettimeofday(&tv0, NULL); + tvd.tv_sec = tv0.tv_sec - tv1.tv_sec; + tvd.tv_usec = tv0.tv_usec - tv1.tv_usec; + if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; } + printf(" Post-process %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec); +#endif + } + return model; +} + +/** + * Calculates skeletons for animation frames, returns a working copy (should be freed after use) + */ +m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton) +{ + unsigned int i; + M3D_INDEX s = frameid; + m3dfr_t *fr; + + if(!model || !model->numbone || !model->bone || (actionid != M3D_UNDEF && (!model->action || + actionid >= model->numaction || frameid >= model->action[actionid].numframe))) { + model->errcode = M3D_ERR_UNKFRAME; + return skeleton; + } + model->errcode = M3D_SUCCESS; + if(!skeleton) { + skeleton = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t)); + if(!skeleton) { + model->errcode = M3D_ERR_ALLOC; + return NULL; + } + goto gen; + } + if(actionid == M3D_UNDEF || !frameid) { +gen: s = 0; + for(i = 0; i < model->numbone; i++) { + skeleton[i].boneid = i; + skeleton[i].pos = model->bone[i].pos; + skeleton[i].ori = model->bone[i].ori; + } + } + if(actionid < model->numaction && (frameid || !model->action[actionid].frame[0].msec)) { + for(; s <= frameid; s++) { + fr = &model->action[actionid].frame[s]; + for(i = 0; i < fr->numtransform; i++) { + skeleton[fr->transform[i].boneid].pos = fr->transform[i].pos; + skeleton[fr->transform[i].boneid].ori = fr->transform[i].ori; + } + } + } + return skeleton; +} + +#ifndef M3D_NOANIMATION +/** + * Returns interpolated animation-pose, a working copy (should be freed after use) + */ +m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec) +{ + unsigned int i, j, l; + M3D_FLOAT r[16], t, c, d, s; + m3db_t *ret; + m3dv_t *v, *p, *f; + m3dtr_t *tmp; + m3dfr_t *fr; + + if(!model || !model->numbone || !model->bone) { + model->errcode = M3D_ERR_UNKFRAME; + return NULL; + } + ret = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t)); + if(!ret) { + model->errcode = M3D_ERR_ALLOC; + return NULL; + } + memcpy(ret, model->bone, model->numbone * sizeof(m3db_t)); + for(i = 0; i < model->numbone; i++) + _m3d_inv((M3D_FLOAT*)&ret[i].mat4); + if(!model->action || actionid >= model->numaction) { + model->errcode = M3D_ERR_UNKFRAME; + return ret; + } + msec %= model->action[actionid].durationmsec; + model->errcode = M3D_SUCCESS; + fr = &model->action[actionid].frame[0]; + for(j = l = 0; j < model->action[actionid].numframe && model->action[actionid].frame[j].msec <= msec; j++) { + fr = &model->action[actionid].frame[j]; + l = fr->msec; + for(i = 0; i < fr->numtransform; i++) { + ret[fr->transform[i].boneid].pos = fr->transform[i].pos; + ret[fr->transform[i].boneid].ori = fr->transform[i].ori; + } + } + if(l != msec) { + model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t)); + if(!model->vertex) { + free(ret); + model->errcode = M3D_ERR_ALLOC; + return NULL; + } + tmp = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t)); + if(tmp) { + for(i = 0; i < model->numbone; i++) { + tmp[i].pos = ret[i].pos; + tmp[i].ori = ret[i].ori; + } + fr = &model->action[actionid].frame[j % model->action[actionid].numframe]; + t = l >= fr->msec ? (M3D_FLOAT)1.0 : (M3D_FLOAT)(msec - l) / (M3D_FLOAT)(fr->msec - l); + for(i = 0; i < fr->numtransform; i++) { + tmp[fr->transform[i].boneid].pos = fr->transform[i].pos; + tmp[fr->transform[i].boneid].ori = fr->transform[i].ori; + } + for(i = 0, j = model->numvertex; i < model->numbone; i++) { + /* interpolation of position */ + if(ret[i].pos != tmp[i].pos) { + p = &model->vertex[ret[i].pos]; + f = &model->vertex[tmp[i].pos]; + v = &model->vertex[j]; + v->x = p->x + t * (f->x - p->x); + v->y = p->y + t * (f->y - p->y); + v->z = p->z + t * (f->z - p->z); + ret[i].pos = j++; + } + /* interpolation of orientation */ + if(ret[i].ori != tmp[i].ori) { + p = &model->vertex[ret[i].ori]; + f = &model->vertex[tmp[i].ori]; + v = &model->vertex[j]; + d = p->w * f->w + p->x * f->x + p->y * f->y + p->z * f->z; + if(d < 0) { d = -d; s = (M3D_FLOAT)-1.0; } else s = (M3D_FLOAT)1.0; +#if 0 + /* don't use SLERP, requires two more variables, libm linkage and it is slow (but nice) */ + a = (M3D_FLOAT)1.0 - t; b = t; + if(d < (M3D_FLOAT)0.999999) { c = acosf(d); b = 1 / sinf(c); a = sinf(a * c) * b; b *= sinf(t * c) * s; } + v->x = p->x * a + f->x * b; + v->y = p->y * a + f->y * b; + v->z = p->z * a + f->z * b; + v->w = p->w * a + f->w * b; +#else + /* approximated NLERP, original approximation by Arseny Kapoulkine, heavily optimized by me */ + c = t - (M3D_FLOAT)0.5; t += t * c * (t - (M3D_FLOAT)1.0) * (((M3D_FLOAT)1.0904 + d * ((M3D_FLOAT)-3.2452 + + d * ((M3D_FLOAT)3.55645 - d * (M3D_FLOAT)1.43519))) * c * c + ((M3D_FLOAT)0.848013 + d * + ((M3D_FLOAT)-1.06021 + d * (M3D_FLOAT)0.215638))); + v->x = p->x + t * (s * f->x - p->x); + v->y = p->y + t * (s * f->y - p->y); + v->z = p->z + t * (s * f->z - p->z); + v->w = p->w + t * (s * f->w - p->w); + d = _m3d_rsq(v->w * v->w + v->x * v->x + v->y * v->y + v->z * v->z); + v->x *= d; v->y *= d; v->z *= d; v->w *= d; +#endif + ret[i].ori = j++; + } + } + M3D_FREE(tmp); + } + } + for(i = 0; i < model->numbone; i++) { + if(ret[i].parent == M3D_UNDEF) { + _m3d_mat((M3D_FLOAT*)&ret[i].mat4, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]); + } else { + _m3d_mat((M3D_FLOAT*)&r, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]); + _m3d_mul((M3D_FLOAT*)&ret[i].mat4, (M3D_FLOAT*)&ret[ret[i].parent].mat4, (M3D_FLOAT*)&r); + } + } + return ret; +} + +#endif /* M3D_NOANIMATION */ + +#endif /* M3D_IMPLEMENTATION */ + +#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER)) +/** + * Free the in-memory model + */ +void m3d_free(m3d_t *model) +{ + unsigned int i, j; + + if(!model) return; +#ifdef M3D_ASCII + /* if model imported from ASCII, we have to free all strings as well */ + if(model->flags & M3D_FLG_FREESTR) { + if(model->name) M3D_FREE(model->name); + if(model->license) M3D_FREE(model->license); + if(model->author) M3D_FREE(model->author); + if(model->desc) M3D_FREE(model->desc); + if(model->bone) + for(i = 0; i < model->numbone; i++) + if(model->bone[i].name) + M3D_FREE(model->bone[i].name); + if(model->shape) + for(i = 0; i < model->numshape; i++) + if(model->shape[i].name) + M3D_FREE(model->shape[i].name); + if(model->numvoxtype) + for(i = 0; i < model->numvoxtype; i++) { + if(model->voxtype[i].name) + M3D_FREE(model->voxtype[i].name); + for(j = 0; j < model->voxtype[i].numitem; j++) + if(model->voxtype[i].item[j].name) + M3D_FREE(model->voxtype[i].item[j].name); + } + if(model->numvoxel) + for(i = 0; i < model->numvoxel; i++) + if(model->voxel[i].name) + M3D_FREE(model->voxel[i].name); + if(model->material) + for(i = 0; i < model->nummaterial; i++) + if(model->material[i].name) + M3D_FREE(model->material[i].name); + if(model->action) + for(i = 0; i < model->numaction; i++) + if(model->action[i].name) + M3D_FREE(model->action[i].name); + if(model->texture) + for(i = 0; i < model->numtexture; i++) + if(model->texture[i].name) + M3D_FREE(model->texture[i].name); + if(model->inlined) + for(i = 0; i < model->numinlined; i++) { + if(model->inlined[i].name) + M3D_FREE(model->inlined[i].name); + if(model->inlined[i].data) + M3D_FREE(model->inlined[i].data); + } + if(model->extra) + for(i = 0; i < model->numextra; i++) + if(model->extra[i]) + M3D_FREE(model->extra[i]); + if(model->label) + for(i = 0; i < model->numlabel; i++) { + if(model->label[i].name) { + for(j = i + 1; j < model->numlabel; j++) + if(model->label[j].name == model->label[i].name) + model->label[j].name = NULL; + M3D_FREE(model->label[i].name); + } + if(model->label[i].lang) { + for(j = i + 1; j < model->numlabel; j++) + if(model->label[j].lang == model->label[i].lang) + model->label[j].lang = NULL; + M3D_FREE(model->label[i].lang); + } + if(model->label[i].text) + M3D_FREE(model->label[i].text); + } + if(model->preview.data) + M3D_FREE(model->preview.data); + } +#endif + if(model->flags & M3D_FLG_FREERAW) M3D_FREE(model->raw); + + if(model->tmap) M3D_FREE(model->tmap); + if(model->bone) { + for(i = 0; i < model->numbone; i++) + if(model->bone[i].weight) + M3D_FREE(model->bone[i].weight); + M3D_FREE(model->bone); + } + if(model->skin) M3D_FREE(model->skin); + if(model->vertex) M3D_FREE(model->vertex); + if(model->face) M3D_FREE(model->face); + if(model->voxtype) { + for(i = 0; i < model->numvoxtype; i++) + if(model->voxtype[i].item) + M3D_FREE(model->voxtype[i].item); + M3D_FREE(model->voxtype); + } + if(model->voxel) { + for(i = 0; i < model->numvoxel; i++) + if(model->voxel[i].data) + M3D_FREE(model->voxel[i].data); + M3D_FREE(model->voxel); + } + if(model->shape) { + for(i = 0; i < model->numshape; i++) { + if(model->shape[i].cmd) { + for(j = 0; j < model->shape[i].numcmd; j++) + if(model->shape[i].cmd[j].arg) M3D_FREE(model->shape[i].cmd[j].arg); + M3D_FREE(model->shape[i].cmd); + } + } + M3D_FREE(model->shape); + } + if(model->material && !(model->flags & M3D_FLG_MTLLIB)) { + for(i = 0; i < model->nummaterial; i++) + if(model->material[i].prop) M3D_FREE(model->material[i].prop); + M3D_FREE(model->material); + } + if(model->texture) { + for(i = 0; i < model->numtexture; i++) + if(model->texture[i].d) M3D_FREE(model->texture[i].d); + M3D_FREE(model->texture); + } + if(model->action) { + for(i = 0; i < model->numaction; i++) { + if(model->action[i].frame) { + for(j = 0; j < model->action[i].numframe; j++) + if(model->action[i].frame[j].transform) M3D_FREE(model->action[i].frame[j].transform); + M3D_FREE(model->action[i].frame); + } + } + M3D_FREE(model->action); + } + if(model->label) M3D_FREE(model->label); + if(model->inlined) M3D_FREE(model->inlined); + if(model->extra) M3D_FREE(model->extra); + free(model); +} +#endif + +#ifdef M3D_EXPORTER +typedef struct { + char *str; + uint32_t offs; +} m3dstr_t; + +typedef struct { + m3dti_t data; + M3D_INDEX oldidx; + M3D_INDEX newidx; +} m3dtisave_t; + +typedef struct { + m3dv_t data; + M3D_INDEX oldidx; + M3D_INDEX newidx; + unsigned char norm; +} m3dvsave_t; + +typedef struct { + m3ds_t data; + M3D_INDEX oldidx; + M3D_INDEX newidx; +} m3dssave_t; + +typedef struct { + m3df_t data; + int group; + uint8_t opacity; +} m3dfsave_t; + +/* create unique list of strings */ +static m3dstr_t *_m3d_addstr(m3dstr_t *str, uint32_t *numstr, char *s) +{ + uint32_t i; + if(!s || !*s) return str; + if(str) { + for(i = 0; i < *numstr; i++) + if(str[i].str == s || !strcmp(str[i].str, s)) return str; + } + str = (m3dstr_t*)M3D_REALLOC(str, ((*numstr) + 1) * sizeof(m3dstr_t)); + str[*numstr].str = s; + str[*numstr].offs = 0; + (*numstr)++; + return str; +} + +/* add strings to header */ +m3dhdr_t *_m3d_addhdr(m3dhdr_t *h, m3dstr_t *s) +{ + int i; + char *safe = _m3d_safestr(s->str, 0); + i = (int)strlen(safe); + h = (m3dhdr_t*)M3D_REALLOC(h, h->length + i+1); + if(!h) { M3D_FREE(safe); return NULL; } + memcpy((uint8_t*)h + h->length, safe, i+1); + s->offs = h->length - 16; + h->length += i+1; + M3D_FREE(safe); + return h; +} + +/* return offset of string */ +static uint32_t _m3d_stridx(m3dstr_t *str, uint32_t numstr, char *s) +{ + uint32_t i; + char *safe; + if(!s || !*s) return 0; + if(str) { + safe = _m3d_safestr(s, 0); + if(!safe) return 0; + if(!*safe) { + free(safe); + return 0; + } + for(i = 0; i < numstr; i++) + if(!strcmp(str[i].str, s)) { + free(safe); + return str[i].offs; + } + free(safe); + } + return 0; +} + +/* compare to faces by their material */ +static int _m3d_facecmp(const void *a, const void *b) { + const m3dfsave_t *A = (const m3dfsave_t*)a, *B = (const m3dfsave_t*)b; + return A->group != B->group ? A->group - B->group : (A->opacity != B->opacity ? (int)B->opacity - (int)A->opacity : + (int)A->data.materialid - (int)B->data.materialid); +} +/* compare face groups */ +static int _m3d_grpcmp(const void *a, const void *b) { return *((uint32_t*)a) - *((uint32_t*)b); } +/* compare UVs */ +static int _m3d_ticmp(const void *a, const void *b) { return memcmp(a, b, sizeof(m3dti_t)); } +/* compare skin groups */ +static int _m3d_skincmp(const void *a, const void *b) { return memcmp(a, b, sizeof(m3ds_t)); } +/* compare vertices */ +static int _m3d_vrtxcmp(const void *a, const void *b) { + int c = memcmp(a, b, 3 * sizeof(M3D_FLOAT)); + if(c) return c; + c = ((m3dvsave_t*)a)->norm - ((m3dvsave_t*)b)->norm; + if(c) return c; + return memcmp(a, b, sizeof(m3dv_t)); +} +/* compare labels */ +static _inline int _m3d_strcmp(char *a, char *b) +{ + if(a == NULL && b != NULL) return -1; + if(a != NULL && b == NULL) return 1; + if(a == NULL && b == NULL) return 0; + return strcmp(a, b); +} +static int _m3d_lblcmp(const void *a, const void *b) { + const m3dl_t *A = (const m3dl_t*)a, *B = (const m3dl_t*)b; + int c = _m3d_strcmp(A->lang, B->lang); + if(!c) c = _m3d_strcmp(A->name, B->name); + if(!c) c = _m3d_strcmp(A->text, B->text); + return c; +} +/* compare two colors by HSV value */ +_inline static int _m3d_cmapcmp(const void *a, const void *b) +{ + uint8_t *A = (uint8_t*)a, *B = (uint8_t*)b; + _register int m, vA, vB; + /* get HSV value for A */ + m = A[2] < A[1]? A[2] : A[1]; if(A[0] < m) m = A[0]; + vA = A[2] > A[1]? A[2] : A[1]; if(A[0] > vA) vA = A[0]; + /* get HSV value for B */ + m = B[2] < B[1]? B[2] : B[1]; if(B[0] < m) m = B[0]; + vB = B[2] > B[1]? B[2] : B[1]; if(B[0] > vB) vB = B[0]; + return vA - vB; +} + +/* create sorted list of colors */ +static uint32_t *_m3d_addcmap(uint32_t *cmap, uint32_t *numcmap, uint32_t color) +{ + uint32_t i; + if(cmap) { + for(i = 0; i < *numcmap; i++) + if(cmap[i] == color) return cmap; + } + cmap = (uint32_t*)M3D_REALLOC(cmap, ((*numcmap) + 1) * sizeof(uint32_t)); + for(i = 0; i < *numcmap && _m3d_cmapcmp(&color, &cmap[i]) > 0; i++); + if(i < *numcmap) memmove(&cmap[i+1], &cmap[i], ((*numcmap) - i)*sizeof(uint32_t)); + cmap[i] = color; + (*numcmap)++; + return cmap; +} + +/* look up a color and return its index */ +static uint32_t _m3d_cmapidx(uint32_t *cmap, uint32_t numcmap, uint32_t color) +{ + uint32_t i; + if(numcmap >= 65536) + return color; + for(i = 0; i < numcmap; i++) + if(cmap[i] == color) return i; + return 0; +} + +/* add index to output */ +static unsigned char *_m3d_addidx(unsigned char *out, char type, uint32_t idx) { + switch(type) { + case 1: *out++ = (uint8_t)(idx); break; + case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; + case 4: *((uint32_t*)out) = (uint32_t)(idx); out += 4; break; + /* case 0: case 8: break; */ + } + return out; +} + +/* round a vertex position */ +static void _m3d_round(int quality, m3dv_t *src, m3dv_t *dst) +{ + _register int t; + /* copy additional attributes */ + if(src != dst) memcpy(dst, src, sizeof(m3dv_t)); + /* round according to quality */ + switch(quality) { + case M3D_EXP_INT8: + t = (int)(src->x * 127 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = (int)(src->y * 127 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = (int)(src->z * 127 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = (int)(src->w * 127 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + break; + case M3D_EXP_INT16: + t = (int)(src->x * 32767 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = (int)(src->y * 32767 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = (int)(src->z * 32767 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = (int)(src->w * 32767 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + break; + } + if(dst->x == (M3D_FLOAT)-0.0) dst->x = (M3D_FLOAT)0.0; + if(dst->y == (M3D_FLOAT)-0.0) dst->y = (M3D_FLOAT)0.0; + if(dst->z == (M3D_FLOAT)-0.0) dst->z = (M3D_FLOAT)0.0; + if(dst->w == (M3D_FLOAT)-0.0) dst->w = (M3D_FLOAT)0.0; +} + +#ifdef M3D_ASCII +/* add a bone to ascii output */ +static char *_m3d_prtbone(char *ptr, m3db_t *bone, M3D_INDEX numbone, M3D_INDEX parent, uint32_t level, M3D_INDEX *vrtxidx) +{ + uint32_t i, j; + char *sn; + + if(level > M3D_BONEMAXLEVEL || !bone) return ptr; + for(i = 0; i < numbone; i++) { + if(bone[i].parent == parent) { + for(j = 0; j < level; j++) *ptr++ = '/'; + sn = _m3d_safestr(bone[i].name, 0); + ptr += sprintf(ptr, "%d %d %s\r\n", vrtxidx[bone[i].pos], vrtxidx[bone[i].ori], sn); + M3D_FREE(sn); + ptr = _m3d_prtbone(ptr, bone, numbone, i, level + 1, vrtxidx); + } + } + return ptr; +} +#endif + +/** + * Function to encode an in-memory model into on storage Model 3D format + */ +unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size) +{ +#ifdef M3D_ASCII + const char *ol; + char *ptr; +#endif + char vc_s, vi_s, si_s, ci_s, ti_s, bi_s, nb_s, sk_s, fc_s, hi_s, fi_s, vd_s, vp_s; + char *sn = NULL, *sl = NULL, *sa = NULL, *sd = NULL; + unsigned char *out = NULL, *z = NULL, weights[M3D_NUMBONE < 8 ? 8 : M3D_NUMBONE], *norm = NULL; + unsigned int i, j, k, l, n, o, len, chunklen, *length; + int maxvox = 0, minvox = 0; + M3D_FLOAT scale = (M3D_FLOAT)0.0, min_x, max_x, min_y, max_y, min_z, max_z; + M3D_INDEX last, *vrtxidx = NULL, *mtrlidx = NULL, *tmapidx = NULL, *skinidx = NULL; +#ifdef M3D_VERTEXMAX + M3D_INDEX lastp; +#endif + uint32_t idx, numcmap = 0, *cmap = NULL, numvrtx = 0, maxvrtx = 0, numtmap = 0, maxtmap = 0, numproc = 0; + uint32_t numskin = 0, maxskin = 0, numstr = 0, maxt = 0, maxbone = 0, numgrp = 0, maxgrp = 0, *grpidx = NULL; + uint8_t *opa; + m3dcd_t *cd; + m3dc_t *cmd; + m3dstr_t *str = NULL; + m3dvsave_t *vrtx = NULL, vertex; + m3dtisave_t *tmap = NULL, tcoord; + m3dssave_t *skin = NULL, sk; + m3dfsave_t *face = NULL; + m3dhdr_t *h = NULL; + m3dm_t *m; + m3da_t *a; + + if(!model) { + if(size) *size = 0; + return NULL; + } + model->errcode = M3D_SUCCESS; +#ifdef M3D_ASCII + if(flags & M3D_EXP_ASCII) quality = M3D_EXP_DOUBLE; +#endif + vrtxidx = (M3D_INDEX*)M3D_MALLOC(model->numvertex * sizeof(M3D_INDEX)); + if(!vrtxidx) goto memerr; + memset(vrtxidx, 255, model->numvertex * sizeof(M3D_INDEX)); + if(model->numvertex && !(flags & M3D_EXP_NONORMAL)){ + norm = (unsigned char*)M3D_MALLOC(model->numvertex * sizeof(unsigned char)); + if(!norm) goto memerr; + memset(norm, 0, model->numvertex * sizeof(unsigned char)); + } + if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) { + mtrlidx = (M3D_INDEX*)M3D_MALLOC(model->nummaterial * sizeof(M3D_INDEX)); + if(!mtrlidx) goto memerr; + memset(mtrlidx, 255, model->nummaterial * sizeof(M3D_INDEX)); + opa = (uint8_t*)M3D_MALLOC(model->nummaterial * 2 * sizeof(M3D_INDEX)); + if(!opa) goto memerr; + memset(opa, 255, model->nummaterial * 2 * sizeof(M3D_INDEX)); + } + if(model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) { + tmapidx = (M3D_INDEX*)M3D_MALLOC(model->numtmap * sizeof(M3D_INDEX)); + if(!tmapidx) goto memerr; + memset(tmapidx, 255, model->numtmap * sizeof(M3D_INDEX)); + } + /** collect array elements that are actually referenced **/ + if(!(flags & M3D_EXP_NOFACE)) { + /* face */ + if(model->numface && model->face) { + M3D_LOG("Processing mesh face"); + face = (m3dfsave_t*)M3D_MALLOC(model->numface * sizeof(m3dfsave_t)); + if(!face) goto memerr; + for(i = 0; i < model->numface; i++) { + memcpy(&face[i].data, &model->face[i], sizeof(m3df_t)); + face[i].group = 0; + face[i].opacity = 255; + if(!(flags & M3D_EXP_NOMATERIAL) && model->face[i].materialid < model->nummaterial) { + if(model->material[model->face[i].materialid].numprop) { + mtrlidx[model->face[i].materialid] = 0; + if(opa[model->face[i].materialid * 2]) { + m = &model->material[model->face[i].materialid]; + for(j = 0; j < m->numprop; j++) + if(m->prop[j].type == m3dp_Kd) { + opa[model->face[i].materialid * 2 + 1] = ((uint8_t*)&m->prop[j].value.color)[3]; + break; + } + for(j = 0; j < m->numprop; j++) + if(m->prop[j].type == m3dp_d) { + opa[model->face[i].materialid * 2 + 1] = (uint8_t)(m->prop[j].value.fnum * 255); + break; + } + opa[model->face[i].materialid * 2] = 0; + } + face[i].opacity = opa[model->face[i].materialid * 2 + 1]; + } else + face[i].data.materialid = M3D_UNDEF; + } + for(j = 0; j < 3; j++) { + k = model->face[i].vertex[j]; + if(k < model->numvertex) + vrtxidx[k] = 0; + if(!(flags & M3D_EXP_NOCMAP)) { + cmap = _m3d_addcmap(cmap, &numcmap, model->vertex[k].color); + if(!cmap) goto memerr; + } + k = model->face[i].normal[j]; + if(k < model->numvertex && !(flags & M3D_EXP_NONORMAL)) { + vrtxidx[k] = 0; + norm[k] = 1; + } + k = model->face[i].texcoord[j]; + if(k < model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) + tmapidx[k] = 0; +#ifdef M3D_VERTEXMAX + k = model->face[i].vertmax[j]; + if(k < model->numvertex && !(flags & M3D_EXP_NOVRTMAX)) + vrtxidx[k] = 0; +#endif + } + /* convert from CW to CCW */ + if(flags & M3D_EXP_IDOSUCK) { + j = face[i].data.vertex[1]; + face[i].data.vertex[1] = face[i].data.vertex[2]; + face[i].data.vertex[2] = j; + j = face[i].data.normal[1]; + face[i].data.normal[1] = face[i].data.normal[2]; + face[i].data.normal[2] = j; + j = face[i].data.texcoord[1]; + face[i].data.texcoord[1] = face[i].data.texcoord[2]; + face[i].data.texcoord[2] = j; +#ifdef M3D_VERTEXMAX + j = face[i].data.vertmax[1]; + face[i].data.vertmax[1] = face[i].data.vertmax[2]; + face[i].data.vertmax[2] = j; +#endif + } + } + } + if((model->numvoxtype && model->voxtype) || (model->numvoxel && model->voxel)) { + M3D_LOG("Processing voxel face"); + for(i = 0; i < model->numvoxtype; i++) { + str = _m3d_addstr(str, &numstr, model->voxtype[i].name); + if(model->voxtype[i].name && !str) goto memerr; + if(!(flags & M3D_EXP_NOCMAP)) { + cmap = _m3d_addcmap(cmap, &numcmap, model->voxtype[i].color); + if(!cmap) goto memerr; + } + for(j = 0; j < model->voxtype[i].numitem; j++) { + str = _m3d_addstr(str, &numstr, model->voxtype[i].item[j].name); + if(model->voxtype[i].item[j].name && !str) goto memerr; + } + } + for(i = 0; i < model->numvoxel; i++) { + str = _m3d_addstr(str, &numstr, model->voxel[i].name); + if(model->voxel[i].name && !str) goto memerr; + if(model->voxel[i].x < minvox) minvox = model->voxel[i].x; + if(model->voxel[i].x + (int)model->voxel[i].w > maxvox) maxvox = model->voxel[i].x + model->voxel[i].w; + if(model->voxel[i].y < minvox) minvox = model->voxel[i].y; + if(model->voxel[i].y + (int)model->voxel[i].h > maxvox) maxvox = model->voxel[i].y + model->voxel[i].h; + if(model->voxel[i].z < minvox) minvox = model->voxel[i].z; + if(model->voxel[i].z + (int)model->voxel[i].d > maxvox) maxvox = model->voxel[i].z + model->voxel[i].d; + } + } + if(model->numshape && model->shape) { + M3D_LOG("Processing shape face"); + for(i = 0; i < model->numshape; i++) { + if(!model->shape[i].numcmd) continue; + str = _m3d_addstr(str, &numstr, model->shape[i].name); + if(model->shape[i].name && !str) goto memerr; + for(j = 0; j < model->shape[i].numcmd; j++) { + cmd = &model->shape[i].cmd[j]; + if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg) + continue; + if(cmd->type == m3dc_mesh) { + if(numgrp + 2 < maxgrp) { + maxgrp += 1024; + grpidx = (uint32_t*)realloc(grpidx, maxgrp * sizeof(uint32_t)); + if(!grpidx) goto memerr; + if(!numgrp) { + grpidx[0] = 0; + grpidx[1] = model->numface; + numgrp += 2; + } + } + grpidx[numgrp + 0] = cmd->arg[0]; + grpidx[numgrp + 1] = cmd->arg[0] + cmd->arg[1]; + numgrp += 2; + } + cd = &m3d_commandtypes[cmd->type]; + for(k = n = 0, l = cd->p; k < l; k++) + switch(cd->a[((k - n) % (cd->p - n)) + n]) { + case m3dcp_mi_t: + if(!(flags & M3D_EXP_NOMATERIAL) && cmd->arg[k] < model->nummaterial) + mtrlidx[cmd->arg[k]] = 0; + break; + case m3dcp_ti_t: + if(!(flags & M3D_EXP_NOTXTCRD) && cmd->arg[k] < model->numtmap) + tmapidx[cmd->arg[k]] = 0; + break; + case m3dcp_qi_t: + case m3dcp_vi_t: + if(cmd->arg[k] < model->numvertex) + vrtxidx[cmd->arg[k]] = 0; + break; + case m3dcp_va_t: + n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1); + break; + } + } + } + } + if(model->numface && face) { + if(numgrp && grpidx) { + qsort(grpidx, numgrp, sizeof(uint32_t), _m3d_grpcmp); + for(i = j = 0; i < model->numface && j < numgrp; i++) { + while(j < numgrp && grpidx[j] < i) j++; + face[i].group = j; + } + } + qsort(face, model->numface, sizeof(m3dfsave_t), _m3d_facecmp); + } + if(grpidx) { M3D_FREE(grpidx); grpidx = NULL; } + if(model->numlabel && model->label) { + M3D_LOG("Processing annotation labels"); + for(i = 0; i < model->numlabel; i++) { + str = _m3d_addstr(str, &numstr, model->label[i].name); + str = _m3d_addstr(str, &numstr, model->label[i].lang); + str = _m3d_addstr(str, &numstr, model->label[i].text); + if(!(flags & M3D_EXP_NOCMAP)) { + cmap = _m3d_addcmap(cmap, &numcmap, model->label[i].color); + if(!cmap) goto memerr; + } + if(model->label[i].vertexid < model->numvertex) + vrtxidx[model->label[i].vertexid] = 0; + } + qsort(model->label, model->numlabel, sizeof(m3dl_t), _m3d_lblcmp); + } + } else if(!(flags & M3D_EXP_NOMATERIAL)) { + /* without a face, simply add all materials, because it can be an mtllib */ + for(i = 0; i < model->nummaterial; i++) + mtrlidx[i] = i; + } + /* bind-pose skeleton */ + if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) { + M3D_LOG("Processing bones"); + for(i = 0; i < model->numbone; i++) { + str = _m3d_addstr(str, &numstr, model->bone[i].name); + if(!str) goto memerr; + k = model->bone[i].pos; + if(k < model->numvertex) + vrtxidx[k] = 0; + k = model->bone[i].ori; + if(k < model->numvertex) + vrtxidx[k] = 0; + } + } + /* actions, animated skeleton poses */ + if(model->numaction && model->action && !(flags & M3D_EXP_NOACTION)) { + M3D_LOG("Processing action list"); + for(j = 0; j < model->numaction; j++) { + a = &model->action[j]; + str = _m3d_addstr(str, &numstr, a->name); + if(!str) goto memerr; + if(a->numframe > 65535) a->numframe = 65535; + for(i = 0; i < a->numframe; i++) { + for(l = 0; l < a->frame[i].numtransform; l++) { + k = a->frame[i].transform[l].pos; + if(k < model->numvertex) + vrtxidx[k] = 0; + k = a->frame[i].transform[l].ori; + if(k < model->numvertex) + vrtxidx[k] = 0; + } + if(l > maxt) maxt = l; + } + } + } + /* add colors to color map and texture names to string table */ + if(!(flags & M3D_EXP_NOMATERIAL)) { + M3D_LOG("Processing materials"); + for(i = k = 0; i < model->nummaterial; i++) { + if(mtrlidx[i] == M3D_UNDEF || !model->material[i].numprop) continue; + mtrlidx[i] = k++; + m = &model->material[i]; + str = _m3d_addstr(str, &numstr, m->name); + if(!str) goto memerr; + if(m->prop) + for(j = 0; j < m->numprop; j++) { + if(!(flags & M3D_EXP_NOCMAP) && m->prop[j].type < 128) { + for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) { + if(m->prop[j].type == m3d_propertytypes[l].id && m3d_propertytypes[l].format == m3dpf_color) { + ((uint8_t*)&m->prop[j].value.color)[3] = opa[i * 2 + 1]; + cmap = _m3d_addcmap(cmap, &numcmap, m->prop[j].value.color); + if(!cmap) goto memerr; + break; + } + } + } + if(m->prop[j].type >= 128 && m->prop[j].value.textureid < model->numtexture && + model->texture[m->prop[j].value.textureid].name) { + str = _m3d_addstr(str, &numstr, model->texture[m->prop[j].value.textureid].name); + if(!str) goto memerr; + } + } + } + } + /* if there's only one black color, don't store it */ + if(numcmap == 1 && cmap && !cmap[0]) numcmap = 0; + + /** compress lists **/ + if(model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) { + M3D_LOG("Compressing tmap"); + tmap = (m3dtisave_t*)M3D_MALLOC(model->numtmap * sizeof(m3dtisave_t)); + if(!tmap) goto memerr; + for(i = 0; i < model->numtmap; i++) { + if(tmapidx[i] == M3D_UNDEF) continue; + switch(quality) { + case M3D_EXP_INT8: + l = (unsigned int)(model->tmap[i].u * 255); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)255.0; + l = (unsigned int)(model->tmap[i].v * 255); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)255.0; + break; + case M3D_EXP_INT16: + l = (unsigned int)(model->tmap[i].u * 65535); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; + l = (unsigned int)(model->tmap[i].v * 65535); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; + break; + default: + tcoord.data.u = model->tmap[i].u; + tcoord.data.v = model->tmap[i].v; + break; + } + if(flags & M3D_EXP_FLIPTXTCRD) + tcoord.data.v = (M3D_FLOAT)1.0 - tcoord.data.v; + tcoord.oldidx = i; + memcpy(&tmap[numtmap++], &tcoord, sizeof(m3dtisave_t)); + } + if(numtmap) { + qsort(tmap, numtmap, sizeof(m3dtisave_t), _m3d_ticmp); + memcpy(&tcoord.data, &tmap[0], sizeof(m3dti_t)); + for(i = 0; i < numtmap; i++) { + if(memcmp(&tcoord.data, &tmap[i].data, sizeof(m3dti_t))) { + memcpy(&tcoord.data, &tmap[i].data, sizeof(m3dti_t)); + maxtmap++; + } + tmap[i].newidx = maxtmap; + tmapidx[tmap[i].oldidx] = maxtmap; + } + maxtmap++; + } + } + if(model->numskin && model->skin && !(flags & M3D_EXP_NOBONE)) { + M3D_LOG("Compressing skin"); + skinidx = (M3D_INDEX*)M3D_MALLOC(model->numskin * sizeof(M3D_INDEX)); + if(!skinidx) goto memerr; + skin = (m3dssave_t*)M3D_MALLOC(model->numskin * sizeof(m3dssave_t)); + if(!skin) goto memerr; + memset(skinidx, 255, model->numskin * sizeof(M3D_INDEX)); + for(i = 0; i < model->numvertex; i++) { + if(vrtxidx[i] != M3D_UNDEF && model->vertex[i].skinid < model->numskin) + skinidx[model->vertex[i].skinid] = 0; + } + for(i = 0; i < model->numskin; i++) { + if(skinidx[i] == M3D_UNDEF) continue; + memset(&sk, 0, sizeof(m3dssave_t)); + for(j = 0, min_x = (M3D_FLOAT)0.0; j < M3D_NUMBONE && model->skin[i].boneid[j] != M3D_UNDEF && + model->skin[i].weight[j] > (M3D_FLOAT)0.0; j++) { + sk.data.boneid[j] = model->skin[i].boneid[j]; + sk.data.weight[j] = model->skin[i].weight[j]; + min_x += sk.data.weight[j]; + } + if(j > maxbone) maxbone = j; + if(min_x != (M3D_FLOAT)1.0 && min_x != (M3D_FLOAT)0.0) + for(j = 0; j < M3D_NUMBONE && sk.data.weight[j] > (M3D_FLOAT)0.0; j++) + sk.data.weight[j] /= min_x; + sk.oldidx = i; + memcpy(&skin[numskin++], &sk, sizeof(m3dssave_t)); + } + if(numskin) { + qsort(skin, numskin, sizeof(m3dssave_t), _m3d_skincmp); + memcpy(&sk.data, &skin[0].data, sizeof(m3ds_t)); + for(i = 0; i < numskin; i++) { + if(memcmp(&sk.data, &skin[i].data, sizeof(m3ds_t))) { + memcpy(&sk.data, &skin[i].data, sizeof(m3ds_t)); + maxskin++; + } + skin[i].newidx = maxskin; + skinidx[skin[i].oldidx] = maxskin; + } + maxskin++; + } + } + + M3D_LOG("Compressing vertex list"); + min_x = min_y = min_z = (M3D_FLOAT)1e10; + max_x = max_y = max_z = (M3D_FLOAT)-1e10; + if(vrtxidx) { + vrtx = (m3dvsave_t*)M3D_MALLOC(model->numvertex * sizeof(m3dvsave_t)); + if(!vrtx) goto memerr; + for(i = numvrtx = 0; i < model->numvertex; i++) { + if(vrtxidx[i] == M3D_UNDEF) continue; + _m3d_round(quality, &model->vertex[i], &vertex.data); + vertex.norm = norm ? norm[i] : 0; + if(vertex.data.skinid != M3D_INDEXMAX && !vertex.norm) { + vertex.data.skinid = vertex.data.skinid != M3D_UNDEF && skinidx ? skinidx[vertex.data.skinid] : M3D_UNDEF; + if(vertex.data.x > max_x) max_x = vertex.data.x; + if(vertex.data.x < min_x) min_x = vertex.data.x; + if(vertex.data.y > max_y) max_y = vertex.data.y; + if(vertex.data.y < min_y) min_y = vertex.data.y; + if(vertex.data.z > max_z) max_z = vertex.data.z; + if(vertex.data.z < min_z) min_z = vertex.data.z; + } +#ifdef M3D_VERTEXTYPE + vertex.data.type = 0; +#endif + vertex.oldidx = i; + memcpy(&vrtx[numvrtx++], &vertex, sizeof(m3dvsave_t)); + } + if(numvrtx) { + qsort(vrtx, numvrtx, sizeof(m3dvsave_t), _m3d_vrtxcmp); + memcpy(&vertex.data, &vrtx[0].data, sizeof(m3dv_t)); + for(i = 0; i < numvrtx; i++) { + if(memcmp(&vertex.data, &vrtx[i].data, vrtx[i].norm ? 3 * sizeof(M3D_FLOAT) : sizeof(m3dv_t))) { + memcpy(&vertex.data, &vrtx[i].data, sizeof(m3dv_t)); + maxvrtx++; + } + vrtx[i].newidx = maxvrtx; + vrtxidx[vrtx[i].oldidx] = maxvrtx; + } + maxvrtx++; + } + } + if(norm) { M3D_FREE(norm); norm = NULL; } + + /* normalize to bounding cube */ + if(numvrtx && !(flags & M3D_EXP_NORECALC)) { + M3D_LOG("Normalizing coordinates"); + if(min_x < (M3D_FLOAT)0.0) min_x = -min_x; + if(max_x < (M3D_FLOAT)0.0) max_x = -max_x; + if(min_y < (M3D_FLOAT)0.0) min_y = -min_y; + if(max_y < (M3D_FLOAT)0.0) max_y = -max_y; + if(min_z < (M3D_FLOAT)0.0) min_z = -min_z; + if(max_z < (M3D_FLOAT)0.0) max_z = -max_z; + scale = min_x; + if(max_x > scale) scale = max_x; + if(min_y > scale) scale = min_y; + if(max_y > scale) scale = max_y; + if(min_z > scale) scale = min_z; + if(max_z > scale) scale = max_z; + if(scale <= (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0; + if(scale != (M3D_FLOAT)1.0) { + for(i = 0; i < numvrtx; i++) { + if(vrtx[i].data.skinid == M3D_INDEXMAX) continue; + vrtx[i].data.x /= scale; + vrtx[i].data.y /= scale; + vrtx[i].data.z /= scale; + } + } + } + if(model->scale > (M3D_FLOAT)0.0) scale = model->scale; + if(scale <= (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0; + + /* meta info */ + sn = _m3d_safestr(model->name && *model->name ? model->name : (char*)"(noname)", 2); + sl = _m3d_safestr(model->license ? model->license : (char*)"MIT", 2); + sa = _m3d_safestr(model->author ? model->author : getenv("LOGNAME"), 2); + if(!sn || !sl || !sa) { +memerr: if(vrtxidx) M3D_FREE(vrtxidx); + if(mtrlidx) M3D_FREE(mtrlidx); + if(tmapidx) M3D_FREE(tmapidx); + if(skinidx) M3D_FREE(skinidx); + if(grpidx) M3D_FREE(grpidx); + if(norm) M3D_FREE(norm); + if(face) M3D_FREE(face); + if(cmap) M3D_FREE(cmap); + if(tmap) M3D_FREE(tmap); + if(skin) M3D_FREE(skin); + if(str) M3D_FREE(str); + if(vrtx) M3D_FREE(vrtx); + if(sn) M3D_FREE(sn); + if(sl) M3D_FREE(sl); + if(sa) M3D_FREE(sa); + if(sd) M3D_FREE(sd); + if(out) M3D_FREE(out); + if(h) M3D_FREE(h); + M3D_LOG("Out of memory"); + model->errcode = M3D_ERR_ALLOC; + return NULL; + } + + M3D_LOG("Serializing model"); +#ifdef M3D_ASCII + if(flags & M3D_EXP_ASCII) { + /* use CRLF to make model creators on Win happy... */ + sd = _m3d_safestr(model->desc, 1); + if(!sd) goto memerr; + ol = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + /* header */ + len = 64 + (unsigned int)(strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd)); + out = (unsigned char*)M3D_MALLOC(len); + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr = (char*)out; + ptr += sprintf(ptr, "3dmodel %g\r\n%s\r\n%s\r\n%s\r\n%s\r\n\r\n", scale, + sn, sl, sa, sd); + M3D_FREE(sl); M3D_FREE(sa); M3D_FREE(sd); + sl = sa = sd = NULL; + /* preview chunk */ + if(model->preview.data && model->preview.length) { + sl = _m3d_safestr(sn, 0); + if(sl) { + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)20); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Preview\r\n%s.png\r\n\r\n", sl); + M3D_FREE(sl); sl = NULL; + } + } + M3D_FREE(sn); sn = NULL; + /* texture map */ + if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) { + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxtmap * 32) + (uintptr_t)12); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Textmap\r\n"); + last = M3D_UNDEF; + for(i = 0; i < numtmap; i++) { + if(tmap[i].newidx == last) continue; + last = tmap[i].newidx; + ptr += sprintf(ptr, "%g %g\r\n", tmap[i].data.u, tmap[i].data.v); + } + ptr += sprintf(ptr, "\r\n"); + } + /* vertex chunk */ + if(numvrtx && vrtx && !(flags & M3D_EXP_NOFACE)) { + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxvrtx * 128) + (uintptr_t)10); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Vertex\r\n"); + last = M3D_UNDEF; + for(i = 0; i < numvrtx; i++) { + if(vrtx[i].newidx == last) continue; + last = vrtx[i].newidx; + ptr += sprintf(ptr, "%g %g %g %g", vrtx[i].data.x, vrtx[i].data.y, vrtx[i].data.z, vrtx[i].data.w); + if(!(flags & M3D_EXP_NOCMAP) && vrtx[i].data.color) + ptr += sprintf(ptr, " #%08x", vrtx[i].data.color); + if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin && vrtx[i].data.skinid < M3D_INDEXMAX) { + if(skin[vrtx[i].data.skinid].data.weight[0] == (M3D_FLOAT)1.0) + ptr += sprintf(ptr, " %d", skin[vrtx[i].data.skinid].data.boneid[0]); + else + for(j = 0; j < M3D_NUMBONE && skin[vrtx[i].data.skinid].data.boneid[j] != M3D_UNDEF && + skin[vrtx[i].data.skinid].data.weight[j] > (M3D_FLOAT)0.0; j++) + ptr += sprintf(ptr, " %d:%g", skin[vrtx[i].data.skinid].data.boneid[j], + skin[vrtx[i].data.skinid].data.weight[j]); + } + ptr += sprintf(ptr, "\r\n"); + } + ptr += sprintf(ptr, "\r\n"); + } + /* bones chunk */ + if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) { + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)9); + for(i = 0; i < model->numbone; i++) { + len += (unsigned int)strlen(model->bone[i].name) + 128; + } + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Bones\r\n"); + ptr = _m3d_prtbone(ptr, model->bone, model->numbone, M3D_UNDEF, 0, vrtxidx); + ptr += sprintf(ptr, "\r\n"); + } + /* materials */ + if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) { + for(j = 0; j < model->nummaterial; j++) { + if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue; + m = &model->material[j]; + sn = _m3d_safestr(m->name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)12); + for(i = 0; i < m->numprop; i++) { + if(m->prop[i].type < 128) + len += 32; + else if(m->prop[i].value.textureid < model->numtexture && model->texture[m->prop[i].value.textureid].name) + len += (unsigned int)strlen(model->texture[m->prop[i].value.textureid].name) + 16; + } + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Material %s\r\n", sn); + M3D_FREE(sn); sn = NULL; + for(i = 0; i < m->numprop; i++) { + k = 256; + if(m->prop[i].type >= 128) { + for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) + if(m->prop[i].type == m3d_propertytypes[l].id) { + sn = m3d_propertytypes[l].key; + break; + } + if(!sn) + for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) + if(m->prop[i].type - 128 == m3d_propertytypes[l].id) { + sn = m3d_propertytypes[l].key; + break; + } + k = sn ? m3dpf_map : 256; + } else { + for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) + if(m->prop[i].type == m3d_propertytypes[l].id) { + sn = m3d_propertytypes[l].key; + k = m3d_propertytypes[l].format; + break; + } + } + switch(k) { + case m3dpf_color: ptr += sprintf(ptr, "%s #%08x\r\n", sn, m->prop[i].value.color); break; + case m3dpf_uint8: + case m3dpf_uint16: + case m3dpf_uint32: ptr += sprintf(ptr, "%s %d\r\n", sn, m->prop[i].value.num); break; + case m3dpf_float: ptr += sprintf(ptr, "%s %g\r\n", sn, m->prop[i].value.fnum); break; + case m3dpf_map: + if(m->prop[i].value.textureid < model->numtexture && + model->texture[m->prop[i].value.textureid].name) { + sl = _m3d_safestr(model->texture[m->prop[i].value.textureid].name, 0); + if(!sl) { setlocale(LC_NUMERIC, ol); goto memerr; } + if(*sl) + ptr += sprintf(ptr, "map_%s %s\r\n", sn, sl); + M3D_FREE(sn); M3D_FREE(sl); sl = NULL; + } + break; + } + sn = NULL; + } + ptr += sprintf(ptr, "\r\n"); + } + } + /* procedural face */ + if(model->numinlined && model->inlined && !(flags & M3D_EXP_NOFACE)) { + /* all inlined assets which are not textures should be procedural surfaces */ + for(j = 0; j < model->numinlined; j++) { + if(!model->inlined[j].name || !*model->inlined[j].name || !model->inlined[j].length || !model->inlined[j].data || + (model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && model->inlined[j].data[3] == 'G')) + continue; + for(i = k = 0; i < model->numtexture; i++) { + if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; } + } + if(k) continue; + sn = _m3d_safestr(model->inlined[j].name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)18); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Procedural\r\n%s\r\n\r\n", sn); + M3D_FREE(sn); sn = NULL; + } + } + /* mesh face */ + if(model->numface && face && !(flags & M3D_EXP_NOFACE)) { + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numface * 128) + (uintptr_t)6); + last = M3D_UNDEF; +#ifdef M3D_VERTEXMAX + lastp = M3D_UNDEF; +#endif + if(!(flags & M3D_EXP_NOMATERIAL)) + for(i = 0; i < model->numface; i++) { + j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF; + if(j != last) { + last = j; + if(last < model->nummaterial) + len += (unsigned int)strlen(model->material[last].name); + len += 6; + } +#ifdef M3D_VERTEXMAX + j = face[i].data.paramid < model->numparam ? face[i].data.paramid : M3D_UNDEF; + if(j != lastp) { + lastp = j; + if(lastp < model->numparam) + len += (unsigned int)strlen(model->param[lastp].name); + len += 6; + } +#endif + } + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Mesh\r\n"); + last = M3D_UNDEF; +#ifdef M3D_VERTEXMAX + lastp = M3D_UNDEF; +#endif + for(i = 0; i < model->numface; i++) { + j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF; + if(!(flags & M3D_EXP_NOMATERIAL) && j != last) { + last = j; + if(last < model->nummaterial) { + sn = _m3d_safestr(model->material[last].name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "use %s\r\n", sn); + M3D_FREE(sn); sn = NULL; + } else + ptr += sprintf(ptr, "use\r\n"); + } +#ifdef M3D_VERTEXMAX + j = face[i].data.paramid < model->numparam ? face[i].data.paramid : M3D_UNDEF; + if(!(flags & M3D_EXP_NOVRTMAX) && j != lastp) { + lastp = j; + if(lastp < model->numparam) { + sn = _m3d_safestr(model->param[lastp].name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "par %s\r\n", sn); + M3D_FREE(sn); sn = NULL; + } else + ptr += sprintf(ptr, "par\r\n"); + } +#endif + /* hardcoded triangles. Should be repeated as many times as the number of edges in polygon */ + for(j = 0; j < 3; j++) { + ptr += sprintf(ptr, "%s%d", j?" ":"", vrtxidx[face[i].data.vertex[j]]); + k = l = M3D_NOTDEFINED; + if(!(flags & M3D_EXP_NOTXTCRD) && (face[i].data.texcoord[j] != M3D_UNDEF) && + (tmapidx[face[i].data.texcoord[j]] != M3D_UNDEF)) { + k = tmapidx[face[i].data.texcoord[j]]; + ptr += sprintf(ptr, "/%d", k); + } + if(!(flags & M3D_EXP_NONORMAL) && (face[i].data.normal[j] != M3D_UNDEF)) { + l = vrtxidx[face[i].data.normal[j]]; + ptr += sprintf(ptr, "%s/%d", k == M3D_NOTDEFINED? "/" : "", l); + } +#ifdef M3D_VERTEXMAX + if(!(flags & M3D_EXP_NOVRTMAX) && (face[i].data.vertmax[j] != M3D_UNDEF)) { + ptr += sprintf(ptr, "%s%s/%d", k == M3D_NOTDEFINED? "/" : "", l == M3D_NOTDEFINED? "/" : "", + vrtxidx[face[i].data.vertmax[j]]); + } +#endif + } + ptr += sprintf(ptr, "\r\n"); + } + ptr += sprintf(ptr, "\r\n"); + } + /* voxel face */ + if(model->numvoxtype && model->voxtype && !(flags & M3D_EXP_NOFACE)) { + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numvoxtype * 128) + (uintptr_t)10); + for(i = 0; i < model->numvoxtype; i++) { + if(model->voxtype[i].name) len += (unsigned int)strlen(model->voxtype[i].name); + for(j = 0; j < model->voxtype[i].numitem; j++) + if(model->voxtype[i].item[j].name) + len += (unsigned int)strlen(model->voxtype[i].item[j].name) + 6; + } + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "VoxTypes\r\n"); + for(i = 0; i < model->numvoxtype; i++) { + ptr += sprintf(ptr, "#%08x", model->voxtype[i].color); + if(model->voxtype[i].rotation) + ptr += sprintf(ptr, "/%02x", model->voxtype[i].rotation); + if(model->voxtype[i].voxshape) + ptr += sprintf(ptr, "%s/%03x", model->voxtype[i].rotation ? "" : "/", model->voxtype[i].voxshape); + sn = _m3d_safestr(model->voxtype[i].name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, " %s", sn && sn[0] ? sn : "-"); + M3D_FREE(sn); sn = NULL; + if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin && model->voxtype[i].skinid < M3D_INDEXMAX) { + if(skin[skinidx[model->voxtype[i].skinid]].data.weight[0] == (M3D_FLOAT)1.0) + ptr += sprintf(ptr, " %d", skin[skinidx[model->voxtype[i].skinid]].data.boneid[0]); + else + for(j = 0; j < M3D_NUMBONE && skin[skinidx[model->voxtype[i].skinid]].data.boneid[j] != M3D_UNDEF && + skin[skinidx[model->voxtype[i].skinid]].data.weight[j] > (M3D_FLOAT)0.0; j++) + ptr += sprintf(ptr, " %d:%g", skin[skinidx[model->voxtype[i].skinid]].data.boneid[j], + skin[skinidx[model->voxtype[i].skinid]].data.weight[j]); + } + if(model->voxtype[i].numitem && model->voxtype[i].item) { + for(j = k = 0; j < model->voxtype[i].numitem; j++) { + if(!model->voxtype[i].item[j].count || !model->voxtype[i].item[j].name || + !model->voxtype[i].item[j].name[0]) continue; + if(!k) { ptr += sprintf(ptr, " {"); k = 1; } + sn = _m3d_safestr(model->voxtype[i].item[j].name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, " %d %s", model->voxtype[i].item[j].count, sn); + M3D_FREE(sn); sn = NULL; + } + if(k) ptr += sprintf(ptr, " }"); + } + while(ptr[-1] == '-' || ptr[-1] == ' ') ptr--; + ptr += sprintf(ptr, "\r\n"); + } + ptr += sprintf(ptr, "\r\n"); + } + if(model->numvoxel && model->voxel && !(flags & M3D_EXP_NOFACE)) { + for(i = 0; i < model->numvoxel; i++) { + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)128); + if(model->voxel[i].name) len += (unsigned int)strlen(model->voxel[i].name); + len += model->voxel[i].h * ((model->voxel[i].w * 6 + 2) * model->voxel[i].d + 9); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Voxel"); + sn = _m3d_safestr(model->voxel[i].name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + if(sn && sn[0]) + ptr += sprintf(ptr, " %s", sn); + M3D_FREE(sn); sn = NULL; + ptr += sprintf(ptr, "\r\n"); + if(model->voxel[i].uncertain) + ptr += sprintf(ptr, "uncertain %d %d\r\n", (model->voxel[i].uncertain * 100) / 255, model->voxel[i].groupid); + if(model->voxel[i].x || model->voxel[i].y || model->voxel[i].z) + ptr += sprintf(ptr, "pos %d %d %d\r\n", model->voxel[i].x, model->voxel[i].y, model->voxel[i].z); + ptr += sprintf(ptr, "dim %d %d %d\r\n", model->voxel[i].w, model->voxel[i].h, model->voxel[i].d); + for(j = n = 0; j < model->voxel[i].h; j++) { + ptr += sprintf(ptr, "layer\r\n"); + for(k = 0; k < model->voxel[i].d; k++) { + for(l = 0; l < model->voxel[i].w; l++, n++) { + switch(model->voxel[i].data[n]) { + case M3D_VOXCLEAR: *ptr++ = '-'; break; + case M3D_VOXUNDEF: *ptr++ = '.'; break; + default: ptr += sprintf(ptr, "%d", model->voxel[i].data[n]); break; + } + *ptr++ = ' '; + } + ptr--; + ptr += sprintf(ptr, "\r\n"); + } + } + ptr += sprintf(ptr, "\r\n"); + } + } + /* mathematical shapes face */ + if(model->numshape && model->numshape && !(flags & M3D_EXP_NOFACE)) { + for(j = 0; j < model->numshape; j++) { + sn = _m3d_safestr(model->shape[j].name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)33); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Shape %s\r\n", sn); + M3D_FREE(sn); sn = NULL; + if(model->shape[j].group != M3D_UNDEF && !(flags & M3D_EXP_NOBONE)) + ptr += sprintf(ptr, "group %d\r\n", model->shape[j].group); + for(i = 0; i < model->shape[j].numcmd; i++) { + cmd = &model->shape[j].cmd[i]; + if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg) + continue; + cd = &m3d_commandtypes[cmd->type]; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(cd->key) + (uintptr_t)3); + for(k = 0; k < cd->p; k++) + switch(cd->a[k]) { + case m3dcp_mi_t: if(cmd->arg[k] != M3D_NOTDEFINED) { len += (unsigned int)strlen(model->material[cmd->arg[k]].name) + 1; } break; + case m3dcp_va_t: len += cmd->arg[k] * (cd->p - k - 1) * 16; k = cd->p; break; + default: len += 16; break; + } + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "%s", cd->key); + for(k = n = 0, l = cd->p; k < l; k++) { + switch(cd->a[((k - n) % (cd->p - n)) + n]) { + case m3dcp_mi_t: + if(cmd->arg[k] != M3D_NOTDEFINED) { + sn = _m3d_safestr(model->material[cmd->arg[k]].name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, " %s", sn); + M3D_FREE(sn); sn = NULL; + } + break; + case m3dcp_vc_t: ptr += sprintf(ptr, " %g", *((float*)&cmd->arg[k])); break; + case m3dcp_va_t: ptr += sprintf(ptr, " %d[", cmd->arg[k]); + n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1); + break; + default: ptr += sprintf(ptr, " %d", cmd->arg[k]); break; + } + } + ptr += sprintf(ptr, "%s\r\n", l > cd->p ? " ]" : ""); + } + ptr += sprintf(ptr, "\r\n"); + } + } + /* annotation labels */ + if(model->numlabel && model->label && !(flags & M3D_EXP_NOFACE)) { + for(i = 0, j = 3, length = NULL; i < model->numlabel; i++) { + if(model->label[i].name) j += (unsigned int)strlen(model->label[i].name); + if(model->label[i].lang) j += (unsigned int)strlen(model->label[i].lang); + if(model->label[i].text) j += (unsigned int)strlen(model->label[i].text); + j += 40; + } + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + for(i = 0; i < model->numlabel; i++) { + if(!i || _m3d_strcmp(sl, model->label[i].lang) || _m3d_strcmp(sn, model->label[i].name)) { + sl = model->label[i].lang; + sn = model->label[i].name; + sd = _m3d_safestr(sn, 0); + if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; } + if(i) ptr += sprintf(ptr, "\r\n"); + ptr += sprintf(ptr, "Labels %s\r\n", sd); + M3D_FREE(sd); sd = NULL; + if(model->label[i].color) + ptr += sprintf(ptr, "color #0x%08x\r\n", model->label[i].color); + if(sl && *sl) { + sd = _m3d_safestr(sl, 0); + if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; } + ptr += sprintf(ptr, "lang %s\r\n", sd); + M3D_FREE(sd); sd = NULL; + } + } + sd = _m3d_safestr(model->label[i].text, 2); + if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; } + ptr += sprintf(ptr, "%d %s\r\n", model->label[i].vertexid, sd); + M3D_FREE(sd); sd = NULL; + } + ptr += sprintf(ptr, "\r\n"); + sn = sl = NULL; + } + /* actions */ + if(model->numaction && model->action && !(flags & M3D_EXP_NOACTION)) { + for(j = 0; j < model->numaction; j++) { + a = &model->action[j]; + sn = _m3d_safestr(a->name, 0); + if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)48); + for(i = 0; i < a->numframe; i++) + len += a->frame[i].numtransform * 128 + 8; + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Action %d %s\r\n", a->durationmsec, sn); + M3D_FREE(sn); sn = NULL; + for(i = 0; i < a->numframe; i++) { + ptr += sprintf(ptr, "frame %d\r\n", a->frame[i].msec); + for(k = 0; k < a->frame[i].numtransform; k++) { + ptr += sprintf(ptr, "%d %d %d\r\n", a->frame[i].transform[k].boneid, + vrtxidx[a->frame[i].transform[k].pos], vrtxidx[a->frame[i].transform[k].ori]); + } + } + ptr += sprintf(ptr, "\r\n"); + } + } + /* inlined assets */ + if(model->numinlined && model->inlined) { + for(i = j = 0; i < model->numinlined; i++) + if(model->inlined[i].name) + j += (unsigned int)strlen(model->inlined[i].name) + 6; + if(j > 0) { + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j + (uintptr_t)16); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Assets\r\n"); + for(i = 0; i < model->numinlined; i++) + if(model->inlined[i].name) + ptr += sprintf(ptr, "%s%s\r\n", model->inlined[i].name, strrchr(model->inlined[i].name, '.') ? "" : ".png"); + ptr += sprintf(ptr, "\r\n"); + } + } + /* extra info */ + if(model->numextra && (flags & M3D_EXP_EXTRA)) { + for(i = 0; i < model->numextra; i++) { + if(model->extra[i]->length < 9) continue; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)17 + (uintptr_t)(model->extra[i]->length * 3)); + out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; + if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } + ptr += sprintf(ptr, "Extra %c%c%c%c\r\n", + model->extra[i]->magic[0] > ' ' ? model->extra[i]->magic[0] : '_', + model->extra[i]->magic[1] > ' ' ? model->extra[i]->magic[1] : '_', + model->extra[i]->magic[2] > ' ' ? model->extra[i]->magic[2] : '_', + model->extra[i]->magic[3] > ' ' ? model->extra[i]->magic[3] : '_'); + for(j = 0; j < model->extra[i]->length; j++) + ptr += sprintf(ptr, "%02x ", *((unsigned char *)model->extra + sizeof(m3dchunk_t) + j)); + ptr--; + ptr += sprintf(ptr, "\r\n\r\n"); + } + } + setlocale(LC_NUMERIC, ol); + len = (unsigned int)((uintptr_t)ptr - (uintptr_t)out); + out = (unsigned char*)M3D_REALLOC(out, len + 1); + if(!out) goto memerr; + out[len] = 0; + } else +#endif + { + /* stricly only use LF (newline) in binary */ + sd = _m3d_safestr(model->desc, 3); + if(!sd) goto memerr; + /* header */ + h = (m3dhdr_t*)M3D_MALLOC(sizeof(m3dhdr_t) + strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd) + 4); + if(!h) goto memerr; + memcpy((uint8_t*)h, "HEAD", 4); + h->length = sizeof(m3dhdr_t); + h->scale = scale; + i = (unsigned int)strlen(sn); memcpy((uint8_t*)h + h->length, sn, i+1); h->length += i+1; M3D_FREE(sn); + i = (unsigned int)strlen(sl); memcpy((uint8_t*)h + h->length, sl, i+1); h->length += i+1; M3D_FREE(sl); + i = (unsigned int)strlen(sa); memcpy((uint8_t*)h + h->length, sa, i+1); h->length += i+1; M3D_FREE(sa); + i = (unsigned int)strlen(sd); memcpy((uint8_t*)h + h->length, sd, i+1); h->length += i+1; M3D_FREE(sd); + sn = sl = sa = sd = NULL; + if(model->inlined) + for(i = 0; i < model->numinlined; i++) { + if(model->inlined[i].name && *model->inlined[i].name && model->inlined[i].length > 0) { + str = _m3d_addstr(str, &numstr, model->inlined[i].name); + if(!str) goto memerr; + } + } + if(str) + for(i = 0; i < numstr; i++) { + h = _m3d_addhdr(h, &str[i]); + if(!h) goto memerr; + } + vc_s = quality == M3D_EXP_INT8? 1 : (quality == M3D_EXP_INT16? 2 : (quality == M3D_EXP_DOUBLE? 8 : 4)); + vi_s = maxvrtx < 254 ? 1 : (maxvrtx < 65534 ? 2 : 4); + si_s = h->length - 16 < 254 ? 1 : (h->length - 16 < 65534 ? 2 : 4); + ci_s = !numcmap || !cmap ? 0 : (numcmap < 254 ? 1 : (numcmap < 65534 ? 2 : 4)); + ti_s = !maxtmap || !tmap ? 0 : (maxtmap < 254 ? 1 : (maxtmap < 65534 ? 2 : 4)); + bi_s = !model->numbone || !model->bone || (flags & M3D_EXP_NOBONE)? 0 : (model->numbone < 254 ? 1 : + (model->numbone < 65534 ? 2 : 4)); + nb_s = maxbone < 2 ? 1 : (maxbone == 2 ? 2 : (maxbone <= 4 ? 4 : 8)); + sk_s = !bi_s || !maxskin || !skin ? 0 : (maxskin < 254 ? 1 : (maxskin < 65534 ? 2 : 4)); + fc_s = maxt < 254 ? 1 : (maxt < 65534 ? 2 : 4); + hi_s = !model->numshape || !model->shape || (flags & M3D_EXP_NOFACE)? 0 : (model->numshape < 254 ? 1 : + (model->numshape < 65534 ? 2 : 4)); + fi_s = !model->numface || !model->face || (flags & M3D_EXP_NOFACE)? 0 : (model->numface < 254 ? 1 : + (model->numface < 65534 ? 2 : 4)); + vd_s = !model->numvoxel || !model->voxel || (flags & M3D_EXP_NOFACE)? 0 : (minvox >= -128 && maxvox <= 127 ? 1 : + (minvox >= -32768 && maxvox <= 32767 ? 2 : 4)); + vp_s = !model->numvoxtype || !model->voxtype || (flags & M3D_EXP_NOFACE)? 0 : (model->numvoxtype < 254 ? 1 : + (model->numvoxtype < 65534 ? 2 : 4)); + h->types = (vc_s == 8 ? (3<<0) : (vc_s == 2 ? (1<<0) : (vc_s == 1 ? (0<<0) : (2<<0)))) | + (vi_s == 2 ? (1<<2) : (vi_s == 1 ? (0<<2) : (2<<2))) | + (si_s == 2 ? (1<<4) : (si_s == 1 ? (0<<4) : (2<<4))) | + (ci_s == 2 ? (1<<6) : (ci_s == 1 ? (0<<6) : (ci_s == 4 ? (2<<6) : (3<<6)))) | + (ti_s == 2 ? (1<<8) : (ti_s == 1 ? (0<<8) : (ti_s == 4 ? (2<<8) : (3<<8)))) | + (bi_s == 2 ? (1<<10): (bi_s == 1 ? (0<<10): (bi_s == 4 ? (2<<10) : (3<<10)))) | + (nb_s == 2 ? (1<<12): (nb_s == 1 ? (0<<12): (2<<12))) | + (sk_s == 2 ? (1<<14): (sk_s == 1 ? (0<<14): (sk_s == 4 ? (2<<14) : (3<<14)))) | + (fc_s == 2 ? (1<<16): (fc_s == 1 ? (0<<16): (2<<16))) | + (hi_s == 2 ? (1<<18): (hi_s == 1 ? (0<<18): (hi_s == 4 ? (2<<18) : (3<<18)))) | + (fi_s == 2 ? (1<<20): (fi_s == 1 ? (0<<20): (fi_s == 4 ? (2<<20) : (3<<20)))) | + (vd_s == 2 ? (1<<22): (vd_s == 1 ? (0<<22): (vd_s == 4 ? (2<<22) : (3<<22)))) | + (vp_s == 2 ? (1<<24): (vp_s == 1 ? (0<<24): (vp_s == 4 ? (2<<24) : (3<<24)))); + len = h->length; + /* color map */ + if(numcmap && cmap && ci_s < 4 && !(flags & M3D_EXP_NOCMAP)) { + chunklen = 8 + numcmap * sizeof(uint32_t); + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "CMAP", 4); + *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen; + memcpy((uint8_t*)h + len + 8, cmap, chunklen - 8); + len += chunklen; + } else numcmap = 0; + /* texture map */ + if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) { + chunklen = 8 + maxtmap * vc_s * 2; + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "TMAP", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + last = M3D_UNDEF; + for(i = 0; i < numtmap; i++) { + if(tmap[i].newidx == last) continue; + last = tmap[i].newidx; + switch(vc_s) { + case 1: *out++ = (uint8_t)(tmap[i].data.u * 255); *out++ = (uint8_t)(tmap[i].data.v * 255); break; + case 2: + *((uint16_t*)out) = (uint16_t)(tmap[i].data.u * 65535); out += 2; + *((uint16_t*)out) = (uint16_t)(tmap[i].data.v * 65535); out += 2; + break; + case 4: *((float*)out) = tmap[i].data.u; out += 4; *((float*)out) = tmap[i].data.v; out += 4; break; + case 8: *((double*)out) = tmap[i].data.u; out += 8; *((double*)out) = tmap[i].data.v; out += 8; break; + } + } + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + out = NULL; + len += *length; + } + /* vertex */ + if(numvrtx && vrtx) { + chunklen = 8 + maxvrtx * (ci_s + sk_s + 4 * vc_s); + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "VRTS", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + last = M3D_UNDEF; + for(i = 0; i < numvrtx; i++) { + if(vrtx[i].newidx == last) continue; + last = vrtx[i].newidx; + switch(vc_s) { + case 1: + *out++ = (int8_t)(vrtx[i].data.x * 127); + *out++ = (int8_t)(vrtx[i].data.y * 127); + *out++ = (int8_t)(vrtx[i].data.z * 127); + *out++ = (int8_t)(vrtx[i].data.w * 127); + break; + case 2: + *((int16_t*)out) = (int16_t)(vrtx[i].data.x * 32767); out += 2; + *((int16_t*)out) = (int16_t)(vrtx[i].data.y * 32767); out += 2; + *((int16_t*)out) = (int16_t)(vrtx[i].data.z * 32767); out += 2; + *((int16_t*)out) = (int16_t)(vrtx[i].data.w * 32767); out += 2; + break; + case 4: + *((float*)out) = vrtx[i].data.x; out += 4; + *((float*)out) = vrtx[i].data.y; out += 4; + *((float*)out) = vrtx[i].data.z; out += 4; + *((float*)out) = vrtx[i].data.w; out += 4; + break; + case 8: + *((double*)out) = vrtx[i].data.x; out += 8; + *((double*)out) = vrtx[i].data.y; out += 8; + *((double*)out) = vrtx[i].data.z; out += 8; + *((double*)out) = vrtx[i].data.w; out += 8; + break; + } + idx = _m3d_cmapidx(cmap, numcmap, vrtx[i].data.color); + switch(ci_s) { + case 1: *out++ = (uint8_t)(idx); break; + case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; + case 4: *((uint32_t*)out) = vrtx[i].data.color; out += 4; break; + } + out = _m3d_addidx(out, sk_s, vrtx[i].data.skinid); + } + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + out = NULL; + len += *length; + } + /* bones chunk */ + if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) { + i = 8 + bi_s + sk_s + model->numbone * (bi_s + si_s + 2*vi_s); + chunklen = i + numskin * nb_s * (bi_s + 1); + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "BONE", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + out = _m3d_addidx(out, bi_s, model->numbone); + out = _m3d_addidx(out, sk_s, maxskin); + for(i = 0; i < model->numbone; i++) { + out = _m3d_addidx(out, bi_s, model->bone[i].parent); + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->bone[i].name)); + out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].pos]); + out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].ori]); + } + if(numskin && skin && sk_s) { + last = M3D_UNDEF; + for(i = 0; i < numskin; i++) { + if(skin[i].newidx == last) continue; + last = skin[i].newidx; + memset(&weights, 0, nb_s); + for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF && + skin[i].data.weight[j] > (M3D_FLOAT)0.0; j++) + weights[j] = (uint8_t)(skin[i].data.weight[j] * 255); + switch(nb_s) { + case 1: weights[0] = 255; break; + case 2: memcpy(out, weights, 2); out += 2; break; + case 4: memcpy(out, weights, 4); out += 4; break; + case 8: memcpy(out, weights, 8); out += 8; break; + } + for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF && weights[j]; j++) { + out = _m3d_addidx(out, bi_s, skin[i].data.boneid[j]); + *length += bi_s; + } + } + } + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + out = NULL; + len += *length; + } + /* materials */ + if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) { + for(j = 0; j < model->nummaterial; j++) { + if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue; + m = &model->material[j]; + chunklen = 12 + si_s + m->numprop * 5; + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "MTRL", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, m->name)); + for(i = 0; i < m->numprop; i++) { + if(m->prop[i].type >= 128) { + if(m->prop[i].value.textureid >= model->numtexture || + !model->texture[m->prop[i].value.textureid].name) continue; + k = m3dpf_map; + } else { + for(k = 256, l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) + if(m->prop[i].type == m3d_propertytypes[l].id) { k = m3d_propertytypes[l].format; break; } + } + if(k == 256) continue; + *out++ = m->prop[i].type; + switch(k) { + case m3dpf_color: + if(!(flags & M3D_EXP_NOCMAP)) { + idx = _m3d_cmapidx(cmap, numcmap, m->prop[i].value.color); + switch(ci_s) { + case 1: *out++ = (uint8_t)(idx); break; + case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; + case 4: *((uint32_t*)out) = (uint32_t)(m->prop[i].value.color); out += 4; break; + } + } else out--; + break; + case m3dpf_uint8: *out++ = m->prop[i].value.num; break; + case m3dpf_uint16: *((uint16_t*)out) = m->prop[i].value.num; out += 2; break; + case m3dpf_uint32: *((uint32_t*)out) = m->prop[i].value.num; out += 4; break; + case m3dpf_float: *((float*)out) = m->prop[i].value.fnum; out += 4; break; + + case m3dpf_map: + idx = _m3d_stridx(str, numstr, model->texture[m->prop[i].value.textureid].name); + out = _m3d_addidx(out, si_s, idx); + break; + } + } + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + len += *length; + out = NULL; + } + } + /* procedural face */ + if(model->numinlined && model->inlined && !(flags & M3D_EXP_NOFACE)) { + /* all inlined assets which are not textures should be procedural surfaces */ + for(j = 0; j < model->numinlined; j++) { + if(!model->inlined[j].name || !model->inlined[j].name[0] || model->inlined[j].length < 4 || + !model->inlined[j].data || (model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && + model->inlined[j].data[3] == 'G')) + continue; + for(i = k = 0; i < model->numtexture; i++) { + if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; } + } + if(k) continue; + numproc++; + chunklen = 8 + si_s; + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "PROC", 4); + *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen; + out = (uint8_t*)h + len + 8; + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->inlined[j].name)); + out = NULL; + len += chunklen; + } + } + /* mesh face */ + if(model->numface && face && !(flags & M3D_EXP_NOFACE)) { + chunklen = 8 + si_s + model->numface * (6 * vi_s + 3 * ti_s + si_s + 1); + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "MESH", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + last = M3D_UNDEF; +#ifdef M3D_VERTEXMAX + lastp = M3D_UNDEF; +#endif + for(i = 0; i < model->numface; i++) { + if(!(flags & M3D_EXP_NOMATERIAL) && face[i].data.materialid != last) { + last = face[i].data.materialid; + idx = last < model->nummaterial ? _m3d_stridx(str, numstr, model->material[last].name) : 0; + *out++ = 0; + out = _m3d_addidx(out, si_s, idx); + } +#ifdef M3D_VERTEXMAX + if(!(flags & M3D_EXP_NOVRTMAX) && face[i].data.paramid != lastp) { + lastp = face[i].data.paramid; + idx = lastp < model->numparam ? _m3d_stridx(str, numstr, model->param[lastp].name) : 0; + *out++ = 0; + out = _m3d_addidx(out, si_s, idx); + } +#endif + /* hardcoded triangles. */ + k = (3 << 4) | + (((flags & M3D_EXP_NOTXTCRD) || !ti_s || face[i].data.texcoord[0] == M3D_UNDEF || + face[i].data.texcoord[1] == M3D_UNDEF || face[i].data.texcoord[2] == M3D_UNDEF) ? 0 : 1) | + (((flags & M3D_EXP_NONORMAL) || face[i].data.normal[0] == M3D_UNDEF || + face[i].data.normal[1] == M3D_UNDEF || face[i].data.normal[2] == M3D_UNDEF) ? 0 : 2) +#ifdef M3D_VERTEXMAX + | (((flags & M3D_EXP_NOVRTMAX) || face[i].data.vertmax[0] == M3D_UNDEF || + face[i].data.vertmax[1] == M3D_UNDEF || face[i].data.vertmax[2] == M3D_UNDEF) ? 0 : 4) +#endif + ; + *out++ = k; + for(j = 0; j < 3; j++) { + out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertex[j]]); + if(k & 1) + out = _m3d_addidx(out, ti_s, tmapidx[face[i].data.texcoord[j]]); + if(k & 2) + out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.normal[j]]); +#ifdef M3D_VERTEXMAX + if(k & 4) + out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertmax[j]]); +#endif + } + } + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + len += *length; + out = NULL; + } + /* voxel face */ + if(model->numvoxtype && model->voxtype && !(flags & M3D_EXP_NOFACE)) { + chunklen = 8 + si_s + model->numvoxtype * (ci_s + si_s + 3 + sk_s); + for(i = 0; i < model->numvoxtype; i++) + chunklen += model->voxtype[i].numitem * (2 + si_s); + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "VOXT", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + for(i = 0; i < model->numvoxtype; i++) { + if(!(flags & M3D_EXP_NOCMAP)) { + idx = _m3d_cmapidx(cmap, numcmap, model->voxtype[i].color); + switch(ci_s) { + case 1: *out++ = (uint8_t)(idx); break; + case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; + case 4: *((uint32_t*)out) = (uint32_t)(model->voxtype[i].color); out += 4; break; + } + } + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxtype[i].name)); + *out++ = (model->voxtype[i].rotation & 0xBF) | (((model->voxtype[i].voxshape >> 8) & 1) << 6); + *out++ = model->voxtype[i].voxshape; + *out++ = model->voxtype[i].numitem; + if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin) + out = _m3d_addidx(out, sk_s, skinidx[model->voxtype[i].skinid]); + for(j = 0; j < model->voxtype[i].numitem; j++) { + out = _m3d_addidx(out, 2, model->voxtype[i].item[j].count); + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxtype[i].item[j].name)); + } + } + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + len += *length; + out = NULL; + } + if(model->numvoxel && model->voxel && !(flags & M3D_EXP_NOFACE)) { + for(j = 0; j < model->numvoxel; j++) { + chunklen = 8 + si_s + 6 * vd_s + 2 + model->voxel[j].w * model->voxel[j].h * model->voxel[j].d * 3; + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "VOXD", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxel[j].name)); + out = _m3d_addidx(out, vd_s, model->voxel[j].x); + out = _m3d_addidx(out, vd_s, model->voxel[j].y); + out = _m3d_addidx(out, vd_s, model->voxel[j].z); + out = _m3d_addidx(out, vd_s, model->voxel[j].w); + out = _m3d_addidx(out, vd_s, model->voxel[j].h); + out = _m3d_addidx(out, vd_s, model->voxel[j].d); + *out++ = model->voxel[j].uncertain; + *out++ = model->voxel[j].groupid; + /* RLE compress voxel data */ + n = model->voxel[j].w * model->voxel[j].h * model->voxel[j].d; + k = o = 0; out[o++] = 0; + for(i = 0; i < n; i++) { + for(l = 1; l < 128 && i + l < n && model->voxel[j].data[i] == model->voxel[j].data[i + l]; l++); + if(l > 1) { + l--; + if(out[k]) { out[k]--; out[o++] = 0x80 | l; } + else out[k] = 0x80 | l; + switch(vp_s) { + case 1: out[o++] = model->voxel[j].data[i]; break; + default: *((uint16_t*)(out + o)) = model->voxel[j].data[i]; o += 2; break; + } + k = o; out[o++] = 0; + i += l; + continue; + } + out[k]++; + switch(vp_s) { + case 1: out[o++] = model->voxel[j].data[i]; break; + default: *((uint16_t*)(out + o)) = model->voxel[j].data[i]; o += 2; break; + } + if(out[k] > 127) { out[k]--; k = o; out[o++] = 0; } + } + if(!(out[k] & 0x80)) { if(out[k]) out[k]--; else o--; } + *length = (uint32_t)((uintptr_t)out + (uintptr_t)o - (uintptr_t)((uint8_t*)h + len)); + len += *length; + out = NULL; + } + } + /* mathematical shapes face */ + if(model->numshape && model->shape && !(flags & M3D_EXP_NOFACE)) { + for(j = 0; j < model->numshape; j++) { + chunklen = 12 + si_s + model->shape[j].numcmd * (M3D_CMDMAXARG + 1) * 4; + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "SHPE", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->shape[j].name)); + out = _m3d_addidx(out, bi_s, model->shape[j].group); + for(i = 0; i < model->shape[j].numcmd; i++) { + cmd = &model->shape[j].cmd[i]; + if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg) + continue; + cd = &m3d_commandtypes[cmd->type]; + *out++ = (cmd->type & 0x7F) | (cmd->type > 127 ? 0x80 : 0); + if(cmd->type > 127) *out++ = (cmd->type >> 7) & 0xff; + for(k = n = 0, l = cd->p; k < l; k++) { + switch(cd->a[((k - n) % (cd->p - n)) + n]) { + case m3dcp_mi_t: + out = _m3d_addidx(out, si_s, cmd->arg[k] < model->nummaterial ? + _m3d_stridx(str, numstr, model->material[cmd->arg[k]].name) : 0); + break; + case m3dcp_vc_t: + min_x = *((float*)&cmd->arg[k]); + switch(vc_s) { + case 1: *out++ = (int8_t)(min_x * 127); break; + case 2: *((int16_t*)out) = (int16_t)(min_x * 32767); out += 2; break; + case 4: *((float*)out) = min_x; out += 4; break; + case 8: *((double*)out) = min_x; out += 8; break; + } + break; + case m3dcp_hi_t: out = _m3d_addidx(out, hi_s, cmd->arg[k]); break; + case m3dcp_fi_t: out = _m3d_addidx(out, fi_s, cmd->arg[k]); break; + case m3dcp_ti_t: out = _m3d_addidx(out, ti_s, cmd->arg[k]); break; + case m3dcp_qi_t: + case m3dcp_vi_t: out = _m3d_addidx(out, vi_s, cmd->arg[k]); break; + case m3dcp_i1_t: out = _m3d_addidx(out, 1, cmd->arg[k]); break; + case m3dcp_i2_t: out = _m3d_addidx(out, 2, cmd->arg[k]); break; + case m3dcp_i4_t: out = _m3d_addidx(out, 4, cmd->arg[k]); break; + case m3dcp_va_t: out = _m3d_addidx(out, 4, cmd->arg[k]); + n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1); + break; + } + } + } + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + len += *length; + out = NULL; + } + } + /* annotation labels */ + if(model->numlabel && model->label) { + for(i = 0, length = NULL; i < model->numlabel; i++) { + if(!i || _m3d_strcmp(sl, model->label[i].lang) || _m3d_strcmp(sn, model->label[i].name)) { + sl = model->label[i].lang; + sn = model->label[i].name; + if(length) { + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + len += *length; + } + chunklen = 8 + 2 * si_s + ci_s + model->numlabel * (vi_s + si_s); + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) { sn = NULL; sl = NULL; goto memerr; } + memcpy((uint8_t*)h + len, "LBLS", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].name)); + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].lang)); + idx = _m3d_cmapidx(cmap, numcmap, model->label[i].color); + switch(ci_s) { + case 1: *out++ = (uint8_t)(idx); break; + case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; + case 4: *((uint32_t*)out) = model->label[i].color; out += 4; break; + } + } + out = _m3d_addidx(out, vi_s, vrtxidx[model->label[i].vertexid]); + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].text)); + } + if(length) { + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + len += *length; + } + out = NULL; + sn = sl = NULL; + } + /* actions */ + if(model->numaction && model->action && model->numbone && model->bone && !(flags & M3D_EXP_NOACTION)) { + for(j = 0; j < model->numaction; j++) { + a = &model->action[j]; + chunklen = 14 + si_s + a->numframe * (4 + fc_s + maxt * (bi_s + 2 * vi_s)); + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "ACTN", 4); + length = (uint32_t*)((uint8_t*)h + len + 4); + out = (uint8_t*)h + len + 8; + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, a->name)); + *((uint16_t*)out) = (uint16_t)(a->numframe); out += 2; + *((uint32_t*)out) = (uint32_t)(a->durationmsec); out += 4; + for(i = 0; i < a->numframe; i++) { + *((uint32_t*)out) = (uint32_t)(a->frame[i].msec); out += 4; + out = _m3d_addidx(out, fc_s, a->frame[i].numtransform); + for(k = 0; k < a->frame[i].numtransform; k++) { + out = _m3d_addidx(out, bi_s, a->frame[i].transform[k].boneid); + out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].pos]); + out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].ori]); + } + } + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); + len += *length; + out = NULL; + } + } + /* inlined assets */ + if(model->numinlined && model->inlined && (numproc || (flags & M3D_EXP_INLINE))) { + for(j = 0; j < model->numinlined; j++) { + if(!model->inlined[j].name || !model->inlined[j].name[0] || model->inlined[j].length<4 || !model->inlined[j].data) + continue; + if(!(flags & M3D_EXP_INLINE)) { + if(model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && model->inlined[j].data[3] == 'G') + continue; + for(i = k = 0; i < model->numtexture; i++) { + if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; } + } + if(k) continue; + } + chunklen = 8 + si_s + model->inlined[j].length; + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "ASET", 4); + *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen; + out = (uint8_t*)h + len + 8; + out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->inlined[j].name)); + memcpy(out, model->inlined[j].data, model->inlined[j].length); + out = NULL; + len += chunklen; + } + } + /* extra chunks */ + if(model->numextra && model->extra && (flags & M3D_EXP_EXTRA)) { + for(j = 0; j < model->numextra; j++) { + if(!model->extra[j] || model->extra[j]->length < 8) + continue; + chunklen = model->extra[j]->length; + h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, model->extra[j], chunklen); + len += chunklen; + } + } + /* add end chunk */ + h = (m3dhdr_t*)M3D_REALLOC(h, len + 4); + if(!h) goto memerr; + memcpy((uint8_t*)h + len, "OMD3", 4); + len += 4; + /* zlib compress */ + if(!(flags & M3D_EXP_NOZLIB)) { + M3D_LOG("Deflating chunks"); + z = stbi_zlib_compress((unsigned char *)h, len, (int*)&l, 9); + if(z && l > 0 && l < len) { len = l; M3D_FREE(h); h = (m3dhdr_t*)z; } + } + /* add file header at the begining */ + len += 8; + out = (unsigned char*)M3D_MALLOC(len); + if(!out) goto memerr; + memcpy(out, "3DMO", 4); + *((uint32_t*)(out + 4)) = len; + /* preview image chunk, must be the first if exists */ + if(model->preview.data && model->preview.length) { + chunklen = 8 + model->preview.length; + out = (unsigned char*)M3D_REALLOC(out, len + chunklen); + if(!out) goto memerr; + memcpy((uint8_t*)out + 8, "PRVW", 4); + *((uint32_t*)((uint8_t*)out + 8 + 4)) = chunklen; + memcpy((uint8_t*)out + 8 + 8, model->preview.data, model->preview.length); + *((uint32_t*)(out + 4)) += chunklen; + } else + chunklen = 0; + memcpy(out + 8 + chunklen, h, len - 8); + } + if(size) *size = out ? len : 0; + if(vrtxidx) M3D_FREE(vrtxidx); + if(mtrlidx) M3D_FREE(mtrlidx); + if(tmapidx) M3D_FREE(tmapidx); + if(skinidx) M3D_FREE(skinidx); + if(norm) M3D_FREE(norm); + if(face) M3D_FREE(face); + if(cmap) M3D_FREE(cmap); + if(tmap) M3D_FREE(tmap); + if(skin) M3D_FREE(skin); + if(str) M3D_FREE(str); + if(vrtx) M3D_FREE(vrtx); + if(h) M3D_FREE(h); + return out; +} +#endif + +#endif + +#ifdef __cplusplus +} +#ifdef M3D_CPPWRAPPER +#include +#include +#include + +/*** C++ wrapper class ***/ +namespace M3D { +#ifdef M3D_IMPLEMENTATION + + class Model { + public: + m3d_t *model; + + public: + Model() { + this->model = (m3d_t*)malloc(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t)); + } + Model(_unused const std::string &data, _unused m3dread_t ReadFileCB, + _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) { +#ifndef M3D_NOIMPORTER + this->model = m3d_load((unsigned char *)data.data(), ReadFileCB, FreeCB, mtllib.model); +#else + Model(); +#endif + } + Model(_unused const std::vector data, _unused m3dread_t ReadFileCB, + _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) { +#ifndef M3D_NOIMPORTER + this->model = m3d_load((unsigned char *)&data[0], ReadFileCB, FreeCB, mtllib.model); +#else + Model(); +#endif + } + Model(_unused const unsigned char *data, _unused m3dread_t ReadFileCB, + _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) { +#ifndef M3D_NOIMPORTER + this->model = m3d_load((unsigned char*)data, ReadFileCB, FreeCB, mtllib.model); +#else + Model(); +#endif + } + ~Model() { m3d_free(this->model); } + + public: + m3d_t *getCStruct() { return this->model; } + std::string getName() { return std::string(this->model->name); } + void setName(std::string name) { this->model->name = (char*)name.c_str(); } + std::string getLicense() { return std::string(this->model->license); } + void setLicense(std::string license) { this->model->license = (char*)license.c_str(); } + std::string getAuthor() { return std::string(this->model->author); } + void setAuthor(std::string author) { this->model->author = (char*)author.c_str(); } + std::string getDescription() { return std::string(this->model->desc); } + void setDescription(std::string desc) { this->model->desc = (char*)desc.c_str(); } + float getScale() { return this->model->scale; } + void setScale(float scale) { this->model->scale = scale; } + std::vector getPreview() { return this->model->preview.data ? + std::vector(this->model->preview.data, this->model->preview.data + this->model->preview.length) : + std::vector(); } + std::vector getColorMap() { return this->model->cmap ? std::vector(this->model->cmap, + this->model->cmap + this->model->numcmap) : std::vector(); } + std::vector getTextureMap() { return this->model->tmap ? std::vector(this->model->tmap, + this->model->tmap + this->model->numtmap) : std::vector(); } + std::vector getTextures() { return this->model->texture ? std::vector(this->model->texture, + this->model->texture + this->model->numtexture) : std::vector(); } + std::string getTextureName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numtexture ? + std::string(this->model->texture[idx].name) : nullptr; } + std::vector getBones() { return this->model->bone ? std::vector(this->model->bone, this->model->bone + + this->model->numbone) : std::vector(); } + std::string getBoneName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numbone ? + std::string(this->model->bone[idx].name) : nullptr; } + std::vector getMaterials() { return this->model->material ? std::vector(this->model->material, + this->model->material + this->model->nummaterial) : std::vector(); } + std::string getMaterialName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->nummaterial ? + std::string(this->model->material[idx].name) : nullptr; } + int getMaterialPropertyInt(int idx, int type) { + if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 0 || type >= 127 || + !this->model->material[idx].prop) return -1; + for (int i = 0; i < this->model->material[idx].numprop; i++) { + if (this->model->material[idx].prop[i].type == type) + return this->model->material[idx].prop[i].value.num; + } + return -1; + } + uint32_t getMaterialPropertyColor(int idx, int type) { return this->getMaterialPropertyInt(idx, type); } + float getMaterialPropertyFloat(int idx, int type) { + if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 0 || type >= 127 || + !this->model->material[idx].prop) return -1.0f; + for (int i = 0; i < this->model->material[idx].numprop; i++) { + if (this->model->material[idx].prop[i].type == type) + return this->model->material[idx].prop[i].value.fnum; + } + return -1.0f; + } + m3dtx_t* getMaterialPropertyMap(int idx, int type) { + if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 128 || type > 255 || + !this->model->material[idx].prop) return nullptr; + for (int i = 0; i < this->model->material[idx].numprop; i++) { + if (this->model->material[idx].prop[i].type == type) + return this->model->material[idx].prop[i].value.textureid < this->model->numtexture ? + &this->model->texture[this->model->material[idx].prop[i].value.textureid] : nullptr; + } + return nullptr; + } + std::vector getVertices() { return this->model->vertex ? std::vector(this->model->vertex, + this->model->vertex + this->model->numvertex) : std::vector(); } + std::vector getFace() { return this->model->face ? std::vector(this->model->face, this->model->face + + this->model->numface) : std::vector(); } + std::vector getVoxelTypes() { return this->model->voxtype ? std::vector(this->model->voxtype, + this->model->voxtype + this->model->numvoxtype) : std::vector(); } + std::string getVoxelTypeName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxtype && + this->model->voxtype[idx].name && this->model->voxtype[idx].name[0] ? + std::string(this->model->voxtype[idx].name) : nullptr; } + std::vector getVoxelTypeItems(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxtype && + this->model->voxtype[idx].item ? std::vector(this->model->voxtype[idx].item, + this->model->voxtype[idx].item + this->model->voxtype[idx].numitem) : std::vector(); } + std::vector getVoxelBlocks() { return this->model->voxel ? std::vector(this->model->voxel, + this->model->voxel + this->model->numvoxel) : std::vector(); } + std::string getVoxelBlockName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxel && + this->model->voxel[idx].name && this->model->voxel[idx].name[0] ? + std::string(this->model->voxel[idx].name) : nullptr; } + std::vector getVoxelBlockData(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxel && + this->model->voxel[idx].data ? std::vector(this->model->voxel[idx].data, + this->model->voxel[idx].data + this->model->voxel[idx].w*this->model->voxel[idx].h*this->model->voxel[idx].d) : + std::vector(); } + std::vector getShape() { return this->model->shape ? std::vector(this->model->shape, + this->model->shape + this->model->numshape) : std::vector(); } + std::string getShapeName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape && + this->model->shape[idx].name && this->model->shape[idx].name[0] ? + std::string(this->model->shape[idx].name) : nullptr; } + unsigned int getShapeGroup(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape ? + this->model->shape[idx].group : 0xFFFFFFFF; } + std::vector getShapeCommands(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape && + this->model->shape[idx].cmd ? std::vector(this->model->shape[idx].cmd, this->model->shape[idx].cmd + + this->model->shape[idx].numcmd) : std::vector(); } + std::vector getAnnotationLabels() { return this->model->label ? std::vector(this->model->label, + this->model->label + this->model->numlabel) : std::vector(); } + std::vector getSkin() { return this->model->skin ? std::vector(this->model->skin, this->model->skin + + this->model->numskin) : std::vector(); } + std::vector getActions() { return this->model->action ? std::vector(this->model->action, + this->model->action + this->model->numaction) : std::vector(); } + std::string getActionName(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? + std::string(this->model->action[aidx].name) : nullptr; } + unsigned int getActionDuration(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? + this->model->action[aidx].durationmsec : 0; } + std::vector getActionFrames(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? + std::vector(this->model->action[aidx].frame, this->model->action[aidx].frame + + this->model->action[aidx].numframe) : std::vector(); } + unsigned int getActionFrameTimestamp(int aidx, int fidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction? + (fidx >= 0 && (unsigned int)fidx < this->model->action[aidx].numframe ? + this->model->action[aidx].frame[fidx].msec : 0) : 0; } + std::vector getActionFrameTransforms(int aidx, int fidx) { + return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? ( + fidx >= 0 && (unsigned int)fidx < this->model->action[aidx].numframe ? + std::vector(this->model->action[aidx].frame[fidx].transform, + this->model->action[aidx].frame[fidx].transform + this->model->action[aidx].frame[fidx].numtransform) : + std::vector()) : std::vector(); } + std::vector getActionFrame(int aidx, int fidx, std::vector skeleton) { + m3dtr_t *pose = m3d_frame(this->model, (unsigned int)aidx, (unsigned int)fidx, + skeleton.size() ? &skeleton[0] : nullptr); + return std::vector(pose, pose + this->model->numbone); } + std::vector getActionPose(int aidx, unsigned int msec) { + m3db_t *pose = m3d_pose(this->model, (unsigned int)aidx, (unsigned int)msec); + return std::vector(pose, pose + this->model->numbone); } + std::vector getInlinedAssets() { return this->model->inlined ? std::vector(this->model->inlined, + this->model->inlined + this->model->numinlined) : std::vector(); } + std::vector> getExtras() { return this->model->extra ? + std::vector>(this->model->extra, + this->model->extra + this->model->numextra) : std::vector>(); } + std::vector Save(_unused int quality, _unused int flags) { +#ifdef M3D_EXPORTER + unsigned int size; + unsigned char *ptr = m3d_save(this->model, quality, flags, &size); + return ptr && size ? std::vector(ptr, ptr + size) : std::vector(); +#else + return std::vector(); +#endif + } + }; + +#else + class Model { + private: + m3d_t *model; + + public: + Model(const std::string &data, m3dread_t ReadFileCB, m3dfree_t FreeCB); + Model(const std::vector data, m3dread_t ReadFileCB, m3dfree_t FreeCB); + Model(const unsigned char *data, m3dread_t ReadFileCB, m3dfree_t FreeCB); + Model(); + ~Model(); + + public: + m3d_t *getCStruct(); + std::string getName(); + void setName(std::string name); + std::string getLicense(); + void setLicense(std::string license); + std::string getAuthor(); + void setAuthor(std::string author); + std::string getDescription(); + void setDescription(std::string desc); + float getScale(); + void setScale(float scale); + std::vector getPreview(); + std::vector getColorMap(); + std::vector getTextureMap(); + std::vector getTextures(); + std::string getTextureName(int idx); + std::vector getBones(); + std::string getBoneName(int idx); + std::vector getMaterials(); + std::string getMaterialName(int idx); + int getMaterialPropertyInt(int idx, int type); + uint32_t getMaterialPropertyColor(int idx, int type); + float getMaterialPropertyFloat(int idx, int type); + m3dtx_t* getMaterialPropertyMap(int idx, int type); + std::vector getVertices(); + std::vector getFace(); + std::vector getVoxelTypes(); + std::string getVoxelTypeName(int idx); + std::vector getVoxelTypeItems(int idx); + std::vector getVoxelBlocks(); + std::string getVoxelBlockName(int idx); + std::vector getVoxelBlockData(int idx); + std::vector getShape(); + std::string getShapeName(int idx); + unsigned int getShapeGroup(int idx); + std::vector getShapeCommands(int idx); + std::vector getAnnotationLabels(); + std::vector getSkin(); + std::vector getActions(); + std::string getActionName(int aidx); + unsigned int getActionDuration(int aidx); + std::vector getActionFrames(int aidx); + unsigned int getActionFrameTimestamp(int aidx, int fidx); + std::vector getActionFrameTransforms(int aidx, int fidx); + std::vector getActionFrame(int aidx, int fidx, std::vector skeleton); + std::vector getActionPose(int aidx, unsigned int msec); + std::vector getInlinedAssets(); + std::vector> getExtras(); + std::vector Save(int quality, int flags); + }; + +#endif /* impl */ +} +#endif + +#endif /* __cplusplus */ + +#endif diff --git a/src/rmodels.c b/src/rmodels.c index ee8b2117b..7b2b19a27 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -12,6 +12,7 @@ * #define SUPPORT_FILEFORMAT_IQM * #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 @@ -86,6 +87,19 @@ #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 + + // Let the M3D loader know about stb_image is used in this project, + // to allow it to use on textures loading + #include "external/stb_image.h" + + #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)) @@ -141,6 +155,9 @@ static Model LoadGLTF(const char *fileName); // Load GLTF mesh data #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 +#endif //---------------------------------------------------------------------------------- // Module Functions Definition @@ -5101,4 +5118,156 @@ static Model LoadVOX(const char *fileName) } #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, 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; + unsigned int bytesRead = 0; + unsigned char *fileData = LoadFileData(fileName, &bytesRead); + int i, j, k, l, mi = -2; + + if (fileData != NULL) + { + m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL); + + if (!m3d || (m3d->errcode != M3D_SUCCESS)) + { + TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data", fileName); + return model; + } + else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i faces/%i materials", fileName, m3d->numface, m3d->nummaterial); + + if (m3d->nummaterial > 0) + { + model.meshCount = model.materialCount = m3d->nummaterial; + TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", model.materialCount); + } + else + { + model.meshCount = model.materialCount = 1; + TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); + } + + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); + model.materials = (Material *)RL_CALLOC(model.meshCount + 1, sizeof(Material)); + + // Map no material to index 0 with default shader, everything else materialid + 1 + model.materials[0] = LoadMaterialDefault(); + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + for (i = l = 0, k = -1; i < m3d->numface; i++, l++) + { + // Materials are grouped together + if (mi != m3d->face[i].materialid) + { + k++; + mi = m3d->face[i].materialid; + + for (j = i, l = 0; (j < m3d->numface) && (mi == m3d->face[j].materialid); j++, l++); + + 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)); + 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; + model.meshes[k].vertices[l * 9 + 1] = m3d->vertex[m3d->face[i].vertex[0]].y; + model.meshes[k].vertices[l * 9 + 2] = m3d->vertex[m3d->face[i].vertex[0]].z; + model.meshes[k].vertices[l * 9 + 3] = m3d->vertex[m3d->face[i].vertex[1]].x; + model.meshes[k].vertices[l * 9 + 4] = m3d->vertex[m3d->face[i].vertex[1]].y; + model.meshes[k].vertices[l * 9 + 5] = m3d->vertex[m3d->face[i].vertex[1]].z; + model.meshes[k].vertices[l * 9 + 6] = m3d->vertex[m3d->face[i].vertex[2]].x; + model.meshes[k].vertices[l * 9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y; + model.meshes[k].vertices[l * 9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z; + + 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] = 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] = 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] = 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; + } + } + + for (i = 0; i < m3d->nummaterial; i++) + { + model.materials[i + 1] = LoadMaterialDefault(); + model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + 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); + model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; + } 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: break; + } + } + } + + m3d_free(m3d); + UnloadFileData(fileData); + } + + return model; +} +#endif + #endif // SUPPORT_MODULE_RMODELS From e835311d0d496692a1ae25c6b37c3141d4e10362 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 21 Aug 2022 05:43:27 -0400 Subject: [PATCH 0010/1710] BINDINGS: raylib-cpp has support for raylib 4.2 (#2652) https://github.com/robloach/raylib-cpp --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 98ad5afab..0aac24cff 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -64,7 +64,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. | name | raylib version | language | license | repo | |:------------------:|:-------------: | :--------:|:-------:|:-------------------------------------------------------------| -| raylib-cpp | **4.0** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | +| raylib-cpp | **4.2** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | ### Older or Unmaintained Language Bindings These are older raylib bindings that are more than 2 versions old or have not been maintained. From 7bb8ffc29e5c31edf2875ac7aaca38316cfe45e5 Mon Sep 17 00:00:00 2001 From: Daijiro Fukuda Date: Sun, 21 Aug 2022 18:44:16 +0900 Subject: [PATCH 0011/1710] Win32: resolve some symbols re-definition of windows.h in glfw3native.h (#2643) * Win32: resolve some symbols re-definition of windows.h in glfw3native.h This reflects GLFW's fix: https://github.com/glfw/glfw/issues/1348 This enables to build with a external GLFW containing the following fix: * https://github.com/glfw/glfw/commit/05f6c13d119ea2662c97527d2421fb4cffd3dbfc Currently, glfw3native.h of the internal GLFW is customized at https://github.com/raysan5/raylib/commit/2feea87b616292b5bce4454a42c2d048f1cce7d8 This fix is compatible with the current customized glfw3native.h. This fix enables us to update it to the latest and remove the customization. * Win32: remove unneeded typedef --- src/external/glfw/include/GLFW/glfw3native.h | 3 --- src/rcore.c | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/external/glfw/include/GLFW/glfw3native.h b/src/external/glfw/include/GLFW/glfw3native.h index f67f54025..323d3c77e 100644 --- a/src/external/glfw/include/GLFW/glfw3native.h +++ b/src/external/glfw/include/GLFW/glfw3native.h @@ -92,9 +92,6 @@ extern "C" { // @raysan5: Actually, only HWND handler needs to be defined // Including windows.h could suppose symbols re-definition issues (i.e Rectangle type) //#include - typedef void *PVOID; - typedef PVOID HANDLE; - typedef HANDLE HWND; #elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) #if defined(__OBJC__) #import diff --git a/src/rcore.c b/src/rcore.c index d90acf6ce..c15022230 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -214,8 +214,12 @@ // Support retrieving native window handlers #if defined(_WIN32) + typedef void *PVOID; + typedef PVOID HANDLE; + typedef HANDLE HWND; #define GLFW_EXPOSE_NATIVE_WIN32 - #include "GLFW/glfw3native.h" // WARNING: It requires customization to avoid windows.h inclusion! + #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 From aa4111a3b2706c3683cd55f9ef0ff0d8ce93a55d Mon Sep 17 00:00:00 2001 From: Rodrigo Escar <2019446+futureapricot@users.noreply.github.com> Date: Sun, 21 Aug 2022 05:45:37 -0400 Subject: [PATCH 0012/1710] Fix PATH for Web target (#2647) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index c03278fbe..1fa63ba65 100644 --- a/src/Makefile +++ b/src/Makefile @@ -187,7 +187,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) CLANG_PATH = $(EMSDK_PATH)/upstream/bin PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-1_64bit NODE_PATH = $(EMSDK_PATH)/node/14.15.5_64bit/bin - export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH) + export PATH := $(EMSDK_PATH):$(EMSCRIPTEN_PATH):$(CLANG_PATH):$(NODE_PATH):$(PYTHON_PATH):C:\raylib\MinGW\bin:$(PATH) endif ifeq ($(PLATFORM),PLATFORM_ANDROID) From e92bc8ca4eaa12ba8068e84a4195073885e132ff Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 Aug 2022 11:11:05 +0200 Subject: [PATCH 0013/1710] REVIEWED: M3D implementation #2648 --- src/raylib.h | 1 + src/rmodels.c | 75 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 8a5d50c26..0746ca2a3 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -43,6 +43,7 @@ * [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) * [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 diff --git a/src/rmodels.c b/src/rmodels.c index 7b2b19a27..37bc95f0f 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -91,8 +91,8 @@ #define M3D_MALLOC RL_MALLOC #define M3D_REALLOC RL_REALLOC #define M3D_FREE RL_FREE - - // Let the M3D loader know about stb_image is used in this project, + + // Let the M3D loader know about stb_image is used in this project, // to allow it to use on textures loading #include "external/stb_image.h" @@ -944,6 +944,9 @@ Model LoadModel(const char *fileName) #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(); @@ -5142,14 +5145,14 @@ static Model LoadM3D(const char *fileName) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data", fileName); return model; - } + } else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i faces/%i materials", fileName, m3d->numface, m3d->nummaterial); if (m3d->nummaterial > 0) { model.meshCount = model.materialCount = m3d->nummaterial; TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", model.materialCount); - } + } else { model.meshCount = model.materialCount = 1; @@ -5159,21 +5162,21 @@ static Model LoadM3D(const char *fileName) model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); model.materials = (Material *)RL_CALLOC(model.meshCount + 1, sizeof(Material)); - + // Map no material to index 0 with default shader, everything else materialid + 1 model.materials[0] = LoadMaterialDefault(); model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - for (i = l = 0, k = -1; i < m3d->numface; i++, l++) + for (i = l = 0, k = -1; i < m3d->numface; i++, l++) { // Materials are grouped together if (mi != m3d->face[i].materialid) { k++; mi = m3d->face[i].materialid; - + for (j = i, l = 0; (j < m3d->numface) && (mi == m3d->face[j].materialid); j++, l++); - + 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)); @@ -5182,18 +5185,18 @@ static Model LoadM3D(const char *fileName) 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; - model.meshes[k].vertices[l * 9 + 1] = m3d->vertex[m3d->face[i].vertex[0]].y; - model.meshes[k].vertices[l * 9 + 2] = m3d->vertex[m3d->face[i].vertex[0]].z; - model.meshes[k].vertices[l * 9 + 3] = m3d->vertex[m3d->face[i].vertex[1]].x; - model.meshes[k].vertices[l * 9 + 4] = m3d->vertex[m3d->face[i].vertex[1]].y; - model.meshes[k].vertices[l * 9 + 5] = m3d->vertex[m3d->face[i].vertex[1]].z; - model.meshes[k].vertices[l * 9 + 6] = m3d->vertex[m3d->face[i].vertex[2]].x; - model.meshes[k].vertices[l * 9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y; - model.meshes[k].vertices[l * 9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z; - + 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; + if (m3d->face[i].texcoord[0] != M3D_UNDEF) { model.meshes[k].texcoords[l * 6 + 0] = m3d->tmap[m3d->face[i].texcoord[0]].u; @@ -5203,7 +5206,7 @@ static Model LoadM3D(const char *fileName) model.meshes[k].texcoords[l * 6 + 4] = m3d->tmap[m3d->face[i].texcoord[2]].u; model.meshes[k].texcoords[l * 6 + 5] = 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; @@ -5222,11 +5225,11 @@ static Model LoadM3D(const char *fileName) { model.materials[i + 1] = LoadMaterialDefault(); model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - + for (j = 0; j < m3d->material[i].numprop; j++) { prop = &m3d->material[i].prop[j]; - + switch (prop->type) { case m3dp_Kd: @@ -5239,6 +5242,10 @@ static Model LoadM3D(const char *fileName) memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].color, &prop->value.color, 4); model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; } 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); @@ -5257,7 +5264,29 @@ static Model LoadM3D(const char *fileName) model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].color = WHITE; model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].value = prop->value.fnum; } break; - default: 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; + default: break; + } + } + } break; } } } From ae745e4fa8f25e16a2e6964b417454e8a35a61b7 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 Aug 2022 11:25:35 +0200 Subject: [PATCH 0014/1710] ADDED: `-latomic` library on Linux (only required for ARM32) This linkage is only required for arm 32bit but I don't know how to detect that specific architecture in the Makefile... --- examples/Makefile | 3 +++ src/Makefile | 2 ++ 2 files changed, 5 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index a977412ed..6a39a1c5b 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -325,6 +325,9 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(RAYLIB_LIBTYPE),SHARED) LDLIBS += -lc endif + + # TODO: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic endif ifeq ($(PLATFORM_OS),OSX) # Libraries for OSX 10.9 desktop compiling diff --git a/src/Makefile b/src/Makefile index 1fa63ba65..ea3376046 100644 --- a/src/Makefile +++ b/src/Makefile @@ -504,6 +504,8 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(USE_WAYLAND_DISPLAY),FALSE) LDLIBS += -lX11 endif + # TODO: On ARM 32bit arch, miniaudio requires atomics library + #LDLIBS += -latomic endif ifeq ($(PLATFORM_OS),OSX) LDLIBS = -framework OpenGL -framework Cocoa -framework IOKit -framework CoreAudio -framework CoreVideo From f66b1a313650051cfaa9c38b1117944630b35d5a Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 26 Aug 2022 10:04:38 +0200 Subject: [PATCH 0015/1710] REVIEWED: Support M3D file loading #2648 --- examples/Makefile | 1 + examples/models/models_loading.c | 5 +- examples/models/models_loading_m3d.c | 127 ++++++++++++++++ src/external/m3d.h | 23 ++- src/rmodels.c | 211 ++++++++++++++++++++++++--- 5 files changed, 337 insertions(+), 30 deletions(-) create mode 100644 examples/models/models_loading_m3d.c diff --git a/examples/Makefile b/examples/Makefile index 6a39a1c5b..b0bf42524 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -466,6 +466,7 @@ MODELS = \ models/models_loading \ models/models_loading_vox \ models/models_loading_gltf \ + models/models_loading_m3d \ models/models_orthographic_projection \ models/models_rlgl_solar_system \ models/models_skybox \ diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c index bb3b490fc..d40661412 100644 --- a/examples/models/models_loading.c +++ b/examples/models/models_loading.c @@ -12,6 +12,8 @@ * 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: +* https://bztsrc.gitlab.io/model3d * * Example originally created with raylib 2.0, last time updated with raylib 4.2 * @@ -80,7 +82,8 @@ int main(void) IsFileExtension(droppedFiles.paths[0], ".gltf") || IsFileExtension(droppedFiles.paths[0], ".glb") || IsFileExtension(droppedFiles.paths[0], ".vox") || - IsFileExtension(droppedFiles.paths[0], ".iqm")) // Model file formats supported + IsFileExtension(droppedFiles.paths[0], ".iqm") || + IsFileExtension(droppedFiles.paths[0], ".m3d")) // Model file formats supported { UnloadModel(model); // Unload previous model model = LoadModel(droppedFiles.paths[0]); // Load new model diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c new file mode 100644 index 000000000..ca77d360a --- /dev/null +++ b/examples/models/models_loading_m3d.c @@ -0,0 +1,127 @@ +/******************************************************************************************* +* +* raylib [models] example - Load M3D model (with optional animations) and play them +* +* Example static mesh Suzanne from Blender +* Example animated seagull model from Scorched 3D, licensed GPLv2 +* +* Copyright (c) 2019-2022 Culacant (@culacant) and Ramon Santamaria (@raysan5) +* Copyright (c) 2022 bzt (@bztsrc) +* +******************************************************************************************** +* +* NOTE: To export a model from blender, just use https://gitlab.com/bztsrc/model3d/-/tree/master/blender +* and make sure to add "(action)" markers to the timeline if you want multiple animations. +* +********************************************************************************************/ + +#include "raylib.h" + +#include + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(int argc, char **argv) +{ + char *model_fn = argc > 1 ? argv[1] : "resources/models/m3d/suzanne.m3d"; + + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - M3D model"); + + // 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 + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + + // Load model + Model model = LoadModel(model_fn); // Load the animated model mesh and basic data + + // Load animation data + unsigned int animsCount = 0; + ModelAnimation *anims = LoadModelAnimations(model_fn, &animsCount); + int animFrameCounter = 0, animId = 0; + + SetCameraMode(camera, CAMERA_FREE); // Set free camera mode + + 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); + + // Play animation when spacebar is held down + if (animsCount) + { + if (IsKeyDown(KEY_SPACE)) + { + animFrameCounter++; + UpdateModelAnimation(model, anims[animId], animFrameCounter); + if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0; + } + + // Select animation on mouse click + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) + { + animFrameCounter = 0; + animId++; + if (animId >= animsCount) animId = 0; + UpdateModelAnimation(model, anims[animId], 0); + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture + if(anims) + for (int i = 0; i < model.boneCount; i++) + { + DrawCube(anims[animId].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, GetScreenHeight() - 30, 10, MAROON); + DrawText("MOUSE LEFT BUTTON to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 20, 10, DARKGRAY); + DrawText("(c) Suzanne 3D model by blender", screenWidth - 200, screenHeight - 20, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + // Unload model animations data + for (unsigned int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); + RL_FREE(anims); + + UnloadModel(model); // Unload model + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/src/external/m3d.h b/src/external/m3d.h index 0dacb6e09..3b29927d4 100644 --- a/src/external/m3d.h +++ b/src/external/m3d.h @@ -1865,6 +1865,9 @@ static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, #define stbi__png_load _m3dstbi__png_load #define stbi_zlib_decode_malloc_guesssize_headerflag _m3dstbi_zlib_decode_malloc_guesssize_headerflag #endif +#if !defined(M3D_NOIMPORTER) && defined(STBI_INCLUDE_STB_IMAGE_H) && !defined(STB_IMAGE_IMPLEMENTATION) +#error "stb_image.h included without STB_IMAGE_IMPLEMENTATION. Sorry, we need some stuff defined inside the ifguard for proper integration" +#endif #if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) /* zlib_compressor from @@ -2165,11 +2168,9 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char unsigned int i, len = 0; unsigned char *buff = NULL; char *fn2; -#ifdef STBI__PNG_TYPE unsigned int w, h; stbi__context s; stbi__result_info ri; -#endif /* do we have loaded this texture already? */ for(i = 0; i < model->numtexture; i++) @@ -2212,7 +2213,6 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL; if(buff) { if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { -#ifdef STBI__PNG_TYPE s.read_from_callbacks = 0; s.img_buffer = s.img_buffer_original = (unsigned char *) buff; s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; @@ -2223,7 +2223,6 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char model->texture[i].w = w; model->texture[i].h = h; model->texture[i].f = (uint8_t)len; -#endif } else { #ifdef M3D_TX_INTERP if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { @@ -3225,7 +3224,7 @@ asciiend: /* parse header */ data += sizeof(m3dhdr_t); - M3D_LOG(data); + M3D_LOG((char*)data); model->name = (char*)data; for(; data < end && *data; data++) {}; data++; model->license = (char*)data; @@ -3264,12 +3263,12 @@ asciiend: } if((sizeof(M3D_INDEX) == 2 && (model->vi_s > 2 || model->si_s > 2 || model->ci_s > 2 || model->ti_s > 2 || model->bi_s > 2 || model->sk_s > 2 || model->fc_s > 2 || model->hi_s > 2 || model->fi_s > 2)) || - (sizeof(M3D_VOXEL) == 2 && model->vp_s > 2)) { + (sizeof(M3D_VOXEL) < (size_t)model->vp_s && model->vp_s != 8)) { M3D_LOG("32 bit indices not supported, unable to load model"); M3D_FREE(model); return NULL; } - if(model->vi_s > 4 || model->si_s > 4) { + if(model->vi_s > 4 || model->si_s > 4 || model->vp_s == 4) { M3D_LOG("Invalid index size, unable to load model"); M3D_FREE(model); return NULL; @@ -3346,12 +3345,12 @@ memerr: M3D_LOG("Out of memory"); for(i = 0, data += sizeof(m3dchunk_t); data < chunk; i++) { switch(model->vc_s) { case 1: - model->tmap[i].u = (M3D_FLOAT)(data[0]) / (M3D_FLOAT)255.0; - model->tmap[i].v = (M3D_FLOAT)(data[1]) / (M3D_FLOAT)255.0; + model->tmap[i].u = (M3D_FLOAT)((uint8_t)data[0]) / (M3D_FLOAT)255.0; + model->tmap[i].v = (M3D_FLOAT)((uint8_t)data[1]) / (M3D_FLOAT)255.0; break; case 2: - model->tmap[i].u = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)65535.0; - model->tmap[i].v = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)65535.0; + model->tmap[i].u = (M3D_FLOAT)(*((uint16_t*)(data+0))) / (M3D_FLOAT)65535.0; + model->tmap[i].v = (M3D_FLOAT)(*((uint16_t*)(data+2))) / (M3D_FLOAT)65535.0; break; case 4: model->tmap[i].u = (M3D_FLOAT)(*((float*)(data+0))); @@ -5219,7 +5218,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); if(model->preview.data && model->preview.length) { sl = _m3d_safestr(sn, 0); if(sl) { - ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)20); + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)20 + strlen(sl)); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Preview\r\n%s.png\r\n\r\n", sl); diff --git a/src/rmodels.c b/src/rmodels.c index 37bc95f0f..ec9e7adf8 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -92,10 +92,6 @@ #define M3D_REALLOC RL_REALLOC #define M3D_FREE RL_FREE - // Let the M3D loader know about stb_image is used in this project, - // to allow it to use on textures loading - #include "external/stb_image.h" - #define M3D_IMPLEMENTATION #include "external/m3d.h" // Model3D file format loading #endif @@ -157,6 +153,7 @@ 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, unsigned int *animCount); // Load M3D animation data #endif //---------------------------------------------------------------------------------- @@ -1862,6 +1859,9 @@ ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCoun #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 = LoadModelAnimationGLTF(fileName, animCount); #endif @@ -5135,19 +5135,29 @@ static Model LoadM3D(const char *fileName) m3dp_t *prop = NULL; unsigned int bytesRead = 0; unsigned char *fileData = LoadFileData(fileName, &bytesRead); - int i, j, k, l, mi = -2; + int i, j, k, l, n, mi = -2; if (fileData != NULL) { m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL); - if (!m3d || (m3d->errcode != M3D_SUCCESS)) + if (!m3d || M3D_ERR_ISFATAL(m3d->errcode)) { - TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data", fileName); + 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; @@ -5161,17 +5171,25 @@ static Model LoadM3D(const char *fileName) model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); - model.materials = (Material *)RL_CALLOC(model.meshCount + 1, sizeof(Material)); + 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(); - model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; for (i = l = 0, k = -1; i < 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 unoptimal 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; @@ -5182,6 +5200,19 @@ static Model LoadM3D(const char *fileName) 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)); + // without material, we rely on vertex colors + if (mi == M3D_UNDEF && model.meshes[k].colors == NULL) + { + model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); + for (j = 0; j < model.meshes[k].vertexCount*4; j += 4) memcpy(&model.meshes[k].colors[j], &WHITE, 4); + } + 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; } @@ -5197,14 +5228,25 @@ static Model LoadM3D(const char *fileName) 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; + if (mi == M3D_UNDEF) + { + // without vertex color (full transparency), we use the default color + 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] = m3d->tmap[m3d->face[i].texcoord[0]].v; + model.meshes[k].texcoords[l * 6 + 1] = 1.0 - 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] = m3d->tmap[m3d->face[i].texcoord[1]].v; + model.meshes[k].texcoords[l * 6 + 3] = 1.0 - 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] = m3d->tmap[m3d->face[i].texcoord[2]].v; + model.meshes[k].texcoords[l * 6 + 5] = 1.0 - m3d->tmap[m3d->face[i].texcoord[2]].v; } if (m3d->face[i].normal[0] != M3D_UNDEF) @@ -5219,12 +5261,28 @@ static Model LoadM3D(const char *fileName) 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's a skin for this mesh, should be, just failsafe + if (skinid != M3D_UNDEF && skinid < 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]; + } + } + } + } } + // Load materials for (i = 0; i < m3d->nummaterial; i++) { model.materials[i + 1] = LoadMaterialDefault(); - model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; for (j = 0; j < m3d->material[i].numprop; j++) { @@ -5240,7 +5298,6 @@ static Model LoadM3D(const char *fileName) case m3dp_Ks: { memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].color, &prop->value.color, 4); - model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; } break; case m3dp_Ns: { @@ -5270,11 +5327,11 @@ static Model LoadM3D(const char *fileName) { Image image = { 0 }; image.data = m3d->texture[prop->value.textureid].d; - image.width = m3d->texture[prop->value.textureid].w; + 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 : + 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) @@ -5283,6 +5340,8 @@ static Model LoadM3D(const char *fileName) 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; } } @@ -5291,12 +5350,130 @@ static Model LoadM3D(const char *fileName) } } + // Load bones + if(m3d->numbone) + { + model.boneCount = m3d->numbone; + model.bones = RL_MALLOC(m3d->numbone*sizeof(BoneInfo)); + model.bindPose = RL_MALLOC(m3d->numbone*sizeof(Transform)); + for (i = 0; i < 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; + model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y; + model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z; + 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 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; + } + } + + // 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 exists (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)); + } + } + m3d_free(m3d); UnloadFileData(fileData); } return model; } + +// Load M3D animation data +#define M3D_ANIMDELAY 17 // that's roughly ~1000 msec / 60 FPS (16.666666* msec) +static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount) +{ + m3d_t *m3d = NULL; + unsigned int bytesRead = 0; + unsigned char *fileData = LoadFileData(fileName, &bytesRead); + ModelAnimation *animations = NULL; + int i, j; + + *animCount = 0; + + 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; + animations[a].bones = RL_MALLOC(m3d->numbone*sizeof(BoneInfo)); + animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *)); + // strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name)); + TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount); + + for (i = 0; i < 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)); + } + + // 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*sizeof(Transform)); + + m3db_t *pose = m3d_pose(m3d, a, i * M3D_ANIMDELAY); + if (pose != NULL) + { + for (j = 0; j < m3d->numbone; j++) + { + animations[a].framePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x; + animations[a].framePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y; + animations[a].framePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z; + animations[a].framePoses[i][j].rotation.x = m3d->vertex[pose[j].ori].x; + animations[a].framePoses[i][j].rotation.y = m3d->vertex[pose[j].ori].y; + animations[a].framePoses[i][j].rotation.z = m3d->vertex[pose[j].ori].z; + animations[a].framePoses[i][j].rotation.w = m3d->vertex[pose[j].ori].w; + animations[a].framePoses[i][j].rotation = QuaternionNormalize(animations[a].framePoses[i][j].rotation); + animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f; + } + RL_FREE(pose); + } + } + } + + m3d_free(m3d); + UnloadFileData(fileData); + } + + return animations; +} #endif #endif // SUPPORT_MODULE_RMODELS From 99948a86e18c7d26f055f7af4944f408a5280079 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Sat, 27 Aug 2022 18:07:30 +0200 Subject: [PATCH 0016/1710] Update raylib-go bindings (#2665) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 0aac24cff..958fd88e0 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -20,7 +20,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.2** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | -| raylib-go | **4.0** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | +| raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | | raylib-hx | 4.0 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | From 8508ae3d153159bea374dcfaa08b45095776bd2b Mon Sep 17 00:00:00 2001 From: Dmitry Matveyev Date: Sun, 28 Aug 2022 17:35:39 +0600 Subject: [PATCH 0017/1710] Update NimraylibNow! bindings version (#2667) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 958fd88e0..e6d167596 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -32,7 +32,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-lua | **4.0** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | **4.0** | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | -| NimraylibNow! | 4.0 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | +| NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.0** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | From 9e0e08cba495cb8799171c113b55591b46827459 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Aug 2022 14:16:51 +0200 Subject: [PATCH 0018/1710] WARNING: UPDATED GLFW to latest master branch! WARNING: This could be a BREAKING CHANGE for some platforms! I'm afraid something could be wrong on `rglfw.c` module. To be able to compile on Windows I had to modify `glfw/src/platform.c` line 74. I couldn't manage to compile without that change, help is welcome! --- .../glfw/CMake/GenerateMappings.cmake | 19 +- .../glfw/CMake/MacOSXBundleInfo.plist.in | 38 - src/external/glfw/CMake/glfw3.pc.in | 4 +- .../glfw/CMake/modules/FindEpollShim.cmake | 2 +- .../CMake/modules/FindWaylandProtocols.cmake | 26 - .../glfw/CMake/modules/FindXKBCommon.cmake | 34 - src/external/glfw/CMakeLists.txt | 228 +- src/external/glfw/CONTRIBUTORS.md | 262 ++ src/external/glfw/README.md | 411 +- src/external/glfw/deps/glad/gl.h | 3676 +++++++++++++---- src/external/glfw/deps/glad/gles2.h | 1805 ++++++++ src/external/glfw/deps/glad/khrplatform.h | 282 -- src/external/glfw/deps/glad/vk_platform.h | 92 - src/external/glfw/deps/glad/vulkan.h | 3554 ++++++++++++++-- src/external/glfw/deps/glad_gl.c | 1791 -------- src/external/glfw/deps/glad_vulkan.c | 593 --- src/external/glfw/include/GLFW/glfw3.h | 492 ++- src/external/glfw/include/GLFW/glfw3native.h | 185 +- src/external/glfw/src/CMakeLists.txt | 264 +- src/external/glfw/src/cocoa_init.m | 156 +- src/external/glfw/src/cocoa_joystick.h | 13 +- src/external/glfw/src/cocoa_joystick.m | 65 +- src/external/glfw/src/cocoa_monitor.m | 62 +- src/external/glfw/src/cocoa_platform.h | 158 +- src/external/glfw/src/cocoa_time.c | 11 +- src/external/glfw/src/cocoa_time.h | 35 + src/external/glfw/src/cocoa_window.m | 446 +- src/external/glfw/src/context.c | 31 +- src/external/glfw/src/egl_context.c | 150 +- src/external/glfw/src/egl_context.h | 229 - src/external/glfw/src/glfw_config.h.in | 58 - src/external/glfw/src/glx_context.c | 137 +- src/external/glfw/src/glx_context.h | 181 - src/external/glfw/src/init.c | 260 +- src/external/glfw/src/input.c | 382 +- src/external/glfw/src/internal.h | 585 ++- src/external/glfw/src/linux_joystick.c | 27 +- src/external/glfw/src/linux_joystick.h | 12 +- src/external/glfw/src/mappings.h | 691 +--- src/external/glfw/src/mappings.h.in | 15 +- src/external/glfw/src/monitor.c | 64 +- src/external/glfw/src/nsgl_context.h | 66 - src/external/glfw/src/nsgl_context.m | 53 +- src/external/glfw/src/null_init.c | 94 +- src/external/glfw/src/null_joystick.c | 13 +- src/external/glfw/src/null_joystick.h | 9 +- src/external/glfw/src/null_monitor.c | 28 +- src/external/glfw/src/null_platform.h | 102 +- src/external/glfw/src/null_window.c | 252 +- src/external/glfw/src/osmesa_context.c | 64 +- src/external/glfw/src/osmesa_context.h | 90 - src/external/glfw/src/platform.c | 189 + src/external/glfw/src/platform.h | 163 + src/external/glfw/src/posix_module.c | 51 + src/external/glfw/src/posix_poll.c | 81 + src/external/glfw/src/posix_poll.h | 32 + src/external/glfw/src/posix_thread.h | 6 +- src/external/glfw/src/posix_time.c | 57 +- src/external/glfw/src/posix_time.h | 9 +- src/external/glfw/src/vulkan.c | 96 +- src/external/glfw/src/wgl_context.c | 244 +- src/external/glfw/src/wgl_context.h | 160 - src/external/glfw/src/win32_init.c | 283 +- src/external/glfw/src/win32_joystick.c | 47 +- src/external/glfw/src/win32_joystick.h | 6 +- src/external/glfw/src/win32_module.c | 49 + src/external/glfw/src/win32_monitor.c | 60 +- src/external/glfw/src/win32_platform.h | 290 +- src/external/glfw/src/win32_thread.c | 3 +- src/external/glfw/src/win32_thread.h | 48 + src/external/glfw/src/win32_time.c | 39 +- src/external/glfw/src/win32_time.h | 38 + src/external/glfw/src/win32_window.c | 681 +-- src/external/glfw/src/window.c | 251 +- src/external/glfw/src/wl_init.c | 1201 ++---- src/external/glfw/src/wl_monitor.c | 111 +- src/external/glfw/src/wl_platform.h | 309 +- src/external/glfw/src/wl_window.c | 2409 ++++++++--- src/external/glfw/src/x11_init.c | 788 ++-- src/external/glfw/src/x11_monitor.c | 32 +- src/external/glfw/src/x11_platform.h | 260 +- src/external/glfw/src/x11_window.c | 795 ++-- src/external/glfw/src/xkb_unicode.c | 4 +- src/external/glfw/src/xkb_unicode.h | 4 +- src/rglfw.c | 48 +- 85 files changed, 16869 insertions(+), 10272 deletions(-) delete mode 100644 src/external/glfw/CMake/MacOSXBundleInfo.plist.in delete mode 100644 src/external/glfw/CMake/modules/FindWaylandProtocols.cmake delete mode 100644 src/external/glfw/CMake/modules/FindXKBCommon.cmake create mode 100644 src/external/glfw/CONTRIBUTORS.md create mode 100644 src/external/glfw/deps/glad/gles2.h delete mode 100644 src/external/glfw/deps/glad/khrplatform.h delete mode 100644 src/external/glfw/deps/glad/vk_platform.h delete mode 100644 src/external/glfw/deps/glad_gl.c delete mode 100644 src/external/glfw/deps/glad_vulkan.c create mode 100644 src/external/glfw/src/cocoa_time.h delete mode 100644 src/external/glfw/src/egl_context.h delete mode 100644 src/external/glfw/src/glfw_config.h.in delete mode 100644 src/external/glfw/src/glx_context.h delete mode 100644 src/external/glfw/src/nsgl_context.h delete mode 100644 src/external/glfw/src/osmesa_context.h create mode 100644 src/external/glfw/src/platform.c create mode 100644 src/external/glfw/src/platform.h create mode 100644 src/external/glfw/src/posix_module.c create mode 100644 src/external/glfw/src/posix_poll.c create mode 100644 src/external/glfw/src/posix_poll.h delete mode 100644 src/external/glfw/src/wgl_context.h create mode 100644 src/external/glfw/src/win32_module.c create mode 100644 src/external/glfw/src/win32_thread.h create mode 100644 src/external/glfw/src/win32_time.h diff --git a/src/external/glfw/CMake/GenerateMappings.cmake b/src/external/glfw/CMake/GenerateMappings.cmake index 7a88e3d44..c8c9e23f2 100644 --- a/src/external/glfw/CMake/GenerateMappings.cmake +++ b/src/external/glfw/CMake/GenerateMappings.cmake @@ -23,8 +23,23 @@ endif() file(STRINGS "${source_path}" lines) foreach(line ${lines}) - if ("${line}" MATCHES "^[0-9a-fA-F].*$") - set(GLFW_GAMEPAD_MAPPINGS "${GLFW_GAMEPAD_MAPPINGS}\"${line}\",\n") + if (line MATCHES "^[0-9a-fA-F]") + if (line MATCHES "platform:Windows") + if (GLFW_WIN32_MAPPINGS) + string(APPEND GLFW_WIN32_MAPPINGS "\n") + endif() + string(APPEND GLFW_WIN32_MAPPINGS "\"${line}\",") + elseif (line MATCHES "platform:Mac OS X") + if (GLFW_COCOA_MAPPINGS) + string(APPEND GLFW_COCOA_MAPPINGS "\n") + endif() + string(APPEND GLFW_COCOA_MAPPINGS "\"${line}\",") + elseif (line MATCHES "platform:Linux") + if (GLFW_LINUX_MAPPINGS) + string(APPEND GLFW_LINUX_MAPPINGS "\n") + endif() + string(APPEND GLFW_LINUX_MAPPINGS "\"${line}\",") + endif() endif() endforeach() diff --git a/src/external/glfw/CMake/MacOSXBundleInfo.plist.in b/src/external/glfw/CMake/MacOSXBundleInfo.plist.in deleted file mode 100644 index 684ad7908..000000000 --- a/src/external/glfw/CMake/MacOSXBundleInfo.plist.in +++ /dev/null @@ -1,38 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - LSRequiresCarbon - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - - diff --git a/src/external/glfw/CMake/glfw3.pc.in b/src/external/glfw/CMake/glfw3.pc.in index f74298d4b..37f4efd91 100644 --- a/src/external/glfw/CMake/glfw3.pc.in +++ b/src/external/glfw/CMake/glfw3.pc.in @@ -7,7 +7,7 @@ Name: GLFW Description: A multi-platform library for OpenGL, window and input Version: @GLFW_VERSION@ URL: https://www.glfw.org/ -Requires.private: @GLFW_PKG_DEPS@ +Requires.private: @GLFW_PKG_CONFIG_REQUIRES_PRIVATE@ Libs: -L${libdir} -l@GLFW_LIB_NAME@ -Libs.private: @GLFW_PKG_LIBS@ +Libs.private: @GLFW_PKG_CONFIG_LIBS_PRIVATE@ Cflags: -I${includedir} diff --git a/src/external/glfw/CMake/modules/FindEpollShim.cmake b/src/external/glfw/CMake/modules/FindEpollShim.cmake index 2facb4192..f34d07090 100644 --- a/src/external/glfw/CMake/modules/FindEpollShim.cmake +++ b/src/external/glfw/CMake/modules/FindEpollShim.cmake @@ -13,5 +13,5 @@ if (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES) endif (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(EPOLLSHIM DEFAULT_MSG EPOLLSHIM_LIBRARIES EPOLLSHIM_INCLUDE_DIRS) +find_package_handle_standard_args(EpollShim DEFAULT_MSG EPOLLSHIM_LIBRARIES EPOLLSHIM_INCLUDE_DIRS) mark_as_advanced(EPOLLSHIM_INCLUDE_DIRS EPOLLSHIM_LIBRARIES) diff --git a/src/external/glfw/CMake/modules/FindWaylandProtocols.cmake b/src/external/glfw/CMake/modules/FindWaylandProtocols.cmake deleted file mode 100644 index 8eb83f27e..000000000 --- a/src/external/glfw/CMake/modules/FindWaylandProtocols.cmake +++ /dev/null @@ -1,26 +0,0 @@ -find_package(PkgConfig) - -pkg_check_modules(WaylandProtocols QUIET wayland-protocols>=${WaylandProtocols_FIND_VERSION}) - -execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-protocols - OUTPUT_VARIABLE WaylandProtocols_PKGDATADIR - RESULT_VARIABLE _pkgconfig_failed) -if (_pkgconfig_failed) - message(FATAL_ERROR "Missing wayland-protocols pkgdatadir") -endif() - -string(REGEX REPLACE "[\r\n]" "" WaylandProtocols_PKGDATADIR "${WaylandProtocols_PKGDATADIR}") - -find_package_handle_standard_args(WaylandProtocols - FOUND_VAR - WaylandProtocols_FOUND - REQUIRED_VARS - WaylandProtocols_PKGDATADIR - VERSION_VAR - WaylandProtocols_VERSION - HANDLE_COMPONENTS -) - -set(WAYLAND_PROTOCOLS_FOUND ${WaylandProtocols_FOUND}) -set(WAYLAND_PROTOCOLS_PKGDATADIR ${WaylandProtocols_PKGDATADIR}) -set(WAYLAND_PROTOCOLS_VERSION ${WaylandProtocols_VERSION}) diff --git a/src/external/glfw/CMake/modules/FindXKBCommon.cmake b/src/external/glfw/CMake/modules/FindXKBCommon.cmake deleted file mode 100644 index 0f571eeac..000000000 --- a/src/external/glfw/CMake/modules/FindXKBCommon.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# - Try to find XKBCommon -# Once done, this will define -# -# XKBCOMMON_FOUND - System has XKBCommon -# XKBCOMMON_INCLUDE_DIRS - The XKBCommon include directories -# XKBCOMMON_LIBRARIES - The libraries needed to use XKBCommon -# XKBCOMMON_DEFINITIONS - Compiler switches required for using XKBCommon - -find_package(PkgConfig) -pkg_check_modules(PC_XKBCOMMON QUIET xkbcommon) -set(XKBCOMMON_DEFINITIONS ${PC_XKBCOMMON_CFLAGS_OTHER}) - -find_path(XKBCOMMON_INCLUDE_DIR - NAMES xkbcommon/xkbcommon.h - HINTS ${PC_XKBCOMMON_INCLUDE_DIR} ${PC_XKBCOMMON_INCLUDE_DIRS} -) - -find_library(XKBCOMMON_LIBRARY - NAMES xkbcommon - HINTS ${PC_XKBCOMMON_LIBRARY} ${PC_XKBCOMMON_LIBRARY_DIRS} -) - -set(XKBCOMMON_LIBRARIES ${XKBCOMMON_LIBRARY}) -set(XKBCOMMON_LIBRARY_DIRS ${XKBCOMMON_LIBRARY_DIRS}) -set(XKBCOMMON_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIR}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(XKBCommon DEFAULT_MSG - XKBCOMMON_LIBRARY - XKBCOMMON_INCLUDE_DIR -) - -mark_as_advanced(XKBCOMMON_LIBRARY XKBCOMMON_INCLUDE_DIR) - diff --git a/src/external/glfw/CMakeLists.txt b/src/external/glfw/CMakeLists.txt index 59ab473c9..f5e538bf7 100644 --- a/src/external/glfw/CMakeLists.txt +++ b/src/external/glfw/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1...3.17 FATAL_ERROR) +cmake_minimum_required(VERSION 3.4...3.20 FATAL_ERROR) project(GLFW VERSION 3.4.0 LANGUAGES C) @@ -18,7 +18,7 @@ endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) -if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(GLFW_STANDALONE TRUE) endif() @@ -27,34 +27,36 @@ option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" ${GLFW_STANDALONE}) option(GLFW_BUILD_TESTS "Build the GLFW test programs" ${GLFW_STANDALONE}) option(GLFW_BUILD_DOCS "Build the GLFW documentation" ON) option(GLFW_INSTALL "Generate installation target" ON) -option(GLFW_VULKAN_STATIC "Assume the Vulkan loader is linked with the application" OFF) include(GNUInstallDirs) include(CMakeDependentOption) -cmake_dependent_option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF - "UNIX" OFF) +if (GLFW_USE_OSMESA) + message(FATAL_ERROR "GLFW_USE_OSMESA has been removed; set the GLFW_PLATFORM init hint") +endif() + +cmake_dependent_option(GLFW_BUILD_WIN32 "Build support for Win32" ON "WIN32" OFF) +cmake_dependent_option(GLFW_BUILD_COCOA "Build support for Cocoa" ON "APPLE" OFF) +cmake_dependent_option(GLFW_BUILD_X11 "Build support for X11" ON "UNIX;NOT APPLE" OFF) +cmake_dependent_option(GLFW_BUILD_WAYLAND "Build support for Wayland" + "${GLFW_USE_WAYLAND}" "UNIX;NOT APPLE" OFF) + cmake_dependent_option(GLFW_USE_HYBRID_HPG "Force use of high-performance GPU on hybrid systems" OFF "WIN32" OFF) -cmake_dependent_option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF - "UNIX;NOT APPLE" OFF) cmake_dependent_option(USE_MSVC_RUNTIME_LIBRARY_DLL "Use MSVC runtime library DLL" ON "MSVC" OFF) -if (BUILD_SHARED_LIBS AND UNIX) - # On Unix-like systems, shared libraries can use the soname system. - set(GLFW_LIB_NAME glfw) -else() - set(GLFW_LIB_NAME glfw3) -endif() +set(GLFW_LIBRARY_TYPE "${GLFW_LIBRARY_TYPE}" CACHE STRING + "Library type override for GLFW (SHARED, STATIC, OBJECT, or empty to follow BUILD_SHARED_LIBS)") -if (GLFW_VULKAN_STATIC) - if (BUILD_SHARED_LIBS) - # If you absolutely must do this, remove this line and add the Vulkan - # loader static library via the CMAKE_SHARED_LINKER_FLAGS - message(FATAL_ERROR "You are trying to link the Vulkan loader static library into the GLFW shared library") +if (GLFW_LIBRARY_TYPE) + if (GLFW_LIBRARY_TYPE STREQUAL "SHARED") + set(GLFW_BUILD_SHARED_LIBRARY TRUE) + else() + set(GLFW_BUILD_SHARED_LIBRARY FALSE) endif() - set(_GLFW_VULKAN_STATIC 1) +else() + set(GLFW_BUILD_SHARED_LIBRARY ${BUILD_SHARED_LIBS}) endif() list(APPEND CMAKE_MODULE_PATH "${GLFW_SOURCE_DIR}/CMake/modules") @@ -67,20 +69,37 @@ if (GLFW_BUILD_DOCS) endif() #-------------------------------------------------------------------- -# Set compiler specific flags +# Report backend selection +#-------------------------------------------------------------------- +if (GLFW_BUILD_WIN32) + message(STATUS "Including Win32 support") +endif() +if (GLFW_BUILD_COCOA) + message(STATUS "Including Cocoa support") +endif() +if (GLFW_BUILD_WAYLAND) + message(STATUS "Including Wayland support") +endif() +if (GLFW_BUILD_X11) + message(STATUS "Including X11 support") +endif() + +#-------------------------------------------------------------------- +# Apply Microsoft C runtime library option +# This is here because it also applies to tests and examples #-------------------------------------------------------------------- if (MSVC AND NOT USE_MSVC_RUNTIME_LIBRARY_DLL) - if (${CMAKE_VERSION} VERSION_LESS 3.15) + if (CMAKE_VERSION VERSION_LESS 3.15) foreach (flag CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) - if (${flag} MATCHES "/MD") + if (flag MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}") endif() - if (${flag} MATCHES "/MDd") + if (flag MATCHES "/MDd") string(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}") endif() @@ -90,167 +109,6 @@ if (MSVC AND NOT USE_MSVC_RUNTIME_LIBRARY_DLL) endif() endif() -#-------------------------------------------------------------------- -# Detect and select backend APIs -#-------------------------------------------------------------------- -if (GLFW_USE_WAYLAND) - set(_GLFW_WAYLAND 1) - message(STATUS "Using Wayland for window creation") -elseif (GLFW_USE_OSMESA) - set(_GLFW_OSMESA 1) - message(STATUS "Using OSMesa for headless context creation") -elseif (WIN32) - set(_GLFW_WIN32 1) - message(STATUS "Using Win32 for window creation") -elseif (APPLE) - set(_GLFW_COCOA 1) - message(STATUS "Using Cocoa for window creation") -elseif (UNIX) - set(_GLFW_X11 1) - message(STATUS "Using X11 for window creation") -else() - message(FATAL_ERROR "No supported platform was detected") -endif() - -#-------------------------------------------------------------------- -# Find and add Unix math and time libraries -#-------------------------------------------------------------------- -if (UNIX AND NOT APPLE) - find_library(RT_LIBRARY rt) - mark_as_advanced(RT_LIBRARY) - if (RT_LIBRARY) - list(APPEND glfw_LIBRARIES "${RT_LIBRARY}") - list(APPEND glfw_PKG_LIBS "-lrt") - endif() - - find_library(MATH_LIBRARY m) - mark_as_advanced(MATH_LIBRARY) - if (MATH_LIBRARY) - list(APPEND glfw_LIBRARIES "${MATH_LIBRARY}") - list(APPEND glfw_PKG_LIBS "-lm") - endif() - - if (CMAKE_DL_LIBS) - list(APPEND glfw_LIBRARIES "${CMAKE_DL_LIBS}") - list(APPEND glfw_PKG_LIBS "-l${CMAKE_DL_LIBS}") - endif() -endif() - -#-------------------------------------------------------------------- -# Use Win32 for window creation -#-------------------------------------------------------------------- -if (_GLFW_WIN32) - - list(APPEND glfw_PKG_LIBS "-lgdi32") - - if (GLFW_USE_HYBRID_HPG) - set(_GLFW_USE_HYBRID_HPG 1) - endif() -endif() - -#-------------------------------------------------------------------- -# Use X11 for window creation -#-------------------------------------------------------------------- -if (_GLFW_X11) - - find_package(X11 REQUIRED) - - # Set up library and include paths - list(APPEND glfw_INCLUDE_DIRS "${X11_X11_INCLUDE_PATH}") - - # Check for XRandR (modern resolution switching and gamma control) - if (NOT X11_Xrandr_INCLUDE_PATH) - message(FATAL_ERROR "RandR headers not found; install libxrandr development package") - endif() - - # Check for Xinerama (legacy multi-monitor support) - if (NOT X11_Xinerama_INCLUDE_PATH) - message(FATAL_ERROR "Xinerama headers not found; install libxinerama development package") - endif() - - # Check for Xkb (X keyboard extension) - if (NOT X11_Xkb_INCLUDE_PATH) - message(FATAL_ERROR "XKB headers not found; install X11 development package") - endif() - - # Check for Xcursor (cursor creation from RGBA images) - if (NOT X11_Xcursor_INCLUDE_PATH) - message(FATAL_ERROR "Xcursor headers not found; install libxcursor development package") - endif() - - # Check for XInput (modern HID input) - if (NOT X11_Xi_INCLUDE_PATH) - message(FATAL_ERROR "XInput headers not found; install libxi development package") - endif() - - # Check for X Shape (custom window input shape) - if (NOT X11_Xshape_INCLUDE_PATH) - message(FATAL_ERROR "X Shape headers not found; install libxext development package") - endif() -endif() - -#-------------------------------------------------------------------- -# Use Wayland for window creation -#-------------------------------------------------------------------- -if (_GLFW_WAYLAND) - - include(FindPkgConfig) - pkg_check_modules(Wayland REQUIRED - wayland-client>=0.2.7 - wayland-cursor>=0.2.7 - wayland-egl>=0.2.7 - xkbcommon) - - list(APPEND glfw_PKG_DEPS "wayland-client") - - list(APPEND glfw_INCLUDE_DIRS "${Wayland_INCLUDE_DIRS}") - list(APPEND glfw_LIBRARIES "${Wayland_LINK_LIBRARIES}") - - include(CheckIncludeFiles) - include(CheckFunctionExists) - check_include_files(xkbcommon/xkbcommon-compose.h HAVE_XKBCOMMON_COMPOSE_H) - check_function_exists(memfd_create HAVE_MEMFD_CREATE) - - if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")) - find_package(EpollShim) - if (EPOLLSHIM_FOUND) - list(APPEND glfw_INCLUDE_DIRS "${EPOLLSHIM_INCLUDE_DIRS}") - list(APPEND glfw_LIBRARIES "${EPOLLSHIM_LIBRARIES}") - endif() - endif() -endif() - -#-------------------------------------------------------------------- -# Use Cocoa for window creation and NSOpenGL for context creation -#-------------------------------------------------------------------- -if (_GLFW_COCOA) - - list(APPEND glfw_LIBRARIES - "-framework Cocoa" - "-framework IOKit" - "-framework CoreFoundation") - - set(glfw_PKG_DEPS "") - set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation") -endif() - -#-------------------------------------------------------------------- -# Add the Vulkan loader as a dependency if necessary -#-------------------------------------------------------------------- -if (GLFW_VULKAN_STATIC) - list(APPEND glfw_PKG_DEPS "vulkan") -endif() - -#-------------------------------------------------------------------- -# Export GLFW library dependencies -#-------------------------------------------------------------------- -foreach(arg ${glfw_PKG_DEPS}) - set(GLFW_PKG_DEPS "${GLFW_PKG_DEPS} ${arg}") -endforeach() -foreach(arg ${glfw_PKG_LIBS}) - set(GLFW_PKG_LIBS "${GLFW_PKG_LIBS} ${arg}") -endforeach() - #-------------------------------------------------------------------- # Create generated files #-------------------------------------------------------------------- @@ -267,8 +125,6 @@ write_basic_package_version_file(src/glfw3ConfigVersion.cmake VERSION ${GLFW_VERSION} COMPATIBILITY SameMajorVersion) -configure_file(CMake/glfw3.pc.in src/glfw3.pc @ONLY) - #-------------------------------------------------------------------- # Add subdirectories #-------------------------------------------------------------------- diff --git a/src/external/glfw/CONTRIBUTORS.md b/src/external/glfw/CONTRIBUTORS.md new file mode 100644 index 000000000..d938afae0 --- /dev/null +++ b/src/external/glfw/CONTRIBUTORS.md @@ -0,0 +1,262 @@ +# Acknowledgements + +GLFW exists because people around the world donated their time and lent their +skills. This list only includes contributions to the main repository and +excludes other invaluable contributions like language bindings and text and +video tutorials. + + - Bobyshev Alexander + - Laurent Aphecetche + - Matt Arsenault + - ashishgamedev + - David Avedissian + - Luca Bacci + - Keith Bauer + - John Bartholomew + - Coşku Baş + - Niklas Behrens + - Andrew Belt + - Nevyn Bengtsson + - Niklas Bergström + - Denis Bernard + - BiBi + - Doug Binks + - blanco + - Waris Boonyasiriwat + - Kyle Brenneman + - Rok Breulj + - TheBrokenRail + - Kai Burjack + - Martin Capitanio + - Nicolas Caramelli + - David Carlier + - Arturo Castro + - Chi-kwan Chan + - TheChocolateOre + - Joseph Chua + - Ian Clarkson + - Michał Cichoń + - Lambert Clara + - Anna Clarke + - Josh Codd + - Yaron Cohen-Tal + - Omar Cornut + - Andrew Corrigan + - Bailey Cosier + - Noel Cower + - CuriouserThing + - Jason Daly + - danhambleton + - Jarrod Davis + - Olivier Delannoy + - Paul R. Deppe + - Michael Dickens + - Роман Донченко + - Mario Dorn + - Wolfgang Draxinger + - Jonathan Dummer + - Ralph Eastwood + - Fredrik Ehnbom + - Robin Eklind + - Jan Ekström + - Siavash Eliasi + - Ahmad Fatoum + - Nikita Fediuchin + - Felipe Ferreira + - Michael Fogleman + - Jason Francis + - Gerald Franz + - Mário Freitas + - GeO4d + - Marcus Geelnard + - ghuser404 + - Charles Giessen + - Ryan C. Gordon + - Stephen Gowen + - Kovid Goyal + - Kevin Grandemange + - Eloi Marín Gratacós + - Stefan Gustavson + - Andrew Gutekanst + - Stephen Gutekanst + - Jonathan Hale + - hdf89shfdfs + - Sylvain Hellegouarch + - Björn Hempel + - Matthew Henry + - heromyth + - Lucas Hinderberger + - Paul Holden + - Hajime Hoshi + - Warren Hu + - Charles Huber + - Brent Huisman + - illustris + - InKryption + - IntellectualKitty + - Aaron Jacobs + - JannikGM + - Erik S. V. Jansson + - jjYBdx4IL + - Toni Jovanoski + - Arseny Kapoulkine + - Cem Karan + - Osman Keskin + - Koray Kilinc + - Josh Kilmer + - Byunghoon Kim + - Cameron King + - Peter Knut + - Christoph Kubisch + - Yuri Kunde Schlesner + - Rokas Kupstys + - Konstantin Käfer + - Eric Larson + - Francis Lecavalier + - Jong Won Lee + - Robin Leffmann + - Glenn Lewis + - Shane Liesegang + - Anders Lindqvist + - Leon Linhart + - Marco Lizza + - Eyal Lotem + - Aaron Loucks + - Luflosi + - lukect + - Tristam MacDonald + - Hans Mackowiak + - Дмитри Малышев + - Zbigniew Mandziejewicz + - Adam Marcus + - Célestin Marot + - Kyle McDonald + - David V. McKay + - David Medlock + - Bryce Mehring + - Jonathan Mercier + - Marcel Metz + - Liam Middlebrook + - Ave Milia + - Jonathan Miller + - Kenneth Miller + - Bruce Mitchener + - Jack Moffitt + - Ravi Mohan + - Jeff Molofee + - Alexander Monakov + - Pierre Morel + - Jon Morton + - Pierre Moulon + - Martins Mozeiko + - Pascal Muetschard + - James Murphy + - Julian Møller + - ndogxj + - F. Nedelec + - n3rdopolis + - Kristian Nielsen + - Joel Niemelä + - Kamil Nowakowski + - onox + - Denis Ovod + - Ozzy + - Andri Pálsson + - luz paz + - Peoro + - Braden Pellett + - Christopher Pelloux + - Michael Pennington + - Arturo J. Pérez + - Vladimir Perminov + - Olivier Perret + - Anthony Pesch + - Orson Peters + - Emmanuel Gil Peyrot + - Cyril Pichard + - Pilzschaf + - Keith Pitt + - Stanislav Podgorskiy + - Konstantin Podsvirov + - Nathan Poirier + - Alexandre Pretyman + - Pablo Prietz + - przemekmirek + - pthom + - Martin Pulec + - Guillaume Racicot + - Christian Rauch + - Philip Rideout + - Eddie Ringle + - Max Risuhin + - Joe Roback + - Jorge Rodriguez + - Jari Ronkainen + - Luca Rood + - Ed Ropple + - Aleksey Rybalkin + - Mikko Rytkönen + - Riku Salminen + - Brandon Schaefer + - Sebastian Schuberth + - Christian Sdunek + - Matt Sealey + - Steve Sexton + - Arkady Shapkin + - Ali Sherief + - Yoshiki Shibukawa + - Dmitri Shuralyov + - Joao da Silva + - Daniel Sieger + - Daniel Skorupski + - Slemmie + - Anthony Smith + - Bradley Smith + - Cliff Smolinsky + - Patrick Snape + - Erlend Sogge Heggen + - Olivier Sohn + - Julian Squires + - Johannes Stein + - Pontus Stenetorp + - Michael Stocker + - Justin Stoecker + - Elviss Strazdins + - Paul Sultana + - Nathan Sweet + - TTK-Bandit + - Jared Tiala + - Sergey Tikhomirov + - Arthur Tombs + - TronicLabs + - Ioannis Tsakpinis + - Samuli Tuomola + - Matthew Turner + - urraka + - Elias Vanderstuyft + - Stef Velzel + - Jari Vetoniemi + - Ricardo Vieira + - Nicholas Vitovitch + - Simon Voordouw + - Corentin Wallez + - Torsten Walluhn + - Patrick Walton + - Xo Wang + - Jay Weisskopf + - Frank Wille + - Andy Williams + - Joel Winarske + - Richard A. Wilkes + - Tatsuya Yatagawa + - Ryogo Yoshimura + - Lukas Zanner + - Andrey Zholos + - Aihui Zhu + - Santi Zupancic + - Jonas Ådahl + - Lasse Öörni + - Leonard König + - All the unmentioned and anonymous contributors in the GLFW community, for bug + reports, patches, feedback, testing and encouragement + diff --git a/src/external/glfw/README.md b/src/external/glfw/README.md index b5b4f7ea4..8b4a1546e 100644 --- a/src/external/glfw/README.md +++ b/src/external/glfw/README.md @@ -1,6 +1,6 @@ # GLFW -[![Build status](https://travis-ci.org/glfw/glfw.svg?branch=master)](https://travis-ci.org/glfw/glfw) +[![Build status](https://github.com/glfw/glfw/actions/workflows/build.yml/badge.svg)](https://github.com/glfw/glfw/actions) [![Build status](https://ci.appveyor.com/api/projects/status/0kf0ct9831i5l6sp/branch/master?svg=true)](https://ci.appveyor.com/project/elmindreda/glfw) [![Coverity Scan](https://scan.coverity.com/projects/4884/badge.svg)](https://scan.coverity.com/projects/glfw-glfw) @@ -14,18 +14,18 @@ GLFW natively supports Windows, macOS and Linux and other Unix-like systems. On Linux both X11 and Wayland are supported. GLFW is licensed under the [zlib/libpng -license](http://www.glfw.org/license.html). +license](https://www.glfw.org/license.html). -You can [download](http://www.glfw.org/download.html) the latest stable release +You can [download](https://www.glfw.org/download.html) the latest stable release as source or Windows binaries, or fetch the `latest` branch from GitHub. Each release starting with 3.0 also has a corresponding [annotated tag](https://github.com/glfw/glfw/releases) with source and binary archives. -The [documentation](http://www.glfw.org/docs/latest/) is available online and is +The [documentation](https://www.glfw.org/docs/latest/) is available online and is included in all source and binary archives. See the [release notes](https://www.glfw.org/docs/latest/news.html) for new features, caveats and deprecations in the latest release. For more details see the [version -history](http://www.glfw.org/changelog.html). +history](https://www.glfw.org/changelog.html). The `master` branch is the stable integration branch and _should_ always compile and run on all supported platforms, although details of newly added features may @@ -34,11 +34,16 @@ fixes live in [other branches](https://github.com/glfw/glfw/branches/all) until they are stable enough to merge. If you are new to GLFW, you may find the -[tutorial](http://www.glfw.org/docs/latest/quick.html) for GLFW 3 useful. If +[tutorial](https://www.glfw.org/docs/latest/quick.html) for GLFW 3 useful. If you have used GLFW 2 in the past, there is a [transition -guide](http://www.glfw.org/docs/latest/moving.html) for moving to the GLFW +guide](https://www.glfw.org/docs/latest/moving.html) for moving to the GLFW 3 API. +GLFW exists because of the contributions of [many people](CONTRIBUTORS.md) +around the world, whether by reporting bugs, providing community support, adding +features, reviewing or testing code, debugging, proofreading docs, suggesting +features or fixing bugs. + ## Compiling GLFW @@ -52,16 +57,16 @@ MinGW-w64, on macOS with Clang and on Linux and other Unix-like systems with GCC and Clang. It will likely compile in other environments as well, but this is not regularly tested. -There are [pre-compiled Windows binaries](http://www.glfw.org/download.html) +There are [pre-compiled Windows binaries](https://www.glfw.org/download.html) available for all supported compilers. -See the [compilation guide](http://www.glfw.org/docs/latest/compile.html) for +See the [compilation guide](https://www.glfw.org/docs/latest/compile.html) for more information about how to compile GLFW yourself. ## Using GLFW -See the [documentation](http://www.glfw.org/docs/latest/) for tutorials, guides +See the [documentation](https://www.glfw.org/docs/latest/) for tutorials, guides and the API reference. @@ -79,7 +84,7 @@ Unix-like systems running the X Window System are supported even without a desktop environment or modern extensions, although some features require a running window or clipboard manager. The OSMesa backend requires Mesa 6.3. -See the [compatibility guide](http://www.glfw.org/docs/latest/compat.html) +See the [compatibility guide](https://www.glfw.org/docs/latest/compat.html) in the documentation for more information. @@ -102,7 +107,7 @@ located in the `deps/` directory. - [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) for test and example UI - [stb\_image\_write](https://github.com/nothings/stb) for writing images to disk -The documentation is generated with [Doxygen](http://doxygen.org/) if CMake can +The documentation is generated with [Doxygen](https://doxygen.org/) if CMake can find that tool. @@ -116,6 +121,17 @@ information on what to include when reporting a bug. ## Changelog + - Added `GLFW_PLATFORM` init hint for runtime platform selection (#1958) + - Added `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, + `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` and `GLFW_PLATFORM_NULL` symbols to + specify the desired platform (#1958) + - Added `glfwGetPlatform` function to query what platform was selected (#1655,#1958) + - Added `glfwPlatformSupported` function to query if a platform is supported + (#1655,#1958) + - Added `glfwInitAllocator` for setting a custom memory allocator (#544,#1628,#1947) + - Added `GLFWallocator` struct and `GLFWallocatefun`, `GLFWreallocatefun` and + `GLFWdeallocatefun` types (#544,#1628,#1947) + - Added `glfwInitVulkanLoader` for using a non-default Vulkan loader (#1374,#1890) - Added `GLFW_RESIZE_NWSE_CURSOR`, `GLFW_RESIZE_NESW_CURSOR`, `GLFW_RESIZE_ALL_CURSOR` and `GLFW_NOT_ALLOWED_CURSOR` cursor shapes (#427) - Added `GLFW_RESIZE_EW_CURSOR` alias for `GLFW_HRESIZE_CURSOR` (#427) @@ -123,16 +139,36 @@ information on what to include when reporting a bug. - Added `GLFW_POINTING_HAND_CURSOR` alias for `GLFW_HAND_CURSOR` (#427) - Added `GLFW_MOUSE_PASSTHROUGH` window hint for letting mouse input pass through the window (#1236,#1568) + - Added `GLFW_CURSOR_CAPTURED` cursor mode to confine the cursor to the window + content area (#58) + - Added `GLFW_POSITION_X` and `GLFW_POSITION_Y` window hints for initial position + (#1603,#1747) + - Added `GLFW_ANY_POSITION` hint value for letting the window manager choose (#1603,#1747) + - Added `GLFW_PLATFORM_UNAVAILABLE` error for platform detection failures (#1958) - Added `GLFW_FEATURE_UNAVAILABLE` error for platform limitations (#1692) - Added `GLFW_FEATURE_UNIMPLEMENTED` error for incomplete backends (#1692) + - Added `GLFW_WAYLAND_APP_ID` window hint string for Wayland app\_id selection + (#2121,#2122) - Added `GLFW_ANGLE_PLATFORM_TYPE` init hint and `GLFW_ANGLE_PLATFORM_TYPE_*` values to select ANGLE backend (#1380) - Added `GLFW_X11_XCB_VULKAN_SURFACE` init hint for selecting X11 Vulkan surface extension (#1793) + - Added `GLFW_NATIVE_INCLUDE_NONE` for disabling inclusion of native headers (#1348) + - Added `GLFW_BUILD_WIN32` CMake option for enabling Win32 support (#1958) + - Added `GLFW_BUILD_COCOA` CMake option for enabling Cocoa support (#1958) + - Added `GLFW_BUILD_X11` CMake option for enabling X11 support (#1958) + - Added `GLFW_LIBRARY_TYPE` CMake variable for overriding the library type + (#279,#1307,#1497,#1574,#1928) + - Added `GLFW_PKG_CONFIG_REQUIRES_PRIVATE` and `GLFW_PKG_CONFIG_LIBS_PRIVATE` CMake + variables exposing pkg-config dependencies (#1307) - Made joystick subsystem initialize at first use (#1284,#1646) - Made `GLFW_DOUBLEBUFFER` a read-only window attribute - Updated the minimum required CMake version to 3.1 + - Updated gamepad mappings from upstream - Disabled tests and examples by default when built as a CMake subdirectory + - Renamed `GLFW_USE_WAYLAND` CMake option to `GLFW_BUILD_WAYLAND` (#1958) + - Removed `GLFW_USE_OSMESA` CMake option enabling the Null platform (#1958) + - Removed CMake generated configuration header - Bugfix: The CMake config-file package used an absolute path and was not relocatable (#1470) - Bugfix: Video modes with a duplicate screen area were discarded (#1555,#1556) @@ -141,9 +177,16 @@ information on what to include when reporting a bug. - Bugfix: Some extension loader headers did not prevent default OpenGL header inclusion (#1695) - Bugfix: Buffers were swapped at creation on single-buffered windows (#1873) + - Bugfix: Gamepad mapping updates could spam `GLFW_INVALID_VALUE` due to + incompatible controllers sharing hardware ID (#1763) + - Bugfix: Native access functions for context handles did not check that the API matched + - Bugfix: `glfwMakeContextCurrent` would access TLS slot before initialization + - Bugfix: `glfwSetGammaRamp` could emit `GLFW_INVALID_VALUE` before initialization + - Bugfix: `glfwGetJoystickUserPointer` returned `NULL` during disconnection (#2092) - [Win32] Added the `GLFW_WIN32_KEYBOARD_MENU` window hint for enabling access to the window menu - [Win32] Added a version info resource to the GLFW DLL + - [Win32] Made hidden helper window use its own window class - [Win32] Disabled framebuffer transparency on Windows 7 when DWM windows are opaque (#1512) - [Win32] Bugfix: `GLFW_INCLUDE_VULKAN` plus `VK_USE_PLATFORM_WIN32_KHR` caused @@ -170,12 +213,29 @@ information on what to include when reporting a bug. - [Win32] Bugfix: `USE_MSVC_RUNTIME_LIBRARY_DLL` had no effect on CMake 3.15 or later (#1783,#1796) - [Win32] Bugfix: Compilation with LLVM for Windows failed (#1807,#1824,#1874) + - [Win32] Bugfix: The foreground lock timeout was overridden, ignoring the user + - [Win32] Bugfix: Content scale queries could fail silently (#1615) + - [Win32] Bugfix: Content scales could have garbage values if monitor was recently + disconnected (#1615) + - [Win32] Bugfix: A window created maximized and undecorated would cover the whole + monitor (#1806) + - [Win32] Bugfix: The default restored window position was lost when creating a maximized + window + - [Win32] Bugfix: `glfwMaximizeWindow` would make a hidden window visible + - [Win32] Bugfix: `Alt+PrtSc` would emit `GLFW_KEY_UNKNOWN` and a different + scancode than `PrtSc` (#1993) + - [Win32] Bugfix: `GLFW_KEY_PAUSE` scancode from `glfwGetKeyScancode` did not + match event scancode (#1993) + - [Win32] Bugfix: Instance-local operations used executable instance (#469,#1296,#1395) + - [Win32] Bugfix: The OSMesa library was not unloaded on termination + - [Win32] Bugfix: Right shift emitted `GLFW_KEY_UNKNOWN` when using a CJK IME (#2050) - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) - [Cocoa] Changed `EGLNativeWindowType` from `NSView` to `CALayer` (#1169) - [Cocoa] Changed F13 key to report Print Screen for cross-platform consistency (#1786) + - [Cocoa] Disabled macOS fullscreen when `GLFW_RESIZABLE` is false - [Cocoa] Removed dependency on the CoreVideo framework - [Cocoa] Bugfix: `glfwSetWindowSize` used a bottom-left anchor point (#1553) - [Cocoa] Bugfix: Window remained on screen after destruction until event poll @@ -190,10 +250,25 @@ information on what to include when reporting a bug. could leak memory - [Cocoa] Bugfix: Objective-C files were compiled as C with CMake 3.19 (#1787) - [Cocoa] Bugfix: Duplicate video modes were not filtered out (#1830) - - [Cocoa] Bugfix: Menubar was not clickable on macOS 10.15+ until it lost and + - [Cocoa] Bugfix: Menu bar was not clickable on macOS 10.15+ until it lost and regained focus (#1648,#1802) - [Cocoa] Bugfix: Monitor name query could segfault on macOS 11 (#1809,#1833) - [Cocoa] Bugfix: The install name of the installed dylib was relative (#1504) + - [Cocoa] Bugfix: The MoltenVK layer contents scale was updated only after + related events were emitted + - [Cocoa] Bugfix: Moving the cursor programmatically would freeze it for + a fraction of a second (#1962) + - [Cocoa] Bugfix: `kIOMasterPortDefault` was deprecated in macOS 12.0 (#1980) + - [Cocoa] Bugfix: `kUTTypeURL` was deprecated in macOS 12.0 (#2003) + - [Cocoa] Bugfix: A connected Apple AirPlay would emit a useless error (#1791) + - [Cocoa] Bugfix: The EGL and OSMesa libraries were not unloaded on termination + - [Cocoa] Bugfix: `GLFW_MAXIMIZED` was always true when `GLFW_RESIZABLE` was false + - [Cocoa] Bugfix: Changing `GLFW_DECORATED` in macOS fullscreen would abort + application (#1886) + - [Cocoa] Bugfix: Setting a monitor from macOS fullscreen would abort + application (#2110) + - [Cocoa] Bugfix: The Vulkan loader was not loaded from the `Frameworks` bundle + subdirectory (#2113,#2120) - [X11] Bugfix: The CMake files did not check for the XInput headers (#1480) - [X11] Bugfix: Key names were not updated when the keyboard layout changed (#1462,#1528) @@ -216,11 +291,32 @@ information on what to include when reporting a bug. - [X11] Bugfix: XKB path used keysyms instead of physical locations for non-printable keys (#1598) - [X11] Bugfix: Function keys were mapped to `GLFW_KEY_UNKNOWN` for some layout - combinaitons (#1598) + combinations (#1598) - [X11] Bugfix: Keys pressed simultaneously with others were not always reported (#1112,#1415,#1472,#1616) - [X11] Bugfix: Some window attributes were not applied on leaving fullscreen (#1863) + - [X11] Bugfix: Changing `GLFW_FLOATING` could leak memory + - [X11] Bugfix: Icon pixel format conversion worked only by accident, relying on + undefined behavior (#1986) + - [X11] Bugfix: Dynamic loading on OpenBSD failed due to soname differences + - [X11] Bugfix: Waiting for events would fail if file descriptor was too large + (#2024) + - [X11] Bugfix: Joystick events could lead to busy-waiting (#1872) + - [X11] Bugfix: `glfwWaitEvents*` did not continue for joystick events + - [X11] Bugfix: `glfwPostEmptyEvent` could be ignored due to race condition + (#379,#1281,#1285,#2033) + - [X11] Bugfix: Dynamic loading on NetBSD failed due to soname differences + - [X11] Bugfix: Left shift of int constant relied on undefined behavior (#1951) + - [X11] Bugfix: The OSMesa libray was not unloaded on termination + - [X11] Bugfix: A malformed response during selection transfer could cause a segfault + - [X11] Bugfix: Some calls would reset Xlib to the default error handler (#2108) + - [Wayland] Added dynamic loading of all Wayland libraries + - [Wayland] Added support for key names via xkbcommon + - [Wayland] Added support for file path drop events (#2040) + - [Wayland] Added support for more human-readable monitor names where available + - [Wayland] Disabled alpha channel for opaque windows on systems lacking + `EGL_EXT_present_opaque` (#1895) - [Wayland] Removed support for `wl_shell` (#1443) - [Wayland] Bugfix: The `GLFW_HAND_CURSOR` shape used the wrong image (#1432) - [Wayland] Bugfix: `CLOCK_MONOTONIC` was not correctly enabled @@ -228,28 +324,92 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: Retrieving partial framebuffer size would segfault - [Wayland] Bugfix: Scrolling offsets were inverted compared to other platforms (#1463) - - [Wayland] Bugfix: Client-Side Decorations were destroyed in the wrong worder + - [Wayland] Bugfix: Client-Side Decorations were destroyed in the wrong order (#1798) - [Wayland] Bugfix: Monitors physical size could report zero (#1784,#1792) + - [Wayland] Bugfix: Some keys were not repeating in Wayland (#1908) + - [Wayland] Bugfix: Non-arrow cursors are offset from the hotspot (#1706,#1899) + - [Wayland] Bugfix: The `O_CLOEXEC` flag was not defined on FreeBSD + - [Wayland] Bugfix: Key repeat could lead to a race condition (#1710) + - [Wayland] Bugfix: Activating a window would emit two input focus events + - [Wayland] Bugfix: Disable key repeat mechanism when window loses input focus + - [Wayland] Bugfix: Window hiding and showing did not work (#1492,#1731) + - [Wayland] Bugfix: A key being repeated was not released when window lost focus + - [Wayland] Bugfix: Showing a hidden window did not emit a window refresh event + - [Wayland] Bugfix: Full screen window creation did not ignore `GLFW_VISIBLE` + - [Wayland] Bugfix: Some keys were reported as wrong key or `GLFW_KEY_UNKNOWN` + - [Wayland] Bugfix: Text input did not repeat along with key repeat + - [Wayland] Bugfix: `glfwPostEmptyEvent` sometimes had no effect (#1520,#1521) + - [Wayland] Bugfix: `glfwSetClipboardString` would fail if set to result of + `glfwGetClipboardString` + - [Wayland] Bugfix: Data source creation error would cause double free at termination + - [Wayland] Bugfix: Partial writes of clipboard string would cause beginning to repeat + - [Wayland] Bugfix: Some errors would cause clipboard string transfer to hang + - [Wayland] Bugfix: Drag and drop data was misinterpreted as clipboard string + - [Wayland] Bugfix: MIME type matching was not performed for clipboard string + - [Wayland] Bugfix: The OSMesa library was not unloaded on termination + - [Wayland] Bugfix: `glfwCreateWindow` could emit `GLFW_FEATURE_UNAVAILABLE` + - [Wayland] Bugfix: Lock key modifier bits were only set when lock keys were pressed + - [Wayland] Bugfix: A window leaving full screen mode would be iconified (#1995) + - [Wayland] Bugfix: A window leaving full screen mode ignored its desired size + - [Wayland] Bugfix: `glfwSetWindowMonitor` did not update windowed mode size + - [Wayland] Bugfix: `glfwRestoreWindow` would make a full screen window windowed + - [Wayland] Bugfix: A window maximized or restored by the user would enter an + inconsistent state + - [Wayland] Bugfix: Window maximization events were not emitted + - [Wayland] Bugfix: `glfwRestoreWindow` assumed it was always in windowed mode + - [Wayland] Bugfix: `glfwSetWindowSize` would resize a full screen window + - [Wayland] Bugfix: A window content scale event would be emitted every time + the window resized + - [Wayland] Bugfix: If `glfwInit` failed it would close stdin + - [Wayland] Bugfix: Manual resizing with fallback decorations behaved erratically + (#1991,#2115,#2127) + - [Wayland] Bugfix: Size limits included frame size for fallback decorations + - [Wayland] Bugfix: Updating `GLFW_DECORATED` had no effect on server-side + decorations + - [Wayland] Bugfix: A monitor would be reported as connected again if its scale + changed + - [Wayland] Bugfix: `glfwTerminate` would segfault if any monitor had changed + scale + - [Wayland] Bugfix: Window content scale events were not emitted when monitor + scale changed + - [Wayland] Bugfix: `glfwSetWindowAspectRatio` reported an error instead of + applying the specified ratio + - [Wayland] Bugfix: `GLFW_MAXIMIZED` window hint had no effect + - [Wayland] Bugfix: `glfwRestoreWindow` had no effect before first show + - [Wayland] Bugfix: Hiding and then showing a window caused program abort on + wlroots compositors (#1268) + - [Wayland] Bugfix: `GLFW_DECORATED` was ignored when showing a window with XDG + decorations + - [Wayland] Bugfix: Connecting a mouse after `glfwInit` would segfault (#1450) + - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled + - [Linux] Bugfix: Joysticks without buttons were ignored (#2042,#2043) + - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) - [NSGL] Removed enforcement of forward-compatible flag for core contexts - [NSGL] Bugfix: `GLFW_COCOA_RETINA_FRAMEBUFFER` had no effect on newer macOS versions (#1442) - [NSGL] Bugfix: Workaround for swap interval on 10.14 broke on 10.12 (#1483) + - [NSGL] Bugfix: Defining `GL_SILENCE_DEPRECATION` externally caused + a duplicate definition warning (#1840) - [EGL] Added platform selection via the `EGL_EXT_platform_base` extension (#442) - [EGL] Added ANGLE backend selection via `EGL_ANGLE_platform_angle` extension (#1380) + [EGL] Added loading of glvnd `libOpenGL.so.0` where available for OpenGL + - [EGL] Bugfix: The `GLFW_DOUBLEBUFFER` context attribute was ignored (#1843) + - [GLX] Added loading of glvnd `libGLX.so.0` where available + - [GLX] Bugfix: Context creation failed if GLX 1.4 was not exported by GLX library ## Contact -On [glfw.org](http://www.glfw.org/) you can find the latest version of GLFW, as +On [glfw.org](https://www.glfw.org/) you can find the latest version of GLFW, as well as news, documentation and other information about the project. If you have questions related to the use of GLFW, we have a [forum](https://discourse.glfw.org/), and the `#glfw` IRC channel on -[Freenode](http://freenode.net/). +[Libera.Chat](https://libera.chat/). If you have a bug to report, a patch to submit or a feature you'd like to request, please file it in the @@ -258,220 +418,3 @@ request, please file it in the Finally, if you're interested in helping out with the development of GLFW or porting it to your favorite platform, join us on the forum, GitHub or IRC. - -## Acknowledgements - -GLFW exists because people around the world donated their time and lent their -skills. - - - Bobyshev Alexander - - Laurent Aphecetche - - Matt Arsenault - - ashishgamedev - - David Avedissian - - Keith Bauer - - John Bartholomew - - Coşku Baş - - Niklas Behrens - - Andrew Belt - - Nevyn Bengtsson - - Niklas Bergström - - Denis Bernard - - Doug Binks - - blanco - - Kyle Brenneman - - Rok Breulj - - Kai Burjack - - Martin Capitanio - - Nicolas Caramelli - - David Carlier - - Arturo Castro - - Chi-kwan Chan - - Ian Clarkson - - Michał Cichoń - - Lambert Clara - - Anna Clarke - - Yaron Cohen-Tal - - Omar Cornut - - Andrew Corrigan - - Bailey Cosier - - Noel Cower - - CuriouserThing - - Jason Daly - - Jarrod Davis - - Olivier Delannoy - - Paul R. Deppe - - Michael Dickens - - Роман Донченко - - Mario Dorn - - Wolfgang Draxinger - - Jonathan Dummer - - Ralph Eastwood - - Fredrik Ehnbom - - Robin Eklind - - Siavash Eliasi - - Felipe Ferreira - - Michael Fogleman - - Gerald Franz - - Mário Freitas - - GeO4d - - Marcus Geelnard - - Charles Giessen - - Ryan C. Gordon - - Stephen Gowen - - Kovid Goyal - - Eloi Marín Gratacós - - Stefan Gustavson - - Jonathan Hale - - hdf89shfdfs - - Sylvain Hellegouarch - - Matthew Henry - - heromyth - - Lucas Hinderberger - - Paul Holden - - Warren Hu - - Charles Huber - - IntellectualKitty - - Aaron Jacobs - - Erik S. V. Jansson - - Toni Jovanoski - - Arseny Kapoulkine - - Cem Karan - - Osman Keskin - - Josh Kilmer - - Byunghoon Kim - - Cameron King - - Peter Knut - - Christoph Kubisch - - Yuri Kunde Schlesner - - Rokas Kupstys - - Konstantin Käfer - - Eric Larson - - Francis Lecavalier - - Jong Won Lee - - Robin Leffmann - - Glenn Lewis - - Shane Liesegang - - Anders Lindqvist - - Leon Linhart - - Marco Lizza - - Eyal Lotem - - Aaron Loucks - - Luflosi - - lukect - - Tristam MacDonald - - Hans Mackowiak - - Дмитри Малышев - - Zbigniew Mandziejewicz - - Adam Marcus - - Célestin Marot - - Kyle McDonald - - David Medlock - - Bryce Mehring - - Jonathan Mercier - - Marcel Metz - - Liam Middlebrook - - Ave Milia - - Jonathan Miller - - Kenneth Miller - - Bruce Mitchener - - Jack Moffitt - - Jeff Molofee - - Alexander Monakov - - Pierre Morel - - Jon Morton - - Pierre Moulon - - Martins Mozeiko - - Julian Møller - - ndogxj - - Kristian Nielsen - - Kamil Nowakowski - - onox - - Denis Ovod - - Ozzy - - Andri Pálsson - - Peoro - - Braden Pellett - - Christopher Pelloux - - Arturo J. Pérez - - Vladimir Perminov - - Anthony Pesch - - Orson Peters - - Emmanuel Gil Peyrot - - Cyril Pichard - - Keith Pitt - - Stanislav Podgorskiy - - Konstantin Podsvirov - - Nathan Poirier - - Alexandre Pretyman - - Pablo Prietz - - przemekmirek - - pthom - - Guillaume Racicot - - Philip Rideout - - Eddie Ringle - - Max Risuhin - - Jorge Rodriguez - - Luca Rood - - Ed Ropple - - Aleksey Rybalkin - - Mikko Rytkönen - - Riku Salminen - - Brandon Schaefer - - Sebastian Schuberth - - Christian Sdunek - - Matt Sealey - - Steve Sexton - - Arkady Shapkin - - Ali Sherief - - Yoshiki Shibukawa - - Dmitri Shuralyov - - Daniel Skorupski - - Bradley Smith - - Cliff Smolinsky - - Patrick Snape - - Erlend Sogge Heggen - - Julian Squires - - Johannes Stein - - Pontus Stenetorp - - Michael Stocker - - Justin Stoecker - - Elviss Strazdins - - Paul Sultana - - Nathan Sweet - - TTK-Bandit - - Jared Tiala - - Sergey Tikhomirov - - Arthur Tombs - - Ioannis Tsakpinis - - Samuli Tuomola - - Matthew Turner - - urraka - - Elias Vanderstuyft - - Stef Velzel - - Jari Vetoniemi - - Ricardo Vieira - - Nicholas Vitovitch - - Simon Voordouw - - Corentin Wallez - - Torsten Walluhn - - Patrick Walton - - Xo Wang - - Waris - - Jay Weisskopf - - Frank Wille - - Andy Williams - - Joel Winarske - - Richard A. Wilkes - - Tatsuya Yatagawa - - Ryogo Yoshimura - - Lukas Zanner - - Andrey Zholos - - Aihui Zhu - - Santi Zupancic - - Jonas Ådahl - - Lasse Öörni - - Leonard König - - All the unmentioned and anonymous contributors in the GLFW community, for bug - reports, patches, feedback, testing and encouragement - diff --git a/src/external/glfw/deps/glad/gl.h b/src/external/glfw/deps/glad/gl.h index 5c7879f8a..b421fe080 100644 --- a/src/external/glfw/deps/glad/gl.h +++ b/src/external/glfw/deps/glad/gl.h @@ -1,5 +1,5 @@ /** - * Loader generated by glad 2.0.0-beta on Sun Apr 14 17:03:32 2019 + * Loader generated by glad 2.0.0-beta on Tue Aug 24 22:51:07 2021 * * Generator: C/C++ * Specification: gl @@ -9,31 +9,51 @@ * - gl:compatibility=3.3 * * Options: - * - MX_GLOBAL = False - * - LOADER = False * - ALIAS = False - * - HEADER_ONLY = False * - DEBUG = False + * - HEADER_ONLY = True + * - LOADER = False * - MX = False + * - MX_GLOBAL = False + * - ON_DEMAND = False * * Commandline: - * --api='gl:compatibility=3.3' --extensions='GL_ARB_multisample,GL_ARB_robustness,GL_KHR_debug' c + * --api='gl:compatibility=3.3' --extensions='GL_ARB_multisample,GL_ARB_robustness,GL_KHR_debug' c --header-only * * Online: - * http://glad.sh/#api=gl%3Acompatibility%3D3.3&extensions=GL_ARB_multisample%2CGL_ARB_robustness%2CGL_KHR_debug&generator=c&options= + * http://glad.sh/#api=gl%3Acompatibility%3D3.3&extensions=GL_ARB_multisample%2CGL_ARB_robustness%2CGL_KHR_debug&generator=c&options=HEADER_ONLY * */ #ifndef GLAD_GL_H_ #define GLAD_GL_H_ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif #ifdef __gl_h_ - #error OpenGL header already included (API: gl), remove previous include! + #error OpenGL (gl.h) header already included (API: gl), remove previous include! #endif #define __gl_h_ 1 - +#ifdef __gl3_h_ + #error OpenGL (gl3.h) header already included (API: gl), remove previous include! +#endif +#define __gl3_h_ 1 +#ifdef __glext_h_ + #error OpenGL (glext.h) header already included (API: gl), remove previous include! +#endif +#define __glext_h_ 1 +#ifdef __gl3ext_h_ + #error OpenGL (gl3ext.h) header already included (API: gl), remove previous include! +#endif +#define __gl3ext_h_ 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#endif #define GLAD_GL +#define GLAD_OPTION_GL_HEADER_ONLY #ifdef __cplusplus extern "C" { @@ -137,15 +157,16 @@ extern "C" { #define GLAPIENTRY GLAD_API_PTR #endif - #define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) #define GLAD_VERSION_MAJOR(version) (version / 10000) #define GLAD_VERSION_MINOR(version) (version % 10000) +#define GLAD_GENERATOR_VERSION "2.0.0-beta" + typedef void (*GLADapiproc)(void); typedef GLADapiproc (*GLADloadfunc)(const char *name); -typedef GLADapiproc (*GLADuserptrloadfunc)(const char *name, void *userptr); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); @@ -1458,69 +1479,401 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_ZOOM_Y 0x0D17 -#include +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are 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 Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_GLAD_API_PTR + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_GLAD_API_PTR funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_GLAD_API_PTR + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_GLAD_API_PTR __stdcall +#else +# define KHRONOS_GLAD_API_PTR +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef _WIN64 +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ + typedef unsigned int GLenum; + typedef unsigned char GLboolean; + typedef unsigned int GLbitfield; + typedef void GLvoid; + typedef khronos_int8_t GLbyte; + typedef khronos_uint8_t GLubyte; + typedef khronos_int16_t GLshort; + typedef khronos_uint16_t GLushort; + typedef int GLint; + typedef unsigned int GLuint; + typedef khronos_int32_t GLclampx; + typedef int GLsizei; + typedef khronos_float_t GLfloat; + typedef khronos_float_t GLclampf; + typedef double GLdouble; + typedef double GLclampd; + typedef void *GLeglClientBufferEXT; + typedef void *GLeglImageOES; + typedef char GLchar; + typedef char GLcharARB; + #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif + typedef khronos_uint16_t GLhalf; + typedef khronos_uint16_t GLhalfARB; + typedef khronos_int32_t GLfixed; + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptr; #else typedef khronos_intptr_t GLintptr; #endif + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptrARB; #else typedef khronos_intptr_t GLintptrARB; #endif + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptr; #else typedef khronos_ssize_t GLsizeiptr; #endif + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptrARB; #else typedef khronos_ssize_t GLsizeiptrARB; #endif + typedef khronos_int64_t GLint64; + typedef khronos_int64_t GLint64EXT; + typedef khronos_uint64_t GLuint64; + typedef khronos_uint64_t GLuint64EXT; + typedef struct __GLsync *GLsync; + struct _cl_context; + struct _cl_event; -typedef void ( *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); -typedef void ( *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); -typedef void ( *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); -typedef void ( *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); + +typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + +typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + +typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + +typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); + typedef unsigned short GLhalfNV; + typedef GLintptr GLvdpauSurfaceNV; -typedef void ( *GLVULKANPROCNV)(void); + +typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); + #define GL_VERSION_1_0 1 @@ -1555,762 +1908,761 @@ GLAD_API_CALL int GLAD_GL_ARB_robustness; GLAD_API_CALL int GLAD_GL_KHR_debug; -typedef void (GLAD_API_PTR *PFNGLACCUMPROC)(GLenum op, GLfloat value); -typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); -typedef void (GLAD_API_PTR *PFNGLALPHAFUNCPROC)(GLenum func, GLfloat ref); -typedef GLboolean (GLAD_API_PTR *PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint * textures, GLboolean * residences); -typedef void (GLAD_API_PTR *PFNGLARRAYELEMENTPROC)(GLint i); -typedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); -typedef void (GLAD_API_PTR *PFNGLBEGINPROC)(GLenum mode); -typedef void (GLAD_API_PTR *PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); -typedef void (GLAD_API_PTR *PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); -typedef void (GLAD_API_PTR *PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); -typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); -typedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); -typedef void (GLAD_API_PTR *PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); -typedef void (GLAD_API_PTR *PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); -typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar * name); -typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar * name); -typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); -typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); -typedef void (GLAD_API_PTR *PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); -typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); -typedef void (GLAD_API_PTR *PFNGLBINDVERTEXARRAYPROC)(GLuint array); -typedef void (GLAD_API_PTR *PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte * bitmap); -typedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); -typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); -typedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); -typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -typedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); -typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); -typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); -typedef void (GLAD_API_PTR *PFNGLCALLLISTPROC)(GLuint list); -typedef void (GLAD_API_PTR *PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void * lists); -typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); -typedef void (GLAD_API_PTR *PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); -typedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); -typedef void (GLAD_API_PTR *PFNGLCLEARACCUMPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); -typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint * value); -typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHPROC)(GLdouble depth); -typedef void (GLAD_API_PTR *PFNGLCLEARINDEXPROC)(GLfloat c); -typedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); -typedef void (GLAD_API_PTR *PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture); -typedef GLenum (GLAD_API_PTR *PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); -typedef void (GLAD_API_PTR *PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble * equation); -typedef void (GLAD_API_PTR *PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); -typedef void (GLAD_API_PTR *PFNGLCOLOR3BVPROC)(const GLbyte * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); -typedef void (GLAD_API_PTR *PFNGLCOLOR3DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); -typedef void (GLAD_API_PTR *PFNGLCOLOR3FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue); -typedef void (GLAD_API_PTR *PFNGLCOLOR3IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); -typedef void (GLAD_API_PTR *PFNGLCOLOR3SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); -typedef void (GLAD_API_PTR *PFNGLCOLOR3UBVPROC)(const GLubyte * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); -typedef void (GLAD_API_PTR *PFNGLCOLOR3UIVPROC)(const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); -typedef void (GLAD_API_PTR *PFNGLCOLOR3USVPROC)(const GLushort * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); -typedef void (GLAD_API_PTR *PFNGLCOLOR4BVPROC)(const GLbyte * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); -typedef void (GLAD_API_PTR *PFNGLCOLOR4DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -typedef void (GLAD_API_PTR *PFNGLCOLOR4FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha); -typedef void (GLAD_API_PTR *PFNGLCOLOR4IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha); -typedef void (GLAD_API_PTR *PFNGLCOLOR4SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); -typedef void (GLAD_API_PTR *PFNGLCOLOR4UBVPROC)(const GLubyte * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha); -typedef void (GLAD_API_PTR *PFNGLCOLOR4UIVPROC)(const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha); -typedef void (GLAD_API_PTR *PFNGLCOLOR4USVPROC)(const GLushort * v); -typedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); -typedef void (GLAD_API_PTR *PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); -typedef void (GLAD_API_PTR *PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode); -typedef void (GLAD_API_PTR *PFNGLCOLORP3UIPROC)(GLenum type, GLuint color); -typedef void (GLAD_API_PTR *PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint * color); -typedef void (GLAD_API_PTR *PFNGLCOLORP4UIPROC)(GLenum type, GLuint color); -typedef void (GLAD_API_PTR *PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint * color); -typedef void (GLAD_API_PTR *PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); -typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); -typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); -typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); -typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); -typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); -typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); -typedef void (GLAD_API_PTR *PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); -typedef void (GLAD_API_PTR *PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); -typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); -typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLACCUMPROC)(GLenum op, GLfloat value); +typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); +typedef void (GLAD_API_PTR *PFNGLALPHAFUNCPROC)(GLenum func, GLfloat ref); +typedef GLboolean (GLAD_API_PTR *PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint * textures, GLboolean * residences); +typedef void (GLAD_API_PTR *PFNGLARRAYELEMENTPROC)(GLint i); +typedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLBEGINPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); +typedef void (GLAD_API_PTR *PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); +typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); +typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +typedef void (GLAD_API_PTR *PFNGLBINDVERTEXARRAYPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte * bitmap); +typedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); +typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); +typedef void (GLAD_API_PTR *PFNGLCALLLISTPROC)(GLuint list); +typedef void (GLAD_API_PTR *PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void * lists); +typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); +typedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLCLEARACCUMPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHPROC)(GLdouble depth); +typedef void (GLAD_API_PTR *PFNGLCLEARINDEXPROC)(GLfloat c); +typedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); +typedef void (GLAD_API_PTR *PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture); +typedef GLenum (GLAD_API_PTR *PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (GLAD_API_PTR *PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble * equation); +typedef void (GLAD_API_PTR *PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3BVPROC)(const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3UBVPROC)(const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3UIVPROC)(const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3USVPROC)(const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4BVPROC)(const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4UBVPROC)(const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4UIVPROC)(const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4USVPROC)(const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (GLAD_API_PTR *PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLCOLORP3UIPROC)(GLenum type, GLuint color); +typedef void (GLAD_API_PTR *PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint * color); +typedef void (GLAD_API_PTR *PFNGLCOLORP4UIPROC)(GLenum type, GLuint color); +typedef void (GLAD_API_PTR *PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint * color); +typedef void (GLAD_API_PTR *PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef GLuint (GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void); -typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); -typedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); -typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void * userParam); -typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); -typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); -typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); -typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); -typedef void (GLAD_API_PTR *PFNGLDELETELISTSPROC)(GLuint list, GLsizei range); -typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); -typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint * ids); -typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); -typedef void (GLAD_API_PTR *PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint * samplers); -typedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); -typedef void (GLAD_API_PTR *PFNGLDELETESYNCPROC)(GLsync sync); -typedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); -typedef void (GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint * arrays); -typedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); -typedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); -typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); -typedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); -typedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); -typedef void (GLAD_API_PTR *PFNGLDISABLECLIENTSTATEPROC)(GLenum array); -typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); -typedef void (GLAD_API_PTR *PFNGLDISABLEIPROC)(GLenum target, GLuint index); -typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); -typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); -typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf); -typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs); -typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); -typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); -typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount); -typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); -typedef void (GLAD_API_PTR *PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); -typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); -typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); -typedef void (GLAD_API_PTR *PFNGLEDGEFLAGPROC)(GLboolean flag); -typedef void (GLAD_API_PTR *PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLEDGEFLAGVPROC)(const GLboolean * flag); -typedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); -typedef void (GLAD_API_PTR *PFNGLENABLECLIENTSTATEPROC)(GLenum array); -typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); -typedef void (GLAD_API_PTR *PFNGLENABLEIPROC)(GLenum target, GLuint index); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); +typedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void * userParam); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); +typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLDELETELISTSPROC)(GLuint list, GLsizei range); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint * samplers); +typedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDELETESYNCPROC)(GLsync sync); +typedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); +typedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); +typedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLDISABLECLIENTSTATEPROC)(GLenum array); +typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEIPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLEDGEFLAGPROC)(GLboolean flag); +typedef void (GLAD_API_PTR *PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLEDGEFLAGVPROC)(const GLboolean * flag); +typedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLENABLECLIENTSTATEPROC)(GLenum array); +typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEIPROC)(GLenum target, GLuint index); typedef void (GLAD_API_PTR *PFNGLENDPROC)(void); typedef void (GLAD_API_PTR *PFNGLENDCONDITIONALRENDERPROC)(void); typedef void (GLAD_API_PTR *PFNGLENDLISTPROC)(void); -typedef void (GLAD_API_PTR *PFNGLENDQUERYPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLENDQUERYPROC)(GLenum target); typedef void (GLAD_API_PTR *PFNGLENDTRANSFORMFEEDBACKPROC)(void); -typedef void (GLAD_API_PTR *PFNGLEVALCOORD1DPROC)(GLdouble u); -typedef void (GLAD_API_PTR *PFNGLEVALCOORD1DVPROC)(const GLdouble * u); -typedef void (GLAD_API_PTR *PFNGLEVALCOORD1FPROC)(GLfloat u); -typedef void (GLAD_API_PTR *PFNGLEVALCOORD1FVPROC)(const GLfloat * u); -typedef void (GLAD_API_PTR *PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v); -typedef void (GLAD_API_PTR *PFNGLEVALCOORD2DVPROC)(const GLdouble * u); -typedef void (GLAD_API_PTR *PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v); -typedef void (GLAD_API_PTR *PFNGLEVALCOORD2FVPROC)(const GLfloat * u); -typedef void (GLAD_API_PTR *PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2); -typedef void (GLAD_API_PTR *PFNGLEVALMESH2PROC)(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); -typedef void (GLAD_API_PTR *PFNGLEVALPOINT1PROC)(GLint i); -typedef void (GLAD_API_PTR *PFNGLEVALPOINT2PROC)(GLint i, GLint j); -typedef void (GLAD_API_PTR *PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat * buffer); -typedef GLsync (GLAD_API_PTR *PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD1DPROC)(GLdouble u); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD1DVPROC)(const GLdouble * u); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD1FPROC)(GLfloat u); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD1FVPROC)(const GLfloat * u); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD2DVPROC)(const GLdouble * u); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD2FVPROC)(const GLfloat * u); +typedef void (GLAD_API_PTR *PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2); +typedef void (GLAD_API_PTR *PFNGLEVALMESH2PROC)(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); +typedef void (GLAD_API_PTR *PFNGLEVALPOINT1PROC)(GLint i); +typedef void (GLAD_API_PTR *PFNGLEVALPOINT2PROC)(GLint i, GLint j); +typedef void (GLAD_API_PTR *PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat * buffer); +typedef GLsync (GLAD_API_PTR *PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); typedef void (GLAD_API_PTR *PFNGLFINISHPROC)(void); typedef void (GLAD_API_PTR *PFNGLFLUSHPROC)(void); -typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); -typedef void (GLAD_API_PTR *PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLFOGCOORDDPROC)(GLdouble coord); -typedef void (GLAD_API_PTR *PFNGLFOGCOORDDVPROC)(const GLdouble * coord); -typedef void (GLAD_API_PTR *PFNGLFOGCOORDFPROC)(GLfloat coord); -typedef void (GLAD_API_PTR *PFNGLFOGCOORDFVPROC)(const GLfloat * coord); -typedef void (GLAD_API_PTR *PFNGLFOGFPROC)(GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLFOGFVPROC)(GLenum pname, const GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLFOGIPROC)(GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLFOGIVPROC)(GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); -typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); -typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -typedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); -typedef void (GLAD_API_PTR *PFNGLFRUSTUMPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -typedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); -typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); -typedef GLuint (GLAD_API_PTR *PFNGLGENLISTSPROC)(GLsizei range); -typedef void (GLAD_API_PTR *PFNGLGENQUERIESPROC)(GLsizei n, GLuint * ids); -typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); -typedef void (GLAD_API_PTR *PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint * samplers); -typedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); -typedef void (GLAD_API_PTR *PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); -typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); -typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); -typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); -typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformBlockName); -typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformName); -typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint * uniformIndices, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); -typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); -typedef void (GLAD_API_PTR *PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean * data); -typedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); -typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 * params); -typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void ** params); -typedef void (GLAD_API_PTR *PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void * data); -typedef void (GLAD_API_PTR *PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble * equation); -typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void * img); -typedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); -typedef void (GLAD_API_PTR *PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble * data); +typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDDPROC)(GLdouble coord); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDDVPROC)(const GLdouble * coord); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDFPROC)(GLfloat coord); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDFVPROC)(const GLfloat * coord); +typedef void (GLAD_API_PTR *PFNGLFOGFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLFOGFVPROC)(GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLFOGIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLFOGIVPROC)(GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLFRUSTUMPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef GLuint (GLAD_API_PTR *PFNGLGENLISTSPROC)(GLsizei range); +typedef void (GLAD_API_PTR *PFNGLGENQUERIESPROC)(GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint * samplers); +typedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformBlockName); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformName); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint * uniformIndices, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); +typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void * data); +typedef void (GLAD_API_PTR *PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble * equation); +typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void * img); +typedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); +typedef void (GLAD_API_PTR *PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble * data); typedef GLenum (GLAD_API_PTR *PFNGLGETERRORPROC)(void); -typedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); -typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar * name); -typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar * name); -typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); +typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar * name); +typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); typedef GLenum (GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSARBPROC)(void); -typedef void (GLAD_API_PTR *PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 * data); -typedef void (GLAD_API_PTR *PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 * data); -typedef void (GLAD_API_PTR *PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint * data); -typedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); -typedef void (GLAD_API_PTR *PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint * v); -typedef void (GLAD_API_PTR *PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat * val); -typedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label); -typedef void (GLAD_API_PTR *PFNGLGETOBJECTPTRLABELPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label); -typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat * values); -typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint * values); -typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort * values); -typedef void (GLAD_API_PTR *PFNGLGETPOINTERVPROC)(GLenum pname, void ** params); -typedef void (GLAD_API_PTR *PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte * mask); -typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); -typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 * params); -typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 * params); -typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint * params); -typedef void (GLAD_API_PTR *PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint * params); -typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); -typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); -typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); -typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); -typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); -typedef void (GLAD_API_PTR *PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei * length, GLint * values); -typedef void (GLAD_API_PTR *PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void * pixels); -typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name); -typedef GLuint (GLAD_API_PTR *PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar * uniformBlockName); -typedef void (GLAD_API_PTR *PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const* uniformNames, GLuint * uniformIndices); -typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); -typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint * params); -typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint * params); -typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); -typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble * params); -typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETNCOLORTABLEARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * table); -typedef void (GLAD_API_PTR *PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint lod, GLsizei bufSize, void * img); -typedef void (GLAD_API_PTR *PFNGLGETNCONVOLUTIONFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * image); -typedef void (GLAD_API_PTR *PFNGLGETNHISTOGRAMARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); -typedef void (GLAD_API_PTR *PFNGLGETNMAPDVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLGETNMAPFVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLGETNMAPIVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint * v); -typedef void (GLAD_API_PTR *PFNGLGETNMINMAXARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); -typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPFVARBPROC)(GLenum map, GLsizei bufSize, GLfloat * values); -typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPUIVARBPROC)(GLenum map, GLsizei bufSize, GLuint * values); -typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPUSVARBPROC)(GLenum map, GLsizei bufSize, GLushort * values); -typedef void (GLAD_API_PTR *PFNGLGETNPOLYGONSTIPPLEARBPROC)(GLsizei bufSize, GLubyte * pattern); -typedef void (GLAD_API_PTR *PFNGLGETNSEPARABLEFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void * row, GLsizei columnBufSize, void * column, void * span); -typedef void (GLAD_API_PTR *PFNGLGETNTEXIMAGEARBPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * img); -typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMDVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble * params); -typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); -typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); -typedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); -typedef void (GLAD_API_PTR *PFNGLINDEXMASKPROC)(GLuint mask); -typedef void (GLAD_API_PTR *PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLINDEXDPROC)(GLdouble c); -typedef void (GLAD_API_PTR *PFNGLINDEXDVPROC)(const GLdouble * c); -typedef void (GLAD_API_PTR *PFNGLINDEXFPROC)(GLfloat c); -typedef void (GLAD_API_PTR *PFNGLINDEXFVPROC)(const GLfloat * c); -typedef void (GLAD_API_PTR *PFNGLINDEXIPROC)(GLint c); -typedef void (GLAD_API_PTR *PFNGLINDEXIVPROC)(const GLint * c); -typedef void (GLAD_API_PTR *PFNGLINDEXSPROC)(GLshort c); -typedef void (GLAD_API_PTR *PFNGLINDEXSVPROC)(const GLshort * c); -typedef void (GLAD_API_PTR *PFNGLINDEXUBPROC)(GLubyte c); -typedef void (GLAD_API_PTR *PFNGLINDEXUBVPROC)(const GLubyte * c); +typedef void (GLAD_API_PTR *PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint * v); +typedef void (GLAD_API_PTR *PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat * val); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTPTRLABELPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat * values); +typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint * values); +typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort * values); +typedef void (GLAD_API_PTR *PFNGLGETPOINTERVPROC)(GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte * mask); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); +typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); +typedef void (GLAD_API_PTR *PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei * length, GLint * values); +typedef void (GLAD_API_PTR *PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name); +typedef GLuint (GLAD_API_PTR *PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar * uniformBlockName); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const* uniformNames, GLuint * uniformIndices); +typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNCOLORTABLEARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * table); +typedef void (GLAD_API_PTR *PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint lod, GLsizei bufSize, void * img); +typedef void (GLAD_API_PTR *PFNGLGETNCONVOLUTIONFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * image); +typedef void (GLAD_API_PTR *PFNGLGETNHISTOGRAMARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); +typedef void (GLAD_API_PTR *PFNGLGETNMAPDVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLGETNMAPFVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLGETNMAPIVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint * v); +typedef void (GLAD_API_PTR *PFNGLGETNMINMAXARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); +typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPFVARBPROC)(GLenum map, GLsizei bufSize, GLfloat * values); +typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPUIVARBPROC)(GLenum map, GLsizei bufSize, GLuint * values); +typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPUSVARBPROC)(GLenum map, GLsizei bufSize, GLushort * values); +typedef void (GLAD_API_PTR *PFNGLGETNPOLYGONSTIPPLEARBPROC)(GLsizei bufSize, GLubyte * pattern); +typedef void (GLAD_API_PTR *PFNGLGETNSEPARABLEFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void * row, GLsizei columnBufSize, void * column, void * span); +typedef void (GLAD_API_PTR *PFNGLGETNTEXIMAGEARBPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * img); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMDVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLINDEXMASKPROC)(GLuint mask); +typedef void (GLAD_API_PTR *PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLINDEXDPROC)(GLdouble c); +typedef void (GLAD_API_PTR *PFNGLINDEXDVPROC)(const GLdouble * c); +typedef void (GLAD_API_PTR *PFNGLINDEXFPROC)(GLfloat c); +typedef void (GLAD_API_PTR *PFNGLINDEXFVPROC)(const GLfloat * c); +typedef void (GLAD_API_PTR *PFNGLINDEXIPROC)(GLint c); +typedef void (GLAD_API_PTR *PFNGLINDEXIVPROC)(const GLint * c); +typedef void (GLAD_API_PTR *PFNGLINDEXSPROC)(GLshort c); +typedef void (GLAD_API_PTR *PFNGLINDEXSVPROC)(const GLshort * c); +typedef void (GLAD_API_PTR *PFNGLINDEXUBPROC)(GLubyte c); +typedef void (GLAD_API_PTR *PFNGLINDEXUBVPROC)(const GLubyte * c); typedef void (GLAD_API_PTR *PFNGLINITNAMESPROC)(void); -typedef void (GLAD_API_PTR *PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void * pointer); -typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); -typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); -typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIPROC)(GLenum target, GLuint index); -typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); -typedef GLboolean (GLAD_API_PTR *PFNGLISLISTPROC)(GLuint list); -typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); -typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYPROC)(GLuint id); -typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); -typedef GLboolean (GLAD_API_PTR *PFNGLISSAMPLERPROC)(GLuint sampler); -typedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); -typedef GLboolean (GLAD_API_PTR *PFNGLISSYNCPROC)(GLsync sync); -typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); -typedef GLboolean (GLAD_API_PTR *PFNGLISVERTEXARRAYPROC)(GLuint array); -typedef void (GLAD_API_PTR *PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern); -typedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); -typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); -typedef void (GLAD_API_PTR *PFNGLLISTBASEPROC)(GLuint base); +typedef void (GLAD_API_PTR *PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void * pointer); +typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIPROC)(GLenum target, GLuint index); +typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISLISTPROC)(GLuint list); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); +typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYPROC)(GLuint id); +typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISSAMPLERPROC)(GLuint sampler); +typedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); +typedef GLboolean (GLAD_API_PTR *PFNGLISSYNCPROC)(GLsync sync); +typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); +typedef GLboolean (GLAD_API_PTR *PFNGLISVERTEXARRAYPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern); +typedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); +typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLLISTBASEPROC)(GLuint base); typedef void (GLAD_API_PTR *PFNGLLOADIDENTITYPROC)(void); -typedef void (GLAD_API_PTR *PFNGLLOADMATRIXDPROC)(const GLdouble * m); -typedef void (GLAD_API_PTR *PFNGLLOADMATRIXFPROC)(const GLfloat * m); -typedef void (GLAD_API_PTR *PFNGLLOADNAMEPROC)(GLuint name); -typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble * m); -typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat * m); -typedef void (GLAD_API_PTR *PFNGLLOGICOPPROC)(GLenum opcode); -typedef void (GLAD_API_PTR *PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points); -typedef void (GLAD_API_PTR *PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points); -typedef void (GLAD_API_PTR *PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points); -typedef void (GLAD_API_PTR *PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points); -typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); -typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); -typedef void (GLAD_API_PTR *PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2); -typedef void (GLAD_API_PTR *PFNGLMAPGRID1FPROC)(GLint un, GLfloat u1, GLfloat u2); -typedef void (GLAD_API_PTR *PFNGLMAPGRID2DPROC)(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); -typedef void (GLAD_API_PTR *PFNGLMAPGRID2FPROC)(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); -typedef void (GLAD_API_PTR *PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLMATRIXMODEPROC)(GLenum mode); -typedef void (GLAD_API_PTR *PFNGLMULTMATRIXDPROC)(const GLdouble * m); -typedef void (GLAD_API_PTR *PFNGLMULTMATRIXFPROC)(const GLfloat * m); -typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble * m); -typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat * m); -typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei drawcount); -typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount); -typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount, const GLint * basevertex); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLNEWLISTPROC)(GLuint list, GLenum mode); -typedef void (GLAD_API_PTR *PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz); -typedef void (GLAD_API_PTR *PFNGLNORMAL3BVPROC)(const GLbyte * v); -typedef void (GLAD_API_PTR *PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz); -typedef void (GLAD_API_PTR *PFNGLNORMAL3DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz); -typedef void (GLAD_API_PTR *PFNGLNORMAL3FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz); -typedef void (GLAD_API_PTR *PFNGLNORMAL3IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz); -typedef void (GLAD_API_PTR *PFNGLNORMAL3SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label); -typedef void (GLAD_API_PTR *PFNGLOBJECTPTRLABELPROC)(const void * ptr, GLsizei length, const GLchar * label); -typedef void (GLAD_API_PTR *PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -typedef void (GLAD_API_PTR *PFNGLPASSTHROUGHPROC)(GLfloat token); -typedef void (GLAD_API_PTR *PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat * values); -typedef void (GLAD_API_PTR *PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint * values); -typedef void (GLAD_API_PTR *PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort * values); -typedef void (GLAD_API_PTR *PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERFPROC)(GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLPIXELZOOMPROC)(GLfloat xfactor, GLfloat yfactor); -typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLPOINTSIZEPROC)(GLfloat size); -typedef void (GLAD_API_PTR *PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); -typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); -typedef void (GLAD_API_PTR *PFNGLPOLYGONSTIPPLEPROC)(const GLubyte * mask); +typedef void (GLAD_API_PTR *PFNGLLOADMATRIXDPROC)(const GLdouble * m); +typedef void (GLAD_API_PTR *PFNGLLOADMATRIXFPROC)(const GLfloat * m); +typedef void (GLAD_API_PTR *PFNGLLOADNAMEPROC)(GLuint name); +typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble * m); +typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat * m); +typedef void (GLAD_API_PTR *PFNGLLOGICOPPROC)(GLenum opcode); +typedef void (GLAD_API_PTR *PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points); +typedef void (GLAD_API_PTR *PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points); +typedef void (GLAD_API_PTR *PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points); +typedef void (GLAD_API_PTR *PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (GLAD_API_PTR *PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2); +typedef void (GLAD_API_PTR *PFNGLMAPGRID1FPROC)(GLint un, GLfloat u1, GLfloat u2); +typedef void (GLAD_API_PTR *PFNGLMAPGRID2DPROC)(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); +typedef void (GLAD_API_PTR *PFNGLMAPGRID2FPROC)(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLMATRIXMODEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLMULTMATRIXDPROC)(const GLdouble * m); +typedef void (GLAD_API_PTR *PFNGLMULTMATRIXFPROC)(const GLfloat * m); +typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble * m); +typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat * m); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei drawcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount, const GLint * basevertex); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLNEWLISTPROC)(GLuint list, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (GLAD_API_PTR *PFNGLNORMAL3BVPROC)(const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (GLAD_API_PTR *PFNGLNORMAL3DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (GLAD_API_PTR *PFNGLNORMAL3FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz); +typedef void (GLAD_API_PTR *PFNGLNORMAL3IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz); +typedef void (GLAD_API_PTR *PFNGLNORMAL3SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLOBJECTPTRLABELPROC)(const void * ptr, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (GLAD_API_PTR *PFNGLPASSTHROUGHPROC)(GLfloat token); +typedef void (GLAD_API_PTR *PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat * values); +typedef void (GLAD_API_PTR *PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint * values); +typedef void (GLAD_API_PTR *PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort * values); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPIXELZOOMPROC)(GLfloat xfactor, GLfloat yfactor); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLPOINTSIZEPROC)(GLfloat size); +typedef void (GLAD_API_PTR *PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +typedef void (GLAD_API_PTR *PFNGLPOLYGONSTIPPLEPROC)(const GLubyte * mask); typedef void (GLAD_API_PTR *PFNGLPOPATTRIBPROC)(void); typedef void (GLAD_API_PTR *PFNGLPOPCLIENTATTRIBPROC)(void); typedef void (GLAD_API_PTR *PFNGLPOPDEBUGGROUPPROC)(void); typedef void (GLAD_API_PTR *PFNGLPOPMATRIXPROC)(void); typedef void (GLAD_API_PTR *PFNGLPOPNAMEPROC)(void); -typedef void (GLAD_API_PTR *PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); -typedef void (GLAD_API_PTR *PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint * textures, const GLfloat * priorities); -typedef void (GLAD_API_PTR *PFNGLPROVOKINGVERTEXPROC)(GLenum mode); -typedef void (GLAD_API_PTR *PFNGLPUSHATTRIBPROC)(GLbitfield mask); -typedef void (GLAD_API_PTR *PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask); -typedef void (GLAD_API_PTR *PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message); +typedef void (GLAD_API_PTR *PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint * textures, const GLfloat * priorities); +typedef void (GLAD_API_PTR *PFNGLPROVOKINGVERTEXPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLPUSHATTRIBPROC)(GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message); typedef void (GLAD_API_PTR *PFNGLPUSHMATRIXPROC)(void); -typedef void (GLAD_API_PTR *PFNGLPUSHNAMEPROC)(GLuint name); -typedef void (GLAD_API_PTR *PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS2DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS2FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS2IPROC)(GLint x, GLint y); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS2IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS2SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS3DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS3FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS3IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS3SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS4DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS4FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS4IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); -typedef void (GLAD_API_PTR *PFNGLRASTERPOS4SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLREADBUFFERPROC)(GLenum src); -typedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); -typedef void (GLAD_API_PTR *PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); -typedef void (GLAD_API_PTR *PFNGLREADNPIXELSARBPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); -typedef void (GLAD_API_PTR *PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); -typedef void (GLAD_API_PTR *PFNGLRECTDVPROC)(const GLdouble * v1, const GLdouble * v2); -typedef void (GLAD_API_PTR *PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); -typedef void (GLAD_API_PTR *PFNGLRECTFVPROC)(const GLfloat * v1, const GLfloat * v2); -typedef void (GLAD_API_PTR *PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2); -typedef void (GLAD_API_PTR *PFNGLRECTIVPROC)(const GLint * v1, const GLint * v2); -typedef void (GLAD_API_PTR *PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2); -typedef void (GLAD_API_PTR *PFNGLRECTSVPROC)(const GLshort * v1, const GLshort * v2); -typedef GLint (GLAD_API_PTR *PFNGLRENDERMODEPROC)(GLenum mode); -typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (GLAD_API_PTR *PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); -typedef void (GLAD_API_PTR *PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); -typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); -typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEARBPROC)(GLfloat value, GLboolean invert); -typedef void (GLAD_API_PTR *PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); -typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint * param); -typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint * param); -typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat * param); -typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint * param); -typedef void (GLAD_API_PTR *PFNGLSCALEDPROC)(GLdouble x, GLdouble y, GLdouble z); -typedef void (GLAD_API_PTR *PFNGLSCALEFPROC)(GLfloat x, GLfloat y, GLfloat z); -typedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte * v); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte * v); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3USVPROC)(const GLushort * v); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint * color); -typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint * buffer); -typedef void (GLAD_API_PTR *PFNGLSHADEMODELPROC)(GLenum mode); -typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); -typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); -typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); -typedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); -typedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); -typedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); -typedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); -typedef void (GLAD_API_PTR *PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD1DPROC)(GLdouble s); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD1DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD1FPROC)(GLfloat s); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD1FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD1IPROC)(GLint s); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD1IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD1SPROC)(GLshort s); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD1SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD2DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD2FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD2IPROC)(GLint s, GLint t); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD2IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD2SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD3DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD3FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD3IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD3SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD4DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD4FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD4IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q); -typedef void (GLAD_API_PTR *PFNGLTEXCOORD4SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint * coords); -typedef void (GLAD_API_PTR *PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param); -typedef void (GLAD_API_PTR *PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble * params); -typedef void (GLAD_API_PTR *PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels); -typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); -typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); -typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); -typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); -typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint * params); -typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); -typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); -typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); -typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); -typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); -typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); -typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); -typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode); -typedef void (GLAD_API_PTR *PFNGLTRANSLATEDPROC)(GLdouble x, GLdouble y, GLdouble z); -typedef void (GLAD_API_PTR *PFNGLTRANSLATEFPROC)(GLfloat x, GLfloat y, GLfloat z); -typedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); -typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); -typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); -typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); -typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); -typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); -typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); -typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); -typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFERPROC)(GLenum target); -typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); -typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); -typedef void (GLAD_API_PTR *PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y); -typedef void (GLAD_API_PTR *PFNGLVERTEX2DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y); -typedef void (GLAD_API_PTR *PFNGLVERTEX2FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX2IPROC)(GLint x, GLint y); -typedef void (GLAD_API_PTR *PFNGLVERTEX2IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX2SPROC)(GLshort x, GLshort y); -typedef void (GLAD_API_PTR *PFNGLVERTEX2SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z); -typedef void (GLAD_API_PTR *PFNGLVERTEX3DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z); -typedef void (GLAD_API_PTR *PFNGLVERTEX3FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z); -typedef void (GLAD_API_PTR *PFNGLVERTEX3IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z); -typedef void (GLAD_API_PTR *PFNGLVERTEX3SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (GLAD_API_PTR *PFNGLVERTEX4DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (GLAD_API_PTR *PFNGLVERTEX4FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w); -typedef void (GLAD_API_PTR *PFNGLVERTEX4IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); -typedef void (GLAD_API_PTR *PFNGLVERTEX4SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort * v); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value); -typedef void (GLAD_API_PTR *PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value); -typedef void (GLAD_API_PTR *PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value); -typedef void (GLAD_API_PTR *PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint * value); -typedef void (GLAD_API_PTR *PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); -typedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (GLAD_API_PTR *PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IPROC)(GLint x, GLint y); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SVPROC)(const GLshort * v); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DVPROC)(const GLdouble * v); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FVPROC)(const GLfloat * v); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IVPROC)(const GLint * v); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z); -typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLPUSHNAMEPROC)(GLuint name); +typedef void (GLAD_API_PTR *PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2IPROC)(GLint x, GLint y); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLREADBUFFERPROC)(GLenum src); +typedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLREADNPIXELSARBPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); +typedef void (GLAD_API_PTR *PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); +typedef void (GLAD_API_PTR *PFNGLRECTDVPROC)(const GLdouble * v1, const GLdouble * v2); +typedef void (GLAD_API_PTR *PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); +typedef void (GLAD_API_PTR *PFNGLRECTFVPROC)(const GLfloat * v1, const GLfloat * v2); +typedef void (GLAD_API_PTR *PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2); +typedef void (GLAD_API_PTR *PFNGLRECTIVPROC)(const GLint * v1, const GLint * v2); +typedef void (GLAD_API_PTR *PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2); +typedef void (GLAD_API_PTR *PFNGLRECTSVPROC)(const GLshort * v1, const GLshort * v2); +typedef GLint (GLAD_API_PTR *PFNGLRENDERMODEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEARBPROC)(GLfloat value, GLboolean invert); +typedef void (GLAD_API_PTR *PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSCALEDPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLSCALEFPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3USVPROC)(const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint * color); +typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint * buffer); +typedef void (GLAD_API_PTR *PFNGLSHADEMODELPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1DPROC)(GLdouble s); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1FPROC)(GLfloat s); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1IPROC)(GLint s); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1SPROC)(GLshort s); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2IPROC)(GLint s, GLint t); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint * coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param); +typedef void (GLAD_API_PTR *PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode); +typedef void (GLAD_API_PTR *PFNGLTRANSLATEDPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLTRANSLATEFPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFERPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLVERTEX2DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLVERTEX2FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX2IPROC)(GLint x, GLint y); +typedef void (GLAD_API_PTR *PFNGLVERTEX2IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX2SPROC)(GLshort x, GLshort y); +typedef void (GLAD_API_PTR *PFNGLVERTEX2SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLVERTEX3DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLVERTEX3FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z); +typedef void (GLAD_API_PTR *PFNGLVERTEX3IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void (GLAD_API_PTR *PFNGLVERTEX3SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLVERTEX4DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLVERTEX4FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w); +typedef void (GLAD_API_PTR *PFNGLVERTEX4IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (GLAD_API_PTR *PFNGLVERTEX4SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IPROC)(GLint x, GLint y); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SVPROC)(const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DVPROC)(const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FVPROC)(const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IVPROC)(const GLint * v); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SVPROC)(const GLshort * v); GLAD_API_CALL PFNGLACCUMPROC glad_glAccum; #define glAccum glad_glAccum @@ -3270,8 +3622,6 @@ GLAD_API_CALL PFNGLREADBUFFERPROC glad_glReadBuffer; #define glReadBuffer glad_glReadBuffer GLAD_API_CALL PFNGLREADPIXELSPROC glad_glReadPixels; #define glReadPixels glad_glReadPixels -GLAD_API_CALL PFNGLREADNPIXELSPROC glad_glReadnPixels; -#define glReadnPixels glad_glReadnPixels GLAD_API_CALL PFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB; #define glReadnPixelsARB glad_glReadnPixelsARB GLAD_API_CALL PFNGLRECTDPROC glad_glRectd; @@ -3826,15 +4176,1821 @@ GLAD_API_CALL PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv; #define glWindowPos3sv glad_glWindowPos3sv + + + GLAD_API_CALL int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr); GLAD_API_CALL int gladLoadGL( GLADloadfunc load); - - - #ifdef __cplusplus } #endif #endif + +/* Source */ +#ifdef GLAD_GL_IMPLEMENTATION +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_GL_VERSION_1_0 = 0; +int GLAD_GL_VERSION_1_1 = 0; +int GLAD_GL_VERSION_1_2 = 0; +int GLAD_GL_VERSION_1_3 = 0; +int GLAD_GL_VERSION_1_4 = 0; +int GLAD_GL_VERSION_1_5 = 0; +int GLAD_GL_VERSION_2_0 = 0; +int GLAD_GL_VERSION_2_1 = 0; +int GLAD_GL_VERSION_3_0 = 0; +int GLAD_GL_VERSION_3_1 = 0; +int GLAD_GL_VERSION_3_2 = 0; +int GLAD_GL_VERSION_3_3 = 0; +int GLAD_GL_ARB_multisample = 0; +int GLAD_GL_ARB_robustness = 0; +int GLAD_GL_KHR_debug = 0; + + + +PFNGLACCUMPROC glad_glAccum = NULL; +PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; +PFNGLALPHAFUNCPROC glad_glAlphaFunc = NULL; +PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident = NULL; +PFNGLARRAYELEMENTPROC glad_glArrayElement = NULL; +PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; +PFNGLBEGINPROC glad_glBegin = NULL; +PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL; +PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; +PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; +PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; +PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; +PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; +PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; +PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL; +PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL; +PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; +PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; +PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL; +PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; +PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; +PFNGLBITMAPPROC glad_glBitmap = NULL; +PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; +PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; +PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; +PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; +PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; +PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; +PFNGLBUFFERDATAPROC glad_glBufferData = NULL; +PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; +PFNGLCALLLISTPROC glad_glCallList = NULL; +PFNGLCALLLISTSPROC glad_glCallLists = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; +PFNGLCLAMPCOLORPROC glad_glClampColor = NULL; +PFNGLCLEARPROC glad_glClear = NULL; +PFNGLCLEARACCUMPROC glad_glClearAccum = NULL; +PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; +PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; +PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; +PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; +PFNGLCLEARCOLORPROC glad_glClearColor = NULL; +PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL; +PFNGLCLEARINDEXPROC glad_glClearIndex = NULL; +PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; +PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture = NULL; +PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL; +PFNGLCLIPPLANEPROC glad_glClipPlane = NULL; +PFNGLCOLOR3BPROC glad_glColor3b = NULL; +PFNGLCOLOR3BVPROC glad_glColor3bv = NULL; +PFNGLCOLOR3DPROC glad_glColor3d = NULL; +PFNGLCOLOR3DVPROC glad_glColor3dv = NULL; +PFNGLCOLOR3FPROC glad_glColor3f = NULL; +PFNGLCOLOR3FVPROC glad_glColor3fv = NULL; +PFNGLCOLOR3IPROC glad_glColor3i = NULL; +PFNGLCOLOR3IVPROC glad_glColor3iv = NULL; +PFNGLCOLOR3SPROC glad_glColor3s = NULL; +PFNGLCOLOR3SVPROC glad_glColor3sv = NULL; +PFNGLCOLOR3UBPROC glad_glColor3ub = NULL; +PFNGLCOLOR3UBVPROC glad_glColor3ubv = NULL; +PFNGLCOLOR3UIPROC glad_glColor3ui = NULL; +PFNGLCOLOR3UIVPROC glad_glColor3uiv = NULL; +PFNGLCOLOR3USPROC glad_glColor3us = NULL; +PFNGLCOLOR3USVPROC glad_glColor3usv = NULL; +PFNGLCOLOR4BPROC glad_glColor4b = NULL; +PFNGLCOLOR4BVPROC glad_glColor4bv = NULL; +PFNGLCOLOR4DPROC glad_glColor4d = NULL; +PFNGLCOLOR4DVPROC glad_glColor4dv = NULL; +PFNGLCOLOR4FPROC glad_glColor4f = NULL; +PFNGLCOLOR4FVPROC glad_glColor4fv = NULL; +PFNGLCOLOR4IPROC glad_glColor4i = NULL; +PFNGLCOLOR4IVPROC glad_glColor4iv = NULL; +PFNGLCOLOR4SPROC glad_glColor4s = NULL; +PFNGLCOLOR4SVPROC glad_glColor4sv = NULL; +PFNGLCOLOR4UBPROC glad_glColor4ub = NULL; +PFNGLCOLOR4UBVPROC glad_glColor4ubv = NULL; +PFNGLCOLOR4UIPROC glad_glColor4ui = NULL; +PFNGLCOLOR4UIVPROC glad_glColor4uiv = NULL; +PFNGLCOLOR4USPROC glad_glColor4us = NULL; +PFNGLCOLOR4USVPROC glad_glColor4usv = NULL; +PFNGLCOLORMASKPROC glad_glColorMask = NULL; +PFNGLCOLORMASKIPROC glad_glColorMaski = NULL; +PFNGLCOLORMATERIALPROC glad_glColorMaterial = NULL; +PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL; +PFNGLCOLORP3UIVPROC glad_glColorP3uiv = NULL; +PFNGLCOLORP4UIPROC glad_glColorP4ui = NULL; +PFNGLCOLORP4UIVPROC glad_glColorP4uiv = NULL; +PFNGLCOLORPOINTERPROC glad_glColorPointer = NULL; +PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; +PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; +PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; +PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; +PFNGLCOPYPIXELSPROC glad_glCopyPixels = NULL; +PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL; +PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL; +PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; +PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; +PFNGLCREATESHADERPROC glad_glCreateShader = NULL; +PFNGLCULLFACEPROC glad_glCullFace = NULL; +PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL; +PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL; +PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL; +PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; +PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; +PFNGLDELETELISTSPROC glad_glDeleteLists = NULL; +PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; +PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; +PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; +PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL; +PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; +PFNGLDELETESYNCPROC glad_glDeleteSync = NULL; +PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; +PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; +PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; +PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; +PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL; +PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; +PFNGLDISABLEPROC glad_glDisable = NULL; +PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; +PFNGLDISABLEIPROC glad_glDisablei = NULL; +PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; +PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; +PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; +PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; +PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; +PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL; +PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; +PFNGLDRAWPIXELSPROC glad_glDrawPixels = NULL; +PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; +PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; +PFNGLEDGEFLAGPROC glad_glEdgeFlag = NULL; +PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer = NULL; +PFNGLEDGEFLAGVPROC glad_glEdgeFlagv = NULL; +PFNGLENABLEPROC glad_glEnable = NULL; +PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState = NULL; +PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; +PFNGLENABLEIPROC glad_glEnablei = NULL; +PFNGLENDPROC glad_glEnd = NULL; +PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL; +PFNGLENDLISTPROC glad_glEndList = NULL; +PFNGLENDQUERYPROC glad_glEndQuery = NULL; +PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; +PFNGLEVALCOORD1DPROC glad_glEvalCoord1d = NULL; +PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv = NULL; +PFNGLEVALCOORD1FPROC glad_glEvalCoord1f = NULL; +PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv = NULL; +PFNGLEVALCOORD2DPROC glad_glEvalCoord2d = NULL; +PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv = NULL; +PFNGLEVALCOORD2FPROC glad_glEvalCoord2f = NULL; +PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv = NULL; +PFNGLEVALMESH1PROC glad_glEvalMesh1 = NULL; +PFNGLEVALMESH2PROC glad_glEvalMesh2 = NULL; +PFNGLEVALPOINT1PROC glad_glEvalPoint1 = NULL; +PFNGLEVALPOINT2PROC glad_glEvalPoint2 = NULL; +PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer = NULL; +PFNGLFENCESYNCPROC glad_glFenceSync = NULL; +PFNGLFINISHPROC glad_glFinish = NULL; +PFNGLFLUSHPROC glad_glFlush = NULL; +PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; +PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer = NULL; +PFNGLFOGCOORDDPROC glad_glFogCoordd = NULL; +PFNGLFOGCOORDDVPROC glad_glFogCoorddv = NULL; +PFNGLFOGCOORDFPROC glad_glFogCoordf = NULL; +PFNGLFOGCOORDFVPROC glad_glFogCoordfv = NULL; +PFNGLFOGFPROC glad_glFogf = NULL; +PFNGLFOGFVPROC glad_glFogfv = NULL; +PFNGLFOGIPROC glad_glFogi = NULL; +PFNGLFOGIVPROC glad_glFogiv = NULL; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; +PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL; +PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL; +PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; +PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL; +PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; +PFNGLFRONTFACEPROC glad_glFrontFace = NULL; +PFNGLFRUSTUMPROC glad_glFrustum = NULL; +PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; +PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; +PFNGLGENLISTSPROC glad_glGenLists = NULL; +PFNGLGENQUERIESPROC glad_glGenQueries = NULL; +PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; +PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL; +PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; +PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; +PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; +PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; +PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; +PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; +PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; +PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL; +PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; +PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; +PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; +PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL; +PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; +PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL; +PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; +PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; +PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL; +PFNGLGETCLIPPLANEPROC glad_glGetClipPlane = NULL; +PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL; +PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL; +PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL; +PFNGLGETERRORPROC glad_glGetError = NULL; +PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; +PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL; +PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; +PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB = NULL; +PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL; +PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL; +PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; +PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; +PFNGLGETLIGHTFVPROC glad_glGetLightfv = NULL; +PFNGLGETLIGHTIVPROC glad_glGetLightiv = NULL; +PFNGLGETMAPDVPROC glad_glGetMapdv = NULL; +PFNGLGETMAPFVPROC glad_glGetMapfv = NULL; +PFNGLGETMAPIVPROC glad_glGetMapiv = NULL; +PFNGLGETMATERIALFVPROC glad_glGetMaterialfv = NULL; +PFNGLGETMATERIALIVPROC glad_glGetMaterialiv = NULL; +PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL; +PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL; +PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL; +PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv = NULL; +PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv = NULL; +PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv = NULL; +PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL; +PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple = NULL; +PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; +PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; +PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL; +PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL; +PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL; +PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; +PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; +PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL; +PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL; +PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL; +PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL; +PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; +PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; +PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; +PFNGLGETSTRINGPROC glad_glGetString = NULL; +PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; +PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL; +PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv = NULL; +PFNGLGETTEXENVIVPROC glad_glGetTexEnviv = NULL; +PFNGLGETTEXGENDVPROC glad_glGetTexGendv = NULL; +PFNGLGETTEXGENFVPROC glad_glGetTexGenfv = NULL; +PFNGLGETTEXGENIVPROC glad_glGetTexGeniv = NULL; +PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL; +PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL; +PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL; +PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL; +PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL; +PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; +PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; +PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; +PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; +PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; +PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; +PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; +PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; +PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; +PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; +PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; +PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; +PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL; +PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; +PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; +PFNGLGETNCOLORTABLEARBPROC glad_glGetnColorTableARB = NULL; +PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB = NULL; +PFNGLGETNCONVOLUTIONFILTERARBPROC glad_glGetnConvolutionFilterARB = NULL; +PFNGLGETNHISTOGRAMARBPROC glad_glGetnHistogramARB = NULL; +PFNGLGETNMAPDVARBPROC glad_glGetnMapdvARB = NULL; +PFNGLGETNMAPFVARBPROC glad_glGetnMapfvARB = NULL; +PFNGLGETNMAPIVARBPROC glad_glGetnMapivARB = NULL; +PFNGLGETNMINMAXARBPROC glad_glGetnMinmaxARB = NULL; +PFNGLGETNPIXELMAPFVARBPROC glad_glGetnPixelMapfvARB = NULL; +PFNGLGETNPIXELMAPUIVARBPROC glad_glGetnPixelMapuivARB = NULL; +PFNGLGETNPIXELMAPUSVARBPROC glad_glGetnPixelMapusvARB = NULL; +PFNGLGETNPOLYGONSTIPPLEARBPROC glad_glGetnPolygonStippleARB = NULL; +PFNGLGETNSEPARABLEFILTERARBPROC glad_glGetnSeparableFilterARB = NULL; +PFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB = NULL; +PFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB = NULL; +PFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB = NULL; +PFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB = NULL; +PFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB = NULL; +PFNGLHINTPROC glad_glHint = NULL; +PFNGLINDEXMASKPROC glad_glIndexMask = NULL; +PFNGLINDEXPOINTERPROC glad_glIndexPointer = NULL; +PFNGLINDEXDPROC glad_glIndexd = NULL; +PFNGLINDEXDVPROC glad_glIndexdv = NULL; +PFNGLINDEXFPROC glad_glIndexf = NULL; +PFNGLINDEXFVPROC glad_glIndexfv = NULL; +PFNGLINDEXIPROC glad_glIndexi = NULL; +PFNGLINDEXIVPROC glad_glIndexiv = NULL; +PFNGLINDEXSPROC glad_glIndexs = NULL; +PFNGLINDEXSVPROC glad_glIndexsv = NULL; +PFNGLINDEXUBPROC glad_glIndexub = NULL; +PFNGLINDEXUBVPROC glad_glIndexubv = NULL; +PFNGLINITNAMESPROC glad_glInitNames = NULL; +PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays = NULL; +PFNGLISBUFFERPROC glad_glIsBuffer = NULL; +PFNGLISENABLEDPROC glad_glIsEnabled = NULL; +PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL; +PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; +PFNGLISLISTPROC glad_glIsList = NULL; +PFNGLISPROGRAMPROC glad_glIsProgram = NULL; +PFNGLISQUERYPROC glad_glIsQuery = NULL; +PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; +PFNGLISSAMPLERPROC glad_glIsSampler = NULL; +PFNGLISSHADERPROC glad_glIsShader = NULL; +PFNGLISSYNCPROC glad_glIsSync = NULL; +PFNGLISTEXTUREPROC glad_glIsTexture = NULL; +PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; +PFNGLLIGHTMODELFPROC glad_glLightModelf = NULL; +PFNGLLIGHTMODELFVPROC glad_glLightModelfv = NULL; +PFNGLLIGHTMODELIPROC glad_glLightModeli = NULL; +PFNGLLIGHTMODELIVPROC glad_glLightModeliv = NULL; +PFNGLLIGHTFPROC glad_glLightf = NULL; +PFNGLLIGHTFVPROC glad_glLightfv = NULL; +PFNGLLIGHTIPROC glad_glLighti = NULL; +PFNGLLIGHTIVPROC glad_glLightiv = NULL; +PFNGLLINESTIPPLEPROC glad_glLineStipple = NULL; +PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; +PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; +PFNGLLISTBASEPROC glad_glListBase = NULL; +PFNGLLOADIDENTITYPROC glad_glLoadIdentity = NULL; +PFNGLLOADMATRIXDPROC glad_glLoadMatrixd = NULL; +PFNGLLOADMATRIXFPROC glad_glLoadMatrixf = NULL; +PFNGLLOADNAMEPROC glad_glLoadName = NULL; +PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd = NULL; +PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf = NULL; +PFNGLLOGICOPPROC glad_glLogicOp = NULL; +PFNGLMAP1DPROC glad_glMap1d = NULL; +PFNGLMAP1FPROC glad_glMap1f = NULL; +PFNGLMAP2DPROC glad_glMap2d = NULL; +PFNGLMAP2FPROC glad_glMap2f = NULL; +PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL; +PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; +PFNGLMAPGRID1DPROC glad_glMapGrid1d = NULL; +PFNGLMAPGRID1FPROC glad_glMapGrid1f = NULL; +PFNGLMAPGRID2DPROC glad_glMapGrid2d = NULL; +PFNGLMAPGRID2FPROC glad_glMapGrid2f = NULL; +PFNGLMATERIALFPROC glad_glMaterialf = NULL; +PFNGLMATERIALFVPROC glad_glMaterialfv = NULL; +PFNGLMATERIALIPROC glad_glMateriali = NULL; +PFNGLMATERIALIVPROC glad_glMaterialiv = NULL; +PFNGLMATRIXMODEPROC glad_glMatrixMode = NULL; +PFNGLMULTMATRIXDPROC glad_glMultMatrixd = NULL; +PFNGLMULTMATRIXFPROC glad_glMultMatrixf = NULL; +PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd = NULL; +PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf = NULL; +PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL; +PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL; +PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL; +PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d = NULL; +PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv = NULL; +PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f = NULL; +PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv = NULL; +PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i = NULL; +PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv = NULL; +PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s = NULL; +PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv = NULL; +PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d = NULL; +PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv = NULL; +PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f = NULL; +PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv = NULL; +PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i = NULL; +PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv = NULL; +PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s = NULL; +PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv = NULL; +PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d = NULL; +PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv = NULL; +PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f = NULL; +PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv = NULL; +PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i = NULL; +PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv = NULL; +PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s = NULL; +PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv = NULL; +PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d = NULL; +PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv = NULL; +PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f = NULL; +PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv = NULL; +PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i = NULL; +PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv = NULL; +PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s = NULL; +PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv = NULL; +PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL; +PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL; +PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL; +PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv = NULL; +PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL; +PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL; +PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL; +PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL; +PFNGLNEWLISTPROC glad_glNewList = NULL; +PFNGLNORMAL3BPROC glad_glNormal3b = NULL; +PFNGLNORMAL3BVPROC glad_glNormal3bv = NULL; +PFNGLNORMAL3DPROC glad_glNormal3d = NULL; +PFNGLNORMAL3DVPROC glad_glNormal3dv = NULL; +PFNGLNORMAL3FPROC glad_glNormal3f = NULL; +PFNGLNORMAL3FVPROC glad_glNormal3fv = NULL; +PFNGLNORMAL3IPROC glad_glNormal3i = NULL; +PFNGLNORMAL3IVPROC glad_glNormal3iv = NULL; +PFNGLNORMAL3SPROC glad_glNormal3s = NULL; +PFNGLNORMAL3SVPROC glad_glNormal3sv = NULL; +PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL; +PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL; +PFNGLNORMALPOINTERPROC glad_glNormalPointer = NULL; +PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL; +PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL; +PFNGLORTHOPROC glad_glOrtho = NULL; +PFNGLPASSTHROUGHPROC glad_glPassThrough = NULL; +PFNGLPIXELMAPFVPROC glad_glPixelMapfv = NULL; +PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv = NULL; +PFNGLPIXELMAPUSVPROC glad_glPixelMapusv = NULL; +PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL; +PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; +PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf = NULL; +PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi = NULL; +PFNGLPIXELZOOMPROC glad_glPixelZoom = NULL; +PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL; +PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL; +PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL; +PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL; +PFNGLPOINTSIZEPROC glad_glPointSize = NULL; +PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL; +PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; +PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple = NULL; +PFNGLPOPATTRIBPROC glad_glPopAttrib = NULL; +PFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib = NULL; +PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL; +PFNGLPOPMATRIXPROC glad_glPopMatrix = NULL; +PFNGLPOPNAMEPROC glad_glPopName = NULL; +PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL; +PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures = NULL; +PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL; +PFNGLPUSHATTRIBPROC glad_glPushAttrib = NULL; +PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib = NULL; +PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL; +PFNGLPUSHMATRIXPROC glad_glPushMatrix = NULL; +PFNGLPUSHNAMEPROC glad_glPushName = NULL; +PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL; +PFNGLRASTERPOS2DPROC glad_glRasterPos2d = NULL; +PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv = NULL; +PFNGLRASTERPOS2FPROC glad_glRasterPos2f = NULL; +PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv = NULL; +PFNGLRASTERPOS2IPROC glad_glRasterPos2i = NULL; +PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv = NULL; +PFNGLRASTERPOS2SPROC glad_glRasterPos2s = NULL; +PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv = NULL; +PFNGLRASTERPOS3DPROC glad_glRasterPos3d = NULL; +PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv = NULL; +PFNGLRASTERPOS3FPROC glad_glRasterPos3f = NULL; +PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv = NULL; +PFNGLRASTERPOS3IPROC glad_glRasterPos3i = NULL; +PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv = NULL; +PFNGLRASTERPOS3SPROC glad_glRasterPos3s = NULL; +PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv = NULL; +PFNGLRASTERPOS4DPROC glad_glRasterPos4d = NULL; +PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv = NULL; +PFNGLRASTERPOS4FPROC glad_glRasterPos4f = NULL; +PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv = NULL; +PFNGLRASTERPOS4IPROC glad_glRasterPos4i = NULL; +PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv = NULL; +PFNGLRASTERPOS4SPROC glad_glRasterPos4s = NULL; +PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv = NULL; +PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; +PFNGLREADPIXELSPROC glad_glReadPixels = NULL; +PFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB = NULL; +PFNGLRECTDPROC glad_glRectd = NULL; +PFNGLRECTDVPROC glad_glRectdv = NULL; +PFNGLRECTFPROC glad_glRectf = NULL; +PFNGLRECTFVPROC glad_glRectfv = NULL; +PFNGLRECTIPROC glad_glRecti = NULL; +PFNGLRECTIVPROC glad_glRectiv = NULL; +PFNGLRECTSPROC glad_glRects = NULL; +PFNGLRECTSVPROC glad_glRectsv = NULL; +PFNGLRENDERMODEPROC glad_glRenderMode = NULL; +PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; +PFNGLROTATEDPROC glad_glRotated = NULL; +PFNGLROTATEFPROC glad_glRotatef = NULL; +PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; +PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB = NULL; +PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL; +PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL; +PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL; +PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL; +PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL; +PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL; +PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL; +PFNGLSCALEDPROC glad_glScaled = NULL; +PFNGLSCALEFPROC glad_glScalef = NULL; +PFNGLSCISSORPROC glad_glScissor = NULL; +PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b = NULL; +PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv = NULL; +PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d = NULL; +PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv = NULL; +PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f = NULL; +PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv = NULL; +PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i = NULL; +PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv = NULL; +PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s = NULL; +PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv = NULL; +PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub = NULL; +PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv = NULL; +PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui = NULL; +PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv = NULL; +PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us = NULL; +PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv = NULL; +PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui = NULL; +PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL; +PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer = NULL; +PFNGLSELECTBUFFERPROC glad_glSelectBuffer = NULL; +PFNGLSHADEMODELPROC glad_glShadeModel = NULL; +PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; +PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; +PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; +PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; +PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; +PFNGLSTENCILOPPROC glad_glStencilOp = NULL; +PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; +PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL; +PFNGLTEXCOORD1DPROC glad_glTexCoord1d = NULL; +PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv = NULL; +PFNGLTEXCOORD1FPROC glad_glTexCoord1f = NULL; +PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv = NULL; +PFNGLTEXCOORD1IPROC glad_glTexCoord1i = NULL; +PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv = NULL; +PFNGLTEXCOORD1SPROC glad_glTexCoord1s = NULL; +PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv = NULL; +PFNGLTEXCOORD2DPROC glad_glTexCoord2d = NULL; +PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv = NULL; +PFNGLTEXCOORD2FPROC glad_glTexCoord2f = NULL; +PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv = NULL; +PFNGLTEXCOORD2IPROC glad_glTexCoord2i = NULL; +PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv = NULL; +PFNGLTEXCOORD2SPROC glad_glTexCoord2s = NULL; +PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv = NULL; +PFNGLTEXCOORD3DPROC glad_glTexCoord3d = NULL; +PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv = NULL; +PFNGLTEXCOORD3FPROC glad_glTexCoord3f = NULL; +PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv = NULL; +PFNGLTEXCOORD3IPROC glad_glTexCoord3i = NULL; +PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv = NULL; +PFNGLTEXCOORD3SPROC glad_glTexCoord3s = NULL; +PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv = NULL; +PFNGLTEXCOORD4DPROC glad_glTexCoord4d = NULL; +PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv = NULL; +PFNGLTEXCOORD4FPROC glad_glTexCoord4f = NULL; +PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv = NULL; +PFNGLTEXCOORD4IPROC glad_glTexCoord4i = NULL; +PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv = NULL; +PFNGLTEXCOORD4SPROC glad_glTexCoord4s = NULL; +PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv = NULL; +PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui = NULL; +PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv = NULL; +PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui = NULL; +PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv = NULL; +PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui = NULL; +PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv = NULL; +PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui = NULL; +PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv = NULL; +PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer = NULL; +PFNGLTEXENVFPROC glad_glTexEnvf = NULL; +PFNGLTEXENVFVPROC glad_glTexEnvfv = NULL; +PFNGLTEXENVIPROC glad_glTexEnvi = NULL; +PFNGLTEXENVIVPROC glad_glTexEnviv = NULL; +PFNGLTEXGENDPROC glad_glTexGend = NULL; +PFNGLTEXGENDVPROC glad_glTexGendv = NULL; +PFNGLTEXGENFPROC glad_glTexGenf = NULL; +PFNGLTEXGENFVPROC glad_glTexGenfv = NULL; +PFNGLTEXGENIPROC glad_glTexGeni = NULL; +PFNGLTEXGENIVPROC glad_glTexGeniv = NULL; +PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL; +PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; +PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL; +PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; +PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL; +PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL; +PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL; +PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; +PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; +PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; +PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; +PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL; +PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; +PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; +PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; +PFNGLTRANSLATEDPROC glad_glTranslated = NULL; +PFNGLTRANSLATEFPROC glad_glTranslatef = NULL; +PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; +PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; +PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; +PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; +PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; +PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; +PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; +PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; +PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; +PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; +PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; +PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; +PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; +PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; +PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; +PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; +PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; +PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; +PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; +PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; +PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; +PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; +PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; +PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; +PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; +PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; +PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; +PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; +PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; +PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; +PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; +PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; +PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; +PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; +PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; +PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; +PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; +PFNGLVERTEX2DPROC glad_glVertex2d = NULL; +PFNGLVERTEX2DVPROC glad_glVertex2dv = NULL; +PFNGLVERTEX2FPROC glad_glVertex2f = NULL; +PFNGLVERTEX2FVPROC glad_glVertex2fv = NULL; +PFNGLVERTEX2IPROC glad_glVertex2i = NULL; +PFNGLVERTEX2IVPROC glad_glVertex2iv = NULL; +PFNGLVERTEX2SPROC glad_glVertex2s = NULL; +PFNGLVERTEX2SVPROC glad_glVertex2sv = NULL; +PFNGLVERTEX3DPROC glad_glVertex3d = NULL; +PFNGLVERTEX3DVPROC glad_glVertex3dv = NULL; +PFNGLVERTEX3FPROC glad_glVertex3f = NULL; +PFNGLVERTEX3FVPROC glad_glVertex3fv = NULL; +PFNGLVERTEX3IPROC glad_glVertex3i = NULL; +PFNGLVERTEX3IVPROC glad_glVertex3iv = NULL; +PFNGLVERTEX3SPROC glad_glVertex3s = NULL; +PFNGLVERTEX3SVPROC glad_glVertex3sv = NULL; +PFNGLVERTEX4DPROC glad_glVertex4d = NULL; +PFNGLVERTEX4DVPROC glad_glVertex4dv = NULL; +PFNGLVERTEX4FPROC glad_glVertex4f = NULL; +PFNGLVERTEX4FVPROC glad_glVertex4fv = NULL; +PFNGLVERTEX4IPROC glad_glVertex4i = NULL; +PFNGLVERTEX4IVPROC glad_glVertex4iv = NULL; +PFNGLVERTEX4SPROC glad_glVertex4s = NULL; +PFNGLVERTEX4SVPROC glad_glVertex4sv = NULL; +PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL; +PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL; +PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; +PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; +PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL; +PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL; +PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL; +PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL; +PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; +PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; +PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL; +PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL; +PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL; +PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL; +PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; +PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; +PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL; +PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL; +PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL; +PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL; +PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL; +PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL; +PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL; +PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL; +PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL; +PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL; +PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL; +PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL; +PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; +PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; +PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL; +PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL; +PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL; +PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL; +PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL; +PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL; +PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL; +PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL; +PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL; +PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL; +PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL; +PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL; +PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL; +PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL; +PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL; +PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL; +PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL; +PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL; +PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL; +PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL; +PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; +PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; +PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL; +PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL; +PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; +PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; +PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL; +PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; +PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL; +PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL; +PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL; +PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL; +PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL; +PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL; +PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL; +PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL; +PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; +PFNGLVERTEXP2UIPROC glad_glVertexP2ui = NULL; +PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv = NULL; +PFNGLVERTEXP3UIPROC glad_glVertexP3ui = NULL; +PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv = NULL; +PFNGLVERTEXP4UIPROC glad_glVertexP4ui = NULL; +PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv = NULL; +PFNGLVERTEXPOINTERPROC glad_glVertexPointer = NULL; +PFNGLVIEWPORTPROC glad_glViewport = NULL; +PFNGLWAITSYNCPROC glad_glWaitSync = NULL; +PFNGLWINDOWPOS2DPROC glad_glWindowPos2d = NULL; +PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv = NULL; +PFNGLWINDOWPOS2FPROC glad_glWindowPos2f = NULL; +PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv = NULL; +PFNGLWINDOWPOS2IPROC glad_glWindowPos2i = NULL; +PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv = NULL; +PFNGLWINDOWPOS2SPROC glad_glWindowPos2s = NULL; +PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv = NULL; +PFNGLWINDOWPOS3DPROC glad_glWindowPos3d = NULL; +PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv = NULL; +PFNGLWINDOWPOS3FPROC glad_glWindowPos3f = NULL; +PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv = NULL; +PFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL; +PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL; +PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL; +PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL; + + +static void glad_gl_load_GL_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_0) return; + glad_glAccum = (PFNGLACCUMPROC) load(userptr, "glAccum"); + glad_glAlphaFunc = (PFNGLALPHAFUNCPROC) load(userptr, "glAlphaFunc"); + glad_glBegin = (PFNGLBEGINPROC) load(userptr, "glBegin"); + glad_glBitmap = (PFNGLBITMAPPROC) load(userptr, "glBitmap"); + glad_glBlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, "glBlendFunc"); + glad_glCallList = (PFNGLCALLLISTPROC) load(userptr, "glCallList"); + glad_glCallLists = (PFNGLCALLLISTSPROC) load(userptr, "glCallLists"); + glad_glClear = (PFNGLCLEARPROC) load(userptr, "glClear"); + glad_glClearAccum = (PFNGLCLEARACCUMPROC) load(userptr, "glClearAccum"); + glad_glClearColor = (PFNGLCLEARCOLORPROC) load(userptr, "glClearColor"); + glad_glClearDepth = (PFNGLCLEARDEPTHPROC) load(userptr, "glClearDepth"); + glad_glClearIndex = (PFNGLCLEARINDEXPROC) load(userptr, "glClearIndex"); + glad_glClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, "glClearStencil"); + glad_glClipPlane = (PFNGLCLIPPLANEPROC) load(userptr, "glClipPlane"); + glad_glColor3b = (PFNGLCOLOR3BPROC) load(userptr, "glColor3b"); + glad_glColor3bv = (PFNGLCOLOR3BVPROC) load(userptr, "glColor3bv"); + glad_glColor3d = (PFNGLCOLOR3DPROC) load(userptr, "glColor3d"); + glad_glColor3dv = (PFNGLCOLOR3DVPROC) load(userptr, "glColor3dv"); + glad_glColor3f = (PFNGLCOLOR3FPROC) load(userptr, "glColor3f"); + glad_glColor3fv = (PFNGLCOLOR3FVPROC) load(userptr, "glColor3fv"); + glad_glColor3i = (PFNGLCOLOR3IPROC) load(userptr, "glColor3i"); + glad_glColor3iv = (PFNGLCOLOR3IVPROC) load(userptr, "glColor3iv"); + glad_glColor3s = (PFNGLCOLOR3SPROC) load(userptr, "glColor3s"); + glad_glColor3sv = (PFNGLCOLOR3SVPROC) load(userptr, "glColor3sv"); + glad_glColor3ub = (PFNGLCOLOR3UBPROC) load(userptr, "glColor3ub"); + glad_glColor3ubv = (PFNGLCOLOR3UBVPROC) load(userptr, "glColor3ubv"); + glad_glColor3ui = (PFNGLCOLOR3UIPROC) load(userptr, "glColor3ui"); + glad_glColor3uiv = (PFNGLCOLOR3UIVPROC) load(userptr, "glColor3uiv"); + glad_glColor3us = (PFNGLCOLOR3USPROC) load(userptr, "glColor3us"); + glad_glColor3usv = (PFNGLCOLOR3USVPROC) load(userptr, "glColor3usv"); + glad_glColor4b = (PFNGLCOLOR4BPROC) load(userptr, "glColor4b"); + glad_glColor4bv = (PFNGLCOLOR4BVPROC) load(userptr, "glColor4bv"); + glad_glColor4d = (PFNGLCOLOR4DPROC) load(userptr, "glColor4d"); + glad_glColor4dv = (PFNGLCOLOR4DVPROC) load(userptr, "glColor4dv"); + glad_glColor4f = (PFNGLCOLOR4FPROC) load(userptr, "glColor4f"); + glad_glColor4fv = (PFNGLCOLOR4FVPROC) load(userptr, "glColor4fv"); + glad_glColor4i = (PFNGLCOLOR4IPROC) load(userptr, "glColor4i"); + glad_glColor4iv = (PFNGLCOLOR4IVPROC) load(userptr, "glColor4iv"); + glad_glColor4s = (PFNGLCOLOR4SPROC) load(userptr, "glColor4s"); + glad_glColor4sv = (PFNGLCOLOR4SVPROC) load(userptr, "glColor4sv"); + glad_glColor4ub = (PFNGLCOLOR4UBPROC) load(userptr, "glColor4ub"); + glad_glColor4ubv = (PFNGLCOLOR4UBVPROC) load(userptr, "glColor4ubv"); + glad_glColor4ui = (PFNGLCOLOR4UIPROC) load(userptr, "glColor4ui"); + glad_glColor4uiv = (PFNGLCOLOR4UIVPROC) load(userptr, "glColor4uiv"); + glad_glColor4us = (PFNGLCOLOR4USPROC) load(userptr, "glColor4us"); + glad_glColor4usv = (PFNGLCOLOR4USVPROC) load(userptr, "glColor4usv"); + glad_glColorMask = (PFNGLCOLORMASKPROC) load(userptr, "glColorMask"); + glad_glColorMaterial = (PFNGLCOLORMATERIALPROC) load(userptr, "glColorMaterial"); + glad_glCopyPixels = (PFNGLCOPYPIXELSPROC) load(userptr, "glCopyPixels"); + glad_glCullFace = (PFNGLCULLFACEPROC) load(userptr, "glCullFace"); + glad_glDeleteLists = (PFNGLDELETELISTSPROC) load(userptr, "glDeleteLists"); + glad_glDepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, "glDepthFunc"); + glad_glDepthMask = (PFNGLDEPTHMASKPROC) load(userptr, "glDepthMask"); + glad_glDepthRange = (PFNGLDEPTHRANGEPROC) load(userptr, "glDepthRange"); + glad_glDisable = (PFNGLDISABLEPROC) load(userptr, "glDisable"); + glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC) load(userptr, "glDrawBuffer"); + glad_glDrawPixels = (PFNGLDRAWPIXELSPROC) load(userptr, "glDrawPixels"); + glad_glEdgeFlag = (PFNGLEDGEFLAGPROC) load(userptr, "glEdgeFlag"); + glad_glEdgeFlagv = (PFNGLEDGEFLAGVPROC) load(userptr, "glEdgeFlagv"); + glad_glEnable = (PFNGLENABLEPROC) load(userptr, "glEnable"); + glad_glEnd = (PFNGLENDPROC) load(userptr, "glEnd"); + glad_glEndList = (PFNGLENDLISTPROC) load(userptr, "glEndList"); + glad_glEvalCoord1d = (PFNGLEVALCOORD1DPROC) load(userptr, "glEvalCoord1d"); + glad_glEvalCoord1dv = (PFNGLEVALCOORD1DVPROC) load(userptr, "glEvalCoord1dv"); + glad_glEvalCoord1f = (PFNGLEVALCOORD1FPROC) load(userptr, "glEvalCoord1f"); + glad_glEvalCoord1fv = (PFNGLEVALCOORD1FVPROC) load(userptr, "glEvalCoord1fv"); + glad_glEvalCoord2d = (PFNGLEVALCOORD2DPROC) load(userptr, "glEvalCoord2d"); + glad_glEvalCoord2dv = (PFNGLEVALCOORD2DVPROC) load(userptr, "glEvalCoord2dv"); + glad_glEvalCoord2f = (PFNGLEVALCOORD2FPROC) load(userptr, "glEvalCoord2f"); + glad_glEvalCoord2fv = (PFNGLEVALCOORD2FVPROC) load(userptr, "glEvalCoord2fv"); + glad_glEvalMesh1 = (PFNGLEVALMESH1PROC) load(userptr, "glEvalMesh1"); + glad_glEvalMesh2 = (PFNGLEVALMESH2PROC) load(userptr, "glEvalMesh2"); + glad_glEvalPoint1 = (PFNGLEVALPOINT1PROC) load(userptr, "glEvalPoint1"); + glad_glEvalPoint2 = (PFNGLEVALPOINT2PROC) load(userptr, "glEvalPoint2"); + glad_glFeedbackBuffer = (PFNGLFEEDBACKBUFFERPROC) load(userptr, "glFeedbackBuffer"); + glad_glFinish = (PFNGLFINISHPROC) load(userptr, "glFinish"); + glad_glFlush = (PFNGLFLUSHPROC) load(userptr, "glFlush"); + glad_glFogf = (PFNGLFOGFPROC) load(userptr, "glFogf"); + glad_glFogfv = (PFNGLFOGFVPROC) load(userptr, "glFogfv"); + glad_glFogi = (PFNGLFOGIPROC) load(userptr, "glFogi"); + glad_glFogiv = (PFNGLFOGIVPROC) load(userptr, "glFogiv"); + glad_glFrontFace = (PFNGLFRONTFACEPROC) load(userptr, "glFrontFace"); + glad_glFrustum = (PFNGLFRUSTUMPROC) load(userptr, "glFrustum"); + glad_glGenLists = (PFNGLGENLISTSPROC) load(userptr, "glGenLists"); + glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, "glGetBooleanv"); + glad_glGetClipPlane = (PFNGLGETCLIPPLANEPROC) load(userptr, "glGetClipPlane"); + glad_glGetDoublev = (PFNGLGETDOUBLEVPROC) load(userptr, "glGetDoublev"); + glad_glGetError = (PFNGLGETERRORPROC) load(userptr, "glGetError"); + glad_glGetFloatv = (PFNGLGETFLOATVPROC) load(userptr, "glGetFloatv"); + glad_glGetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, "glGetIntegerv"); + glad_glGetLightfv = (PFNGLGETLIGHTFVPROC) load(userptr, "glGetLightfv"); + glad_glGetLightiv = (PFNGLGETLIGHTIVPROC) load(userptr, "glGetLightiv"); + glad_glGetMapdv = (PFNGLGETMAPDVPROC) load(userptr, "glGetMapdv"); + glad_glGetMapfv = (PFNGLGETMAPFVPROC) load(userptr, "glGetMapfv"); + glad_glGetMapiv = (PFNGLGETMAPIVPROC) load(userptr, "glGetMapiv"); + glad_glGetMaterialfv = (PFNGLGETMATERIALFVPROC) load(userptr, "glGetMaterialfv"); + glad_glGetMaterialiv = (PFNGLGETMATERIALIVPROC) load(userptr, "glGetMaterialiv"); + glad_glGetPixelMapfv = (PFNGLGETPIXELMAPFVPROC) load(userptr, "glGetPixelMapfv"); + glad_glGetPixelMapuiv = (PFNGLGETPIXELMAPUIVPROC) load(userptr, "glGetPixelMapuiv"); + glad_glGetPixelMapusv = (PFNGLGETPIXELMAPUSVPROC) load(userptr, "glGetPixelMapusv"); + glad_glGetPolygonStipple = (PFNGLGETPOLYGONSTIPPLEPROC) load(userptr, "glGetPolygonStipple"); + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + glad_glGetTexEnvfv = (PFNGLGETTEXENVFVPROC) load(userptr, "glGetTexEnvfv"); + glad_glGetTexEnviv = (PFNGLGETTEXENVIVPROC) load(userptr, "glGetTexEnviv"); + glad_glGetTexGendv = (PFNGLGETTEXGENDVPROC) load(userptr, "glGetTexGendv"); + glad_glGetTexGenfv = (PFNGLGETTEXGENFVPROC) load(userptr, "glGetTexGenfv"); + glad_glGetTexGeniv = (PFNGLGETTEXGENIVPROC) load(userptr, "glGetTexGeniv"); + glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC) load(userptr, "glGetTexImage"); + glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC) load(userptr, "glGetTexLevelParameterfv"); + glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC) load(userptr, "glGetTexLevelParameteriv"); + glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, "glGetTexParameterfv"); + glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, "glGetTexParameteriv"); + glad_glHint = (PFNGLHINTPROC) load(userptr, "glHint"); + glad_glIndexMask = (PFNGLINDEXMASKPROC) load(userptr, "glIndexMask"); + glad_glIndexd = (PFNGLINDEXDPROC) load(userptr, "glIndexd"); + glad_glIndexdv = (PFNGLINDEXDVPROC) load(userptr, "glIndexdv"); + glad_glIndexf = (PFNGLINDEXFPROC) load(userptr, "glIndexf"); + glad_glIndexfv = (PFNGLINDEXFVPROC) load(userptr, "glIndexfv"); + glad_glIndexi = (PFNGLINDEXIPROC) load(userptr, "glIndexi"); + glad_glIndexiv = (PFNGLINDEXIVPROC) load(userptr, "glIndexiv"); + glad_glIndexs = (PFNGLINDEXSPROC) load(userptr, "glIndexs"); + glad_glIndexsv = (PFNGLINDEXSVPROC) load(userptr, "glIndexsv"); + glad_glInitNames = (PFNGLINITNAMESPROC) load(userptr, "glInitNames"); + glad_glIsEnabled = (PFNGLISENABLEDPROC) load(userptr, "glIsEnabled"); + glad_glIsList = (PFNGLISLISTPROC) load(userptr, "glIsList"); + glad_glLightModelf = (PFNGLLIGHTMODELFPROC) load(userptr, "glLightModelf"); + glad_glLightModelfv = (PFNGLLIGHTMODELFVPROC) load(userptr, "glLightModelfv"); + glad_glLightModeli = (PFNGLLIGHTMODELIPROC) load(userptr, "glLightModeli"); + glad_glLightModeliv = (PFNGLLIGHTMODELIVPROC) load(userptr, "glLightModeliv"); + glad_glLightf = (PFNGLLIGHTFPROC) load(userptr, "glLightf"); + glad_glLightfv = (PFNGLLIGHTFVPROC) load(userptr, "glLightfv"); + glad_glLighti = (PFNGLLIGHTIPROC) load(userptr, "glLighti"); + glad_glLightiv = (PFNGLLIGHTIVPROC) load(userptr, "glLightiv"); + glad_glLineStipple = (PFNGLLINESTIPPLEPROC) load(userptr, "glLineStipple"); + glad_glLineWidth = (PFNGLLINEWIDTHPROC) load(userptr, "glLineWidth"); + glad_glListBase = (PFNGLLISTBASEPROC) load(userptr, "glListBase"); + glad_glLoadIdentity = (PFNGLLOADIDENTITYPROC) load(userptr, "glLoadIdentity"); + glad_glLoadMatrixd = (PFNGLLOADMATRIXDPROC) load(userptr, "glLoadMatrixd"); + glad_glLoadMatrixf = (PFNGLLOADMATRIXFPROC) load(userptr, "glLoadMatrixf"); + glad_glLoadName = (PFNGLLOADNAMEPROC) load(userptr, "glLoadName"); + glad_glLogicOp = (PFNGLLOGICOPPROC) load(userptr, "glLogicOp"); + glad_glMap1d = (PFNGLMAP1DPROC) load(userptr, "glMap1d"); + glad_glMap1f = (PFNGLMAP1FPROC) load(userptr, "glMap1f"); + glad_glMap2d = (PFNGLMAP2DPROC) load(userptr, "glMap2d"); + glad_glMap2f = (PFNGLMAP2FPROC) load(userptr, "glMap2f"); + glad_glMapGrid1d = (PFNGLMAPGRID1DPROC) load(userptr, "glMapGrid1d"); + glad_glMapGrid1f = (PFNGLMAPGRID1FPROC) load(userptr, "glMapGrid1f"); + glad_glMapGrid2d = (PFNGLMAPGRID2DPROC) load(userptr, "glMapGrid2d"); + glad_glMapGrid2f = (PFNGLMAPGRID2FPROC) load(userptr, "glMapGrid2f"); + glad_glMaterialf = (PFNGLMATERIALFPROC) load(userptr, "glMaterialf"); + glad_glMaterialfv = (PFNGLMATERIALFVPROC) load(userptr, "glMaterialfv"); + glad_glMateriali = (PFNGLMATERIALIPROC) load(userptr, "glMateriali"); + glad_glMaterialiv = (PFNGLMATERIALIVPROC) load(userptr, "glMaterialiv"); + glad_glMatrixMode = (PFNGLMATRIXMODEPROC) load(userptr, "glMatrixMode"); + glad_glMultMatrixd = (PFNGLMULTMATRIXDPROC) load(userptr, "glMultMatrixd"); + glad_glMultMatrixf = (PFNGLMULTMATRIXFPROC) load(userptr, "glMultMatrixf"); + glad_glNewList = (PFNGLNEWLISTPROC) load(userptr, "glNewList"); + glad_glNormal3b = (PFNGLNORMAL3BPROC) load(userptr, "glNormal3b"); + glad_glNormal3bv = (PFNGLNORMAL3BVPROC) load(userptr, "glNormal3bv"); + glad_glNormal3d = (PFNGLNORMAL3DPROC) load(userptr, "glNormal3d"); + glad_glNormal3dv = (PFNGLNORMAL3DVPROC) load(userptr, "glNormal3dv"); + glad_glNormal3f = (PFNGLNORMAL3FPROC) load(userptr, "glNormal3f"); + glad_glNormal3fv = (PFNGLNORMAL3FVPROC) load(userptr, "glNormal3fv"); + glad_glNormal3i = (PFNGLNORMAL3IPROC) load(userptr, "glNormal3i"); + glad_glNormal3iv = (PFNGLNORMAL3IVPROC) load(userptr, "glNormal3iv"); + glad_glNormal3s = (PFNGLNORMAL3SPROC) load(userptr, "glNormal3s"); + glad_glNormal3sv = (PFNGLNORMAL3SVPROC) load(userptr, "glNormal3sv"); + glad_glOrtho = (PFNGLORTHOPROC) load(userptr, "glOrtho"); + glad_glPassThrough = (PFNGLPASSTHROUGHPROC) load(userptr, "glPassThrough"); + glad_glPixelMapfv = (PFNGLPIXELMAPFVPROC) load(userptr, "glPixelMapfv"); + glad_glPixelMapuiv = (PFNGLPIXELMAPUIVPROC) load(userptr, "glPixelMapuiv"); + glad_glPixelMapusv = (PFNGLPIXELMAPUSVPROC) load(userptr, "glPixelMapusv"); + glad_glPixelStoref = (PFNGLPIXELSTOREFPROC) load(userptr, "glPixelStoref"); + glad_glPixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, "glPixelStorei"); + glad_glPixelTransferf = (PFNGLPIXELTRANSFERFPROC) load(userptr, "glPixelTransferf"); + glad_glPixelTransferi = (PFNGLPIXELTRANSFERIPROC) load(userptr, "glPixelTransferi"); + glad_glPixelZoom = (PFNGLPIXELZOOMPROC) load(userptr, "glPixelZoom"); + glad_glPointSize = (PFNGLPOINTSIZEPROC) load(userptr, "glPointSize"); + glad_glPolygonMode = (PFNGLPOLYGONMODEPROC) load(userptr, "glPolygonMode"); + glad_glPolygonStipple = (PFNGLPOLYGONSTIPPLEPROC) load(userptr, "glPolygonStipple"); + glad_glPopAttrib = (PFNGLPOPATTRIBPROC) load(userptr, "glPopAttrib"); + glad_glPopMatrix = (PFNGLPOPMATRIXPROC) load(userptr, "glPopMatrix"); + glad_glPopName = (PFNGLPOPNAMEPROC) load(userptr, "glPopName"); + glad_glPushAttrib = (PFNGLPUSHATTRIBPROC) load(userptr, "glPushAttrib"); + glad_glPushMatrix = (PFNGLPUSHMATRIXPROC) load(userptr, "glPushMatrix"); + glad_glPushName = (PFNGLPUSHNAMEPROC) load(userptr, "glPushName"); + glad_glRasterPos2d = (PFNGLRASTERPOS2DPROC) load(userptr, "glRasterPos2d"); + glad_glRasterPos2dv = (PFNGLRASTERPOS2DVPROC) load(userptr, "glRasterPos2dv"); + glad_glRasterPos2f = (PFNGLRASTERPOS2FPROC) load(userptr, "glRasterPos2f"); + glad_glRasterPos2fv = (PFNGLRASTERPOS2FVPROC) load(userptr, "glRasterPos2fv"); + glad_glRasterPos2i = (PFNGLRASTERPOS2IPROC) load(userptr, "glRasterPos2i"); + glad_glRasterPos2iv = (PFNGLRASTERPOS2IVPROC) load(userptr, "glRasterPos2iv"); + glad_glRasterPos2s = (PFNGLRASTERPOS2SPROC) load(userptr, "glRasterPos2s"); + glad_glRasterPos2sv = (PFNGLRASTERPOS2SVPROC) load(userptr, "glRasterPos2sv"); + glad_glRasterPos3d = (PFNGLRASTERPOS3DPROC) load(userptr, "glRasterPos3d"); + glad_glRasterPos3dv = (PFNGLRASTERPOS3DVPROC) load(userptr, "glRasterPos3dv"); + glad_glRasterPos3f = (PFNGLRASTERPOS3FPROC) load(userptr, "glRasterPos3f"); + glad_glRasterPos3fv = (PFNGLRASTERPOS3FVPROC) load(userptr, "glRasterPos3fv"); + glad_glRasterPos3i = (PFNGLRASTERPOS3IPROC) load(userptr, "glRasterPos3i"); + glad_glRasterPos3iv = (PFNGLRASTERPOS3IVPROC) load(userptr, "glRasterPos3iv"); + glad_glRasterPos3s = (PFNGLRASTERPOS3SPROC) load(userptr, "glRasterPos3s"); + glad_glRasterPos3sv = (PFNGLRASTERPOS3SVPROC) load(userptr, "glRasterPos3sv"); + glad_glRasterPos4d = (PFNGLRASTERPOS4DPROC) load(userptr, "glRasterPos4d"); + glad_glRasterPos4dv = (PFNGLRASTERPOS4DVPROC) load(userptr, "glRasterPos4dv"); + glad_glRasterPos4f = (PFNGLRASTERPOS4FPROC) load(userptr, "glRasterPos4f"); + glad_glRasterPos4fv = (PFNGLRASTERPOS4FVPROC) load(userptr, "glRasterPos4fv"); + glad_glRasterPos4i = (PFNGLRASTERPOS4IPROC) load(userptr, "glRasterPos4i"); + glad_glRasterPos4iv = (PFNGLRASTERPOS4IVPROC) load(userptr, "glRasterPos4iv"); + glad_glRasterPos4s = (PFNGLRASTERPOS4SPROC) load(userptr, "glRasterPos4s"); + glad_glRasterPos4sv = (PFNGLRASTERPOS4SVPROC) load(userptr, "glRasterPos4sv"); + glad_glReadBuffer = (PFNGLREADBUFFERPROC) load(userptr, "glReadBuffer"); + glad_glReadPixels = (PFNGLREADPIXELSPROC) load(userptr, "glReadPixels"); + glad_glRectd = (PFNGLRECTDPROC) load(userptr, "glRectd"); + glad_glRectdv = (PFNGLRECTDVPROC) load(userptr, "glRectdv"); + glad_glRectf = (PFNGLRECTFPROC) load(userptr, "glRectf"); + glad_glRectfv = (PFNGLRECTFVPROC) load(userptr, "glRectfv"); + glad_glRecti = (PFNGLRECTIPROC) load(userptr, "glRecti"); + glad_glRectiv = (PFNGLRECTIVPROC) load(userptr, "glRectiv"); + glad_glRects = (PFNGLRECTSPROC) load(userptr, "glRects"); + glad_glRectsv = (PFNGLRECTSVPROC) load(userptr, "glRectsv"); + glad_glRenderMode = (PFNGLRENDERMODEPROC) load(userptr, "glRenderMode"); + glad_glRotated = (PFNGLROTATEDPROC) load(userptr, "glRotated"); + glad_glRotatef = (PFNGLROTATEFPROC) load(userptr, "glRotatef"); + glad_glScaled = (PFNGLSCALEDPROC) load(userptr, "glScaled"); + glad_glScalef = (PFNGLSCALEFPROC) load(userptr, "glScalef"); + glad_glScissor = (PFNGLSCISSORPROC) load(userptr, "glScissor"); + glad_glSelectBuffer = (PFNGLSELECTBUFFERPROC) load(userptr, "glSelectBuffer"); + glad_glShadeModel = (PFNGLSHADEMODELPROC) load(userptr, "glShadeModel"); + glad_glStencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, "glStencilFunc"); + glad_glStencilMask = (PFNGLSTENCILMASKPROC) load(userptr, "glStencilMask"); + glad_glStencilOp = (PFNGLSTENCILOPPROC) load(userptr, "glStencilOp"); + glad_glTexCoord1d = (PFNGLTEXCOORD1DPROC) load(userptr, "glTexCoord1d"); + glad_glTexCoord1dv = (PFNGLTEXCOORD1DVPROC) load(userptr, "glTexCoord1dv"); + glad_glTexCoord1f = (PFNGLTEXCOORD1FPROC) load(userptr, "glTexCoord1f"); + glad_glTexCoord1fv = (PFNGLTEXCOORD1FVPROC) load(userptr, "glTexCoord1fv"); + glad_glTexCoord1i = (PFNGLTEXCOORD1IPROC) load(userptr, "glTexCoord1i"); + glad_glTexCoord1iv = (PFNGLTEXCOORD1IVPROC) load(userptr, "glTexCoord1iv"); + glad_glTexCoord1s = (PFNGLTEXCOORD1SPROC) load(userptr, "glTexCoord1s"); + glad_glTexCoord1sv = (PFNGLTEXCOORD1SVPROC) load(userptr, "glTexCoord1sv"); + glad_glTexCoord2d = (PFNGLTEXCOORD2DPROC) load(userptr, "glTexCoord2d"); + glad_glTexCoord2dv = (PFNGLTEXCOORD2DVPROC) load(userptr, "glTexCoord2dv"); + glad_glTexCoord2f = (PFNGLTEXCOORD2FPROC) load(userptr, "glTexCoord2f"); + glad_glTexCoord2fv = (PFNGLTEXCOORD2FVPROC) load(userptr, "glTexCoord2fv"); + glad_glTexCoord2i = (PFNGLTEXCOORD2IPROC) load(userptr, "glTexCoord2i"); + glad_glTexCoord2iv = (PFNGLTEXCOORD2IVPROC) load(userptr, "glTexCoord2iv"); + glad_glTexCoord2s = (PFNGLTEXCOORD2SPROC) load(userptr, "glTexCoord2s"); + glad_glTexCoord2sv = (PFNGLTEXCOORD2SVPROC) load(userptr, "glTexCoord2sv"); + glad_glTexCoord3d = (PFNGLTEXCOORD3DPROC) load(userptr, "glTexCoord3d"); + glad_glTexCoord3dv = (PFNGLTEXCOORD3DVPROC) load(userptr, "glTexCoord3dv"); + glad_glTexCoord3f = (PFNGLTEXCOORD3FPROC) load(userptr, "glTexCoord3f"); + glad_glTexCoord3fv = (PFNGLTEXCOORD3FVPROC) load(userptr, "glTexCoord3fv"); + glad_glTexCoord3i = (PFNGLTEXCOORD3IPROC) load(userptr, "glTexCoord3i"); + glad_glTexCoord3iv = (PFNGLTEXCOORD3IVPROC) load(userptr, "glTexCoord3iv"); + glad_glTexCoord3s = (PFNGLTEXCOORD3SPROC) load(userptr, "glTexCoord3s"); + glad_glTexCoord3sv = (PFNGLTEXCOORD3SVPROC) load(userptr, "glTexCoord3sv"); + glad_glTexCoord4d = (PFNGLTEXCOORD4DPROC) load(userptr, "glTexCoord4d"); + glad_glTexCoord4dv = (PFNGLTEXCOORD4DVPROC) load(userptr, "glTexCoord4dv"); + glad_glTexCoord4f = (PFNGLTEXCOORD4FPROC) load(userptr, "glTexCoord4f"); + glad_glTexCoord4fv = (PFNGLTEXCOORD4FVPROC) load(userptr, "glTexCoord4fv"); + glad_glTexCoord4i = (PFNGLTEXCOORD4IPROC) load(userptr, "glTexCoord4i"); + glad_glTexCoord4iv = (PFNGLTEXCOORD4IVPROC) load(userptr, "glTexCoord4iv"); + glad_glTexCoord4s = (PFNGLTEXCOORD4SPROC) load(userptr, "glTexCoord4s"); + glad_glTexCoord4sv = (PFNGLTEXCOORD4SVPROC) load(userptr, "glTexCoord4sv"); + glad_glTexEnvf = (PFNGLTEXENVFPROC) load(userptr, "glTexEnvf"); + glad_glTexEnvfv = (PFNGLTEXENVFVPROC) load(userptr, "glTexEnvfv"); + glad_glTexEnvi = (PFNGLTEXENVIPROC) load(userptr, "glTexEnvi"); + glad_glTexEnviv = (PFNGLTEXENVIVPROC) load(userptr, "glTexEnviv"); + glad_glTexGend = (PFNGLTEXGENDPROC) load(userptr, "glTexGend"); + glad_glTexGendv = (PFNGLTEXGENDVPROC) load(userptr, "glTexGendv"); + glad_glTexGenf = (PFNGLTEXGENFPROC) load(userptr, "glTexGenf"); + glad_glTexGenfv = (PFNGLTEXGENFVPROC) load(userptr, "glTexGenfv"); + glad_glTexGeni = (PFNGLTEXGENIPROC) load(userptr, "glTexGeni"); + glad_glTexGeniv = (PFNGLTEXGENIVPROC) load(userptr, "glTexGeniv"); + glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC) load(userptr, "glTexImage1D"); + glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, "glTexImage2D"); + glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, "glTexParameterf"); + glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, "glTexParameterfv"); + glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, "glTexParameteri"); + glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, "glTexParameteriv"); + glad_glTranslated = (PFNGLTRANSLATEDPROC) load(userptr, "glTranslated"); + glad_glTranslatef = (PFNGLTRANSLATEFPROC) load(userptr, "glTranslatef"); + glad_glVertex2d = (PFNGLVERTEX2DPROC) load(userptr, "glVertex2d"); + glad_glVertex2dv = (PFNGLVERTEX2DVPROC) load(userptr, "glVertex2dv"); + glad_glVertex2f = (PFNGLVERTEX2FPROC) load(userptr, "glVertex2f"); + glad_glVertex2fv = (PFNGLVERTEX2FVPROC) load(userptr, "glVertex2fv"); + glad_glVertex2i = (PFNGLVERTEX2IPROC) load(userptr, "glVertex2i"); + glad_glVertex2iv = (PFNGLVERTEX2IVPROC) load(userptr, "glVertex2iv"); + glad_glVertex2s = (PFNGLVERTEX2SPROC) load(userptr, "glVertex2s"); + glad_glVertex2sv = (PFNGLVERTEX2SVPROC) load(userptr, "glVertex2sv"); + glad_glVertex3d = (PFNGLVERTEX3DPROC) load(userptr, "glVertex3d"); + glad_glVertex3dv = (PFNGLVERTEX3DVPROC) load(userptr, "glVertex3dv"); + glad_glVertex3f = (PFNGLVERTEX3FPROC) load(userptr, "glVertex3f"); + glad_glVertex3fv = (PFNGLVERTEX3FVPROC) load(userptr, "glVertex3fv"); + glad_glVertex3i = (PFNGLVERTEX3IPROC) load(userptr, "glVertex3i"); + glad_glVertex3iv = (PFNGLVERTEX3IVPROC) load(userptr, "glVertex3iv"); + glad_glVertex3s = (PFNGLVERTEX3SPROC) load(userptr, "glVertex3s"); + glad_glVertex3sv = (PFNGLVERTEX3SVPROC) load(userptr, "glVertex3sv"); + glad_glVertex4d = (PFNGLVERTEX4DPROC) load(userptr, "glVertex4d"); + glad_glVertex4dv = (PFNGLVERTEX4DVPROC) load(userptr, "glVertex4dv"); + glad_glVertex4f = (PFNGLVERTEX4FPROC) load(userptr, "glVertex4f"); + glad_glVertex4fv = (PFNGLVERTEX4FVPROC) load(userptr, "glVertex4fv"); + glad_glVertex4i = (PFNGLVERTEX4IPROC) load(userptr, "glVertex4i"); + glad_glVertex4iv = (PFNGLVERTEX4IVPROC) load(userptr, "glVertex4iv"); + glad_glVertex4s = (PFNGLVERTEX4SPROC) load(userptr, "glVertex4s"); + glad_glVertex4sv = (PFNGLVERTEX4SVPROC) load(userptr, "glVertex4sv"); + glad_glViewport = (PFNGLVIEWPORTPROC) load(userptr, "glViewport"); +} +static void glad_gl_load_GL_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_1) return; + glad_glAreTexturesResident = (PFNGLARETEXTURESRESIDENTPROC) load(userptr, "glAreTexturesResident"); + glad_glArrayElement = (PFNGLARRAYELEMENTPROC) load(userptr, "glArrayElement"); + glad_glBindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, "glBindTexture"); + glad_glColorPointer = (PFNGLCOLORPOINTERPROC) load(userptr, "glColorPointer"); + glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC) load(userptr, "glCopyTexImage1D"); + glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, "glCopyTexImage2D"); + glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC) load(userptr, "glCopyTexSubImage1D"); + glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, "glCopyTexSubImage2D"); + glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, "glDeleteTextures"); + glad_glDisableClientState = (PFNGLDISABLECLIENTSTATEPROC) load(userptr, "glDisableClientState"); + glad_glDrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, "glDrawArrays"); + glad_glDrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, "glDrawElements"); + glad_glEdgeFlagPointer = (PFNGLEDGEFLAGPOINTERPROC) load(userptr, "glEdgeFlagPointer"); + glad_glEnableClientState = (PFNGLENABLECLIENTSTATEPROC) load(userptr, "glEnableClientState"); + glad_glGenTextures = (PFNGLGENTEXTURESPROC) load(userptr, "glGenTextures"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv"); + glad_glIndexPointer = (PFNGLINDEXPOINTERPROC) load(userptr, "glIndexPointer"); + glad_glIndexub = (PFNGLINDEXUBPROC) load(userptr, "glIndexub"); + glad_glIndexubv = (PFNGLINDEXUBVPROC) load(userptr, "glIndexubv"); + glad_glInterleavedArrays = (PFNGLINTERLEAVEDARRAYSPROC) load(userptr, "glInterleavedArrays"); + glad_glIsTexture = (PFNGLISTEXTUREPROC) load(userptr, "glIsTexture"); + glad_glNormalPointer = (PFNGLNORMALPOINTERPROC) load(userptr, "glNormalPointer"); + glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, "glPolygonOffset"); + glad_glPopClientAttrib = (PFNGLPOPCLIENTATTRIBPROC) load(userptr, "glPopClientAttrib"); + glad_glPrioritizeTextures = (PFNGLPRIORITIZETEXTURESPROC) load(userptr, "glPrioritizeTextures"); + glad_glPushClientAttrib = (PFNGLPUSHCLIENTATTRIBPROC) load(userptr, "glPushClientAttrib"); + glad_glTexCoordPointer = (PFNGLTEXCOORDPOINTERPROC) load(userptr, "glTexCoordPointer"); + glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC) load(userptr, "glTexSubImage1D"); + glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, "glTexSubImage2D"); + glad_glVertexPointer = (PFNGLVERTEXPOINTERPROC) load(userptr, "glVertexPointer"); +} +static void glad_gl_load_GL_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_2) return; + glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC) load(userptr, "glCopyTexSubImage3D"); + glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) load(userptr, "glDrawRangeElements"); + glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC) load(userptr, "glTexImage3D"); + glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC) load(userptr, "glTexSubImage3D"); +} +static void glad_gl_load_GL_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_3) return; + glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); + glad_glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC) load(userptr, "glClientActiveTexture"); + glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) load(userptr, "glCompressedTexImage1D"); + glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); + glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) load(userptr, "glCompressedTexImage3D"); + glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) load(userptr, "glCompressedTexSubImage1D"); + glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); + glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) load(userptr, "glCompressedTexSubImage3D"); + glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) load(userptr, "glGetCompressedTexImage"); + glad_glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC) load(userptr, "glLoadTransposeMatrixd"); + glad_glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC) load(userptr, "glLoadTransposeMatrixf"); + glad_glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC) load(userptr, "glMultTransposeMatrixd"); + glad_glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC) load(userptr, "glMultTransposeMatrixf"); + glad_glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC) load(userptr, "glMultiTexCoord1d"); + glad_glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC) load(userptr, "glMultiTexCoord1dv"); + glad_glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC) load(userptr, "glMultiTexCoord1f"); + glad_glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC) load(userptr, "glMultiTexCoord1fv"); + glad_glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC) load(userptr, "glMultiTexCoord1i"); + glad_glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC) load(userptr, "glMultiTexCoord1iv"); + glad_glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC) load(userptr, "glMultiTexCoord1s"); + glad_glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC) load(userptr, "glMultiTexCoord1sv"); + glad_glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC) load(userptr, "glMultiTexCoord2d"); + glad_glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC) load(userptr, "glMultiTexCoord2dv"); + glad_glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) load(userptr, "glMultiTexCoord2f"); + glad_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC) load(userptr, "glMultiTexCoord2fv"); + glad_glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC) load(userptr, "glMultiTexCoord2i"); + glad_glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC) load(userptr, "glMultiTexCoord2iv"); + glad_glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC) load(userptr, "glMultiTexCoord2s"); + glad_glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC) load(userptr, "glMultiTexCoord2sv"); + glad_glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC) load(userptr, "glMultiTexCoord3d"); + glad_glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC) load(userptr, "glMultiTexCoord3dv"); + glad_glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC) load(userptr, "glMultiTexCoord3f"); + glad_glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC) load(userptr, "glMultiTexCoord3fv"); + glad_glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC) load(userptr, "glMultiTexCoord3i"); + glad_glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC) load(userptr, "glMultiTexCoord3iv"); + glad_glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC) load(userptr, "glMultiTexCoord3s"); + glad_glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC) load(userptr, "glMultiTexCoord3sv"); + glad_glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC) load(userptr, "glMultiTexCoord4d"); + glad_glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC) load(userptr, "glMultiTexCoord4dv"); + glad_glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC) load(userptr, "glMultiTexCoord4f"); + glad_glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC) load(userptr, "glMultiTexCoord4fv"); + glad_glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC) load(userptr, "glMultiTexCoord4i"); + glad_glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC) load(userptr, "glMultiTexCoord4iv"); + glad_glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC) load(userptr, "glMultiTexCoord4s"); + glad_glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC) load(userptr, "glMultiTexCoord4sv"); + glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); +} +static void glad_gl_load_GL_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_4) return; + glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); + glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); + glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, "glBlendFuncSeparate"); + glad_glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC) load(userptr, "glFogCoordPointer"); + glad_glFogCoordd = (PFNGLFOGCOORDDPROC) load(userptr, "glFogCoordd"); + glad_glFogCoorddv = (PFNGLFOGCOORDDVPROC) load(userptr, "glFogCoorddv"); + glad_glFogCoordf = (PFNGLFOGCOORDFPROC) load(userptr, "glFogCoordf"); + glad_glFogCoordfv = (PFNGLFOGCOORDFVPROC) load(userptr, "glFogCoordfv"); + glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC) load(userptr, "glMultiDrawArrays"); + glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC) load(userptr, "glMultiDrawElements"); + glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC) load(userptr, "glPointParameterf"); + glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC) load(userptr, "glPointParameterfv"); + glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC) load(userptr, "glPointParameteri"); + glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC) load(userptr, "glPointParameteriv"); + glad_glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC) load(userptr, "glSecondaryColor3b"); + glad_glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC) load(userptr, "glSecondaryColor3bv"); + glad_glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC) load(userptr, "glSecondaryColor3d"); + glad_glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC) load(userptr, "glSecondaryColor3dv"); + glad_glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC) load(userptr, "glSecondaryColor3f"); + glad_glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC) load(userptr, "glSecondaryColor3fv"); + glad_glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC) load(userptr, "glSecondaryColor3i"); + glad_glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC) load(userptr, "glSecondaryColor3iv"); + glad_glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC) load(userptr, "glSecondaryColor3s"); + glad_glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC) load(userptr, "glSecondaryColor3sv"); + glad_glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC) load(userptr, "glSecondaryColor3ub"); + glad_glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC) load(userptr, "glSecondaryColor3ubv"); + glad_glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC) load(userptr, "glSecondaryColor3ui"); + glad_glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC) load(userptr, "glSecondaryColor3uiv"); + glad_glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC) load(userptr, "glSecondaryColor3us"); + glad_glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC) load(userptr, "glSecondaryColor3usv"); + glad_glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC) load(userptr, "glSecondaryColorPointer"); + glad_glWindowPos2d = (PFNGLWINDOWPOS2DPROC) load(userptr, "glWindowPos2d"); + glad_glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC) load(userptr, "glWindowPos2dv"); + glad_glWindowPos2f = (PFNGLWINDOWPOS2FPROC) load(userptr, "glWindowPos2f"); + glad_glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC) load(userptr, "glWindowPos2fv"); + glad_glWindowPos2i = (PFNGLWINDOWPOS2IPROC) load(userptr, "glWindowPos2i"); + glad_glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC) load(userptr, "glWindowPos2iv"); + glad_glWindowPos2s = (PFNGLWINDOWPOS2SPROC) load(userptr, "glWindowPos2s"); + glad_glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC) load(userptr, "glWindowPos2sv"); + glad_glWindowPos3d = (PFNGLWINDOWPOS3DPROC) load(userptr, "glWindowPos3d"); + glad_glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC) load(userptr, "glWindowPos3dv"); + glad_glWindowPos3f = (PFNGLWINDOWPOS3FPROC) load(userptr, "glWindowPos3f"); + glad_glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC) load(userptr, "glWindowPos3fv"); + glad_glWindowPos3i = (PFNGLWINDOWPOS3IPROC) load(userptr, "glWindowPos3i"); + glad_glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC) load(userptr, "glWindowPos3iv"); + glad_glWindowPos3s = (PFNGLWINDOWPOS3SPROC) load(userptr, "glWindowPos3s"); + glad_glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC) load(userptr, "glWindowPos3sv"); +} +static void glad_gl_load_GL_VERSION_1_5( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_5) return; + glad_glBeginQuery = (PFNGLBEGINQUERYPROC) load(userptr, "glBeginQuery"); + glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); + glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); + glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); + glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); + glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC) load(userptr, "glDeleteQueries"); + glad_glEndQuery = (PFNGLENDQUERYPROC) load(userptr, "glEndQuery"); + glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); + glad_glGenQueries = (PFNGLGENQUERIESPROC) load(userptr, "glGenQueries"); + glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); + glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) load(userptr, "glGetBufferPointerv"); + glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) load(userptr, "glGetBufferSubData"); + glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) load(userptr, "glGetQueryObjectiv"); + glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) load(userptr, "glGetQueryObjectuiv"); + glad_glGetQueryiv = (PFNGLGETQUERYIVPROC) load(userptr, "glGetQueryiv"); + glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); + glad_glIsQuery = (PFNGLISQUERYPROC) load(userptr, "glIsQuery"); + glad_glMapBuffer = (PFNGLMAPBUFFERPROC) load(userptr, "glMapBuffer"); + glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) load(userptr, "glUnmapBuffer"); +} +static void glad_gl_load_GL_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_2_0) return; + glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); + glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); + glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, "glBlendEquationSeparate"); + glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); + glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); + glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); + glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, "glDeleteProgram"); + glad_glDeleteShader = (PFNGLDELETESHADERPROC) load(userptr, "glDeleteShader"); + glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); + glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); + glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC) load(userptr, "glDrawBuffers"); + glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); + glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); + glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); + glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, "glGetAttachedShaders"); + glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); + glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, "glGetProgramInfoLog"); + glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, "glGetProgramiv"); + glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, "glGetShaderInfoLog"); + glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); + glad_glGetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, "glGetShaderiv"); + glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); + glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); + glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); + glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); + glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load(userptr, "glGetVertexAttribdv"); + glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); + glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); + glad_glIsProgram = (PFNGLISPROGRAMPROC) load(userptr, "glIsProgram"); + glad_glIsShader = (PFNGLISSHADERPROC) load(userptr, "glIsShader"); + glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); + glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); + glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, "glStencilFuncSeparate"); + glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, "glStencilMaskSeparate"); + glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, "glStencilOpSeparate"); + glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); + glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); + glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); + glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); + glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); + glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); + glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); + glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); + glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); + glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); + glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); + glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); + glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); + glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); + glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); + glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); + glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); + glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); + glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); + glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); + glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); + glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load(userptr, "glVertexAttrib1d"); + glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load(userptr, "glVertexAttrib1dv"); + glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); + glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); + glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load(userptr, "glVertexAttrib1s"); + glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load(userptr, "glVertexAttrib1sv"); + glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load(userptr, "glVertexAttrib2d"); + glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load(userptr, "glVertexAttrib2dv"); + glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); + glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); + glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load(userptr, "glVertexAttrib2s"); + glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load(userptr, "glVertexAttrib2sv"); + glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load(userptr, "glVertexAttrib3d"); + glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load(userptr, "glVertexAttrib3dv"); + glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); + glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); + glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load(userptr, "glVertexAttrib3s"); + glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load(userptr, "glVertexAttrib3sv"); + glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load(userptr, "glVertexAttrib4Nbv"); + glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load(userptr, "glVertexAttrib4Niv"); + glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load(userptr, "glVertexAttrib4Nsv"); + glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load(userptr, "glVertexAttrib4Nub"); + glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load(userptr, "glVertexAttrib4Nubv"); + glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load(userptr, "glVertexAttrib4Nuiv"); + glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load(userptr, "glVertexAttrib4Nusv"); + glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load(userptr, "glVertexAttrib4bv"); + glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load(userptr, "glVertexAttrib4d"); + glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load(userptr, "glVertexAttrib4dv"); + glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); + glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); + glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load(userptr, "glVertexAttrib4iv"); + glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load(userptr, "glVertexAttrib4s"); + glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load(userptr, "glVertexAttrib4sv"); + glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load(userptr, "glVertexAttrib4ubv"); + glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load(userptr, "glVertexAttrib4uiv"); + glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load(userptr, "glVertexAttrib4usv"); + glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); +} +static void glad_gl_load_GL_VERSION_2_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_2_1) return; + glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) load(userptr, "glUniformMatrix2x3fv"); + glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) load(userptr, "glUniformMatrix2x4fv"); + glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) load(userptr, "glUniformMatrix3x2fv"); + glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) load(userptr, "glUniformMatrix3x4fv"); + glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC) load(userptr, "glUniformMatrix4x2fv"); + glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) load(userptr, "glUniformMatrix4x3fv"); +} +static void glad_gl_load_GL_VERSION_3_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_0) return; + glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC) load(userptr, "glBeginConditionalRender"); + glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) load(userptr, "glBeginTransformFeedback"); + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC) load(userptr, "glBindFragDataLocation"); + glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); + glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); + glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load(userptr, "glBindVertexArray"); + glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load(userptr, "glBlitFramebuffer"); + glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); + glad_glClampColor = (PFNGLCLAMPCOLORPROC) load(userptr, "glClampColor"); + glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC) load(userptr, "glClearBufferfi"); + glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC) load(userptr, "glClearBufferfv"); + glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC) load(userptr, "glClearBufferiv"); + glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC) load(userptr, "glClearBufferuiv"); + glad_glColorMaski = (PFNGLCOLORMASKIPROC) load(userptr, "glColorMaski"); + glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); + glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); + glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load(userptr, "glDeleteVertexArrays"); + glad_glDisablei = (PFNGLDISABLEIPROC) load(userptr, "glDisablei"); + glad_glEnablei = (PFNGLENABLEIPROC) load(userptr, "glEnablei"); + glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC) load(userptr, "glEndConditionalRender"); + glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) load(userptr, "glEndTransformFeedback"); + glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load(userptr, "glFlushMappedBufferRange"); + glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); + glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load(userptr, "glFramebufferTexture1D"); + glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); + glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load(userptr, "glFramebufferTexture3D"); + glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glFramebufferTextureLayer"); + glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); + glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); + glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load(userptr, "glGenVertexArrays"); + glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); + glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC) load(userptr, "glGetBooleani_v"); + glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC) load(userptr, "glGetFragDataLocation"); + glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); + glad_glGetStringi = (PFNGLGETSTRINGIPROC) load(userptr, "glGetStringi"); + glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC) load(userptr, "glGetTexParameterIiv"); + glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC) load(userptr, "glGetTexParameterIuiv"); + glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) load(userptr, "glGetTransformFeedbackVarying"); + glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC) load(userptr, "glGetUniformuiv"); + glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC) load(userptr, "glGetVertexAttribIiv"); + glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC) load(userptr, "glGetVertexAttribIuiv"); + glad_glIsEnabledi = (PFNGLISENABLEDIPROC) load(userptr, "glIsEnabledi"); + glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); + glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); + glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC) load(userptr, "glIsVertexArray"); + glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load(userptr, "glMapBufferRange"); + glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); + glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample"); + glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC) load(userptr, "glTexParameterIiv"); + glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC) load(userptr, "glTexParameterIuiv"); + glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) load(userptr, "glTransformFeedbackVaryings"); + glad_glUniform1ui = (PFNGLUNIFORM1UIPROC) load(userptr, "glUniform1ui"); + glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC) load(userptr, "glUniform1uiv"); + glad_glUniform2ui = (PFNGLUNIFORM2UIPROC) load(userptr, "glUniform2ui"); + glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC) load(userptr, "glUniform2uiv"); + glad_glUniform3ui = (PFNGLUNIFORM3UIPROC) load(userptr, "glUniform3ui"); + glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC) load(userptr, "glUniform3uiv"); + glad_glUniform4ui = (PFNGLUNIFORM4UIPROC) load(userptr, "glUniform4ui"); + glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC) load(userptr, "glUniform4uiv"); + glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC) load(userptr, "glVertexAttribI1i"); + glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC) load(userptr, "glVertexAttribI1iv"); + glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC) load(userptr, "glVertexAttribI1ui"); + glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC) load(userptr, "glVertexAttribI1uiv"); + glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC) load(userptr, "glVertexAttribI2i"); + glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC) load(userptr, "glVertexAttribI2iv"); + glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC) load(userptr, "glVertexAttribI2ui"); + glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC) load(userptr, "glVertexAttribI2uiv"); + glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC) load(userptr, "glVertexAttribI3i"); + glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC) load(userptr, "glVertexAttribI3iv"); + glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC) load(userptr, "glVertexAttribI3ui"); + glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC) load(userptr, "glVertexAttribI3uiv"); + glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC) load(userptr, "glVertexAttribI4bv"); + glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC) load(userptr, "glVertexAttribI4i"); + glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC) load(userptr, "glVertexAttribI4iv"); + glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC) load(userptr, "glVertexAttribI4sv"); + glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC) load(userptr, "glVertexAttribI4ubv"); + glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC) load(userptr, "glVertexAttribI4ui"); + glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC) load(userptr, "glVertexAttribI4uiv"); + glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC) load(userptr, "glVertexAttribI4usv"); + glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) load(userptr, "glVertexAttribIPointer"); +} +static void glad_gl_load_GL_VERSION_3_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_1) return; + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load(userptr, "glCopyBufferSubData"); + glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) load(userptr, "glDrawArraysInstanced"); + glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) load(userptr, "glDrawElementsInstanced"); + glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load(userptr, "glGetActiveUniformBlockName"); + glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load(userptr, "glGetActiveUniformBlockiv"); + glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load(userptr, "glGetActiveUniformName"); + glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load(userptr, "glGetActiveUniformsiv"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load(userptr, "glGetUniformBlockIndex"); + glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load(userptr, "glGetUniformIndices"); + glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) load(userptr, "glPrimitiveRestartIndex"); + glad_glTexBuffer = (PFNGLTEXBUFFERPROC) load(userptr, "glTexBuffer"); + glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load(userptr, "glUniformBlockBinding"); +} +static void glad_gl_load_GL_VERSION_3_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_2) return; + glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) load(userptr, "glClientWaitSync"); + glad_glDeleteSync = (PFNGLDELETESYNCPROC) load(userptr, "glDeleteSync"); + glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glDrawElementsBaseVertex"); + glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) load(userptr, "glDrawElementsInstancedBaseVertex"); + glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) load(userptr, "glDrawRangeElementsBaseVertex"); + glad_glFenceSync = (PFNGLFENCESYNCPROC) load(userptr, "glFenceSync"); + glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC) load(userptr, "glFramebufferTexture"); + glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC) load(userptr, "glGetBufferParameteri64v"); + glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC) load(userptr, "glGetInteger64i_v"); + glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC) load(userptr, "glGetInteger64v"); + glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) load(userptr, "glGetMultisamplefv"); + glad_glGetSynciv = (PFNGLGETSYNCIVPROC) load(userptr, "glGetSynciv"); + glad_glIsSync = (PFNGLISSYNCPROC) load(userptr, "glIsSync"); + glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glMultiDrawElementsBaseVertex"); + glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC) load(userptr, "glProvokingVertex"); + glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC) load(userptr, "glSampleMaski"); + glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) load(userptr, "glTexImage2DMultisample"); + glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) load(userptr, "glTexImage3DMultisample"); + glad_glWaitSync = (PFNGLWAITSYNCPROC) load(userptr, "glWaitSync"); +} +static void glad_gl_load_GL_VERSION_3_3( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_3) return; + glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) load(userptr, "glBindFragDataLocationIndexed"); + glad_glBindSampler = (PFNGLBINDSAMPLERPROC) load(userptr, "glBindSampler"); + glad_glColorP3ui = (PFNGLCOLORP3UIPROC) load(userptr, "glColorP3ui"); + glad_glColorP3uiv = (PFNGLCOLORP3UIVPROC) load(userptr, "glColorP3uiv"); + glad_glColorP4ui = (PFNGLCOLORP4UIPROC) load(userptr, "glColorP4ui"); + glad_glColorP4uiv = (PFNGLCOLORP4UIVPROC) load(userptr, "glColorP4uiv"); + glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC) load(userptr, "glDeleteSamplers"); + glad_glGenSamplers = (PFNGLGENSAMPLERSPROC) load(userptr, "glGenSamplers"); + glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) load(userptr, "glGetFragDataIndex"); + glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) load(userptr, "glGetQueryObjecti64v"); + glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) load(userptr, "glGetQueryObjectui64v"); + glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC) load(userptr, "glGetSamplerParameterIiv"); + glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC) load(userptr, "glGetSamplerParameterIuiv"); + glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC) load(userptr, "glGetSamplerParameterfv"); + glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC) load(userptr, "glGetSamplerParameteriv"); + glad_glIsSampler = (PFNGLISSAMPLERPROC) load(userptr, "glIsSampler"); + glad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC) load(userptr, "glMultiTexCoordP1ui"); + glad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC) load(userptr, "glMultiTexCoordP1uiv"); + glad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC) load(userptr, "glMultiTexCoordP2ui"); + glad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC) load(userptr, "glMultiTexCoordP2uiv"); + glad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC) load(userptr, "glMultiTexCoordP3ui"); + glad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC) load(userptr, "glMultiTexCoordP3uiv"); + glad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC) load(userptr, "glMultiTexCoordP4ui"); + glad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC) load(userptr, "glMultiTexCoordP4uiv"); + glad_glNormalP3ui = (PFNGLNORMALP3UIPROC) load(userptr, "glNormalP3ui"); + glad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC) load(userptr, "glNormalP3uiv"); + glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC) load(userptr, "glQueryCounter"); + glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC) load(userptr, "glSamplerParameterIiv"); + glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC) load(userptr, "glSamplerParameterIuiv"); + glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) load(userptr, "glSamplerParameterf"); + glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) load(userptr, "glSamplerParameterfv"); + glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) load(userptr, "glSamplerParameteri"); + glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) load(userptr, "glSamplerParameteriv"); + glad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC) load(userptr, "glSecondaryColorP3ui"); + glad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC) load(userptr, "glSecondaryColorP3uiv"); + glad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC) load(userptr, "glTexCoordP1ui"); + glad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC) load(userptr, "glTexCoordP1uiv"); + glad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC) load(userptr, "glTexCoordP2ui"); + glad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC) load(userptr, "glTexCoordP2uiv"); + glad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC) load(userptr, "glTexCoordP3ui"); + glad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC) load(userptr, "glTexCoordP3uiv"); + glad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC) load(userptr, "glTexCoordP4ui"); + glad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC) load(userptr, "glTexCoordP4uiv"); + glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC) load(userptr, "glVertexAttribDivisor"); + glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC) load(userptr, "glVertexAttribP1ui"); + glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC) load(userptr, "glVertexAttribP1uiv"); + glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC) load(userptr, "glVertexAttribP2ui"); + glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC) load(userptr, "glVertexAttribP2uiv"); + glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC) load(userptr, "glVertexAttribP3ui"); + glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC) load(userptr, "glVertexAttribP3uiv"); + glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC) load(userptr, "glVertexAttribP4ui"); + glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC) load(userptr, "glVertexAttribP4uiv"); + glad_glVertexP2ui = (PFNGLVERTEXP2UIPROC) load(userptr, "glVertexP2ui"); + glad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC) load(userptr, "glVertexP2uiv"); + glad_glVertexP3ui = (PFNGLVERTEXP3UIPROC) load(userptr, "glVertexP3ui"); + glad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC) load(userptr, "glVertexP3uiv"); + glad_glVertexP4ui = (PFNGLVERTEXP4UIPROC) load(userptr, "glVertexP4ui"); + glad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC) load(userptr, "glVertexP4uiv"); +} +static void glad_gl_load_GL_ARB_multisample( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_multisample) return; + glad_glSampleCoverageARB = (PFNGLSAMPLECOVERAGEARBPROC) load(userptr, "glSampleCoverageARB"); +} +static void glad_gl_load_GL_ARB_robustness( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_robustness) return; + glad_glGetGraphicsResetStatusARB = (PFNGLGETGRAPHICSRESETSTATUSARBPROC) load(userptr, "glGetGraphicsResetStatusARB"); + glad_glGetnColorTableARB = (PFNGLGETNCOLORTABLEARBPROC) load(userptr, "glGetnColorTableARB"); + glad_glGetnCompressedTexImageARB = (PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) load(userptr, "glGetnCompressedTexImageARB"); + glad_glGetnConvolutionFilterARB = (PFNGLGETNCONVOLUTIONFILTERARBPROC) load(userptr, "glGetnConvolutionFilterARB"); + glad_glGetnHistogramARB = (PFNGLGETNHISTOGRAMARBPROC) load(userptr, "glGetnHistogramARB"); + glad_glGetnMapdvARB = (PFNGLGETNMAPDVARBPROC) load(userptr, "glGetnMapdvARB"); + glad_glGetnMapfvARB = (PFNGLGETNMAPFVARBPROC) load(userptr, "glGetnMapfvARB"); + glad_glGetnMapivARB = (PFNGLGETNMAPIVARBPROC) load(userptr, "glGetnMapivARB"); + glad_glGetnMinmaxARB = (PFNGLGETNMINMAXARBPROC) load(userptr, "glGetnMinmaxARB"); + glad_glGetnPixelMapfvARB = (PFNGLGETNPIXELMAPFVARBPROC) load(userptr, "glGetnPixelMapfvARB"); + glad_glGetnPixelMapuivARB = (PFNGLGETNPIXELMAPUIVARBPROC) load(userptr, "glGetnPixelMapuivARB"); + glad_glGetnPixelMapusvARB = (PFNGLGETNPIXELMAPUSVARBPROC) load(userptr, "glGetnPixelMapusvARB"); + glad_glGetnPolygonStippleARB = (PFNGLGETNPOLYGONSTIPPLEARBPROC) load(userptr, "glGetnPolygonStippleARB"); + glad_glGetnSeparableFilterARB = (PFNGLGETNSEPARABLEFILTERARBPROC) load(userptr, "glGetnSeparableFilterARB"); + glad_glGetnTexImageARB = (PFNGLGETNTEXIMAGEARBPROC) load(userptr, "glGetnTexImageARB"); + glad_glGetnUniformdvARB = (PFNGLGETNUNIFORMDVARBPROC) load(userptr, "glGetnUniformdvARB"); + glad_glGetnUniformfvARB = (PFNGLGETNUNIFORMFVARBPROC) load(userptr, "glGetnUniformfvARB"); + glad_glGetnUniformivARB = (PFNGLGETNUNIFORMIVARBPROC) load(userptr, "glGetnUniformivARB"); + glad_glGetnUniformuivARB = (PFNGLGETNUNIFORMUIVARBPROC) load(userptr, "glGetnUniformuivARB"); + glad_glReadnPixelsARB = (PFNGLREADNPIXELSARBPROC) load(userptr, "glReadnPixelsARB"); +} +static void glad_gl_load_GL_KHR_debug( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_debug) return; + glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) load(userptr, "glDebugMessageCallback"); + glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) load(userptr, "glDebugMessageControl"); + glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) load(userptr, "glDebugMessageInsert"); + glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) load(userptr, "glGetDebugMessageLog"); + glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC) load(userptr, "glGetObjectLabel"); + glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC) load(userptr, "glGetObjectPtrLabel"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv"); + glad_glObjectLabel = (PFNGLOBJECTLABELPROC) load(userptr, "glObjectLabel"); + glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC) load(userptr, "glObjectPtrLabel"); + glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) load(userptr, "glPopDebugGroup"); + glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) load(userptr, "glPushDebugGroup"); +} + + + +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define GLAD_GL_IS_SOME_NEW_VERSION 1 +#else +#define GLAD_GL_IS_SOME_NEW_VERSION 0 +#endif + +static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { +#if GLAD_GL_IS_SOME_NEW_VERSION + if(GLAD_VERSION_MAJOR(version) < 3) { +#else + (void) version; + (void) out_num_exts_i; + (void) out_exts_i; +#endif + if (glad_glGetString == NULL) { + return 0; + } + *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); +#if GLAD_GL_IS_SOME_NEW_VERSION + } else { + unsigned int index = 0; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) { + return 0; + } + glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); + if (num_exts_i > 0) { + exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); + } + if (exts_i == NULL) { + return 0; + } + for(index = 0; index < num_exts_i; index++) { + const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index); + size_t len = strlen(gl_str_tmp) + 1; + + char *local_str = (char*) malloc(len * sizeof(char)); + if(local_str != NULL) { + memcpy(local_str, gl_str_tmp, len * sizeof(char)); + } + + exts_i[index] = local_str; + } + + *out_num_exts_i = num_exts_i; + *out_exts_i = exts_i; + } +#endif + return 1; +} +static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { + if (exts_i != NULL) { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + free((void *) (exts_i[index])); + } + free((void *)exts_i); + exts_i = NULL; + } +} +static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { + if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { + const char *extensions; + const char *loc; + const char *terminator; + extensions = exts; + if(extensions == NULL || ext == NULL) { + return 0; + } + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } + } else { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + if(strcmp(e, ext) == 0) { + return 1; + } + } + } + return 0; +} + +static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_gl_find_extensions_gl( int version) { + const char *exts = NULL; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; + + GLAD_GL_ARB_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multisample"); + GLAD_GL_ARB_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_robustness"); + GLAD_GL_KHR_debug = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_debug"); + + glad_gl_free_extensions(exts_i, num_exts_i); + + return 1; +} + +static int glad_gl_find_core_gl(void) { + int i; + const char* version; + const char* prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + "OpenGL SC ", + NULL + }; + int major = 0; + int minor = 0; + version = (const char*) glad_glGetString(GL_VERSION); + if (!version) return 0; + for (i = 0; prefixes[i]; i++) { + const size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } + + GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); + + GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; + GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; + GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; + GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; + GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; + GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; + GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; + GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; + GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; + GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; + GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; + GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { + int version; + + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + if(glad_glGetString == NULL) return 0; + if(glad_glGetString(GL_VERSION) == NULL) return 0; + version = glad_gl_find_core_gl(); + + glad_gl_load_GL_VERSION_1_0(load, userptr); + glad_gl_load_GL_VERSION_1_1(load, userptr); + glad_gl_load_GL_VERSION_1_2(load, userptr); + glad_gl_load_GL_VERSION_1_3(load, userptr); + glad_gl_load_GL_VERSION_1_4(load, userptr); + glad_gl_load_GL_VERSION_1_5(load, userptr); + glad_gl_load_GL_VERSION_2_0(load, userptr); + glad_gl_load_GL_VERSION_2_1(load, userptr); + glad_gl_load_GL_VERSION_3_0(load, userptr); + glad_gl_load_GL_VERSION_3_1(load, userptr); + glad_gl_load_GL_VERSION_3_2(load, userptr); + glad_gl_load_GL_VERSION_3_3(load, userptr); + + if (!glad_gl_find_extensions_gl(version)) return 0; + glad_gl_load_GL_ARB_multisample(load, userptr); + glad_gl_load_GL_ARB_robustness(load, userptr); + glad_gl_load_GL_KHR_debug(load, userptr); + + + + return version; +} + + +int gladLoadGL( GLADloadfunc load) { + return gladLoadGLUserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* GLAD_GL_IMPLEMENTATION */ + diff --git a/src/external/glfw/deps/glad/gles2.h b/src/external/glfw/deps/glad/gles2.h new file mode 100644 index 000000000..d67f11078 --- /dev/null +++ b/src/external/glfw/deps/glad/gles2.h @@ -0,0 +1,1805 @@ +/** + * Loader generated by glad 2.0.0-beta on Tue Aug 24 22:51:42 2021 + * + * Generator: C/C++ + * Specification: gl + * Extensions: 0 + * + * APIs: + * - gles2=2.0 + * + * Options: + * - ALIAS = False + * - DEBUG = False + * - HEADER_ONLY = True + * - LOADER = False + * - MX = False + * - MX_GLOBAL = False + * - ON_DEMAND = False + * + * Commandline: + * --api='gles2=2.0' --extensions='' c --header-only + * + * Online: + * http://glad.sh/#api=gles2%3D2.0&extensions=&generator=c&options=HEADER_ONLY + * + */ + +#ifndef GLAD_GLES2_H_ +#define GLAD_GLES2_H_ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#ifdef __gl2_h_ + #error OpenGL ES 2 header already included (API: gles2), remove previous include! +#endif +#define __gl2_h_ 1 +#ifdef __gl3_h_ + #error OpenGL ES 3 header already included (API: gles2), remove previous include! +#endif +#define __gl3_h_ 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#define GLAD_GLES2 +#define GLAD_OPTION_GLES2_HEADER_ONLY + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GLAD_PLATFORM_H_ +#define GLAD_PLATFORM_H_ + +#ifndef GLAD_PLATFORM_WIN32 + #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) + #define GLAD_PLATFORM_WIN32 1 + #else + #define GLAD_PLATFORM_WIN32 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_APPLE + #ifdef __APPLE__ + #define GLAD_PLATFORM_APPLE 1 + #else + #define GLAD_PLATFORM_APPLE 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_EMSCRIPTEN + #ifdef __EMSCRIPTEN__ + #define GLAD_PLATFORM_EMSCRIPTEN 1 + #else + #define GLAD_PLATFORM_EMSCRIPTEN 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_UWP + #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) + #ifdef __has_include + #if __has_include() + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #endif + + #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #define GLAD_PLATFORM_UWP 1 + #endif + #endif + + #ifndef GLAD_PLATFORM_UWP + #define GLAD_PLATFORM_UWP 0 + #endif +#endif + +#ifdef __GNUC__ + #define GLAD_GNUC_EXTENSION __extension__ +#else + #define GLAD_GNUC_EXTENSION +#endif + +#ifndef GLAD_API_CALL + #if defined(GLAD_API_CALL_EXPORT) + #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) + #if defined(GLAD_API_CALL_EXPORT_BUILD) + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllexport)) extern + #else + #define GLAD_API_CALL __declspec(dllexport) extern + #endif + #else + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllimport)) extern + #else + #define GLAD_API_CALL __declspec(dllimport) extern + #endif + #endif + #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) + #define GLAD_API_CALL __attribute__ ((visibility ("default"))) extern + #else + #define GLAD_API_CALL extern + #endif + #else + #define GLAD_API_CALL extern + #endif +#endif + +#ifdef APIENTRY + #define GLAD_API_PTR APIENTRY +#elif GLAD_PLATFORM_WIN32 + #define GLAD_API_PTR __stdcall +#else + #define GLAD_API_PTR +#endif + +#ifndef GLAPI +#define GLAPI GLAD_API_CALL +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY GLAD_API_PTR +#endif + +#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) +#define GLAD_VERSION_MAJOR(version) (version / 10000) +#define GLAD_VERSION_MINOR(version) (version % 10000) + +#define GLAD_GENERATOR_VERSION "2.0.0-beta" + +typedef void (*GLADapiproc)(void); + +typedef GLADapiproc (*GLADloadfunc)(const char *name); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); + +typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); +typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); + +#endif /* GLAD_PLATFORM_H_ */ + +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALPHA 0x1906 +#define GL_ALPHA_BITS 0x0D55 +#define GL_ALWAYS 0x0207 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_BACK 0x0405 +#define GL_BLEND 0x0BE2 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLUE_BITS 0x0D54 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_BYTE 0x1400 +#define GL_CCW 0x0901 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_CW 0x0900 +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_DELETE_STATUS 0x8B80 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_DEPTH_BITS 0x0D56 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DITHER 0x0BD0 +#define GL_DONT_CARE 0x1100 +#define GL_DST_ALPHA 0x0304 +#define GL_DST_COLOR 0x0306 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_EQUAL 0x0202 +#define GL_EXTENSIONS 0x1F03 +#define GL_FALSE 0 +#define GL_FASTEST 0x1101 +#define GL_FIXED 0x140C +#define GL_FLOAT 0x1406 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_FRONT 0x0404 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_FRONT_FACE 0x0B46 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_GEQUAL 0x0206 +#define GL_GREATER 0x0204 +#define GL_GREEN_BITS 0x0D53 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_HIGH_INT 0x8DF5 +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_INT 0x1404 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVERT 0x150A +#define GL_KEEP 0x1E00 +#define GL_LEQUAL 0x0203 +#define GL_LESS 0x0201 +#define GL_LINEAR 0x2601 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINK_STATUS 0x8B82 +#define GL_LOW_FLOAT 0x8DF0 +#define GL_LOW_INT 0x8DF3 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_NEAREST 0x2600 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEVER 0x0200 +#define GL_NICEST 0x1102 +#define GL_NONE 0 +#define GL_NOTEQUAL 0x0205 +#define GL_NO_ERROR 0 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_ONE 1 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_POINTS 0x0000 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_RED_BITS 0x0D52 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERER 0x1F01 +#define GL_REPEAT 0x2901 +#define GL_REPLACE 0x1E01 +#define GL_RGB 0x1907 +#define GL_RGB565 0x8D62 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA 0x1908 +#define GL_RGBA4 0x8056 +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_TYPE 0x8B4F +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_SHORT 0x1402 +#define GL_SRC_ALPHA 0x0302 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_SRC_COLOR 0x0300 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_STENCIL_BITS 0x0D57 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STREAM_DRAW 0x88E0 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE 0x1702 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#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_TRIANGLES 0x0004 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRUE 1 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_VENDOR 0x1F00 +#define GL_VERSION 0x1F02 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_VIEWPORT 0x0BA2 +#define GL_ZERO 0 + + +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are 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 Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_GLAD_API_PTR + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_GLAD_API_PTR funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_GLAD_API_PTR + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_GLAD_API_PTR __stdcall +#else +# define KHRONOS_GLAD_API_PTR +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef _WIN64 +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ + +typedef unsigned int GLenum; + +typedef unsigned char GLboolean; + +typedef unsigned int GLbitfield; + +typedef void GLvoid; + +typedef khronos_int8_t GLbyte; + +typedef khronos_uint8_t GLubyte; + +typedef khronos_int16_t GLshort; + +typedef khronos_uint16_t GLushort; + +typedef int GLint; + +typedef unsigned int GLuint; + +typedef khronos_int32_t GLclampx; + +typedef int GLsizei; + +typedef khronos_float_t GLfloat; + +typedef khronos_float_t GLclampf; + +typedef double GLdouble; + +typedef double GLclampd; + +typedef void *GLeglClientBufferEXT; + +typedef void *GLeglImageOES; + +typedef char GLchar; + +typedef char GLcharARB; + +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif + +typedef khronos_uint16_t GLhalf; + +typedef khronos_uint16_t GLhalfARB; + +typedef khronos_int32_t GLfixed; + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptr; +#else +typedef khronos_intptr_t GLintptr; +#endif + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptrARB; +#else +typedef khronos_intptr_t GLintptrARB; +#endif + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptr; +#else +typedef khronos_ssize_t GLsizeiptr; +#endif + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptrARB; +#else +typedef khronos_ssize_t GLsizeiptrARB; +#endif + +typedef khronos_int64_t GLint64; + +typedef khronos_int64_t GLint64EXT; + +typedef khronos_uint64_t GLuint64; + +typedef khronos_uint64_t GLuint64EXT; + +typedef struct __GLsync *GLsync; + +struct _cl_context; + +struct _cl_event; + +typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + +typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + +typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + +typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); + +typedef unsigned short GLhalfNV; + +typedef GLintptr GLvdpauSurfaceNV; + +typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); + + + +#define GL_ES_VERSION_2_0 1 +GLAD_API_CALL int GLAD_GL_ES_VERSION_2_0; + + +typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); +typedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +typedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); +typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); +typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHFPROC)(GLfloat d); +typedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef GLuint (GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); +typedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); +typedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f); +typedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLFINISHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); +typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef GLenum (GLAD_API_PTR *PFNGLGETERRORPROC)(void); +typedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); +typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision); +typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); +typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); +typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); +typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); +typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); +typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); +typedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); +typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +typedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLRELEASESHADERCOMPILERPROC)(void); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +typedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint * shaders, GLenum binaryFormat, const void * binary, GLsizei length); +typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); + +GLAD_API_CALL PFNGLACTIVETEXTUREPROC glad_glActiveTexture; +#define glActiveTexture glad_glActiveTexture +GLAD_API_CALL PFNGLATTACHSHADERPROC glad_glAttachShader; +#define glAttachShader glad_glAttachShader +GLAD_API_CALL PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; +#define glBindAttribLocation glad_glBindAttribLocation +GLAD_API_CALL PFNGLBINDBUFFERPROC glad_glBindBuffer; +#define glBindBuffer glad_glBindBuffer +GLAD_API_CALL PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; +#define glBindFramebuffer glad_glBindFramebuffer +GLAD_API_CALL PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; +#define glBindRenderbuffer glad_glBindRenderbuffer +GLAD_API_CALL PFNGLBINDTEXTUREPROC glad_glBindTexture; +#define glBindTexture glad_glBindTexture +GLAD_API_CALL PFNGLBLENDCOLORPROC glad_glBlendColor; +#define glBlendColor glad_glBlendColor +GLAD_API_CALL PFNGLBLENDEQUATIONPROC glad_glBlendEquation; +#define glBlendEquation glad_glBlendEquation +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; +#define glBlendEquationSeparate glad_glBlendEquationSeparate +GLAD_API_CALL PFNGLBLENDFUNCPROC glad_glBlendFunc; +#define glBlendFunc glad_glBlendFunc +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; +#define glBlendFuncSeparate glad_glBlendFuncSeparate +GLAD_API_CALL PFNGLBUFFERDATAPROC glad_glBufferData; +#define glBufferData glad_glBufferData +GLAD_API_CALL PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; +#define glBufferSubData glad_glBufferSubData +GLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; +#define glCheckFramebufferStatus glad_glCheckFramebufferStatus +GLAD_API_CALL PFNGLCLEARPROC glad_glClear; +#define glClear glad_glClear +GLAD_API_CALL PFNGLCLEARCOLORPROC glad_glClearColor; +#define glClearColor glad_glClearColor +GLAD_API_CALL PFNGLCLEARDEPTHFPROC glad_glClearDepthf; +#define glClearDepthf glad_glClearDepthf +GLAD_API_CALL PFNGLCLEARSTENCILPROC glad_glClearStencil; +#define glClearStencil glad_glClearStencil +GLAD_API_CALL PFNGLCOLORMASKPROC glad_glColorMask; +#define glColorMask glad_glColorMask +GLAD_API_CALL PFNGLCOMPILESHADERPROC glad_glCompileShader; +#define glCompileShader glad_glCompileShader +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; +#define glCompressedTexImage2D glad_glCompressedTexImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; +#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D +GLAD_API_CALL PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; +#define glCopyTexImage2D glad_glCopyTexImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; +#define glCopyTexSubImage2D glad_glCopyTexSubImage2D +GLAD_API_CALL PFNGLCREATEPROGRAMPROC glad_glCreateProgram; +#define glCreateProgram glad_glCreateProgram +GLAD_API_CALL PFNGLCREATESHADERPROC glad_glCreateShader; +#define glCreateShader glad_glCreateShader +GLAD_API_CALL PFNGLCULLFACEPROC glad_glCullFace; +#define glCullFace glad_glCullFace +GLAD_API_CALL PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; +#define glDeleteBuffers glad_glDeleteBuffers +GLAD_API_CALL PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; +#define glDeleteFramebuffers glad_glDeleteFramebuffers +GLAD_API_CALL PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; +#define glDeleteProgram glad_glDeleteProgram +GLAD_API_CALL PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; +#define glDeleteRenderbuffers glad_glDeleteRenderbuffers +GLAD_API_CALL PFNGLDELETESHADERPROC glad_glDeleteShader; +#define glDeleteShader glad_glDeleteShader +GLAD_API_CALL PFNGLDELETETEXTURESPROC glad_glDeleteTextures; +#define glDeleteTextures glad_glDeleteTextures +GLAD_API_CALL PFNGLDEPTHFUNCPROC glad_glDepthFunc; +#define glDepthFunc glad_glDepthFunc +GLAD_API_CALL PFNGLDEPTHMASKPROC glad_glDepthMask; +#define glDepthMask glad_glDepthMask +GLAD_API_CALL PFNGLDEPTHRANGEFPROC glad_glDepthRangef; +#define glDepthRangef glad_glDepthRangef +GLAD_API_CALL PFNGLDETACHSHADERPROC glad_glDetachShader; +#define glDetachShader glad_glDetachShader +GLAD_API_CALL PFNGLDISABLEPROC glad_glDisable; +#define glDisable glad_glDisable +GLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; +#define glDisableVertexAttribArray glad_glDisableVertexAttribArray +GLAD_API_CALL PFNGLDRAWARRAYSPROC glad_glDrawArrays; +#define glDrawArrays glad_glDrawArrays +GLAD_API_CALL PFNGLDRAWELEMENTSPROC glad_glDrawElements; +#define glDrawElements glad_glDrawElements +GLAD_API_CALL PFNGLENABLEPROC glad_glEnable; +#define glEnable glad_glEnable +GLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; +#define glEnableVertexAttribArray glad_glEnableVertexAttribArray +GLAD_API_CALL PFNGLFINISHPROC glad_glFinish; +#define glFinish glad_glFinish +GLAD_API_CALL PFNGLFLUSHPROC glad_glFlush; +#define glFlush glad_glFlush +GLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; +#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; +#define glFramebufferTexture2D glad_glFramebufferTexture2D +GLAD_API_CALL PFNGLFRONTFACEPROC glad_glFrontFace; +#define glFrontFace glad_glFrontFace +GLAD_API_CALL PFNGLGENBUFFERSPROC glad_glGenBuffers; +#define glGenBuffers glad_glGenBuffers +GLAD_API_CALL PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; +#define glGenFramebuffers glad_glGenFramebuffers +GLAD_API_CALL PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; +#define glGenRenderbuffers glad_glGenRenderbuffers +GLAD_API_CALL PFNGLGENTEXTURESPROC glad_glGenTextures; +#define glGenTextures glad_glGenTextures +GLAD_API_CALL PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; +#define glGenerateMipmap glad_glGenerateMipmap +GLAD_API_CALL PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; +#define glGetActiveAttrib glad_glGetActiveAttrib +GLAD_API_CALL PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; +#define glGetActiveUniform glad_glGetActiveUniform +GLAD_API_CALL PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; +#define glGetAttachedShaders glad_glGetAttachedShaders +GLAD_API_CALL PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; +#define glGetAttribLocation glad_glGetAttribLocation +GLAD_API_CALL PFNGLGETBOOLEANVPROC glad_glGetBooleanv; +#define glGetBooleanv glad_glGetBooleanv +GLAD_API_CALL PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; +#define glGetBufferParameteriv glad_glGetBufferParameteriv +GLAD_API_CALL PFNGLGETERRORPROC glad_glGetError; +#define glGetError glad_glGetError +GLAD_API_CALL PFNGLGETFLOATVPROC glad_glGetFloatv; +#define glGetFloatv glad_glGetFloatv +GLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; +#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv +GLAD_API_CALL PFNGLGETINTEGERVPROC glad_glGetIntegerv; +#define glGetIntegerv glad_glGetIntegerv +GLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; +#define glGetProgramInfoLog glad_glGetProgramInfoLog +GLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; +#define glGetProgramiv glad_glGetProgramiv +GLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; +#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv +GLAD_API_CALL PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; +#define glGetShaderInfoLog glad_glGetShaderInfoLog +GLAD_API_CALL PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat; +#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat +GLAD_API_CALL PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; +#define glGetShaderSource glad_glGetShaderSource +GLAD_API_CALL PFNGLGETSHADERIVPROC glad_glGetShaderiv; +#define glGetShaderiv glad_glGetShaderiv +GLAD_API_CALL PFNGLGETSTRINGPROC glad_glGetString; +#define glGetString glad_glGetString +GLAD_API_CALL PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; +#define glGetTexParameterfv glad_glGetTexParameterfv +GLAD_API_CALL PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; +#define glGetTexParameteriv glad_glGetTexParameteriv +GLAD_API_CALL PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; +#define glGetUniformLocation glad_glGetUniformLocation +GLAD_API_CALL PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; +#define glGetUniformfv glad_glGetUniformfv +GLAD_API_CALL PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; +#define glGetUniformiv glad_glGetUniformiv +GLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; +#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv +GLAD_API_CALL PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; +#define glGetVertexAttribfv glad_glGetVertexAttribfv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; +#define glGetVertexAttribiv glad_glGetVertexAttribiv +GLAD_API_CALL PFNGLHINTPROC glad_glHint; +#define glHint glad_glHint +GLAD_API_CALL PFNGLISBUFFERPROC glad_glIsBuffer; +#define glIsBuffer glad_glIsBuffer +GLAD_API_CALL PFNGLISENABLEDPROC glad_glIsEnabled; +#define glIsEnabled glad_glIsEnabled +GLAD_API_CALL PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; +#define glIsFramebuffer glad_glIsFramebuffer +GLAD_API_CALL PFNGLISPROGRAMPROC glad_glIsProgram; +#define glIsProgram glad_glIsProgram +GLAD_API_CALL PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; +#define glIsRenderbuffer glad_glIsRenderbuffer +GLAD_API_CALL PFNGLISSHADERPROC glad_glIsShader; +#define glIsShader glad_glIsShader +GLAD_API_CALL PFNGLISTEXTUREPROC glad_glIsTexture; +#define glIsTexture glad_glIsTexture +GLAD_API_CALL PFNGLLINEWIDTHPROC glad_glLineWidth; +#define glLineWidth glad_glLineWidth +GLAD_API_CALL PFNGLLINKPROGRAMPROC glad_glLinkProgram; +#define glLinkProgram glad_glLinkProgram +GLAD_API_CALL PFNGLPIXELSTOREIPROC glad_glPixelStorei; +#define glPixelStorei glad_glPixelStorei +GLAD_API_CALL PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; +#define glPolygonOffset glad_glPolygonOffset +GLAD_API_CALL PFNGLREADPIXELSPROC glad_glReadPixels; +#define glReadPixels glad_glReadPixels +GLAD_API_CALL PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler; +#define glReleaseShaderCompiler glad_glReleaseShaderCompiler +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; +#define glRenderbufferStorage glad_glRenderbufferStorage +GLAD_API_CALL PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; +#define glSampleCoverage glad_glSampleCoverage +GLAD_API_CALL PFNGLSCISSORPROC glad_glScissor; +#define glScissor glad_glScissor +GLAD_API_CALL PFNGLSHADERBINARYPROC glad_glShaderBinary; +#define glShaderBinary glad_glShaderBinary +GLAD_API_CALL PFNGLSHADERSOURCEPROC glad_glShaderSource; +#define glShaderSource glad_glShaderSource +GLAD_API_CALL PFNGLSTENCILFUNCPROC glad_glStencilFunc; +#define glStencilFunc glad_glStencilFunc +GLAD_API_CALL PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; +#define glStencilFuncSeparate glad_glStencilFuncSeparate +GLAD_API_CALL PFNGLSTENCILMASKPROC glad_glStencilMask; +#define glStencilMask glad_glStencilMask +GLAD_API_CALL PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; +#define glStencilMaskSeparate glad_glStencilMaskSeparate +GLAD_API_CALL PFNGLSTENCILOPPROC glad_glStencilOp; +#define glStencilOp glad_glStencilOp +GLAD_API_CALL PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; +#define glStencilOpSeparate glad_glStencilOpSeparate +GLAD_API_CALL PFNGLTEXIMAGE2DPROC glad_glTexImage2D; +#define glTexImage2D glad_glTexImage2D +GLAD_API_CALL PFNGLTEXPARAMETERFPROC glad_glTexParameterf; +#define glTexParameterf glad_glTexParameterf +GLAD_API_CALL PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; +#define glTexParameterfv glad_glTexParameterfv +GLAD_API_CALL PFNGLTEXPARAMETERIPROC glad_glTexParameteri; +#define glTexParameteri glad_glTexParameteri +GLAD_API_CALL PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; +#define glTexParameteriv glad_glTexParameteriv +GLAD_API_CALL PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; +#define glTexSubImage2D glad_glTexSubImage2D +GLAD_API_CALL PFNGLUNIFORM1FPROC glad_glUniform1f; +#define glUniform1f glad_glUniform1f +GLAD_API_CALL PFNGLUNIFORM1FVPROC glad_glUniform1fv; +#define glUniform1fv glad_glUniform1fv +GLAD_API_CALL PFNGLUNIFORM1IPROC glad_glUniform1i; +#define glUniform1i glad_glUniform1i +GLAD_API_CALL PFNGLUNIFORM1IVPROC glad_glUniform1iv; +#define glUniform1iv glad_glUniform1iv +GLAD_API_CALL PFNGLUNIFORM2FPROC glad_glUniform2f; +#define glUniform2f glad_glUniform2f +GLAD_API_CALL PFNGLUNIFORM2FVPROC glad_glUniform2fv; +#define glUniform2fv glad_glUniform2fv +GLAD_API_CALL PFNGLUNIFORM2IPROC glad_glUniform2i; +#define glUniform2i glad_glUniform2i +GLAD_API_CALL PFNGLUNIFORM2IVPROC glad_glUniform2iv; +#define glUniform2iv glad_glUniform2iv +GLAD_API_CALL PFNGLUNIFORM3FPROC glad_glUniform3f; +#define glUniform3f glad_glUniform3f +GLAD_API_CALL PFNGLUNIFORM3FVPROC glad_glUniform3fv; +#define glUniform3fv glad_glUniform3fv +GLAD_API_CALL PFNGLUNIFORM3IPROC glad_glUniform3i; +#define glUniform3i glad_glUniform3i +GLAD_API_CALL PFNGLUNIFORM3IVPROC glad_glUniform3iv; +#define glUniform3iv glad_glUniform3iv +GLAD_API_CALL PFNGLUNIFORM4FPROC glad_glUniform4f; +#define glUniform4f glad_glUniform4f +GLAD_API_CALL PFNGLUNIFORM4FVPROC glad_glUniform4fv; +#define glUniform4fv glad_glUniform4fv +GLAD_API_CALL PFNGLUNIFORM4IPROC glad_glUniform4i; +#define glUniform4i glad_glUniform4i +GLAD_API_CALL PFNGLUNIFORM4IVPROC glad_glUniform4iv; +#define glUniform4iv glad_glUniform4iv +GLAD_API_CALL PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; +#define glUniformMatrix2fv glad_glUniformMatrix2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; +#define glUniformMatrix3fv glad_glUniformMatrix3fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; +#define glUniformMatrix4fv glad_glUniformMatrix4fv +GLAD_API_CALL PFNGLUSEPROGRAMPROC glad_glUseProgram; +#define glUseProgram glad_glUseProgram +GLAD_API_CALL PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; +#define glValidateProgram glad_glValidateProgram +GLAD_API_CALL PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; +#define glVertexAttrib1f glad_glVertexAttrib1f +GLAD_API_CALL PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; +#define glVertexAttrib1fv glad_glVertexAttrib1fv +GLAD_API_CALL PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; +#define glVertexAttrib2f glad_glVertexAttrib2f +GLAD_API_CALL PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; +#define glVertexAttrib2fv glad_glVertexAttrib2fv +GLAD_API_CALL PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; +#define glVertexAttrib3f glad_glVertexAttrib3f +GLAD_API_CALL PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; +#define glVertexAttrib3fv glad_glVertexAttrib3fv +GLAD_API_CALL PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; +#define glVertexAttrib4f glad_glVertexAttrib4f +GLAD_API_CALL PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; +#define glVertexAttrib4fv glad_glVertexAttrib4fv +GLAD_API_CALL PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; +#define glVertexAttribPointer glad_glVertexAttribPointer +GLAD_API_CALL PFNGLVIEWPORTPROC glad_glViewport; +#define glViewport glad_glViewport + + + + + +GLAD_API_CALL int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr); +GLAD_API_CALL int gladLoadGLES2( GLADloadfunc load); + + + +#ifdef __cplusplus +} +#endif +#endif + +/* Source */ +#ifdef GLAD_GLES2_IMPLEMENTATION +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_GL_ES_VERSION_2_0 = 0; + + + +PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; +PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; +PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; +PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; +PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; +PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; +PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; +PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; +PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; +PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; +PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; +PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; +PFNGLBUFFERDATAPROC glad_glBufferData = NULL; +PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; +PFNGLCLEARPROC glad_glClear = NULL; +PFNGLCLEARCOLORPROC glad_glClearColor = NULL; +PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL; +PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; +PFNGLCOLORMASKPROC glad_glColorMask = NULL; +PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; +PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; +PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; +PFNGLCREATESHADERPROC glad_glCreateShader = NULL; +PFNGLCULLFACEPROC glad_glCullFace = NULL; +PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; +PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; +PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; +PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; +PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; +PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; +PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; +PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; +PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL; +PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; +PFNGLDISABLEPROC glad_glDisable = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; +PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; +PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; +PFNGLENABLEPROC glad_glEnable = NULL; +PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; +PFNGLFINISHPROC glad_glFinish = NULL; +PFNGLFLUSHPROC glad_glFlush = NULL; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; +PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; +PFNGLFRONTFACEPROC glad_glFrontFace = NULL; +PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; +PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; +PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; +PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; +PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; +PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; +PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; +PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; +PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; +PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; +PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; +PFNGLGETERRORPROC glad_glGetError = NULL; +PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; +PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; +PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; +PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; +PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; +PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL; +PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; +PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; +PFNGLGETSTRINGPROC glad_glGetString = NULL; +PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; +PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; +PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; +PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; +PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; +PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; +PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; +PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; +PFNGLHINTPROC glad_glHint = NULL; +PFNGLISBUFFERPROC glad_glIsBuffer = NULL; +PFNGLISENABLEDPROC glad_glIsEnabled = NULL; +PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; +PFNGLISPROGRAMPROC glad_glIsProgram = NULL; +PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; +PFNGLISSHADERPROC glad_glIsShader = NULL; +PFNGLISTEXTUREPROC glad_glIsTexture = NULL; +PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; +PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; +PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; +PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; +PFNGLREADPIXELSPROC glad_glReadPixels = NULL; +PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL; +PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; +PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; +PFNGLSCISSORPROC glad_glScissor = NULL; +PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL; +PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; +PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; +PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; +PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; +PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; +PFNGLSTENCILOPPROC glad_glStencilOp = NULL; +PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; +PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; +PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; +PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; +PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; +PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; +PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; +PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; +PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; +PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; +PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; +PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; +PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; +PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; +PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; +PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; +PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; +PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; +PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; +PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; +PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; +PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; +PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; +PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; +PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; +PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; +PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; +PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; +PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; +PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; +PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; +PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; +PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; +PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; +PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; +PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; +PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; +PFNGLVIEWPORTPROC glad_glViewport = NULL; + + +static void glad_gl_load_GL_ES_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ES_VERSION_2_0) return; + glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); + glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); + glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); + glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); + glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); + glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); + glad_glBindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, "glBindTexture"); + glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); + glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); + glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, "glBlendEquationSeparate"); + glad_glBlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, "glBlendFunc"); + glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, "glBlendFuncSeparate"); + glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); + glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); + glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); + glad_glClear = (PFNGLCLEARPROC) load(userptr, "glClear"); + glad_glClearColor = (PFNGLCLEARCOLORPROC) load(userptr, "glClearColor"); + glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC) load(userptr, "glClearDepthf"); + glad_glClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, "glClearStencil"); + glad_glColorMask = (PFNGLCOLORMASKPROC) load(userptr, "glColorMask"); + glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); + glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); + glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); + glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, "glCopyTexImage2D"); + glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, "glCopyTexSubImage2D"); + glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); + glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); + glad_glCullFace = (PFNGLCULLFACEPROC) load(userptr, "glCullFace"); + glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); + glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); + glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, "glDeleteProgram"); + glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); + glad_glDeleteShader = (PFNGLDELETESHADERPROC) load(userptr, "glDeleteShader"); + glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, "glDeleteTextures"); + glad_glDepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, "glDepthFunc"); + glad_glDepthMask = (PFNGLDEPTHMASKPROC) load(userptr, "glDepthMask"); + glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC) load(userptr, "glDepthRangef"); + glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); + glad_glDisable = (PFNGLDISABLEPROC) load(userptr, "glDisable"); + glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); + glad_glDrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, "glDrawArrays"); + glad_glDrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, "glDrawElements"); + glad_glEnable = (PFNGLENABLEPROC) load(userptr, "glEnable"); + glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); + glad_glFinish = (PFNGLFINISHPROC) load(userptr, "glFinish"); + glad_glFlush = (PFNGLFLUSHPROC) load(userptr, "glFlush"); + glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); + glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); + glad_glFrontFace = (PFNGLFRONTFACEPROC) load(userptr, "glFrontFace"); + glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); + glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); + glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); + glad_glGenTextures = (PFNGLGENTEXTURESPROC) load(userptr, "glGenTextures"); + glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); + glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); + glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); + glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, "glGetAttachedShaders"); + glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); + glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, "glGetBooleanv"); + glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); + glad_glGetError = (PFNGLGETERRORPROC) load(userptr, "glGetError"); + glad_glGetFloatv = (PFNGLGETFLOATVPROC) load(userptr, "glGetFloatv"); + glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); + glad_glGetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, "glGetIntegerv"); + glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, "glGetProgramInfoLog"); + glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, "glGetProgramiv"); + glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); + glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, "glGetShaderInfoLog"); + glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC) load(userptr, "glGetShaderPrecisionFormat"); + glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); + glad_glGetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, "glGetShaderiv"); + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, "glGetTexParameterfv"); + glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, "glGetTexParameteriv"); + glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); + glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); + glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); + glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); + glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); + glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); + glad_glHint = (PFNGLHINTPROC) load(userptr, "glHint"); + glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); + glad_glIsEnabled = (PFNGLISENABLEDPROC) load(userptr, "glIsEnabled"); + glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); + glad_glIsProgram = (PFNGLISPROGRAMPROC) load(userptr, "glIsProgram"); + glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); + glad_glIsShader = (PFNGLISSHADERPROC) load(userptr, "glIsShader"); + glad_glIsTexture = (PFNGLISTEXTUREPROC) load(userptr, "glIsTexture"); + glad_glLineWidth = (PFNGLLINEWIDTHPROC) load(userptr, "glLineWidth"); + glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); + glad_glPixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, "glPixelStorei"); + glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, "glPolygonOffset"); + glad_glReadPixels = (PFNGLREADPIXELSPROC) load(userptr, "glReadPixels"); + glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC) load(userptr, "glReleaseShaderCompiler"); + glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); + glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); + glad_glScissor = (PFNGLSCISSORPROC) load(userptr, "glScissor"); + glad_glShaderBinary = (PFNGLSHADERBINARYPROC) load(userptr, "glShaderBinary"); + glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); + glad_glStencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, "glStencilFunc"); + glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, "glStencilFuncSeparate"); + glad_glStencilMask = (PFNGLSTENCILMASKPROC) load(userptr, "glStencilMask"); + glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, "glStencilMaskSeparate"); + glad_glStencilOp = (PFNGLSTENCILOPPROC) load(userptr, "glStencilOp"); + glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, "glStencilOpSeparate"); + glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, "glTexImage2D"); + glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, "glTexParameterf"); + glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, "glTexParameterfv"); + glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, "glTexParameteri"); + glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, "glTexParameteriv"); + glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, "glTexSubImage2D"); + glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); + glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); + glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); + glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); + glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); + glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); + glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); + glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); + glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); + glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); + glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); + glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); + glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); + glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); + glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); + glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); + glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); + glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); + glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); + glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); + glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); + glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); + glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); + glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); + glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); + glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); + glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); + glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); + glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); + glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); + glad_glViewport = (PFNGLVIEWPORTPROC) load(userptr, "glViewport"); +} + + + +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define GLAD_GL_IS_SOME_NEW_VERSION 1 +#else +#define GLAD_GL_IS_SOME_NEW_VERSION 0 +#endif + +static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { +#if GLAD_GL_IS_SOME_NEW_VERSION + if(GLAD_VERSION_MAJOR(version) < 3) { +#else + (void) version; + (void) out_num_exts_i; + (void) out_exts_i; +#endif + if (glad_glGetString == NULL) { + return 0; + } + *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); +#if GLAD_GL_IS_SOME_NEW_VERSION + } else { + unsigned int index = 0; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) { + return 0; + } + glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); + if (num_exts_i > 0) { + exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); + } + if (exts_i == NULL) { + return 0; + } + for(index = 0; index < num_exts_i; index++) { + const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index); + size_t len = strlen(gl_str_tmp) + 1; + + char *local_str = (char*) malloc(len * sizeof(char)); + if(local_str != NULL) { + memcpy(local_str, gl_str_tmp, len * sizeof(char)); + } + + exts_i[index] = local_str; + } + + *out_num_exts_i = num_exts_i; + *out_exts_i = exts_i; + } +#endif + return 1; +} +static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { + if (exts_i != NULL) { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + free((void *) (exts_i[index])); + } + free((void *)exts_i); + exts_i = NULL; + } +} +static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { + if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { + const char *extensions; + const char *loc; + const char *terminator; + extensions = exts; + if(extensions == NULL || ext == NULL) { + return 0; + } + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } + } else { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + if(strcmp(e, ext) == 0) { + return 1; + } + } + } + return 0; +} + +static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_gl_find_extensions_gles2( int version) { + const char *exts = NULL; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; + + (void) glad_gl_has_extension; + + glad_gl_free_extensions(exts_i, num_exts_i); + + return 1; +} + +static int glad_gl_find_core_gles2(void) { + int i; + const char* version; + const char* prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + "OpenGL SC ", + NULL + }; + int major = 0; + int minor = 0; + version = (const char*) glad_glGetString(GL_VERSION); + if (!version) return 0; + for (i = 0; prefixes[i]; i++) { + const size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } + + GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); + + GLAD_GL_ES_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr) { + int version; + + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + if(glad_glGetString == NULL) return 0; + if(glad_glGetString(GL_VERSION) == NULL) return 0; + version = glad_gl_find_core_gles2(); + + glad_gl_load_GL_ES_VERSION_2_0(load, userptr); + + if (!glad_gl_find_extensions_gles2(version)) return 0; + + + + return version; +} + + +int gladLoadGLES2( GLADloadfunc load) { + return gladLoadGLES2UserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* GLAD_GLES2_IMPLEMENTATION */ + diff --git a/src/external/glfw/deps/glad/khrplatform.h b/src/external/glfw/deps/glad/khrplatform.h deleted file mode 100644 index 975bbffed..000000000 --- a/src/external/glfw/deps/glad/khrplatform.h +++ /dev/null @@ -1,282 +0,0 @@ -#ifndef __khrplatform_h_ -#define __khrplatform_h_ - -/* -** Copyright (c) 2008-2018 The Khronos Group Inc. -** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are 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 Materials. -** -** THE MATERIALS ARE 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 -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -*/ - -/* Khronos platform-specific types and definitions. - * - * The master copy of khrplatform.h is maintained in the Khronos EGL - * Registry repository at https://github.com/KhronosGroup/EGL-Registry - * The last semantic modification to khrplatform.h was at commit ID: - * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 - * - * Adopters may modify this file to suit their platform. Adopters are - * encouraged to submit platform specific modifications to the Khronos - * group so that they can be included in future versions of this file. - * Please submit changes by filing pull requests or issues on - * the EGL Registry repository linked above. - * - * - * See the Implementer's Guidelines for information about where this file - * should be located on your system and for more details of its use: - * http://www.khronos.org/registry/implementers_guide.pdf - * - * This file should be included as - * #include - * by Khronos client API header files that use its types and defines. - * - * The types in khrplatform.h should only be used to define API-specific types. - * - * Types defined in khrplatform.h: - * khronos_int8_t signed 8 bit - * khronos_uint8_t unsigned 8 bit - * khronos_int16_t signed 16 bit - * khronos_uint16_t unsigned 16 bit - * khronos_int32_t signed 32 bit - * khronos_uint32_t unsigned 32 bit - * khronos_int64_t signed 64 bit - * khronos_uint64_t unsigned 64 bit - * khronos_intptr_t signed same number of bits as a pointer - * khronos_uintptr_t unsigned same number of bits as a pointer - * khronos_ssize_t signed size - * khronos_usize_t unsigned size - * khronos_float_t signed 32 bit floating point - * khronos_time_ns_t unsigned 64 bit time in nanoseconds - * khronos_utime_nanoseconds_t unsigned time interval or absolute time in - * nanoseconds - * khronos_stime_nanoseconds_t signed time interval in nanoseconds - * khronos_boolean_enum_t enumerated boolean type. This should - * only be used as a base type when a client API's boolean type is - * an enum. Client APIs which use an integer or other type for - * booleans cannot use this as the base type for their boolean. - * - * Tokens defined in khrplatform.h: - * - * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. - * - * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. - * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. - * - * Calling convention macros defined in this file: - * KHRONOS_APICALL - * KHRONOS_APIENTRY - * KHRONOS_APIATTRIBUTES - * - * These may be used in function prototypes as: - * - * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( - * int arg1, - * int arg2) KHRONOS_APIATTRIBUTES; - */ - -/*------------------------------------------------------------------------- - * Definition of KHRONOS_APICALL - *------------------------------------------------------------------------- - * This precedes the return type of the function in the function prototype. - */ -#if defined(_WIN32) && !defined(__SCITECH_SNAP__) -# define KHRONOS_APICALL __declspec(dllimport) -#elif defined (__SYMBIAN32__) -# define KHRONOS_APICALL IMPORT_C -#elif defined(__ANDROID__) -# define KHRONOS_APICALL __attribute__((visibility("default"))) -#else -# define KHRONOS_APICALL -#endif - -/*------------------------------------------------------------------------- - * Definition of KHRONOS_APIENTRY - *------------------------------------------------------------------------- - * This follows the return type of the function and precedes the function - * name in the function prototype. - */ -#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) - /* Win32 but not WinCE */ -# define KHRONOS_APIENTRY __stdcall -#else -# define KHRONOS_APIENTRY -#endif - -/*------------------------------------------------------------------------- - * Definition of KHRONOS_APIATTRIBUTES - *------------------------------------------------------------------------- - * This follows the closing parenthesis of the function prototype arguments. - */ -#if defined (__ARMCC_2__) -#define KHRONOS_APIATTRIBUTES __softfp -#else -#define KHRONOS_APIATTRIBUTES -#endif - -/*------------------------------------------------------------------------- - * basic type definitions - *-----------------------------------------------------------------------*/ -#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) - - -/* - * Using - */ -#include -typedef int32_t khronos_int32_t; -typedef uint32_t khronos_uint32_t; -typedef int64_t khronos_int64_t; -typedef uint64_t khronos_uint64_t; -#define KHRONOS_SUPPORT_INT64 1 -#define KHRONOS_SUPPORT_FLOAT 1 - -#elif defined(__VMS ) || defined(__sgi) - -/* - * Using - */ -#include -typedef int32_t khronos_int32_t; -typedef uint32_t khronos_uint32_t; -typedef int64_t khronos_int64_t; -typedef uint64_t khronos_uint64_t; -#define KHRONOS_SUPPORT_INT64 1 -#define KHRONOS_SUPPORT_FLOAT 1 - -#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) - -/* - * Win32 - */ -typedef __int32 khronos_int32_t; -typedef unsigned __int32 khronos_uint32_t; -typedef __int64 khronos_int64_t; -typedef unsigned __int64 khronos_uint64_t; -#define KHRONOS_SUPPORT_INT64 1 -#define KHRONOS_SUPPORT_FLOAT 1 - -#elif defined(__sun__) || defined(__digital__) - -/* - * Sun or Digital - */ -typedef int khronos_int32_t; -typedef unsigned int khronos_uint32_t; -#if defined(__arch64__) || defined(_LP64) -typedef long int khronos_int64_t; -typedef unsigned long int khronos_uint64_t; -#else -typedef long long int khronos_int64_t; -typedef unsigned long long int khronos_uint64_t; -#endif /* __arch64__ */ -#define KHRONOS_SUPPORT_INT64 1 -#define KHRONOS_SUPPORT_FLOAT 1 - -#elif 0 - -/* - * Hypothetical platform with no float or int64 support - */ -typedef int khronos_int32_t; -typedef unsigned int khronos_uint32_t; -#define KHRONOS_SUPPORT_INT64 0 -#define KHRONOS_SUPPORT_FLOAT 0 - -#else - -/* - * Generic fallback - */ -#include -typedef int32_t khronos_int32_t; -typedef uint32_t khronos_uint32_t; -typedef int64_t khronos_int64_t; -typedef uint64_t khronos_uint64_t; -#define KHRONOS_SUPPORT_INT64 1 -#define KHRONOS_SUPPORT_FLOAT 1 - -#endif - - -/* - * Types that are (so far) the same on all platforms - */ -typedef signed char khronos_int8_t; -typedef unsigned char khronos_uint8_t; -typedef signed short int khronos_int16_t; -typedef unsigned short int khronos_uint16_t; - -/* - * Types that differ between LLP64 and LP64 architectures - in LLP64, - * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears - * to be the only LLP64 architecture in current use. - */ -#ifdef _WIN64 -typedef signed long long int khronos_intptr_t; -typedef unsigned long long int khronos_uintptr_t; -typedef signed long long int khronos_ssize_t; -typedef unsigned long long int khronos_usize_t; -#else -typedef signed long int khronos_intptr_t; -typedef unsigned long int khronos_uintptr_t; -typedef signed long int khronos_ssize_t; -typedef unsigned long int khronos_usize_t; -#endif - -#if KHRONOS_SUPPORT_FLOAT -/* - * Float type - */ -typedef float khronos_float_t; -#endif - -#if KHRONOS_SUPPORT_INT64 -/* Time types - * - * These types can be used to represent a time interval in nanoseconds or - * an absolute Unadjusted System Time. Unadjusted System Time is the number - * of nanoseconds since some arbitrary system event (e.g. since the last - * time the system booted). The Unadjusted System Time is an unsigned - * 64 bit value that wraps back to 0 every 584 years. Time intervals - * may be either signed or unsigned. - */ -typedef khronos_uint64_t khronos_utime_nanoseconds_t; -typedef khronos_int64_t khronos_stime_nanoseconds_t; -#endif - -/* - * Dummy value used to pad enum types to 32 bits. - */ -#ifndef KHRONOS_MAX_ENUM -#define KHRONOS_MAX_ENUM 0x7FFFFFFF -#endif - -/* - * Enumerated boolean type - * - * Values other than zero should be considered to be true. Therefore - * comparisons should not be made against KHRONOS_TRUE. - */ -typedef enum { - KHRONOS_FALSE = 0, - KHRONOS_TRUE = 1, - KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM -} khronos_boolean_enum_t; - -#endif /* __khrplatform_h_ */ diff --git a/src/external/glfw/deps/glad/vk_platform.h b/src/external/glfw/deps/glad/vk_platform.h deleted file mode 100644 index d7d22e1e7..000000000 --- a/src/external/glfw/deps/glad/vk_platform.h +++ /dev/null @@ -1,92 +0,0 @@ -/* */ -/* File: vk_platform.h */ -/* */ -/* -** Copyright (c) 2014-2017 The Khronos Group Inc. -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - - -#ifndef VK_PLATFORM_H_ -#define VK_PLATFORM_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -/* -*************************************************************************************************** -* Platform-specific directives and type declarations -*************************************************************************************************** -*/ - -/* Platform-specific calling convention macros. - * - * Platforms should define these so that Vulkan clients call Vulkan commands - * with the same calling conventions that the Vulkan implementation expects. - * - * VKAPI_ATTR - Placed before the return type in function declarations. - * Useful for C++11 and GCC/Clang-style function attribute syntax. - * VKAPI_CALL - Placed after the return type in function declarations. - * Useful for MSVC-style calling convention syntax. - * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. - * - * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); - * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); - */ -#if defined(_WIN32) - /* On Windows, Vulkan commands use the stdcall convention */ - #define VKAPI_ATTR - #define VKAPI_CALL __stdcall - #define VKAPI_PTR VKAPI_CALL -#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 - #error "Vulkan isn't supported for the 'armeabi' NDK ABI" -#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) - /* On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" */ - /* calling convention, i.e. float parameters are passed in registers. This */ - /* is true even if the rest of the application passes floats on the stack, */ - /* as it does by default when compiling for the armeabi-v7a NDK ABI. */ - #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) - #define VKAPI_CALL - #define VKAPI_PTR VKAPI_ATTR -#else - /* On other platforms, use the default calling convention */ - #define VKAPI_ATTR - #define VKAPI_CALL - #define VKAPI_PTR -#endif - -#include - -#if !defined(VK_NO_STDINT_H) - #if defined(_MSC_VER) && (_MSC_VER < 1600) - typedef signed __int8 int8_t; - typedef unsigned __int8 uint8_t; - typedef signed __int16 int16_t; - typedef unsigned __int16 uint16_t; - typedef signed __int32 int32_t; - typedef unsigned __int32 uint32_t; - typedef signed __int64 int64_t; - typedef unsigned __int64 uint64_t; - #else - #include - #endif -#endif /* !defined(VK_NO_STDINT_H) */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ - -#endif diff --git a/src/external/glfw/deps/glad/vulkan.h b/src/external/glfw/deps/glad/vulkan.h index 6bace71d8..469ffe5e0 100644 --- a/src/external/glfw/deps/glad/vulkan.h +++ b/src/external/glfw/deps/glad/vulkan.h @@ -1,26 +1,27 @@ /** - * Loader generated by glad 2.0.0-beta on Sun Apr 14 17:03:38 2019 + * Loader generated by glad 2.0.0-beta on Thu Jul 7 20:52:04 2022 * * Generator: C/C++ * Specification: vk - * Extensions: 3 + * Extensions: 4 * * APIs: - * - vulkan=1.1 + * - vulkan=1.3 * * Options: - * - MX_GLOBAL = False - * - LOADER = False * - ALIAS = False - * - HEADER_ONLY = False * - DEBUG = False + * - HEADER_ONLY = True + * - LOADER = False * - MX = False + * - MX_GLOBAL = False + * - ON_DEMAND = False * * Commandline: - * --api='vulkan=1.1' --extensions='VK_EXT_debug_report,VK_KHR_surface,VK_KHR_swapchain' c + * --api='vulkan=1.3' --extensions='VK_EXT_debug_report,VK_KHR_portability_enumeration,VK_KHR_surface,VK_KHR_swapchain' c --header-only * * Online: - * http://glad.sh/#api=vulkan%3D1.1&extensions=VK_EXT_debug_report%2CVK_KHR_surface%2CVK_KHR_swapchain&generator=c&options= + * http://glad.sh/#api=vulkan%3D1.3&extensions=VK_EXT_debug_report%2CVK_KHR_portability_enumeration%2CVK_KHR_surface%2CVK_KHR_swapchain&generator=c&options=HEADER_ONLY * */ @@ -28,17 +29,18 @@ #define GLAD_VULKAN_H_ #ifdef VULKAN_H_ - #error header already included (API: vulkan), remove previous include! + #error header already included (API: vulkan), remove previous include! #endif #define VULKAN_H_ 1 #ifdef VULKAN_CORE_H_ - #error header already included (API: vulkan), remove previous include! + #error header already included (API: vulkan), remove previous include! #endif #define VULKAN_CORE_H_ 1 #define GLAD_VULKAN +#define GLAD_OPTION_VULKAN_HEADER_ONLY #ifdef __cplusplus extern "C" { @@ -142,15 +144,16 @@ extern "C" { #define GLAPIENTRY GLAD_API_PTR #endif - #define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) #define GLAD_VERSION_MAJOR(version) (version / 10000) #define GLAD_VERSION_MINOR(version) (version % 10000) +#define GLAD_GENERATOR_VERSION "2.0.0-beta" + typedef void (*GLADapiproc)(void); typedef GLADapiproc (*GLADloadfunc)(const char *name); -typedef GLADapiproc (*GLADuserptrloadfunc)(const char *name, void *userptr); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); @@ -159,21 +162,25 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define VK_ATTACHMENT_UNUSED (~0U) #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" -#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 9 +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 10 #define VK_FALSE 0 +#define VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME "VK_KHR_portability_enumeration" +#define VK_KHR_PORTABILITY_ENUMERATION_SPEC_VERSION 1 #define VK_KHR_SURFACE_EXTENSION_NAME "VK_KHR_surface" #define VK_KHR_SURFACE_SPEC_VERSION 25 #define VK_KHR_SWAPCHAIN_EXTENSION_NAME "VK_KHR_swapchain" #define VK_KHR_SWAPCHAIN_SPEC_VERSION 70 -#define VK_LOD_CLAMP_NONE 1000.0f +#define VK_LOD_CLAMP_NONE 1000.0F #define VK_LUID_SIZE 8 #define VK_MAX_DESCRIPTION_SIZE 256 #define VK_MAX_DEVICE_GROUP_SIZE 32 +#define VK_MAX_DRIVER_INFO_SIZE 256 +#define VK_MAX_DRIVER_NAME_SIZE 256 #define VK_MAX_EXTENSION_NAME_SIZE 256 #define VK_MAX_MEMORY_HEAPS 16 #define VK_MAX_MEMORY_TYPES 32 #define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 -#define VK_QUEUE_FAMILY_EXTERNAL (~0U-1) +#define VK_QUEUE_FAMILY_EXTERNAL (~1U) #define VK_QUEUE_FAMILY_IGNORED (~0U) #define VK_REMAINING_ARRAY_LAYERS (~0U) #define VK_REMAINING_MIP_LEVELS (~0U) @@ -183,29 +190,148 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define VK_WHOLE_SIZE (~0ULL) -#include +/* */ +/* File: vk_platform.h */ +/* */ +/* +** Copyright 2014-2022 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + + +#ifndef VK_PLATFORM_H_ +#define VK_PLATFORM_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* +*************************************************************************************************** +* Platform-specific directives and type declarations +*************************************************************************************************** +*/ + +/* Platform-specific calling convention macros. + * + * Platforms should define these so that Vulkan clients call Vulkan commands + * with the same calling conventions that the Vulkan implementation expects. + * + * VKAPI_ATTR - Placed before the return type in function declarations. + * Useful for C++11 and GCC/Clang-style function attribute syntax. + * VKAPI_CALL - Placed after the return type in function declarations. + * Useful for MSVC-style calling convention syntax. + * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. + * + * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); + * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); + */ +#if defined(_WIN32) + /* On Windows, Vulkan commands use the stdcall convention */ + #define VKAPI_ATTR + #define VKAPI_CALL __stdcall + #define VKAPI_PTR VKAPI_CALL +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 + #error "Vulkan is not supported for the 'armeabi' NDK ABI" +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) + /* On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" */ + /* calling convention, i.e. float parameters are passed in registers. This */ + /* is true even if the rest of the application passes floats on the stack, */ + /* as it does by default when compiling for the armeabi-v7a NDK ABI. */ + #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) + #define VKAPI_CALL + #define VKAPI_PTR VKAPI_ATTR +#else + /* On other platforms, use the default calling convention */ + #define VKAPI_ATTR + #define VKAPI_CALL + #define VKAPI_PTR +#endif + +#if !defined(VK_NO_STDDEF_H) + #include +#endif /* !defined(VK_NO_STDDEF_H) */ + +#if !defined(VK_NO_STDINT_H) + #if defined(_MSC_VER) && (_MSC_VER < 1600) + typedef signed __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef signed __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef signed __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; + #else + #include + #endif +#endif /* !defined(VK_NO_STDINT_H) */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif +/* DEPRECATED: This define is deprecated. VK_MAKE_API_VERSION should be used instead. */ #define VK_MAKE_VERSION(major, minor, patch) \ - (((major) << 22) | ((minor) << 12) | (patch)) + ((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) +/* DEPRECATED: This define is deprecated. VK_API_VERSION_MAJOR should be used instead. */ #define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) -#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) -#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) +/* DEPRECATED: This define is deprecated. VK_API_VERSION_MINOR should be used instead. */ +#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) +/* DEPRECATED: This define is deprecated. VK_API_VERSION_PATCH should be used instead. */ +#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) +#define VK_MAKE_API_VERSION(variant, major, minor, patch) \ + ((((uint32_t)(variant)) << 29) | (((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) +#define VK_API_VERSION_VARIANT(version) ((uint32_t)(version) >> 29) +#define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU) +#define VK_API_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) +#define VK_API_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) /* DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. */ /*#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0 */ /* Vulkan 1.0 version number */ -#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)/* Patch version should always be set to 0 */ +#define VK_API_VERSION_1_0 VK_MAKE_API_VERSION(0, 1, 0, 0)/* Patch version should always be set to 0 */ /* Vulkan 1.1 version number */ -#define VK_API_VERSION_1_1 VK_MAKE_VERSION(1, 1, 0)/* Patch version should always be set to 0 */ +#define VK_API_VERSION_1_1 VK_MAKE_API_VERSION(0, 1, 1, 0)/* Patch version should always be set to 0 */ +/* Vulkan 1.2 version number */ +#define VK_API_VERSION_1_2 VK_MAKE_API_VERSION(0, 1, 2, 0)/* Patch version should always be set to 0 */ +/* Vulkan 1.3 version number */ +#define VK_API_VERSION_1_3 VK_MAKE_API_VERSION(0, 1, 3, 0)/* Patch version should always be set to 0 */ /* Version of this file */ -#define VK_HEADER_VERSION 106 +#define VK_HEADER_VERSION 220 +/* Complete version of this file */ +#define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; -#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE) -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#ifndef VK_USE_64_BIT_PTR_DEFINES + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VK_USE_64_BIT_PTR_DEFINES 1 + #else + #define VK_USE_64_BIT_PTR_DEFINES 0 + #endif +#endif +#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE + #if (VK_USE_64_BIT_PTR_DEFINES==1) + #if (defined(__cplusplus) && (__cplusplus >= 201103L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L)) + #define VK_NULL_HANDLE nullptr + #else + #define VK_NULL_HANDLE ((void*)0) + #endif + #else + #define VK_NULL_HANDLE 0ULL + #endif +#endif +#ifndef VK_NULL_HANDLE + #define VK_NULL_HANDLE 0 +#endif +#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE + #if (VK_USE_64_BIT_PTR_DEFINES==1) #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; -#else + #else #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; + #endif #endif -#endif -#define VK_NULL_HANDLE 0 @@ -241,17 +367,21 @@ VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineCache) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorUpdateTemplate) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSamplerYcbcrConversion) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPrivateDataSlot) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) typedef enum VkAttachmentLoadOp { VK_ATTACHMENT_LOAD_OP_LOAD = 0, VK_ATTACHMENT_LOAD_OP_CLEAR = 1, - VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2 + VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2, + VK_ATTACHMENT_LOAD_OP_MAX_ENUM = 0x7FFFFFFF } VkAttachmentLoadOp; typedef enum VkAttachmentStoreOp { VK_ATTACHMENT_STORE_OP_STORE = 0, - VK_ATTACHMENT_STORE_OP_DONT_CARE = 1 + VK_ATTACHMENT_STORE_OP_DONT_CARE = 1, + VK_ATTACHMENT_STORE_OP_NONE = 1000301000, + VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF } VkAttachmentStoreOp; typedef enum VkBlendFactor { VK_BLEND_FACTOR_ZERO = 0, @@ -272,14 +402,16 @@ typedef enum VkBlendFactor { VK_BLEND_FACTOR_SRC1_COLOR = 15, VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 16, VK_BLEND_FACTOR_SRC1_ALPHA = 17, - VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18 + VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18, + VK_BLEND_FACTOR_MAX_ENUM = 0x7FFFFFFF } VkBlendFactor; typedef enum VkBlendOp { VK_BLEND_OP_ADD = 0, VK_BLEND_OP_SUBTRACT = 1, VK_BLEND_OP_REVERSE_SUBTRACT = 2, VK_BLEND_OP_MIN = 3, - VK_BLEND_OP_MAX = 4 + VK_BLEND_OP_MAX = 4, + VK_BLEND_OP_MAX_ENUM = 0x7FFFFFFF } VkBlendOp; typedef enum VkBorderColor { VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0, @@ -287,21 +419,45 @@ typedef enum VkBorderColor { VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 2, VK_BORDER_COLOR_INT_OPAQUE_BLACK = 3, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4, - VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5 + VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5, + VK_BORDER_COLOR_MAX_ENUM = 0x7FFFFFFF } VkBorderColor; - +typedef enum VkFramebufferCreateFlagBits { + VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT = 1, + VK_FRAMEBUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFramebufferCreateFlagBits; typedef enum VkPipelineCacheHeaderVersion { - VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1 + VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1, + VK_PIPELINE_CACHE_HEADER_VERSION_MAX_ENUM = 0x7FFFFFFF } VkPipelineCacheHeaderVersion; - +typedef enum VkPipelineCacheCreateFlagBits { + VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 1, + VK_PIPELINE_CACHE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCacheCreateFlagBits; +typedef enum VkPipelineShaderStageCreateFlagBits { + VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT = 1, + VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT = 2, + VK_PIPELINE_SHADER_STAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineShaderStageCreateFlagBits; +typedef enum VkDescriptorSetLayoutCreateFlagBits { + VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT = 2, + VK_DESCRIPTOR_SET_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorSetLayoutCreateFlagBits; +typedef enum VkInstanceCreateFlagBits { + VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR = 1, + VK_INSTANCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkInstanceCreateFlagBits; typedef enum VkDeviceQueueCreateFlagBits { - VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT = 1 + VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT = 1, + VK_DEVICE_QUEUE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDeviceQueueCreateFlagBits; typedef enum VkBufferCreateFlagBits { VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 1, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 2, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 4, - VK_BUFFER_CREATE_PROTECTED_BIT = 8 + VK_BUFFER_CREATE_PROTECTED_BIT = 8, + VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT = 16, + VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkBufferCreateFlagBits; typedef enum VkBufferUsageFlagBits { VK_BUFFER_USAGE_TRANSFER_SRC_BIT = 1, @@ -312,13 +468,16 @@ typedef enum VkBufferUsageFlagBits { VK_BUFFER_USAGE_STORAGE_BUFFER_BIT = 32, VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 64, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 128, - VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 256 + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 256, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT = 131072, + VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkBufferUsageFlagBits; typedef enum VkColorComponentFlagBits { VK_COLOR_COMPONENT_R_BIT = 1, VK_COLOR_COMPONENT_G_BIT = 2, VK_COLOR_COMPONENT_B_BIT = 4, - VK_COLOR_COMPONENT_A_BIT = 8 + VK_COLOR_COMPONENT_A_BIT = 8, + VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkColorComponentFlagBits; typedef enum VkComponentSwizzle { VK_COMPONENT_SWIZZLE_IDENTITY = 0, @@ -327,27 +486,33 @@ typedef enum VkComponentSwizzle { VK_COMPONENT_SWIZZLE_R = 3, VK_COMPONENT_SWIZZLE_G = 4, VK_COMPONENT_SWIZZLE_B = 5, - VK_COMPONENT_SWIZZLE_A = 6 + VK_COMPONENT_SWIZZLE_A = 6, + VK_COMPONENT_SWIZZLE_MAX_ENUM = 0x7FFFFFFF } VkComponentSwizzle; typedef enum VkCommandPoolCreateFlagBits { VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 1, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 2, - VK_COMMAND_POOL_CREATE_PROTECTED_BIT = 4 + VK_COMMAND_POOL_CREATE_PROTECTED_BIT = 4, + VK_COMMAND_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandPoolCreateFlagBits; typedef enum VkCommandPoolResetFlagBits { - VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 1 + VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 1, + VK_COMMAND_POOL_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandPoolResetFlagBits; typedef enum VkCommandBufferResetFlagBits { - VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 1 + VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 1, + VK_COMMAND_BUFFER_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferResetFlagBits; typedef enum VkCommandBufferLevel { VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0, - VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1 + VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1, + VK_COMMAND_BUFFER_LEVEL_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferLevel; typedef enum VkCommandBufferUsageFlagBits { VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT = 1, VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT = 2, - VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 4 + VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 4, + VK_COMMAND_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferUsageFlagBits; typedef enum VkCompareOp { VK_COMPARE_OP_NEVER = 0, @@ -357,13 +522,15 @@ typedef enum VkCompareOp { VK_COMPARE_OP_GREATER = 4, VK_COMPARE_OP_NOT_EQUAL = 5, VK_COMPARE_OP_GREATER_OR_EQUAL = 6, - VK_COMPARE_OP_ALWAYS = 7 + VK_COMPARE_OP_ALWAYS = 7, + VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF } VkCompareOp; typedef enum VkCullModeFlagBits { VK_CULL_MODE_NONE = 0, VK_CULL_MODE_FRONT_BIT = 1, VK_CULL_MODE_BACK_BIT = 2, - VK_CULL_MODE_FRONT_AND_BACK = 0x00000003 + VK_CULL_MODE_FRONT_AND_BACK = 0x00000003, + VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCullModeFlagBits; typedef enum VkDescriptorType { VK_DESCRIPTOR_TYPE_SAMPLER = 0, @@ -376,7 +543,9 @@ typedef enum VkDescriptorType { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 7, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, - VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10 + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, + VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK = 1000138000, + VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF } VkDescriptorType; typedef enum VkDynamicState { VK_DYNAMIC_STATE_VIEWPORT = 0, @@ -388,15 +557,32 @@ typedef enum VkDynamicState { VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7, VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8, - VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1) + VK_DYNAMIC_STATE_CULL_MODE = 1000267000, + VK_DYNAMIC_STATE_FRONT_FACE = 1000267001, + VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY = 1000267002, + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT = 1000267003, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT = 1000267004, + VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE = 1000267005, + VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE = 1000267006, + VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE = 1000267007, + VK_DYNAMIC_STATE_DEPTH_COMPARE_OP = 1000267008, + VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE = 1000267009, + VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE = 1000267010, + VK_DYNAMIC_STATE_STENCIL_OP = 1000267011, + VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE = 1000377001, + VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE = 1000377002, + VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE = 1000377004, + VK_DYNAMIC_STATE_MAX_ENUM = 0x7FFFFFFF } VkDynamicState; typedef enum VkFenceCreateFlagBits { - VK_FENCE_CREATE_SIGNALED_BIT = 1 + VK_FENCE_CREATE_SIGNALED_BIT = 1, + VK_FENCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFenceCreateFlagBits; typedef enum VkPolygonMode { VK_POLYGON_MODE_FILL = 0, VK_POLYGON_MODE_LINE = 1, - VK_POLYGON_MODE_POINT = 2 + VK_POLYGON_MODE_POINT = 2, + VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF } VkPolygonMode; typedef enum VkFormat { VK_FORMAT_UNDEFINED = 0, @@ -617,7 +803,28 @@ typedef enum VkFormat { VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, - VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033 + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, + VK_FORMAT_G8_B8R8_2PLANE_444_UNORM = 1000330000, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16 = 1000330001, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16 = 1000330002, + VK_FORMAT_G16_B16R16_2PLANE_444_UNORM = 1000330003, + VK_FORMAT_A4R4G4B4_UNORM_PACK16 = 1000340000, + VK_FORMAT_A4B4G4R4_UNORM_PACK16 = 1000340001, + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000, + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001, + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002, + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003, + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004, + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005, + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006, + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK = 1000066007, + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK = 1000066008, + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK = 1000066009, + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK = 1000066010, + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK = 1000066011, + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK = 1000066012, + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK = 1000066013, + VK_FORMAT_MAX_ENUM = 0x7FFFFFFF } VkFormat; typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT = 1, @@ -641,11 +848,14 @@ typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT = 1048576, VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT = 2097152, VK_FORMAT_FEATURE_DISJOINT_BIT = 4194304, - VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT = 8388608 + VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT = 8388608, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT = 65536, + VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFormatFeatureFlagBits; typedef enum VkFrontFace { VK_FRONT_FACE_COUNTER_CLOCKWISE = 0, - VK_FRONT_FACE_CLOCKWISE = 1 + VK_FRONT_FACE_CLOCKWISE = 1, + VK_FRONT_FACE_MAX_ENUM = 0x7FFFFFFF } VkFrontFace; typedef enum VkImageAspectFlagBits { VK_IMAGE_ASPECT_COLOR_BIT = 1, @@ -654,7 +864,9 @@ typedef enum VkImageAspectFlagBits { VK_IMAGE_ASPECT_METADATA_BIT = 8, VK_IMAGE_ASPECT_PLANE_0_BIT = 16, VK_IMAGE_ASPECT_PLANE_1_BIT = 32, - VK_IMAGE_ASPECT_PLANE_2_BIT = 64 + VK_IMAGE_ASPECT_PLANE_2_BIT = 64, + VK_IMAGE_ASPECT_NONE = 0, + VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageAspectFlagBits; typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 1, @@ -668,7 +880,8 @@ typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT = 128, VK_IMAGE_CREATE_EXTENDED_USAGE_BIT = 256, VK_IMAGE_CREATE_PROTECTED_BIT = 2048, - VK_IMAGE_CREATE_DISJOINT_BIT = 512 + VK_IMAGE_CREATE_DISJOINT_BIT = 512, + VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageCreateFlagBits; typedef enum VkImageLayout { VK_IMAGE_LAYOUT_UNDEFINED = 0, @@ -682,16 +895,25 @@ typedef enum VkImageLayout { VK_IMAGE_LAYOUT_PREINITIALIZED = 8, VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL = 1000117000, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL = 1000117001, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002 + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL = 1000241000, + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL = 1000241001, + VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL = 1000241002, + VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL = 1000241003, + VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL = 1000314000, + VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL = 1000314001, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, + VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF } VkImageLayout; typedef enum VkImageTiling { VK_IMAGE_TILING_OPTIMAL = 0, - VK_IMAGE_TILING_LINEAR = 1 + VK_IMAGE_TILING_LINEAR = 1, + VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF } VkImageTiling; typedef enum VkImageType { VK_IMAGE_TYPE_1D = 0, VK_IMAGE_TYPE_2D = 1, - VK_IMAGE_TYPE_3D = 2 + VK_IMAGE_TYPE_3D = 2, + VK_IMAGE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkImageType; typedef enum VkImageUsageFlagBits { VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 1, @@ -701,9 +923,9 @@ typedef enum VkImageUsageFlagBits { VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 16, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 32, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 64, - VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 128 + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 128, + VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageUsageFlagBits; - typedef enum VkImageViewType { VK_IMAGE_VIEW_TYPE_1D = 0, VK_IMAGE_VIEW_TYPE_2D = 1, @@ -711,15 +933,18 @@ typedef enum VkImageViewType { VK_IMAGE_VIEW_TYPE_CUBE = 3, VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4, VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5, - VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6 + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6, + VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF } VkImageViewType; typedef enum VkSharingMode { VK_SHARING_MODE_EXCLUSIVE = 0, - VK_SHARING_MODE_CONCURRENT = 1 + VK_SHARING_MODE_CONCURRENT = 1, + VK_SHARING_MODE_MAX_ENUM = 0x7FFFFFFF } VkSharingMode; typedef enum VkIndexType { VK_INDEX_TYPE_UINT16 = 0, - VK_INDEX_TYPE_UINT32 = 1 + VK_INDEX_TYPE_UINT32 = 1, + VK_INDEX_TYPE_MAX_ENUM = 0x7FFFFFFF } VkIndexType; typedef enum VkLogicOp { VK_LOGIC_OP_CLEAR = 0, @@ -737,11 +962,13 @@ typedef enum VkLogicOp { VK_LOGIC_OP_COPY_INVERTED = 12, VK_LOGIC_OP_OR_INVERTED = 13, VK_LOGIC_OP_NAND = 14, - VK_LOGIC_OP_SET = 15 + VK_LOGIC_OP_SET = 15, + VK_LOGIC_OP_MAX_ENUM = 0x7FFFFFFF } VkLogicOp; typedef enum VkMemoryHeapFlagBits { VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 1, - VK_MEMORY_HEAP_MULTI_INSTANCE_BIT = 2 + VK_MEMORY_HEAP_MULTI_INSTANCE_BIT = 2, + VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryHeapFlagBits; typedef enum VkAccessFlagBits { VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 1, @@ -760,7 +987,9 @@ typedef enum VkAccessFlagBits { VK_ACCESS_HOST_READ_BIT = 8192, VK_ACCESS_HOST_WRITE_BIT = 16384, VK_ACCESS_MEMORY_READ_BIT = 32768, - VK_ACCESS_MEMORY_WRITE_BIT = 65536 + VK_ACCESS_MEMORY_WRITE_BIT = 65536, + VK_ACCESS_NONE = 0, + VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkAccessFlagBits; typedef enum VkMemoryPropertyFlagBits { VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 1, @@ -768,25 +997,32 @@ typedef enum VkMemoryPropertyFlagBits { VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 4, VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 8, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 16, - VK_MEMORY_PROPERTY_PROTECTED_BIT = 32 + VK_MEMORY_PROPERTY_PROTECTED_BIT = 32, + VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryPropertyFlagBits; typedef enum VkPhysicalDeviceType { VK_PHYSICAL_DEVICE_TYPE_OTHER = 0, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3, - VK_PHYSICAL_DEVICE_TYPE_CPU = 4 + VK_PHYSICAL_DEVICE_TYPE_CPU = 4, + VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkPhysicalDeviceType; typedef enum VkPipelineBindPoint { VK_PIPELINE_BIND_POINT_GRAPHICS = 0, - VK_PIPELINE_BIND_POINT_COMPUTE = 1 + VK_PIPELINE_BIND_POINT_COMPUTE = 1, + VK_PIPELINE_BIND_POINT_MAX_ENUM = 0x7FFFFFFF } VkPipelineBindPoint; typedef enum VkPipelineCreateFlagBits { VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 1, VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 2, VK_PIPELINE_CREATE_DERIVATIVE_BIT = 4, VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT = 8, - VK_PIPELINE_CREATE_DISPATCH_BASE = 16 + VK_PIPELINE_CREATE_DISPATCH_BASE_BIT = 16, + VK_PIPELINE_CREATE_DISPATCH_BASE = VK_PIPELINE_CREATE_DISPATCH_BASE_BIT, + VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT = 256, + VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT = 512, + VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineCreateFlagBits; typedef enum VkPrimitiveTopology { VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0, @@ -799,10 +1035,12 @@ typedef enum VkPrimitiveTopology { VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9, - VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10 + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10, + VK_PRIMITIVE_TOPOLOGY_MAX_ENUM = 0x7FFFFFFF } VkPrimitiveTopology; typedef enum VkQueryControlFlagBits { - VK_QUERY_CONTROL_PRECISE_BIT = 1 + VK_QUERY_CONTROL_PRECISE_BIT = 1, + VK_QUERY_CONTROL_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryControlFlagBits; typedef enum VkQueryPipelineStatisticFlagBits { VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT = 1, @@ -815,29 +1053,34 @@ typedef enum VkQueryPipelineStatisticFlagBits { VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT = 128, VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT = 256, VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT = 512, - VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 1024 + VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 1024, + VK_QUERY_PIPELINE_STATISTIC_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryPipelineStatisticFlagBits; typedef enum VkQueryResultFlagBits { VK_QUERY_RESULT_64_BIT = 1, VK_QUERY_RESULT_WAIT_BIT = 2, VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 4, - VK_QUERY_RESULT_PARTIAL_BIT = 8 + VK_QUERY_RESULT_PARTIAL_BIT = 8, + VK_QUERY_RESULT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryResultFlagBits; typedef enum VkQueryType { VK_QUERY_TYPE_OCCLUSION = 0, VK_QUERY_TYPE_PIPELINE_STATISTICS = 1, - VK_QUERY_TYPE_TIMESTAMP = 2 + VK_QUERY_TYPE_TIMESTAMP = 2, + VK_QUERY_TYPE_MAX_ENUM = 0x7FFFFFFF } VkQueryType; typedef enum VkQueueFlagBits { VK_QUEUE_GRAPHICS_BIT = 1, VK_QUEUE_COMPUTE_BIT = 2, VK_QUEUE_TRANSFER_BIT = 4, VK_QUEUE_SPARSE_BINDING_BIT = 8, - VK_QUEUE_PROTECTED_BIT = 16 + VK_QUEUE_PROTECTED_BIT = 16, + VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueueFlagBits; typedef enum VkSubpassContents { VK_SUBPASS_CONTENTS_INLINE = 0, - VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1 + VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1, + VK_SUBPASS_CONTENTS_MAX_ENUM = 0x7FFFFFFF } VkSubpassContents; typedef enum VkResult { VK_SUCCESS = 0, @@ -858,13 +1101,18 @@ typedef enum VkResult { VK_ERROR_TOO_MANY_OBJECTS = -10, VK_ERROR_FORMAT_NOT_SUPPORTED = -11, VK_ERROR_FRAGMENTED_POOL = -12, + VK_ERROR_UNKNOWN = -13, VK_ERROR_OUT_OF_POOL_MEMORY = -1000069000, VK_ERROR_INVALID_EXTERNAL_HANDLE = -1000072003, + VK_ERROR_FRAGMENTATION = -1000161000, + VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS = -1000257000, + VK_PIPELINE_COMPILE_REQUIRED = 1000297000, VK_ERROR_SURFACE_LOST_KHR = -1000000000, VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, VK_SUBOPTIMAL_KHR = 1000001003, VK_ERROR_OUT_OF_DATE_KHR = -1000001004, - VK_ERROR_VALIDATION_FAILED_EXT = -1000011001 + VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_RESULT_MAX_ENUM = 0x7FFFFFFF } VkResult; typedef enum VkShaderStageFlagBits { VK_SHADER_STAGE_VERTEX_BIT = 1, @@ -874,15 +1122,19 @@ typedef enum VkShaderStageFlagBits { VK_SHADER_STAGE_FRAGMENT_BIT = 16, VK_SHADER_STAGE_COMPUTE_BIT = 32, VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, - VK_SHADER_STAGE_ALL = 0x7FFFFFFF + VK_SHADER_STAGE_ALL = 0x7FFFFFFF, + VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkShaderStageFlagBits; typedef enum VkSparseMemoryBindFlagBits { - VK_SPARSE_MEMORY_BIND_METADATA_BIT = 1 + VK_SPARSE_MEMORY_BIND_METADATA_BIT = 1, + VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSparseMemoryBindFlagBits; typedef enum VkStencilFaceFlagBits { VK_STENCIL_FACE_FRONT_BIT = 1, VK_STENCIL_FACE_BACK_BIT = 2, - VK_STENCIL_FRONT_AND_BACK = 0x00000003 + VK_STENCIL_FACE_FRONT_AND_BACK = 0x00000003, + VK_STENCIL_FRONT_AND_BACK = VK_STENCIL_FACE_FRONT_AND_BACK, + VK_STENCIL_FACE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkStencilFaceFlagBits; typedef enum VkStencilOp { VK_STENCIL_OP_KEEP = 0, @@ -892,7 +1144,8 @@ typedef enum VkStencilOp { VK_STENCIL_OP_DECREMENT_AND_CLAMP = 4, VK_STENCIL_OP_INVERT = 5, VK_STENCIL_OP_INCREMENT_AND_WRAP = 6, - VK_STENCIL_OP_DECREMENT_AND_WRAP = 7 + VK_STENCIL_OP_DECREMENT_AND_WRAP = 7, + VK_STENCIL_OP_MAX_ENUM = 0x7FFFFFFF } VkStencilOp; typedef enum VkStructureType { VK_STRUCTURE_TYPE_APPLICATION_INFO = 0, @@ -1011,6 +1264,108 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT = 1000168001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES = 1000063000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES = 49, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES = 50, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES = 51, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES = 52, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO = 1000147000, + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2 = 1000109000, + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2 = 1000109001, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2 = 1000109002, + VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2 = 1000109003, + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2 = 1000109004, + VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO = 1000109005, + VK_STRUCTURE_TYPE_SUBPASS_END_INFO = 1000109006, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES = 1000177000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES = 1000196000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES = 1000180000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES = 1000082000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES = 1000197000, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO = 1000161000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES = 1000161001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES = 1000161002, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO = 1000161003, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT = 1000161004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES = 1000199000, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE = 1000199001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES = 1000221000, + VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO = 1000246000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES = 1000130000, + VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO = 1000130001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES = 1000211000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES = 1000108000, + VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO = 1000108001, + VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO = 1000108002, + VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO = 1000108003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES = 1000253000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES = 1000175000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES = 1000241000, + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_STENCIL_LAYOUT = 1000241001, + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT = 1000241002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES = 1000261000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES = 1000207000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES = 1000207001, + VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO = 1000207002, + VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO = 1000207003, + VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO = 1000207004, + VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO = 1000207005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES = 1000257000, + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO = 1000244001, + VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO = 1000257002, + VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO = 1000257003, + VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO = 1000257004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES = 53, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES = 54, + VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO = 1000192000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES = 1000215000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES = 1000245000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES = 1000276000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES = 1000295000, + VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO = 1000295001, + VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO = 1000295002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES = 1000297000, + VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 = 1000314000, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2 = 1000314001, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 = 1000314002, + VK_STRUCTURE_TYPE_DEPENDENCY_INFO = 1000314003, + VK_STRUCTURE_TYPE_SUBMIT_INFO_2 = 1000314004, + VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO = 1000314005, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO = 1000314006, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES = 1000314007, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES = 1000325000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES = 1000335000, + VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 = 1000337000, + VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2 = 1000337001, + VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 = 1000337002, + VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2 = 1000337003, + VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2 = 1000337004, + VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2 = 1000337005, + VK_STRUCTURE_TYPE_BUFFER_COPY_2 = 1000337006, + VK_STRUCTURE_TYPE_IMAGE_COPY_2 = 1000337007, + VK_STRUCTURE_TYPE_IMAGE_BLIT_2 = 1000337008, + VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 = 1000337009, + VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2 = 1000337010, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES = 1000225000, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO = 1000225001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES = 1000225002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES = 1000138000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES = 1000138001, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK = 1000138002, + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO = 1000138003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES = 1000066000, + VK_STRUCTURE_TYPE_RENDERING_INFO = 1000044000, + VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO = 1000044001, + VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO = 1000044002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES = 1000044003, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO = 1000044004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES = 1000280000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES = 1000280001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES = 1000281001, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3 = 1000360000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES = 1000413000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES = 1000413001, + VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS = 1000413002, + VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS = 1000413003, VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000, VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001, VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, @@ -1020,35 +1375,43 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHR = 1000060011, VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHR = 1000060012, VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, - VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT + VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; typedef enum VkSystemAllocationScope { VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 1, VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 2, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 3, - VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4 + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4, + VK_SYSTEM_ALLOCATION_SCOPE_MAX_ENUM = 0x7FFFFFFF } VkSystemAllocationScope; typedef enum VkInternalAllocationType { - VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0 + VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0, + VK_INTERNAL_ALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF } VkInternalAllocationType; typedef enum VkSamplerAddressMode { VK_SAMPLER_ADDRESS_MODE_REPEAT = 0, VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2, - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3 + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3, + VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4, + VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF } VkSamplerAddressMode; typedef enum VkFilter { VK_FILTER_NEAREST = 0, - VK_FILTER_LINEAR = 1 + VK_FILTER_LINEAR = 1, + VK_FILTER_MAX_ENUM = 0x7FFFFFFF } VkFilter; typedef enum VkSamplerMipmapMode { VK_SAMPLER_MIPMAP_MODE_NEAREST = 0, - VK_SAMPLER_MIPMAP_MODE_LINEAR = 1 + VK_SAMPLER_MIPMAP_MODE_LINEAR = 1, + VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF } VkSamplerMipmapMode; typedef enum VkVertexInputRate { VK_VERTEX_INPUT_RATE_VERTEX = 0, - VK_VERTEX_INPUT_RATE_INSTANCE = 1 + VK_VERTEX_INPUT_RATE_INSTANCE = 1, + VK_VERTEX_INPUT_RATE_MAX_ENUM = 0x7FFFFFFF } VkVertexInputRate; typedef enum VkPipelineStageFlagBits { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 1, @@ -1067,12 +1430,15 @@ typedef enum VkPipelineStageFlagBits { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = 8192, VK_PIPELINE_STAGE_HOST_BIT = 16384, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 32768, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 65536 + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 65536, + VK_PIPELINE_STAGE_NONE = 0, + VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineStageFlagBits; typedef enum VkSparseImageFormatFlagBits { VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 1, VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT = 2, - VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 4 + VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 4, + VK_SPARSE_IMAGE_FORMAT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSparseImageFormatFlagBits; typedef enum VkSampleCountFlagBits { VK_SAMPLE_COUNT_1_BIT = 1, @@ -1081,18 +1447,23 @@ typedef enum VkSampleCountFlagBits { VK_SAMPLE_COUNT_8_BIT = 8, VK_SAMPLE_COUNT_16_BIT = 16, VK_SAMPLE_COUNT_32_BIT = 32, - VK_SAMPLE_COUNT_64_BIT = 64 + VK_SAMPLE_COUNT_64_BIT = 64, + VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSampleCountFlagBits; typedef enum VkAttachmentDescriptionFlagBits { - VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 1 + VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 1, + VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkAttachmentDescriptionFlagBits; typedef enum VkDescriptorPoolCreateFlagBits { - VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 1 + VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 1, + VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT = 2, + VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDescriptorPoolCreateFlagBits; typedef enum VkDependencyFlagBits { VK_DEPENDENCY_BY_REGION_BIT = 1, VK_DEPENDENCY_DEVICE_GROUP_BIT = 4, - VK_DEPENDENCY_VIEW_LOCAL_BIT = 2 + VK_DEPENDENCY_VIEW_LOCAL_BIT = 2, + VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDependencyFlagBits; typedef enum VkObjectType { VK_OBJECT_TYPE_UNKNOWN = 0, @@ -1123,33 +1494,252 @@ typedef enum VkObjectType { VK_OBJECT_TYPE_COMMAND_POOL = 25, VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000, VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, + VK_OBJECT_TYPE_PRIVATE_DATA_SLOT = 1000295000, VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, - VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000 + VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, + VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF } VkObjectType; +typedef enum VkEventCreateFlagBits { + VK_EVENT_CREATE_DEVICE_ONLY_BIT = 1, + VK_EVENT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkEventCreateFlagBits; typedef enum VkDescriptorUpdateTemplateType { - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET = 0 + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET = 0, + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkDescriptorUpdateTemplateType; - typedef enum VkPointClippingBehavior { VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES = 0, - VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY = 1 + VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY = 1, + VK_POINT_CLIPPING_BEHAVIOR_MAX_ENUM = 0x7FFFFFFF } VkPointClippingBehavior; +typedef enum VkResolveModeFlagBits { + VK_RESOLVE_MODE_NONE = 0, + VK_RESOLVE_MODE_SAMPLE_ZERO_BIT = 1, + VK_RESOLVE_MODE_AVERAGE_BIT = 2, + VK_RESOLVE_MODE_MIN_BIT = 4, + VK_RESOLVE_MODE_MAX_BIT = 8, + VK_RESOLVE_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkResolveModeFlagBits; +typedef enum VkDescriptorBindingFlagBits { + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT = 1, + VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT = 2, + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT = 4, + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT = 8, + VK_DESCRIPTOR_BINDING_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorBindingFlagBits; +typedef enum VkSemaphoreType { + VK_SEMAPHORE_TYPE_BINARY = 0, + VK_SEMAPHORE_TYPE_TIMELINE = 1, + VK_SEMAPHORE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkSemaphoreType; +typedef enum VkPipelineCreationFeedbackFlagBits { + VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT = 1, + VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT = VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT, + VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT = 2, + VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT = VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT, + VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT = 4, + VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT = VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT, + VK_PIPELINE_CREATION_FEEDBACK_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCreationFeedbackFlagBits; +typedef enum VkSemaphoreWaitFlagBits { + VK_SEMAPHORE_WAIT_ANY_BIT = 1, + VK_SEMAPHORE_WAIT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSemaphoreWaitFlagBits; +typedef enum VkToolPurposeFlagBits { + VK_TOOL_PURPOSE_VALIDATION_BIT = 1, + VK_TOOL_PURPOSE_VALIDATION_BIT_EXT = VK_TOOL_PURPOSE_VALIDATION_BIT, + VK_TOOL_PURPOSE_PROFILING_BIT = 2, + VK_TOOL_PURPOSE_PROFILING_BIT_EXT = VK_TOOL_PURPOSE_PROFILING_BIT, + VK_TOOL_PURPOSE_TRACING_BIT = 4, + VK_TOOL_PURPOSE_TRACING_BIT_EXT = VK_TOOL_PURPOSE_TRACING_BIT, + VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT = 8, + VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT_EXT = VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT, + VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT = 16, + VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT_EXT = VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT, + VK_TOOL_PURPOSE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkToolPurposeFlagBits; +typedef uint64_t VkAccessFlagBits2; +static const VkAccessFlagBits2 VK_ACCESS_2_NONE = 0; +static const VkAccessFlagBits2 VK_ACCESS_2_NONE_KHR = 0; +static const VkAccessFlagBits2 VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT = 1; +static const VkAccessFlagBits2 VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT_KHR = 1; +static const VkAccessFlagBits2 VK_ACCESS_2_INDEX_READ_BIT = 2; +static const VkAccessFlagBits2 VK_ACCESS_2_INDEX_READ_BIT_KHR = 2; +static const VkAccessFlagBits2 VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT = 4; +static const VkAccessFlagBits2 VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT_KHR = 4; +static const VkAccessFlagBits2 VK_ACCESS_2_UNIFORM_READ_BIT = 8; +static const VkAccessFlagBits2 VK_ACCESS_2_UNIFORM_READ_BIT_KHR = 8; +static const VkAccessFlagBits2 VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT = 16; +static const VkAccessFlagBits2 VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT_KHR = 16; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_READ_BIT = 32; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_READ_BIT_KHR = 32; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_WRITE_BIT = 64; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_WRITE_BIT_KHR = 64; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT = 128; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR = 128; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT = 256; +static const VkAccessFlagBits2 VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT_KHR = 256; +static const VkAccessFlagBits2 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 512; +static const VkAccessFlagBits2 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT_KHR = 512; +static const VkAccessFlagBits2 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 1024; +static const VkAccessFlagBits2 VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT_KHR = 1024; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFER_READ_BIT = 2048; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFER_READ_BIT_KHR = 2048; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFER_WRITE_BIT = 4096; +static const VkAccessFlagBits2 VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR = 4096; +static const VkAccessFlagBits2 VK_ACCESS_2_HOST_READ_BIT = 8192; +static const VkAccessFlagBits2 VK_ACCESS_2_HOST_READ_BIT_KHR = 8192; +static const VkAccessFlagBits2 VK_ACCESS_2_HOST_WRITE_BIT = 16384; +static const VkAccessFlagBits2 VK_ACCESS_2_HOST_WRITE_BIT_KHR = 16384; +static const VkAccessFlagBits2 VK_ACCESS_2_MEMORY_READ_BIT = 32768; +static const VkAccessFlagBits2 VK_ACCESS_2_MEMORY_READ_BIT_KHR = 32768; +static const VkAccessFlagBits2 VK_ACCESS_2_MEMORY_WRITE_BIT = 65536; +static const VkAccessFlagBits2 VK_ACCESS_2_MEMORY_WRITE_BIT_KHR = 65536; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_SAMPLED_READ_BIT = 4294967296; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_SAMPLED_READ_BIT_KHR = 4294967296; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_STORAGE_READ_BIT = 8589934592; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_STORAGE_READ_BIT_KHR = 8589934592; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT = 17179869184; +static const VkAccessFlagBits2 VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT_KHR = 17179869184; + +typedef uint64_t VkPipelineStageFlagBits2; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_NONE = 0; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_NONE_KHR = 0; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT = 1; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR = 1; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT = 2; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT_KHR = 2; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT = 4; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT_KHR = 4; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT = 8; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT_KHR = 8; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT = 16; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT_KHR = 16; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT = 32; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT_KHR = 32; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT = 64; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT_KHR = 64; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT = 128; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT_KHR = 128; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT = 256; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT_KHR = 256; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT = 512; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT_KHR = 512; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT = 1024; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR = 1024; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT = 2048; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT_KHR = 2048; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT = 4096; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT_KHR = 4096; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TRANSFER_BIT = 4096; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_TRANSFER_BIT_KHR = 4096; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT = 8192; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR = 8192; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_HOST_BIT = 16384; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_HOST_BIT_KHR = 16384; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT = 32768; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT_KHR = 32768; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT = 65536; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR = 65536; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COPY_BIT = 4294967296; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_COPY_BIT_KHR = 4294967296; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_RESOLVE_BIT = 8589934592; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_RESOLVE_BIT_KHR = 8589934592; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_BLIT_BIT = 17179869184; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_BLIT_BIT_KHR = 17179869184; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_CLEAR_BIT = 34359738368; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_CLEAR_BIT_KHR = 34359738368; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT = 68719476736; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT_KHR = 68719476736; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT = 137438953472; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT_KHR = 137438953472; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT = 274877906944; +static const VkPipelineStageFlagBits2 VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT_KHR = 274877906944; + +typedef uint64_t VkFormatFeatureFlagBits2; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT = 1; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT_KHR = 1; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT = 2; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT_KHR = 2; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT = 4; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT_KHR = 4; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT = 8; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT_KHR = 8; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT = 16; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT_KHR = 16; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT = 32; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT_KHR = 32; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT = 64; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT_KHR = 64; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT = 128; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR = 128; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT = 256; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR = 256; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT = 512; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR = 512; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLIT_SRC_BIT = 1024; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLIT_SRC_BIT_KHR = 1024; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLIT_DST_BIT = 2048; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_BLIT_DST_BIT_KHR = 2048; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 4096; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT_KHR = 4096; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT = 8192; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT = 8192; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT = 16384; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT_KHR = 16384; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT = 32768; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT_KHR = 32768; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT = 65536; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT_KHR = 65536; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT = 131072; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 131072; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT = 262144; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 262144; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT = 524288; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 524288; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT = 1048576; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 1048576; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT = 2097152; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 2097152; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DISJOINT_BIT = 4194304; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_DISJOINT_BIT_KHR = 4194304; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT = 8388608; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT_KHR = 8388608; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT = 2147483648; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT_KHR = 2147483648; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT = 4294967296; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT_KHR = 4294967296; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT = 8589934592; +static const VkFormatFeatureFlagBits2 VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT_KHR = 8589934592; + +typedef enum VkRenderingFlagBits { + VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT = 1, + VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR = VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT, + VK_RENDERING_SUSPENDING_BIT = 2, + VK_RENDERING_SUSPENDING_BIT_KHR = VK_RENDERING_SUSPENDING_BIT, + VK_RENDERING_RESUMING_BIT = 4, + VK_RENDERING_RESUMING_BIT_KHR = VK_RENDERING_RESUMING_BIT, + VK_RENDERING_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkRenderingFlagBits; typedef enum VkColorSpaceKHR { VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0, - VK_COLORSPACE_SRGB_NONLINEAR_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + VK_COLORSPACE_SRGB_NONLINEAR_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF } VkColorSpaceKHR; typedef enum VkCompositeAlphaFlagBitsKHR { VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 1, VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 2, VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 4, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 8 + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 8, + VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkCompositeAlphaFlagBitsKHR; typedef enum VkPresentModeKHR { VK_PRESENT_MODE_IMMEDIATE_KHR = 0, VK_PRESENT_MODE_MAILBOX_KHR = 1, VK_PRESENT_MODE_FIFO_KHR = 2, - VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3 + VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3, + VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF } VkPresentModeKHR; typedef enum VkSurfaceTransformFlagBitsKHR { VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 1, @@ -1160,14 +1750,16 @@ typedef enum VkSurfaceTransformFlagBitsKHR { VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR = 32, VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 64, VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 128, - VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 256 + VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 256, + VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkSurfaceTransformFlagBitsKHR; typedef enum VkDebugReportFlagBitsEXT { VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 1, VK_DEBUG_REPORT_WARNING_BIT_EXT = 2, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 4, VK_DEBUG_REPORT_ERROR_BIT_EXT = 8, - VK_DEBUG_REPORT_DEBUG_BIT_EXT = 16 + VK_DEBUG_REPORT_DEBUG_BIT_EXT = 16, + VK_DEBUG_REPORT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportFlagBitsEXT; typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, @@ -1202,12 +1794,11 @@ typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, - VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, - VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT = 33, VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT = 1000156000, - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT = 1000085000 + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT = 1000085000, + VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportObjectTypeEXT; typedef enum VkExternalMemoryHandleTypeFlagBits { VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT = 1, @@ -1216,58 +1807,73 @@ typedef enum VkExternalMemoryHandleTypeFlagBits { VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT = 8, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT = 16, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT = 32, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT = 64 + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT = 64, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkExternalMemoryHandleTypeFlagBits; typedef enum VkExternalMemoryFeatureFlagBits { VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT = 1, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT = 2, - VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT = 4 + VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT = 4, + VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkExternalMemoryFeatureFlagBits; typedef enum VkExternalSemaphoreHandleTypeFlagBits { VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT = 1, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT = 2, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 4, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT = 8, - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT = 16 + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D11_FENCE_BIT = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT = 16, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkExternalSemaphoreHandleTypeFlagBits; typedef enum VkExternalSemaphoreFeatureFlagBits { VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT = 1, - VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT = 2 + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT = 2, + VK_EXTERNAL_SEMAPHORE_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkExternalSemaphoreFeatureFlagBits; typedef enum VkSemaphoreImportFlagBits { - VK_SEMAPHORE_IMPORT_TEMPORARY_BIT = 1 + VK_SEMAPHORE_IMPORT_TEMPORARY_BIT = 1, + VK_SEMAPHORE_IMPORT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSemaphoreImportFlagBits; typedef enum VkExternalFenceHandleTypeFlagBits { VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT = 1, VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT = 2, VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 4, - VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT = 8 + VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT = 8, + VK_EXTERNAL_FENCE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkExternalFenceHandleTypeFlagBits; typedef enum VkExternalFenceFeatureFlagBits { VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT = 1, - VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT = 2 + VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT = 2, + VK_EXTERNAL_FENCE_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkExternalFenceFeatureFlagBits; typedef enum VkFenceImportFlagBits { - VK_FENCE_IMPORT_TEMPORARY_BIT = 1 + VK_FENCE_IMPORT_TEMPORARY_BIT = 1, + VK_FENCE_IMPORT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFenceImportFlagBits; typedef enum VkPeerMemoryFeatureFlagBits { VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT = 1, VK_PEER_MEMORY_FEATURE_COPY_DST_BIT = 2, VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT = 4, - VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT = 8 + VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT = 8, + VK_PEER_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPeerMemoryFeatureFlagBits; typedef enum VkMemoryAllocateFlagBits { - VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT = 1 + VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT = 1, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT = 2, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT = 4, + VK_MEMORY_ALLOCATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryAllocateFlagBits; typedef enum VkDeviceGroupPresentModeFlagBitsKHR { VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR = 1, VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHR = 2, VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHR = 4, - VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHR = 8 + VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHR = 8, + VK_DEVICE_GROUP_PRESENT_MODE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkDeviceGroupPresentModeFlagBitsKHR; typedef enum VkSwapchainCreateFlagBitsKHR { VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = 1, - VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR = 2 + VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR = 2, + VK_SWAPCHAIN_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkSwapchainCreateFlagBitsKHR; typedef enum VkSubgroupFeatureFlagBits { VK_SUBGROUP_FEATURE_BASIC_BIT = 1, @@ -1277,32 +1883,84 @@ typedef enum VkSubgroupFeatureFlagBits { VK_SUBGROUP_FEATURE_SHUFFLE_BIT = 16, VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT = 32, VK_SUBGROUP_FEATURE_CLUSTERED_BIT = 64, - VK_SUBGROUP_FEATURE_QUAD_BIT = 128 + VK_SUBGROUP_FEATURE_QUAD_BIT = 128, + VK_SUBGROUP_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSubgroupFeatureFlagBits; typedef enum VkTessellationDomainOrigin { VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT = 0, - VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT = 1 + VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT = 1, + VK_TESSELLATION_DOMAIN_ORIGIN_MAX_ENUM = 0x7FFFFFFF } VkTessellationDomainOrigin; typedef enum VkSamplerYcbcrModelConversion { VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY = 1, VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709 = 2, VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601 = 3, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 = 4 + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 = 4, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_MAX_ENUM = 0x7FFFFFFF } VkSamplerYcbcrModelConversion; typedef enum VkSamplerYcbcrRange { VK_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, - VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1 + VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1, + VK_SAMPLER_YCBCR_RANGE_MAX_ENUM = 0x7FFFFFFF } VkSamplerYcbcrRange; typedef enum VkChromaLocation { VK_CHROMA_LOCATION_COSITED_EVEN = 0, - VK_CHROMA_LOCATION_MIDPOINT = 1 + VK_CHROMA_LOCATION_MIDPOINT = 1, + VK_CHROMA_LOCATION_MAX_ENUM = 0x7FFFFFFF } VkChromaLocation; +typedef enum VkSamplerReductionMode { + VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE = 0, + VK_SAMPLER_REDUCTION_MODE_MIN = 1, + VK_SAMPLER_REDUCTION_MODE_MAX = 2, + VK_SAMPLER_REDUCTION_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerReductionMode; +typedef enum VkShaderFloatControlsIndependence { + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY = 0, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL = 1, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE = 2, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_MAX_ENUM = 0x7FFFFFFF +} VkShaderFloatControlsIndependence; +typedef enum VkSubmitFlagBits { + VK_SUBMIT_PROTECTED_BIT = 1, + VK_SUBMIT_PROTECTED_BIT_KHR = VK_SUBMIT_PROTECTED_BIT, + VK_SUBMIT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSubmitFlagBits; typedef enum VkVendorId { VK_VENDOR_ID_VIV = 0x10001, VK_VENDOR_ID_VSI = 0x10002, - VK_VENDOR_ID_KAZAN = 0x10003 + VK_VENDOR_ID_KAZAN = 0x10003, + VK_VENDOR_ID_CODEPLAY = 0x10004, + VK_VENDOR_ID_MESA = 0x10005, + VK_VENDOR_ID_POCL = 0x10006, + VK_VENDOR_ID_MAX_ENUM = 0x7FFFFFFF } VkVendorId; +typedef enum VkDriverId { + VK_DRIVER_ID_AMD_PROPRIETARY = 1, + VK_DRIVER_ID_AMD_OPEN_SOURCE = 2, + VK_DRIVER_ID_MESA_RADV = 3, + VK_DRIVER_ID_NVIDIA_PROPRIETARY = 4, + VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS = 5, + VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA = 6, + VK_DRIVER_ID_IMAGINATION_PROPRIETARY = 7, + VK_DRIVER_ID_QUALCOMM_PROPRIETARY = 8, + VK_DRIVER_ID_ARM_PROPRIETARY = 9, + VK_DRIVER_ID_GOOGLE_SWIFTSHADER = 10, + VK_DRIVER_ID_GGP_PROPRIETARY = 11, + VK_DRIVER_ID_BROADCOM_PROPRIETARY = 12, + VK_DRIVER_ID_MESA_LLVMPIPE = 13, + VK_DRIVER_ID_MOLTENVK = 14, + VK_DRIVER_ID_COREAVI_PROPRIETARY = 15, + VK_DRIVER_ID_JUICE_PROPRIETARY = 16, + VK_DRIVER_ID_VERISILICON_PROPRIETARY = 17, + VK_DRIVER_ID_MESA_TURNIP = 18, + VK_DRIVER_ID_MESA_V3DV = 19, + VK_DRIVER_ID_MESA_PANVK = 20, + VK_DRIVER_ID_SAMSUNG_PROPRIETARY = 21, + VK_DRIVER_ID_MESA_VENUS = 22, + VK_DRIVER_ID_MESA_DOZEN = 23, + VK_DRIVER_ID_MAX_ENUM = 0x7FFFFFFF +} VkDriverId; typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( void* pUserData, size_t size, @@ -1332,28 +1990,34 @@ typedef struct VkBaseOutStructure { VkStructureType sType; struct VkBaseOutStructure * pNext; } VkBaseOutStructure; + typedef struct VkBaseInStructure { VkStructureType sType; const struct VkBaseInStructure * pNext; } VkBaseInStructure; + typedef struct VkOffset2D { int32_t x; int32_t y; } VkOffset2D; + typedef struct VkOffset3D { int32_t x; int32_t y; int32_t z; } VkOffset3D; + typedef struct VkExtent2D { uint32_t width; uint32_t height; } VkExtent2D; + typedef struct VkExtent3D { uint32_t width; uint32_t height; uint32_t depth; } VkExtent3D; + typedef struct VkViewport { float x; float y; @@ -1362,31 +2026,37 @@ typedef struct VkViewport { float minDepth; float maxDepth; } VkViewport; + typedef struct VkRect2D { VkOffset2D offset; VkExtent2D extent; } VkRect2D; + typedef struct VkClearRect { VkRect2D rect; uint32_t baseArrayLayer; uint32_t layerCount; } VkClearRect; + typedef struct VkComponentMapping { VkComponentSwizzle r; VkComponentSwizzle g; VkComponentSwizzle b; VkComponentSwizzle a; } VkComponentMapping; + typedef struct VkExtensionProperties { char extensionName [ VK_MAX_EXTENSION_NAME_SIZE ]; uint32_t specVersion; } VkExtensionProperties; + typedef struct VkLayerProperties { char layerName [ VK_MAX_EXTENSION_NAME_SIZE ]; uint32_t specVersion; uint32_t implementationVersion; char description [ VK_MAX_DESCRIPTION_SIZE ]; } VkLayerProperties; + typedef struct VkApplicationInfo { VkStructureType sType; const void * pNext; @@ -1396,6 +2066,7 @@ typedef struct VkApplicationInfo { uint32_t engineVersion; uint32_t apiVersion; } VkApplicationInfo; + typedef struct VkAllocationCallbacks { void * pUserData; PFN_vkAllocationFunction pfnAllocation; @@ -1404,11 +2075,13 @@ typedef struct VkAllocationCallbacks { PFN_vkInternalAllocationNotification pfnInternalAllocation; PFN_vkInternalFreeNotification pfnInternalFree; } VkAllocationCallbacks; + typedef struct VkDescriptorImageInfo { VkSampler sampler; VkImageView imageView; VkImageLayout imageLayout; } VkDescriptorImageInfo; + typedef struct VkCopyDescriptorSet { VkStructureType sType; const void * pNext; @@ -1420,10 +2093,12 @@ typedef struct VkCopyDescriptorSet { uint32_t dstArrayElement; uint32_t descriptorCount; } VkCopyDescriptorSet; + typedef struct VkDescriptorPoolSize { VkDescriptorType type; uint32_t descriptorCount; } VkDescriptorPoolSize; + typedef struct VkDescriptorSetAllocateInfo { VkStructureType sType; const void * pNext; @@ -1431,28 +2106,33 @@ typedef struct VkDescriptorSetAllocateInfo { uint32_t descriptorSetCount; const VkDescriptorSetLayout * pSetLayouts; } VkDescriptorSetAllocateInfo; + typedef struct VkSpecializationMapEntry { uint32_t constantID; uint32_t offset; size_t size; } VkSpecializationMapEntry; + typedef struct VkSpecializationInfo { uint32_t mapEntryCount; const VkSpecializationMapEntry * pMapEntries; size_t dataSize; const void * pData; } VkSpecializationInfo; + typedef struct VkVertexInputBindingDescription { uint32_t binding; uint32_t stride; VkVertexInputRate inputRate; } VkVertexInputBindingDescription; + typedef struct VkVertexInputAttributeDescription { uint32_t location; uint32_t binding; VkFormat format; uint32_t offset; } VkVertexInputAttributeDescription; + typedef struct VkStencilOpState { VkStencilOp failOp; VkStencilOp passOp; @@ -1462,6 +2142,15 @@ typedef struct VkStencilOpState { uint32_t writeMask; uint32_t reference; } VkStencilOpState; + +typedef struct VkPipelineCacheHeaderVersionOne { + uint32_t headerSize; + VkPipelineCacheHeaderVersion headerVersion; + uint32_t vendorID; + uint32_t deviceID; + uint8_t pipelineCacheUUID [ VK_UUID_SIZE ]; +} VkPipelineCacheHeaderVersionOne; + typedef struct VkCommandBufferAllocateInfo { VkStructureType sType; const void * pNext; @@ -1469,29 +2158,35 @@ typedef struct VkCommandBufferAllocateInfo { VkCommandBufferLevel level; uint32_t commandBufferCount; } VkCommandBufferAllocateInfo; + typedef union VkClearColorValue { float float32 [4]; int32_t int32 [4]; uint32_t uint32 [4]; } VkClearColorValue; + typedef struct VkClearDepthStencilValue { float depth; uint32_t stencil; } VkClearDepthStencilValue; + typedef union VkClearValue { VkClearColorValue color; VkClearDepthStencilValue depthStencil; } VkClearValue; + typedef struct VkAttachmentReference { uint32_t attachment; VkImageLayout layout; } VkAttachmentReference; + typedef struct VkDrawIndirectCommand { uint32_t vertexCount; uint32_t instanceCount; uint32_t firstVertex; uint32_t firstInstance; } VkDrawIndirectCommand; + typedef struct VkDrawIndexedIndirectCommand { uint32_t indexCount; uint32_t instanceCount; @@ -1499,15 +2194,18 @@ typedef struct VkDrawIndexedIndirectCommand { int32_t vertexOffset; uint32_t firstInstance; } VkDrawIndexedIndirectCommand; + typedef struct VkDispatchIndirectCommand { uint32_t x; uint32_t y; uint32_t z; } VkDispatchIndirectCommand; + typedef struct VkSurfaceFormatKHR { VkFormat format; VkColorSpaceKHR colorSpace; } VkSurfaceFormatKHR; + typedef struct VkPresentInfoKHR { VkStructureType sType; const void * pNext; @@ -1518,27 +2216,54 @@ typedef struct VkPresentInfoKHR { const uint32_t * pImageIndices; VkResult * pResults; } VkPresentInfoKHR; + +typedef struct VkDevicePrivateDataCreateInfo { + VkStructureType sType; + const void * pNext; + uint32_t privateDataSlotRequestCount; +} VkDevicePrivateDataCreateInfo; + +typedef struct VkConformanceVersion { + uint8_t major; + uint8_t minor; + uint8_t subminor; + uint8_t patch; +} VkConformanceVersion; + +typedef struct VkPhysicalDeviceDriverProperties { + VkStructureType sType; + void * pNext; + VkDriverId driverID; + char driverName [ VK_MAX_DRIVER_NAME_SIZE ]; + char driverInfo [ VK_MAX_DRIVER_INFO_SIZE ]; + VkConformanceVersion conformanceVersion; +} VkPhysicalDeviceDriverProperties; + typedef struct VkPhysicalDeviceExternalImageFormatInfo { VkStructureType sType; const void * pNext; VkExternalMemoryHandleTypeFlagBits handleType; } VkPhysicalDeviceExternalImageFormatInfo; + typedef struct VkPhysicalDeviceExternalSemaphoreInfo { VkStructureType sType; const void * pNext; VkExternalSemaphoreHandleTypeFlagBits handleType; } VkPhysicalDeviceExternalSemaphoreInfo; + typedef struct VkPhysicalDeviceExternalFenceInfo { VkStructureType sType; const void * pNext; VkExternalFenceHandleTypeFlagBits handleType; } VkPhysicalDeviceExternalFenceInfo; + typedef struct VkPhysicalDeviceMultiviewProperties { VkStructureType sType; void * pNext; uint32_t maxMultiviewViewCount; uint32_t maxMultiviewInstanceIndex; } VkPhysicalDeviceMultiviewProperties; + typedef struct VkRenderPassMultiviewCreateInfo { VkStructureType sType; const void * pNext; @@ -1549,12 +2274,14 @@ typedef struct VkRenderPassMultiviewCreateInfo { uint32_t correlationMaskCount; const uint32_t * pCorrelationMasks; } VkRenderPassMultiviewCreateInfo; + typedef struct VkBindBufferMemoryDeviceGroupInfo { VkStructureType sType; const void * pNext; uint32_t deviceIndexCount; const uint32_t * pDeviceIndices; } VkBindBufferMemoryDeviceGroupInfo; + typedef struct VkBindImageMemoryDeviceGroupInfo { VkStructureType sType; const void * pNext; @@ -1563,6 +2290,7 @@ typedef struct VkBindImageMemoryDeviceGroupInfo { uint32_t splitInstanceBindRegionCount; const VkRect2D * pSplitInstanceBindRegions; } VkBindImageMemoryDeviceGroupInfo; + typedef struct VkDeviceGroupRenderPassBeginInfo { VkStructureType sType; const void * pNext; @@ -1570,11 +2298,13 @@ typedef struct VkDeviceGroupRenderPassBeginInfo { uint32_t deviceRenderAreaCount; const VkRect2D * pDeviceRenderAreas; } VkDeviceGroupRenderPassBeginInfo; + typedef struct VkDeviceGroupCommandBufferBeginInfo { VkStructureType sType; const void * pNext; uint32_t deviceMask; } VkDeviceGroupCommandBufferBeginInfo; + typedef struct VkDeviceGroupSubmitInfo { VkStructureType sType; const void * pNext; @@ -1585,23 +2315,27 @@ typedef struct VkDeviceGroupSubmitInfo { uint32_t signalSemaphoreCount; const uint32_t * pSignalSemaphoreDeviceIndices; } VkDeviceGroupSubmitInfo; + typedef struct VkDeviceGroupBindSparseInfo { VkStructureType sType; const void * pNext; uint32_t resourceDeviceIndex; uint32_t memoryDeviceIndex; } VkDeviceGroupBindSparseInfo; + typedef struct VkImageSwapchainCreateInfoKHR { VkStructureType sType; const void * pNext; VkSwapchainKHR swapchain; } VkImageSwapchainCreateInfoKHR; + typedef struct VkBindImageMemorySwapchainInfoKHR { VkStructureType sType; const void * pNext; VkSwapchainKHR swapchain; uint32_t imageIndex; } VkBindImageMemorySwapchainInfoKHR; + typedef struct VkAcquireNextImageInfoKHR { VkStructureType sType; const void * pNext; @@ -1611,6 +2345,7 @@ typedef struct VkAcquireNextImageInfoKHR { VkFence fence; uint32_t deviceMask; } VkAcquireNextImageInfoKHR; + typedef struct VkDeviceGroupPresentInfoKHR { VkStructureType sType; const void * pNext; @@ -1618,12 +2353,14 @@ typedef struct VkDeviceGroupPresentInfoKHR { const uint32_t * pDeviceMasks; VkDeviceGroupPresentModeFlagBitsKHR mode; } VkDeviceGroupPresentInfoKHR; + typedef struct VkDeviceGroupDeviceCreateInfo { VkStructureType sType; const void * pNext; uint32_t physicalDeviceCount; const VkPhysicalDevice * pPhysicalDevices; } VkDeviceGroupDeviceCreateInfo; + typedef struct VkDescriptorUpdateTemplateEntry { uint32_t dstBinding; uint32_t dstArrayElement; @@ -1632,61 +2369,243 @@ typedef struct VkDescriptorUpdateTemplateEntry { size_t offset; size_t stride; } VkDescriptorUpdateTemplateEntry; + typedef struct VkBufferMemoryRequirementsInfo2 { VkStructureType sType; const void * pNext; VkBuffer buffer; } VkBufferMemoryRequirementsInfo2; + typedef struct VkImageMemoryRequirementsInfo2 { VkStructureType sType; const void * pNext; VkImage image; } VkImageMemoryRequirementsInfo2; + typedef struct VkImageSparseMemoryRequirementsInfo2 { VkStructureType sType; const void * pNext; VkImage image; } VkImageSparseMemoryRequirementsInfo2; + typedef struct VkPhysicalDevicePointClippingProperties { VkStructureType sType; void * pNext; - VkPointClippingBehavior pointClippingBehavior; + VkPointClippingBehavior pointClippingBehavior; } VkPhysicalDevicePointClippingProperties; + typedef struct VkMemoryDedicatedAllocateInfo { VkStructureType sType; const void * pNext; VkImage image; VkBuffer buffer; } VkMemoryDedicatedAllocateInfo; + typedef struct VkPipelineTessellationDomainOriginStateCreateInfo { VkStructureType sType; const void * pNext; VkTessellationDomainOrigin domainOrigin; } VkPipelineTessellationDomainOriginStateCreateInfo; + typedef struct VkSamplerYcbcrConversionInfo { VkStructureType sType; const void * pNext; VkSamplerYcbcrConversion conversion; } VkSamplerYcbcrConversionInfo; + typedef struct VkBindImagePlaneMemoryInfo { VkStructureType sType; const void * pNext; VkImageAspectFlagBits planeAspect; } VkBindImagePlaneMemoryInfo; + typedef struct VkImagePlaneMemoryRequirementsInfo { VkStructureType sType; const void * pNext; VkImageAspectFlagBits planeAspect; } VkImagePlaneMemoryRequirementsInfo; + typedef struct VkSamplerYcbcrConversionImageFormatProperties { VkStructureType sType; void * pNext; uint32_t combinedImageSamplerDescriptorCount; } VkSamplerYcbcrConversionImageFormatProperties; + +typedef struct VkSamplerReductionModeCreateInfo { + VkStructureType sType; + const void * pNext; + VkSamplerReductionMode reductionMode; +} VkSamplerReductionModeCreateInfo; + +typedef struct VkPhysicalDeviceInlineUniformBlockProperties { + VkStructureType sType; + void * pNext; + uint32_t maxInlineUniformBlockSize; + uint32_t maxPerStageDescriptorInlineUniformBlocks; + uint32_t maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks; + uint32_t maxDescriptorSetInlineUniformBlocks; + uint32_t maxDescriptorSetUpdateAfterBindInlineUniformBlocks; +} VkPhysicalDeviceInlineUniformBlockProperties; + +typedef struct VkWriteDescriptorSetInlineUniformBlock { + VkStructureType sType; + const void * pNext; + uint32_t dataSize; + const void * pData; +} VkWriteDescriptorSetInlineUniformBlock; + +typedef struct VkDescriptorPoolInlineUniformBlockCreateInfo { + VkStructureType sType; + const void * pNext; + uint32_t maxInlineUniformBlockBindings; +} VkDescriptorPoolInlineUniformBlockCreateInfo; + +typedef struct VkImageFormatListCreateInfo { + VkStructureType sType; + const void * pNext; + uint32_t viewFormatCount; + const VkFormat * pViewFormats; +} VkImageFormatListCreateInfo; + +typedef struct VkDescriptorSetVariableDescriptorCountAllocateInfo { + VkStructureType sType; + const void * pNext; + uint32_t descriptorSetCount; + const uint32_t * pDescriptorCounts; +} VkDescriptorSetVariableDescriptorCountAllocateInfo; + +typedef struct VkDescriptorSetVariableDescriptorCountLayoutSupport { + VkStructureType sType; + void * pNext; + uint32_t maxVariableDescriptorCount; +} VkDescriptorSetVariableDescriptorCountLayoutSupport; + +typedef struct VkSubpassBeginInfo { + VkStructureType sType; + const void * pNext; + VkSubpassContents contents; +} VkSubpassBeginInfo; + +typedef struct VkSubpassEndInfo { + VkStructureType sType; + const void * pNext; +} VkSubpassEndInfo; + +typedef struct VkPhysicalDeviceTimelineSemaphoreProperties { + VkStructureType sType; + void * pNext; + uint64_t maxTimelineSemaphoreValueDifference; +} VkPhysicalDeviceTimelineSemaphoreProperties; + +typedef struct VkSemaphoreTypeCreateInfo { + VkStructureType sType; + const void * pNext; + VkSemaphoreType semaphoreType; + uint64_t initialValue; +} VkSemaphoreTypeCreateInfo; + +typedef struct VkTimelineSemaphoreSubmitInfo { + VkStructureType sType; + const void * pNext; + uint32_t waitSemaphoreValueCount; + const uint64_t * pWaitSemaphoreValues; + uint32_t signalSemaphoreValueCount; + const uint64_t * pSignalSemaphoreValues; +} VkTimelineSemaphoreSubmitInfo; + +typedef struct VkSemaphoreSignalInfo { + VkStructureType sType; + const void * pNext; + VkSemaphore semaphore; + uint64_t value; +} VkSemaphoreSignalInfo; + +typedef struct VkBufferDeviceAddressInfo { + VkStructureType sType; + const void * pNext; + VkBuffer buffer; +} VkBufferDeviceAddressInfo; + +typedef struct VkBufferOpaqueCaptureAddressCreateInfo { + VkStructureType sType; + const void * pNext; + uint64_t opaqueCaptureAddress; +} VkBufferOpaqueCaptureAddressCreateInfo; + +typedef struct VkRenderPassAttachmentBeginInfo { + VkStructureType sType; + const void * pNext; + uint32_t attachmentCount; + const VkImageView * pAttachments; +} VkRenderPassAttachmentBeginInfo; + +typedef struct VkAttachmentReferenceStencilLayout { + VkStructureType sType; + void * pNext; + VkImageLayout stencilLayout; +} VkAttachmentReferenceStencilLayout; + +typedef struct VkAttachmentDescriptionStencilLayout { + VkStructureType sType; + void * pNext; + VkImageLayout stencilInitialLayout; + VkImageLayout stencilFinalLayout; +} VkAttachmentDescriptionStencilLayout; + +typedef struct VkPipelineShaderStageRequiredSubgroupSizeCreateInfo { + VkStructureType sType; + void * pNext; + uint32_t requiredSubgroupSize; +} VkPipelineShaderStageRequiredSubgroupSizeCreateInfo; + +typedef struct VkMemoryOpaqueCaptureAddressAllocateInfo { + VkStructureType sType; + const void * pNext; + uint64_t opaqueCaptureAddress; +} VkMemoryOpaqueCaptureAddressAllocateInfo; + +typedef struct VkDeviceMemoryOpaqueCaptureAddressInfo { + VkStructureType sType; + const void * pNext; + VkDeviceMemory memory; +} VkDeviceMemoryOpaqueCaptureAddressInfo; + +typedef struct VkCommandBufferSubmitInfo { + VkStructureType sType; + const void * pNext; + VkCommandBuffer commandBuffer; + uint32_t deviceMask; +} VkCommandBufferSubmitInfo; + +typedef struct VkPipelineRenderingCreateInfo { + VkStructureType sType; + const void * pNext; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkFormat * pColorAttachmentFormats; + VkFormat depthAttachmentFormat; + VkFormat stencilAttachmentFormat; +} VkPipelineRenderingCreateInfo; + +typedef struct VkRenderingAttachmentInfo { + VkStructureType sType; + const void * pNext; + VkImageView imageView; + VkImageLayout imageLayout; + VkResolveModeFlagBits resolveMode; + VkImageView resolveImageView; + VkImageLayout resolveImageLayout; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkClearValue clearValue; +} VkRenderingAttachmentInfo; + typedef uint32_t VkSampleMask; typedef uint32_t VkBool32; typedef uint32_t VkFlags; +typedef uint64_t VkFlags64; typedef uint64_t VkDeviceSize; +typedef uint64_t VkDeviceAddress; typedef VkFlags VkFramebufferCreateFlags; typedef VkFlags VkQueryPoolCreateFlags; typedef VkFlags VkRenderPassCreateFlags; @@ -1746,7 +2665,14 @@ typedef VkFlags VkDescriptorPoolCreateFlags; typedef VkFlags VkDescriptorPoolResetFlags; typedef VkFlags VkDependencyFlags; typedef VkFlags VkSubgroupFeatureFlags; +typedef VkFlags VkPrivateDataSlotCreateFlags; typedef VkFlags VkDescriptorUpdateTemplateCreateFlags; +typedef VkFlags VkPipelineCreationFeedbackFlags; +typedef VkFlags VkSemaphoreWaitFlags; +typedef VkFlags64 VkAccessFlags2; +typedef VkFlags64 VkPipelineStageFlags2; +typedef VkFlags64 VkFormatFeatureFlags2; +typedef VkFlags VkRenderingFlags; typedef VkFlags VkCompositeAlphaFlagsKHR; typedef VkFlags VkSurfaceTransformFlagsKHR; typedef VkFlags VkSwapchainCreateFlagsKHR; @@ -1763,6 +2689,10 @@ typedef VkFlags VkSemaphoreImportFlags; typedef VkFlags VkExternalFenceHandleTypeFlags; typedef VkFlags VkExternalFenceFeatureFlags; typedef VkFlags VkFenceImportFlags; +typedef VkFlags VkDescriptorBindingFlags; +typedef VkFlags VkResolveModeFlags; +typedef VkFlags VkToolPurposeFlags; +typedef VkFlags VkSubmitFlags; typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, @@ -1780,6 +2710,7 @@ typedef struct VkDeviceQueueCreateInfo { uint32_t queueCount; const float * pQueuePriorities; } VkDeviceQueueCreateInfo; + typedef struct VkInstanceCreateInfo { VkStructureType sType; const void * pNext; @@ -1790,28 +2721,33 @@ typedef struct VkInstanceCreateInfo { uint32_t enabledExtensionCount; const char * const* ppEnabledExtensionNames; } VkInstanceCreateInfo; + typedef struct VkQueueFamilyProperties { VkQueueFlags queueFlags; uint32_t queueCount; uint32_t timestampValidBits; VkExtent3D minImageTransferGranularity; } VkQueueFamilyProperties; + typedef struct VkMemoryAllocateInfo { VkStructureType sType; const void * pNext; VkDeviceSize allocationSize; uint32_t memoryTypeIndex; } VkMemoryAllocateInfo; + typedef struct VkMemoryRequirements { VkDeviceSize size; VkDeviceSize alignment; uint32_t memoryTypeBits; } VkMemoryRequirements; + typedef struct VkSparseImageFormatProperties { - VkImageAspectFlags aspectMask; - VkExtent3D imageGranularity; + VkImageAspectFlags aspectMask; + VkExtent3D imageGranularity; VkSparseImageFormatFlags flags; } VkSparseImageFormatProperties; + typedef struct VkSparseImageMemoryRequirements { VkSparseImageFormatProperties formatProperties; uint32_t imageMipTailFirstLod; @@ -1819,14 +2755,17 @@ typedef struct VkSparseImageMemoryRequirements { VkDeviceSize imageMipTailOffset; VkDeviceSize imageMipTailStride; } VkSparseImageMemoryRequirements; + typedef struct VkMemoryType { VkMemoryPropertyFlags propertyFlags; uint32_t heapIndex; } VkMemoryType; + typedef struct VkMemoryHeap { VkDeviceSize size; VkMemoryHeapFlags flags; } VkMemoryHeap; + typedef struct VkMappedMemoryRange { VkStructureType sType; const void * pNext; @@ -1834,11 +2773,13 @@ typedef struct VkMappedMemoryRange { VkDeviceSize offset; VkDeviceSize size; } VkMappedMemoryRange; + typedef struct VkFormatProperties { VkFormatFeatureFlags linearTilingFeatures; VkFormatFeatureFlags optimalTilingFeatures; VkFormatFeatureFlags bufferFeatures; } VkFormatProperties; + typedef struct VkImageFormatProperties { VkExtent3D maxExtent; uint32_t maxMipLevels; @@ -1846,11 +2787,13 @@ typedef struct VkImageFormatProperties { VkSampleCountFlags sampleCounts; VkDeviceSize maxResourceSize; } VkImageFormatProperties; + typedef struct VkDescriptorBufferInfo { VkBuffer buffer; VkDeviceSize offset; VkDeviceSize range; } VkDescriptorBufferInfo; + typedef struct VkWriteDescriptorSet { VkStructureType sType; const void * pNext; @@ -1863,6 +2806,7 @@ typedef struct VkWriteDescriptorSet { const VkDescriptorBufferInfo * pBufferInfo; const VkBufferView * pTexelBufferView; } VkWriteDescriptorSet; + typedef struct VkBufferCreateInfo { VkStructureType sType; const void * pNext; @@ -1873,6 +2817,7 @@ typedef struct VkBufferCreateInfo { uint32_t queueFamilyIndexCount; const uint32_t * pQueueFamilyIndices; } VkBufferCreateInfo; + typedef struct VkBufferViewCreateInfo { VkStructureType sType; const void * pNext; @@ -1882,17 +2827,20 @@ typedef struct VkBufferViewCreateInfo { VkDeviceSize offset; VkDeviceSize range; } VkBufferViewCreateInfo; + typedef struct VkImageSubresource { VkImageAspectFlags aspectMask; uint32_t mipLevel; uint32_t arrayLayer; } VkImageSubresource; + typedef struct VkImageSubresourceLayers { VkImageAspectFlags aspectMask; uint32_t mipLevel; uint32_t baseArrayLayer; uint32_t layerCount; } VkImageSubresourceLayers; + typedef struct VkImageSubresourceRange { VkImageAspectFlags aspectMask; uint32_t baseMipLevel; @@ -1900,12 +2848,14 @@ typedef struct VkImageSubresourceRange { uint32_t baseArrayLayer; uint32_t layerCount; } VkImageSubresourceRange; + typedef struct VkMemoryBarrier { VkStructureType sType; const void * pNext; VkAccessFlags srcAccessMask; VkAccessFlags dstAccessMask; } VkMemoryBarrier; + typedef struct VkBufferMemoryBarrier { VkStructureType sType; const void * pNext; @@ -1917,6 +2867,7 @@ typedef struct VkBufferMemoryBarrier { VkDeviceSize offset; VkDeviceSize size; } VkBufferMemoryBarrier; + typedef struct VkImageMemoryBarrier { VkStructureType sType; const void * pNext; @@ -1929,6 +2880,7 @@ typedef struct VkImageMemoryBarrier { VkImage image; VkImageSubresourceRange subresourceRange; } VkImageMemoryBarrier; + typedef struct VkImageCreateInfo { VkStructureType sType; const void * pNext; @@ -1946,6 +2898,7 @@ typedef struct VkImageCreateInfo { const uint32_t * pQueueFamilyIndices; VkImageLayout initialLayout; } VkImageCreateInfo; + typedef struct VkSubresourceLayout { VkDeviceSize offset; VkDeviceSize size; @@ -1953,6 +2906,7 @@ typedef struct VkSubresourceLayout { VkDeviceSize arrayPitch; VkDeviceSize depthPitch; } VkSubresourceLayout; + typedef struct VkImageViewCreateInfo { VkStructureType sType; const void * pNext; @@ -1963,11 +2917,13 @@ typedef struct VkImageViewCreateInfo { VkComponentMapping components; VkImageSubresourceRange subresourceRange; } VkImageViewCreateInfo; + typedef struct VkBufferCopy { VkDeviceSize srcOffset; VkDeviceSize dstOffset; VkDeviceSize size; } VkBufferCopy; + typedef struct VkSparseMemoryBind { VkDeviceSize resourceOffset; VkDeviceSize size; @@ -1975,6 +2931,7 @@ typedef struct VkSparseMemoryBind { VkDeviceSize memoryOffset; VkSparseMemoryBindFlags flags; } VkSparseMemoryBind; + typedef struct VkSparseImageMemoryBind { VkImageSubresource subresource; VkOffset3D offset; @@ -1983,21 +2940,25 @@ typedef struct VkSparseImageMemoryBind { VkDeviceSize memoryOffset; VkSparseMemoryBindFlags flags; } VkSparseImageMemoryBind; + typedef struct VkSparseBufferMemoryBindInfo { VkBuffer buffer; uint32_t bindCount; const VkSparseMemoryBind * pBinds; } VkSparseBufferMemoryBindInfo; + typedef struct VkSparseImageOpaqueMemoryBindInfo { VkImage image; uint32_t bindCount; const VkSparseMemoryBind * pBinds; } VkSparseImageOpaqueMemoryBindInfo; + typedef struct VkSparseImageMemoryBindInfo { VkImage image; uint32_t bindCount; const VkSparseImageMemoryBind * pBinds; } VkSparseImageMemoryBindInfo; + typedef struct VkBindSparseInfo { VkStructureType sType; const void * pNext; @@ -2012,6 +2973,7 @@ typedef struct VkBindSparseInfo { uint32_t signalSemaphoreCount; const VkSemaphore * pSignalSemaphores; } VkBindSparseInfo; + typedef struct VkImageCopy { VkImageSubresourceLayers srcSubresource; VkOffset3D srcOffset; @@ -2019,12 +2981,14 @@ typedef struct VkImageCopy { VkOffset3D dstOffset; VkExtent3D extent; } VkImageCopy; + typedef struct VkImageBlit { VkImageSubresourceLayers srcSubresource; VkOffset3D srcOffsets [2]; VkImageSubresourceLayers dstSubresource; VkOffset3D dstOffsets [2]; } VkImageBlit; + typedef struct VkBufferImageCopy { VkDeviceSize bufferOffset; uint32_t bufferRowLength; @@ -2033,6 +2997,7 @@ typedef struct VkBufferImageCopy { VkOffset3D imageOffset; VkExtent3D imageExtent; } VkBufferImageCopy; + typedef struct VkImageResolve { VkImageSubresourceLayers srcSubresource; VkOffset3D srcOffset; @@ -2040,6 +3005,7 @@ typedef struct VkImageResolve { VkOffset3D dstOffset; VkExtent3D extent; } VkImageResolve; + typedef struct VkShaderModuleCreateInfo { VkStructureType sType; const void * pNext; @@ -2047,6 +3013,7 @@ typedef struct VkShaderModuleCreateInfo { size_t codeSize; const uint32_t * pCode; } VkShaderModuleCreateInfo; + typedef struct VkDescriptorSetLayoutBinding { uint32_t binding; VkDescriptorType descriptorType; @@ -2054,6 +3021,7 @@ typedef struct VkDescriptorSetLayoutBinding { VkShaderStageFlags stageFlags; const VkSampler * pImmutableSamplers; } VkDescriptorSetLayoutBinding; + typedef struct VkDescriptorSetLayoutCreateInfo { VkStructureType sType; const void * pNext; @@ -2061,6 +3029,7 @@ typedef struct VkDescriptorSetLayoutCreateInfo { uint32_t bindingCount; const VkDescriptorSetLayoutBinding * pBindings; } VkDescriptorSetLayoutCreateInfo; + typedef struct VkDescriptorPoolCreateInfo { VkStructureType sType; const void * pNext; @@ -2069,6 +3038,7 @@ typedef struct VkDescriptorPoolCreateInfo { uint32_t poolSizeCount; const VkDescriptorPoolSize * pPoolSizes; } VkDescriptorPoolCreateInfo; + typedef struct VkPipelineShaderStageCreateInfo { VkStructureType sType; const void * pNext; @@ -2078,6 +3048,7 @@ typedef struct VkPipelineShaderStageCreateInfo { const char * pName; const VkSpecializationInfo * pSpecializationInfo; } VkPipelineShaderStageCreateInfo; + typedef struct VkComputePipelineCreateInfo { VkStructureType sType; const void * pNext; @@ -2087,6 +3058,7 @@ typedef struct VkComputePipelineCreateInfo { VkPipeline basePipelineHandle; int32_t basePipelineIndex; } VkComputePipelineCreateInfo; + typedef struct VkPipelineVertexInputStateCreateInfo { VkStructureType sType; const void * pNext; @@ -2096,6 +3068,7 @@ typedef struct VkPipelineVertexInputStateCreateInfo { uint32_t vertexAttributeDescriptionCount; const VkVertexInputAttributeDescription * pVertexAttributeDescriptions; } VkPipelineVertexInputStateCreateInfo; + typedef struct VkPipelineInputAssemblyStateCreateInfo { VkStructureType sType; const void * pNext; @@ -2103,12 +3076,14 @@ typedef struct VkPipelineInputAssemblyStateCreateInfo { VkPrimitiveTopology topology; VkBool32 primitiveRestartEnable; } VkPipelineInputAssemblyStateCreateInfo; + typedef struct VkPipelineTessellationStateCreateInfo { VkStructureType sType; const void * pNext; VkPipelineTessellationStateCreateFlags flags; uint32_t patchControlPoints; } VkPipelineTessellationStateCreateInfo; + typedef struct VkPipelineViewportStateCreateInfo { VkStructureType sType; const void * pNext; @@ -2118,6 +3093,7 @@ typedef struct VkPipelineViewportStateCreateInfo { uint32_t scissorCount; const VkRect2D * pScissors; } VkPipelineViewportStateCreateInfo; + typedef struct VkPipelineRasterizationStateCreateInfo { VkStructureType sType; const void * pNext; @@ -2133,6 +3109,7 @@ typedef struct VkPipelineRasterizationStateCreateInfo { float depthBiasSlopeFactor; float lineWidth; } VkPipelineRasterizationStateCreateInfo; + typedef struct VkPipelineMultisampleStateCreateInfo { VkStructureType sType; const void * pNext; @@ -2144,6 +3121,7 @@ typedef struct VkPipelineMultisampleStateCreateInfo { VkBool32 alphaToCoverageEnable; VkBool32 alphaToOneEnable; } VkPipelineMultisampleStateCreateInfo; + typedef struct VkPipelineColorBlendAttachmentState { VkBool32 blendEnable; VkBlendFactor srcColorBlendFactor; @@ -2154,6 +3132,7 @@ typedef struct VkPipelineColorBlendAttachmentState { VkBlendOp alphaBlendOp; VkColorComponentFlags colorWriteMask; } VkPipelineColorBlendAttachmentState; + typedef struct VkPipelineColorBlendStateCreateInfo { VkStructureType sType; const void * pNext; @@ -2164,6 +3143,7 @@ typedef struct VkPipelineColorBlendStateCreateInfo { const VkPipelineColorBlendAttachmentState * pAttachments; float blendConstants [4]; } VkPipelineColorBlendStateCreateInfo; + typedef struct VkPipelineDynamicStateCreateInfo { VkStructureType sType; const void * pNext; @@ -2171,6 +3151,7 @@ typedef struct VkPipelineDynamicStateCreateInfo { uint32_t dynamicStateCount; const VkDynamicState * pDynamicStates; } VkPipelineDynamicStateCreateInfo; + typedef struct VkPipelineDepthStencilStateCreateInfo { VkStructureType sType; const void * pNext; @@ -2185,6 +3166,7 @@ typedef struct VkPipelineDepthStencilStateCreateInfo { float minDepthBounds; float maxDepthBounds; } VkPipelineDepthStencilStateCreateInfo; + typedef struct VkGraphicsPipelineCreateInfo { VkStructureType sType; const void * pNext; @@ -2206,6 +3188,7 @@ typedef struct VkGraphicsPipelineCreateInfo { VkPipeline basePipelineHandle; int32_t basePipelineIndex; } VkGraphicsPipelineCreateInfo; + typedef struct VkPipelineCacheCreateInfo { VkStructureType sType; const void * pNext; @@ -2213,11 +3196,13 @@ typedef struct VkPipelineCacheCreateInfo { size_t initialDataSize; const void * pInitialData; } VkPipelineCacheCreateInfo; + typedef struct VkPushConstantRange { VkShaderStageFlags stageFlags; uint32_t offset; uint32_t size; } VkPushConstantRange; + typedef struct VkPipelineLayoutCreateInfo { VkStructureType sType; const void * pNext; @@ -2227,6 +3212,7 @@ typedef struct VkPipelineLayoutCreateInfo { uint32_t pushConstantRangeCount; const VkPushConstantRange * pPushConstantRanges; } VkPipelineLayoutCreateInfo; + typedef struct VkSamplerCreateInfo { VkStructureType sType; const void * pNext; @@ -2247,12 +3233,14 @@ typedef struct VkSamplerCreateInfo { VkBorderColor borderColor; VkBool32 unnormalizedCoordinates; } VkSamplerCreateInfo; + typedef struct VkCommandPoolCreateInfo { VkStructureType sType; const void * pNext; VkCommandPoolCreateFlags flags; uint32_t queueFamilyIndex; } VkCommandPoolCreateInfo; + typedef struct VkCommandBufferInheritanceInfo { VkStructureType sType; const void * pNext; @@ -2263,12 +3251,14 @@ typedef struct VkCommandBufferInheritanceInfo { VkQueryControlFlags queryFlags; VkQueryPipelineStatisticFlags pipelineStatistics; } VkCommandBufferInheritanceInfo; + typedef struct VkCommandBufferBeginInfo { VkStructureType sType; const void * pNext; VkCommandBufferUsageFlags flags; const VkCommandBufferInheritanceInfo * pInheritanceInfo; } VkCommandBufferBeginInfo; + typedef struct VkRenderPassBeginInfo { VkStructureType sType; const void * pNext; @@ -2278,11 +3268,13 @@ typedef struct VkRenderPassBeginInfo { uint32_t clearValueCount; const VkClearValue * pClearValues; } VkRenderPassBeginInfo; + typedef struct VkClearAttachment { VkImageAspectFlags aspectMask; uint32_t colorAttachment; VkClearValue clearValue; } VkClearAttachment; + typedef struct VkAttachmentDescription { VkAttachmentDescriptionFlags flags; VkFormat format; @@ -2294,6 +3286,7 @@ typedef struct VkAttachmentDescription { VkImageLayout initialLayout; VkImageLayout finalLayout; } VkAttachmentDescription; + typedef struct VkSubpassDescription { VkSubpassDescriptionFlags flags; VkPipelineBindPoint pipelineBindPoint; @@ -2306,6 +3299,7 @@ typedef struct VkSubpassDescription { uint32_t preserveAttachmentCount; const uint32_t * pPreserveAttachments; } VkSubpassDescription; + typedef struct VkSubpassDependency { uint32_t srcSubpass; uint32_t dstSubpass; @@ -2315,10 +3309,11 @@ typedef struct VkSubpassDependency { VkAccessFlags dstAccessMask; VkDependencyFlags dependencyFlags; } VkSubpassDependency; + typedef struct VkRenderPassCreateInfo { VkStructureType sType; const void * pNext; - VkRenderPassCreateFlags flags; + VkRenderPassCreateFlags flags; uint32_t attachmentCount; const VkAttachmentDescription * pAttachments; uint32_t subpassCount; @@ -2326,16 +3321,19 @@ typedef struct VkRenderPassCreateInfo { uint32_t dependencyCount; const VkSubpassDependency * pDependencies; } VkRenderPassCreateInfo; + typedef struct VkEventCreateInfo { VkStructureType sType; const void * pNext; VkEventCreateFlags flags; } VkEventCreateInfo; + typedef struct VkFenceCreateInfo { VkStructureType sType; const void * pNext; VkFenceCreateFlags flags; } VkFenceCreateInfo; + typedef struct VkPhysicalDeviceFeatures { VkBool32 robustBufferAccess; VkBool32 fullDrawIndexUint32; @@ -2393,13 +3391,15 @@ typedef struct VkPhysicalDeviceFeatures { VkBool32 variableMultisampleRate; VkBool32 inheritedQueries; } VkPhysicalDeviceFeatures; + typedef struct VkPhysicalDeviceSparseProperties { - VkBool32 residencyStandard2DBlockShape; - VkBool32 residencyStandard2DMultisampleBlockShape; - VkBool32 residencyStandard3DBlockShape; - VkBool32 residencyAlignedMipSize; - VkBool32 residencyNonResidentStrict; + VkBool32 residencyStandard2DBlockShape; + VkBool32 residencyStandard2DMultisampleBlockShape; + VkBool32 residencyStandard3DBlockShape; + VkBool32 residencyAlignedMipSize; + VkBool32 residencyNonResidentStrict; } VkPhysicalDeviceSparseProperties; + typedef struct VkPhysicalDeviceLimits { uint32_t maxImageDimension1D; uint32_t maxImageDimension2D; @@ -2456,28 +3456,28 @@ typedef struct VkPhysicalDeviceLimits { uint32_t maxComputeWorkGroupCount [3]; uint32_t maxComputeWorkGroupInvocations; uint32_t maxComputeWorkGroupSize [3]; - uint32_t subPixelPrecisionBits; - uint32_t subTexelPrecisionBits; - uint32_t mipmapPrecisionBits; + uint32_t subPixelPrecisionBits; + uint32_t subTexelPrecisionBits; + uint32_t mipmapPrecisionBits; uint32_t maxDrawIndexedIndexValue; uint32_t maxDrawIndirectCount; float maxSamplerLodBias; float maxSamplerAnisotropy; uint32_t maxViewports; uint32_t maxViewportDimensions [2]; - float viewportBoundsRange [2]; - uint32_t viewportSubPixelBits; - size_t minMemoryMapAlignment; - VkDeviceSize minTexelBufferOffsetAlignment; - VkDeviceSize minUniformBufferOffsetAlignment; - VkDeviceSize minStorageBufferOffsetAlignment; + float viewportBoundsRange [2]; + uint32_t viewportSubPixelBits; + size_t minMemoryMapAlignment; + VkDeviceSize minTexelBufferOffsetAlignment; + VkDeviceSize minUniformBufferOffsetAlignment; + VkDeviceSize minStorageBufferOffsetAlignment; int32_t minTexelOffset; uint32_t maxTexelOffset; int32_t minTexelGatherOffset; uint32_t maxTexelGatherOffset; float minInterpolationOffset; float maxInterpolationOffset; - uint32_t subPixelInterpolationOffsetBits; + uint32_t subPixelInterpolationOffsetBits; uint32_t maxFramebufferWidth; uint32_t maxFramebufferHeight; uint32_t maxFramebufferLayers; @@ -2492,27 +3492,29 @@ typedef struct VkPhysicalDeviceLimits { VkSampleCountFlags sampledImageStencilSampleCounts; VkSampleCountFlags storageImageSampleCounts; uint32_t maxSampleMaskWords; - VkBool32 timestampComputeAndGraphics; - float timestampPeriod; + VkBool32 timestampComputeAndGraphics; + float timestampPeriod; uint32_t maxClipDistances; uint32_t maxCullDistances; uint32_t maxCombinedClipAndCullDistances; uint32_t discreteQueuePriorities; - float pointSizeRange [2]; - float lineWidthRange [2]; - float pointSizeGranularity; - float lineWidthGranularity; - VkBool32 strictLines; - VkBool32 standardSampleLocations; - VkDeviceSize optimalBufferCopyOffsetAlignment; - VkDeviceSize optimalBufferCopyRowPitchAlignment; - VkDeviceSize nonCoherentAtomSize; + float pointSizeRange [2]; + float lineWidthRange [2]; + float pointSizeGranularity; + float lineWidthGranularity; + VkBool32 strictLines; + VkBool32 standardSampleLocations; + VkDeviceSize optimalBufferCopyOffsetAlignment; + VkDeviceSize optimalBufferCopyRowPitchAlignment; + VkDeviceSize nonCoherentAtomSize; } VkPhysicalDeviceLimits; + typedef struct VkSemaphoreCreateInfo { VkStructureType sType; const void * pNext; VkSemaphoreCreateFlags flags; } VkSemaphoreCreateInfo; + typedef struct VkQueryPoolCreateInfo { VkStructureType sType; const void * pNext; @@ -2521,17 +3523,19 @@ typedef struct VkQueryPoolCreateInfo { uint32_t queryCount; VkQueryPipelineStatisticFlags pipelineStatistics; } VkQueryPoolCreateInfo; + typedef struct VkFramebufferCreateInfo { VkStructureType sType; const void * pNext; VkFramebufferCreateFlags flags; - VkRenderPass renderPass; + VkRenderPass renderPass; uint32_t attachmentCount; const VkImageView * pAttachments; uint32_t width; uint32_t height; uint32_t layers; } VkFramebufferCreateInfo; + typedef struct VkSubmitInfo { VkStructureType sType; const void * pNext; @@ -2543,6 +3547,7 @@ typedef struct VkSubmitInfo { uint32_t signalSemaphoreCount; const VkSemaphore * pSignalSemaphores; } VkSubmitInfo; + typedef struct VkSurfaceCapabilitiesKHR { uint32_t minImageCount; uint32_t maxImageCount; @@ -2555,6 +3560,7 @@ typedef struct VkSurfaceCapabilitiesKHR { VkCompositeAlphaFlagsKHR supportedCompositeAlpha; VkImageUsageFlags supportedUsageFlags; } VkSurfaceCapabilitiesKHR; + typedef struct VkSwapchainCreateInfoKHR { VkStructureType sType; const void * pNext; @@ -2575,6 +3581,7 @@ typedef struct VkSwapchainCreateInfoKHR { VkBool32 clipped; VkSwapchainKHR oldSwapchain; } VkSwapchainCreateInfoKHR; + typedef struct VkDebugReportCallbackCreateInfoEXT { VkStructureType sType; const void * pNext; @@ -2582,21 +3589,37 @@ typedef struct VkDebugReportCallbackCreateInfoEXT { PFN_vkDebugReportCallbackEXT pfnCallback; void * pUserData; } VkDebugReportCallbackCreateInfoEXT; + +typedef struct VkPrivateDataSlotCreateInfo { + VkStructureType sType; + const void * pNext; + VkPrivateDataSlotCreateFlags flags; +} VkPrivateDataSlotCreateInfo; + +typedef struct VkPhysicalDevicePrivateDataFeatures { + VkStructureType sType; + void * pNext; + VkBool32 privateData; +} VkPhysicalDevicePrivateDataFeatures; + typedef struct VkPhysicalDeviceFeatures2 { VkStructureType sType; void * pNext; VkPhysicalDeviceFeatures features; } VkPhysicalDeviceFeatures2; + typedef struct VkFormatProperties2 { VkStructureType sType; void * pNext; VkFormatProperties formatProperties; } VkFormatProperties2; + typedef struct VkImageFormatProperties2 { VkStructureType sType; void * pNext; VkImageFormatProperties imageFormatProperties; } VkImageFormatProperties2; + typedef struct VkPhysicalDeviceImageFormatInfo2 { VkStructureType sType; const void * pNext; @@ -2606,16 +3629,19 @@ typedef struct VkPhysicalDeviceImageFormatInfo2 { VkImageUsageFlags usage; VkImageCreateFlags flags; } VkPhysicalDeviceImageFormatInfo2; + typedef struct VkQueueFamilyProperties2 { VkStructureType sType; void * pNext; - VkQueueFamilyProperties queueFamilyProperties; + VkQueueFamilyProperties queueFamilyProperties; } VkQueueFamilyProperties2; + typedef struct VkSparseImageFormatProperties2 { VkStructureType sType; void * pNext; - VkSparseImageFormatProperties properties; + VkSparseImageFormatProperties properties; } VkSparseImageFormatProperties2; + typedef struct VkPhysicalDeviceSparseImageFormatInfo2 { VkStructureType sType; const void * pNext; @@ -2625,23 +3651,28 @@ typedef struct VkPhysicalDeviceSparseImageFormatInfo2 { VkImageUsageFlags usage; VkImageTiling tiling; } VkPhysicalDeviceSparseImageFormatInfo2; + typedef struct VkPhysicalDeviceVariablePointersFeatures { VkStructureType sType; void * pNext; VkBool32 variablePointersStorageBuffer; VkBool32 variablePointers; } VkPhysicalDeviceVariablePointersFeatures; -typedef struct VkPhysicalDeviceVariablePointerFeatures VkPhysicalDeviceVariablePointerFeatures; + +typedef struct VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointerFeatures; + typedef struct VkExternalMemoryProperties { VkExternalMemoryFeatureFlags externalMemoryFeatures; VkExternalMemoryHandleTypeFlags exportFromImportedHandleTypes; VkExternalMemoryHandleTypeFlags compatibleHandleTypes; } VkExternalMemoryProperties; + typedef struct VkExternalImageFormatProperties { VkStructureType sType; void * pNext; VkExternalMemoryProperties externalMemoryProperties; } VkExternalImageFormatProperties; + typedef struct VkPhysicalDeviceExternalBufferInfo { VkStructureType sType; const void * pNext; @@ -2649,35 +3680,41 @@ typedef struct VkPhysicalDeviceExternalBufferInfo { VkBufferUsageFlags usage; VkExternalMemoryHandleTypeFlagBits handleType; } VkPhysicalDeviceExternalBufferInfo; + typedef struct VkExternalBufferProperties { VkStructureType sType; void * pNext; VkExternalMemoryProperties externalMemoryProperties; } VkExternalBufferProperties; + typedef struct VkPhysicalDeviceIDProperties { VkStructureType sType; void * pNext; - uint8_t deviceUUID [ VK_UUID_SIZE ]; - uint8_t driverUUID [ VK_UUID_SIZE ]; - uint8_t deviceLUID [ VK_LUID_SIZE ]; - uint32_t deviceNodeMask; - VkBool32 deviceLUIDValid; + uint8_t deviceUUID [ VK_UUID_SIZE ]; + uint8_t driverUUID [ VK_UUID_SIZE ]; + uint8_t deviceLUID [ VK_LUID_SIZE ]; + uint32_t deviceNodeMask; + VkBool32 deviceLUIDValid; } VkPhysicalDeviceIDProperties; + typedef struct VkExternalMemoryImageCreateInfo { VkStructureType sType; const void * pNext; VkExternalMemoryHandleTypeFlags handleTypes; } VkExternalMemoryImageCreateInfo; + typedef struct VkExternalMemoryBufferCreateInfo { VkStructureType sType; const void * pNext; VkExternalMemoryHandleTypeFlags handleTypes; } VkExternalMemoryBufferCreateInfo; + typedef struct VkExportMemoryAllocateInfo { VkStructureType sType; const void * pNext; VkExternalMemoryHandleTypeFlags handleTypes; } VkExportMemoryAllocateInfo; + typedef struct VkExternalSemaphoreProperties { VkStructureType sType; void * pNext; @@ -2685,11 +3722,13 @@ typedef struct VkExternalSemaphoreProperties { VkExternalSemaphoreHandleTypeFlags compatibleHandleTypes; VkExternalSemaphoreFeatureFlags externalSemaphoreFeatures; } VkExternalSemaphoreProperties; + typedef struct VkExportSemaphoreCreateInfo { VkStructureType sType; const void * pNext; VkExternalSemaphoreHandleTypeFlags handleTypes; } VkExportSemaphoreCreateInfo; + typedef struct VkExternalFenceProperties { VkStructureType sType; void * pNext; @@ -2697,11 +3736,13 @@ typedef struct VkExternalFenceProperties { VkExternalFenceHandleTypeFlags compatibleHandleTypes; VkExternalFenceFeatureFlags externalFenceFeatures; } VkExternalFenceProperties; + typedef struct VkExportFenceCreateInfo { VkStructureType sType; const void * pNext; VkExternalFenceHandleTypeFlags handleTypes; } VkExportFenceCreateInfo; + typedef struct VkPhysicalDeviceMultiviewFeatures { VkStructureType sType; void * pNext; @@ -2709,6 +3750,7 @@ typedef struct VkPhysicalDeviceMultiviewFeatures { VkBool32 multiviewGeometryShader; VkBool32 multiviewTessellationShader; } VkPhysicalDeviceMultiviewFeatures; + typedef struct VkPhysicalDeviceGroupProperties { VkStructureType sType; void * pNext; @@ -2716,12 +3758,14 @@ typedef struct VkPhysicalDeviceGroupProperties { VkPhysicalDevice physicalDevices [ VK_MAX_DEVICE_GROUP_SIZE ]; VkBool32 subsetAllocation; } VkPhysicalDeviceGroupProperties; + typedef struct VkMemoryAllocateFlagsInfo { VkStructureType sType; const void * pNext; VkMemoryAllocateFlags flags; uint32_t deviceMask; } VkMemoryAllocateFlagsInfo; + typedef struct VkBindBufferMemoryInfo { VkStructureType sType; const void * pNext; @@ -2729,6 +3773,7 @@ typedef struct VkBindBufferMemoryInfo { VkDeviceMemory memory; VkDeviceSize memoryOffset; } VkBindBufferMemoryInfo; + typedef struct VkBindImageMemoryInfo { VkStructureType sType; const void * pNext; @@ -2736,17 +3781,20 @@ typedef struct VkBindImageMemoryInfo { VkDeviceMemory memory; VkDeviceSize memoryOffset; } VkBindImageMemoryInfo; + typedef struct VkDeviceGroupPresentCapabilitiesKHR { VkStructureType sType; - const void * pNext; + void * pNext; uint32_t presentMask [ VK_MAX_DEVICE_GROUP_SIZE ]; VkDeviceGroupPresentModeFlagsKHR modes; } VkDeviceGroupPresentCapabilitiesKHR; + typedef struct VkDeviceGroupSwapchainCreateInfoKHR { VkStructureType sType; const void * pNext; VkDeviceGroupPresentModeFlagsKHR modes; } VkDeviceGroupSwapchainCreateInfoKHR; + typedef struct VkDescriptorUpdateTemplateCreateInfo { VkStructureType sType; const void * pNext; @@ -2759,17 +3807,20 @@ typedef struct VkDescriptorUpdateTemplateCreateInfo { VkPipelineLayout pipelineLayout; uint32_t set; } VkDescriptorUpdateTemplateCreateInfo; + typedef struct VkInputAttachmentAspectReference { uint32_t subpass; uint32_t inputAttachmentIndex; VkImageAspectFlags aspectMask; } VkInputAttachmentAspectReference; + typedef struct VkRenderPassInputAttachmentAspectCreateInfo { VkStructureType sType; const void * pNext; uint32_t aspectReferenceCount; const VkInputAttachmentAspectReference * pAspectReferences; } VkRenderPassInputAttachmentAspectCreateInfo; + typedef struct VkPhysicalDevice16BitStorageFeatures { VkStructureType sType; void * pNext; @@ -2778,36 +3829,60 @@ typedef struct VkPhysicalDevice16BitStorageFeatures { VkBool32 storagePushConstant16; VkBool32 storageInputOutput16; } VkPhysicalDevice16BitStorageFeatures; + typedef struct VkPhysicalDeviceSubgroupProperties { VkStructureType sType; void * pNext; - uint32_t subgroupSize; + uint32_t subgroupSize; VkShaderStageFlags supportedStages; VkSubgroupFeatureFlags supportedOperations; VkBool32 quadOperationsInAllStages; } VkPhysicalDeviceSubgroupProperties; + +typedef struct VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures { + VkStructureType sType; + void * pNext; + VkBool32 shaderSubgroupExtendedTypes; +} VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures; + +typedef struct VkDeviceBufferMemoryRequirements { + VkStructureType sType; + const void * pNext; + const VkBufferCreateInfo * pCreateInfo; +} VkDeviceBufferMemoryRequirements; + +typedef struct VkDeviceImageMemoryRequirements { + VkStructureType sType; + const void * pNext; + const VkImageCreateInfo * pCreateInfo; + VkImageAspectFlagBits planeAspect; +} VkDeviceImageMemoryRequirements; + typedef struct VkMemoryRequirements2 { VkStructureType sType; void * pNext; VkMemoryRequirements memoryRequirements; } VkMemoryRequirements2; -typedef struct VkMemoryRequirements2KHR VkMemoryRequirements2KHR; + typedef struct VkSparseImageMemoryRequirements2 { VkStructureType sType; void * pNext; VkSparseImageMemoryRequirements memoryRequirements; } VkSparseImageMemoryRequirements2; + typedef struct VkMemoryDedicatedRequirements { VkStructureType sType; void * pNext; VkBool32 prefersDedicatedAllocation; VkBool32 requiresDedicatedAllocation; } VkMemoryDedicatedRequirements; + typedef struct VkImageViewUsageCreateInfo { VkStructureType sType; const void * pNext; VkImageUsageFlags usage; } VkImageViewUsageCreateInfo; + typedef struct VkSamplerYcbcrConversionCreateInfo { VkStructureType sType; const void * pNext; @@ -2820,26 +3895,31 @@ typedef struct VkSamplerYcbcrConversionCreateInfo { VkFilter chromaFilter; VkBool32 forceExplicitReconstruction; } VkSamplerYcbcrConversionCreateInfo; + typedef struct VkPhysicalDeviceSamplerYcbcrConversionFeatures { VkStructureType sType; void * pNext; VkBool32 samplerYcbcrConversion; } VkPhysicalDeviceSamplerYcbcrConversionFeatures; + typedef struct VkProtectedSubmitInfo { VkStructureType sType; const void * pNext; VkBool32 protectedSubmit; } VkProtectedSubmitInfo; + typedef struct VkPhysicalDeviceProtectedMemoryFeatures { VkStructureType sType; void * pNext; VkBool32 protectedMemory; } VkPhysicalDeviceProtectedMemoryFeatures; + typedef struct VkPhysicalDeviceProtectedMemoryProperties { VkStructureType sType; void * pNext; VkBool32 protectedNoFault; } VkPhysicalDeviceProtectedMemoryProperties; + typedef struct VkDeviceQueueInfo2 { VkStructureType sType; const void * pNext; @@ -2847,23 +3927,887 @@ typedef struct VkDeviceQueueInfo2 { uint32_t queueFamilyIndex; uint32_t queueIndex; } VkDeviceQueueInfo2; + +typedef struct VkPhysicalDeviceSamplerFilterMinmaxProperties { + VkStructureType sType; + void * pNext; + VkBool32 filterMinmaxSingleComponentFormats; + VkBool32 filterMinmaxImageComponentMapping; +} VkPhysicalDeviceSamplerFilterMinmaxProperties; + +typedef struct VkPhysicalDeviceInlineUniformBlockFeatures { + VkStructureType sType; + void * pNext; + VkBool32 inlineUniformBlock; + VkBool32 descriptorBindingInlineUniformBlockUpdateAfterBind; +} VkPhysicalDeviceInlineUniformBlockFeatures; + typedef struct VkPhysicalDeviceMaintenance3Properties { VkStructureType sType; void * pNext; uint32_t maxPerSetDescriptors; VkDeviceSize maxMemoryAllocationSize; } VkPhysicalDeviceMaintenance3Properties; + +typedef struct VkPhysicalDeviceMaintenance4Features { + VkStructureType sType; + void * pNext; + VkBool32 maintenance4; +} VkPhysicalDeviceMaintenance4Features; + +typedef struct VkPhysicalDeviceMaintenance4Properties { + VkStructureType sType; + void * pNext; + VkDeviceSize maxBufferSize; +} VkPhysicalDeviceMaintenance4Properties; + typedef struct VkDescriptorSetLayoutSupport { VkStructureType sType; void * pNext; VkBool32 supported; } VkDescriptorSetLayoutSupport; + typedef struct VkPhysicalDeviceShaderDrawParametersFeatures { VkStructureType sType; void * pNext; VkBool32 shaderDrawParameters; } VkPhysicalDeviceShaderDrawParametersFeatures; -typedef struct VkPhysicalDeviceShaderDrawParameterFeatures VkPhysicalDeviceShaderDrawParameterFeatures; + +typedef struct VkPhysicalDeviceShaderDrawParametersFeatures VkPhysicalDeviceShaderDrawParameterFeatures; + +typedef struct VkPhysicalDeviceShaderFloat16Int8Features { + VkStructureType sType; + void * pNext; + VkBool32 shaderFloat16; + VkBool32 shaderInt8; +} VkPhysicalDeviceShaderFloat16Int8Features; + +typedef struct VkPhysicalDeviceFloatControlsProperties { + VkStructureType sType; + void * pNext; + VkShaderFloatControlsIndependence denormBehaviorIndependence; + VkShaderFloatControlsIndependence roundingModeIndependence; + VkBool32 shaderSignedZeroInfNanPreserveFloat16; + VkBool32 shaderSignedZeroInfNanPreserveFloat32; + VkBool32 shaderSignedZeroInfNanPreserveFloat64; + VkBool32 shaderDenormPreserveFloat16; + VkBool32 shaderDenormPreserveFloat32; + VkBool32 shaderDenormPreserveFloat64; + VkBool32 shaderDenormFlushToZeroFloat16; + VkBool32 shaderDenormFlushToZeroFloat32; + VkBool32 shaderDenormFlushToZeroFloat64; + VkBool32 shaderRoundingModeRTEFloat16; + VkBool32 shaderRoundingModeRTEFloat32; + VkBool32 shaderRoundingModeRTEFloat64; + VkBool32 shaderRoundingModeRTZFloat16; + VkBool32 shaderRoundingModeRTZFloat32; + VkBool32 shaderRoundingModeRTZFloat64; +} VkPhysicalDeviceFloatControlsProperties; + +typedef struct VkPhysicalDeviceHostQueryResetFeatures { + VkStructureType sType; + void * pNext; + VkBool32 hostQueryReset; +} VkPhysicalDeviceHostQueryResetFeatures; + +typedef struct VkPhysicalDeviceDescriptorIndexingFeatures { + VkStructureType sType; + void * pNext; + VkBool32 shaderInputAttachmentArrayDynamicIndexing; + VkBool32 shaderUniformTexelBufferArrayDynamicIndexing; + VkBool32 shaderStorageTexelBufferArrayDynamicIndexing; + VkBool32 shaderUniformBufferArrayNonUniformIndexing; + VkBool32 shaderSampledImageArrayNonUniformIndexing; + VkBool32 shaderStorageBufferArrayNonUniformIndexing; + VkBool32 shaderStorageImageArrayNonUniformIndexing; + VkBool32 shaderInputAttachmentArrayNonUniformIndexing; + VkBool32 shaderUniformTexelBufferArrayNonUniformIndexing; + VkBool32 shaderStorageTexelBufferArrayNonUniformIndexing; + VkBool32 descriptorBindingUniformBufferUpdateAfterBind; + VkBool32 descriptorBindingSampledImageUpdateAfterBind; + VkBool32 descriptorBindingStorageImageUpdateAfterBind; + VkBool32 descriptorBindingStorageBufferUpdateAfterBind; + VkBool32 descriptorBindingUniformTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingStorageTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingUpdateUnusedWhilePending; + VkBool32 descriptorBindingPartiallyBound; + VkBool32 descriptorBindingVariableDescriptorCount; + VkBool32 runtimeDescriptorArray; +} VkPhysicalDeviceDescriptorIndexingFeatures; + +typedef struct VkPhysicalDeviceDescriptorIndexingProperties { + VkStructureType sType; + void * pNext; + uint32_t maxUpdateAfterBindDescriptorsInAllPools; + VkBool32 shaderUniformBufferArrayNonUniformIndexingNative; + VkBool32 shaderSampledImageArrayNonUniformIndexingNative; + VkBool32 shaderStorageBufferArrayNonUniformIndexingNative; + VkBool32 shaderStorageImageArrayNonUniformIndexingNative; + VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative; + VkBool32 robustBufferAccessUpdateAfterBind; + VkBool32 quadDivergentImplicitLod; + uint32_t maxPerStageDescriptorUpdateAfterBindSamplers; + uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages; + uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments; + uint32_t maxPerStageUpdateAfterBindResources; + uint32_t maxDescriptorSetUpdateAfterBindSamplers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindSampledImages; + uint32_t maxDescriptorSetUpdateAfterBindStorageImages; + uint32_t maxDescriptorSetUpdateAfterBindInputAttachments; +} VkPhysicalDeviceDescriptorIndexingProperties; + +typedef struct VkDescriptorSetLayoutBindingFlagsCreateInfo { + VkStructureType sType; + const void * pNext; + uint32_t bindingCount; + const VkDescriptorBindingFlags * pBindingFlags; +} VkDescriptorSetLayoutBindingFlagsCreateInfo; + +typedef struct VkAttachmentDescription2 { + VkStructureType sType; + const void * pNext; + VkAttachmentDescriptionFlags flags; + VkFormat format; + VkSampleCountFlagBits samples; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkAttachmentLoadOp stencilLoadOp; + VkAttachmentStoreOp stencilStoreOp; + VkImageLayout initialLayout; + VkImageLayout finalLayout; +} VkAttachmentDescription2; + +typedef struct VkAttachmentReference2 { + VkStructureType sType; + const void * pNext; + uint32_t attachment; + VkImageLayout layout; + VkImageAspectFlags aspectMask; +} VkAttachmentReference2; + +typedef struct VkSubpassDescription2 { + VkStructureType sType; + const void * pNext; + VkSubpassDescriptionFlags flags; + VkPipelineBindPoint pipelineBindPoint; + uint32_t viewMask; + uint32_t inputAttachmentCount; + const VkAttachmentReference2 * pInputAttachments; + uint32_t colorAttachmentCount; + const VkAttachmentReference2 * pColorAttachments; + const VkAttachmentReference2 * pResolveAttachments; + const VkAttachmentReference2 * pDepthStencilAttachment; + uint32_t preserveAttachmentCount; + const uint32_t * pPreserveAttachments; +} VkSubpassDescription2; + +typedef struct VkSubpassDependency2 { + VkStructureType sType; + const void * pNext; + uint32_t srcSubpass; + uint32_t dstSubpass; + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkDependencyFlags dependencyFlags; + int32_t viewOffset; +} VkSubpassDependency2; + +typedef struct VkRenderPassCreateInfo2 { + VkStructureType sType; + const void * pNext; + VkRenderPassCreateFlags flags; + uint32_t attachmentCount; + const VkAttachmentDescription2 * pAttachments; + uint32_t subpassCount; + const VkSubpassDescription2 * pSubpasses; + uint32_t dependencyCount; + const VkSubpassDependency2 * pDependencies; + uint32_t correlatedViewMaskCount; + const uint32_t * pCorrelatedViewMasks; +} VkRenderPassCreateInfo2; + +typedef struct VkPhysicalDeviceTimelineSemaphoreFeatures { + VkStructureType sType; + void * pNext; + VkBool32 timelineSemaphore; +} VkPhysicalDeviceTimelineSemaphoreFeatures; + +typedef struct VkSemaphoreWaitInfo { + VkStructureType sType; + const void * pNext; + VkSemaphoreWaitFlags flags; + uint32_t semaphoreCount; + const VkSemaphore * pSemaphores; + const uint64_t * pValues; +} VkSemaphoreWaitInfo; + +typedef struct VkPhysicalDevice8BitStorageFeatures { + VkStructureType sType; + void * pNext; + VkBool32 storageBuffer8BitAccess; + VkBool32 uniformAndStorageBuffer8BitAccess; + VkBool32 storagePushConstant8; +} VkPhysicalDevice8BitStorageFeatures; + +typedef struct VkPhysicalDeviceVulkanMemoryModelFeatures { + VkStructureType sType; + void * pNext; + VkBool32 vulkanMemoryModel; + VkBool32 vulkanMemoryModelDeviceScope; + VkBool32 vulkanMemoryModelAvailabilityVisibilityChains; +} VkPhysicalDeviceVulkanMemoryModelFeatures; + +typedef struct VkPhysicalDeviceShaderAtomicInt64Features { + VkStructureType sType; + void * pNext; + VkBool32 shaderBufferInt64Atomics; + VkBool32 shaderSharedInt64Atomics; +} VkPhysicalDeviceShaderAtomicInt64Features; + +typedef struct VkPhysicalDeviceDepthStencilResolveProperties { + VkStructureType sType; + void * pNext; + VkResolveModeFlags supportedDepthResolveModes; + VkResolveModeFlags supportedStencilResolveModes; + VkBool32 independentResolveNone; + VkBool32 independentResolve; +} VkPhysicalDeviceDepthStencilResolveProperties; + +typedef struct VkSubpassDescriptionDepthStencilResolve { + VkStructureType sType; + const void * pNext; + VkResolveModeFlagBits depthResolveMode; + VkResolveModeFlagBits stencilResolveMode; + const VkAttachmentReference2 * pDepthStencilResolveAttachment; +} VkSubpassDescriptionDepthStencilResolve; + +typedef struct VkImageStencilUsageCreateInfo { + VkStructureType sType; + const void * pNext; + VkImageUsageFlags stencilUsage; +} VkImageStencilUsageCreateInfo; + +typedef struct VkPhysicalDeviceScalarBlockLayoutFeatures { + VkStructureType sType; + void * pNext; + VkBool32 scalarBlockLayout; +} VkPhysicalDeviceScalarBlockLayoutFeatures; + +typedef struct VkPhysicalDeviceUniformBufferStandardLayoutFeatures { + VkStructureType sType; + void * pNext; + VkBool32 uniformBufferStandardLayout; +} VkPhysicalDeviceUniformBufferStandardLayoutFeatures; + +typedef struct VkPhysicalDeviceBufferDeviceAddressFeatures { + VkStructureType sType; + void * pNext; + VkBool32 bufferDeviceAddress; + VkBool32 bufferDeviceAddressCaptureReplay; + VkBool32 bufferDeviceAddressMultiDevice; +} VkPhysicalDeviceBufferDeviceAddressFeatures; + +typedef struct VkPhysicalDeviceImagelessFramebufferFeatures { + VkStructureType sType; + void * pNext; + VkBool32 imagelessFramebuffer; +} VkPhysicalDeviceImagelessFramebufferFeatures; + +typedef struct VkFramebufferAttachmentImageInfo { + VkStructureType sType; + const void * pNext; + VkImageCreateFlags flags; + VkImageUsageFlags usage; + uint32_t width; + uint32_t height; + uint32_t layerCount; + uint32_t viewFormatCount; + const VkFormat * pViewFormats; +} VkFramebufferAttachmentImageInfo; + +typedef struct VkPhysicalDeviceTextureCompressionASTCHDRFeatures { + VkStructureType sType; + void * pNext; + VkBool32 textureCompressionASTC_HDR; +} VkPhysicalDeviceTextureCompressionASTCHDRFeatures; + +typedef struct VkPipelineCreationFeedback { + VkPipelineCreationFeedbackFlags flags; + uint64_t duration; +} VkPipelineCreationFeedback; + +typedef struct VkPipelineCreationFeedbackCreateInfo { + VkStructureType sType; + const void * pNext; + VkPipelineCreationFeedback * pPipelineCreationFeedback; + uint32_t pipelineStageCreationFeedbackCount; + VkPipelineCreationFeedback * pPipelineStageCreationFeedbacks; +} VkPipelineCreationFeedbackCreateInfo; + +typedef struct VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures { + VkStructureType sType; + void * pNext; + VkBool32 separateDepthStencilLayouts; +} VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures; + +typedef struct VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures { + VkStructureType sType; + void * pNext; + VkBool32 shaderDemoteToHelperInvocation; +} VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures; + +typedef struct VkPhysicalDeviceTexelBufferAlignmentProperties { + VkStructureType sType; + void * pNext; + VkDeviceSize storageTexelBufferOffsetAlignmentBytes; + VkBool32 storageTexelBufferOffsetSingleTexelAlignment; + VkDeviceSize uniformTexelBufferOffsetAlignmentBytes; + VkBool32 uniformTexelBufferOffsetSingleTexelAlignment; +} VkPhysicalDeviceTexelBufferAlignmentProperties; + +typedef struct VkPhysicalDeviceSubgroupSizeControlFeatures { + VkStructureType sType; + void * pNext; + VkBool32 subgroupSizeControl; + VkBool32 computeFullSubgroups; +} VkPhysicalDeviceSubgroupSizeControlFeatures; + +typedef struct VkPhysicalDeviceSubgroupSizeControlProperties { + VkStructureType sType; + void * pNext; + uint32_t minSubgroupSize; + uint32_t maxSubgroupSize; + uint32_t maxComputeWorkgroupSubgroups; + VkShaderStageFlags requiredSubgroupSizeStages; +} VkPhysicalDeviceSubgroupSizeControlProperties; + +typedef struct VkPhysicalDevicePipelineCreationCacheControlFeatures { + VkStructureType sType; + void * pNext; + VkBool32 pipelineCreationCacheControl; +} VkPhysicalDevicePipelineCreationCacheControlFeatures; + +typedef struct VkPhysicalDeviceVulkan11Features { + VkStructureType sType; + void * pNext; + VkBool32 storageBuffer16BitAccess; + VkBool32 uniformAndStorageBuffer16BitAccess; + VkBool32 storagePushConstant16; + VkBool32 storageInputOutput16; + VkBool32 multiview; + VkBool32 multiviewGeometryShader; + VkBool32 multiviewTessellationShader; + VkBool32 variablePointersStorageBuffer; + VkBool32 variablePointers; + VkBool32 protectedMemory; + VkBool32 samplerYcbcrConversion; + VkBool32 shaderDrawParameters; +} VkPhysicalDeviceVulkan11Features; + +typedef struct VkPhysicalDeviceVulkan11Properties { + VkStructureType sType; + void * pNext; + uint8_t deviceUUID [ VK_UUID_SIZE ]; + uint8_t driverUUID [ VK_UUID_SIZE ]; + uint8_t deviceLUID [ VK_LUID_SIZE ]; + uint32_t deviceNodeMask; + VkBool32 deviceLUIDValid; + uint32_t subgroupSize; + VkShaderStageFlags subgroupSupportedStages; + VkSubgroupFeatureFlags subgroupSupportedOperations; + VkBool32 subgroupQuadOperationsInAllStages; + VkPointClippingBehavior pointClippingBehavior; + uint32_t maxMultiviewViewCount; + uint32_t maxMultiviewInstanceIndex; + VkBool32 protectedNoFault; + uint32_t maxPerSetDescriptors; + VkDeviceSize maxMemoryAllocationSize; +} VkPhysicalDeviceVulkan11Properties; + +typedef struct VkPhysicalDeviceVulkan12Features { + VkStructureType sType; + void * pNext; + VkBool32 samplerMirrorClampToEdge; + VkBool32 drawIndirectCount; + VkBool32 storageBuffer8BitAccess; + VkBool32 uniformAndStorageBuffer8BitAccess; + VkBool32 storagePushConstant8; + VkBool32 shaderBufferInt64Atomics; + VkBool32 shaderSharedInt64Atomics; + VkBool32 shaderFloat16; + VkBool32 shaderInt8; + VkBool32 descriptorIndexing; + VkBool32 shaderInputAttachmentArrayDynamicIndexing; + VkBool32 shaderUniformTexelBufferArrayDynamicIndexing; + VkBool32 shaderStorageTexelBufferArrayDynamicIndexing; + VkBool32 shaderUniformBufferArrayNonUniformIndexing; + VkBool32 shaderSampledImageArrayNonUniformIndexing; + VkBool32 shaderStorageBufferArrayNonUniformIndexing; + VkBool32 shaderStorageImageArrayNonUniformIndexing; + VkBool32 shaderInputAttachmentArrayNonUniformIndexing; + VkBool32 shaderUniformTexelBufferArrayNonUniformIndexing; + VkBool32 shaderStorageTexelBufferArrayNonUniformIndexing; + VkBool32 descriptorBindingUniformBufferUpdateAfterBind; + VkBool32 descriptorBindingSampledImageUpdateAfterBind; + VkBool32 descriptorBindingStorageImageUpdateAfterBind; + VkBool32 descriptorBindingStorageBufferUpdateAfterBind; + VkBool32 descriptorBindingUniformTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingStorageTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingUpdateUnusedWhilePending; + VkBool32 descriptorBindingPartiallyBound; + VkBool32 descriptorBindingVariableDescriptorCount; + VkBool32 runtimeDescriptorArray; + VkBool32 samplerFilterMinmax; + VkBool32 scalarBlockLayout; + VkBool32 imagelessFramebuffer; + VkBool32 uniformBufferStandardLayout; + VkBool32 shaderSubgroupExtendedTypes; + VkBool32 separateDepthStencilLayouts; + VkBool32 hostQueryReset; + VkBool32 timelineSemaphore; + VkBool32 bufferDeviceAddress; + VkBool32 bufferDeviceAddressCaptureReplay; + VkBool32 bufferDeviceAddressMultiDevice; + VkBool32 vulkanMemoryModel; + VkBool32 vulkanMemoryModelDeviceScope; + VkBool32 vulkanMemoryModelAvailabilityVisibilityChains; + VkBool32 shaderOutputViewportIndex; + VkBool32 shaderOutputLayer; + VkBool32 subgroupBroadcastDynamicId; +} VkPhysicalDeviceVulkan12Features; + +typedef struct VkPhysicalDeviceVulkan12Properties { + VkStructureType sType; + void * pNext; + VkDriverId driverID; + char driverName [ VK_MAX_DRIVER_NAME_SIZE ]; + char driverInfo [ VK_MAX_DRIVER_INFO_SIZE ]; + VkConformanceVersion conformanceVersion; + VkShaderFloatControlsIndependence denormBehaviorIndependence; + VkShaderFloatControlsIndependence roundingModeIndependence; + VkBool32 shaderSignedZeroInfNanPreserveFloat16; + VkBool32 shaderSignedZeroInfNanPreserveFloat32; + VkBool32 shaderSignedZeroInfNanPreserveFloat64; + VkBool32 shaderDenormPreserveFloat16; + VkBool32 shaderDenormPreserveFloat32; + VkBool32 shaderDenormPreserveFloat64; + VkBool32 shaderDenormFlushToZeroFloat16; + VkBool32 shaderDenormFlushToZeroFloat32; + VkBool32 shaderDenormFlushToZeroFloat64; + VkBool32 shaderRoundingModeRTEFloat16; + VkBool32 shaderRoundingModeRTEFloat32; + VkBool32 shaderRoundingModeRTEFloat64; + VkBool32 shaderRoundingModeRTZFloat16; + VkBool32 shaderRoundingModeRTZFloat32; + VkBool32 shaderRoundingModeRTZFloat64; + uint32_t maxUpdateAfterBindDescriptorsInAllPools; + VkBool32 shaderUniformBufferArrayNonUniformIndexingNative; + VkBool32 shaderSampledImageArrayNonUniformIndexingNative; + VkBool32 shaderStorageBufferArrayNonUniformIndexingNative; + VkBool32 shaderStorageImageArrayNonUniformIndexingNative; + VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative; + VkBool32 robustBufferAccessUpdateAfterBind; + VkBool32 quadDivergentImplicitLod; + uint32_t maxPerStageDescriptorUpdateAfterBindSamplers; + uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages; + uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments; + uint32_t maxPerStageUpdateAfterBindResources; + uint32_t maxDescriptorSetUpdateAfterBindSamplers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindSampledImages; + uint32_t maxDescriptorSetUpdateAfterBindStorageImages; + uint32_t maxDescriptorSetUpdateAfterBindInputAttachments; + VkResolveModeFlags supportedDepthResolveModes; + VkResolveModeFlags supportedStencilResolveModes; + VkBool32 independentResolveNone; + VkBool32 independentResolve; + VkBool32 filterMinmaxSingleComponentFormats; + VkBool32 filterMinmaxImageComponentMapping; + uint64_t maxTimelineSemaphoreValueDifference; + VkSampleCountFlags framebufferIntegerColorSampleCounts; +} VkPhysicalDeviceVulkan12Properties; + +typedef struct VkPhysicalDeviceVulkan13Features { + VkStructureType sType; + void * pNext; + VkBool32 robustImageAccess; + VkBool32 inlineUniformBlock; + VkBool32 descriptorBindingInlineUniformBlockUpdateAfterBind; + VkBool32 pipelineCreationCacheControl; + VkBool32 privateData; + VkBool32 shaderDemoteToHelperInvocation; + VkBool32 shaderTerminateInvocation; + VkBool32 subgroupSizeControl; + VkBool32 computeFullSubgroups; + VkBool32 synchronization2; + VkBool32 textureCompressionASTC_HDR; + VkBool32 shaderZeroInitializeWorkgroupMemory; + VkBool32 dynamicRendering; + VkBool32 shaderIntegerDotProduct; + VkBool32 maintenance4; +} VkPhysicalDeviceVulkan13Features; + +typedef struct VkPhysicalDeviceVulkan13Properties { + VkStructureType sType; + void * pNext; + uint32_t minSubgroupSize; + uint32_t maxSubgroupSize; + uint32_t maxComputeWorkgroupSubgroups; + VkShaderStageFlags requiredSubgroupSizeStages; + uint32_t maxInlineUniformBlockSize; + uint32_t maxPerStageDescriptorInlineUniformBlocks; + uint32_t maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks; + uint32_t maxDescriptorSetInlineUniformBlocks; + uint32_t maxDescriptorSetUpdateAfterBindInlineUniformBlocks; + uint32_t maxInlineUniformTotalSize; + VkBool32 integerDotProduct8BitUnsignedAccelerated; + VkBool32 integerDotProduct8BitSignedAccelerated; + VkBool32 integerDotProduct8BitMixedSignednessAccelerated; + VkBool32 integerDotProduct4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedSignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProduct16BitUnsignedAccelerated; + VkBool32 integerDotProduct16BitSignedAccelerated; + VkBool32 integerDotProduct16BitMixedSignednessAccelerated; + VkBool32 integerDotProduct32BitUnsignedAccelerated; + VkBool32 integerDotProduct32BitSignedAccelerated; + VkBool32 integerDotProduct32BitMixedSignednessAccelerated; + VkBool32 integerDotProduct64BitUnsignedAccelerated; + VkBool32 integerDotProduct64BitSignedAccelerated; + VkBool32 integerDotProduct64BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated; + VkDeviceSize storageTexelBufferOffsetAlignmentBytes; + VkBool32 storageTexelBufferOffsetSingleTexelAlignment; + VkDeviceSize uniformTexelBufferOffsetAlignmentBytes; + VkBool32 uniformTexelBufferOffsetSingleTexelAlignment; + VkDeviceSize maxBufferSize; +} VkPhysicalDeviceVulkan13Properties; + +typedef struct VkPhysicalDeviceToolProperties { + VkStructureType sType; + void * pNext; + char name [ VK_MAX_EXTENSION_NAME_SIZE ]; + char version [ VK_MAX_EXTENSION_NAME_SIZE ]; + VkToolPurposeFlags purposes; + char description [ VK_MAX_DESCRIPTION_SIZE ]; + char layer [ VK_MAX_EXTENSION_NAME_SIZE ]; +} VkPhysicalDeviceToolProperties; + +typedef struct VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures { + VkStructureType sType; + void * pNext; + VkBool32 shaderZeroInitializeWorkgroupMemory; +} VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures; + +typedef struct VkPhysicalDeviceImageRobustnessFeatures { + VkStructureType sType; + void * pNext; + VkBool32 robustImageAccess; +} VkPhysicalDeviceImageRobustnessFeatures; + +typedef struct VkBufferCopy2 { + VkStructureType sType; + const void * pNext; + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VkDeviceSize size; +} VkBufferCopy2; + +typedef struct VkImageCopy2 { + VkStructureType sType; + const void * pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageCopy2; + +typedef struct VkImageBlit2 { + VkStructureType sType; + const void * pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffsets [2]; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffsets [2]; +} VkImageBlit2; + +typedef struct VkBufferImageCopy2 { + VkStructureType sType; + const void * pNext; + VkDeviceSize bufferOffset; + uint32_t bufferRowLength; + uint32_t bufferImageHeight; + VkImageSubresourceLayers imageSubresource; + VkOffset3D imageOffset; + VkExtent3D imageExtent; +} VkBufferImageCopy2; + +typedef struct VkImageResolve2 { + VkStructureType sType; + const void * pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageResolve2; + +typedef struct VkCopyBufferInfo2 { + VkStructureType sType; + const void * pNext; + VkBuffer srcBuffer; + VkBuffer dstBuffer; + uint32_t regionCount; + const VkBufferCopy2 * pRegions; +} VkCopyBufferInfo2; + +typedef struct VkCopyImageInfo2 { + VkStructureType sType; + const void * pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageCopy2 * pRegions; +} VkCopyImageInfo2; + +typedef struct VkBlitImageInfo2 { + VkStructureType sType; + const void * pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageBlit2 * pRegions; + VkFilter filter; +} VkBlitImageInfo2; + +typedef struct VkCopyBufferToImageInfo2 { + VkStructureType sType; + const void * pNext; + VkBuffer srcBuffer; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkBufferImageCopy2 * pRegions; +} VkCopyBufferToImageInfo2; + +typedef struct VkCopyImageToBufferInfo2 { + VkStructureType sType; + const void * pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkBuffer dstBuffer; + uint32_t regionCount; + const VkBufferImageCopy2 * pRegions; +} VkCopyImageToBufferInfo2; + +typedef struct VkResolveImageInfo2 { + VkStructureType sType; + const void * pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageResolve2 * pRegions; +} VkResolveImageInfo2; + +typedef struct VkPhysicalDeviceShaderTerminateInvocationFeatures { + VkStructureType sType; + void * pNext; + VkBool32 shaderTerminateInvocation; +} VkPhysicalDeviceShaderTerminateInvocationFeatures; + +typedef struct VkMemoryBarrier2 { + VkStructureType sType; + const void * pNext; + VkPipelineStageFlags2 srcStageMask; + VkAccessFlags2 srcAccessMask; + VkPipelineStageFlags2 dstStageMask; + VkAccessFlags2 dstAccessMask; +} VkMemoryBarrier2; + +typedef struct VkImageMemoryBarrier2 { + VkStructureType sType; + const void * pNext; + VkPipelineStageFlags2 srcStageMask; + VkAccessFlags2 srcAccessMask; + VkPipelineStageFlags2 dstStageMask; + VkAccessFlags2 dstAccessMask; + VkImageLayout oldLayout; + VkImageLayout newLayout; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkImage image; + VkImageSubresourceRange subresourceRange; +} VkImageMemoryBarrier2; + +typedef struct VkBufferMemoryBarrier2 { + VkStructureType sType; + const void * pNext; + VkPipelineStageFlags2 srcStageMask; + VkAccessFlags2 srcAccessMask; + VkPipelineStageFlags2 dstStageMask; + VkAccessFlags2 dstAccessMask; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize size; +} VkBufferMemoryBarrier2; + +typedef struct VkDependencyInfo { + VkStructureType sType; + const void * pNext; + VkDependencyFlags dependencyFlags; + uint32_t memoryBarrierCount; + const VkMemoryBarrier2 * pMemoryBarriers; + uint32_t bufferMemoryBarrierCount; + const VkBufferMemoryBarrier2 * pBufferMemoryBarriers; + uint32_t imageMemoryBarrierCount; + const VkImageMemoryBarrier2 * pImageMemoryBarriers; +} VkDependencyInfo; + +typedef struct VkSemaphoreSubmitInfo { + VkStructureType sType; + const void * pNext; + VkSemaphore semaphore; + uint64_t value; + VkPipelineStageFlags2 stageMask; + uint32_t deviceIndex; +} VkSemaphoreSubmitInfo; + +typedef struct VkSubmitInfo2 { + VkStructureType sType; + const void * pNext; + VkSubmitFlags flags; + uint32_t waitSemaphoreInfoCount; + const VkSemaphoreSubmitInfo * pWaitSemaphoreInfos; + uint32_t commandBufferInfoCount; + const VkCommandBufferSubmitInfo * pCommandBufferInfos; + uint32_t signalSemaphoreInfoCount; + const VkSemaphoreSubmitInfo * pSignalSemaphoreInfos; +} VkSubmitInfo2; + +typedef struct VkPhysicalDeviceSynchronization2Features { + VkStructureType sType; + void * pNext; + VkBool32 synchronization2; +} VkPhysicalDeviceSynchronization2Features; + +typedef struct VkPhysicalDeviceShaderIntegerDotProductFeatures { + VkStructureType sType; + void * pNext; + VkBool32 shaderIntegerDotProduct; +} VkPhysicalDeviceShaderIntegerDotProductFeatures; + +typedef struct VkPhysicalDeviceShaderIntegerDotProductProperties { + VkStructureType sType; + void * pNext; + VkBool32 integerDotProduct8BitUnsignedAccelerated; + VkBool32 integerDotProduct8BitSignedAccelerated; + VkBool32 integerDotProduct8BitMixedSignednessAccelerated; + VkBool32 integerDotProduct4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedSignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProduct16BitUnsignedAccelerated; + VkBool32 integerDotProduct16BitSignedAccelerated; + VkBool32 integerDotProduct16BitMixedSignednessAccelerated; + VkBool32 integerDotProduct32BitUnsignedAccelerated; + VkBool32 integerDotProduct32BitSignedAccelerated; + VkBool32 integerDotProduct32BitMixedSignednessAccelerated; + VkBool32 integerDotProduct64BitUnsignedAccelerated; + VkBool32 integerDotProduct64BitSignedAccelerated; + VkBool32 integerDotProduct64BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated; +} VkPhysicalDeviceShaderIntegerDotProductProperties; + +typedef struct VkFormatProperties3 { + VkStructureType sType; + void * pNext; + VkFormatFeatureFlags2 linearTilingFeatures; + VkFormatFeatureFlags2 optimalTilingFeatures; + VkFormatFeatureFlags2 bufferFeatures; +} VkFormatProperties3; + +typedef struct VkRenderingInfo { + VkStructureType sType; + const void * pNext; + VkRenderingFlags flags; + VkRect2D renderArea; + uint32_t layerCount; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkRenderingAttachmentInfo * pColorAttachments; + const VkRenderingAttachmentInfo * pDepthAttachment; + const VkRenderingAttachmentInfo * pStencilAttachment; +} VkRenderingInfo; + +typedef struct VkPhysicalDeviceDynamicRenderingFeatures { + VkStructureType sType; + void * pNext; + VkBool32 dynamicRendering; +} VkPhysicalDeviceDynamicRenderingFeatures; + +typedef struct VkCommandBufferInheritanceRenderingInfo { + VkStructureType sType; + const void * pNext; + VkRenderingFlags flags; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkFormat * pColorAttachmentFormats; + VkFormat depthAttachmentFormat; + VkFormat stencilAttachmentFormat; + VkSampleCountFlagBits rasterizationSamples; +} VkCommandBufferInheritanceRenderingInfo; + typedef struct VkPhysicalDeviceProperties { uint32_t apiVersion; uint32_t driverVersion; @@ -2875,6 +4819,7 @@ typedef struct VkPhysicalDeviceProperties { VkPhysicalDeviceLimits limits; VkPhysicalDeviceSparseProperties sparseProperties; } VkPhysicalDeviceProperties; + typedef struct VkDeviceCreateInfo { VkStructureType sType; const void * pNext; @@ -2887,218 +4832,285 @@ typedef struct VkDeviceCreateInfo { const char * const* ppEnabledExtensionNames; const VkPhysicalDeviceFeatures * pEnabledFeatures; } VkDeviceCreateInfo; + typedef struct VkPhysicalDeviceMemoryProperties { uint32_t memoryTypeCount; VkMemoryType memoryTypes [ VK_MAX_MEMORY_TYPES ]; uint32_t memoryHeapCount; VkMemoryHeap memoryHeaps [ VK_MAX_MEMORY_HEAPS ]; } VkPhysicalDeviceMemoryProperties; + typedef struct VkPhysicalDeviceProperties2 { VkStructureType sType; void * pNext; VkPhysicalDeviceProperties properties; } VkPhysicalDeviceProperties2; + typedef struct VkPhysicalDeviceMemoryProperties2 { VkStructureType sType; void * pNext; VkPhysicalDeviceMemoryProperties memoryProperties; } VkPhysicalDeviceMemoryProperties2; +typedef struct VkFramebufferAttachmentsCreateInfo { + VkStructureType sType; + const void * pNext; + uint32_t attachmentImageInfoCount; + const VkFramebufferAttachmentImageInfo * pAttachmentImageInfos; +} VkFramebufferAttachmentsCreateInfo; + + #define VK_VERSION_1_0 1 GLAD_API_CALL int GLAD_VK_VERSION_1_0; #define VK_VERSION_1_1 1 GLAD_API_CALL int GLAD_VK_VERSION_1_1; +#define VK_VERSION_1_2 1 +GLAD_API_CALL int GLAD_VK_VERSION_1_2; +#define VK_VERSION_1_3 1 +GLAD_API_CALL int GLAD_VK_VERSION_1_3; #define VK_EXT_debug_report 1 GLAD_API_CALL int GLAD_VK_EXT_debug_report; +#define VK_KHR_portability_enumeration 1 +GLAD_API_CALL int GLAD_VK_KHR_portability_enumeration; #define VK_KHR_surface 1 GLAD_API_CALL int GLAD_VK_KHR_surface; #define VK_KHR_swapchain 1 GLAD_API_CALL int GLAD_VK_KHR_swapchain; -typedef VkResult (GLAD_API_PTR *PFN_vkAcquireNextImage2KHR)(VkDevice device, const VkAcquireNextImageInfoKHR * pAcquireInfo, uint32_t * pImageIndex); -typedef VkResult (GLAD_API_PTR *PFN_vkAcquireNextImageKHR)(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t * pImageIndex); -typedef VkResult (GLAD_API_PTR *PFN_vkAllocateCommandBuffers)(VkDevice device, const VkCommandBufferAllocateInfo * pAllocateInfo, VkCommandBuffer * pCommandBuffers); -typedef VkResult (GLAD_API_PTR *PFN_vkAllocateDescriptorSets)(VkDevice device, const VkDescriptorSetAllocateInfo * pAllocateInfo, VkDescriptorSet * pDescriptorSets); -typedef VkResult (GLAD_API_PTR *PFN_vkAllocateMemory)(VkDevice device, const VkMemoryAllocateInfo * pAllocateInfo, const VkAllocationCallbacks * pAllocator, VkDeviceMemory * pMemory); -typedef VkResult (GLAD_API_PTR *PFN_vkBeginCommandBuffer)(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo * pBeginInfo); -typedef VkResult (GLAD_API_PTR *PFN_vkBindBufferMemory)(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset); -typedef VkResult (GLAD_API_PTR *PFN_vkBindBufferMemory2)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo * pBindInfos); -typedef VkResult (GLAD_API_PTR *PFN_vkBindImageMemory)(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset); -typedef VkResult (GLAD_API_PTR *PFN_vkBindImageMemory2)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo * pBindInfos); -typedef void (GLAD_API_PTR *PFN_vkCmdBeginQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); -typedef void (GLAD_API_PTR *PFN_vkCmdBeginRenderPass)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo * pRenderPassBegin, VkSubpassContents contents); -typedef void (GLAD_API_PTR *PFN_vkCmdBindDescriptorSets)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet * pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t * pDynamicOffsets); -typedef void (GLAD_API_PTR *PFN_vkCmdBindIndexBuffer)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); -typedef void (GLAD_API_PTR *PFN_vkCmdBindPipeline)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); -typedef void (GLAD_API_PTR *PFN_vkCmdBindVertexBuffers)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer * pBuffers, const VkDeviceSize * pOffsets); -typedef void (GLAD_API_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit * pRegions, VkFilter filter); -typedef void (GLAD_API_PTR *PFN_vkCmdClearAttachments)(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment * pAttachments, uint32_t rectCount, const VkClearRect * pRects); -typedef void (GLAD_API_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue * pColor, uint32_t rangeCount, const VkImageSubresourceRange * pRanges); -typedef void (GLAD_API_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue * pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange * pRanges); -typedef void (GLAD_API_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy * pRegions); -typedef void (GLAD_API_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy * pRegions); -typedef void (GLAD_API_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy * pRegions); -typedef void (GLAD_API_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy * pRegions); -typedef void (GLAD_API_PTR *PFN_vkCmdCopyQueryPoolResults)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); -typedef void (GLAD_API_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); -typedef void (GLAD_API_PTR *PFN_vkCmdDispatchBase)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); -typedef void (GLAD_API_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset); -typedef void (GLAD_API_PTR *PFN_vkCmdDraw)(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance); -typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); -typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); -typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); -typedef void (GLAD_API_PTR *PFN_vkCmdEndQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query); -typedef void (GLAD_API_PTR *PFN_vkCmdEndRenderPass)(VkCommandBuffer commandBuffer); -typedef void (GLAD_API_PTR *PFN_vkCmdExecuteCommands)(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer * pCommandBuffers); -typedef void (GLAD_API_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); -typedef void (GLAD_API_PTR *PFN_vkCmdNextSubpass)(VkCommandBuffer commandBuffer, VkSubpassContents contents); -typedef void (GLAD_API_PTR *PFN_vkCmdPipelineBarrier)(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier * pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier * pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier * pImageMemoryBarriers); -typedef void (GLAD_API_PTR *PFN_vkCmdPushConstants)(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void * pValues); -typedef void (GLAD_API_PTR *PFN_vkCmdResetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); -typedef void (GLAD_API_PTR *PFN_vkCmdResetQueryPool)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); -typedef void (GLAD_API_PTR *PFN_vkCmdResolveImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve * pRegions); -typedef void (GLAD_API_PTR *PFN_vkCmdSetBlendConstants)(VkCommandBuffer commandBuffer, const float blendConstants [4]); -typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthBias)(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor); -typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthBounds)(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds); -typedef void (GLAD_API_PTR *PFN_vkCmdSetDeviceMask)(VkCommandBuffer commandBuffer, uint32_t deviceMask); -typedef void (GLAD_API_PTR *PFN_vkCmdSetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); -typedef void (GLAD_API_PTR *PFN_vkCmdSetLineWidth)(VkCommandBuffer commandBuffer, float lineWidth); -typedef void (GLAD_API_PTR *PFN_vkCmdSetScissor)(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D * pScissors); -typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilCompareMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask); -typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilReference)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference); -typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilWriteMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask); -typedef void (GLAD_API_PTR *PFN_vkCmdSetViewport)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport * pViewports); -typedef void (GLAD_API_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void * pData); -typedef void (GLAD_API_PTR *PFN_vkCmdWaitEvents)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent * pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier * pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier * pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier * pImageMemoryBarriers); -typedef void (GLAD_API_PTR *PFN_vkCmdWriteTimestamp)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateBuffer)(VkDevice device, const VkBufferCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkBuffer * pBuffer); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateBufferView)(VkDevice device, const VkBufferViewCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkBufferView * pView); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateCommandPool)(VkDevice device, const VkCommandPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkCommandPool * pCommandPool); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateComputePipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo * pCreateInfos, const VkAllocationCallbacks * pAllocator, VkPipeline * pPipelines); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDebugReportCallbackEXT * pCallback); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateDescriptorPool)(VkDevice device, const VkDescriptorPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDescriptorPool * pDescriptorPool); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateDescriptorSetLayout)(VkDevice device, const VkDescriptorSetLayoutCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDescriptorSetLayout * pSetLayout); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateDescriptorUpdateTemplate)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDescriptorUpdateTemplate * pDescriptorUpdateTemplate); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateDevice)(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDevice * pDevice); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateEvent)(VkDevice device, const VkEventCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkEvent * pEvent); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateFence)(VkDevice device, const VkFenceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkFence * pFence); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateFramebuffer)(VkDevice device, const VkFramebufferCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkFramebuffer * pFramebuffer); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo * pCreateInfos, const VkAllocationCallbacks * pAllocator, VkPipeline * pPipelines); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateImage)(VkDevice device, const VkImageCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkImage * pImage); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateImageView)(VkDevice device, const VkImageViewCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkImageView * pView); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateInstance)(const VkInstanceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkInstance * pInstance); -typedef VkResult (GLAD_API_PTR *PFN_vkCreatePipelineCache)(VkDevice device, const VkPipelineCacheCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkPipelineCache * pPipelineCache); -typedef VkResult (GLAD_API_PTR *PFN_vkCreatePipelineLayout)(VkDevice device, const VkPipelineLayoutCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkPipelineLayout * pPipelineLayout); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateQueryPool)(VkDevice device, const VkQueryPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkQueryPool * pQueryPool); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateRenderPass)(VkDevice device, const VkRenderPassCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkRenderPass * pRenderPass); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateSampler)(VkDevice device, const VkSamplerCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSampler * pSampler); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateSamplerYcbcrConversion)(VkDevice device, const VkSamplerYcbcrConversionCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSamplerYcbcrConversion * pYcbcrConversion); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateSemaphore)(VkDevice device, const VkSemaphoreCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSemaphore * pSemaphore); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateShaderModule)(VkDevice device, const VkShaderModuleCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkShaderModule * pShaderModule); -typedef VkResult (GLAD_API_PTR *PFN_vkCreateSwapchainKHR)(VkDevice device, const VkSwapchainCreateInfoKHR * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSwapchainKHR * pSwapchain); -typedef void (GLAD_API_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char * pLayerPrefix, const char * pMessage); -typedef void (GLAD_API_PTR *PFN_vkDestroyBuffer)(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyBufferView)(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyCommandPool)(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyDescriptorSetLayout)(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyDescriptorUpdateTemplate)(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyDevice)(VkDevice device, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyEvent)(VkDevice device, VkEvent event, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyFramebuffer)(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyImage)(VkDevice device, VkImage image, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyImageView)(VkDevice device, VkImageView imageView, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyInstance)(VkInstance instance, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyPipeline)(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyPipelineCache)(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyPipelineLayout)(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyQueryPool)(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyRenderPass)(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroySampler)(VkDevice device, VkSampler sampler, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroySamplerYcbcrConversion)(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroySemaphore)(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroyShaderModule)(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroySurfaceKHR)(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkDestroySwapchainKHR)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks * pAllocator); -typedef VkResult (GLAD_API_PTR *PFN_vkDeviceWaitIdle)(VkDevice device); -typedef VkResult (GLAD_API_PTR *PFN_vkEndCommandBuffer)(VkCommandBuffer commandBuffer); -typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateDeviceExtensionProperties)(VkPhysicalDevice physicalDevice, const char * pLayerName, uint32_t * pPropertyCount, VkExtensionProperties * pProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateDeviceLayerProperties)(VkPhysicalDevice physicalDevice, uint32_t * pPropertyCount, VkLayerProperties * pProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateInstanceExtensionProperties)(const char * pLayerName, uint32_t * pPropertyCount, VkExtensionProperties * pProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateInstanceLayerProperties)(uint32_t * pPropertyCount, VkLayerProperties * pProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateInstanceVersion)(uint32_t * pApiVersion); -typedef VkResult (GLAD_API_PTR *PFN_vkEnumeratePhysicalDeviceGroups)(VkInstance instance, uint32_t * pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties * pPhysicalDeviceGroupProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t * pPhysicalDeviceCount, VkPhysicalDevice * pPhysicalDevices); -typedef VkResult (GLAD_API_PTR *PFN_vkFlushMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange * pMemoryRanges); -typedef void (GLAD_API_PTR *PFN_vkFreeCommandBuffers)(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer * pCommandBuffers); -typedef VkResult (GLAD_API_PTR *PFN_vkFreeDescriptorSets)(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet * pDescriptorSets); -typedef void (GLAD_API_PTR *PFN_vkFreeMemory)(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks * pAllocator); -typedef void (GLAD_API_PTR *PFN_vkGetBufferMemoryRequirements)(VkDevice device, VkBuffer buffer, VkMemoryRequirements * pMemoryRequirements); -typedef void (GLAD_API_PTR *PFN_vkGetBufferMemoryRequirements2)(VkDevice device, const VkBufferMemoryRequirementsInfo2 * pInfo, VkMemoryRequirements2 * pMemoryRequirements); -typedef void (GLAD_API_PTR *PFN_vkGetDescriptorSetLayoutSupport)(VkDevice device, const VkDescriptorSetLayoutCreateInfo * pCreateInfo, VkDescriptorSetLayoutSupport * pSupport); -typedef void (GLAD_API_PTR *PFN_vkGetDeviceGroupPeerMemoryFeatures)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags * pPeerMemoryFeatures); -typedef VkResult (GLAD_API_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHR)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR * pDeviceGroupPresentCapabilities); -typedef VkResult (GLAD_API_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHR)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR * pModes); -typedef void (GLAD_API_PTR *PFN_vkGetDeviceMemoryCommitment)(VkDevice device, VkDeviceMemory memory, VkDeviceSize * pCommittedMemoryInBytes); -typedef PFN_vkVoidFunction (GLAD_API_PTR *PFN_vkGetDeviceProcAddr)(VkDevice device, const char * pName); -typedef void (GLAD_API_PTR *PFN_vkGetDeviceQueue)(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue * pQueue); -typedef void (GLAD_API_PTR *PFN_vkGetDeviceQueue2)(VkDevice device, const VkDeviceQueueInfo2 * pQueueInfo, VkQueue * pQueue); -typedef VkResult (GLAD_API_PTR *PFN_vkGetEventStatus)(VkDevice device, VkEvent event); -typedef VkResult (GLAD_API_PTR *PFN_vkGetFenceStatus)(VkDevice device, VkFence fence); -typedef void (GLAD_API_PTR *PFN_vkGetImageMemoryRequirements)(VkDevice device, VkImage image, VkMemoryRequirements * pMemoryRequirements); -typedef void (GLAD_API_PTR *PFN_vkGetImageMemoryRequirements2)(VkDevice device, const VkImageMemoryRequirementsInfo2 * pInfo, VkMemoryRequirements2 * pMemoryRequirements); -typedef void (GLAD_API_PTR *PFN_vkGetImageSparseMemoryRequirements)(VkDevice device, VkImage image, uint32_t * pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements * pSparseMemoryRequirements); -typedef void (GLAD_API_PTR *PFN_vkGetImageSparseMemoryRequirements2)(VkDevice device, const VkImageSparseMemoryRequirementsInfo2 * pInfo, uint32_t * pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2 * pSparseMemoryRequirements); -typedef void (GLAD_API_PTR *PFN_vkGetImageSubresourceLayout)(VkDevice device, VkImage image, const VkImageSubresource * pSubresource, VkSubresourceLayout * pLayout); -typedef PFN_vkVoidFunction (GLAD_API_PTR *PFN_vkGetInstanceProcAddr)(VkInstance instance, const char * pName); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceExternalBufferProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo * pExternalBufferInfo, VkExternalBufferProperties * pExternalBufferProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceExternalFenceProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo * pExternalFenceInfo, VkExternalFenceProperties * pExternalFenceProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceExternalSemaphoreProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo * pExternalSemaphoreInfo, VkExternalSemaphoreProperties * pExternalSemaphoreProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFeatures)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures * pFeatures); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFeatures2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2 * pFeatures); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties * pFormatProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFormatProperties2)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2 * pFormatProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties * pImageFormatProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 * pImageFormatInfo, VkImageFormatProperties2 * pImageFormatProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties * pMemoryProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2 * pMemoryProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pRectCount, VkRect2D * pRects); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties * pProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceProperties2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 * pProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysicalDevice physicalDevice, uint32_t * pQueueFamilyPropertyCount, VkQueueFamilyProperties * pQueueFamilyProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2)(VkPhysicalDevice physicalDevice, uint32_t * pQueueFamilyPropertyCount, VkQueueFamilyProperties2 * pQueueFamilyProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t * pPropertyCount, VkSparseImageFormatProperties * pProperties); -typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2 * pFormatInfo, uint32_t * pPropertyCount, VkSparseImageFormatProperties2 * pProperties); -typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR * pSurfaceCapabilities); -typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pSurfaceFormatCount, VkSurfaceFormatKHR * pSurfaceFormats); -typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pPresentModeCount, VkPresentModeKHR * pPresentModes); -typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32 * pSupported); -typedef VkResult (GLAD_API_PTR *PFN_vkGetPipelineCacheData)(VkDevice device, VkPipelineCache pipelineCache, size_t * pDataSize, void * pData); -typedef VkResult (GLAD_API_PTR *PFN_vkGetQueryPoolResults)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void * pData, VkDeviceSize stride, VkQueryResultFlags flags); -typedef void (GLAD_API_PTR *PFN_vkGetRenderAreaGranularity)(VkDevice device, VkRenderPass renderPass, VkExtent2D * pGranularity); -typedef VkResult (GLAD_API_PTR *PFN_vkGetSwapchainImagesKHR)(VkDevice device, VkSwapchainKHR swapchain, uint32_t * pSwapchainImageCount, VkImage * pSwapchainImages); -typedef VkResult (GLAD_API_PTR *PFN_vkInvalidateMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange * pMemoryRanges); -typedef VkResult (GLAD_API_PTR *PFN_vkMapMemory)(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void ** ppData); -typedef VkResult (GLAD_API_PTR *PFN_vkMergePipelineCaches)(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache * pSrcCaches); -typedef VkResult (GLAD_API_PTR *PFN_vkQueueBindSparse)(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo * pBindInfo, VkFence fence); -typedef VkResult (GLAD_API_PTR *PFN_vkQueuePresentKHR)(VkQueue queue, const VkPresentInfoKHR * pPresentInfo); -typedef VkResult (GLAD_API_PTR *PFN_vkQueueSubmit)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo * pSubmits, VkFence fence); -typedef VkResult (GLAD_API_PTR *PFN_vkQueueWaitIdle)(VkQueue queue); -typedef VkResult (GLAD_API_PTR *PFN_vkResetCommandBuffer)(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags); -typedef VkResult (GLAD_API_PTR *PFN_vkResetCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags); -typedef VkResult (GLAD_API_PTR *PFN_vkResetDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags); -typedef VkResult (GLAD_API_PTR *PFN_vkResetEvent)(VkDevice device, VkEvent event); -typedef VkResult (GLAD_API_PTR *PFN_vkResetFences)(VkDevice device, uint32_t fenceCount, const VkFence * pFences); -typedef VkResult (GLAD_API_PTR *PFN_vkSetEvent)(VkDevice device, VkEvent event); -typedef void (GLAD_API_PTR *PFN_vkTrimCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); -typedef void (GLAD_API_PTR *PFN_vkUnmapMemory)(VkDevice device, VkDeviceMemory memory); -typedef void (GLAD_API_PTR *PFN_vkUpdateDescriptorSetWithTemplate)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void * pData); -typedef void (GLAD_API_PTR *PFN_vkUpdateDescriptorSets)(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet * pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet * pDescriptorCopies); -typedef VkResult (GLAD_API_PTR *PFN_vkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence * pFences, VkBool32 waitAll, uint64_t timeout); +typedef VkResult (GLAD_API_PTR *PFN_vkAcquireNextImage2KHR)(VkDevice device, const VkAcquireNextImageInfoKHR * pAcquireInfo, uint32_t * pImageIndex); +typedef VkResult (GLAD_API_PTR *PFN_vkAcquireNextImageKHR)(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t * pImageIndex); +typedef VkResult (GLAD_API_PTR *PFN_vkAllocateCommandBuffers)(VkDevice device, const VkCommandBufferAllocateInfo * pAllocateInfo, VkCommandBuffer * pCommandBuffers); +typedef VkResult (GLAD_API_PTR *PFN_vkAllocateDescriptorSets)(VkDevice device, const VkDescriptorSetAllocateInfo * pAllocateInfo, VkDescriptorSet * pDescriptorSets); +typedef VkResult (GLAD_API_PTR *PFN_vkAllocateMemory)(VkDevice device, const VkMemoryAllocateInfo * pAllocateInfo, const VkAllocationCallbacks * pAllocator, VkDeviceMemory * pMemory); +typedef VkResult (GLAD_API_PTR *PFN_vkBeginCommandBuffer)(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo * pBeginInfo); +typedef VkResult (GLAD_API_PTR *PFN_vkBindBufferMemory)(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset); +typedef VkResult (GLAD_API_PTR *PFN_vkBindBufferMemory2)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo * pBindInfos); +typedef VkResult (GLAD_API_PTR *PFN_vkBindImageMemory)(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset); +typedef VkResult (GLAD_API_PTR *PFN_vkBindImageMemory2)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo * pBindInfos); +typedef void (GLAD_API_PTR *PFN_vkCmdBeginQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); +typedef void (GLAD_API_PTR *PFN_vkCmdBeginRenderPass)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo * pRenderPassBegin, VkSubpassContents contents); +typedef void (GLAD_API_PTR *PFN_vkCmdBeginRenderPass2)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo * pRenderPassBegin, const VkSubpassBeginInfo * pSubpassBeginInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdBeginRendering)(VkCommandBuffer commandBuffer, const VkRenderingInfo * pRenderingInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdBindDescriptorSets)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet * pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t * pDynamicOffsets); +typedef void (GLAD_API_PTR *PFN_vkCmdBindIndexBuffer)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); +typedef void (GLAD_API_PTR *PFN_vkCmdBindPipeline)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); +typedef void (GLAD_API_PTR *PFN_vkCmdBindVertexBuffers)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer * pBuffers, const VkDeviceSize * pOffsets); +typedef void (GLAD_API_PTR *PFN_vkCmdBindVertexBuffers2)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer * pBuffers, const VkDeviceSize * pOffsets, const VkDeviceSize * pSizes, const VkDeviceSize * pStrides); +typedef void (GLAD_API_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit * pRegions, VkFilter filter); +typedef void (GLAD_API_PTR *PFN_vkCmdBlitImage2)(VkCommandBuffer commandBuffer, const VkBlitImageInfo2 * pBlitImageInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdClearAttachments)(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment * pAttachments, uint32_t rectCount, const VkClearRect * pRects); +typedef void (GLAD_API_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue * pColor, uint32_t rangeCount, const VkImageSubresourceRange * pRanges); +typedef void (GLAD_API_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue * pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange * pRanges); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyBuffer2)(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2 * pCopyBufferInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyBufferToImage2)(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2 * pCopyBufferToImageInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyImage2)(VkCommandBuffer commandBuffer, const VkCopyImageInfo2 * pCopyImageInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyImageToBuffer2)(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2 * pCopyImageToBufferInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdCopyQueryPoolResults)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); +typedef void (GLAD_API_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); +typedef void (GLAD_API_PTR *PFN_vkCmdDispatchBase)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); +typedef void (GLAD_API_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset); +typedef void (GLAD_API_PTR *PFN_vkCmdDraw)(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance); +typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); +typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndexedIndirectCount)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (GLAD_API_PTR *PFN_vkCmdDrawIndirectCount)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef void (GLAD_API_PTR *PFN_vkCmdEndQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query); +typedef void (GLAD_API_PTR *PFN_vkCmdEndRenderPass)(VkCommandBuffer commandBuffer); +typedef void (GLAD_API_PTR *PFN_vkCmdEndRenderPass2)(VkCommandBuffer commandBuffer, const VkSubpassEndInfo * pSubpassEndInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdEndRendering)(VkCommandBuffer commandBuffer); +typedef void (GLAD_API_PTR *PFN_vkCmdExecuteCommands)(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer * pCommandBuffers); +typedef void (GLAD_API_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); +typedef void (GLAD_API_PTR *PFN_vkCmdNextSubpass)(VkCommandBuffer commandBuffer, VkSubpassContents contents); +typedef void (GLAD_API_PTR *PFN_vkCmdNextSubpass2)(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo * pSubpassBeginInfo, const VkSubpassEndInfo * pSubpassEndInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdPipelineBarrier)(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier * pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier * pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier * pImageMemoryBarriers); +typedef void (GLAD_API_PTR *PFN_vkCmdPipelineBarrier2)(VkCommandBuffer commandBuffer, const VkDependencyInfo * pDependencyInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdPushConstants)(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void * pValues); +typedef void (GLAD_API_PTR *PFN_vkCmdResetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); +typedef void (GLAD_API_PTR *PFN_vkCmdResetEvent2)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask); +typedef void (GLAD_API_PTR *PFN_vkCmdResetQueryPool)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); +typedef void (GLAD_API_PTR *PFN_vkCmdResolveImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve * pRegions); +typedef void (GLAD_API_PTR *PFN_vkCmdResolveImage2)(VkCommandBuffer commandBuffer, const VkResolveImageInfo2 * pResolveImageInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdSetBlendConstants)(VkCommandBuffer commandBuffer, const float blendConstants [4]); +typedef void (GLAD_API_PTR *PFN_vkCmdSetCullMode)(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthBias)(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthBiasEnable)(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthBounds)(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthBoundsTestEnable)(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthCompareOp)(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthTestEnable)(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDepthWriteEnable)(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable); +typedef void (GLAD_API_PTR *PFN_vkCmdSetDeviceMask)(VkCommandBuffer commandBuffer, uint32_t deviceMask); +typedef void (GLAD_API_PTR *PFN_vkCmdSetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); +typedef void (GLAD_API_PTR *PFN_vkCmdSetEvent2)(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo * pDependencyInfo); +typedef void (GLAD_API_PTR *PFN_vkCmdSetFrontFace)(VkCommandBuffer commandBuffer, VkFrontFace frontFace); +typedef void (GLAD_API_PTR *PFN_vkCmdSetLineWidth)(VkCommandBuffer commandBuffer, float lineWidth); +typedef void (GLAD_API_PTR *PFN_vkCmdSetPrimitiveRestartEnable)(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable); +typedef void (GLAD_API_PTR *PFN_vkCmdSetPrimitiveTopology)(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology); +typedef void (GLAD_API_PTR *PFN_vkCmdSetRasterizerDiscardEnable)(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable); +typedef void (GLAD_API_PTR *PFN_vkCmdSetScissor)(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D * pScissors); +typedef void (GLAD_API_PTR *PFN_vkCmdSetScissorWithCount)(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D * pScissors); +typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilCompareMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask); +typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilOp)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp); +typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilReference)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference); +typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilTestEnable)(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable); +typedef void (GLAD_API_PTR *PFN_vkCmdSetStencilWriteMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask); +typedef void (GLAD_API_PTR *PFN_vkCmdSetViewport)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport * pViewports); +typedef void (GLAD_API_PTR *PFN_vkCmdSetViewportWithCount)(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport * pViewports); +typedef void (GLAD_API_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void * pData); +typedef void (GLAD_API_PTR *PFN_vkCmdWaitEvents)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent * pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier * pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier * pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier * pImageMemoryBarriers); +typedef void (GLAD_API_PTR *PFN_vkCmdWaitEvents2)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent * pEvents, const VkDependencyInfo * pDependencyInfos); +typedef void (GLAD_API_PTR *PFN_vkCmdWriteTimestamp)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query); +typedef void (GLAD_API_PTR *PFN_vkCmdWriteTimestamp2)(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateBuffer)(VkDevice device, const VkBufferCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkBuffer * pBuffer); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateBufferView)(VkDevice device, const VkBufferViewCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkBufferView * pView); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateCommandPool)(VkDevice device, const VkCommandPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkCommandPool * pCommandPool); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateComputePipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo * pCreateInfos, const VkAllocationCallbacks * pAllocator, VkPipeline * pPipelines); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDebugReportCallbackEXT * pCallback); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDescriptorPool)(VkDevice device, const VkDescriptorPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDescriptorPool * pDescriptorPool); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDescriptorSetLayout)(VkDevice device, const VkDescriptorSetLayoutCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDescriptorSetLayout * pSetLayout); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDescriptorUpdateTemplate)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDescriptorUpdateTemplate * pDescriptorUpdateTemplate); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateDevice)(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkDevice * pDevice); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateEvent)(VkDevice device, const VkEventCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkEvent * pEvent); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateFence)(VkDevice device, const VkFenceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkFence * pFence); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateFramebuffer)(VkDevice device, const VkFramebufferCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkFramebuffer * pFramebuffer); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo * pCreateInfos, const VkAllocationCallbacks * pAllocator, VkPipeline * pPipelines); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateImage)(VkDevice device, const VkImageCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkImage * pImage); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateImageView)(VkDevice device, const VkImageViewCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkImageView * pView); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateInstance)(const VkInstanceCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkInstance * pInstance); +typedef VkResult (GLAD_API_PTR *PFN_vkCreatePipelineCache)(VkDevice device, const VkPipelineCacheCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkPipelineCache * pPipelineCache); +typedef VkResult (GLAD_API_PTR *PFN_vkCreatePipelineLayout)(VkDevice device, const VkPipelineLayoutCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkPipelineLayout * pPipelineLayout); +typedef VkResult (GLAD_API_PTR *PFN_vkCreatePrivateDataSlot)(VkDevice device, const VkPrivateDataSlotCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkPrivateDataSlot * pPrivateDataSlot); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateQueryPool)(VkDevice device, const VkQueryPoolCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkQueryPool * pQueryPool); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateRenderPass)(VkDevice device, const VkRenderPassCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkRenderPass * pRenderPass); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateRenderPass2)(VkDevice device, const VkRenderPassCreateInfo2 * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkRenderPass * pRenderPass); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateSampler)(VkDevice device, const VkSamplerCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSampler * pSampler); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateSamplerYcbcrConversion)(VkDevice device, const VkSamplerYcbcrConversionCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSamplerYcbcrConversion * pYcbcrConversion); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateSemaphore)(VkDevice device, const VkSemaphoreCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSemaphore * pSemaphore); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateShaderModule)(VkDevice device, const VkShaderModuleCreateInfo * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkShaderModule * pShaderModule); +typedef VkResult (GLAD_API_PTR *PFN_vkCreateSwapchainKHR)(VkDevice device, const VkSwapchainCreateInfoKHR * pCreateInfo, const VkAllocationCallbacks * pAllocator, VkSwapchainKHR * pSwapchain); +typedef void (GLAD_API_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char * pLayerPrefix, const char * pMessage); +typedef void (GLAD_API_PTR *PFN_vkDestroyBuffer)(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyBufferView)(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyCommandPool)(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDescriptorSetLayout)(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDescriptorUpdateTemplate)(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyDevice)(VkDevice device, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyEvent)(VkDevice device, VkEvent event, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyFramebuffer)(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyImage)(VkDevice device, VkImage image, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyImageView)(VkDevice device, VkImageView imageView, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyInstance)(VkInstance instance, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyPipeline)(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyPipelineCache)(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyPipelineLayout)(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyPrivateDataSlot)(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyQueryPool)(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyRenderPass)(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySampler)(VkDevice device, VkSampler sampler, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySamplerYcbcrConversion)(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySemaphore)(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroyShaderModule)(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySurfaceKHR)(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks * pAllocator); +typedef void (GLAD_API_PTR *PFN_vkDestroySwapchainKHR)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks * pAllocator); +typedef VkResult (GLAD_API_PTR *PFN_vkDeviceWaitIdle)(VkDevice device); +typedef VkResult (GLAD_API_PTR *PFN_vkEndCommandBuffer)(VkCommandBuffer commandBuffer); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateDeviceExtensionProperties)(VkPhysicalDevice physicalDevice, const char * pLayerName, uint32_t * pPropertyCount, VkExtensionProperties * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateDeviceLayerProperties)(VkPhysicalDevice physicalDevice, uint32_t * pPropertyCount, VkLayerProperties * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateInstanceExtensionProperties)(const char * pLayerName, uint32_t * pPropertyCount, VkExtensionProperties * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateInstanceLayerProperties)(uint32_t * pPropertyCount, VkLayerProperties * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumerateInstanceVersion)(uint32_t * pApiVersion); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumeratePhysicalDeviceGroups)(VkInstance instance, uint32_t * pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties * pPhysicalDeviceGroupProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t * pPhysicalDeviceCount, VkPhysicalDevice * pPhysicalDevices); +typedef VkResult (GLAD_API_PTR *PFN_vkFlushMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange * pMemoryRanges); +typedef void (GLAD_API_PTR *PFN_vkFreeCommandBuffers)(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer * pCommandBuffers); +typedef VkResult (GLAD_API_PTR *PFN_vkFreeDescriptorSets)(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet * pDescriptorSets); +typedef void (GLAD_API_PTR *PFN_vkFreeMemory)(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks * pAllocator); +typedef VkDeviceAddress (GLAD_API_PTR *PFN_vkGetBufferDeviceAddress)(VkDevice device, const VkBufferDeviceAddressInfo * pInfo); +typedef void (GLAD_API_PTR *PFN_vkGetBufferMemoryRequirements)(VkDevice device, VkBuffer buffer, VkMemoryRequirements * pMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetBufferMemoryRequirements2)(VkDevice device, const VkBufferMemoryRequirementsInfo2 * pInfo, VkMemoryRequirements2 * pMemoryRequirements); +typedef uint64_t (GLAD_API_PTR *PFN_vkGetBufferOpaqueCaptureAddress)(VkDevice device, const VkBufferDeviceAddressInfo * pInfo); +typedef void (GLAD_API_PTR *PFN_vkGetDescriptorSetLayoutSupport)(VkDevice device, const VkDescriptorSetLayoutCreateInfo * pCreateInfo, VkDescriptorSetLayoutSupport * pSupport); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceBufferMemoryRequirements)(VkDevice device, const VkDeviceBufferMemoryRequirements * pInfo, VkMemoryRequirements2 * pMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceGroupPeerMemoryFeatures)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags * pPeerMemoryFeatures); +typedef VkResult (GLAD_API_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHR)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR * pDeviceGroupPresentCapabilities); +typedef VkResult (GLAD_API_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHR)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR * pModes); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceImageMemoryRequirements)(VkDevice device, const VkDeviceImageMemoryRequirements * pInfo, VkMemoryRequirements2 * pMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceImageSparseMemoryRequirements)(VkDevice device, const VkDeviceImageMemoryRequirements * pInfo, uint32_t * pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2 * pSparseMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceMemoryCommitment)(VkDevice device, VkDeviceMemory memory, VkDeviceSize * pCommittedMemoryInBytes); +typedef uint64_t (GLAD_API_PTR *PFN_vkGetDeviceMemoryOpaqueCaptureAddress)(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo * pInfo); +typedef PFN_vkVoidFunction (GLAD_API_PTR *PFN_vkGetDeviceProcAddr)(VkDevice device, const char * pName); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceQueue)(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue * pQueue); +typedef void (GLAD_API_PTR *PFN_vkGetDeviceQueue2)(VkDevice device, const VkDeviceQueueInfo2 * pQueueInfo, VkQueue * pQueue); +typedef VkResult (GLAD_API_PTR *PFN_vkGetEventStatus)(VkDevice device, VkEvent event); +typedef VkResult (GLAD_API_PTR *PFN_vkGetFenceStatus)(VkDevice device, VkFence fence); +typedef void (GLAD_API_PTR *PFN_vkGetImageMemoryRequirements)(VkDevice device, VkImage image, VkMemoryRequirements * pMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetImageMemoryRequirements2)(VkDevice device, const VkImageMemoryRequirementsInfo2 * pInfo, VkMemoryRequirements2 * pMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetImageSparseMemoryRequirements)(VkDevice device, VkImage image, uint32_t * pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements * pSparseMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetImageSparseMemoryRequirements2)(VkDevice device, const VkImageSparseMemoryRequirementsInfo2 * pInfo, uint32_t * pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2 * pSparseMemoryRequirements); +typedef void (GLAD_API_PTR *PFN_vkGetImageSubresourceLayout)(VkDevice device, VkImage image, const VkImageSubresource * pSubresource, VkSubresourceLayout * pLayout); +typedef PFN_vkVoidFunction (GLAD_API_PTR *PFN_vkGetInstanceProcAddr)(VkInstance instance, const char * pName); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceExternalBufferProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo * pExternalBufferInfo, VkExternalBufferProperties * pExternalBufferProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceExternalFenceProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo * pExternalFenceInfo, VkExternalFenceProperties * pExternalFenceProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceExternalSemaphoreProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo * pExternalSemaphoreInfo, VkExternalSemaphoreProperties * pExternalSemaphoreProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFeatures)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures * pFeatures); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFeatures2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2 * pFeatures); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties * pFormatProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceFormatProperties2)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2 * pFormatProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties * pImageFormatProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2 * pImageFormatInfo, VkImageFormatProperties2 * pImageFormatProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties * pMemoryProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2 * pMemoryProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pRectCount, VkRect2D * pRects); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties * pProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceProperties2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 * pProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysicalDevice physicalDevice, uint32_t * pQueueFamilyPropertyCount, VkQueueFamilyProperties * pQueueFamilyProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2)(VkPhysicalDevice physicalDevice, uint32_t * pQueueFamilyPropertyCount, VkQueueFamilyProperties2 * pQueueFamilyProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t * pPropertyCount, VkSparseImageFormatProperties * pProperties); +typedef void (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2 * pFormatInfo, uint32_t * pPropertyCount, VkSparseImageFormatProperties2 * pProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR * pSurfaceCapabilities); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pSurfaceFormatCount, VkSurfaceFormatKHR * pSurfaceFormats); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t * pPresentModeCount, VkPresentModeKHR * pPresentModes); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32 * pSupported); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPhysicalDeviceToolProperties)(VkPhysicalDevice physicalDevice, uint32_t * pToolCount, VkPhysicalDeviceToolProperties * pToolProperties); +typedef VkResult (GLAD_API_PTR *PFN_vkGetPipelineCacheData)(VkDevice device, VkPipelineCache pipelineCache, size_t * pDataSize, void * pData); +typedef void (GLAD_API_PTR *PFN_vkGetPrivateData)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t * pData); +typedef VkResult (GLAD_API_PTR *PFN_vkGetQueryPoolResults)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void * pData, VkDeviceSize stride, VkQueryResultFlags flags); +typedef void (GLAD_API_PTR *PFN_vkGetRenderAreaGranularity)(VkDevice device, VkRenderPass renderPass, VkExtent2D * pGranularity); +typedef VkResult (GLAD_API_PTR *PFN_vkGetSemaphoreCounterValue)(VkDevice device, VkSemaphore semaphore, uint64_t * pValue); +typedef VkResult (GLAD_API_PTR *PFN_vkGetSwapchainImagesKHR)(VkDevice device, VkSwapchainKHR swapchain, uint32_t * pSwapchainImageCount, VkImage * pSwapchainImages); +typedef VkResult (GLAD_API_PTR *PFN_vkInvalidateMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange * pMemoryRanges); +typedef VkResult (GLAD_API_PTR *PFN_vkMapMemory)(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void ** ppData); +typedef VkResult (GLAD_API_PTR *PFN_vkMergePipelineCaches)(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache * pSrcCaches); +typedef VkResult (GLAD_API_PTR *PFN_vkQueueBindSparse)(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo * pBindInfo, VkFence fence); +typedef VkResult (GLAD_API_PTR *PFN_vkQueuePresentKHR)(VkQueue queue, const VkPresentInfoKHR * pPresentInfo); +typedef VkResult (GLAD_API_PTR *PFN_vkQueueSubmit)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo * pSubmits, VkFence fence); +typedef VkResult (GLAD_API_PTR *PFN_vkQueueSubmit2)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2 * pSubmits, VkFence fence); +typedef VkResult (GLAD_API_PTR *PFN_vkQueueWaitIdle)(VkQueue queue); +typedef VkResult (GLAD_API_PTR *PFN_vkResetCommandBuffer)(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags); +typedef VkResult (GLAD_API_PTR *PFN_vkResetCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags); +typedef VkResult (GLAD_API_PTR *PFN_vkResetDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags); +typedef VkResult (GLAD_API_PTR *PFN_vkResetEvent)(VkDevice device, VkEvent event); +typedef VkResult (GLAD_API_PTR *PFN_vkResetFences)(VkDevice device, uint32_t fenceCount, const VkFence * pFences); +typedef void (GLAD_API_PTR *PFN_vkResetQueryPool)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); +typedef VkResult (GLAD_API_PTR *PFN_vkSetEvent)(VkDevice device, VkEvent event); +typedef VkResult (GLAD_API_PTR *PFN_vkSetPrivateData)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data); +typedef VkResult (GLAD_API_PTR *PFN_vkSignalSemaphore)(VkDevice device, const VkSemaphoreSignalInfo * pSignalInfo); +typedef void (GLAD_API_PTR *PFN_vkTrimCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); +typedef void (GLAD_API_PTR *PFN_vkUnmapMemory)(VkDevice device, VkDeviceMemory memory); +typedef void (GLAD_API_PTR *PFN_vkUpdateDescriptorSetWithTemplate)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void * pData); +typedef void (GLAD_API_PTR *PFN_vkUpdateDescriptorSets)(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet * pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet * pDescriptorCopies); +typedef VkResult (GLAD_API_PTR *PFN_vkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence * pFences, VkBool32 waitAll, uint64_t timeout); +typedef VkResult (GLAD_API_PTR *PFN_vkWaitSemaphores)(VkDevice device, const VkSemaphoreWaitInfo * pWaitInfo, uint64_t timeout); GLAD_API_CALL PFN_vkAcquireNextImage2KHR glad_vkAcquireNextImage2KHR; #define vkAcquireNextImage2KHR glad_vkAcquireNextImage2KHR @@ -3124,6 +5136,10 @@ GLAD_API_CALL PFN_vkCmdBeginQuery glad_vkCmdBeginQuery; #define vkCmdBeginQuery glad_vkCmdBeginQuery GLAD_API_CALL PFN_vkCmdBeginRenderPass glad_vkCmdBeginRenderPass; #define vkCmdBeginRenderPass glad_vkCmdBeginRenderPass +GLAD_API_CALL PFN_vkCmdBeginRenderPass2 glad_vkCmdBeginRenderPass2; +#define vkCmdBeginRenderPass2 glad_vkCmdBeginRenderPass2 +GLAD_API_CALL PFN_vkCmdBeginRendering glad_vkCmdBeginRendering; +#define vkCmdBeginRendering glad_vkCmdBeginRendering GLAD_API_CALL PFN_vkCmdBindDescriptorSets glad_vkCmdBindDescriptorSets; #define vkCmdBindDescriptorSets glad_vkCmdBindDescriptorSets GLAD_API_CALL PFN_vkCmdBindIndexBuffer glad_vkCmdBindIndexBuffer; @@ -3132,8 +5148,12 @@ GLAD_API_CALL PFN_vkCmdBindPipeline glad_vkCmdBindPipeline; #define vkCmdBindPipeline glad_vkCmdBindPipeline GLAD_API_CALL PFN_vkCmdBindVertexBuffers glad_vkCmdBindVertexBuffers; #define vkCmdBindVertexBuffers glad_vkCmdBindVertexBuffers +GLAD_API_CALL PFN_vkCmdBindVertexBuffers2 glad_vkCmdBindVertexBuffers2; +#define vkCmdBindVertexBuffers2 glad_vkCmdBindVertexBuffers2 GLAD_API_CALL PFN_vkCmdBlitImage glad_vkCmdBlitImage; #define vkCmdBlitImage glad_vkCmdBlitImage +GLAD_API_CALL PFN_vkCmdBlitImage2 glad_vkCmdBlitImage2; +#define vkCmdBlitImage2 glad_vkCmdBlitImage2 GLAD_API_CALL PFN_vkCmdClearAttachments glad_vkCmdClearAttachments; #define vkCmdClearAttachments glad_vkCmdClearAttachments GLAD_API_CALL PFN_vkCmdClearColorImage glad_vkCmdClearColorImage; @@ -3142,12 +5162,20 @@ GLAD_API_CALL PFN_vkCmdClearDepthStencilImage glad_vkCmdClearDepthStencilImage; #define vkCmdClearDepthStencilImage glad_vkCmdClearDepthStencilImage GLAD_API_CALL PFN_vkCmdCopyBuffer glad_vkCmdCopyBuffer; #define vkCmdCopyBuffer glad_vkCmdCopyBuffer +GLAD_API_CALL PFN_vkCmdCopyBuffer2 glad_vkCmdCopyBuffer2; +#define vkCmdCopyBuffer2 glad_vkCmdCopyBuffer2 GLAD_API_CALL PFN_vkCmdCopyBufferToImage glad_vkCmdCopyBufferToImage; #define vkCmdCopyBufferToImage glad_vkCmdCopyBufferToImage +GLAD_API_CALL PFN_vkCmdCopyBufferToImage2 glad_vkCmdCopyBufferToImage2; +#define vkCmdCopyBufferToImage2 glad_vkCmdCopyBufferToImage2 GLAD_API_CALL PFN_vkCmdCopyImage glad_vkCmdCopyImage; #define vkCmdCopyImage glad_vkCmdCopyImage +GLAD_API_CALL PFN_vkCmdCopyImage2 glad_vkCmdCopyImage2; +#define vkCmdCopyImage2 glad_vkCmdCopyImage2 GLAD_API_CALL PFN_vkCmdCopyImageToBuffer glad_vkCmdCopyImageToBuffer; #define vkCmdCopyImageToBuffer glad_vkCmdCopyImageToBuffer +GLAD_API_CALL PFN_vkCmdCopyImageToBuffer2 glad_vkCmdCopyImageToBuffer2; +#define vkCmdCopyImageToBuffer2 glad_vkCmdCopyImageToBuffer2 GLAD_API_CALL PFN_vkCmdCopyQueryPoolResults glad_vkCmdCopyQueryPoolResults; #define vkCmdCopyQueryPoolResults glad_vkCmdCopyQueryPoolResults GLAD_API_CALL PFN_vkCmdDispatch glad_vkCmdDispatch; @@ -3162,56 +5190,106 @@ GLAD_API_CALL PFN_vkCmdDrawIndexed glad_vkCmdDrawIndexed; #define vkCmdDrawIndexed glad_vkCmdDrawIndexed GLAD_API_CALL PFN_vkCmdDrawIndexedIndirect glad_vkCmdDrawIndexedIndirect; #define vkCmdDrawIndexedIndirect glad_vkCmdDrawIndexedIndirect +GLAD_API_CALL PFN_vkCmdDrawIndexedIndirectCount glad_vkCmdDrawIndexedIndirectCount; +#define vkCmdDrawIndexedIndirectCount glad_vkCmdDrawIndexedIndirectCount GLAD_API_CALL PFN_vkCmdDrawIndirect glad_vkCmdDrawIndirect; #define vkCmdDrawIndirect glad_vkCmdDrawIndirect +GLAD_API_CALL PFN_vkCmdDrawIndirectCount glad_vkCmdDrawIndirectCount; +#define vkCmdDrawIndirectCount glad_vkCmdDrawIndirectCount GLAD_API_CALL PFN_vkCmdEndQuery glad_vkCmdEndQuery; #define vkCmdEndQuery glad_vkCmdEndQuery GLAD_API_CALL PFN_vkCmdEndRenderPass glad_vkCmdEndRenderPass; #define vkCmdEndRenderPass glad_vkCmdEndRenderPass +GLAD_API_CALL PFN_vkCmdEndRenderPass2 glad_vkCmdEndRenderPass2; +#define vkCmdEndRenderPass2 glad_vkCmdEndRenderPass2 +GLAD_API_CALL PFN_vkCmdEndRendering glad_vkCmdEndRendering; +#define vkCmdEndRendering glad_vkCmdEndRendering GLAD_API_CALL PFN_vkCmdExecuteCommands glad_vkCmdExecuteCommands; #define vkCmdExecuteCommands glad_vkCmdExecuteCommands GLAD_API_CALL PFN_vkCmdFillBuffer glad_vkCmdFillBuffer; #define vkCmdFillBuffer glad_vkCmdFillBuffer GLAD_API_CALL PFN_vkCmdNextSubpass glad_vkCmdNextSubpass; #define vkCmdNextSubpass glad_vkCmdNextSubpass +GLAD_API_CALL PFN_vkCmdNextSubpass2 glad_vkCmdNextSubpass2; +#define vkCmdNextSubpass2 glad_vkCmdNextSubpass2 GLAD_API_CALL PFN_vkCmdPipelineBarrier glad_vkCmdPipelineBarrier; #define vkCmdPipelineBarrier glad_vkCmdPipelineBarrier +GLAD_API_CALL PFN_vkCmdPipelineBarrier2 glad_vkCmdPipelineBarrier2; +#define vkCmdPipelineBarrier2 glad_vkCmdPipelineBarrier2 GLAD_API_CALL PFN_vkCmdPushConstants glad_vkCmdPushConstants; #define vkCmdPushConstants glad_vkCmdPushConstants GLAD_API_CALL PFN_vkCmdResetEvent glad_vkCmdResetEvent; #define vkCmdResetEvent glad_vkCmdResetEvent +GLAD_API_CALL PFN_vkCmdResetEvent2 glad_vkCmdResetEvent2; +#define vkCmdResetEvent2 glad_vkCmdResetEvent2 GLAD_API_CALL PFN_vkCmdResetQueryPool glad_vkCmdResetQueryPool; #define vkCmdResetQueryPool glad_vkCmdResetQueryPool GLAD_API_CALL PFN_vkCmdResolveImage glad_vkCmdResolveImage; #define vkCmdResolveImage glad_vkCmdResolveImage +GLAD_API_CALL PFN_vkCmdResolveImage2 glad_vkCmdResolveImage2; +#define vkCmdResolveImage2 glad_vkCmdResolveImage2 GLAD_API_CALL PFN_vkCmdSetBlendConstants glad_vkCmdSetBlendConstants; #define vkCmdSetBlendConstants glad_vkCmdSetBlendConstants +GLAD_API_CALL PFN_vkCmdSetCullMode glad_vkCmdSetCullMode; +#define vkCmdSetCullMode glad_vkCmdSetCullMode GLAD_API_CALL PFN_vkCmdSetDepthBias glad_vkCmdSetDepthBias; #define vkCmdSetDepthBias glad_vkCmdSetDepthBias +GLAD_API_CALL PFN_vkCmdSetDepthBiasEnable glad_vkCmdSetDepthBiasEnable; +#define vkCmdSetDepthBiasEnable glad_vkCmdSetDepthBiasEnable GLAD_API_CALL PFN_vkCmdSetDepthBounds glad_vkCmdSetDepthBounds; #define vkCmdSetDepthBounds glad_vkCmdSetDepthBounds +GLAD_API_CALL PFN_vkCmdSetDepthBoundsTestEnable glad_vkCmdSetDepthBoundsTestEnable; +#define vkCmdSetDepthBoundsTestEnable glad_vkCmdSetDepthBoundsTestEnable +GLAD_API_CALL PFN_vkCmdSetDepthCompareOp glad_vkCmdSetDepthCompareOp; +#define vkCmdSetDepthCompareOp glad_vkCmdSetDepthCompareOp +GLAD_API_CALL PFN_vkCmdSetDepthTestEnable glad_vkCmdSetDepthTestEnable; +#define vkCmdSetDepthTestEnable glad_vkCmdSetDepthTestEnable +GLAD_API_CALL PFN_vkCmdSetDepthWriteEnable glad_vkCmdSetDepthWriteEnable; +#define vkCmdSetDepthWriteEnable glad_vkCmdSetDepthWriteEnable GLAD_API_CALL PFN_vkCmdSetDeviceMask glad_vkCmdSetDeviceMask; #define vkCmdSetDeviceMask glad_vkCmdSetDeviceMask GLAD_API_CALL PFN_vkCmdSetEvent glad_vkCmdSetEvent; #define vkCmdSetEvent glad_vkCmdSetEvent +GLAD_API_CALL PFN_vkCmdSetEvent2 glad_vkCmdSetEvent2; +#define vkCmdSetEvent2 glad_vkCmdSetEvent2 +GLAD_API_CALL PFN_vkCmdSetFrontFace glad_vkCmdSetFrontFace; +#define vkCmdSetFrontFace glad_vkCmdSetFrontFace GLAD_API_CALL PFN_vkCmdSetLineWidth glad_vkCmdSetLineWidth; #define vkCmdSetLineWidth glad_vkCmdSetLineWidth +GLAD_API_CALL PFN_vkCmdSetPrimitiveRestartEnable glad_vkCmdSetPrimitiveRestartEnable; +#define vkCmdSetPrimitiveRestartEnable glad_vkCmdSetPrimitiveRestartEnable +GLAD_API_CALL PFN_vkCmdSetPrimitiveTopology glad_vkCmdSetPrimitiveTopology; +#define vkCmdSetPrimitiveTopology glad_vkCmdSetPrimitiveTopology +GLAD_API_CALL PFN_vkCmdSetRasterizerDiscardEnable glad_vkCmdSetRasterizerDiscardEnable; +#define vkCmdSetRasterizerDiscardEnable glad_vkCmdSetRasterizerDiscardEnable GLAD_API_CALL PFN_vkCmdSetScissor glad_vkCmdSetScissor; #define vkCmdSetScissor glad_vkCmdSetScissor +GLAD_API_CALL PFN_vkCmdSetScissorWithCount glad_vkCmdSetScissorWithCount; +#define vkCmdSetScissorWithCount glad_vkCmdSetScissorWithCount GLAD_API_CALL PFN_vkCmdSetStencilCompareMask glad_vkCmdSetStencilCompareMask; #define vkCmdSetStencilCompareMask glad_vkCmdSetStencilCompareMask +GLAD_API_CALL PFN_vkCmdSetStencilOp glad_vkCmdSetStencilOp; +#define vkCmdSetStencilOp glad_vkCmdSetStencilOp GLAD_API_CALL PFN_vkCmdSetStencilReference glad_vkCmdSetStencilReference; #define vkCmdSetStencilReference glad_vkCmdSetStencilReference +GLAD_API_CALL PFN_vkCmdSetStencilTestEnable glad_vkCmdSetStencilTestEnable; +#define vkCmdSetStencilTestEnable glad_vkCmdSetStencilTestEnable GLAD_API_CALL PFN_vkCmdSetStencilWriteMask glad_vkCmdSetStencilWriteMask; #define vkCmdSetStencilWriteMask glad_vkCmdSetStencilWriteMask GLAD_API_CALL PFN_vkCmdSetViewport glad_vkCmdSetViewport; #define vkCmdSetViewport glad_vkCmdSetViewport +GLAD_API_CALL PFN_vkCmdSetViewportWithCount glad_vkCmdSetViewportWithCount; +#define vkCmdSetViewportWithCount glad_vkCmdSetViewportWithCount GLAD_API_CALL PFN_vkCmdUpdateBuffer glad_vkCmdUpdateBuffer; #define vkCmdUpdateBuffer glad_vkCmdUpdateBuffer GLAD_API_CALL PFN_vkCmdWaitEvents glad_vkCmdWaitEvents; #define vkCmdWaitEvents glad_vkCmdWaitEvents +GLAD_API_CALL PFN_vkCmdWaitEvents2 glad_vkCmdWaitEvents2; +#define vkCmdWaitEvents2 glad_vkCmdWaitEvents2 GLAD_API_CALL PFN_vkCmdWriteTimestamp glad_vkCmdWriteTimestamp; #define vkCmdWriteTimestamp glad_vkCmdWriteTimestamp +GLAD_API_CALL PFN_vkCmdWriteTimestamp2 glad_vkCmdWriteTimestamp2; +#define vkCmdWriteTimestamp2 glad_vkCmdWriteTimestamp2 GLAD_API_CALL PFN_vkCreateBuffer glad_vkCreateBuffer; #define vkCreateBuffer glad_vkCreateBuffer GLAD_API_CALL PFN_vkCreateBufferView glad_vkCreateBufferView; @@ -3248,10 +5326,14 @@ GLAD_API_CALL PFN_vkCreatePipelineCache glad_vkCreatePipelineCache; #define vkCreatePipelineCache glad_vkCreatePipelineCache GLAD_API_CALL PFN_vkCreatePipelineLayout glad_vkCreatePipelineLayout; #define vkCreatePipelineLayout glad_vkCreatePipelineLayout +GLAD_API_CALL PFN_vkCreatePrivateDataSlot glad_vkCreatePrivateDataSlot; +#define vkCreatePrivateDataSlot glad_vkCreatePrivateDataSlot GLAD_API_CALL PFN_vkCreateQueryPool glad_vkCreateQueryPool; #define vkCreateQueryPool glad_vkCreateQueryPool GLAD_API_CALL PFN_vkCreateRenderPass glad_vkCreateRenderPass; #define vkCreateRenderPass glad_vkCreateRenderPass +GLAD_API_CALL PFN_vkCreateRenderPass2 glad_vkCreateRenderPass2; +#define vkCreateRenderPass2 glad_vkCreateRenderPass2 GLAD_API_CALL PFN_vkCreateSampler glad_vkCreateSampler; #define vkCreateSampler glad_vkCreateSampler GLAD_API_CALL PFN_vkCreateSamplerYcbcrConversion glad_vkCreateSamplerYcbcrConversion; @@ -3298,6 +5380,8 @@ GLAD_API_CALL PFN_vkDestroyPipelineCache glad_vkDestroyPipelineCache; #define vkDestroyPipelineCache glad_vkDestroyPipelineCache GLAD_API_CALL PFN_vkDestroyPipelineLayout glad_vkDestroyPipelineLayout; #define vkDestroyPipelineLayout glad_vkDestroyPipelineLayout +GLAD_API_CALL PFN_vkDestroyPrivateDataSlot glad_vkDestroyPrivateDataSlot; +#define vkDestroyPrivateDataSlot glad_vkDestroyPrivateDataSlot GLAD_API_CALL PFN_vkDestroyQueryPool glad_vkDestroyQueryPool; #define vkDestroyQueryPool glad_vkDestroyQueryPool GLAD_API_CALL PFN_vkDestroyRenderPass glad_vkDestroyRenderPass; @@ -3340,20 +5424,32 @@ GLAD_API_CALL PFN_vkFreeDescriptorSets glad_vkFreeDescriptorSets; #define vkFreeDescriptorSets glad_vkFreeDescriptorSets GLAD_API_CALL PFN_vkFreeMemory glad_vkFreeMemory; #define vkFreeMemory glad_vkFreeMemory +GLAD_API_CALL PFN_vkGetBufferDeviceAddress glad_vkGetBufferDeviceAddress; +#define vkGetBufferDeviceAddress glad_vkGetBufferDeviceAddress GLAD_API_CALL PFN_vkGetBufferMemoryRequirements glad_vkGetBufferMemoryRequirements; #define vkGetBufferMemoryRequirements glad_vkGetBufferMemoryRequirements GLAD_API_CALL PFN_vkGetBufferMemoryRequirements2 glad_vkGetBufferMemoryRequirements2; #define vkGetBufferMemoryRequirements2 glad_vkGetBufferMemoryRequirements2 +GLAD_API_CALL PFN_vkGetBufferOpaqueCaptureAddress glad_vkGetBufferOpaqueCaptureAddress; +#define vkGetBufferOpaqueCaptureAddress glad_vkGetBufferOpaqueCaptureAddress GLAD_API_CALL PFN_vkGetDescriptorSetLayoutSupport glad_vkGetDescriptorSetLayoutSupport; #define vkGetDescriptorSetLayoutSupport glad_vkGetDescriptorSetLayoutSupport +GLAD_API_CALL PFN_vkGetDeviceBufferMemoryRequirements glad_vkGetDeviceBufferMemoryRequirements; +#define vkGetDeviceBufferMemoryRequirements glad_vkGetDeviceBufferMemoryRequirements GLAD_API_CALL PFN_vkGetDeviceGroupPeerMemoryFeatures glad_vkGetDeviceGroupPeerMemoryFeatures; #define vkGetDeviceGroupPeerMemoryFeatures glad_vkGetDeviceGroupPeerMemoryFeatures GLAD_API_CALL PFN_vkGetDeviceGroupPresentCapabilitiesKHR glad_vkGetDeviceGroupPresentCapabilitiesKHR; #define vkGetDeviceGroupPresentCapabilitiesKHR glad_vkGetDeviceGroupPresentCapabilitiesKHR GLAD_API_CALL PFN_vkGetDeviceGroupSurfacePresentModesKHR glad_vkGetDeviceGroupSurfacePresentModesKHR; #define vkGetDeviceGroupSurfacePresentModesKHR glad_vkGetDeviceGroupSurfacePresentModesKHR +GLAD_API_CALL PFN_vkGetDeviceImageMemoryRequirements glad_vkGetDeviceImageMemoryRequirements; +#define vkGetDeviceImageMemoryRequirements glad_vkGetDeviceImageMemoryRequirements +GLAD_API_CALL PFN_vkGetDeviceImageSparseMemoryRequirements glad_vkGetDeviceImageSparseMemoryRequirements; +#define vkGetDeviceImageSparseMemoryRequirements glad_vkGetDeviceImageSparseMemoryRequirements GLAD_API_CALL PFN_vkGetDeviceMemoryCommitment glad_vkGetDeviceMemoryCommitment; #define vkGetDeviceMemoryCommitment glad_vkGetDeviceMemoryCommitment +GLAD_API_CALL PFN_vkGetDeviceMemoryOpaqueCaptureAddress glad_vkGetDeviceMemoryOpaqueCaptureAddress; +#define vkGetDeviceMemoryOpaqueCaptureAddress glad_vkGetDeviceMemoryOpaqueCaptureAddress GLAD_API_CALL PFN_vkGetDeviceProcAddr glad_vkGetDeviceProcAddr; #define vkGetDeviceProcAddr glad_vkGetDeviceProcAddr GLAD_API_CALL PFN_vkGetDeviceQueue glad_vkGetDeviceQueue; @@ -3420,12 +5516,18 @@ GLAD_API_CALL PFN_vkGetPhysicalDeviceSurfacePresentModesKHR glad_vkGetPhysicalDe #define vkGetPhysicalDeviceSurfacePresentModesKHR glad_vkGetPhysicalDeviceSurfacePresentModesKHR GLAD_API_CALL PFN_vkGetPhysicalDeviceSurfaceSupportKHR glad_vkGetPhysicalDeviceSurfaceSupportKHR; #define vkGetPhysicalDeviceSurfaceSupportKHR glad_vkGetPhysicalDeviceSurfaceSupportKHR +GLAD_API_CALL PFN_vkGetPhysicalDeviceToolProperties glad_vkGetPhysicalDeviceToolProperties; +#define vkGetPhysicalDeviceToolProperties glad_vkGetPhysicalDeviceToolProperties GLAD_API_CALL PFN_vkGetPipelineCacheData glad_vkGetPipelineCacheData; #define vkGetPipelineCacheData glad_vkGetPipelineCacheData +GLAD_API_CALL PFN_vkGetPrivateData glad_vkGetPrivateData; +#define vkGetPrivateData glad_vkGetPrivateData GLAD_API_CALL PFN_vkGetQueryPoolResults glad_vkGetQueryPoolResults; #define vkGetQueryPoolResults glad_vkGetQueryPoolResults GLAD_API_CALL PFN_vkGetRenderAreaGranularity glad_vkGetRenderAreaGranularity; #define vkGetRenderAreaGranularity glad_vkGetRenderAreaGranularity +GLAD_API_CALL PFN_vkGetSemaphoreCounterValue glad_vkGetSemaphoreCounterValue; +#define vkGetSemaphoreCounterValue glad_vkGetSemaphoreCounterValue GLAD_API_CALL PFN_vkGetSwapchainImagesKHR glad_vkGetSwapchainImagesKHR; #define vkGetSwapchainImagesKHR glad_vkGetSwapchainImagesKHR GLAD_API_CALL PFN_vkInvalidateMappedMemoryRanges glad_vkInvalidateMappedMemoryRanges; @@ -3440,6 +5542,8 @@ GLAD_API_CALL PFN_vkQueuePresentKHR glad_vkQueuePresentKHR; #define vkQueuePresentKHR glad_vkQueuePresentKHR GLAD_API_CALL PFN_vkQueueSubmit glad_vkQueueSubmit; #define vkQueueSubmit glad_vkQueueSubmit +GLAD_API_CALL PFN_vkQueueSubmit2 glad_vkQueueSubmit2; +#define vkQueueSubmit2 glad_vkQueueSubmit2 GLAD_API_CALL PFN_vkQueueWaitIdle glad_vkQueueWaitIdle; #define vkQueueWaitIdle glad_vkQueueWaitIdle GLAD_API_CALL PFN_vkResetCommandBuffer glad_vkResetCommandBuffer; @@ -3452,8 +5556,14 @@ GLAD_API_CALL PFN_vkResetEvent glad_vkResetEvent; #define vkResetEvent glad_vkResetEvent GLAD_API_CALL PFN_vkResetFences glad_vkResetFences; #define vkResetFences glad_vkResetFences +GLAD_API_CALL PFN_vkResetQueryPool glad_vkResetQueryPool; +#define vkResetQueryPool glad_vkResetQueryPool GLAD_API_CALL PFN_vkSetEvent glad_vkSetEvent; #define vkSetEvent glad_vkSetEvent +GLAD_API_CALL PFN_vkSetPrivateData glad_vkSetPrivateData; +#define vkSetPrivateData glad_vkSetPrivateData +GLAD_API_CALL PFN_vkSignalSemaphore glad_vkSignalSemaphore; +#define vkSignalSemaphore glad_vkSignalSemaphore GLAD_API_CALL PFN_vkTrimCommandPool glad_vkTrimCommandPool; #define vkTrimCommandPool glad_vkTrimCommandPool GLAD_API_CALL PFN_vkUnmapMemory glad_vkUnmapMemory; @@ -3464,6 +5574,11 @@ GLAD_API_CALL PFN_vkUpdateDescriptorSets glad_vkUpdateDescriptorSets; #define vkUpdateDescriptorSets glad_vkUpdateDescriptorSets GLAD_API_CALL PFN_vkWaitForFences glad_vkWaitForFences; #define vkWaitForFences glad_vkWaitForFences +GLAD_API_CALL PFN_vkWaitSemaphores glad_vkWaitSemaphores; +#define vkWaitSemaphores glad_vkWaitSemaphores + + + GLAD_API_CALL int gladLoadVulkanUserPtr( VkPhysicalDevice physical_device, GLADuserptrloadfunc load, void *userptr); @@ -3471,10 +5586,745 @@ GLAD_API_CALL int gladLoadVulkan( VkPhysicalDevice physical_device, GLADloadfunc +#ifdef __cplusplus +} +#endif +#endif +/* Source */ +#ifdef GLAD_VULKAN_IMPLEMENTATION +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_VK_VERSION_1_0 = 0; +int GLAD_VK_VERSION_1_1 = 0; +int GLAD_VK_VERSION_1_2 = 0; +int GLAD_VK_VERSION_1_3 = 0; +int GLAD_VK_EXT_debug_report = 0; +int GLAD_VK_KHR_portability_enumeration = 0; +int GLAD_VK_KHR_surface = 0; +int GLAD_VK_KHR_swapchain = 0; + + + +PFN_vkAcquireNextImage2KHR glad_vkAcquireNextImage2KHR = NULL; +PFN_vkAcquireNextImageKHR glad_vkAcquireNextImageKHR = NULL; +PFN_vkAllocateCommandBuffers glad_vkAllocateCommandBuffers = NULL; +PFN_vkAllocateDescriptorSets glad_vkAllocateDescriptorSets = NULL; +PFN_vkAllocateMemory glad_vkAllocateMemory = NULL; +PFN_vkBeginCommandBuffer glad_vkBeginCommandBuffer = NULL; +PFN_vkBindBufferMemory glad_vkBindBufferMemory = NULL; +PFN_vkBindBufferMemory2 glad_vkBindBufferMemory2 = NULL; +PFN_vkBindImageMemory glad_vkBindImageMemory = NULL; +PFN_vkBindImageMemory2 glad_vkBindImageMemory2 = NULL; +PFN_vkCmdBeginQuery glad_vkCmdBeginQuery = NULL; +PFN_vkCmdBeginRenderPass glad_vkCmdBeginRenderPass = NULL; +PFN_vkCmdBeginRenderPass2 glad_vkCmdBeginRenderPass2 = NULL; +PFN_vkCmdBeginRendering glad_vkCmdBeginRendering = NULL; +PFN_vkCmdBindDescriptorSets glad_vkCmdBindDescriptorSets = NULL; +PFN_vkCmdBindIndexBuffer glad_vkCmdBindIndexBuffer = NULL; +PFN_vkCmdBindPipeline glad_vkCmdBindPipeline = NULL; +PFN_vkCmdBindVertexBuffers glad_vkCmdBindVertexBuffers = NULL; +PFN_vkCmdBindVertexBuffers2 glad_vkCmdBindVertexBuffers2 = NULL; +PFN_vkCmdBlitImage glad_vkCmdBlitImage = NULL; +PFN_vkCmdBlitImage2 glad_vkCmdBlitImage2 = NULL; +PFN_vkCmdClearAttachments glad_vkCmdClearAttachments = NULL; +PFN_vkCmdClearColorImage glad_vkCmdClearColorImage = NULL; +PFN_vkCmdClearDepthStencilImage glad_vkCmdClearDepthStencilImage = NULL; +PFN_vkCmdCopyBuffer glad_vkCmdCopyBuffer = NULL; +PFN_vkCmdCopyBuffer2 glad_vkCmdCopyBuffer2 = NULL; +PFN_vkCmdCopyBufferToImage glad_vkCmdCopyBufferToImage = NULL; +PFN_vkCmdCopyBufferToImage2 glad_vkCmdCopyBufferToImage2 = NULL; +PFN_vkCmdCopyImage glad_vkCmdCopyImage = NULL; +PFN_vkCmdCopyImage2 glad_vkCmdCopyImage2 = NULL; +PFN_vkCmdCopyImageToBuffer glad_vkCmdCopyImageToBuffer = NULL; +PFN_vkCmdCopyImageToBuffer2 glad_vkCmdCopyImageToBuffer2 = NULL; +PFN_vkCmdCopyQueryPoolResults glad_vkCmdCopyQueryPoolResults = NULL; +PFN_vkCmdDispatch glad_vkCmdDispatch = NULL; +PFN_vkCmdDispatchBase glad_vkCmdDispatchBase = NULL; +PFN_vkCmdDispatchIndirect glad_vkCmdDispatchIndirect = NULL; +PFN_vkCmdDraw glad_vkCmdDraw = NULL; +PFN_vkCmdDrawIndexed glad_vkCmdDrawIndexed = NULL; +PFN_vkCmdDrawIndexedIndirect glad_vkCmdDrawIndexedIndirect = NULL; +PFN_vkCmdDrawIndexedIndirectCount glad_vkCmdDrawIndexedIndirectCount = NULL; +PFN_vkCmdDrawIndirect glad_vkCmdDrawIndirect = NULL; +PFN_vkCmdDrawIndirectCount glad_vkCmdDrawIndirectCount = NULL; +PFN_vkCmdEndQuery glad_vkCmdEndQuery = NULL; +PFN_vkCmdEndRenderPass glad_vkCmdEndRenderPass = NULL; +PFN_vkCmdEndRenderPass2 glad_vkCmdEndRenderPass2 = NULL; +PFN_vkCmdEndRendering glad_vkCmdEndRendering = NULL; +PFN_vkCmdExecuteCommands glad_vkCmdExecuteCommands = NULL; +PFN_vkCmdFillBuffer glad_vkCmdFillBuffer = NULL; +PFN_vkCmdNextSubpass glad_vkCmdNextSubpass = NULL; +PFN_vkCmdNextSubpass2 glad_vkCmdNextSubpass2 = NULL; +PFN_vkCmdPipelineBarrier glad_vkCmdPipelineBarrier = NULL; +PFN_vkCmdPipelineBarrier2 glad_vkCmdPipelineBarrier2 = NULL; +PFN_vkCmdPushConstants glad_vkCmdPushConstants = NULL; +PFN_vkCmdResetEvent glad_vkCmdResetEvent = NULL; +PFN_vkCmdResetEvent2 glad_vkCmdResetEvent2 = NULL; +PFN_vkCmdResetQueryPool glad_vkCmdResetQueryPool = NULL; +PFN_vkCmdResolveImage glad_vkCmdResolveImage = NULL; +PFN_vkCmdResolveImage2 glad_vkCmdResolveImage2 = NULL; +PFN_vkCmdSetBlendConstants glad_vkCmdSetBlendConstants = NULL; +PFN_vkCmdSetCullMode glad_vkCmdSetCullMode = NULL; +PFN_vkCmdSetDepthBias glad_vkCmdSetDepthBias = NULL; +PFN_vkCmdSetDepthBiasEnable glad_vkCmdSetDepthBiasEnable = NULL; +PFN_vkCmdSetDepthBounds glad_vkCmdSetDepthBounds = NULL; +PFN_vkCmdSetDepthBoundsTestEnable glad_vkCmdSetDepthBoundsTestEnable = NULL; +PFN_vkCmdSetDepthCompareOp glad_vkCmdSetDepthCompareOp = NULL; +PFN_vkCmdSetDepthTestEnable glad_vkCmdSetDepthTestEnable = NULL; +PFN_vkCmdSetDepthWriteEnable glad_vkCmdSetDepthWriteEnable = NULL; +PFN_vkCmdSetDeviceMask glad_vkCmdSetDeviceMask = NULL; +PFN_vkCmdSetEvent glad_vkCmdSetEvent = NULL; +PFN_vkCmdSetEvent2 glad_vkCmdSetEvent2 = NULL; +PFN_vkCmdSetFrontFace glad_vkCmdSetFrontFace = NULL; +PFN_vkCmdSetLineWidth glad_vkCmdSetLineWidth = NULL; +PFN_vkCmdSetPrimitiveRestartEnable glad_vkCmdSetPrimitiveRestartEnable = NULL; +PFN_vkCmdSetPrimitiveTopology glad_vkCmdSetPrimitiveTopology = NULL; +PFN_vkCmdSetRasterizerDiscardEnable glad_vkCmdSetRasterizerDiscardEnable = NULL; +PFN_vkCmdSetScissor glad_vkCmdSetScissor = NULL; +PFN_vkCmdSetScissorWithCount glad_vkCmdSetScissorWithCount = NULL; +PFN_vkCmdSetStencilCompareMask glad_vkCmdSetStencilCompareMask = NULL; +PFN_vkCmdSetStencilOp glad_vkCmdSetStencilOp = NULL; +PFN_vkCmdSetStencilReference glad_vkCmdSetStencilReference = NULL; +PFN_vkCmdSetStencilTestEnable glad_vkCmdSetStencilTestEnable = NULL; +PFN_vkCmdSetStencilWriteMask glad_vkCmdSetStencilWriteMask = NULL; +PFN_vkCmdSetViewport glad_vkCmdSetViewport = NULL; +PFN_vkCmdSetViewportWithCount glad_vkCmdSetViewportWithCount = NULL; +PFN_vkCmdUpdateBuffer glad_vkCmdUpdateBuffer = NULL; +PFN_vkCmdWaitEvents glad_vkCmdWaitEvents = NULL; +PFN_vkCmdWaitEvents2 glad_vkCmdWaitEvents2 = NULL; +PFN_vkCmdWriteTimestamp glad_vkCmdWriteTimestamp = NULL; +PFN_vkCmdWriteTimestamp2 glad_vkCmdWriteTimestamp2 = NULL; +PFN_vkCreateBuffer glad_vkCreateBuffer = NULL; +PFN_vkCreateBufferView glad_vkCreateBufferView = NULL; +PFN_vkCreateCommandPool glad_vkCreateCommandPool = NULL; +PFN_vkCreateComputePipelines glad_vkCreateComputePipelines = NULL; +PFN_vkCreateDebugReportCallbackEXT glad_vkCreateDebugReportCallbackEXT = NULL; +PFN_vkCreateDescriptorPool glad_vkCreateDescriptorPool = NULL; +PFN_vkCreateDescriptorSetLayout glad_vkCreateDescriptorSetLayout = NULL; +PFN_vkCreateDescriptorUpdateTemplate glad_vkCreateDescriptorUpdateTemplate = NULL; +PFN_vkCreateDevice glad_vkCreateDevice = NULL; +PFN_vkCreateEvent glad_vkCreateEvent = NULL; +PFN_vkCreateFence glad_vkCreateFence = NULL; +PFN_vkCreateFramebuffer glad_vkCreateFramebuffer = NULL; +PFN_vkCreateGraphicsPipelines glad_vkCreateGraphicsPipelines = NULL; +PFN_vkCreateImage glad_vkCreateImage = NULL; +PFN_vkCreateImageView glad_vkCreateImageView = NULL; +PFN_vkCreateInstance glad_vkCreateInstance = NULL; +PFN_vkCreatePipelineCache glad_vkCreatePipelineCache = NULL; +PFN_vkCreatePipelineLayout glad_vkCreatePipelineLayout = NULL; +PFN_vkCreatePrivateDataSlot glad_vkCreatePrivateDataSlot = NULL; +PFN_vkCreateQueryPool glad_vkCreateQueryPool = NULL; +PFN_vkCreateRenderPass glad_vkCreateRenderPass = NULL; +PFN_vkCreateRenderPass2 glad_vkCreateRenderPass2 = NULL; +PFN_vkCreateSampler glad_vkCreateSampler = NULL; +PFN_vkCreateSamplerYcbcrConversion glad_vkCreateSamplerYcbcrConversion = NULL; +PFN_vkCreateSemaphore glad_vkCreateSemaphore = NULL; +PFN_vkCreateShaderModule glad_vkCreateShaderModule = NULL; +PFN_vkCreateSwapchainKHR glad_vkCreateSwapchainKHR = NULL; +PFN_vkDebugReportMessageEXT glad_vkDebugReportMessageEXT = NULL; +PFN_vkDestroyBuffer glad_vkDestroyBuffer = NULL; +PFN_vkDestroyBufferView glad_vkDestroyBufferView = NULL; +PFN_vkDestroyCommandPool glad_vkDestroyCommandPool = NULL; +PFN_vkDestroyDebugReportCallbackEXT glad_vkDestroyDebugReportCallbackEXT = NULL; +PFN_vkDestroyDescriptorPool glad_vkDestroyDescriptorPool = NULL; +PFN_vkDestroyDescriptorSetLayout glad_vkDestroyDescriptorSetLayout = NULL; +PFN_vkDestroyDescriptorUpdateTemplate glad_vkDestroyDescriptorUpdateTemplate = NULL; +PFN_vkDestroyDevice glad_vkDestroyDevice = NULL; +PFN_vkDestroyEvent glad_vkDestroyEvent = NULL; +PFN_vkDestroyFence glad_vkDestroyFence = NULL; +PFN_vkDestroyFramebuffer glad_vkDestroyFramebuffer = NULL; +PFN_vkDestroyImage glad_vkDestroyImage = NULL; +PFN_vkDestroyImageView glad_vkDestroyImageView = NULL; +PFN_vkDestroyInstance glad_vkDestroyInstance = NULL; +PFN_vkDestroyPipeline glad_vkDestroyPipeline = NULL; +PFN_vkDestroyPipelineCache glad_vkDestroyPipelineCache = NULL; +PFN_vkDestroyPipelineLayout glad_vkDestroyPipelineLayout = NULL; +PFN_vkDestroyPrivateDataSlot glad_vkDestroyPrivateDataSlot = NULL; +PFN_vkDestroyQueryPool glad_vkDestroyQueryPool = NULL; +PFN_vkDestroyRenderPass glad_vkDestroyRenderPass = NULL; +PFN_vkDestroySampler glad_vkDestroySampler = NULL; +PFN_vkDestroySamplerYcbcrConversion glad_vkDestroySamplerYcbcrConversion = NULL; +PFN_vkDestroySemaphore glad_vkDestroySemaphore = NULL; +PFN_vkDestroyShaderModule glad_vkDestroyShaderModule = NULL; +PFN_vkDestroySurfaceKHR glad_vkDestroySurfaceKHR = NULL; +PFN_vkDestroySwapchainKHR glad_vkDestroySwapchainKHR = NULL; +PFN_vkDeviceWaitIdle glad_vkDeviceWaitIdle = NULL; +PFN_vkEndCommandBuffer glad_vkEndCommandBuffer = NULL; +PFN_vkEnumerateDeviceExtensionProperties glad_vkEnumerateDeviceExtensionProperties = NULL; +PFN_vkEnumerateDeviceLayerProperties glad_vkEnumerateDeviceLayerProperties = NULL; +PFN_vkEnumerateInstanceExtensionProperties glad_vkEnumerateInstanceExtensionProperties = NULL; +PFN_vkEnumerateInstanceLayerProperties glad_vkEnumerateInstanceLayerProperties = NULL; +PFN_vkEnumerateInstanceVersion glad_vkEnumerateInstanceVersion = NULL; +PFN_vkEnumeratePhysicalDeviceGroups glad_vkEnumeratePhysicalDeviceGroups = NULL; +PFN_vkEnumeratePhysicalDevices glad_vkEnumeratePhysicalDevices = NULL; +PFN_vkFlushMappedMemoryRanges glad_vkFlushMappedMemoryRanges = NULL; +PFN_vkFreeCommandBuffers glad_vkFreeCommandBuffers = NULL; +PFN_vkFreeDescriptorSets glad_vkFreeDescriptorSets = NULL; +PFN_vkFreeMemory glad_vkFreeMemory = NULL; +PFN_vkGetBufferDeviceAddress glad_vkGetBufferDeviceAddress = NULL; +PFN_vkGetBufferMemoryRequirements glad_vkGetBufferMemoryRequirements = NULL; +PFN_vkGetBufferMemoryRequirements2 glad_vkGetBufferMemoryRequirements2 = NULL; +PFN_vkGetBufferOpaqueCaptureAddress glad_vkGetBufferOpaqueCaptureAddress = NULL; +PFN_vkGetDescriptorSetLayoutSupport glad_vkGetDescriptorSetLayoutSupport = NULL; +PFN_vkGetDeviceBufferMemoryRequirements glad_vkGetDeviceBufferMemoryRequirements = NULL; +PFN_vkGetDeviceGroupPeerMemoryFeatures glad_vkGetDeviceGroupPeerMemoryFeatures = NULL; +PFN_vkGetDeviceGroupPresentCapabilitiesKHR glad_vkGetDeviceGroupPresentCapabilitiesKHR = NULL; +PFN_vkGetDeviceGroupSurfacePresentModesKHR glad_vkGetDeviceGroupSurfacePresentModesKHR = NULL; +PFN_vkGetDeviceImageMemoryRequirements glad_vkGetDeviceImageMemoryRequirements = NULL; +PFN_vkGetDeviceImageSparseMemoryRequirements glad_vkGetDeviceImageSparseMemoryRequirements = NULL; +PFN_vkGetDeviceMemoryCommitment glad_vkGetDeviceMemoryCommitment = NULL; +PFN_vkGetDeviceMemoryOpaqueCaptureAddress glad_vkGetDeviceMemoryOpaqueCaptureAddress = NULL; +PFN_vkGetDeviceProcAddr glad_vkGetDeviceProcAddr = NULL; +PFN_vkGetDeviceQueue glad_vkGetDeviceQueue = NULL; +PFN_vkGetDeviceQueue2 glad_vkGetDeviceQueue2 = NULL; +PFN_vkGetEventStatus glad_vkGetEventStatus = NULL; +PFN_vkGetFenceStatus glad_vkGetFenceStatus = NULL; +PFN_vkGetImageMemoryRequirements glad_vkGetImageMemoryRequirements = NULL; +PFN_vkGetImageMemoryRequirements2 glad_vkGetImageMemoryRequirements2 = NULL; +PFN_vkGetImageSparseMemoryRequirements glad_vkGetImageSparseMemoryRequirements = NULL; +PFN_vkGetImageSparseMemoryRequirements2 glad_vkGetImageSparseMemoryRequirements2 = NULL; +PFN_vkGetImageSubresourceLayout glad_vkGetImageSubresourceLayout = NULL; +PFN_vkGetInstanceProcAddr glad_vkGetInstanceProcAddr = NULL; +PFN_vkGetPhysicalDeviceExternalBufferProperties glad_vkGetPhysicalDeviceExternalBufferProperties = NULL; +PFN_vkGetPhysicalDeviceExternalFenceProperties glad_vkGetPhysicalDeviceExternalFenceProperties = NULL; +PFN_vkGetPhysicalDeviceExternalSemaphoreProperties glad_vkGetPhysicalDeviceExternalSemaphoreProperties = NULL; +PFN_vkGetPhysicalDeviceFeatures glad_vkGetPhysicalDeviceFeatures = NULL; +PFN_vkGetPhysicalDeviceFeatures2 glad_vkGetPhysicalDeviceFeatures2 = NULL; +PFN_vkGetPhysicalDeviceFormatProperties glad_vkGetPhysicalDeviceFormatProperties = NULL; +PFN_vkGetPhysicalDeviceFormatProperties2 glad_vkGetPhysicalDeviceFormatProperties2 = NULL; +PFN_vkGetPhysicalDeviceImageFormatProperties glad_vkGetPhysicalDeviceImageFormatProperties = NULL; +PFN_vkGetPhysicalDeviceImageFormatProperties2 glad_vkGetPhysicalDeviceImageFormatProperties2 = NULL; +PFN_vkGetPhysicalDeviceMemoryProperties glad_vkGetPhysicalDeviceMemoryProperties = NULL; +PFN_vkGetPhysicalDeviceMemoryProperties2 glad_vkGetPhysicalDeviceMemoryProperties2 = NULL; +PFN_vkGetPhysicalDevicePresentRectanglesKHR glad_vkGetPhysicalDevicePresentRectanglesKHR = NULL; +PFN_vkGetPhysicalDeviceProperties glad_vkGetPhysicalDeviceProperties = NULL; +PFN_vkGetPhysicalDeviceProperties2 glad_vkGetPhysicalDeviceProperties2 = NULL; +PFN_vkGetPhysicalDeviceQueueFamilyProperties glad_vkGetPhysicalDeviceQueueFamilyProperties = NULL; +PFN_vkGetPhysicalDeviceQueueFamilyProperties2 glad_vkGetPhysicalDeviceQueueFamilyProperties2 = NULL; +PFN_vkGetPhysicalDeviceSparseImageFormatProperties glad_vkGetPhysicalDeviceSparseImageFormatProperties = NULL; +PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 glad_vkGetPhysicalDeviceSparseImageFormatProperties2 = NULL; +PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR glad_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = NULL; +PFN_vkGetPhysicalDeviceSurfaceFormatsKHR glad_vkGetPhysicalDeviceSurfaceFormatsKHR = NULL; +PFN_vkGetPhysicalDeviceSurfacePresentModesKHR glad_vkGetPhysicalDeviceSurfacePresentModesKHR = NULL; +PFN_vkGetPhysicalDeviceSurfaceSupportKHR glad_vkGetPhysicalDeviceSurfaceSupportKHR = NULL; +PFN_vkGetPhysicalDeviceToolProperties glad_vkGetPhysicalDeviceToolProperties = NULL; +PFN_vkGetPipelineCacheData glad_vkGetPipelineCacheData = NULL; +PFN_vkGetPrivateData glad_vkGetPrivateData = NULL; +PFN_vkGetQueryPoolResults glad_vkGetQueryPoolResults = NULL; +PFN_vkGetRenderAreaGranularity glad_vkGetRenderAreaGranularity = NULL; +PFN_vkGetSemaphoreCounterValue glad_vkGetSemaphoreCounterValue = NULL; +PFN_vkGetSwapchainImagesKHR glad_vkGetSwapchainImagesKHR = NULL; +PFN_vkInvalidateMappedMemoryRanges glad_vkInvalidateMappedMemoryRanges = NULL; +PFN_vkMapMemory glad_vkMapMemory = NULL; +PFN_vkMergePipelineCaches glad_vkMergePipelineCaches = NULL; +PFN_vkQueueBindSparse glad_vkQueueBindSparse = NULL; +PFN_vkQueuePresentKHR glad_vkQueuePresentKHR = NULL; +PFN_vkQueueSubmit glad_vkQueueSubmit = NULL; +PFN_vkQueueSubmit2 glad_vkQueueSubmit2 = NULL; +PFN_vkQueueWaitIdle glad_vkQueueWaitIdle = NULL; +PFN_vkResetCommandBuffer glad_vkResetCommandBuffer = NULL; +PFN_vkResetCommandPool glad_vkResetCommandPool = NULL; +PFN_vkResetDescriptorPool glad_vkResetDescriptorPool = NULL; +PFN_vkResetEvent glad_vkResetEvent = NULL; +PFN_vkResetFences glad_vkResetFences = NULL; +PFN_vkResetQueryPool glad_vkResetQueryPool = NULL; +PFN_vkSetEvent glad_vkSetEvent = NULL; +PFN_vkSetPrivateData glad_vkSetPrivateData = NULL; +PFN_vkSignalSemaphore glad_vkSignalSemaphore = NULL; +PFN_vkTrimCommandPool glad_vkTrimCommandPool = NULL; +PFN_vkUnmapMemory glad_vkUnmapMemory = NULL; +PFN_vkUpdateDescriptorSetWithTemplate glad_vkUpdateDescriptorSetWithTemplate = NULL; +PFN_vkUpdateDescriptorSets glad_vkUpdateDescriptorSets = NULL; +PFN_vkWaitForFences glad_vkWaitForFences = NULL; +PFN_vkWaitSemaphores glad_vkWaitSemaphores = NULL; + + +static void glad_vk_load_VK_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_VERSION_1_0) return; + glad_vkAllocateCommandBuffers = (PFN_vkAllocateCommandBuffers) load(userptr, "vkAllocateCommandBuffers"); + glad_vkAllocateDescriptorSets = (PFN_vkAllocateDescriptorSets) load(userptr, "vkAllocateDescriptorSets"); + glad_vkAllocateMemory = (PFN_vkAllocateMemory) load(userptr, "vkAllocateMemory"); + glad_vkBeginCommandBuffer = (PFN_vkBeginCommandBuffer) load(userptr, "vkBeginCommandBuffer"); + glad_vkBindBufferMemory = (PFN_vkBindBufferMemory) load(userptr, "vkBindBufferMemory"); + glad_vkBindImageMemory = (PFN_vkBindImageMemory) load(userptr, "vkBindImageMemory"); + glad_vkCmdBeginQuery = (PFN_vkCmdBeginQuery) load(userptr, "vkCmdBeginQuery"); + glad_vkCmdBeginRenderPass = (PFN_vkCmdBeginRenderPass) load(userptr, "vkCmdBeginRenderPass"); + glad_vkCmdBindDescriptorSets = (PFN_vkCmdBindDescriptorSets) load(userptr, "vkCmdBindDescriptorSets"); + glad_vkCmdBindIndexBuffer = (PFN_vkCmdBindIndexBuffer) load(userptr, "vkCmdBindIndexBuffer"); + glad_vkCmdBindPipeline = (PFN_vkCmdBindPipeline) load(userptr, "vkCmdBindPipeline"); + glad_vkCmdBindVertexBuffers = (PFN_vkCmdBindVertexBuffers) load(userptr, "vkCmdBindVertexBuffers"); + glad_vkCmdBlitImage = (PFN_vkCmdBlitImage) load(userptr, "vkCmdBlitImage"); + glad_vkCmdClearAttachments = (PFN_vkCmdClearAttachments) load(userptr, "vkCmdClearAttachments"); + glad_vkCmdClearColorImage = (PFN_vkCmdClearColorImage) load(userptr, "vkCmdClearColorImage"); + glad_vkCmdClearDepthStencilImage = (PFN_vkCmdClearDepthStencilImage) load(userptr, "vkCmdClearDepthStencilImage"); + glad_vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer) load(userptr, "vkCmdCopyBuffer"); + glad_vkCmdCopyBufferToImage = (PFN_vkCmdCopyBufferToImage) load(userptr, "vkCmdCopyBufferToImage"); + glad_vkCmdCopyImage = (PFN_vkCmdCopyImage) load(userptr, "vkCmdCopyImage"); + glad_vkCmdCopyImageToBuffer = (PFN_vkCmdCopyImageToBuffer) load(userptr, "vkCmdCopyImageToBuffer"); + glad_vkCmdCopyQueryPoolResults = (PFN_vkCmdCopyQueryPoolResults) load(userptr, "vkCmdCopyQueryPoolResults"); + glad_vkCmdDispatch = (PFN_vkCmdDispatch) load(userptr, "vkCmdDispatch"); + glad_vkCmdDispatchIndirect = (PFN_vkCmdDispatchIndirect) load(userptr, "vkCmdDispatchIndirect"); + glad_vkCmdDraw = (PFN_vkCmdDraw) load(userptr, "vkCmdDraw"); + glad_vkCmdDrawIndexed = (PFN_vkCmdDrawIndexed) load(userptr, "vkCmdDrawIndexed"); + glad_vkCmdDrawIndexedIndirect = (PFN_vkCmdDrawIndexedIndirect) load(userptr, "vkCmdDrawIndexedIndirect"); + glad_vkCmdDrawIndirect = (PFN_vkCmdDrawIndirect) load(userptr, "vkCmdDrawIndirect"); + glad_vkCmdEndQuery = (PFN_vkCmdEndQuery) load(userptr, "vkCmdEndQuery"); + glad_vkCmdEndRenderPass = (PFN_vkCmdEndRenderPass) load(userptr, "vkCmdEndRenderPass"); + glad_vkCmdExecuteCommands = (PFN_vkCmdExecuteCommands) load(userptr, "vkCmdExecuteCommands"); + glad_vkCmdFillBuffer = (PFN_vkCmdFillBuffer) load(userptr, "vkCmdFillBuffer"); + glad_vkCmdNextSubpass = (PFN_vkCmdNextSubpass) load(userptr, "vkCmdNextSubpass"); + glad_vkCmdPipelineBarrier = (PFN_vkCmdPipelineBarrier) load(userptr, "vkCmdPipelineBarrier"); + glad_vkCmdPushConstants = (PFN_vkCmdPushConstants) load(userptr, "vkCmdPushConstants"); + glad_vkCmdResetEvent = (PFN_vkCmdResetEvent) load(userptr, "vkCmdResetEvent"); + glad_vkCmdResetQueryPool = (PFN_vkCmdResetQueryPool) load(userptr, "vkCmdResetQueryPool"); + glad_vkCmdResolveImage = (PFN_vkCmdResolveImage) load(userptr, "vkCmdResolveImage"); + glad_vkCmdSetBlendConstants = (PFN_vkCmdSetBlendConstants) load(userptr, "vkCmdSetBlendConstants"); + glad_vkCmdSetDepthBias = (PFN_vkCmdSetDepthBias) load(userptr, "vkCmdSetDepthBias"); + glad_vkCmdSetDepthBounds = (PFN_vkCmdSetDepthBounds) load(userptr, "vkCmdSetDepthBounds"); + glad_vkCmdSetEvent = (PFN_vkCmdSetEvent) load(userptr, "vkCmdSetEvent"); + glad_vkCmdSetLineWidth = (PFN_vkCmdSetLineWidth) load(userptr, "vkCmdSetLineWidth"); + glad_vkCmdSetScissor = (PFN_vkCmdSetScissor) load(userptr, "vkCmdSetScissor"); + glad_vkCmdSetStencilCompareMask = (PFN_vkCmdSetStencilCompareMask) load(userptr, "vkCmdSetStencilCompareMask"); + glad_vkCmdSetStencilReference = (PFN_vkCmdSetStencilReference) load(userptr, "vkCmdSetStencilReference"); + glad_vkCmdSetStencilWriteMask = (PFN_vkCmdSetStencilWriteMask) load(userptr, "vkCmdSetStencilWriteMask"); + glad_vkCmdSetViewport = (PFN_vkCmdSetViewport) load(userptr, "vkCmdSetViewport"); + glad_vkCmdUpdateBuffer = (PFN_vkCmdUpdateBuffer) load(userptr, "vkCmdUpdateBuffer"); + glad_vkCmdWaitEvents = (PFN_vkCmdWaitEvents) load(userptr, "vkCmdWaitEvents"); + glad_vkCmdWriteTimestamp = (PFN_vkCmdWriteTimestamp) load(userptr, "vkCmdWriteTimestamp"); + glad_vkCreateBuffer = (PFN_vkCreateBuffer) load(userptr, "vkCreateBuffer"); + glad_vkCreateBufferView = (PFN_vkCreateBufferView) load(userptr, "vkCreateBufferView"); + glad_vkCreateCommandPool = (PFN_vkCreateCommandPool) load(userptr, "vkCreateCommandPool"); + glad_vkCreateComputePipelines = (PFN_vkCreateComputePipelines) load(userptr, "vkCreateComputePipelines"); + glad_vkCreateDescriptorPool = (PFN_vkCreateDescriptorPool) load(userptr, "vkCreateDescriptorPool"); + glad_vkCreateDescriptorSetLayout = (PFN_vkCreateDescriptorSetLayout) load(userptr, "vkCreateDescriptorSetLayout"); + glad_vkCreateDevice = (PFN_vkCreateDevice) load(userptr, "vkCreateDevice"); + glad_vkCreateEvent = (PFN_vkCreateEvent) load(userptr, "vkCreateEvent"); + glad_vkCreateFence = (PFN_vkCreateFence) load(userptr, "vkCreateFence"); + glad_vkCreateFramebuffer = (PFN_vkCreateFramebuffer) load(userptr, "vkCreateFramebuffer"); + glad_vkCreateGraphicsPipelines = (PFN_vkCreateGraphicsPipelines) load(userptr, "vkCreateGraphicsPipelines"); + glad_vkCreateImage = (PFN_vkCreateImage) load(userptr, "vkCreateImage"); + glad_vkCreateImageView = (PFN_vkCreateImageView) load(userptr, "vkCreateImageView"); + glad_vkCreateInstance = (PFN_vkCreateInstance) load(userptr, "vkCreateInstance"); + glad_vkCreatePipelineCache = (PFN_vkCreatePipelineCache) load(userptr, "vkCreatePipelineCache"); + glad_vkCreatePipelineLayout = (PFN_vkCreatePipelineLayout) load(userptr, "vkCreatePipelineLayout"); + glad_vkCreateQueryPool = (PFN_vkCreateQueryPool) load(userptr, "vkCreateQueryPool"); + glad_vkCreateRenderPass = (PFN_vkCreateRenderPass) load(userptr, "vkCreateRenderPass"); + glad_vkCreateSampler = (PFN_vkCreateSampler) load(userptr, "vkCreateSampler"); + glad_vkCreateSemaphore = (PFN_vkCreateSemaphore) load(userptr, "vkCreateSemaphore"); + glad_vkCreateShaderModule = (PFN_vkCreateShaderModule) load(userptr, "vkCreateShaderModule"); + glad_vkDestroyBuffer = (PFN_vkDestroyBuffer) load(userptr, "vkDestroyBuffer"); + glad_vkDestroyBufferView = (PFN_vkDestroyBufferView) load(userptr, "vkDestroyBufferView"); + glad_vkDestroyCommandPool = (PFN_vkDestroyCommandPool) load(userptr, "vkDestroyCommandPool"); + glad_vkDestroyDescriptorPool = (PFN_vkDestroyDescriptorPool) load(userptr, "vkDestroyDescriptorPool"); + glad_vkDestroyDescriptorSetLayout = (PFN_vkDestroyDescriptorSetLayout) load(userptr, "vkDestroyDescriptorSetLayout"); + glad_vkDestroyDevice = (PFN_vkDestroyDevice) load(userptr, "vkDestroyDevice"); + glad_vkDestroyEvent = (PFN_vkDestroyEvent) load(userptr, "vkDestroyEvent"); + glad_vkDestroyFence = (PFN_vkDestroyFence) load(userptr, "vkDestroyFence"); + glad_vkDestroyFramebuffer = (PFN_vkDestroyFramebuffer) load(userptr, "vkDestroyFramebuffer"); + glad_vkDestroyImage = (PFN_vkDestroyImage) load(userptr, "vkDestroyImage"); + glad_vkDestroyImageView = (PFN_vkDestroyImageView) load(userptr, "vkDestroyImageView"); + glad_vkDestroyInstance = (PFN_vkDestroyInstance) load(userptr, "vkDestroyInstance"); + glad_vkDestroyPipeline = (PFN_vkDestroyPipeline) load(userptr, "vkDestroyPipeline"); + glad_vkDestroyPipelineCache = (PFN_vkDestroyPipelineCache) load(userptr, "vkDestroyPipelineCache"); + glad_vkDestroyPipelineLayout = (PFN_vkDestroyPipelineLayout) load(userptr, "vkDestroyPipelineLayout"); + glad_vkDestroyQueryPool = (PFN_vkDestroyQueryPool) load(userptr, "vkDestroyQueryPool"); + glad_vkDestroyRenderPass = (PFN_vkDestroyRenderPass) load(userptr, "vkDestroyRenderPass"); + glad_vkDestroySampler = (PFN_vkDestroySampler) load(userptr, "vkDestroySampler"); + glad_vkDestroySemaphore = (PFN_vkDestroySemaphore) load(userptr, "vkDestroySemaphore"); + glad_vkDestroyShaderModule = (PFN_vkDestroyShaderModule) load(userptr, "vkDestroyShaderModule"); + glad_vkDeviceWaitIdle = (PFN_vkDeviceWaitIdle) load(userptr, "vkDeviceWaitIdle"); + glad_vkEndCommandBuffer = (PFN_vkEndCommandBuffer) load(userptr, "vkEndCommandBuffer"); + glad_vkEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties) load(userptr, "vkEnumerateDeviceExtensionProperties"); + glad_vkEnumerateDeviceLayerProperties = (PFN_vkEnumerateDeviceLayerProperties) load(userptr, "vkEnumerateDeviceLayerProperties"); + glad_vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) load(userptr, "vkEnumerateInstanceExtensionProperties"); + glad_vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties) load(userptr, "vkEnumerateInstanceLayerProperties"); + glad_vkEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices) load(userptr, "vkEnumeratePhysicalDevices"); + glad_vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges) load(userptr, "vkFlushMappedMemoryRanges"); + glad_vkFreeCommandBuffers = (PFN_vkFreeCommandBuffers) load(userptr, "vkFreeCommandBuffers"); + glad_vkFreeDescriptorSets = (PFN_vkFreeDescriptorSets) load(userptr, "vkFreeDescriptorSets"); + glad_vkFreeMemory = (PFN_vkFreeMemory) load(userptr, "vkFreeMemory"); + glad_vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements) load(userptr, "vkGetBufferMemoryRequirements"); + glad_vkGetDeviceMemoryCommitment = (PFN_vkGetDeviceMemoryCommitment) load(userptr, "vkGetDeviceMemoryCommitment"); + glad_vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr) load(userptr, "vkGetDeviceProcAddr"); + glad_vkGetDeviceQueue = (PFN_vkGetDeviceQueue) load(userptr, "vkGetDeviceQueue"); + glad_vkGetEventStatus = (PFN_vkGetEventStatus) load(userptr, "vkGetEventStatus"); + glad_vkGetFenceStatus = (PFN_vkGetFenceStatus) load(userptr, "vkGetFenceStatus"); + glad_vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements) load(userptr, "vkGetImageMemoryRequirements"); + glad_vkGetImageSparseMemoryRequirements = (PFN_vkGetImageSparseMemoryRequirements) load(userptr, "vkGetImageSparseMemoryRequirements"); + glad_vkGetImageSubresourceLayout = (PFN_vkGetImageSubresourceLayout) load(userptr, "vkGetImageSubresourceLayout"); + glad_vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) load(userptr, "vkGetInstanceProcAddr"); + glad_vkGetPhysicalDeviceFeatures = (PFN_vkGetPhysicalDeviceFeatures) load(userptr, "vkGetPhysicalDeviceFeatures"); + glad_vkGetPhysicalDeviceFormatProperties = (PFN_vkGetPhysicalDeviceFormatProperties) load(userptr, "vkGetPhysicalDeviceFormatProperties"); + glad_vkGetPhysicalDeviceImageFormatProperties = (PFN_vkGetPhysicalDeviceImageFormatProperties) load(userptr, "vkGetPhysicalDeviceImageFormatProperties"); + glad_vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) load(userptr, "vkGetPhysicalDeviceMemoryProperties"); + glad_vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) load(userptr, "vkGetPhysicalDeviceProperties"); + glad_vkGetPhysicalDeviceQueueFamilyProperties = (PFN_vkGetPhysicalDeviceQueueFamilyProperties) load(userptr, "vkGetPhysicalDeviceQueueFamilyProperties"); + glad_vkGetPhysicalDeviceSparseImageFormatProperties = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties) load(userptr, "vkGetPhysicalDeviceSparseImageFormatProperties"); + glad_vkGetPipelineCacheData = (PFN_vkGetPipelineCacheData) load(userptr, "vkGetPipelineCacheData"); + glad_vkGetQueryPoolResults = (PFN_vkGetQueryPoolResults) load(userptr, "vkGetQueryPoolResults"); + glad_vkGetRenderAreaGranularity = (PFN_vkGetRenderAreaGranularity) load(userptr, "vkGetRenderAreaGranularity"); + glad_vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges) load(userptr, "vkInvalidateMappedMemoryRanges"); + glad_vkMapMemory = (PFN_vkMapMemory) load(userptr, "vkMapMemory"); + glad_vkMergePipelineCaches = (PFN_vkMergePipelineCaches) load(userptr, "vkMergePipelineCaches"); + glad_vkQueueBindSparse = (PFN_vkQueueBindSparse) load(userptr, "vkQueueBindSparse"); + glad_vkQueueSubmit = (PFN_vkQueueSubmit) load(userptr, "vkQueueSubmit"); + glad_vkQueueWaitIdle = (PFN_vkQueueWaitIdle) load(userptr, "vkQueueWaitIdle"); + glad_vkResetCommandBuffer = (PFN_vkResetCommandBuffer) load(userptr, "vkResetCommandBuffer"); + glad_vkResetCommandPool = (PFN_vkResetCommandPool) load(userptr, "vkResetCommandPool"); + glad_vkResetDescriptorPool = (PFN_vkResetDescriptorPool) load(userptr, "vkResetDescriptorPool"); + glad_vkResetEvent = (PFN_vkResetEvent) load(userptr, "vkResetEvent"); + glad_vkResetFences = (PFN_vkResetFences) load(userptr, "vkResetFences"); + glad_vkSetEvent = (PFN_vkSetEvent) load(userptr, "vkSetEvent"); + glad_vkUnmapMemory = (PFN_vkUnmapMemory) load(userptr, "vkUnmapMemory"); + glad_vkUpdateDescriptorSets = (PFN_vkUpdateDescriptorSets) load(userptr, "vkUpdateDescriptorSets"); + glad_vkWaitForFences = (PFN_vkWaitForFences) load(userptr, "vkWaitForFences"); +} +static void glad_vk_load_VK_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_VERSION_1_1) return; + glad_vkBindBufferMemory2 = (PFN_vkBindBufferMemory2) load(userptr, "vkBindBufferMemory2"); + glad_vkBindImageMemory2 = (PFN_vkBindImageMemory2) load(userptr, "vkBindImageMemory2"); + glad_vkCmdDispatchBase = (PFN_vkCmdDispatchBase) load(userptr, "vkCmdDispatchBase"); + glad_vkCmdSetDeviceMask = (PFN_vkCmdSetDeviceMask) load(userptr, "vkCmdSetDeviceMask"); + glad_vkCreateDescriptorUpdateTemplate = (PFN_vkCreateDescriptorUpdateTemplate) load(userptr, "vkCreateDescriptorUpdateTemplate"); + glad_vkCreateSamplerYcbcrConversion = (PFN_vkCreateSamplerYcbcrConversion) load(userptr, "vkCreateSamplerYcbcrConversion"); + glad_vkDestroyDescriptorUpdateTemplate = (PFN_vkDestroyDescriptorUpdateTemplate) load(userptr, "vkDestroyDescriptorUpdateTemplate"); + glad_vkDestroySamplerYcbcrConversion = (PFN_vkDestroySamplerYcbcrConversion) load(userptr, "vkDestroySamplerYcbcrConversion"); + glad_vkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion) load(userptr, "vkEnumerateInstanceVersion"); + glad_vkEnumeratePhysicalDeviceGroups = (PFN_vkEnumeratePhysicalDeviceGroups) load(userptr, "vkEnumeratePhysicalDeviceGroups"); + glad_vkGetBufferMemoryRequirements2 = (PFN_vkGetBufferMemoryRequirements2) load(userptr, "vkGetBufferMemoryRequirements2"); + glad_vkGetDescriptorSetLayoutSupport = (PFN_vkGetDescriptorSetLayoutSupport) load(userptr, "vkGetDescriptorSetLayoutSupport"); + glad_vkGetDeviceGroupPeerMemoryFeatures = (PFN_vkGetDeviceGroupPeerMemoryFeatures) load(userptr, "vkGetDeviceGroupPeerMemoryFeatures"); + glad_vkGetDeviceQueue2 = (PFN_vkGetDeviceQueue2) load(userptr, "vkGetDeviceQueue2"); + glad_vkGetImageMemoryRequirements2 = (PFN_vkGetImageMemoryRequirements2) load(userptr, "vkGetImageMemoryRequirements2"); + glad_vkGetImageSparseMemoryRequirements2 = (PFN_vkGetImageSparseMemoryRequirements2) load(userptr, "vkGetImageSparseMemoryRequirements2"); + glad_vkGetPhysicalDeviceExternalBufferProperties = (PFN_vkGetPhysicalDeviceExternalBufferProperties) load(userptr, "vkGetPhysicalDeviceExternalBufferProperties"); + glad_vkGetPhysicalDeviceExternalFenceProperties = (PFN_vkGetPhysicalDeviceExternalFenceProperties) load(userptr, "vkGetPhysicalDeviceExternalFenceProperties"); + glad_vkGetPhysicalDeviceExternalSemaphoreProperties = (PFN_vkGetPhysicalDeviceExternalSemaphoreProperties) load(userptr, "vkGetPhysicalDeviceExternalSemaphoreProperties"); + glad_vkGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2) load(userptr, "vkGetPhysicalDeviceFeatures2"); + glad_vkGetPhysicalDeviceFormatProperties2 = (PFN_vkGetPhysicalDeviceFormatProperties2) load(userptr, "vkGetPhysicalDeviceFormatProperties2"); + glad_vkGetPhysicalDeviceImageFormatProperties2 = (PFN_vkGetPhysicalDeviceImageFormatProperties2) load(userptr, "vkGetPhysicalDeviceImageFormatProperties2"); + glad_vkGetPhysicalDeviceMemoryProperties2 = (PFN_vkGetPhysicalDeviceMemoryProperties2) load(userptr, "vkGetPhysicalDeviceMemoryProperties2"); + glad_vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) load(userptr, "vkGetPhysicalDeviceProperties2"); + glad_vkGetPhysicalDeviceQueueFamilyProperties2 = (PFN_vkGetPhysicalDeviceQueueFamilyProperties2) load(userptr, "vkGetPhysicalDeviceQueueFamilyProperties2"); + glad_vkGetPhysicalDeviceSparseImageFormatProperties2 = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties2) load(userptr, "vkGetPhysicalDeviceSparseImageFormatProperties2"); + glad_vkTrimCommandPool = (PFN_vkTrimCommandPool) load(userptr, "vkTrimCommandPool"); + glad_vkUpdateDescriptorSetWithTemplate = (PFN_vkUpdateDescriptorSetWithTemplate) load(userptr, "vkUpdateDescriptorSetWithTemplate"); +} +static void glad_vk_load_VK_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_VERSION_1_2) return; + glad_vkCmdBeginRenderPass2 = (PFN_vkCmdBeginRenderPass2) load(userptr, "vkCmdBeginRenderPass2"); + glad_vkCmdDrawIndexedIndirectCount = (PFN_vkCmdDrawIndexedIndirectCount) load(userptr, "vkCmdDrawIndexedIndirectCount"); + glad_vkCmdDrawIndirectCount = (PFN_vkCmdDrawIndirectCount) load(userptr, "vkCmdDrawIndirectCount"); + glad_vkCmdEndRenderPass2 = (PFN_vkCmdEndRenderPass2) load(userptr, "vkCmdEndRenderPass2"); + glad_vkCmdNextSubpass2 = (PFN_vkCmdNextSubpass2) load(userptr, "vkCmdNextSubpass2"); + glad_vkCreateRenderPass2 = (PFN_vkCreateRenderPass2) load(userptr, "vkCreateRenderPass2"); + glad_vkGetBufferDeviceAddress = (PFN_vkGetBufferDeviceAddress) load(userptr, "vkGetBufferDeviceAddress"); + glad_vkGetBufferOpaqueCaptureAddress = (PFN_vkGetBufferOpaqueCaptureAddress) load(userptr, "vkGetBufferOpaqueCaptureAddress"); + glad_vkGetDeviceMemoryOpaqueCaptureAddress = (PFN_vkGetDeviceMemoryOpaqueCaptureAddress) load(userptr, "vkGetDeviceMemoryOpaqueCaptureAddress"); + glad_vkGetSemaphoreCounterValue = (PFN_vkGetSemaphoreCounterValue) load(userptr, "vkGetSemaphoreCounterValue"); + glad_vkResetQueryPool = (PFN_vkResetQueryPool) load(userptr, "vkResetQueryPool"); + glad_vkSignalSemaphore = (PFN_vkSignalSemaphore) load(userptr, "vkSignalSemaphore"); + glad_vkWaitSemaphores = (PFN_vkWaitSemaphores) load(userptr, "vkWaitSemaphores"); +} +static void glad_vk_load_VK_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_VERSION_1_3) return; + glad_vkCmdBeginRendering = (PFN_vkCmdBeginRendering) load(userptr, "vkCmdBeginRendering"); + glad_vkCmdBindVertexBuffers2 = (PFN_vkCmdBindVertexBuffers2) load(userptr, "vkCmdBindVertexBuffers2"); + glad_vkCmdBlitImage2 = (PFN_vkCmdBlitImage2) load(userptr, "vkCmdBlitImage2"); + glad_vkCmdCopyBuffer2 = (PFN_vkCmdCopyBuffer2) load(userptr, "vkCmdCopyBuffer2"); + glad_vkCmdCopyBufferToImage2 = (PFN_vkCmdCopyBufferToImage2) load(userptr, "vkCmdCopyBufferToImage2"); + glad_vkCmdCopyImage2 = (PFN_vkCmdCopyImage2) load(userptr, "vkCmdCopyImage2"); + glad_vkCmdCopyImageToBuffer2 = (PFN_vkCmdCopyImageToBuffer2) load(userptr, "vkCmdCopyImageToBuffer2"); + glad_vkCmdEndRendering = (PFN_vkCmdEndRendering) load(userptr, "vkCmdEndRendering"); + glad_vkCmdPipelineBarrier2 = (PFN_vkCmdPipelineBarrier2) load(userptr, "vkCmdPipelineBarrier2"); + glad_vkCmdResetEvent2 = (PFN_vkCmdResetEvent2) load(userptr, "vkCmdResetEvent2"); + glad_vkCmdResolveImage2 = (PFN_vkCmdResolveImage2) load(userptr, "vkCmdResolveImage2"); + glad_vkCmdSetCullMode = (PFN_vkCmdSetCullMode) load(userptr, "vkCmdSetCullMode"); + glad_vkCmdSetDepthBiasEnable = (PFN_vkCmdSetDepthBiasEnable) load(userptr, "vkCmdSetDepthBiasEnable"); + glad_vkCmdSetDepthBoundsTestEnable = (PFN_vkCmdSetDepthBoundsTestEnable) load(userptr, "vkCmdSetDepthBoundsTestEnable"); + glad_vkCmdSetDepthCompareOp = (PFN_vkCmdSetDepthCompareOp) load(userptr, "vkCmdSetDepthCompareOp"); + glad_vkCmdSetDepthTestEnable = (PFN_vkCmdSetDepthTestEnable) load(userptr, "vkCmdSetDepthTestEnable"); + glad_vkCmdSetDepthWriteEnable = (PFN_vkCmdSetDepthWriteEnable) load(userptr, "vkCmdSetDepthWriteEnable"); + glad_vkCmdSetEvent2 = (PFN_vkCmdSetEvent2) load(userptr, "vkCmdSetEvent2"); + glad_vkCmdSetFrontFace = (PFN_vkCmdSetFrontFace) load(userptr, "vkCmdSetFrontFace"); + glad_vkCmdSetPrimitiveRestartEnable = (PFN_vkCmdSetPrimitiveRestartEnable) load(userptr, "vkCmdSetPrimitiveRestartEnable"); + glad_vkCmdSetPrimitiveTopology = (PFN_vkCmdSetPrimitiveTopology) load(userptr, "vkCmdSetPrimitiveTopology"); + glad_vkCmdSetRasterizerDiscardEnable = (PFN_vkCmdSetRasterizerDiscardEnable) load(userptr, "vkCmdSetRasterizerDiscardEnable"); + glad_vkCmdSetScissorWithCount = (PFN_vkCmdSetScissorWithCount) load(userptr, "vkCmdSetScissorWithCount"); + glad_vkCmdSetStencilOp = (PFN_vkCmdSetStencilOp) load(userptr, "vkCmdSetStencilOp"); + glad_vkCmdSetStencilTestEnable = (PFN_vkCmdSetStencilTestEnable) load(userptr, "vkCmdSetStencilTestEnable"); + glad_vkCmdSetViewportWithCount = (PFN_vkCmdSetViewportWithCount) load(userptr, "vkCmdSetViewportWithCount"); + glad_vkCmdWaitEvents2 = (PFN_vkCmdWaitEvents2) load(userptr, "vkCmdWaitEvents2"); + glad_vkCmdWriteTimestamp2 = (PFN_vkCmdWriteTimestamp2) load(userptr, "vkCmdWriteTimestamp2"); + glad_vkCreatePrivateDataSlot = (PFN_vkCreatePrivateDataSlot) load(userptr, "vkCreatePrivateDataSlot"); + glad_vkDestroyPrivateDataSlot = (PFN_vkDestroyPrivateDataSlot) load(userptr, "vkDestroyPrivateDataSlot"); + glad_vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements) load(userptr, "vkGetDeviceBufferMemoryRequirements"); + glad_vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements) load(userptr, "vkGetDeviceImageMemoryRequirements"); + glad_vkGetDeviceImageSparseMemoryRequirements = (PFN_vkGetDeviceImageSparseMemoryRequirements) load(userptr, "vkGetDeviceImageSparseMemoryRequirements"); + glad_vkGetPhysicalDeviceToolProperties = (PFN_vkGetPhysicalDeviceToolProperties) load(userptr, "vkGetPhysicalDeviceToolProperties"); + glad_vkGetPrivateData = (PFN_vkGetPrivateData) load(userptr, "vkGetPrivateData"); + glad_vkQueueSubmit2 = (PFN_vkQueueSubmit2) load(userptr, "vkQueueSubmit2"); + glad_vkSetPrivateData = (PFN_vkSetPrivateData) load(userptr, "vkSetPrivateData"); +} +static void glad_vk_load_VK_EXT_debug_report( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_EXT_debug_report) return; + glad_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT) load(userptr, "vkCreateDebugReportCallbackEXT"); + glad_vkDebugReportMessageEXT = (PFN_vkDebugReportMessageEXT) load(userptr, "vkDebugReportMessageEXT"); + glad_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) load(userptr, "vkDestroyDebugReportCallbackEXT"); +} +static void glad_vk_load_VK_KHR_surface( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_KHR_surface) return; + glad_vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR) load(userptr, "vkDestroySurfaceKHR"); + glad_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR) load(userptr, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); + glad_vkGetPhysicalDeviceSurfaceFormatsKHR = (PFN_vkGetPhysicalDeviceSurfaceFormatsKHR) load(userptr, "vkGetPhysicalDeviceSurfaceFormatsKHR"); + glad_vkGetPhysicalDeviceSurfacePresentModesKHR = (PFN_vkGetPhysicalDeviceSurfacePresentModesKHR) load(userptr, "vkGetPhysicalDeviceSurfacePresentModesKHR"); + glad_vkGetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR) load(userptr, "vkGetPhysicalDeviceSurfaceSupportKHR"); +} +static void glad_vk_load_VK_KHR_swapchain( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_VK_KHR_swapchain) return; + glad_vkAcquireNextImage2KHR = (PFN_vkAcquireNextImage2KHR) load(userptr, "vkAcquireNextImage2KHR"); + glad_vkAcquireNextImageKHR = (PFN_vkAcquireNextImageKHR) load(userptr, "vkAcquireNextImageKHR"); + glad_vkCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR) load(userptr, "vkCreateSwapchainKHR"); + glad_vkDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR) load(userptr, "vkDestroySwapchainKHR"); + glad_vkGetDeviceGroupPresentCapabilitiesKHR = (PFN_vkGetDeviceGroupPresentCapabilitiesKHR) load(userptr, "vkGetDeviceGroupPresentCapabilitiesKHR"); + glad_vkGetDeviceGroupSurfacePresentModesKHR = (PFN_vkGetDeviceGroupSurfacePresentModesKHR) load(userptr, "vkGetDeviceGroupSurfacePresentModesKHR"); + glad_vkGetPhysicalDevicePresentRectanglesKHR = (PFN_vkGetPhysicalDevicePresentRectanglesKHR) load(userptr, "vkGetPhysicalDevicePresentRectanglesKHR"); + glad_vkGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR) load(userptr, "vkGetSwapchainImagesKHR"); + glad_vkQueuePresentKHR = (PFN_vkQueuePresentKHR) load(userptr, "vkQueuePresentKHR"); +} + + + +static int glad_vk_get_extensions( VkPhysicalDevice physical_device, uint32_t *out_extension_count, char ***out_extensions) { + uint32_t i; + uint32_t instance_extension_count = 0; + uint32_t device_extension_count = 0; + uint32_t max_extension_count = 0; + uint32_t total_extension_count = 0; + char **extensions = NULL; + VkExtensionProperties *ext_properties = NULL; + VkResult result; + + if (glad_vkEnumerateInstanceExtensionProperties == NULL || (physical_device != NULL && glad_vkEnumerateDeviceExtensionProperties == NULL)) { + return 0; + } + + result = glad_vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, NULL); + if (result != VK_SUCCESS) { + return 0; + } + + if (physical_device != NULL) { + result = glad_vkEnumerateDeviceExtensionProperties(physical_device, NULL, &device_extension_count, NULL); + if (result != VK_SUCCESS) { + return 0; + } + } + + total_extension_count = instance_extension_count + device_extension_count; + if (total_extension_count <= 0) { + return 0; + } + + max_extension_count = instance_extension_count > device_extension_count + ? instance_extension_count : device_extension_count; + + ext_properties = (VkExtensionProperties*) malloc(max_extension_count * sizeof(VkExtensionProperties)); + if (ext_properties == NULL) { + goto glad_vk_get_extensions_error; + } + + result = glad_vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, ext_properties); + if (result != VK_SUCCESS) { + goto glad_vk_get_extensions_error; + } + + extensions = (char**) calloc(total_extension_count, sizeof(char*)); + if (extensions == NULL) { + goto glad_vk_get_extensions_error; + } + + for (i = 0; i < instance_extension_count; ++i) { + VkExtensionProperties ext = ext_properties[i]; + + size_t extension_name_length = strlen(ext.extensionName) + 1; + extensions[i] = (char*) malloc(extension_name_length * sizeof(char)); + if (extensions[i] == NULL) { + goto glad_vk_get_extensions_error; + } + memcpy(extensions[i], ext.extensionName, extension_name_length * sizeof(char)); + } + + if (physical_device != NULL) { + result = glad_vkEnumerateDeviceExtensionProperties(physical_device, NULL, &device_extension_count, ext_properties); + if (result != VK_SUCCESS) { + goto glad_vk_get_extensions_error; + } + + for (i = 0; i < device_extension_count; ++i) { + VkExtensionProperties ext = ext_properties[i]; + + size_t extension_name_length = strlen(ext.extensionName) + 1; + extensions[instance_extension_count + i] = (char*) malloc(extension_name_length * sizeof(char)); + if (extensions[instance_extension_count + i] == NULL) { + goto glad_vk_get_extensions_error; + } + memcpy(extensions[instance_extension_count + i], ext.extensionName, extension_name_length * sizeof(char)); + } + } + + free((void*) ext_properties); + + *out_extension_count = total_extension_count; + *out_extensions = extensions; + + return 1; + +glad_vk_get_extensions_error: + free((void*) ext_properties); + if (extensions != NULL) { + for (i = 0; i < total_extension_count; ++i) { + free((void*) extensions[i]); + } + free(extensions); + } + return 0; +} + +static void glad_vk_free_extensions(uint32_t extension_count, char **extensions) { + uint32_t i; + + for(i = 0; i < extension_count ; ++i) { + free((void*) (extensions[i])); + } + + free((void*) extensions); +} + +static int glad_vk_has_extension(const char *name, uint32_t extension_count, char **extensions) { + uint32_t i; + + for (i = 0; i < extension_count; ++i) { + if(extensions[i] != NULL && strcmp(name, extensions[i]) == 0) { + return 1; + } + } + + return 0; +} + +static GLADapiproc glad_vk_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_vk_find_extensions_vulkan( VkPhysicalDevice physical_device) { + uint32_t extension_count = 0; + char **extensions = NULL; + if (!glad_vk_get_extensions(physical_device, &extension_count, &extensions)) return 0; + + GLAD_VK_EXT_debug_report = glad_vk_has_extension("VK_EXT_debug_report", extension_count, extensions); + GLAD_VK_KHR_portability_enumeration = glad_vk_has_extension("VK_KHR_portability_enumeration", extension_count, extensions); + GLAD_VK_KHR_surface = glad_vk_has_extension("VK_KHR_surface", extension_count, extensions); + GLAD_VK_KHR_swapchain = glad_vk_has_extension("VK_KHR_swapchain", extension_count, extensions); + + (void) glad_vk_has_extension; + + glad_vk_free_extensions(extension_count, extensions); + + return 1; +} + +static int glad_vk_find_core_vulkan( VkPhysicalDevice physical_device) { + int major = 1; + int minor = 0; + +#ifdef VK_VERSION_1_1 + if (glad_vkEnumerateInstanceVersion != NULL) { + uint32_t version; + VkResult result; + + result = glad_vkEnumerateInstanceVersion(&version); + if (result == VK_SUCCESS) { + major = (int) VK_VERSION_MAJOR(version); + minor = (int) VK_VERSION_MINOR(version); + } + } +#endif + + if (physical_device != NULL && glad_vkGetPhysicalDeviceProperties != NULL) { + VkPhysicalDeviceProperties properties; + glad_vkGetPhysicalDeviceProperties(physical_device, &properties); + + major = (int) VK_VERSION_MAJOR(properties.apiVersion); + minor = (int) VK_VERSION_MINOR(properties.apiVersion); + } + + GLAD_VK_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; + GLAD_VK_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; + GLAD_VK_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; + GLAD_VK_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadVulkanUserPtr( VkPhysicalDevice physical_device, GLADuserptrloadfunc load, void *userptr) { + int version; + +#ifdef VK_VERSION_1_1 + glad_vkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion) load(userptr, "vkEnumerateInstanceVersion"); +#endif + version = glad_vk_find_core_vulkan( physical_device); + if (!version) { + return 0; + } + + glad_vk_load_VK_VERSION_1_0(load, userptr); + glad_vk_load_VK_VERSION_1_1(load, userptr); + glad_vk_load_VK_VERSION_1_2(load, userptr); + glad_vk_load_VK_VERSION_1_3(load, userptr); + + if (!glad_vk_find_extensions_vulkan( physical_device)) return 0; + glad_vk_load_VK_EXT_debug_report(load, userptr); + glad_vk_load_VK_KHR_surface(load, userptr); + glad_vk_load_VK_KHR_swapchain(load, userptr); + + + return version; +} + + +int gladLoadVulkan( VkPhysicalDevice physical_device, GLADloadfunc load) { + return gladLoadVulkanUserPtr( physical_device, glad_vk_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + #ifdef __cplusplus } #endif -#endif + +#endif /* GLAD_VULKAN_IMPLEMENTATION */ + diff --git a/src/external/glfw/deps/glad_gl.c b/src/external/glfw/deps/glad_gl.c deleted file mode 100644 index 2d4c87fe0..000000000 --- a/src/external/glfw/deps/glad_gl.c +++ /dev/null @@ -1,1791 +0,0 @@ -#include -#include -#include -#include - -#ifndef GLAD_IMPL_UTIL_C_ -#define GLAD_IMPL_UTIL_C_ - -#ifdef _MSC_VER -#define GLAD_IMPL_UTIL_SSCANF sscanf_s -#else -#define GLAD_IMPL_UTIL_SSCANF sscanf -#endif - -#endif /* GLAD_IMPL_UTIL_C_ */ - - -int GLAD_GL_VERSION_1_0 = 0; -int GLAD_GL_VERSION_1_1 = 0; -int GLAD_GL_VERSION_1_2 = 0; -int GLAD_GL_VERSION_1_3 = 0; -int GLAD_GL_VERSION_1_4 = 0; -int GLAD_GL_VERSION_1_5 = 0; -int GLAD_GL_VERSION_2_0 = 0; -int GLAD_GL_VERSION_2_1 = 0; -int GLAD_GL_VERSION_3_0 = 0; -int GLAD_GL_VERSION_3_1 = 0; -int GLAD_GL_VERSION_3_2 = 0; -int GLAD_GL_VERSION_3_3 = 0; -int GLAD_GL_ARB_multisample = 0; -int GLAD_GL_ARB_robustness = 0; -int GLAD_GL_KHR_debug = 0; - - - -PFNGLACCUMPROC glad_glAccum = NULL; -PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; -PFNGLALPHAFUNCPROC glad_glAlphaFunc = NULL; -PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident = NULL; -PFNGLARRAYELEMENTPROC glad_glArrayElement = NULL; -PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; -PFNGLBEGINPROC glad_glBegin = NULL; -PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL; -PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; -PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; -PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; -PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; -PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; -PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; -PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL; -PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL; -PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; -PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; -PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL; -PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; -PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; -PFNGLBITMAPPROC glad_glBitmap = NULL; -PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; -PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; -PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; -PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; -PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; -PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; -PFNGLBUFFERDATAPROC glad_glBufferData = NULL; -PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; -PFNGLCALLLISTPROC glad_glCallList = NULL; -PFNGLCALLLISTSPROC glad_glCallLists = NULL; -PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; -PFNGLCLAMPCOLORPROC glad_glClampColor = NULL; -PFNGLCLEARPROC glad_glClear = NULL; -PFNGLCLEARACCUMPROC glad_glClearAccum = NULL; -PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; -PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; -PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; -PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; -PFNGLCLEARCOLORPROC glad_glClearColor = NULL; -PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL; -PFNGLCLEARINDEXPROC glad_glClearIndex = NULL; -PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; -PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture = NULL; -PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL; -PFNGLCLIPPLANEPROC glad_glClipPlane = NULL; -PFNGLCOLOR3BPROC glad_glColor3b = NULL; -PFNGLCOLOR3BVPROC glad_glColor3bv = NULL; -PFNGLCOLOR3DPROC glad_glColor3d = NULL; -PFNGLCOLOR3DVPROC glad_glColor3dv = NULL; -PFNGLCOLOR3FPROC glad_glColor3f = NULL; -PFNGLCOLOR3FVPROC glad_glColor3fv = NULL; -PFNGLCOLOR3IPROC glad_glColor3i = NULL; -PFNGLCOLOR3IVPROC glad_glColor3iv = NULL; -PFNGLCOLOR3SPROC glad_glColor3s = NULL; -PFNGLCOLOR3SVPROC glad_glColor3sv = NULL; -PFNGLCOLOR3UBPROC glad_glColor3ub = NULL; -PFNGLCOLOR3UBVPROC glad_glColor3ubv = NULL; -PFNGLCOLOR3UIPROC glad_glColor3ui = NULL; -PFNGLCOLOR3UIVPROC glad_glColor3uiv = NULL; -PFNGLCOLOR3USPROC glad_glColor3us = NULL; -PFNGLCOLOR3USVPROC glad_glColor3usv = NULL; -PFNGLCOLOR4BPROC glad_glColor4b = NULL; -PFNGLCOLOR4BVPROC glad_glColor4bv = NULL; -PFNGLCOLOR4DPROC glad_glColor4d = NULL; -PFNGLCOLOR4DVPROC glad_glColor4dv = NULL; -PFNGLCOLOR4FPROC glad_glColor4f = NULL; -PFNGLCOLOR4FVPROC glad_glColor4fv = NULL; -PFNGLCOLOR4IPROC glad_glColor4i = NULL; -PFNGLCOLOR4IVPROC glad_glColor4iv = NULL; -PFNGLCOLOR4SPROC glad_glColor4s = NULL; -PFNGLCOLOR4SVPROC glad_glColor4sv = NULL; -PFNGLCOLOR4UBPROC glad_glColor4ub = NULL; -PFNGLCOLOR4UBVPROC glad_glColor4ubv = NULL; -PFNGLCOLOR4UIPROC glad_glColor4ui = NULL; -PFNGLCOLOR4UIVPROC glad_glColor4uiv = NULL; -PFNGLCOLOR4USPROC glad_glColor4us = NULL; -PFNGLCOLOR4USVPROC glad_glColor4usv = NULL; -PFNGLCOLORMASKPROC glad_glColorMask = NULL; -PFNGLCOLORMASKIPROC glad_glColorMaski = NULL; -PFNGLCOLORMATERIALPROC glad_glColorMaterial = NULL; -PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL; -PFNGLCOLORP3UIVPROC glad_glColorP3uiv = NULL; -PFNGLCOLORP4UIPROC glad_glColorP4ui = NULL; -PFNGLCOLORP4UIVPROC glad_glColorP4uiv = NULL; -PFNGLCOLORPOINTERPROC glad_glColorPointer = NULL; -PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; -PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL; -PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; -PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; -PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL; -PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; -PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; -PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; -PFNGLCOPYPIXELSPROC glad_glCopyPixels = NULL; -PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL; -PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; -PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL; -PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; -PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; -PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; -PFNGLCREATESHADERPROC glad_glCreateShader = NULL; -PFNGLCULLFACEPROC glad_glCullFace = NULL; -PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL; -PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL; -PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL; -PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; -PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; -PFNGLDELETELISTSPROC glad_glDeleteLists = NULL; -PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; -PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; -PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; -PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL; -PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; -PFNGLDELETESYNCPROC glad_glDeleteSync = NULL; -PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; -PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; -PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; -PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; -PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL; -PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; -PFNGLDISABLEPROC glad_glDisable = NULL; -PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState = NULL; -PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; -PFNGLDISABLEIPROC glad_glDisablei = NULL; -PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; -PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; -PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; -PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; -PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; -PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL; -PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; -PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; -PFNGLDRAWPIXELSPROC glad_glDrawPixels = NULL; -PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; -PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; -PFNGLEDGEFLAGPROC glad_glEdgeFlag = NULL; -PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer = NULL; -PFNGLEDGEFLAGVPROC glad_glEdgeFlagv = NULL; -PFNGLENABLEPROC glad_glEnable = NULL; -PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState = NULL; -PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; -PFNGLENABLEIPROC glad_glEnablei = NULL; -PFNGLENDPROC glad_glEnd = NULL; -PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL; -PFNGLENDLISTPROC glad_glEndList = NULL; -PFNGLENDQUERYPROC glad_glEndQuery = NULL; -PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; -PFNGLEVALCOORD1DPROC glad_glEvalCoord1d = NULL; -PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv = NULL; -PFNGLEVALCOORD1FPROC glad_glEvalCoord1f = NULL; -PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv = NULL; -PFNGLEVALCOORD2DPROC glad_glEvalCoord2d = NULL; -PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv = NULL; -PFNGLEVALCOORD2FPROC glad_glEvalCoord2f = NULL; -PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv = NULL; -PFNGLEVALMESH1PROC glad_glEvalMesh1 = NULL; -PFNGLEVALMESH2PROC glad_glEvalMesh2 = NULL; -PFNGLEVALPOINT1PROC glad_glEvalPoint1 = NULL; -PFNGLEVALPOINT2PROC glad_glEvalPoint2 = NULL; -PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer = NULL; -PFNGLFENCESYNCPROC glad_glFenceSync = NULL; -PFNGLFINISHPROC glad_glFinish = NULL; -PFNGLFLUSHPROC glad_glFlush = NULL; -PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; -PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer = NULL; -PFNGLFOGCOORDDPROC glad_glFogCoordd = NULL; -PFNGLFOGCOORDDVPROC glad_glFogCoorddv = NULL; -PFNGLFOGCOORDFPROC glad_glFogCoordf = NULL; -PFNGLFOGCOORDFVPROC glad_glFogCoordfv = NULL; -PFNGLFOGFPROC glad_glFogf = NULL; -PFNGLFOGFVPROC glad_glFogfv = NULL; -PFNGLFOGIPROC glad_glFogi = NULL; -PFNGLFOGIVPROC glad_glFogiv = NULL; -PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; -PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL; -PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL; -PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; -PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL; -PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; -PFNGLFRONTFACEPROC glad_glFrontFace = NULL; -PFNGLFRUSTUMPROC glad_glFrustum = NULL; -PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; -PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; -PFNGLGENLISTSPROC glad_glGenLists = NULL; -PFNGLGENQUERIESPROC glad_glGenQueries = NULL; -PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; -PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL; -PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; -PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; -PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; -PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; -PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; -PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; -PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; -PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL; -PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; -PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; -PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; -PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL; -PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; -PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL; -PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; -PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; -PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL; -PFNGLGETCLIPPLANEPROC glad_glGetClipPlane = NULL; -PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL; -PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL; -PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL; -PFNGLGETERRORPROC glad_glGetError = NULL; -PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; -PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL; -PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; -PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; -PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB = NULL; -PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL; -PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL; -PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; -PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; -PFNGLGETLIGHTFVPROC glad_glGetLightfv = NULL; -PFNGLGETLIGHTIVPROC glad_glGetLightiv = NULL; -PFNGLGETMAPDVPROC glad_glGetMapdv = NULL; -PFNGLGETMAPFVPROC glad_glGetMapfv = NULL; -PFNGLGETMAPIVPROC glad_glGetMapiv = NULL; -PFNGLGETMATERIALFVPROC glad_glGetMaterialfv = NULL; -PFNGLGETMATERIALIVPROC glad_glGetMaterialiv = NULL; -PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL; -PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL; -PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL; -PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv = NULL; -PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv = NULL; -PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv = NULL; -PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL; -PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple = NULL; -PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; -PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; -PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL; -PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL; -PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL; -PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; -PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; -PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; -PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL; -PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL; -PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL; -PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL; -PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; -PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; -PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; -PFNGLGETSTRINGPROC glad_glGetString = NULL; -PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; -PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL; -PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv = NULL; -PFNGLGETTEXENVIVPROC glad_glGetTexEnviv = NULL; -PFNGLGETTEXGENDVPROC glad_glGetTexGendv = NULL; -PFNGLGETTEXGENFVPROC glad_glGetTexGenfv = NULL; -PFNGLGETTEXGENIVPROC glad_glGetTexGeniv = NULL; -PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL; -PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL; -PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL; -PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL; -PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL; -PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; -PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; -PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; -PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; -PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; -PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; -PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; -PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; -PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; -PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; -PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; -PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; -PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL; -PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; -PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; -PFNGLGETNCOLORTABLEARBPROC glad_glGetnColorTableARB = NULL; -PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB = NULL; -PFNGLGETNCONVOLUTIONFILTERARBPROC glad_glGetnConvolutionFilterARB = NULL; -PFNGLGETNHISTOGRAMARBPROC glad_glGetnHistogramARB = NULL; -PFNGLGETNMAPDVARBPROC glad_glGetnMapdvARB = NULL; -PFNGLGETNMAPFVARBPROC glad_glGetnMapfvARB = NULL; -PFNGLGETNMAPIVARBPROC glad_glGetnMapivARB = NULL; -PFNGLGETNMINMAXARBPROC glad_glGetnMinmaxARB = NULL; -PFNGLGETNPIXELMAPFVARBPROC glad_glGetnPixelMapfvARB = NULL; -PFNGLGETNPIXELMAPUIVARBPROC glad_glGetnPixelMapuivARB = NULL; -PFNGLGETNPIXELMAPUSVARBPROC glad_glGetnPixelMapusvARB = NULL; -PFNGLGETNPOLYGONSTIPPLEARBPROC glad_glGetnPolygonStippleARB = NULL; -PFNGLGETNSEPARABLEFILTERARBPROC glad_glGetnSeparableFilterARB = NULL; -PFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB = NULL; -PFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB = NULL; -PFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB = NULL; -PFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB = NULL; -PFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB = NULL; -PFNGLHINTPROC glad_glHint = NULL; -PFNGLINDEXMASKPROC glad_glIndexMask = NULL; -PFNGLINDEXPOINTERPROC glad_glIndexPointer = NULL; -PFNGLINDEXDPROC glad_glIndexd = NULL; -PFNGLINDEXDVPROC glad_glIndexdv = NULL; -PFNGLINDEXFPROC glad_glIndexf = NULL; -PFNGLINDEXFVPROC glad_glIndexfv = NULL; -PFNGLINDEXIPROC glad_glIndexi = NULL; -PFNGLINDEXIVPROC glad_glIndexiv = NULL; -PFNGLINDEXSPROC glad_glIndexs = NULL; -PFNGLINDEXSVPROC glad_glIndexsv = NULL; -PFNGLINDEXUBPROC glad_glIndexub = NULL; -PFNGLINDEXUBVPROC glad_glIndexubv = NULL; -PFNGLINITNAMESPROC glad_glInitNames = NULL; -PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays = NULL; -PFNGLISBUFFERPROC glad_glIsBuffer = NULL; -PFNGLISENABLEDPROC glad_glIsEnabled = NULL; -PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL; -PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; -PFNGLISLISTPROC glad_glIsList = NULL; -PFNGLISPROGRAMPROC glad_glIsProgram = NULL; -PFNGLISQUERYPROC glad_glIsQuery = NULL; -PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; -PFNGLISSAMPLERPROC glad_glIsSampler = NULL; -PFNGLISSHADERPROC glad_glIsShader = NULL; -PFNGLISSYNCPROC glad_glIsSync = NULL; -PFNGLISTEXTUREPROC glad_glIsTexture = NULL; -PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; -PFNGLLIGHTMODELFPROC glad_glLightModelf = NULL; -PFNGLLIGHTMODELFVPROC glad_glLightModelfv = NULL; -PFNGLLIGHTMODELIPROC glad_glLightModeli = NULL; -PFNGLLIGHTMODELIVPROC glad_glLightModeliv = NULL; -PFNGLLIGHTFPROC glad_glLightf = NULL; -PFNGLLIGHTFVPROC glad_glLightfv = NULL; -PFNGLLIGHTIPROC glad_glLighti = NULL; -PFNGLLIGHTIVPROC glad_glLightiv = NULL; -PFNGLLINESTIPPLEPROC glad_glLineStipple = NULL; -PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; -PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; -PFNGLLISTBASEPROC glad_glListBase = NULL; -PFNGLLOADIDENTITYPROC glad_glLoadIdentity = NULL; -PFNGLLOADMATRIXDPROC glad_glLoadMatrixd = NULL; -PFNGLLOADMATRIXFPROC glad_glLoadMatrixf = NULL; -PFNGLLOADNAMEPROC glad_glLoadName = NULL; -PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd = NULL; -PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf = NULL; -PFNGLLOGICOPPROC glad_glLogicOp = NULL; -PFNGLMAP1DPROC glad_glMap1d = NULL; -PFNGLMAP1FPROC glad_glMap1f = NULL; -PFNGLMAP2DPROC glad_glMap2d = NULL; -PFNGLMAP2FPROC glad_glMap2f = NULL; -PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL; -PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; -PFNGLMAPGRID1DPROC glad_glMapGrid1d = NULL; -PFNGLMAPGRID1FPROC glad_glMapGrid1f = NULL; -PFNGLMAPGRID2DPROC glad_glMapGrid2d = NULL; -PFNGLMAPGRID2FPROC glad_glMapGrid2f = NULL; -PFNGLMATERIALFPROC glad_glMaterialf = NULL; -PFNGLMATERIALFVPROC glad_glMaterialfv = NULL; -PFNGLMATERIALIPROC glad_glMateriali = NULL; -PFNGLMATERIALIVPROC glad_glMaterialiv = NULL; -PFNGLMATRIXMODEPROC glad_glMatrixMode = NULL; -PFNGLMULTMATRIXDPROC glad_glMultMatrixd = NULL; -PFNGLMULTMATRIXFPROC glad_glMultMatrixf = NULL; -PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd = NULL; -PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf = NULL; -PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL; -PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL; -PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL; -PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d = NULL; -PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv = NULL; -PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f = NULL; -PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv = NULL; -PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i = NULL; -PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv = NULL; -PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s = NULL; -PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv = NULL; -PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d = NULL; -PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv = NULL; -PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f = NULL; -PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv = NULL; -PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i = NULL; -PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv = NULL; -PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s = NULL; -PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv = NULL; -PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d = NULL; -PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv = NULL; -PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f = NULL; -PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv = NULL; -PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i = NULL; -PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv = NULL; -PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s = NULL; -PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv = NULL; -PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d = NULL; -PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv = NULL; -PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f = NULL; -PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv = NULL; -PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i = NULL; -PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv = NULL; -PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s = NULL; -PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv = NULL; -PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL; -PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL; -PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL; -PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv = NULL; -PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL; -PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL; -PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL; -PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL; -PFNGLNEWLISTPROC glad_glNewList = NULL; -PFNGLNORMAL3BPROC glad_glNormal3b = NULL; -PFNGLNORMAL3BVPROC glad_glNormal3bv = NULL; -PFNGLNORMAL3DPROC glad_glNormal3d = NULL; -PFNGLNORMAL3DVPROC glad_glNormal3dv = NULL; -PFNGLNORMAL3FPROC glad_glNormal3f = NULL; -PFNGLNORMAL3FVPROC glad_glNormal3fv = NULL; -PFNGLNORMAL3IPROC glad_glNormal3i = NULL; -PFNGLNORMAL3IVPROC glad_glNormal3iv = NULL; -PFNGLNORMAL3SPROC glad_glNormal3s = NULL; -PFNGLNORMAL3SVPROC glad_glNormal3sv = NULL; -PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL; -PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL; -PFNGLNORMALPOINTERPROC glad_glNormalPointer = NULL; -PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL; -PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL; -PFNGLORTHOPROC glad_glOrtho = NULL; -PFNGLPASSTHROUGHPROC glad_glPassThrough = NULL; -PFNGLPIXELMAPFVPROC glad_glPixelMapfv = NULL; -PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv = NULL; -PFNGLPIXELMAPUSVPROC glad_glPixelMapusv = NULL; -PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL; -PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; -PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf = NULL; -PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi = NULL; -PFNGLPIXELZOOMPROC glad_glPixelZoom = NULL; -PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL; -PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL; -PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL; -PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL; -PFNGLPOINTSIZEPROC glad_glPointSize = NULL; -PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL; -PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; -PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple = NULL; -PFNGLPOPATTRIBPROC glad_glPopAttrib = NULL; -PFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib = NULL; -PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL; -PFNGLPOPMATRIXPROC glad_glPopMatrix = NULL; -PFNGLPOPNAMEPROC glad_glPopName = NULL; -PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL; -PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures = NULL; -PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL; -PFNGLPUSHATTRIBPROC glad_glPushAttrib = NULL; -PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib = NULL; -PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL; -PFNGLPUSHMATRIXPROC glad_glPushMatrix = NULL; -PFNGLPUSHNAMEPROC glad_glPushName = NULL; -PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL; -PFNGLRASTERPOS2DPROC glad_glRasterPos2d = NULL; -PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv = NULL; -PFNGLRASTERPOS2FPROC glad_glRasterPos2f = NULL; -PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv = NULL; -PFNGLRASTERPOS2IPROC glad_glRasterPos2i = NULL; -PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv = NULL; -PFNGLRASTERPOS2SPROC glad_glRasterPos2s = NULL; -PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv = NULL; -PFNGLRASTERPOS3DPROC glad_glRasterPos3d = NULL; -PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv = NULL; -PFNGLRASTERPOS3FPROC glad_glRasterPos3f = NULL; -PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv = NULL; -PFNGLRASTERPOS3IPROC glad_glRasterPos3i = NULL; -PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv = NULL; -PFNGLRASTERPOS3SPROC glad_glRasterPos3s = NULL; -PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv = NULL; -PFNGLRASTERPOS4DPROC glad_glRasterPos4d = NULL; -PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv = NULL; -PFNGLRASTERPOS4FPROC glad_glRasterPos4f = NULL; -PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv = NULL; -PFNGLRASTERPOS4IPROC glad_glRasterPos4i = NULL; -PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv = NULL; -PFNGLRASTERPOS4SPROC glad_glRasterPos4s = NULL; -PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv = NULL; -PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; -PFNGLREADPIXELSPROC glad_glReadPixels = NULL; -PFNGLREADNPIXELSPROC glad_glReadnPixels = NULL; -PFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB = NULL; -PFNGLRECTDPROC glad_glRectd = NULL; -PFNGLRECTDVPROC glad_glRectdv = NULL; -PFNGLRECTFPROC glad_glRectf = NULL; -PFNGLRECTFVPROC glad_glRectfv = NULL; -PFNGLRECTIPROC glad_glRecti = NULL; -PFNGLRECTIVPROC glad_glRectiv = NULL; -PFNGLRECTSPROC glad_glRects = NULL; -PFNGLRECTSVPROC glad_glRectsv = NULL; -PFNGLRENDERMODEPROC glad_glRenderMode = NULL; -PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; -PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; -PFNGLROTATEDPROC glad_glRotated = NULL; -PFNGLROTATEFPROC glad_glRotatef = NULL; -PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; -PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB = NULL; -PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL; -PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL; -PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL; -PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL; -PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL; -PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL; -PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL; -PFNGLSCALEDPROC glad_glScaled = NULL; -PFNGLSCALEFPROC glad_glScalef = NULL; -PFNGLSCISSORPROC glad_glScissor = NULL; -PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b = NULL; -PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv = NULL; -PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d = NULL; -PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv = NULL; -PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f = NULL; -PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv = NULL; -PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i = NULL; -PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv = NULL; -PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s = NULL; -PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv = NULL; -PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub = NULL; -PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv = NULL; -PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui = NULL; -PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv = NULL; -PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us = NULL; -PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv = NULL; -PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui = NULL; -PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL; -PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer = NULL; -PFNGLSELECTBUFFERPROC glad_glSelectBuffer = NULL; -PFNGLSHADEMODELPROC glad_glShadeModel = NULL; -PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; -PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; -PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; -PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; -PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; -PFNGLSTENCILOPPROC glad_glStencilOp = NULL; -PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; -PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL; -PFNGLTEXCOORD1DPROC glad_glTexCoord1d = NULL; -PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv = NULL; -PFNGLTEXCOORD1FPROC glad_glTexCoord1f = NULL; -PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv = NULL; -PFNGLTEXCOORD1IPROC glad_glTexCoord1i = NULL; -PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv = NULL; -PFNGLTEXCOORD1SPROC glad_glTexCoord1s = NULL; -PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv = NULL; -PFNGLTEXCOORD2DPROC glad_glTexCoord2d = NULL; -PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv = NULL; -PFNGLTEXCOORD2FPROC glad_glTexCoord2f = NULL; -PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv = NULL; -PFNGLTEXCOORD2IPROC glad_glTexCoord2i = NULL; -PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv = NULL; -PFNGLTEXCOORD2SPROC glad_glTexCoord2s = NULL; -PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv = NULL; -PFNGLTEXCOORD3DPROC glad_glTexCoord3d = NULL; -PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv = NULL; -PFNGLTEXCOORD3FPROC glad_glTexCoord3f = NULL; -PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv = NULL; -PFNGLTEXCOORD3IPROC glad_glTexCoord3i = NULL; -PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv = NULL; -PFNGLTEXCOORD3SPROC glad_glTexCoord3s = NULL; -PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv = NULL; -PFNGLTEXCOORD4DPROC glad_glTexCoord4d = NULL; -PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv = NULL; -PFNGLTEXCOORD4FPROC glad_glTexCoord4f = NULL; -PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv = NULL; -PFNGLTEXCOORD4IPROC glad_glTexCoord4i = NULL; -PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv = NULL; -PFNGLTEXCOORD4SPROC glad_glTexCoord4s = NULL; -PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv = NULL; -PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui = NULL; -PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv = NULL; -PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui = NULL; -PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv = NULL; -PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui = NULL; -PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv = NULL; -PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui = NULL; -PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv = NULL; -PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer = NULL; -PFNGLTEXENVFPROC glad_glTexEnvf = NULL; -PFNGLTEXENVFVPROC glad_glTexEnvfv = NULL; -PFNGLTEXENVIPROC glad_glTexEnvi = NULL; -PFNGLTEXENVIVPROC glad_glTexEnviv = NULL; -PFNGLTEXGENDPROC glad_glTexGend = NULL; -PFNGLTEXGENDVPROC glad_glTexGendv = NULL; -PFNGLTEXGENFPROC glad_glTexGenf = NULL; -PFNGLTEXGENFVPROC glad_glTexGenfv = NULL; -PFNGLTEXGENIPROC glad_glTexGeni = NULL; -PFNGLTEXGENIVPROC glad_glTexGeniv = NULL; -PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL; -PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; -PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL; -PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; -PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL; -PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL; -PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL; -PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; -PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; -PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; -PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; -PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL; -PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; -PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; -PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; -PFNGLTRANSLATEDPROC glad_glTranslated = NULL; -PFNGLTRANSLATEFPROC glad_glTranslatef = NULL; -PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; -PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; -PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; -PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; -PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; -PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; -PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; -PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; -PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; -PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; -PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; -PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; -PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; -PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; -PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; -PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; -PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; -PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; -PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; -PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; -PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; -PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; -PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; -PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; -PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; -PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; -PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; -PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; -PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; -PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; -PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; -PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; -PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; -PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; -PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; -PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; -PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; -PFNGLVERTEX2DPROC glad_glVertex2d = NULL; -PFNGLVERTEX2DVPROC glad_glVertex2dv = NULL; -PFNGLVERTEX2FPROC glad_glVertex2f = NULL; -PFNGLVERTEX2FVPROC glad_glVertex2fv = NULL; -PFNGLVERTEX2IPROC glad_glVertex2i = NULL; -PFNGLVERTEX2IVPROC glad_glVertex2iv = NULL; -PFNGLVERTEX2SPROC glad_glVertex2s = NULL; -PFNGLVERTEX2SVPROC glad_glVertex2sv = NULL; -PFNGLVERTEX3DPROC glad_glVertex3d = NULL; -PFNGLVERTEX3DVPROC glad_glVertex3dv = NULL; -PFNGLVERTEX3FPROC glad_glVertex3f = NULL; -PFNGLVERTEX3FVPROC glad_glVertex3fv = NULL; -PFNGLVERTEX3IPROC glad_glVertex3i = NULL; -PFNGLVERTEX3IVPROC glad_glVertex3iv = NULL; -PFNGLVERTEX3SPROC glad_glVertex3s = NULL; -PFNGLVERTEX3SVPROC glad_glVertex3sv = NULL; -PFNGLVERTEX4DPROC glad_glVertex4d = NULL; -PFNGLVERTEX4DVPROC glad_glVertex4dv = NULL; -PFNGLVERTEX4FPROC glad_glVertex4f = NULL; -PFNGLVERTEX4FVPROC glad_glVertex4fv = NULL; -PFNGLVERTEX4IPROC glad_glVertex4i = NULL; -PFNGLVERTEX4IVPROC glad_glVertex4iv = NULL; -PFNGLVERTEX4SPROC glad_glVertex4s = NULL; -PFNGLVERTEX4SVPROC glad_glVertex4sv = NULL; -PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL; -PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL; -PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; -PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; -PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL; -PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL; -PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL; -PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL; -PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; -PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; -PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL; -PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL; -PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL; -PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL; -PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; -PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; -PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL; -PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL; -PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL; -PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL; -PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL; -PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL; -PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL; -PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL; -PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL; -PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL; -PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL; -PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL; -PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; -PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; -PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL; -PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL; -PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL; -PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL; -PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL; -PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL; -PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL; -PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL; -PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL; -PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL; -PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL; -PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL; -PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL; -PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL; -PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL; -PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL; -PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL; -PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL; -PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL; -PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL; -PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; -PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; -PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL; -PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL; -PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; -PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; -PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL; -PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; -PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL; -PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL; -PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL; -PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL; -PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL; -PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL; -PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL; -PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL; -PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; -PFNGLVERTEXP2UIPROC glad_glVertexP2ui = NULL; -PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv = NULL; -PFNGLVERTEXP3UIPROC glad_glVertexP3ui = NULL; -PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv = NULL; -PFNGLVERTEXP4UIPROC glad_glVertexP4ui = NULL; -PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv = NULL; -PFNGLVERTEXPOINTERPROC glad_glVertexPointer = NULL; -PFNGLVIEWPORTPROC glad_glViewport = NULL; -PFNGLWAITSYNCPROC glad_glWaitSync = NULL; -PFNGLWINDOWPOS2DPROC glad_glWindowPos2d = NULL; -PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv = NULL; -PFNGLWINDOWPOS2FPROC glad_glWindowPos2f = NULL; -PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv = NULL; -PFNGLWINDOWPOS2IPROC glad_glWindowPos2i = NULL; -PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv = NULL; -PFNGLWINDOWPOS2SPROC glad_glWindowPos2s = NULL; -PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv = NULL; -PFNGLWINDOWPOS3DPROC glad_glWindowPos3d = NULL; -PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv = NULL; -PFNGLWINDOWPOS3FPROC glad_glWindowPos3f = NULL; -PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv = NULL; -PFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL; -PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL; -PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL; -PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL; - - -static void glad_gl_load_GL_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_1_0) return; - glAccum = (PFNGLACCUMPROC) load("glAccum", userptr); - glAlphaFunc = (PFNGLALPHAFUNCPROC) load("glAlphaFunc", userptr); - glBegin = (PFNGLBEGINPROC) load("glBegin", userptr); - glBitmap = (PFNGLBITMAPPROC) load("glBitmap", userptr); - glBlendFunc = (PFNGLBLENDFUNCPROC) load("glBlendFunc", userptr); - glCallList = (PFNGLCALLLISTPROC) load("glCallList", userptr); - glCallLists = (PFNGLCALLLISTSPROC) load("glCallLists", userptr); - glClear = (PFNGLCLEARPROC) load("glClear", userptr); - glClearAccum = (PFNGLCLEARACCUMPROC) load("glClearAccum", userptr); - glClearColor = (PFNGLCLEARCOLORPROC) load("glClearColor", userptr); - glClearDepth = (PFNGLCLEARDEPTHPROC) load("glClearDepth", userptr); - glClearIndex = (PFNGLCLEARINDEXPROC) load("glClearIndex", userptr); - glClearStencil = (PFNGLCLEARSTENCILPROC) load("glClearStencil", userptr); - glClipPlane = (PFNGLCLIPPLANEPROC) load("glClipPlane", userptr); - glColor3b = (PFNGLCOLOR3BPROC) load("glColor3b", userptr); - glColor3bv = (PFNGLCOLOR3BVPROC) load("glColor3bv", userptr); - glColor3d = (PFNGLCOLOR3DPROC) load("glColor3d", userptr); - glColor3dv = (PFNGLCOLOR3DVPROC) load("glColor3dv", userptr); - glColor3f = (PFNGLCOLOR3FPROC) load("glColor3f", userptr); - glColor3fv = (PFNGLCOLOR3FVPROC) load("glColor3fv", userptr); - glColor3i = (PFNGLCOLOR3IPROC) load("glColor3i", userptr); - glColor3iv = (PFNGLCOLOR3IVPROC) load("glColor3iv", userptr); - glColor3s = (PFNGLCOLOR3SPROC) load("glColor3s", userptr); - glColor3sv = (PFNGLCOLOR3SVPROC) load("glColor3sv", userptr); - glColor3ub = (PFNGLCOLOR3UBPROC) load("glColor3ub", userptr); - glColor3ubv = (PFNGLCOLOR3UBVPROC) load("glColor3ubv", userptr); - glColor3ui = (PFNGLCOLOR3UIPROC) load("glColor3ui", userptr); - glColor3uiv = (PFNGLCOLOR3UIVPROC) load("glColor3uiv", userptr); - glColor3us = (PFNGLCOLOR3USPROC) load("glColor3us", userptr); - glColor3usv = (PFNGLCOLOR3USVPROC) load("glColor3usv", userptr); - glColor4b = (PFNGLCOLOR4BPROC) load("glColor4b", userptr); - glColor4bv = (PFNGLCOLOR4BVPROC) load("glColor4bv", userptr); - glColor4d = (PFNGLCOLOR4DPROC) load("glColor4d", userptr); - glColor4dv = (PFNGLCOLOR4DVPROC) load("glColor4dv", userptr); - glColor4f = (PFNGLCOLOR4FPROC) load("glColor4f", userptr); - glColor4fv = (PFNGLCOLOR4FVPROC) load("glColor4fv", userptr); - glColor4i = (PFNGLCOLOR4IPROC) load("glColor4i", userptr); - glColor4iv = (PFNGLCOLOR4IVPROC) load("glColor4iv", userptr); - glColor4s = (PFNGLCOLOR4SPROC) load("glColor4s", userptr); - glColor4sv = (PFNGLCOLOR4SVPROC) load("glColor4sv", userptr); - glColor4ub = (PFNGLCOLOR4UBPROC) load("glColor4ub", userptr); - glColor4ubv = (PFNGLCOLOR4UBVPROC) load("glColor4ubv", userptr); - glColor4ui = (PFNGLCOLOR4UIPROC) load("glColor4ui", userptr); - glColor4uiv = (PFNGLCOLOR4UIVPROC) load("glColor4uiv", userptr); - glColor4us = (PFNGLCOLOR4USPROC) load("glColor4us", userptr); - glColor4usv = (PFNGLCOLOR4USVPROC) load("glColor4usv", userptr); - glColorMask = (PFNGLCOLORMASKPROC) load("glColorMask", userptr); - glColorMaterial = (PFNGLCOLORMATERIALPROC) load("glColorMaterial", userptr); - glCopyPixels = (PFNGLCOPYPIXELSPROC) load("glCopyPixels", userptr); - glCullFace = (PFNGLCULLFACEPROC) load("glCullFace", userptr); - glDeleteLists = (PFNGLDELETELISTSPROC) load("glDeleteLists", userptr); - glDepthFunc = (PFNGLDEPTHFUNCPROC) load("glDepthFunc", userptr); - glDepthMask = (PFNGLDEPTHMASKPROC) load("glDepthMask", userptr); - glDepthRange = (PFNGLDEPTHRANGEPROC) load("glDepthRange", userptr); - glDisable = (PFNGLDISABLEPROC) load("glDisable", userptr); - glDrawBuffer = (PFNGLDRAWBUFFERPROC) load("glDrawBuffer", userptr); - glDrawPixels = (PFNGLDRAWPIXELSPROC) load("glDrawPixels", userptr); - glEdgeFlag = (PFNGLEDGEFLAGPROC) load("glEdgeFlag", userptr); - glEdgeFlagv = (PFNGLEDGEFLAGVPROC) load("glEdgeFlagv", userptr); - glEnable = (PFNGLENABLEPROC) load("glEnable", userptr); - glEnd = (PFNGLENDPROC) load("glEnd", userptr); - glEndList = (PFNGLENDLISTPROC) load("glEndList", userptr); - glEvalCoord1d = (PFNGLEVALCOORD1DPROC) load("glEvalCoord1d", userptr); - glEvalCoord1dv = (PFNGLEVALCOORD1DVPROC) load("glEvalCoord1dv", userptr); - glEvalCoord1f = (PFNGLEVALCOORD1FPROC) load("glEvalCoord1f", userptr); - glEvalCoord1fv = (PFNGLEVALCOORD1FVPROC) load("glEvalCoord1fv", userptr); - glEvalCoord2d = (PFNGLEVALCOORD2DPROC) load("glEvalCoord2d", userptr); - glEvalCoord2dv = (PFNGLEVALCOORD2DVPROC) load("glEvalCoord2dv", userptr); - glEvalCoord2f = (PFNGLEVALCOORD2FPROC) load("glEvalCoord2f", userptr); - glEvalCoord2fv = (PFNGLEVALCOORD2FVPROC) load("glEvalCoord2fv", userptr); - glEvalMesh1 = (PFNGLEVALMESH1PROC) load("glEvalMesh1", userptr); - glEvalMesh2 = (PFNGLEVALMESH2PROC) load("glEvalMesh2", userptr); - glEvalPoint1 = (PFNGLEVALPOINT1PROC) load("glEvalPoint1", userptr); - glEvalPoint2 = (PFNGLEVALPOINT2PROC) load("glEvalPoint2", userptr); - glFeedbackBuffer = (PFNGLFEEDBACKBUFFERPROC) load("glFeedbackBuffer", userptr); - glFinish = (PFNGLFINISHPROC) load("glFinish", userptr); - glFlush = (PFNGLFLUSHPROC) load("glFlush", userptr); - glFogf = (PFNGLFOGFPROC) load("glFogf", userptr); - glFogfv = (PFNGLFOGFVPROC) load("glFogfv", userptr); - glFogi = (PFNGLFOGIPROC) load("glFogi", userptr); - glFogiv = (PFNGLFOGIVPROC) load("glFogiv", userptr); - glFrontFace = (PFNGLFRONTFACEPROC) load("glFrontFace", userptr); - glFrustum = (PFNGLFRUSTUMPROC) load("glFrustum", userptr); - glGenLists = (PFNGLGENLISTSPROC) load("glGenLists", userptr); - glGetBooleanv = (PFNGLGETBOOLEANVPROC) load("glGetBooleanv", userptr); - glGetClipPlane = (PFNGLGETCLIPPLANEPROC) load("glGetClipPlane", userptr); - glGetDoublev = (PFNGLGETDOUBLEVPROC) load("glGetDoublev", userptr); - glGetError = (PFNGLGETERRORPROC) load("glGetError", userptr); - glGetFloatv = (PFNGLGETFLOATVPROC) load("glGetFloatv", userptr); - glGetIntegerv = (PFNGLGETINTEGERVPROC) load("glGetIntegerv", userptr); - glGetLightfv = (PFNGLGETLIGHTFVPROC) load("glGetLightfv", userptr); - glGetLightiv = (PFNGLGETLIGHTIVPROC) load("glGetLightiv", userptr); - glGetMapdv = (PFNGLGETMAPDVPROC) load("glGetMapdv", userptr); - glGetMapfv = (PFNGLGETMAPFVPROC) load("glGetMapfv", userptr); - glGetMapiv = (PFNGLGETMAPIVPROC) load("glGetMapiv", userptr); - glGetMaterialfv = (PFNGLGETMATERIALFVPROC) load("glGetMaterialfv", userptr); - glGetMaterialiv = (PFNGLGETMATERIALIVPROC) load("glGetMaterialiv", userptr); - glGetPixelMapfv = (PFNGLGETPIXELMAPFVPROC) load("glGetPixelMapfv", userptr); - glGetPixelMapuiv = (PFNGLGETPIXELMAPUIVPROC) load("glGetPixelMapuiv", userptr); - glGetPixelMapusv = (PFNGLGETPIXELMAPUSVPROC) load("glGetPixelMapusv", userptr); - glGetPolygonStipple = (PFNGLGETPOLYGONSTIPPLEPROC) load("glGetPolygonStipple", userptr); - glGetString = (PFNGLGETSTRINGPROC) load("glGetString", userptr); - glGetTexEnvfv = (PFNGLGETTEXENVFVPROC) load("glGetTexEnvfv", userptr); - glGetTexEnviv = (PFNGLGETTEXENVIVPROC) load("glGetTexEnviv", userptr); - glGetTexGendv = (PFNGLGETTEXGENDVPROC) load("glGetTexGendv", userptr); - glGetTexGenfv = (PFNGLGETTEXGENFVPROC) load("glGetTexGenfv", userptr); - glGetTexGeniv = (PFNGLGETTEXGENIVPROC) load("glGetTexGeniv", userptr); - glGetTexImage = (PFNGLGETTEXIMAGEPROC) load("glGetTexImage", userptr); - glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC) load("glGetTexLevelParameterfv", userptr); - glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC) load("glGetTexLevelParameteriv", userptr); - glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load("glGetTexParameterfv", userptr); - glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load("glGetTexParameteriv", userptr); - glHint = (PFNGLHINTPROC) load("glHint", userptr); - glIndexMask = (PFNGLINDEXMASKPROC) load("glIndexMask", userptr); - glIndexd = (PFNGLINDEXDPROC) load("glIndexd", userptr); - glIndexdv = (PFNGLINDEXDVPROC) load("glIndexdv", userptr); - glIndexf = (PFNGLINDEXFPROC) load("glIndexf", userptr); - glIndexfv = (PFNGLINDEXFVPROC) load("glIndexfv", userptr); - glIndexi = (PFNGLINDEXIPROC) load("glIndexi", userptr); - glIndexiv = (PFNGLINDEXIVPROC) load("glIndexiv", userptr); - glIndexs = (PFNGLINDEXSPROC) load("glIndexs", userptr); - glIndexsv = (PFNGLINDEXSVPROC) load("glIndexsv", userptr); - glInitNames = (PFNGLINITNAMESPROC) load("glInitNames", userptr); - glIsEnabled = (PFNGLISENABLEDPROC) load("glIsEnabled", userptr); - glIsList = (PFNGLISLISTPROC) load("glIsList", userptr); - glLightModelf = (PFNGLLIGHTMODELFPROC) load("glLightModelf", userptr); - glLightModelfv = (PFNGLLIGHTMODELFVPROC) load("glLightModelfv", userptr); - glLightModeli = (PFNGLLIGHTMODELIPROC) load("glLightModeli", userptr); - glLightModeliv = (PFNGLLIGHTMODELIVPROC) load("glLightModeliv", userptr); - glLightf = (PFNGLLIGHTFPROC) load("glLightf", userptr); - glLightfv = (PFNGLLIGHTFVPROC) load("glLightfv", userptr); - glLighti = (PFNGLLIGHTIPROC) load("glLighti", userptr); - glLightiv = (PFNGLLIGHTIVPROC) load("glLightiv", userptr); - glLineStipple = (PFNGLLINESTIPPLEPROC) load("glLineStipple", userptr); - glLineWidth = (PFNGLLINEWIDTHPROC) load("glLineWidth", userptr); - glListBase = (PFNGLLISTBASEPROC) load("glListBase", userptr); - glLoadIdentity = (PFNGLLOADIDENTITYPROC) load("glLoadIdentity", userptr); - glLoadMatrixd = (PFNGLLOADMATRIXDPROC) load("glLoadMatrixd", userptr); - glLoadMatrixf = (PFNGLLOADMATRIXFPROC) load("glLoadMatrixf", userptr); - glLoadName = (PFNGLLOADNAMEPROC) load("glLoadName", userptr); - glLogicOp = (PFNGLLOGICOPPROC) load("glLogicOp", userptr); - glMap1d = (PFNGLMAP1DPROC) load("glMap1d", userptr); - glMap1f = (PFNGLMAP1FPROC) load("glMap1f", userptr); - glMap2d = (PFNGLMAP2DPROC) load("glMap2d", userptr); - glMap2f = (PFNGLMAP2FPROC) load("glMap2f", userptr); - glMapGrid1d = (PFNGLMAPGRID1DPROC) load("glMapGrid1d", userptr); - glMapGrid1f = (PFNGLMAPGRID1FPROC) load("glMapGrid1f", userptr); - glMapGrid2d = (PFNGLMAPGRID2DPROC) load("glMapGrid2d", userptr); - glMapGrid2f = (PFNGLMAPGRID2FPROC) load("glMapGrid2f", userptr); - glMaterialf = (PFNGLMATERIALFPROC) load("glMaterialf", userptr); - glMaterialfv = (PFNGLMATERIALFVPROC) load("glMaterialfv", userptr); - glMateriali = (PFNGLMATERIALIPROC) load("glMateriali", userptr); - glMaterialiv = (PFNGLMATERIALIVPROC) load("glMaterialiv", userptr); - glMatrixMode = (PFNGLMATRIXMODEPROC) load("glMatrixMode", userptr); - glMultMatrixd = (PFNGLMULTMATRIXDPROC) load("glMultMatrixd", userptr); - glMultMatrixf = (PFNGLMULTMATRIXFPROC) load("glMultMatrixf", userptr); - glNewList = (PFNGLNEWLISTPROC) load("glNewList", userptr); - glNormal3b = (PFNGLNORMAL3BPROC) load("glNormal3b", userptr); - glNormal3bv = (PFNGLNORMAL3BVPROC) load("glNormal3bv", userptr); - glNormal3d = (PFNGLNORMAL3DPROC) load("glNormal3d", userptr); - glNormal3dv = (PFNGLNORMAL3DVPROC) load("glNormal3dv", userptr); - glNormal3f = (PFNGLNORMAL3FPROC) load("glNormal3f", userptr); - glNormal3fv = (PFNGLNORMAL3FVPROC) load("glNormal3fv", userptr); - glNormal3i = (PFNGLNORMAL3IPROC) load("glNormal3i", userptr); - glNormal3iv = (PFNGLNORMAL3IVPROC) load("glNormal3iv", userptr); - glNormal3s = (PFNGLNORMAL3SPROC) load("glNormal3s", userptr); - glNormal3sv = (PFNGLNORMAL3SVPROC) load("glNormal3sv", userptr); - glOrtho = (PFNGLORTHOPROC) load("glOrtho", userptr); - glPassThrough = (PFNGLPASSTHROUGHPROC) load("glPassThrough", userptr); - glPixelMapfv = (PFNGLPIXELMAPFVPROC) load("glPixelMapfv", userptr); - glPixelMapuiv = (PFNGLPIXELMAPUIVPROC) load("glPixelMapuiv", userptr); - glPixelMapusv = (PFNGLPIXELMAPUSVPROC) load("glPixelMapusv", userptr); - glPixelStoref = (PFNGLPIXELSTOREFPROC) load("glPixelStoref", userptr); - glPixelStorei = (PFNGLPIXELSTOREIPROC) load("glPixelStorei", userptr); - glPixelTransferf = (PFNGLPIXELTRANSFERFPROC) load("glPixelTransferf", userptr); - glPixelTransferi = (PFNGLPIXELTRANSFERIPROC) load("glPixelTransferi", userptr); - glPixelZoom = (PFNGLPIXELZOOMPROC) load("glPixelZoom", userptr); - glPointSize = (PFNGLPOINTSIZEPROC) load("glPointSize", userptr); - glPolygonMode = (PFNGLPOLYGONMODEPROC) load("glPolygonMode", userptr); - glPolygonStipple = (PFNGLPOLYGONSTIPPLEPROC) load("glPolygonStipple", userptr); - glPopAttrib = (PFNGLPOPATTRIBPROC) load("glPopAttrib", userptr); - glPopMatrix = (PFNGLPOPMATRIXPROC) load("glPopMatrix", userptr); - glPopName = (PFNGLPOPNAMEPROC) load("glPopName", userptr); - glPushAttrib = (PFNGLPUSHATTRIBPROC) load("glPushAttrib", userptr); - glPushMatrix = (PFNGLPUSHMATRIXPROC) load("glPushMatrix", userptr); - glPushName = (PFNGLPUSHNAMEPROC) load("glPushName", userptr); - glRasterPos2d = (PFNGLRASTERPOS2DPROC) load("glRasterPos2d", userptr); - glRasterPos2dv = (PFNGLRASTERPOS2DVPROC) load("glRasterPos2dv", userptr); - glRasterPos2f = (PFNGLRASTERPOS2FPROC) load("glRasterPos2f", userptr); - glRasterPos2fv = (PFNGLRASTERPOS2FVPROC) load("glRasterPos2fv", userptr); - glRasterPos2i = (PFNGLRASTERPOS2IPROC) load("glRasterPos2i", userptr); - glRasterPos2iv = (PFNGLRASTERPOS2IVPROC) load("glRasterPos2iv", userptr); - glRasterPos2s = (PFNGLRASTERPOS2SPROC) load("glRasterPos2s", userptr); - glRasterPos2sv = (PFNGLRASTERPOS2SVPROC) load("glRasterPos2sv", userptr); - glRasterPos3d = (PFNGLRASTERPOS3DPROC) load("glRasterPos3d", userptr); - glRasterPos3dv = (PFNGLRASTERPOS3DVPROC) load("glRasterPos3dv", userptr); - glRasterPos3f = (PFNGLRASTERPOS3FPROC) load("glRasterPos3f", userptr); - glRasterPos3fv = (PFNGLRASTERPOS3FVPROC) load("glRasterPos3fv", userptr); - glRasterPos3i = (PFNGLRASTERPOS3IPROC) load("glRasterPos3i", userptr); - glRasterPos3iv = (PFNGLRASTERPOS3IVPROC) load("glRasterPos3iv", userptr); - glRasterPos3s = (PFNGLRASTERPOS3SPROC) load("glRasterPos3s", userptr); - glRasterPos3sv = (PFNGLRASTERPOS3SVPROC) load("glRasterPos3sv", userptr); - glRasterPos4d = (PFNGLRASTERPOS4DPROC) load("glRasterPos4d", userptr); - glRasterPos4dv = (PFNGLRASTERPOS4DVPROC) load("glRasterPos4dv", userptr); - glRasterPos4f = (PFNGLRASTERPOS4FPROC) load("glRasterPos4f", userptr); - glRasterPos4fv = (PFNGLRASTERPOS4FVPROC) load("glRasterPos4fv", userptr); - glRasterPos4i = (PFNGLRASTERPOS4IPROC) load("glRasterPos4i", userptr); - glRasterPos4iv = (PFNGLRASTERPOS4IVPROC) load("glRasterPos4iv", userptr); - glRasterPos4s = (PFNGLRASTERPOS4SPROC) load("glRasterPos4s", userptr); - glRasterPos4sv = (PFNGLRASTERPOS4SVPROC) load("glRasterPos4sv", userptr); - glReadBuffer = (PFNGLREADBUFFERPROC) load("glReadBuffer", userptr); - glReadPixels = (PFNGLREADPIXELSPROC) load("glReadPixels", userptr); - glRectd = (PFNGLRECTDPROC) load("glRectd", userptr); - glRectdv = (PFNGLRECTDVPROC) load("glRectdv", userptr); - glRectf = (PFNGLRECTFPROC) load("glRectf", userptr); - glRectfv = (PFNGLRECTFVPROC) load("glRectfv", userptr); - glRecti = (PFNGLRECTIPROC) load("glRecti", userptr); - glRectiv = (PFNGLRECTIVPROC) load("glRectiv", userptr); - glRects = (PFNGLRECTSPROC) load("glRects", userptr); - glRectsv = (PFNGLRECTSVPROC) load("glRectsv", userptr); - glRenderMode = (PFNGLRENDERMODEPROC) load("glRenderMode", userptr); - glRotated = (PFNGLROTATEDPROC) load("glRotated", userptr); - glRotatef = (PFNGLROTATEFPROC) load("glRotatef", userptr); - glScaled = (PFNGLSCALEDPROC) load("glScaled", userptr); - glScalef = (PFNGLSCALEFPROC) load("glScalef", userptr); - glScissor = (PFNGLSCISSORPROC) load("glScissor", userptr); - glSelectBuffer = (PFNGLSELECTBUFFERPROC) load("glSelectBuffer", userptr); - glShadeModel = (PFNGLSHADEMODELPROC) load("glShadeModel", userptr); - glStencilFunc = (PFNGLSTENCILFUNCPROC) load("glStencilFunc", userptr); - glStencilMask = (PFNGLSTENCILMASKPROC) load("glStencilMask", userptr); - glStencilOp = (PFNGLSTENCILOPPROC) load("glStencilOp", userptr); - glTexCoord1d = (PFNGLTEXCOORD1DPROC) load("glTexCoord1d", userptr); - glTexCoord1dv = (PFNGLTEXCOORD1DVPROC) load("glTexCoord1dv", userptr); - glTexCoord1f = (PFNGLTEXCOORD1FPROC) load("glTexCoord1f", userptr); - glTexCoord1fv = (PFNGLTEXCOORD1FVPROC) load("glTexCoord1fv", userptr); - glTexCoord1i = (PFNGLTEXCOORD1IPROC) load("glTexCoord1i", userptr); - glTexCoord1iv = (PFNGLTEXCOORD1IVPROC) load("glTexCoord1iv", userptr); - glTexCoord1s = (PFNGLTEXCOORD1SPROC) load("glTexCoord1s", userptr); - glTexCoord1sv = (PFNGLTEXCOORD1SVPROC) load("glTexCoord1sv", userptr); - glTexCoord2d = (PFNGLTEXCOORD2DPROC) load("glTexCoord2d", userptr); - glTexCoord2dv = (PFNGLTEXCOORD2DVPROC) load("glTexCoord2dv", userptr); - glTexCoord2f = (PFNGLTEXCOORD2FPROC) load("glTexCoord2f", userptr); - glTexCoord2fv = (PFNGLTEXCOORD2FVPROC) load("glTexCoord2fv", userptr); - glTexCoord2i = (PFNGLTEXCOORD2IPROC) load("glTexCoord2i", userptr); - glTexCoord2iv = (PFNGLTEXCOORD2IVPROC) load("glTexCoord2iv", userptr); - glTexCoord2s = (PFNGLTEXCOORD2SPROC) load("glTexCoord2s", userptr); - glTexCoord2sv = (PFNGLTEXCOORD2SVPROC) load("glTexCoord2sv", userptr); - glTexCoord3d = (PFNGLTEXCOORD3DPROC) load("glTexCoord3d", userptr); - glTexCoord3dv = (PFNGLTEXCOORD3DVPROC) load("glTexCoord3dv", userptr); - glTexCoord3f = (PFNGLTEXCOORD3FPROC) load("glTexCoord3f", userptr); - glTexCoord3fv = (PFNGLTEXCOORD3FVPROC) load("glTexCoord3fv", userptr); - glTexCoord3i = (PFNGLTEXCOORD3IPROC) load("glTexCoord3i", userptr); - glTexCoord3iv = (PFNGLTEXCOORD3IVPROC) load("glTexCoord3iv", userptr); - glTexCoord3s = (PFNGLTEXCOORD3SPROC) load("glTexCoord3s", userptr); - glTexCoord3sv = (PFNGLTEXCOORD3SVPROC) load("glTexCoord3sv", userptr); - glTexCoord4d = (PFNGLTEXCOORD4DPROC) load("glTexCoord4d", userptr); - glTexCoord4dv = (PFNGLTEXCOORD4DVPROC) load("glTexCoord4dv", userptr); - glTexCoord4f = (PFNGLTEXCOORD4FPROC) load("glTexCoord4f", userptr); - glTexCoord4fv = (PFNGLTEXCOORD4FVPROC) load("glTexCoord4fv", userptr); - glTexCoord4i = (PFNGLTEXCOORD4IPROC) load("glTexCoord4i", userptr); - glTexCoord4iv = (PFNGLTEXCOORD4IVPROC) load("glTexCoord4iv", userptr); - glTexCoord4s = (PFNGLTEXCOORD4SPROC) load("glTexCoord4s", userptr); - glTexCoord4sv = (PFNGLTEXCOORD4SVPROC) load("glTexCoord4sv", userptr); - glTexEnvf = (PFNGLTEXENVFPROC) load("glTexEnvf", userptr); - glTexEnvfv = (PFNGLTEXENVFVPROC) load("glTexEnvfv", userptr); - glTexEnvi = (PFNGLTEXENVIPROC) load("glTexEnvi", userptr); - glTexEnviv = (PFNGLTEXENVIVPROC) load("glTexEnviv", userptr); - glTexGend = (PFNGLTEXGENDPROC) load("glTexGend", userptr); - glTexGendv = (PFNGLTEXGENDVPROC) load("glTexGendv", userptr); - glTexGenf = (PFNGLTEXGENFPROC) load("glTexGenf", userptr); - glTexGenfv = (PFNGLTEXGENFVPROC) load("glTexGenfv", userptr); - glTexGeni = (PFNGLTEXGENIPROC) load("glTexGeni", userptr); - glTexGeniv = (PFNGLTEXGENIVPROC) load("glTexGeniv", userptr); - glTexImage1D = (PFNGLTEXIMAGE1DPROC) load("glTexImage1D", userptr); - glTexImage2D = (PFNGLTEXIMAGE2DPROC) load("glTexImage2D", userptr); - glTexParameterf = (PFNGLTEXPARAMETERFPROC) load("glTexParameterf", userptr); - glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load("glTexParameterfv", userptr); - glTexParameteri = (PFNGLTEXPARAMETERIPROC) load("glTexParameteri", userptr); - glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load("glTexParameteriv", userptr); - glTranslated = (PFNGLTRANSLATEDPROC) load("glTranslated", userptr); - glTranslatef = (PFNGLTRANSLATEFPROC) load("glTranslatef", userptr); - glVertex2d = (PFNGLVERTEX2DPROC) load("glVertex2d", userptr); - glVertex2dv = (PFNGLVERTEX2DVPROC) load("glVertex2dv", userptr); - glVertex2f = (PFNGLVERTEX2FPROC) load("glVertex2f", userptr); - glVertex2fv = (PFNGLVERTEX2FVPROC) load("glVertex2fv", userptr); - glVertex2i = (PFNGLVERTEX2IPROC) load("glVertex2i", userptr); - glVertex2iv = (PFNGLVERTEX2IVPROC) load("glVertex2iv", userptr); - glVertex2s = (PFNGLVERTEX2SPROC) load("glVertex2s", userptr); - glVertex2sv = (PFNGLVERTEX2SVPROC) load("glVertex2sv", userptr); - glVertex3d = (PFNGLVERTEX3DPROC) load("glVertex3d", userptr); - glVertex3dv = (PFNGLVERTEX3DVPROC) load("glVertex3dv", userptr); - glVertex3f = (PFNGLVERTEX3FPROC) load("glVertex3f", userptr); - glVertex3fv = (PFNGLVERTEX3FVPROC) load("glVertex3fv", userptr); - glVertex3i = (PFNGLVERTEX3IPROC) load("glVertex3i", userptr); - glVertex3iv = (PFNGLVERTEX3IVPROC) load("glVertex3iv", userptr); - glVertex3s = (PFNGLVERTEX3SPROC) load("glVertex3s", userptr); - glVertex3sv = (PFNGLVERTEX3SVPROC) load("glVertex3sv", userptr); - glVertex4d = (PFNGLVERTEX4DPROC) load("glVertex4d", userptr); - glVertex4dv = (PFNGLVERTEX4DVPROC) load("glVertex4dv", userptr); - glVertex4f = (PFNGLVERTEX4FPROC) load("glVertex4f", userptr); - glVertex4fv = (PFNGLVERTEX4FVPROC) load("glVertex4fv", userptr); - glVertex4i = (PFNGLVERTEX4IPROC) load("glVertex4i", userptr); - glVertex4iv = (PFNGLVERTEX4IVPROC) load("glVertex4iv", userptr); - glVertex4s = (PFNGLVERTEX4SPROC) load("glVertex4s", userptr); - glVertex4sv = (PFNGLVERTEX4SVPROC) load("glVertex4sv", userptr); - glViewport = (PFNGLVIEWPORTPROC) load("glViewport", userptr); -} -static void glad_gl_load_GL_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_1_1) return; - glAreTexturesResident = (PFNGLARETEXTURESRESIDENTPROC) load("glAreTexturesResident", userptr); - glArrayElement = (PFNGLARRAYELEMENTPROC) load("glArrayElement", userptr); - glBindTexture = (PFNGLBINDTEXTUREPROC) load("glBindTexture", userptr); - glColorPointer = (PFNGLCOLORPOINTERPROC) load("glColorPointer", userptr); - glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC) load("glCopyTexImage1D", userptr); - glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load("glCopyTexImage2D", userptr); - glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC) load("glCopyTexSubImage1D", userptr); - glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load("glCopyTexSubImage2D", userptr); - glDeleteTextures = (PFNGLDELETETEXTURESPROC) load("glDeleteTextures", userptr); - glDisableClientState = (PFNGLDISABLECLIENTSTATEPROC) load("glDisableClientState", userptr); - glDrawArrays = (PFNGLDRAWARRAYSPROC) load("glDrawArrays", userptr); - glDrawElements = (PFNGLDRAWELEMENTSPROC) load("glDrawElements", userptr); - glEdgeFlagPointer = (PFNGLEDGEFLAGPOINTERPROC) load("glEdgeFlagPointer", userptr); - glEnableClientState = (PFNGLENABLECLIENTSTATEPROC) load("glEnableClientState", userptr); - glGenTextures = (PFNGLGENTEXTURESPROC) load("glGenTextures", userptr); - glGetPointerv = (PFNGLGETPOINTERVPROC) load("glGetPointerv", userptr); - glIndexPointer = (PFNGLINDEXPOINTERPROC) load("glIndexPointer", userptr); - glIndexub = (PFNGLINDEXUBPROC) load("glIndexub", userptr); - glIndexubv = (PFNGLINDEXUBVPROC) load("glIndexubv", userptr); - glInterleavedArrays = (PFNGLINTERLEAVEDARRAYSPROC) load("glInterleavedArrays", userptr); - glIsTexture = (PFNGLISTEXTUREPROC) load("glIsTexture", userptr); - glNormalPointer = (PFNGLNORMALPOINTERPROC) load("glNormalPointer", userptr); - glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load("glPolygonOffset", userptr); - glPopClientAttrib = (PFNGLPOPCLIENTATTRIBPROC) load("glPopClientAttrib", userptr); - glPrioritizeTextures = (PFNGLPRIORITIZETEXTURESPROC) load("glPrioritizeTextures", userptr); - glPushClientAttrib = (PFNGLPUSHCLIENTATTRIBPROC) load("glPushClientAttrib", userptr); - glTexCoordPointer = (PFNGLTEXCOORDPOINTERPROC) load("glTexCoordPointer", userptr); - glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC) load("glTexSubImage1D", userptr); - glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load("glTexSubImage2D", userptr); - glVertexPointer = (PFNGLVERTEXPOINTERPROC) load("glVertexPointer", userptr); -} -static void glad_gl_load_GL_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_1_2) return; - glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC) load("glCopyTexSubImage3D", userptr); - glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) load("glDrawRangeElements", userptr); - glTexImage3D = (PFNGLTEXIMAGE3DPROC) load("glTexImage3D", userptr); - glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC) load("glTexSubImage3D", userptr); -} -static void glad_gl_load_GL_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_1_3) return; - glActiveTexture = (PFNGLACTIVETEXTUREPROC) load("glActiveTexture", userptr); - glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC) load("glClientActiveTexture", userptr); - glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) load("glCompressedTexImage1D", userptr); - glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load("glCompressedTexImage2D", userptr); - glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) load("glCompressedTexImage3D", userptr); - glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) load("glCompressedTexSubImage1D", userptr); - glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load("glCompressedTexSubImage2D", userptr); - glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) load("glCompressedTexSubImage3D", userptr); - glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) load("glGetCompressedTexImage", userptr); - glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC) load("glLoadTransposeMatrixd", userptr); - glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC) load("glLoadTransposeMatrixf", userptr); - glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC) load("glMultTransposeMatrixd", userptr); - glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC) load("glMultTransposeMatrixf", userptr); - glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC) load("glMultiTexCoord1d", userptr); - glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC) load("glMultiTexCoord1dv", userptr); - glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC) load("glMultiTexCoord1f", userptr); - glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC) load("glMultiTexCoord1fv", userptr); - glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC) load("glMultiTexCoord1i", userptr); - glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC) load("glMultiTexCoord1iv", userptr); - glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC) load("glMultiTexCoord1s", userptr); - glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC) load("glMultiTexCoord1sv", userptr); - glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC) load("glMultiTexCoord2d", userptr); - glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC) load("glMultiTexCoord2dv", userptr); - glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) load("glMultiTexCoord2f", userptr); - glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC) load("glMultiTexCoord2fv", userptr); - glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC) load("glMultiTexCoord2i", userptr); - glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC) load("glMultiTexCoord2iv", userptr); - glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC) load("glMultiTexCoord2s", userptr); - glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC) load("glMultiTexCoord2sv", userptr); - glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC) load("glMultiTexCoord3d", userptr); - glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC) load("glMultiTexCoord3dv", userptr); - glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC) load("glMultiTexCoord3f", userptr); - glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC) load("glMultiTexCoord3fv", userptr); - glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC) load("glMultiTexCoord3i", userptr); - glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC) load("glMultiTexCoord3iv", userptr); - glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC) load("glMultiTexCoord3s", userptr); - glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC) load("glMultiTexCoord3sv", userptr); - glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC) load("glMultiTexCoord4d", userptr); - glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC) load("glMultiTexCoord4dv", userptr); - glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC) load("glMultiTexCoord4f", userptr); - glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC) load("glMultiTexCoord4fv", userptr); - glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC) load("glMultiTexCoord4i", userptr); - glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC) load("glMultiTexCoord4iv", userptr); - glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC) load("glMultiTexCoord4s", userptr); - glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC) load("glMultiTexCoord4sv", userptr); - glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load("glSampleCoverage", userptr); -} -static void glad_gl_load_GL_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_1_4) return; - glBlendColor = (PFNGLBLENDCOLORPROC) load("glBlendColor", userptr); - glBlendEquation = (PFNGLBLENDEQUATIONPROC) load("glBlendEquation", userptr); - glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load("glBlendFuncSeparate", userptr); - glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC) load("glFogCoordPointer", userptr); - glFogCoordd = (PFNGLFOGCOORDDPROC) load("glFogCoordd", userptr); - glFogCoorddv = (PFNGLFOGCOORDDVPROC) load("glFogCoorddv", userptr); - glFogCoordf = (PFNGLFOGCOORDFPROC) load("glFogCoordf", userptr); - glFogCoordfv = (PFNGLFOGCOORDFVPROC) load("glFogCoordfv", userptr); - glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC) load("glMultiDrawArrays", userptr); - glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC) load("glMultiDrawElements", userptr); - glPointParameterf = (PFNGLPOINTPARAMETERFPROC) load("glPointParameterf", userptr); - glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC) load("glPointParameterfv", userptr); - glPointParameteri = (PFNGLPOINTPARAMETERIPROC) load("glPointParameteri", userptr); - glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC) load("glPointParameteriv", userptr); - glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC) load("glSecondaryColor3b", userptr); - glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC) load("glSecondaryColor3bv", userptr); - glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC) load("glSecondaryColor3d", userptr); - glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC) load("glSecondaryColor3dv", userptr); - glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC) load("glSecondaryColor3f", userptr); - glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC) load("glSecondaryColor3fv", userptr); - glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC) load("glSecondaryColor3i", userptr); - glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC) load("glSecondaryColor3iv", userptr); - glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC) load("glSecondaryColor3s", userptr); - glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC) load("glSecondaryColor3sv", userptr); - glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC) load("glSecondaryColor3ub", userptr); - glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC) load("glSecondaryColor3ubv", userptr); - glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC) load("glSecondaryColor3ui", userptr); - glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC) load("glSecondaryColor3uiv", userptr); - glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC) load("glSecondaryColor3us", userptr); - glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC) load("glSecondaryColor3usv", userptr); - glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC) load("glSecondaryColorPointer", userptr); - glWindowPos2d = (PFNGLWINDOWPOS2DPROC) load("glWindowPos2d", userptr); - glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC) load("glWindowPos2dv", userptr); - glWindowPos2f = (PFNGLWINDOWPOS2FPROC) load("glWindowPos2f", userptr); - glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC) load("glWindowPos2fv", userptr); - glWindowPos2i = (PFNGLWINDOWPOS2IPROC) load("glWindowPos2i", userptr); - glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC) load("glWindowPos2iv", userptr); - glWindowPos2s = (PFNGLWINDOWPOS2SPROC) load("glWindowPos2s", userptr); - glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC) load("glWindowPos2sv", userptr); - glWindowPos3d = (PFNGLWINDOWPOS3DPROC) load("glWindowPos3d", userptr); - glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC) load("glWindowPos3dv", userptr); - glWindowPos3f = (PFNGLWINDOWPOS3FPROC) load("glWindowPos3f", userptr); - glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC) load("glWindowPos3fv", userptr); - glWindowPos3i = (PFNGLWINDOWPOS3IPROC) load("glWindowPos3i", userptr); - glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC) load("glWindowPos3iv", userptr); - glWindowPos3s = (PFNGLWINDOWPOS3SPROC) load("glWindowPos3s", userptr); - glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC) load("glWindowPos3sv", userptr); -} -static void glad_gl_load_GL_VERSION_1_5( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_1_5) return; - glBeginQuery = (PFNGLBEGINQUERYPROC) load("glBeginQuery", userptr); - glBindBuffer = (PFNGLBINDBUFFERPROC) load("glBindBuffer", userptr); - glBufferData = (PFNGLBUFFERDATAPROC) load("glBufferData", userptr); - glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load("glBufferSubData", userptr); - glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load("glDeleteBuffers", userptr); - glDeleteQueries = (PFNGLDELETEQUERIESPROC) load("glDeleteQueries", userptr); - glEndQuery = (PFNGLENDQUERYPROC) load("glEndQuery", userptr); - glGenBuffers = (PFNGLGENBUFFERSPROC) load("glGenBuffers", userptr); - glGenQueries = (PFNGLGENQUERIESPROC) load("glGenQueries", userptr); - glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load("glGetBufferParameteriv", userptr); - glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) load("glGetBufferPointerv", userptr); - glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) load("glGetBufferSubData", userptr); - glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) load("glGetQueryObjectiv", userptr); - glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) load("glGetQueryObjectuiv", userptr); - glGetQueryiv = (PFNGLGETQUERYIVPROC) load("glGetQueryiv", userptr); - glIsBuffer = (PFNGLISBUFFERPROC) load("glIsBuffer", userptr); - glIsQuery = (PFNGLISQUERYPROC) load("glIsQuery", userptr); - glMapBuffer = (PFNGLMAPBUFFERPROC) load("glMapBuffer", userptr); - glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) load("glUnmapBuffer", userptr); -} -static void glad_gl_load_GL_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_2_0) return; - glAttachShader = (PFNGLATTACHSHADERPROC) load("glAttachShader", userptr); - glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load("glBindAttribLocation", userptr); - glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load("glBlendEquationSeparate", userptr); - glCompileShader = (PFNGLCOMPILESHADERPROC) load("glCompileShader", userptr); - glCreateProgram = (PFNGLCREATEPROGRAMPROC) load("glCreateProgram", userptr); - glCreateShader = (PFNGLCREATESHADERPROC) load("glCreateShader", userptr); - glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load("glDeleteProgram", userptr); - glDeleteShader = (PFNGLDELETESHADERPROC) load("glDeleteShader", userptr); - glDetachShader = (PFNGLDETACHSHADERPROC) load("glDetachShader", userptr); - glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load("glDisableVertexAttribArray", userptr); - glDrawBuffers = (PFNGLDRAWBUFFERSPROC) load("glDrawBuffers", userptr); - glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load("glEnableVertexAttribArray", userptr); - glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load("glGetActiveAttrib", userptr); - glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load("glGetActiveUniform", userptr); - glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load("glGetAttachedShaders", userptr); - glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load("glGetAttribLocation", userptr); - glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load("glGetProgramInfoLog", userptr); - glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load("glGetProgramiv", userptr); - glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load("glGetShaderInfoLog", userptr); - glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load("glGetShaderSource", userptr); - glGetShaderiv = (PFNGLGETSHADERIVPROC) load("glGetShaderiv", userptr); - glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load("glGetUniformLocation", userptr); - glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load("glGetUniformfv", userptr); - glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load("glGetUniformiv", userptr); - glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load("glGetVertexAttribPointerv", userptr); - glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load("glGetVertexAttribdv", userptr); - glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load("glGetVertexAttribfv", userptr); - glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load("glGetVertexAttribiv", userptr); - glIsProgram = (PFNGLISPROGRAMPROC) load("glIsProgram", userptr); - glIsShader = (PFNGLISSHADERPROC) load("glIsShader", userptr); - glLinkProgram = (PFNGLLINKPROGRAMPROC) load("glLinkProgram", userptr); - glShaderSource = (PFNGLSHADERSOURCEPROC) load("glShaderSource", userptr); - glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load("glStencilFuncSeparate", userptr); - glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load("glStencilMaskSeparate", userptr); - glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load("glStencilOpSeparate", userptr); - glUniform1f = (PFNGLUNIFORM1FPROC) load("glUniform1f", userptr); - glUniform1fv = (PFNGLUNIFORM1FVPROC) load("glUniform1fv", userptr); - glUniform1i = (PFNGLUNIFORM1IPROC) load("glUniform1i", userptr); - glUniform1iv = (PFNGLUNIFORM1IVPROC) load("glUniform1iv", userptr); - glUniform2f = (PFNGLUNIFORM2FPROC) load("glUniform2f", userptr); - glUniform2fv = (PFNGLUNIFORM2FVPROC) load("glUniform2fv", userptr); - glUniform2i = (PFNGLUNIFORM2IPROC) load("glUniform2i", userptr); - glUniform2iv = (PFNGLUNIFORM2IVPROC) load("glUniform2iv", userptr); - glUniform3f = (PFNGLUNIFORM3FPROC) load("glUniform3f", userptr); - glUniform3fv = (PFNGLUNIFORM3FVPROC) load("glUniform3fv", userptr); - glUniform3i = (PFNGLUNIFORM3IPROC) load("glUniform3i", userptr); - glUniform3iv = (PFNGLUNIFORM3IVPROC) load("glUniform3iv", userptr); - glUniform4f = (PFNGLUNIFORM4FPROC) load("glUniform4f", userptr); - glUniform4fv = (PFNGLUNIFORM4FVPROC) load("glUniform4fv", userptr); - glUniform4i = (PFNGLUNIFORM4IPROC) load("glUniform4i", userptr); - glUniform4iv = (PFNGLUNIFORM4IVPROC) load("glUniform4iv", userptr); - glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load("glUniformMatrix2fv", userptr); - glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load("glUniformMatrix3fv", userptr); - glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load("glUniformMatrix4fv", userptr); - glUseProgram = (PFNGLUSEPROGRAMPROC) load("glUseProgram", userptr); - glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load("glValidateProgram", userptr); - glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load("glVertexAttrib1d", userptr); - glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load("glVertexAttrib1dv", userptr); - glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load("glVertexAttrib1f", userptr); - glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load("glVertexAttrib1fv", userptr); - glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load("glVertexAttrib1s", userptr); - glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load("glVertexAttrib1sv", userptr); - glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load("glVertexAttrib2d", userptr); - glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load("glVertexAttrib2dv", userptr); - glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load("glVertexAttrib2f", userptr); - glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load("glVertexAttrib2fv", userptr); - glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load("glVertexAttrib2s", userptr); - glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load("glVertexAttrib2sv", userptr); - glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load("glVertexAttrib3d", userptr); - glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load("glVertexAttrib3dv", userptr); - glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load("glVertexAttrib3f", userptr); - glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load("glVertexAttrib3fv", userptr); - glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load("glVertexAttrib3s", userptr); - glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load("glVertexAttrib3sv", userptr); - glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load("glVertexAttrib4Nbv", userptr); - glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load("glVertexAttrib4Niv", userptr); - glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load("glVertexAttrib4Nsv", userptr); - glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load("glVertexAttrib4Nub", userptr); - glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load("glVertexAttrib4Nubv", userptr); - glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load("glVertexAttrib4Nuiv", userptr); - glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load("glVertexAttrib4Nusv", userptr); - glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load("glVertexAttrib4bv", userptr); - glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load("glVertexAttrib4d", userptr); - glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load("glVertexAttrib4dv", userptr); - glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load("glVertexAttrib4f", userptr); - glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load("glVertexAttrib4fv", userptr); - glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load("glVertexAttrib4iv", userptr); - glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load("glVertexAttrib4s", userptr); - glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load("glVertexAttrib4sv", userptr); - glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load("glVertexAttrib4ubv", userptr); - glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load("glVertexAttrib4uiv", userptr); - glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load("glVertexAttrib4usv", userptr); - glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load("glVertexAttribPointer", userptr); -} -static void glad_gl_load_GL_VERSION_2_1( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_2_1) return; - glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) load("glUniformMatrix2x3fv", userptr); - glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) load("glUniformMatrix2x4fv", userptr); - glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) load("glUniformMatrix3x2fv", userptr); - glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) load("glUniformMatrix3x4fv", userptr); - glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC) load("glUniformMatrix4x2fv", userptr); - glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) load("glUniformMatrix4x3fv", userptr); -} -static void glad_gl_load_GL_VERSION_3_0( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_3_0) return; - glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC) load("glBeginConditionalRender", userptr); - glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) load("glBeginTransformFeedback", userptr); - glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load("glBindBufferBase", userptr); - glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load("glBindBufferRange", userptr); - glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC) load("glBindFragDataLocation", userptr); - glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load("glBindFramebuffer", userptr); - glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load("glBindRenderbuffer", userptr); - glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load("glBindVertexArray", userptr); - glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load("glBlitFramebuffer", userptr); - glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load("glCheckFramebufferStatus", userptr); - glClampColor = (PFNGLCLAMPCOLORPROC) load("glClampColor", userptr); - glClearBufferfi = (PFNGLCLEARBUFFERFIPROC) load("glClearBufferfi", userptr); - glClearBufferfv = (PFNGLCLEARBUFFERFVPROC) load("glClearBufferfv", userptr); - glClearBufferiv = (PFNGLCLEARBUFFERIVPROC) load("glClearBufferiv", userptr); - glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC) load("glClearBufferuiv", userptr); - glColorMaski = (PFNGLCOLORMASKIPROC) load("glColorMaski", userptr); - glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load("glDeleteFramebuffers", userptr); - glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load("glDeleteRenderbuffers", userptr); - glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load("glDeleteVertexArrays", userptr); - glDisablei = (PFNGLDISABLEIPROC) load("glDisablei", userptr); - glEnablei = (PFNGLENABLEIPROC) load("glEnablei", userptr); - glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC) load("glEndConditionalRender", userptr); - glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) load("glEndTransformFeedback", userptr); - glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load("glFlushMappedBufferRange", userptr); - glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load("glFramebufferRenderbuffer", userptr); - glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load("glFramebufferTexture1D", userptr); - glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load("glFramebufferTexture2D", userptr); - glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load("glFramebufferTexture3D", userptr); - glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load("glFramebufferTextureLayer", userptr); - glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load("glGenFramebuffers", userptr); - glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load("glGenRenderbuffers", userptr); - glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load("glGenVertexArrays", userptr); - glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load("glGenerateMipmap", userptr); - glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC) load("glGetBooleani_v", userptr); - glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC) load("glGetFragDataLocation", userptr); - glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load("glGetFramebufferAttachmentParameteriv", userptr); - glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load("glGetIntegeri_v", userptr); - glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load("glGetRenderbufferParameteriv", userptr); - glGetStringi = (PFNGLGETSTRINGIPROC) load("glGetStringi", userptr); - glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC) load("glGetTexParameterIiv", userptr); - glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC) load("glGetTexParameterIuiv", userptr); - glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) load("glGetTransformFeedbackVarying", userptr); - glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC) load("glGetUniformuiv", userptr); - glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC) load("glGetVertexAttribIiv", userptr); - glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC) load("glGetVertexAttribIuiv", userptr); - glIsEnabledi = (PFNGLISENABLEDIPROC) load("glIsEnabledi", userptr); - glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load("glIsFramebuffer", userptr); - glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load("glIsRenderbuffer", userptr); - glIsVertexArray = (PFNGLISVERTEXARRAYPROC) load("glIsVertexArray", userptr); - glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load("glMapBufferRange", userptr); - glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load("glRenderbufferStorage", userptr); - glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load("glRenderbufferStorageMultisample", userptr); - glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC) load("glTexParameterIiv", userptr); - glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC) load("glTexParameterIuiv", userptr); - glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) load("glTransformFeedbackVaryings", userptr); - glUniform1ui = (PFNGLUNIFORM1UIPROC) load("glUniform1ui", userptr); - glUniform1uiv = (PFNGLUNIFORM1UIVPROC) load("glUniform1uiv", userptr); - glUniform2ui = (PFNGLUNIFORM2UIPROC) load("glUniform2ui", userptr); - glUniform2uiv = (PFNGLUNIFORM2UIVPROC) load("glUniform2uiv", userptr); - glUniform3ui = (PFNGLUNIFORM3UIPROC) load("glUniform3ui", userptr); - glUniform3uiv = (PFNGLUNIFORM3UIVPROC) load("glUniform3uiv", userptr); - glUniform4ui = (PFNGLUNIFORM4UIPROC) load("glUniform4ui", userptr); - glUniform4uiv = (PFNGLUNIFORM4UIVPROC) load("glUniform4uiv", userptr); - glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC) load("glVertexAttribI1i", userptr); - glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC) load("glVertexAttribI1iv", userptr); - glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC) load("glVertexAttribI1ui", userptr); - glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC) load("glVertexAttribI1uiv", userptr); - glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC) load("glVertexAttribI2i", userptr); - glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC) load("glVertexAttribI2iv", userptr); - glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC) load("glVertexAttribI2ui", userptr); - glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC) load("glVertexAttribI2uiv", userptr); - glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC) load("glVertexAttribI3i", userptr); - glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC) load("glVertexAttribI3iv", userptr); - glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC) load("glVertexAttribI3ui", userptr); - glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC) load("glVertexAttribI3uiv", userptr); - glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC) load("glVertexAttribI4bv", userptr); - glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC) load("glVertexAttribI4i", userptr); - glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC) load("glVertexAttribI4iv", userptr); - glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC) load("glVertexAttribI4sv", userptr); - glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC) load("glVertexAttribI4ubv", userptr); - glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC) load("glVertexAttribI4ui", userptr); - glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC) load("glVertexAttribI4uiv", userptr); - glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC) load("glVertexAttribI4usv", userptr); - glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) load("glVertexAttribIPointer", userptr); -} -static void glad_gl_load_GL_VERSION_3_1( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_3_1) return; - glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load("glBindBufferBase", userptr); - glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load("glBindBufferRange", userptr); - glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load("glCopyBufferSubData", userptr); - glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) load("glDrawArraysInstanced", userptr); - glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) load("glDrawElementsInstanced", userptr); - glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load("glGetActiveUniformBlockName", userptr); - glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load("glGetActiveUniformBlockiv", userptr); - glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load("glGetActiveUniformName", userptr); - glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load("glGetActiveUniformsiv", userptr); - glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load("glGetIntegeri_v", userptr); - glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load("glGetUniformBlockIndex", userptr); - glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load("glGetUniformIndices", userptr); - glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) load("glPrimitiveRestartIndex", userptr); - glTexBuffer = (PFNGLTEXBUFFERPROC) load("glTexBuffer", userptr); - glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load("glUniformBlockBinding", userptr); -} -static void glad_gl_load_GL_VERSION_3_2( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_3_2) return; - glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) load("glClientWaitSync", userptr); - glDeleteSync = (PFNGLDELETESYNCPROC) load("glDeleteSync", userptr); - glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) load("glDrawElementsBaseVertex", userptr); - glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) load("glDrawElementsInstancedBaseVertex", userptr); - glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) load("glDrawRangeElementsBaseVertex", userptr); - glFenceSync = (PFNGLFENCESYNCPROC) load("glFenceSync", userptr); - glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC) load("glFramebufferTexture", userptr); - glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC) load("glGetBufferParameteri64v", userptr); - glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC) load("glGetInteger64i_v", userptr); - glGetInteger64v = (PFNGLGETINTEGER64VPROC) load("glGetInteger64v", userptr); - glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) load("glGetMultisamplefv", userptr); - glGetSynciv = (PFNGLGETSYNCIVPROC) load("glGetSynciv", userptr); - glIsSync = (PFNGLISSYNCPROC) load("glIsSync", userptr); - glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) load("glMultiDrawElementsBaseVertex", userptr); - glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC) load("glProvokingVertex", userptr); - glSampleMaski = (PFNGLSAMPLEMASKIPROC) load("glSampleMaski", userptr); - glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) load("glTexImage2DMultisample", userptr); - glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) load("glTexImage3DMultisample", userptr); - glWaitSync = (PFNGLWAITSYNCPROC) load("glWaitSync", userptr); -} -static void glad_gl_load_GL_VERSION_3_3( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_VERSION_3_3) return; - glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) load("glBindFragDataLocationIndexed", userptr); - glBindSampler = (PFNGLBINDSAMPLERPROC) load("glBindSampler", userptr); - glColorP3ui = (PFNGLCOLORP3UIPROC) load("glColorP3ui", userptr); - glColorP3uiv = (PFNGLCOLORP3UIVPROC) load("glColorP3uiv", userptr); - glColorP4ui = (PFNGLCOLORP4UIPROC) load("glColorP4ui", userptr); - glColorP4uiv = (PFNGLCOLORP4UIVPROC) load("glColorP4uiv", userptr); - glDeleteSamplers = (PFNGLDELETESAMPLERSPROC) load("glDeleteSamplers", userptr); - glGenSamplers = (PFNGLGENSAMPLERSPROC) load("glGenSamplers", userptr); - glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) load("glGetFragDataIndex", userptr); - glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) load("glGetQueryObjecti64v", userptr); - glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) load("glGetQueryObjectui64v", userptr); - glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC) load("glGetSamplerParameterIiv", userptr); - glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC) load("glGetSamplerParameterIuiv", userptr); - glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC) load("glGetSamplerParameterfv", userptr); - glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC) load("glGetSamplerParameteriv", userptr); - glIsSampler = (PFNGLISSAMPLERPROC) load("glIsSampler", userptr); - glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC) load("glMultiTexCoordP1ui", userptr); - glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC) load("glMultiTexCoordP1uiv", userptr); - glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC) load("glMultiTexCoordP2ui", userptr); - glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC) load("glMultiTexCoordP2uiv", userptr); - glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC) load("glMultiTexCoordP3ui", userptr); - glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC) load("glMultiTexCoordP3uiv", userptr); - glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC) load("glMultiTexCoordP4ui", userptr); - glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC) load("glMultiTexCoordP4uiv", userptr); - glNormalP3ui = (PFNGLNORMALP3UIPROC) load("glNormalP3ui", userptr); - glNormalP3uiv = (PFNGLNORMALP3UIVPROC) load("glNormalP3uiv", userptr); - glQueryCounter = (PFNGLQUERYCOUNTERPROC) load("glQueryCounter", userptr); - glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC) load("glSamplerParameterIiv", userptr); - glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC) load("glSamplerParameterIuiv", userptr); - glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) load("glSamplerParameterf", userptr); - glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) load("glSamplerParameterfv", userptr); - glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) load("glSamplerParameteri", userptr); - glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) load("glSamplerParameteriv", userptr); - glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC) load("glSecondaryColorP3ui", userptr); - glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC) load("glSecondaryColorP3uiv", userptr); - glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC) load("glTexCoordP1ui", userptr); - glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC) load("glTexCoordP1uiv", userptr); - glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC) load("glTexCoordP2ui", userptr); - glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC) load("glTexCoordP2uiv", userptr); - glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC) load("glTexCoordP3ui", userptr); - glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC) load("glTexCoordP3uiv", userptr); - glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC) load("glTexCoordP4ui", userptr); - glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC) load("glTexCoordP4uiv", userptr); - glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC) load("glVertexAttribDivisor", userptr); - glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC) load("glVertexAttribP1ui", userptr); - glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC) load("glVertexAttribP1uiv", userptr); - glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC) load("glVertexAttribP2ui", userptr); - glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC) load("glVertexAttribP2uiv", userptr); - glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC) load("glVertexAttribP3ui", userptr); - glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC) load("glVertexAttribP3uiv", userptr); - glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC) load("glVertexAttribP4ui", userptr); - glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC) load("glVertexAttribP4uiv", userptr); - glVertexP2ui = (PFNGLVERTEXP2UIPROC) load("glVertexP2ui", userptr); - glVertexP2uiv = (PFNGLVERTEXP2UIVPROC) load("glVertexP2uiv", userptr); - glVertexP3ui = (PFNGLVERTEXP3UIPROC) load("glVertexP3ui", userptr); - glVertexP3uiv = (PFNGLVERTEXP3UIVPROC) load("glVertexP3uiv", userptr); - glVertexP4ui = (PFNGLVERTEXP4UIPROC) load("glVertexP4ui", userptr); - glVertexP4uiv = (PFNGLVERTEXP4UIVPROC) load("glVertexP4uiv", userptr); -} -static void glad_gl_load_GL_ARB_multisample( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_ARB_multisample) return; - glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load("glSampleCoverage", userptr); - glSampleCoverageARB = (PFNGLSAMPLECOVERAGEARBPROC) load("glSampleCoverageARB", userptr); -} -static void glad_gl_load_GL_ARB_robustness( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_ARB_robustness) return; - glGetGraphicsResetStatusARB = (PFNGLGETGRAPHICSRESETSTATUSARBPROC) load("glGetGraphicsResetStatusARB", userptr); - glGetnColorTableARB = (PFNGLGETNCOLORTABLEARBPROC) load("glGetnColorTableARB", userptr); - glGetnCompressedTexImageARB = (PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) load("glGetnCompressedTexImageARB", userptr); - glGetnConvolutionFilterARB = (PFNGLGETNCONVOLUTIONFILTERARBPROC) load("glGetnConvolutionFilterARB", userptr); - glGetnHistogramARB = (PFNGLGETNHISTOGRAMARBPROC) load("glGetnHistogramARB", userptr); - glGetnMapdvARB = (PFNGLGETNMAPDVARBPROC) load("glGetnMapdvARB", userptr); - glGetnMapfvARB = (PFNGLGETNMAPFVARBPROC) load("glGetnMapfvARB", userptr); - glGetnMapivARB = (PFNGLGETNMAPIVARBPROC) load("glGetnMapivARB", userptr); - glGetnMinmaxARB = (PFNGLGETNMINMAXARBPROC) load("glGetnMinmaxARB", userptr); - glGetnPixelMapfvARB = (PFNGLGETNPIXELMAPFVARBPROC) load("glGetnPixelMapfvARB", userptr); - glGetnPixelMapuivARB = (PFNGLGETNPIXELMAPUIVARBPROC) load("glGetnPixelMapuivARB", userptr); - glGetnPixelMapusvARB = (PFNGLGETNPIXELMAPUSVARBPROC) load("glGetnPixelMapusvARB", userptr); - glGetnPolygonStippleARB = (PFNGLGETNPOLYGONSTIPPLEARBPROC) load("glGetnPolygonStippleARB", userptr); - glGetnSeparableFilterARB = (PFNGLGETNSEPARABLEFILTERARBPROC) load("glGetnSeparableFilterARB", userptr); - glGetnTexImageARB = (PFNGLGETNTEXIMAGEARBPROC) load("glGetnTexImageARB", userptr); - glGetnUniformdvARB = (PFNGLGETNUNIFORMDVARBPROC) load("glGetnUniformdvARB", userptr); - glGetnUniformfvARB = (PFNGLGETNUNIFORMFVARBPROC) load("glGetnUniformfvARB", userptr); - glGetnUniformivARB = (PFNGLGETNUNIFORMIVARBPROC) load("glGetnUniformivARB", userptr); - glGetnUniformuivARB = (PFNGLGETNUNIFORMUIVARBPROC) load("glGetnUniformuivARB", userptr); - glReadnPixels = (PFNGLREADNPIXELSPROC) load("glReadnPixels", userptr); - glReadnPixelsARB = (PFNGLREADNPIXELSARBPROC) load("glReadnPixelsARB", userptr); -} -static void glad_gl_load_GL_KHR_debug( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_GL_KHR_debug) return; - glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) load("glDebugMessageCallback", userptr); - glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) load("glDebugMessageControl", userptr); - glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) load("glDebugMessageInsert", userptr); - glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) load("glGetDebugMessageLog", userptr); - glGetObjectLabel = (PFNGLGETOBJECTLABELPROC) load("glGetObjectLabel", userptr); - glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC) load("glGetObjectPtrLabel", userptr); - glGetPointerv = (PFNGLGETPOINTERVPROC) load("glGetPointerv", userptr); - glObjectLabel = (PFNGLOBJECTLABELPROC) load("glObjectLabel", userptr); - glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC) load("glObjectPtrLabel", userptr); - glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) load("glPopDebugGroup", userptr); - glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) load("glPushDebugGroup", userptr); -} - - - -#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) -#define GLAD_GL_IS_SOME_NEW_VERSION 1 -#else -#define GLAD_GL_IS_SOME_NEW_VERSION 0 -#endif - -static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { -#if GLAD_GL_IS_SOME_NEW_VERSION - if(GLAD_VERSION_MAJOR(version) < 3) { -#else - (void) version; - (void) out_num_exts_i; - (void) out_exts_i; -#endif - if (glGetString == NULL) { - return 0; - } - *out_exts = (const char *)glGetString(GL_EXTENSIONS); -#if GLAD_GL_IS_SOME_NEW_VERSION - } else { - unsigned int index = 0; - unsigned int num_exts_i = 0; - char **exts_i = NULL; - if (glGetStringi == NULL || glGetIntegerv == NULL) { - return 0; - } - glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); - if (num_exts_i > 0) { - exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); - } - if (exts_i == NULL) { - return 0; - } - for(index = 0; index < num_exts_i; index++) { - const char *gl_str_tmp = (const char*) glGetStringi(GL_EXTENSIONS, index); - size_t len = strlen(gl_str_tmp) + 1; - - char *local_str = (char*) malloc(len * sizeof(char)); - if(local_str != NULL) { - memcpy(local_str, gl_str_tmp, len * sizeof(char)); - } - - exts_i[index] = local_str; - } - - *out_num_exts_i = num_exts_i; - *out_exts_i = exts_i; - } -#endif - return 1; -} -static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { - if (exts_i != NULL) { - unsigned int index; - for(index = 0; index < num_exts_i; index++) { - free((void *) (exts_i[index])); - } - free((void *)exts_i); - exts_i = NULL; - } -} -static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { - if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { - const char *extensions; - const char *loc; - const char *terminator; - extensions = exts; - if(extensions == NULL || ext == NULL) { - return 0; - } - while(1) { - loc = strstr(extensions, ext); - if(loc == NULL) { - return 0; - } - terminator = loc + strlen(ext); - if((loc == extensions || *(loc - 1) == ' ') && - (*terminator == ' ' || *terminator == '\0')) { - return 1; - } - extensions = terminator; - } - } else { - unsigned int index; - for(index = 0; index < num_exts_i; index++) { - const char *e = exts_i[index]; - if(strcmp(e, ext) == 0) { - return 1; - } - } - } - return 0; -} - -static GLADapiproc glad_gl_get_proc_from_userptr(const char* name, void *userptr) { - return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); -} - -static int glad_gl_find_extensions_gl( int version) { - const char *exts = NULL; - unsigned int num_exts_i = 0; - char **exts_i = NULL; - if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; - - GLAD_GL_ARB_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multisample"); - GLAD_GL_ARB_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_robustness"); - GLAD_GL_KHR_debug = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_debug"); - - glad_gl_free_extensions(exts_i, num_exts_i); - - return 1; -} - -static int glad_gl_find_core_gl(void) { - int i, major, minor; - const char* version; - const char* prefixes[] = { - "OpenGL ES-CM ", - "OpenGL ES-CL ", - "OpenGL ES ", - NULL - }; - version = (const char*) glGetString(GL_VERSION); - if (!version) return 0; - for (i = 0; prefixes[i]; i++) { - const size_t length = strlen(prefixes[i]); - if (strncmp(version, prefixes[i], length) == 0) { - version += length; - break; - } - } - - GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); - - GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; - GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; - GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; - GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; - GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; - GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; - GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; - GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; - GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; - GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; - GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; - GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3; - - return GLAD_MAKE_VERSION(major, minor); -} - -int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { - int version; - - glGetString = (PFNGLGETSTRINGPROC) load("glGetString", userptr); - if(glGetString == NULL) return 0; - if(glGetString(GL_VERSION) == NULL) return 0; - version = glad_gl_find_core_gl(); - - glad_gl_load_GL_VERSION_1_0(load, userptr); - glad_gl_load_GL_VERSION_1_1(load, userptr); - glad_gl_load_GL_VERSION_1_2(load, userptr); - glad_gl_load_GL_VERSION_1_3(load, userptr); - glad_gl_load_GL_VERSION_1_4(load, userptr); - glad_gl_load_GL_VERSION_1_5(load, userptr); - glad_gl_load_GL_VERSION_2_0(load, userptr); - glad_gl_load_GL_VERSION_2_1(load, userptr); - glad_gl_load_GL_VERSION_3_0(load, userptr); - glad_gl_load_GL_VERSION_3_1(load, userptr); - glad_gl_load_GL_VERSION_3_2(load, userptr); - glad_gl_load_GL_VERSION_3_3(load, userptr); - - if (!glad_gl_find_extensions_gl(version)) return 0; - glad_gl_load_GL_ARB_multisample(load, userptr); - glad_gl_load_GL_ARB_robustness(load, userptr); - glad_gl_load_GL_KHR_debug(load, userptr); - - - - return version; -} - - -int gladLoadGL( GLADloadfunc load) { - return gladLoadGLUserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); -} - - - - diff --git a/src/external/glfw/deps/glad_vulkan.c b/src/external/glfw/deps/glad_vulkan.c deleted file mode 100644 index 5adfbbbe8..000000000 --- a/src/external/glfw/deps/glad_vulkan.c +++ /dev/null @@ -1,593 +0,0 @@ -#include -#include -#include -#include - -#ifndef GLAD_IMPL_UTIL_C_ -#define GLAD_IMPL_UTIL_C_ - -#ifdef _MSC_VER -#define GLAD_IMPL_UTIL_SSCANF sscanf_s -#else -#define GLAD_IMPL_UTIL_SSCANF sscanf -#endif - -#endif /* GLAD_IMPL_UTIL_C_ */ - - -int GLAD_VK_VERSION_1_0 = 0; -int GLAD_VK_VERSION_1_1 = 0; -int GLAD_VK_EXT_debug_report = 0; -int GLAD_VK_KHR_surface = 0; -int GLAD_VK_KHR_swapchain = 0; - - - -PFN_vkAcquireNextImage2KHR glad_vkAcquireNextImage2KHR = NULL; -PFN_vkAcquireNextImageKHR glad_vkAcquireNextImageKHR = NULL; -PFN_vkAllocateCommandBuffers glad_vkAllocateCommandBuffers = NULL; -PFN_vkAllocateDescriptorSets glad_vkAllocateDescriptorSets = NULL; -PFN_vkAllocateMemory glad_vkAllocateMemory = NULL; -PFN_vkBeginCommandBuffer glad_vkBeginCommandBuffer = NULL; -PFN_vkBindBufferMemory glad_vkBindBufferMemory = NULL; -PFN_vkBindBufferMemory2 glad_vkBindBufferMemory2 = NULL; -PFN_vkBindImageMemory glad_vkBindImageMemory = NULL; -PFN_vkBindImageMemory2 glad_vkBindImageMemory2 = NULL; -PFN_vkCmdBeginQuery glad_vkCmdBeginQuery = NULL; -PFN_vkCmdBeginRenderPass glad_vkCmdBeginRenderPass = NULL; -PFN_vkCmdBindDescriptorSets glad_vkCmdBindDescriptorSets = NULL; -PFN_vkCmdBindIndexBuffer glad_vkCmdBindIndexBuffer = NULL; -PFN_vkCmdBindPipeline glad_vkCmdBindPipeline = NULL; -PFN_vkCmdBindVertexBuffers glad_vkCmdBindVertexBuffers = NULL; -PFN_vkCmdBlitImage glad_vkCmdBlitImage = NULL; -PFN_vkCmdClearAttachments glad_vkCmdClearAttachments = NULL; -PFN_vkCmdClearColorImage glad_vkCmdClearColorImage = NULL; -PFN_vkCmdClearDepthStencilImage glad_vkCmdClearDepthStencilImage = NULL; -PFN_vkCmdCopyBuffer glad_vkCmdCopyBuffer = NULL; -PFN_vkCmdCopyBufferToImage glad_vkCmdCopyBufferToImage = NULL; -PFN_vkCmdCopyImage glad_vkCmdCopyImage = NULL; -PFN_vkCmdCopyImageToBuffer glad_vkCmdCopyImageToBuffer = NULL; -PFN_vkCmdCopyQueryPoolResults glad_vkCmdCopyQueryPoolResults = NULL; -PFN_vkCmdDispatch glad_vkCmdDispatch = NULL; -PFN_vkCmdDispatchBase glad_vkCmdDispatchBase = NULL; -PFN_vkCmdDispatchIndirect glad_vkCmdDispatchIndirect = NULL; -PFN_vkCmdDraw glad_vkCmdDraw = NULL; -PFN_vkCmdDrawIndexed glad_vkCmdDrawIndexed = NULL; -PFN_vkCmdDrawIndexedIndirect glad_vkCmdDrawIndexedIndirect = NULL; -PFN_vkCmdDrawIndirect glad_vkCmdDrawIndirect = NULL; -PFN_vkCmdEndQuery glad_vkCmdEndQuery = NULL; -PFN_vkCmdEndRenderPass glad_vkCmdEndRenderPass = NULL; -PFN_vkCmdExecuteCommands glad_vkCmdExecuteCommands = NULL; -PFN_vkCmdFillBuffer glad_vkCmdFillBuffer = NULL; -PFN_vkCmdNextSubpass glad_vkCmdNextSubpass = NULL; -PFN_vkCmdPipelineBarrier glad_vkCmdPipelineBarrier = NULL; -PFN_vkCmdPushConstants glad_vkCmdPushConstants = NULL; -PFN_vkCmdResetEvent glad_vkCmdResetEvent = NULL; -PFN_vkCmdResetQueryPool glad_vkCmdResetQueryPool = NULL; -PFN_vkCmdResolveImage glad_vkCmdResolveImage = NULL; -PFN_vkCmdSetBlendConstants glad_vkCmdSetBlendConstants = NULL; -PFN_vkCmdSetDepthBias glad_vkCmdSetDepthBias = NULL; -PFN_vkCmdSetDepthBounds glad_vkCmdSetDepthBounds = NULL; -PFN_vkCmdSetDeviceMask glad_vkCmdSetDeviceMask = NULL; -PFN_vkCmdSetEvent glad_vkCmdSetEvent = NULL; -PFN_vkCmdSetLineWidth glad_vkCmdSetLineWidth = NULL; -PFN_vkCmdSetScissor glad_vkCmdSetScissor = NULL; -PFN_vkCmdSetStencilCompareMask glad_vkCmdSetStencilCompareMask = NULL; -PFN_vkCmdSetStencilReference glad_vkCmdSetStencilReference = NULL; -PFN_vkCmdSetStencilWriteMask glad_vkCmdSetStencilWriteMask = NULL; -PFN_vkCmdSetViewport glad_vkCmdSetViewport = NULL; -PFN_vkCmdUpdateBuffer glad_vkCmdUpdateBuffer = NULL; -PFN_vkCmdWaitEvents glad_vkCmdWaitEvents = NULL; -PFN_vkCmdWriteTimestamp glad_vkCmdWriteTimestamp = NULL; -PFN_vkCreateBuffer glad_vkCreateBuffer = NULL; -PFN_vkCreateBufferView glad_vkCreateBufferView = NULL; -PFN_vkCreateCommandPool glad_vkCreateCommandPool = NULL; -PFN_vkCreateComputePipelines glad_vkCreateComputePipelines = NULL; -PFN_vkCreateDebugReportCallbackEXT glad_vkCreateDebugReportCallbackEXT = NULL; -PFN_vkCreateDescriptorPool glad_vkCreateDescriptorPool = NULL; -PFN_vkCreateDescriptorSetLayout glad_vkCreateDescriptorSetLayout = NULL; -PFN_vkCreateDescriptorUpdateTemplate glad_vkCreateDescriptorUpdateTemplate = NULL; -PFN_vkCreateDevice glad_vkCreateDevice = NULL; -PFN_vkCreateEvent glad_vkCreateEvent = NULL; -PFN_vkCreateFence glad_vkCreateFence = NULL; -PFN_vkCreateFramebuffer glad_vkCreateFramebuffer = NULL; -PFN_vkCreateGraphicsPipelines glad_vkCreateGraphicsPipelines = NULL; -PFN_vkCreateImage glad_vkCreateImage = NULL; -PFN_vkCreateImageView glad_vkCreateImageView = NULL; -PFN_vkCreateInstance glad_vkCreateInstance = NULL; -PFN_vkCreatePipelineCache glad_vkCreatePipelineCache = NULL; -PFN_vkCreatePipelineLayout glad_vkCreatePipelineLayout = NULL; -PFN_vkCreateQueryPool glad_vkCreateQueryPool = NULL; -PFN_vkCreateRenderPass glad_vkCreateRenderPass = NULL; -PFN_vkCreateSampler glad_vkCreateSampler = NULL; -PFN_vkCreateSamplerYcbcrConversion glad_vkCreateSamplerYcbcrConversion = NULL; -PFN_vkCreateSemaphore glad_vkCreateSemaphore = NULL; -PFN_vkCreateShaderModule glad_vkCreateShaderModule = NULL; -PFN_vkCreateSwapchainKHR glad_vkCreateSwapchainKHR = NULL; -PFN_vkDebugReportMessageEXT glad_vkDebugReportMessageEXT = NULL; -PFN_vkDestroyBuffer glad_vkDestroyBuffer = NULL; -PFN_vkDestroyBufferView glad_vkDestroyBufferView = NULL; -PFN_vkDestroyCommandPool glad_vkDestroyCommandPool = NULL; -PFN_vkDestroyDebugReportCallbackEXT glad_vkDestroyDebugReportCallbackEXT = NULL; -PFN_vkDestroyDescriptorPool glad_vkDestroyDescriptorPool = NULL; -PFN_vkDestroyDescriptorSetLayout glad_vkDestroyDescriptorSetLayout = NULL; -PFN_vkDestroyDescriptorUpdateTemplate glad_vkDestroyDescriptorUpdateTemplate = NULL; -PFN_vkDestroyDevice glad_vkDestroyDevice = NULL; -PFN_vkDestroyEvent glad_vkDestroyEvent = NULL; -PFN_vkDestroyFence glad_vkDestroyFence = NULL; -PFN_vkDestroyFramebuffer glad_vkDestroyFramebuffer = NULL; -PFN_vkDestroyImage glad_vkDestroyImage = NULL; -PFN_vkDestroyImageView glad_vkDestroyImageView = NULL; -PFN_vkDestroyInstance glad_vkDestroyInstance = NULL; -PFN_vkDestroyPipeline glad_vkDestroyPipeline = NULL; -PFN_vkDestroyPipelineCache glad_vkDestroyPipelineCache = NULL; -PFN_vkDestroyPipelineLayout glad_vkDestroyPipelineLayout = NULL; -PFN_vkDestroyQueryPool glad_vkDestroyQueryPool = NULL; -PFN_vkDestroyRenderPass glad_vkDestroyRenderPass = NULL; -PFN_vkDestroySampler glad_vkDestroySampler = NULL; -PFN_vkDestroySamplerYcbcrConversion glad_vkDestroySamplerYcbcrConversion = NULL; -PFN_vkDestroySemaphore glad_vkDestroySemaphore = NULL; -PFN_vkDestroyShaderModule glad_vkDestroyShaderModule = NULL; -PFN_vkDestroySurfaceKHR glad_vkDestroySurfaceKHR = NULL; -PFN_vkDestroySwapchainKHR glad_vkDestroySwapchainKHR = NULL; -PFN_vkDeviceWaitIdle glad_vkDeviceWaitIdle = NULL; -PFN_vkEndCommandBuffer glad_vkEndCommandBuffer = NULL; -PFN_vkEnumerateDeviceExtensionProperties glad_vkEnumerateDeviceExtensionProperties = NULL; -PFN_vkEnumerateDeviceLayerProperties glad_vkEnumerateDeviceLayerProperties = NULL; -PFN_vkEnumerateInstanceExtensionProperties glad_vkEnumerateInstanceExtensionProperties = NULL; -PFN_vkEnumerateInstanceLayerProperties glad_vkEnumerateInstanceLayerProperties = NULL; -PFN_vkEnumerateInstanceVersion glad_vkEnumerateInstanceVersion = NULL; -PFN_vkEnumeratePhysicalDeviceGroups glad_vkEnumeratePhysicalDeviceGroups = NULL; -PFN_vkEnumeratePhysicalDevices glad_vkEnumeratePhysicalDevices = NULL; -PFN_vkFlushMappedMemoryRanges glad_vkFlushMappedMemoryRanges = NULL; -PFN_vkFreeCommandBuffers glad_vkFreeCommandBuffers = NULL; -PFN_vkFreeDescriptorSets glad_vkFreeDescriptorSets = NULL; -PFN_vkFreeMemory glad_vkFreeMemory = NULL; -PFN_vkGetBufferMemoryRequirements glad_vkGetBufferMemoryRequirements = NULL; -PFN_vkGetBufferMemoryRequirements2 glad_vkGetBufferMemoryRequirements2 = NULL; -PFN_vkGetDescriptorSetLayoutSupport glad_vkGetDescriptorSetLayoutSupport = NULL; -PFN_vkGetDeviceGroupPeerMemoryFeatures glad_vkGetDeviceGroupPeerMemoryFeatures = NULL; -PFN_vkGetDeviceGroupPresentCapabilitiesKHR glad_vkGetDeviceGroupPresentCapabilitiesKHR = NULL; -PFN_vkGetDeviceGroupSurfacePresentModesKHR glad_vkGetDeviceGroupSurfacePresentModesKHR = NULL; -PFN_vkGetDeviceMemoryCommitment glad_vkGetDeviceMemoryCommitment = NULL; -PFN_vkGetDeviceProcAddr glad_vkGetDeviceProcAddr = NULL; -PFN_vkGetDeviceQueue glad_vkGetDeviceQueue = NULL; -PFN_vkGetDeviceQueue2 glad_vkGetDeviceQueue2 = NULL; -PFN_vkGetEventStatus glad_vkGetEventStatus = NULL; -PFN_vkGetFenceStatus glad_vkGetFenceStatus = NULL; -PFN_vkGetImageMemoryRequirements glad_vkGetImageMemoryRequirements = NULL; -PFN_vkGetImageMemoryRequirements2 glad_vkGetImageMemoryRequirements2 = NULL; -PFN_vkGetImageSparseMemoryRequirements glad_vkGetImageSparseMemoryRequirements = NULL; -PFN_vkGetImageSparseMemoryRequirements2 glad_vkGetImageSparseMemoryRequirements2 = NULL; -PFN_vkGetImageSubresourceLayout glad_vkGetImageSubresourceLayout = NULL; -PFN_vkGetInstanceProcAddr glad_vkGetInstanceProcAddr = NULL; -PFN_vkGetPhysicalDeviceExternalBufferProperties glad_vkGetPhysicalDeviceExternalBufferProperties = NULL; -PFN_vkGetPhysicalDeviceExternalFenceProperties glad_vkGetPhysicalDeviceExternalFenceProperties = NULL; -PFN_vkGetPhysicalDeviceExternalSemaphoreProperties glad_vkGetPhysicalDeviceExternalSemaphoreProperties = NULL; -PFN_vkGetPhysicalDeviceFeatures glad_vkGetPhysicalDeviceFeatures = NULL; -PFN_vkGetPhysicalDeviceFeatures2 glad_vkGetPhysicalDeviceFeatures2 = NULL; -PFN_vkGetPhysicalDeviceFormatProperties glad_vkGetPhysicalDeviceFormatProperties = NULL; -PFN_vkGetPhysicalDeviceFormatProperties2 glad_vkGetPhysicalDeviceFormatProperties2 = NULL; -PFN_vkGetPhysicalDeviceImageFormatProperties glad_vkGetPhysicalDeviceImageFormatProperties = NULL; -PFN_vkGetPhysicalDeviceImageFormatProperties2 glad_vkGetPhysicalDeviceImageFormatProperties2 = NULL; -PFN_vkGetPhysicalDeviceMemoryProperties glad_vkGetPhysicalDeviceMemoryProperties = NULL; -PFN_vkGetPhysicalDeviceMemoryProperties2 glad_vkGetPhysicalDeviceMemoryProperties2 = NULL; -PFN_vkGetPhysicalDevicePresentRectanglesKHR glad_vkGetPhysicalDevicePresentRectanglesKHR = NULL; -PFN_vkGetPhysicalDeviceProperties glad_vkGetPhysicalDeviceProperties = NULL; -PFN_vkGetPhysicalDeviceProperties2 glad_vkGetPhysicalDeviceProperties2 = NULL; -PFN_vkGetPhysicalDeviceQueueFamilyProperties glad_vkGetPhysicalDeviceQueueFamilyProperties = NULL; -PFN_vkGetPhysicalDeviceQueueFamilyProperties2 glad_vkGetPhysicalDeviceQueueFamilyProperties2 = NULL; -PFN_vkGetPhysicalDeviceSparseImageFormatProperties glad_vkGetPhysicalDeviceSparseImageFormatProperties = NULL; -PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 glad_vkGetPhysicalDeviceSparseImageFormatProperties2 = NULL; -PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR glad_vkGetPhysicalDeviceSurfaceCapabilitiesKHR = NULL; -PFN_vkGetPhysicalDeviceSurfaceFormatsKHR glad_vkGetPhysicalDeviceSurfaceFormatsKHR = NULL; -PFN_vkGetPhysicalDeviceSurfacePresentModesKHR glad_vkGetPhysicalDeviceSurfacePresentModesKHR = NULL; -PFN_vkGetPhysicalDeviceSurfaceSupportKHR glad_vkGetPhysicalDeviceSurfaceSupportKHR = NULL; -PFN_vkGetPipelineCacheData glad_vkGetPipelineCacheData = NULL; -PFN_vkGetQueryPoolResults glad_vkGetQueryPoolResults = NULL; -PFN_vkGetRenderAreaGranularity glad_vkGetRenderAreaGranularity = NULL; -PFN_vkGetSwapchainImagesKHR glad_vkGetSwapchainImagesKHR = NULL; -PFN_vkInvalidateMappedMemoryRanges glad_vkInvalidateMappedMemoryRanges = NULL; -PFN_vkMapMemory glad_vkMapMemory = NULL; -PFN_vkMergePipelineCaches glad_vkMergePipelineCaches = NULL; -PFN_vkQueueBindSparse glad_vkQueueBindSparse = NULL; -PFN_vkQueuePresentKHR glad_vkQueuePresentKHR = NULL; -PFN_vkQueueSubmit glad_vkQueueSubmit = NULL; -PFN_vkQueueWaitIdle glad_vkQueueWaitIdle = NULL; -PFN_vkResetCommandBuffer glad_vkResetCommandBuffer = NULL; -PFN_vkResetCommandPool glad_vkResetCommandPool = NULL; -PFN_vkResetDescriptorPool glad_vkResetDescriptorPool = NULL; -PFN_vkResetEvent glad_vkResetEvent = NULL; -PFN_vkResetFences glad_vkResetFences = NULL; -PFN_vkSetEvent glad_vkSetEvent = NULL; -PFN_vkTrimCommandPool glad_vkTrimCommandPool = NULL; -PFN_vkUnmapMemory glad_vkUnmapMemory = NULL; -PFN_vkUpdateDescriptorSetWithTemplate glad_vkUpdateDescriptorSetWithTemplate = NULL; -PFN_vkUpdateDescriptorSets glad_vkUpdateDescriptorSets = NULL; -PFN_vkWaitForFences glad_vkWaitForFences = NULL; - - -static void glad_vk_load_VK_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_VK_VERSION_1_0) return; - vkAllocateCommandBuffers = (PFN_vkAllocateCommandBuffers) load("vkAllocateCommandBuffers", userptr); - vkAllocateDescriptorSets = (PFN_vkAllocateDescriptorSets) load("vkAllocateDescriptorSets", userptr); - vkAllocateMemory = (PFN_vkAllocateMemory) load("vkAllocateMemory", userptr); - vkBeginCommandBuffer = (PFN_vkBeginCommandBuffer) load("vkBeginCommandBuffer", userptr); - vkBindBufferMemory = (PFN_vkBindBufferMemory) load("vkBindBufferMemory", userptr); - vkBindImageMemory = (PFN_vkBindImageMemory) load("vkBindImageMemory", userptr); - vkCmdBeginQuery = (PFN_vkCmdBeginQuery) load("vkCmdBeginQuery", userptr); - vkCmdBeginRenderPass = (PFN_vkCmdBeginRenderPass) load("vkCmdBeginRenderPass", userptr); - vkCmdBindDescriptorSets = (PFN_vkCmdBindDescriptorSets) load("vkCmdBindDescriptorSets", userptr); - vkCmdBindIndexBuffer = (PFN_vkCmdBindIndexBuffer) load("vkCmdBindIndexBuffer", userptr); - vkCmdBindPipeline = (PFN_vkCmdBindPipeline) load("vkCmdBindPipeline", userptr); - vkCmdBindVertexBuffers = (PFN_vkCmdBindVertexBuffers) load("vkCmdBindVertexBuffers", userptr); - vkCmdBlitImage = (PFN_vkCmdBlitImage) load("vkCmdBlitImage", userptr); - vkCmdClearAttachments = (PFN_vkCmdClearAttachments) load("vkCmdClearAttachments", userptr); - vkCmdClearColorImage = (PFN_vkCmdClearColorImage) load("vkCmdClearColorImage", userptr); - vkCmdClearDepthStencilImage = (PFN_vkCmdClearDepthStencilImage) load("vkCmdClearDepthStencilImage", userptr); - vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer) load("vkCmdCopyBuffer", userptr); - vkCmdCopyBufferToImage = (PFN_vkCmdCopyBufferToImage) load("vkCmdCopyBufferToImage", userptr); - vkCmdCopyImage = (PFN_vkCmdCopyImage) load("vkCmdCopyImage", userptr); - vkCmdCopyImageToBuffer = (PFN_vkCmdCopyImageToBuffer) load("vkCmdCopyImageToBuffer", userptr); - vkCmdCopyQueryPoolResults = (PFN_vkCmdCopyQueryPoolResults) load("vkCmdCopyQueryPoolResults", userptr); - vkCmdDispatch = (PFN_vkCmdDispatch) load("vkCmdDispatch", userptr); - vkCmdDispatchIndirect = (PFN_vkCmdDispatchIndirect) load("vkCmdDispatchIndirect", userptr); - vkCmdDraw = (PFN_vkCmdDraw) load("vkCmdDraw", userptr); - vkCmdDrawIndexed = (PFN_vkCmdDrawIndexed) load("vkCmdDrawIndexed", userptr); - vkCmdDrawIndexedIndirect = (PFN_vkCmdDrawIndexedIndirect) load("vkCmdDrawIndexedIndirect", userptr); - vkCmdDrawIndirect = (PFN_vkCmdDrawIndirect) load("vkCmdDrawIndirect", userptr); - vkCmdEndQuery = (PFN_vkCmdEndQuery) load("vkCmdEndQuery", userptr); - vkCmdEndRenderPass = (PFN_vkCmdEndRenderPass) load("vkCmdEndRenderPass", userptr); - vkCmdExecuteCommands = (PFN_vkCmdExecuteCommands) load("vkCmdExecuteCommands", userptr); - vkCmdFillBuffer = (PFN_vkCmdFillBuffer) load("vkCmdFillBuffer", userptr); - vkCmdNextSubpass = (PFN_vkCmdNextSubpass) load("vkCmdNextSubpass", userptr); - vkCmdPipelineBarrier = (PFN_vkCmdPipelineBarrier) load("vkCmdPipelineBarrier", userptr); - vkCmdPushConstants = (PFN_vkCmdPushConstants) load("vkCmdPushConstants", userptr); - vkCmdResetEvent = (PFN_vkCmdResetEvent) load("vkCmdResetEvent", userptr); - vkCmdResetQueryPool = (PFN_vkCmdResetQueryPool) load("vkCmdResetQueryPool", userptr); - vkCmdResolveImage = (PFN_vkCmdResolveImage) load("vkCmdResolveImage", userptr); - vkCmdSetBlendConstants = (PFN_vkCmdSetBlendConstants) load("vkCmdSetBlendConstants", userptr); - vkCmdSetDepthBias = (PFN_vkCmdSetDepthBias) load("vkCmdSetDepthBias", userptr); - vkCmdSetDepthBounds = (PFN_vkCmdSetDepthBounds) load("vkCmdSetDepthBounds", userptr); - vkCmdSetEvent = (PFN_vkCmdSetEvent) load("vkCmdSetEvent", userptr); - vkCmdSetLineWidth = (PFN_vkCmdSetLineWidth) load("vkCmdSetLineWidth", userptr); - vkCmdSetScissor = (PFN_vkCmdSetScissor) load("vkCmdSetScissor", userptr); - vkCmdSetStencilCompareMask = (PFN_vkCmdSetStencilCompareMask) load("vkCmdSetStencilCompareMask", userptr); - vkCmdSetStencilReference = (PFN_vkCmdSetStencilReference) load("vkCmdSetStencilReference", userptr); - vkCmdSetStencilWriteMask = (PFN_vkCmdSetStencilWriteMask) load("vkCmdSetStencilWriteMask", userptr); - vkCmdSetViewport = (PFN_vkCmdSetViewport) load("vkCmdSetViewport", userptr); - vkCmdUpdateBuffer = (PFN_vkCmdUpdateBuffer) load("vkCmdUpdateBuffer", userptr); - vkCmdWaitEvents = (PFN_vkCmdWaitEvents) load("vkCmdWaitEvents", userptr); - vkCmdWriteTimestamp = (PFN_vkCmdWriteTimestamp) load("vkCmdWriteTimestamp", userptr); - vkCreateBuffer = (PFN_vkCreateBuffer) load("vkCreateBuffer", userptr); - vkCreateBufferView = (PFN_vkCreateBufferView) load("vkCreateBufferView", userptr); - vkCreateCommandPool = (PFN_vkCreateCommandPool) load("vkCreateCommandPool", userptr); - vkCreateComputePipelines = (PFN_vkCreateComputePipelines) load("vkCreateComputePipelines", userptr); - vkCreateDescriptorPool = (PFN_vkCreateDescriptorPool) load("vkCreateDescriptorPool", userptr); - vkCreateDescriptorSetLayout = (PFN_vkCreateDescriptorSetLayout) load("vkCreateDescriptorSetLayout", userptr); - vkCreateDevice = (PFN_vkCreateDevice) load("vkCreateDevice", userptr); - vkCreateEvent = (PFN_vkCreateEvent) load("vkCreateEvent", userptr); - vkCreateFence = (PFN_vkCreateFence) load("vkCreateFence", userptr); - vkCreateFramebuffer = (PFN_vkCreateFramebuffer) load("vkCreateFramebuffer", userptr); - vkCreateGraphicsPipelines = (PFN_vkCreateGraphicsPipelines) load("vkCreateGraphicsPipelines", userptr); - vkCreateImage = (PFN_vkCreateImage) load("vkCreateImage", userptr); - vkCreateImageView = (PFN_vkCreateImageView) load("vkCreateImageView", userptr); - vkCreateInstance = (PFN_vkCreateInstance) load("vkCreateInstance", userptr); - vkCreatePipelineCache = (PFN_vkCreatePipelineCache) load("vkCreatePipelineCache", userptr); - vkCreatePipelineLayout = (PFN_vkCreatePipelineLayout) load("vkCreatePipelineLayout", userptr); - vkCreateQueryPool = (PFN_vkCreateQueryPool) load("vkCreateQueryPool", userptr); - vkCreateRenderPass = (PFN_vkCreateRenderPass) load("vkCreateRenderPass", userptr); - vkCreateSampler = (PFN_vkCreateSampler) load("vkCreateSampler", userptr); - vkCreateSemaphore = (PFN_vkCreateSemaphore) load("vkCreateSemaphore", userptr); - vkCreateShaderModule = (PFN_vkCreateShaderModule) load("vkCreateShaderModule", userptr); - vkDestroyBuffer = (PFN_vkDestroyBuffer) load("vkDestroyBuffer", userptr); - vkDestroyBufferView = (PFN_vkDestroyBufferView) load("vkDestroyBufferView", userptr); - vkDestroyCommandPool = (PFN_vkDestroyCommandPool) load("vkDestroyCommandPool", userptr); - vkDestroyDescriptorPool = (PFN_vkDestroyDescriptorPool) load("vkDestroyDescriptorPool", userptr); - vkDestroyDescriptorSetLayout = (PFN_vkDestroyDescriptorSetLayout) load("vkDestroyDescriptorSetLayout", userptr); - vkDestroyDevice = (PFN_vkDestroyDevice) load("vkDestroyDevice", userptr); - vkDestroyEvent = (PFN_vkDestroyEvent) load("vkDestroyEvent", userptr); - vkDestroyFence = (PFN_vkDestroyFence) load("vkDestroyFence", userptr); - vkDestroyFramebuffer = (PFN_vkDestroyFramebuffer) load("vkDestroyFramebuffer", userptr); - vkDestroyImage = (PFN_vkDestroyImage) load("vkDestroyImage", userptr); - vkDestroyImageView = (PFN_vkDestroyImageView) load("vkDestroyImageView", userptr); - vkDestroyInstance = (PFN_vkDestroyInstance) load("vkDestroyInstance", userptr); - vkDestroyPipeline = (PFN_vkDestroyPipeline) load("vkDestroyPipeline", userptr); - vkDestroyPipelineCache = (PFN_vkDestroyPipelineCache) load("vkDestroyPipelineCache", userptr); - vkDestroyPipelineLayout = (PFN_vkDestroyPipelineLayout) load("vkDestroyPipelineLayout", userptr); - vkDestroyQueryPool = (PFN_vkDestroyQueryPool) load("vkDestroyQueryPool", userptr); - vkDestroyRenderPass = (PFN_vkDestroyRenderPass) load("vkDestroyRenderPass", userptr); - vkDestroySampler = (PFN_vkDestroySampler) load("vkDestroySampler", userptr); - vkDestroySemaphore = (PFN_vkDestroySemaphore) load("vkDestroySemaphore", userptr); - vkDestroyShaderModule = (PFN_vkDestroyShaderModule) load("vkDestroyShaderModule", userptr); - vkDeviceWaitIdle = (PFN_vkDeviceWaitIdle) load("vkDeviceWaitIdle", userptr); - vkEndCommandBuffer = (PFN_vkEndCommandBuffer) load("vkEndCommandBuffer", userptr); - vkEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties) load("vkEnumerateDeviceExtensionProperties", userptr); - vkEnumerateDeviceLayerProperties = (PFN_vkEnumerateDeviceLayerProperties) load("vkEnumerateDeviceLayerProperties", userptr); - vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) load("vkEnumerateInstanceExtensionProperties", userptr); - vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties) load("vkEnumerateInstanceLayerProperties", userptr); - vkEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices) load("vkEnumeratePhysicalDevices", userptr); - vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges) load("vkFlushMappedMemoryRanges", userptr); - vkFreeCommandBuffers = (PFN_vkFreeCommandBuffers) load("vkFreeCommandBuffers", userptr); - vkFreeDescriptorSets = (PFN_vkFreeDescriptorSets) load("vkFreeDescriptorSets", userptr); - vkFreeMemory = (PFN_vkFreeMemory) load("vkFreeMemory", userptr); - vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements) load("vkGetBufferMemoryRequirements", userptr); - vkGetDeviceMemoryCommitment = (PFN_vkGetDeviceMemoryCommitment) load("vkGetDeviceMemoryCommitment", userptr); - vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr) load("vkGetDeviceProcAddr", userptr); - vkGetDeviceQueue = (PFN_vkGetDeviceQueue) load("vkGetDeviceQueue", userptr); - vkGetEventStatus = (PFN_vkGetEventStatus) load("vkGetEventStatus", userptr); - vkGetFenceStatus = (PFN_vkGetFenceStatus) load("vkGetFenceStatus", userptr); - vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements) load("vkGetImageMemoryRequirements", userptr); - vkGetImageSparseMemoryRequirements = (PFN_vkGetImageSparseMemoryRequirements) load("vkGetImageSparseMemoryRequirements", userptr); - vkGetImageSubresourceLayout = (PFN_vkGetImageSubresourceLayout) load("vkGetImageSubresourceLayout", userptr); - vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) load("vkGetInstanceProcAddr", userptr); - vkGetPhysicalDeviceFeatures = (PFN_vkGetPhysicalDeviceFeatures) load("vkGetPhysicalDeviceFeatures", userptr); - vkGetPhysicalDeviceFormatProperties = (PFN_vkGetPhysicalDeviceFormatProperties) load("vkGetPhysicalDeviceFormatProperties", userptr); - vkGetPhysicalDeviceImageFormatProperties = (PFN_vkGetPhysicalDeviceImageFormatProperties) load("vkGetPhysicalDeviceImageFormatProperties", userptr); - vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) load("vkGetPhysicalDeviceMemoryProperties", userptr); - vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) load("vkGetPhysicalDeviceProperties", userptr); - vkGetPhysicalDeviceQueueFamilyProperties = (PFN_vkGetPhysicalDeviceQueueFamilyProperties) load("vkGetPhysicalDeviceQueueFamilyProperties", userptr); - vkGetPhysicalDeviceSparseImageFormatProperties = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties) load("vkGetPhysicalDeviceSparseImageFormatProperties", userptr); - vkGetPipelineCacheData = (PFN_vkGetPipelineCacheData) load("vkGetPipelineCacheData", userptr); - vkGetQueryPoolResults = (PFN_vkGetQueryPoolResults) load("vkGetQueryPoolResults", userptr); - vkGetRenderAreaGranularity = (PFN_vkGetRenderAreaGranularity) load("vkGetRenderAreaGranularity", userptr); - vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges) load("vkInvalidateMappedMemoryRanges", userptr); - vkMapMemory = (PFN_vkMapMemory) load("vkMapMemory", userptr); - vkMergePipelineCaches = (PFN_vkMergePipelineCaches) load("vkMergePipelineCaches", userptr); - vkQueueBindSparse = (PFN_vkQueueBindSparse) load("vkQueueBindSparse", userptr); - vkQueueSubmit = (PFN_vkQueueSubmit) load("vkQueueSubmit", userptr); - vkQueueWaitIdle = (PFN_vkQueueWaitIdle) load("vkQueueWaitIdle", userptr); - vkResetCommandBuffer = (PFN_vkResetCommandBuffer) load("vkResetCommandBuffer", userptr); - vkResetCommandPool = (PFN_vkResetCommandPool) load("vkResetCommandPool", userptr); - vkResetDescriptorPool = (PFN_vkResetDescriptorPool) load("vkResetDescriptorPool", userptr); - vkResetEvent = (PFN_vkResetEvent) load("vkResetEvent", userptr); - vkResetFences = (PFN_vkResetFences) load("vkResetFences", userptr); - vkSetEvent = (PFN_vkSetEvent) load("vkSetEvent", userptr); - vkUnmapMemory = (PFN_vkUnmapMemory) load("vkUnmapMemory", userptr); - vkUpdateDescriptorSets = (PFN_vkUpdateDescriptorSets) load("vkUpdateDescriptorSets", userptr); - vkWaitForFences = (PFN_vkWaitForFences) load("vkWaitForFences", userptr); -} -static void glad_vk_load_VK_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_VK_VERSION_1_1) return; - vkBindBufferMemory2 = (PFN_vkBindBufferMemory2) load("vkBindBufferMemory2", userptr); - vkBindImageMemory2 = (PFN_vkBindImageMemory2) load("vkBindImageMemory2", userptr); - vkCmdDispatchBase = (PFN_vkCmdDispatchBase) load("vkCmdDispatchBase", userptr); - vkCmdSetDeviceMask = (PFN_vkCmdSetDeviceMask) load("vkCmdSetDeviceMask", userptr); - vkCreateDescriptorUpdateTemplate = (PFN_vkCreateDescriptorUpdateTemplate) load("vkCreateDescriptorUpdateTemplate", userptr); - vkCreateSamplerYcbcrConversion = (PFN_vkCreateSamplerYcbcrConversion) load("vkCreateSamplerYcbcrConversion", userptr); - vkDestroyDescriptorUpdateTemplate = (PFN_vkDestroyDescriptorUpdateTemplate) load("vkDestroyDescriptorUpdateTemplate", userptr); - vkDestroySamplerYcbcrConversion = (PFN_vkDestroySamplerYcbcrConversion) load("vkDestroySamplerYcbcrConversion", userptr); - vkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion) load("vkEnumerateInstanceVersion", userptr); - vkEnumeratePhysicalDeviceGroups = (PFN_vkEnumeratePhysicalDeviceGroups) load("vkEnumeratePhysicalDeviceGroups", userptr); - vkGetBufferMemoryRequirements2 = (PFN_vkGetBufferMemoryRequirements2) load("vkGetBufferMemoryRequirements2", userptr); - vkGetDescriptorSetLayoutSupport = (PFN_vkGetDescriptorSetLayoutSupport) load("vkGetDescriptorSetLayoutSupport", userptr); - vkGetDeviceGroupPeerMemoryFeatures = (PFN_vkGetDeviceGroupPeerMemoryFeatures) load("vkGetDeviceGroupPeerMemoryFeatures", userptr); - vkGetDeviceQueue2 = (PFN_vkGetDeviceQueue2) load("vkGetDeviceQueue2", userptr); - vkGetImageMemoryRequirements2 = (PFN_vkGetImageMemoryRequirements2) load("vkGetImageMemoryRequirements2", userptr); - vkGetImageSparseMemoryRequirements2 = (PFN_vkGetImageSparseMemoryRequirements2) load("vkGetImageSparseMemoryRequirements2", userptr); - vkGetPhysicalDeviceExternalBufferProperties = (PFN_vkGetPhysicalDeviceExternalBufferProperties) load("vkGetPhysicalDeviceExternalBufferProperties", userptr); - vkGetPhysicalDeviceExternalFenceProperties = (PFN_vkGetPhysicalDeviceExternalFenceProperties) load("vkGetPhysicalDeviceExternalFenceProperties", userptr); - vkGetPhysicalDeviceExternalSemaphoreProperties = (PFN_vkGetPhysicalDeviceExternalSemaphoreProperties) load("vkGetPhysicalDeviceExternalSemaphoreProperties", userptr); - vkGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2) load("vkGetPhysicalDeviceFeatures2", userptr); - vkGetPhysicalDeviceFormatProperties2 = (PFN_vkGetPhysicalDeviceFormatProperties2) load("vkGetPhysicalDeviceFormatProperties2", userptr); - vkGetPhysicalDeviceImageFormatProperties2 = (PFN_vkGetPhysicalDeviceImageFormatProperties2) load("vkGetPhysicalDeviceImageFormatProperties2", userptr); - vkGetPhysicalDeviceMemoryProperties2 = (PFN_vkGetPhysicalDeviceMemoryProperties2) load("vkGetPhysicalDeviceMemoryProperties2", userptr); - vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) load("vkGetPhysicalDeviceProperties2", userptr); - vkGetPhysicalDeviceQueueFamilyProperties2 = (PFN_vkGetPhysicalDeviceQueueFamilyProperties2) load("vkGetPhysicalDeviceQueueFamilyProperties2", userptr); - vkGetPhysicalDeviceSparseImageFormatProperties2 = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties2) load("vkGetPhysicalDeviceSparseImageFormatProperties2", userptr); - vkTrimCommandPool = (PFN_vkTrimCommandPool) load("vkTrimCommandPool", userptr); - vkUpdateDescriptorSetWithTemplate = (PFN_vkUpdateDescriptorSetWithTemplate) load("vkUpdateDescriptorSetWithTemplate", userptr); -} -static void glad_vk_load_VK_EXT_debug_report( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_VK_EXT_debug_report) return; - vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT) load("vkCreateDebugReportCallbackEXT", userptr); - vkDebugReportMessageEXT = (PFN_vkDebugReportMessageEXT) load("vkDebugReportMessageEXT", userptr); - vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) load("vkDestroyDebugReportCallbackEXT", userptr); -} -static void glad_vk_load_VK_KHR_surface( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_VK_KHR_surface) return; - vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR) load("vkDestroySurfaceKHR", userptr); - vkGetPhysicalDeviceSurfaceCapabilitiesKHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR) load("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", userptr); - vkGetPhysicalDeviceSurfaceFormatsKHR = (PFN_vkGetPhysicalDeviceSurfaceFormatsKHR) load("vkGetPhysicalDeviceSurfaceFormatsKHR", userptr); - vkGetPhysicalDeviceSurfacePresentModesKHR = (PFN_vkGetPhysicalDeviceSurfacePresentModesKHR) load("vkGetPhysicalDeviceSurfacePresentModesKHR", userptr); - vkGetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR) load("vkGetPhysicalDeviceSurfaceSupportKHR", userptr); -} -static void glad_vk_load_VK_KHR_swapchain( GLADuserptrloadfunc load, void* userptr) { - if(!GLAD_VK_KHR_swapchain) return; - vkAcquireNextImage2KHR = (PFN_vkAcquireNextImage2KHR) load("vkAcquireNextImage2KHR", userptr); - vkAcquireNextImageKHR = (PFN_vkAcquireNextImageKHR) load("vkAcquireNextImageKHR", userptr); - vkCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR) load("vkCreateSwapchainKHR", userptr); - vkDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR) load("vkDestroySwapchainKHR", userptr); - vkGetDeviceGroupPresentCapabilitiesKHR = (PFN_vkGetDeviceGroupPresentCapabilitiesKHR) load("vkGetDeviceGroupPresentCapabilitiesKHR", userptr); - vkGetDeviceGroupSurfacePresentModesKHR = (PFN_vkGetDeviceGroupSurfacePresentModesKHR) load("vkGetDeviceGroupSurfacePresentModesKHR", userptr); - vkGetPhysicalDevicePresentRectanglesKHR = (PFN_vkGetPhysicalDevicePresentRectanglesKHR) load("vkGetPhysicalDevicePresentRectanglesKHR", userptr); - vkGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR) load("vkGetSwapchainImagesKHR", userptr); - vkQueuePresentKHR = (PFN_vkQueuePresentKHR) load("vkQueuePresentKHR", userptr); -} - - - -static int glad_vk_get_extensions( VkPhysicalDevice physical_device, uint32_t *out_extension_count, char ***out_extensions) { - uint32_t i; - uint32_t instance_extension_count = 0; - uint32_t device_extension_count = 0; - uint32_t max_extension_count; - uint32_t total_extension_count; - char **extensions; - VkExtensionProperties *ext_properties; - VkResult result; - - if (vkEnumerateInstanceExtensionProperties == NULL || (physical_device != NULL && vkEnumerateDeviceExtensionProperties == NULL)) { - return 0; - } - - result = vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, NULL); - if (result != VK_SUCCESS) { - return 0; - } - - if (physical_device != NULL) { - result = vkEnumerateDeviceExtensionProperties(physical_device, NULL, &device_extension_count, NULL); - if (result != VK_SUCCESS) { - return 0; - } - } - - total_extension_count = instance_extension_count + device_extension_count; - max_extension_count = instance_extension_count > device_extension_count - ? instance_extension_count : device_extension_count; - - ext_properties = (VkExtensionProperties*) malloc(max_extension_count * sizeof(VkExtensionProperties)); - if (ext_properties == NULL) { - return 0; - } - - result = vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, ext_properties); - if (result != VK_SUCCESS) { - free((void*) ext_properties); - return 0; - } - - extensions = (char**) calloc(total_extension_count, sizeof(char*)); - if (extensions == NULL) { - free((void*) ext_properties); - return 0; - } - - for (i = 0; i < instance_extension_count; ++i) { - VkExtensionProperties ext = ext_properties[i]; - - size_t extension_name_length = strlen(ext.extensionName) + 1; - extensions[i] = (char*) malloc(extension_name_length * sizeof(char)); - memcpy(extensions[i], ext.extensionName, extension_name_length * sizeof(char)); - } - - if (physical_device != NULL) { - result = vkEnumerateDeviceExtensionProperties(physical_device, NULL, &device_extension_count, ext_properties); - if (result != VK_SUCCESS) { - for (i = 0; i < instance_extension_count; ++i) { - free((void*) extensions[i]); - } - free(extensions); - return 0; - } - - for (i = 0; i < device_extension_count; ++i) { - VkExtensionProperties ext = ext_properties[i]; - - size_t extension_name_length = strlen(ext.extensionName) + 1; - extensions[instance_extension_count + i] = (char*) malloc(extension_name_length * sizeof(char)); - memcpy(extensions[instance_extension_count + i], ext.extensionName, extension_name_length * sizeof(char)); - } - } - - free((void*) ext_properties); - - *out_extension_count = total_extension_count; - *out_extensions = extensions; - - return 1; -} - -static void glad_vk_free_extensions(uint32_t extension_count, char **extensions) { - uint32_t i; - - for(i = 0; i < extension_count ; ++i) { - free((void*) (extensions[i])); - } - - free((void*) extensions); -} - -static int glad_vk_has_extension(const char *name, uint32_t extension_count, char **extensions) { - uint32_t i; - - for (i = 0; i < extension_count; ++i) { - if(strcmp(name, extensions[i]) == 0) { - return 1; - } - } - - return 0; -} - -static GLADapiproc glad_vk_get_proc_from_userptr(const char* name, void *userptr) { - return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); -} - -static int glad_vk_find_extensions_vulkan( VkPhysicalDevice physical_device) { - uint32_t extension_count = 0; - char **extensions = NULL; - if (!glad_vk_get_extensions(physical_device, &extension_count, &extensions)) return 0; - - GLAD_VK_EXT_debug_report = glad_vk_has_extension("VK_EXT_debug_report", extension_count, extensions); - GLAD_VK_KHR_surface = glad_vk_has_extension("VK_KHR_surface", extension_count, extensions); - GLAD_VK_KHR_swapchain = glad_vk_has_extension("VK_KHR_swapchain", extension_count, extensions); - - glad_vk_free_extensions(extension_count, extensions); - - return 1; -} - -static int glad_vk_find_core_vulkan( VkPhysicalDevice physical_device) { - int major = 1; - int minor = 0; - -#ifdef VK_VERSION_1_1 - if (vkEnumerateInstanceVersion != NULL) { - uint32_t version; - VkResult result; - - result = vkEnumerateInstanceVersion(&version); - if (result == VK_SUCCESS) { - major = (int) VK_VERSION_MAJOR(version); - minor = (int) VK_VERSION_MINOR(version); - } - } -#endif - - if (physical_device != NULL && vkGetPhysicalDeviceProperties != NULL) { - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(physical_device, &properties); - - major = (int) VK_VERSION_MAJOR(properties.apiVersion); - minor = (int) VK_VERSION_MINOR(properties.apiVersion); - } - - GLAD_VK_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; - GLAD_VK_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; - - return GLAD_MAKE_VERSION(major, minor); -} - -int gladLoadVulkanUserPtr( VkPhysicalDevice physical_device, GLADuserptrloadfunc load, void *userptr) { - int version; - -#ifdef VK_VERSION_1_1 - vkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion) load("vkEnumerateInstanceVersion", userptr); -#endif - version = glad_vk_find_core_vulkan( physical_device); - if (!version) { - return 0; - } - - glad_vk_load_VK_VERSION_1_0(load, userptr); - glad_vk_load_VK_VERSION_1_1(load, userptr); - - if (!glad_vk_find_extensions_vulkan( physical_device)) return 0; - glad_vk_load_VK_EXT_debug_report(load, userptr); - glad_vk_load_VK_KHR_surface(load, userptr); - glad_vk_load_VK_KHR_swapchain(load, userptr); - - - return version; -} - - -int gladLoadVulkan( VkPhysicalDevice physical_device, GLADloadfunc load) { - return gladLoadVulkanUserPtr( physical_device, glad_vk_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); -} - - - - diff --git a/src/external/glfw/include/GLFW/glfw3.h b/src/external/glfw/include/GLFW/glfw3.h index 7728dad10..268754657 100644 --- a/src/external/glfw/include/GLFW/glfw3.h +++ b/src/external/glfw/include/GLFW/glfw3.h @@ -190,6 +190,9 @@ extern "C" { #else /*__APPLE__*/ #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif #endif /*__APPLE__*/ @@ -259,13 +262,12 @@ extern "C" { /* We are building GLFW as a Win32 DLL */ #define GLFWAPI __declspec(dllexport) #elif defined(_WIN32) && defined(GLFW_DLL) - /* We are calling GLFW as a Win32 DLL */ + /* We are calling a GLFW Win32 DLL */ #define GLFWAPI __declspec(dllimport) #elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) - /* We are building GLFW as a shared / dynamic library */ + /* We are building GLFW as a Unix shared library */ #define GLFWAPI __attribute__((visibility("default"))) #else - /* We are building or calling GLFW as a static library */ #define GLFWAPI #endif @@ -372,7 +374,7 @@ extern "C" { * * The naming of the key codes follow these rules: * - The US keyboard layout is used - * - Names of printable alpha-numeric characters are used (e.g. "A", "R", + * - Names of printable alphanumeric characters are used (e.g. "A", "R", * "3", etc.) * - For non-alphanumeric characters, Unicode:ish names are used (e.g. * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not @@ -717,7 +719,7 @@ extern "C" { * GLFW could not find support for the requested API on the system. * * @analysis The installed graphics driver does not support the requested - * API, or does not support it via the chosen context creation backend. + * API, or does not support it via the chosen context creation API. * Below are a few examples. * * @par @@ -786,7 +788,7 @@ extern "C" { /*! @brief The specified cursor shape is not available. * * The specified standard cursor shape is not available, either because the - * current system cursor theme does not provide it or because it is not + * current platform cursor theme does not provide it or because it is not * available on the platform. * * @analysis Platform or system settings limitation. Pick another @@ -821,6 +823,28 @@ extern "C" { * updating any existing out parameters. */ #define GLFW_FEATURE_UNIMPLEMENTED 0x0001000D +/*! @brief Platform unavailable or no matching platform was found. + * + * If emitted during initialization, no matching platform was found. If @ref + * GLFW_PLATFORM is set to `GLFW_ANY_PLATFORM`, GLFW could not detect any of the + * platforms supported by this library binary, except for the Null platform. If set to + * a specific platform, it is either not supported by this library binary or GLFW was not + * able to detect it. + * + * If emitted by a native access function, GLFW was initialized for a different platform + * than the function is for. + * + * @analysis Failure to detect any platform usually only happens on non-macOS Unix + * systems, either when no window system is running or the program was run from + * a terminal that does not have the necessary environment variables. Fall back to + * a different platform if possible or notify the user that no usable platform was + * detected. + * + * Failure to detect a specific platform may have the same cause as above or be because + * support for that platform was not compiled in. Call @ref glfwPlatformSupported to + * check whether a specific platform is supported by a library binary. + */ +#define GLFW_PLATFORM_UNAVAILABLE 0x0001000E /*! @} */ /*! @addtogroup window @@ -903,6 +927,18 @@ extern "C" { */ #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +/*! @brief Initial position x-coordinate window hint. + * + * Initial position x-coordinate [window hint](@ref GLFW_POSITION_X). + */ +#define GLFW_POSITION_X 0x0002000E + +/*! @brief Initial position y-coordinate window hint. + * + * Initial position y-coordinate [window hint](@ref GLFW_POSITION_Y). + */ +#define GLFW_POSITION_Y 0x0002000F + /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). @@ -1003,7 +1039,7 @@ extern "C" { * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). */ #define GLFW_CONTEXT_VERSION_MINOR 0x00022003 -/*! @brief Context client API revision number hint and attribute. +/*! @brief Context client API revision number attribute. * * Context client API revision number * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). @@ -1081,6 +1117,12 @@ extern "C" { */ #define GLFW_X11_INSTANCE_NAME 0x00024002 #define GLFW_WIN32_KEYBOARD_MENU 0x00025001 +/*! @brief Wayland specific + * [window hint](@ref GLFW_WAYLAND_APP_ID_hint). + * + * Allows specification of the Wayland app_id. + */ +#define GLFW_WAYLAND_APP_ID 0x00026001 /*! @} */ #define GLFW_NO_API 0 @@ -1104,6 +1146,7 @@ extern "C" { #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 #define GLFW_CURSOR_DISABLED 0x00034003 +#define GLFW_CURSOR_CAPTURED 0x00034004 #define GLFW_ANY_RELEASE_BEHAVIOR 0 #define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 @@ -1121,11 +1164,13 @@ extern "C" { #define GLFW_ANGLE_PLATFORM_TYPE_VULKAN 0x00037007 #define GLFW_ANGLE_PLATFORM_TYPE_METAL 0x00037008 +#define GLFW_ANY_POSITION 0x80000000 + /*! @defgroup shapes Standard cursor shapes * @brief Standard system cursor shapes. * * These are the [standard cursor shapes](@ref cursor_standard) that can be - * requested from the window system. + * requested from the platform (window system). * * @ingroup input * @{ */ @@ -1242,6 +1287,11 @@ extern "C" { * ANGLE rendering backend [init hint](@ref GLFW_ANGLE_PLATFORM_TYPE_hint). */ #define GLFW_ANGLE_PLATFORM_TYPE 0x00050002 +/*! @brief Platform selection init hint. + * + * Platform selection [init hint](@ref GLFW_PLATFORM). + */ +#define GLFW_PLATFORM 0x00050003 /*! @brief macOS specific init hint. * * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). @@ -1259,6 +1309,20 @@ extern "C" { #define GLFW_X11_XCB_VULKAN_SURFACE 0x00052001 /*! @} */ +/*! @addtogroup init + * @{ */ +/*! @brief Hint value that enables automatic platform selection. + * + * Hint value for @ref GLFW_PLATFORM that enables automatic platform selection. + */ +#define GLFW_ANY_PLATFORM 0x00060000 +#define GLFW_PLATFORM_WIN32 0x00060001 +#define GLFW_PLATFORM_COCOA 0x00060002 +#define GLFW_PLATFORM_WAYLAND 0x00060003 +#define GLFW_PLATFORM_X11 0x00060004 +#define GLFW_PLATFORM_NULL 0x00060005 +/*! @} */ + #define GLFW_DONT_CARE -1 @@ -1330,6 +1394,131 @@ typedef struct GLFWwindow GLFWwindow; */ typedef struct GLFWcursor GLFWcursor; +/*! @brief The function pointer type for memory allocation callbacks. + * + * This is the function pointer type for memory allocation callbacks. A memory + * allocation callback function has the following signature: + * @code + * void* function_name(size_t size, void* user) + * @endcode + * + * This function must return either a memory block at least `size` bytes long, + * or `NULL` if allocation failed. Note that not all parts of GLFW handle allocation + * failures gracefully yet. + * + * This function may be called during @ref glfwInit but before the library is + * flagged as initialized, as well as during @ref glfwTerminate after the + * library is no longer flagged as initialized. + * + * Any memory allocated by this function will be deallocated during library + * termination or earlier. + * + * The size will always be greater than zero. Allocations of size zero are filtered out + * before reaching the custom allocator. + * + * @param[in] size The minimum size, in bytes, of the memory block. + * @param[in] user The user-defined pointer from the allocator. + * @return The address of the newly allocated memory block, or `NULL` if an + * error occurred. + * + * @pointer_lifetime The returned memory block must be valid at least until it + * is deallocated. + * + * @reentrancy This function should not call any GLFW function. + * + * @thread_safety This function may be called from any thread that calls GLFW functions. + * + * @sa @ref init_allocator + * @sa @ref GLFWallocator + * + * @since Added in version 3.4. + * + * @ingroup init + */ +typedef void* (* GLFWallocatefun)(size_t size, void* user); + +/*! @brief The function pointer type for memory reallocation callbacks. + * + * This is the function pointer type for memory reallocation callbacks. + * A memory reallocation callback function has the following signature: + * @code + * void* function_name(void* block, size_t size, void* user) + * @endcode + * + * This function must return a memory block at least `size` bytes long, or + * `NULL` if allocation failed. Note that not all parts of GLFW handle allocation + * failures gracefully yet. + * + * This function may be called during @ref glfwInit but before the library is + * flagged as initialized, as well as during @ref glfwTerminate after the + * library is no longer flagged as initialized. + * + * Any memory allocated by this function will be deallocated during library + * termination or earlier. + * + * The block address will never be `NULL` and the size will always be greater than zero. + * Reallocations of a block to size zero are converted into deallocations. Reallocations + * of `NULL` to a non-zero size are converted into regular allocations. + * + * @param[in] block The address of the memory block to reallocate. + * @param[in] size The new minimum size, in bytes, of the memory block. + * @param[in] user The user-defined pointer from the allocator. + * @return The address of the newly allocated or resized memory block, or + * `NULL` if an error occurred. + * + * @pointer_lifetime The returned memory block must be valid at least until it + * is deallocated. + * + * @reentrancy This function should not call any GLFW function. + * + * @thread_safety This function may be called from any thread that calls GLFW functions. + * + * @sa @ref init_allocator + * @sa @ref GLFWallocator + * + * @since Added in version 3.4. + * + * @ingroup init + */ +typedef void* (* GLFWreallocatefun)(void* block, size_t size, void* user); + +/*! @brief The function pointer type for memory deallocation callbacks. + * + * This is the function pointer type for memory deallocation callbacks. + * A memory deallocation callback function has the following signature: + * @code + * void function_name(void* block, void* user) + * @endcode + * + * This function may deallocate the specified memory block. This memory block + * will have been allocated with the same allocator. + * + * This function may be called during @ref glfwInit but before the library is + * flagged as initialized, as well as during @ref glfwTerminate after the + * library is no longer flagged as initialized. + * + * The block address will never be `NULL`. Deallocations of `NULL` are filtered out + * before reaching the custom allocator. + * + * @param[in] block The address of the memory block to deallocate. + * @param[in] user The user-defined pointer from the allocator. + * + * @pointer_lifetime The specified memory block will not be accessed by GLFW + * after this function is called. + * + * @reentrancy This function should not call any GLFW function. + * + * @thread_safety This function may be called from any thread that calls GLFW functions. + * + * @sa @ref init_allocator + * @sa @ref GLFWallocator + * + * @since Added in version 3.4. + * + * @ingroup init + */ +typedef void (* GLFWdeallocatefun)(void* block, void* user); + /*! @brief The function pointer type for error callbacks. * * This is the function pointer type for error callbacks. An error callback @@ -1352,7 +1541,7 @@ typedef struct GLFWcursor GLFWcursor; * * @ingroup init */ -typedef void (* GLFWerrorfun)(int,const char*); +typedef void (* GLFWerrorfun)(int error_code, const char* description); /*! @brief The function pointer type for window position callbacks. * @@ -1375,7 +1564,7 @@ typedef void (* GLFWerrorfun)(int,const char*); * * @ingroup window */ -typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); +typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos); /*! @brief The function pointer type for window size callbacks. * @@ -1397,7 +1586,7 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); +typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int width, int height); /*! @brief The function pointer type for window close callbacks. * @@ -1417,7 +1606,7 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowclosefun)(GLFWwindow*); +typedef void (* GLFWwindowclosefun)(GLFWwindow* window); /*! @brief The function pointer type for window content refresh callbacks. * @@ -1437,7 +1626,7 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*); * * @ingroup window */ -typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); +typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window); /*! @brief The function pointer type for window focus callbacks. * @@ -1458,7 +1647,7 @@ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); * * @ingroup window */ -typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); +typedef void (* GLFWwindowfocusfun)(GLFWwindow* window, int focused); /*! @brief The function pointer type for window iconify callbacks. * @@ -1479,7 +1668,7 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); +typedef void (* GLFWwindowiconifyfun)(GLFWwindow* window, int iconified); /*! @brief The function pointer type for window maximize callbacks. * @@ -1500,7 +1689,7 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); +typedef void (* GLFWwindowmaximizefun)(GLFWwindow* window, int maximized); /*! @brief The function pointer type for framebuffer size callbacks. * @@ -1521,7 +1710,7 @@ typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); +typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height); /*! @brief The function pointer type for window content scale callbacks. * @@ -1542,7 +1731,7 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale); /*! @brief The function pointer type for mouse button callbacks. * @@ -1568,7 +1757,7 @@ typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); * * @ingroup input */ -typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); +typedef void (* GLFWmousebuttonfun)(GLFWwindow* window, int button, int action, int mods); /*! @brief The function pointer type for cursor position callbacks. * @@ -1591,7 +1780,7 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * * @ingroup input */ -typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); +typedef void (* GLFWcursorposfun)(GLFWwindow* window, double xpos, double ypos); /*! @brief The function pointer type for cursor enter/leave callbacks. * @@ -1612,7 +1801,7 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); * * @ingroup input */ -typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); +typedef void (* GLFWcursorenterfun)(GLFWwindow* window, int entered); /*! @brief The function pointer type for scroll callbacks. * @@ -1633,7 +1822,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); * * @ingroup input */ -typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); +typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffset); /*! @brief The function pointer type for keyboard key callbacks. * @@ -1645,7 +1834,7 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * * @param[in] window The window that received the event. * @param[in] key The [keyboard key](@ref keys) that was pressed or released. - * @param[in] scancode The system-specific scancode of the key. + * @param[in] scancode The platform-specific scancode of the key. * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future * releases may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were @@ -1659,7 +1848,7 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * * @ingroup input */ -typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); +typedef void (* GLFWkeyfun)(GLFWwindow* window, int key, int scancode, int action, int mods); /*! @brief The function pointer type for Unicode character callbacks. * @@ -1680,7 +1869,7 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); * * @ingroup input */ -typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); +typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); /*! @brief The function pointer type for Unicode character with modifiers * callbacks. @@ -1707,7 +1896,7 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * * @ingroup input */ -typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); +typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); /*! @brief The function pointer type for path drop callbacks. * @@ -1731,7 +1920,7 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); * * @ingroup input */ -typedef void (* GLFWdropfun)(GLFWwindow*,int,const char*[]); +typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]); /*! @brief The function pointer type for monitor configuration callbacks. * @@ -1752,7 +1941,7 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char*[]); * * @ingroup monitor */ -typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); +typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); /*! @brief The function pointer type for joystick configuration callbacks. * @@ -1773,7 +1962,7 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * * @ingroup input */ -typedef void (* GLFWjoystickfun)(int,int); +typedef void (* GLFWjoystickfun)(int jid, int event); /*! @brief Video mode type. * @@ -1887,6 +2076,23 @@ typedef struct GLFWgamepadstate float axes[6]; } GLFWgamepadstate; +/*! @brief + * + * @sa @ref init_allocator + * @sa @ref glfwInitAllocator + * + * @since Added in version 3.4. + * + * @ingroup init + */ +typedef struct GLFWallocator +{ + GLFWallocatefun allocate; + GLFWreallocatefun reallocate; + GLFWdeallocatefun deallocate; + void* user; +} GLFWallocator; + /************************************************************************* * GLFW API functions @@ -1905,10 +2111,15 @@ typedef struct GLFWgamepadstate * Additional calls to this function after successful initialization but before * termination will return `GLFW_TRUE` immediately. * + * The @ref GLFW_PLATFORM init hint controls which platforms are considered during + * initialization. This also depends on which platforms the library was compiled to + * support. + * * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_PLATFORM_UNAVAILABLE and @ref + * GLFW_PLATFORM_ERROR. * * @remark @macos This function will change the current directory of the * application to the `Contents/Resources` subdirectory of the application's @@ -1930,6 +2141,8 @@ typedef struct GLFWgamepadstate * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init + * @sa @ref glfwInitHint + * @sa @ref glfwInitAllocator * @sa @ref glfwTerminate * * @since Added in version 1.0. @@ -2004,6 +2217,81 @@ GLFWAPI void glfwTerminate(void); */ GLFWAPI void glfwInitHint(int hint, int value); +/*! @brief Sets the init allocator to the desired value. + * + * To use the default allocator, call this function with a `NULL` argument. + * + * If you specify an allocator struct, every member must be a valid function + * pointer. If any member is `NULL`, this function emits @ref + * GLFW_INVALID_VALUE and the init allocator is unchanged. + * + * @param[in] allocator The allocator to use at the next initialization, or + * `NULL` to use the default one. + * + * @errors Possible errors include @ref GLFW_INVALID_VALUE. + * + * @pointer_lifetime The specified allocator is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref init_allocator + * @sa @ref glfwInit + * + * @since Added in version 3.4. + * + * @ingroup init + */ +GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator); + +#if defined(VK_VERSION_1_0) + +/*! @brief Sets the desired Vulkan `vkGetInstanceProcAddr` function. + * + * This function sets the `vkGetInstanceProcAddr` function that GLFW will use for all + * Vulkan related entry point queries. + * + * This feature is mostly useful on macOS, if your copy of the Vulkan loader is in + * a location where GLFW cannot find it through dynamic loading, or if you are still + * using the static library version of the loader. + * + * If set to `NULL`, GLFW will try to load the Vulkan loader dynamically by its standard + * name and get this function from there. This is the default behavior. + * + * The standard name of the loader is `vulkan-1.dll` on Windows, `libvulkan.so.1` on + * Linux and other Unix-like systems and `libvulkan.1.dylib` on macOS. If your code is + * also loading it via these names then you probably don't need to use this function. + * + * The function address you set is never reset by GLFW, but it only takes effect during + * initialization. Once GLFW has been initialized, any updates will be ignored until the + * library is terminated and initialized again. + * + * @param[in] loader The address of the function to use, or `NULL`. + * + * @par Loader function signature + * @code + * PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance instance, const char* name) + * @endcode + * For more information about this function, see the + * [Vulkan Registry](https://www.khronos.org/registry/vulkan/). + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref vulkan_loader + * @sa @ref glfwInit + * + * @since Added in version 3.4. + * + * @ingroup init + */ +GLFWAPI void glfwInitVulkanLoader(PFN_vkGetInstanceProcAddr loader); + +#endif /*VK_VERSION_1_0*/ + /*! @brief Retrieves the version of the GLFW library. * * This function retrieves the major, minor and revision numbers of the GLFW @@ -2034,15 +2322,18 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); /*! @brief Returns a string describing the compile-time configuration. * * This function returns the compile-time generated - * [version string](@ref intro_version_string) of the GLFW library binary. It - * describes the version, platform, compiler and any platform-specific - * compile-time options. It should not be confused with the OpenGL or OpenGL - * ES version string, queried with `glGetString`. + * [version string](@ref intro_version_string) of the GLFW library binary. It describes + * the version, platforms, compiler and any platform or operating system specific + * compile-time options. It should not be confused with the OpenGL or OpenGL ES version + * string, queried with `glGetString`. * * __Do not use the version string__ to parse the GLFW library version. The * @ref glfwGetVersion function provides the version of the running library * binary in numerical format. * + * __Do not use the version string__ to parse what platforms are supported. The @ref + * glfwPlatformSupported function lets you query platform support. + * * @return The ASCII encoded GLFW version string. * * @errors None. @@ -2139,6 +2430,51 @@ GLFWAPI int glfwGetError(const char** description); */ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); +/*! @brief Returns the currently selected platform. + * + * This function returns the platform that was selected during initialization. The + * returned value will be one of `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, + * `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`. + * + * @return The currently selected platform, or zero if an error occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref platform + * @sa @ref glfwPlatformSupported + * + * @since Added in version 3.4. + * + * @ingroup init + */ +GLFWAPI int glfwGetPlatform(void); + +/*! @brief Returns whether the library includes support for the specified platform. + * + * This function returns whether the library was compiled with support for the specified + * platform. The platform must be one of `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, + * `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`. + * + * @param[in] platform The platform to query. + * @return `GLFW_TRUE` if the platform is supported, or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_INVALID_ENUM. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref platform + * @sa @ref glfwGetPlatform + * + * @since Added in version 3.4. + * + * @ingroup init + */ +GLFWAPI int glfwPlatformSupported(int platform); + /*! @brief Returns the currently connected monitors. * * This function returns an array of handles for all currently connected @@ -2222,7 +2558,7 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); * This function returns the position, in screen coordinates, of the upper-left * corner of the work area of the specified monitor along with the work area * size in screen coordinates. The work area is defined as the area of the - * monitor not occluded by the operating system task bar where present. If no + * monitor not occluded by the window system task bar where present. If no * task bar exists then the work area is the monitor resolution in screen * coordinates. * @@ -2253,7 +2589,7 @@ GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, * This function returns the size, in millimetres, of the display area of the * specified monitor. * - * Some systems do not provide accurate monitor size information, either + * Some platforms do not provide accurate monitor size information, either * because the monitor * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) * data is incorrect or because the driver does not report it accurately. @@ -2269,8 +2605,8 @@ GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @win32 calculates the returned physical size from the - * current resolution and system DPI instead of querying the monitor EDID data. + * @remark @win32 On Windows 8 and earlier the physical size is calculated from + * the current resolution and system DPI instead of querying the monitor EDID data. * * @thread_safety This function must only be called from the main thread. * @@ -2714,10 +3050,10 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * OpenGL or OpenGL ES context. * * By default, newly created windows use the placement recommended by the - * window system. To create the window at a specific position, make it - * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window - * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) - * it. + * window system. To create the window at a specific position, set the @ref + * GLFW_POSITION_X and @ref GLFW_POSITION_Y window hints before creation. To + * restore the default behavior, set either or both hints back to + * `GLFW_ANY_POSITION`. * * As long as at least one full screen window is not iconified, the screensaver * is prohibited from starting. @@ -2942,7 +3278,8 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * count is zero. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks). + * GLFW_INVALID_VALUE, @ref GLFW_PLATFORM_ERROR and @ref + * GLFW_FEATURE_UNAVAILABLE (see remarks). * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -3267,7 +3604,7 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * regardless of their DPI and scaling settings. This relies on the system DPI * and scaling settings being somewhat correct. * - * On systems where each monitors can have its own content scale, the window + * On platforms where each monitors can have its own content scale, the window * content scale will depend on which monitor the system considers the window * to be on. * @@ -3355,8 +3692,9 @@ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); * previously restored. If the window is already iconified, this function does * nothing. * - * If the specified window is a full screen window, the original monitor - * resolution is restored until the window is restored. + * If the specified window is a full screen window, GLFW restores the original + * video mode of the monitor. The window's desired video mode is set again + * when the window is restored. * * @param[in] window The window to iconify. * @@ -3386,8 +3724,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); * (minimized) or maximized. If the window is already restored, this function * does nothing. * - * If the specified window is a full screen window, the resolution chosen for - * the window is restored on the selected monitor. + * If the specified window is an iconified full screen window, its desired + * video mode is set again for its monitor when the window is restored. * * @param[in] window The window to restore. * @@ -3448,6 +3786,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Because Wayland wants every frame of the desktop to be + * complete, this function does not immediately make the window visible. + * Instead it will become visible the next time the window framebuffer is + * updated after this call. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide @@ -3650,6 +3993,9 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int * errors. However, this function should not fail as long as it is passed * valid arguments and the library has been [initialized](@ref intro_init). * + * @remark @wayland The Wayland protocol provides no way to check whether a + * window is iconfied, so @ref GLFW_ICONIFIED always returns `GLFW_FALSE`. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs @@ -4235,6 +4581,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. + * - `GLFW_CURSOR_CAPTURED` makes the cursor visible and confines it to the + * content area of the window. * * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are @@ -4409,8 +4757,7 @@ GLFWAPI int glfwGetKeyScancode(int key); * * This function returns the last state reported for the specified key to the * specified window. The returned state is one of `GLFW_PRESS` or - * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to - * the key callback. + * `GLFW_RELEASE`. The action `GLFW_REPEAT` is only reported to the key callback. * * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns * `GLFW_PRESS` the first time you call it for a key that was pressed, even if @@ -4571,8 +4918,8 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @return The handle of the created cursor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -5387,6 +5734,8 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string); * joystick is not present, does not have a mapping or an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref GLFW_INVALID_ENUM. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected, the gamepad mappings are updated or the library is terminated. @@ -5476,8 +5825,8 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref @@ -5506,7 +5855,7 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); * * The resolution of the timer is system dependent, but is usually on the order * of a few micro- or nanoseconds. It uses the highest-resolution monotonic - * time source on each supported platform. + * time source on each operating system. * * @return The current time, in seconds, or zero if an * [error](@ref error_handling) occurred. @@ -5717,7 +6066,7 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. * * @remark This function is not called during context creation, leaving the - * swap interval set to whatever is the default on that platform. This is done + * swap interval set to whatever is the default for that API. This is done * because some swap interval extensions used by GLFW do not allow the swap * interval to be reset to zero once it has been set to a non-zero value. * @@ -5821,13 +6170,11 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); * This function returns whether the Vulkan loader and any minimally functional * ICD have been found. * - * The availability of a Vulkan loader and even an ICD does not by itself - * guarantee that surface creation or even instance creation is possible. - * For example, on Fermi systems Nvidia will install an ICD that provides no - * actual Vulkan support. Call @ref glfwGetRequiredInstanceExtensions to check - * whether the extensions necessary for Vulkan surface creation are available - * and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue - * family of a physical device supports image presentation. + * The availability of a Vulkan loader and even an ICD does not by itself guarantee that + * surface creation or even instance creation is possible. Call @ref + * glfwGetRequiredInstanceExtensions to check whether the extensions necessary for Vulkan + * surface creation are available and @ref glfwGetPhysicalDevicePresentationSupport to + * check whether a queue family of a physical device supports image presentation. * * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` * otherwise. @@ -5873,9 +6220,6 @@ GLFWAPI int glfwVulkanSupported(void); * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * - * @remark @macos GLFW currently supports both the `VK_MVK_macos_surface` and - * the newer `VK_EXT_metal_surface` extensions. - * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * library is terminated. @@ -6014,17 +6358,20 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. * - * @remark @macos This function currently only supports the - * `VK_MVK_macos_surface` extension from MoltenVK. + * @remark @macos GLFW prefers the `VK_EXT_metal_surface` extension, with the + * `VK_MVK_macos_surface` extension as a fallback. The name of the selected + * extension, if any, is included in the array returned by @ref + * glfwGetRequiredInstanceExtensions. * * @remark @macos This function creates and sets a `CAMetalLayer` instance for * the window content view, which is required for MoltenVK to function. * - * @remark @x11 GLFW by default attempts to use the `VK_KHR_xcb_surface` - * extension, if available. You can make it prefer the `VK_KHR_xlib_surface` - * extension by setting the + * @remark @x11 By default GLFW prefers the `VK_KHR_xcb_surface` extension, + * with the `VK_KHR_xlib_surface` extension as a fallback. You can make + * `VK_KHR_xlib_surface` the preferred extension by setting the * [GLFW_X11_XCB_VULKAN_SURFACE](@ref GLFW_X11_XCB_VULKAN_SURFACE_hint) init - * hint. + * hint. The name of the selected extension, if any, is included in the array + * returned by @ref glfwGetRequiredInstanceExtensions. * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. @@ -6062,6 +6409,7 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window */ #ifndef GLAPIENTRY #define GLAPIENTRY APIENTRY + #define GLFW_GLAPIENTRY_DEFINED #endif /* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ diff --git a/src/external/glfw/include/GLFW/glfw3native.h b/src/external/glfw/include/GLFW/glfw3native.h index 323d3c77e..171abe36a 100644 --- a/src/external/glfw/include/GLFW/glfw3native.h +++ b/src/external/glfw/include/GLFW/glfw3native.h @@ -74,6 +74,16 @@ extern "C" { * and which platform-specific headers to include. It is then up your (by * definition platform-specific) code to handle which of these should be * defined. + * + * If you do not want the platform-specific headers to be included, define + * `GLFW_NATIVE_INCLUDE_NONE` before including the @ref glfw3native.h header. + * + * @code + * #define GLFW_EXPOSE_NATIVE_WIN32 + * #define GLFW_EXPOSE_NATIVE_WGL + * #define GLFW_NATIVE_INCLUDE_NONE + * #include + * @endcode */ @@ -81,46 +91,71 @@ extern "C" { * System headers and types *************************************************************************/ -#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) - // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for - // example to allow applications to correctly declare a GL_KHR_debug callback) - // but windows.h assumes no one will define APIENTRY before it does - #if defined(GLFW_APIENTRY_DEFINED) - #undef APIENTRY - #undef GLFW_APIENTRY_DEFINED - #endif -// @raysan5: Actually, only HWND handler needs to be defined -// Including windows.h could suppose symbols re-definition issues (i.e Rectangle type) -//#include -#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) - #if defined(__OBJC__) - #import - #else - #include - typedef void* id; - #endif -#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) - #include - #include -#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) - #include -#endif +#if !defined(GLFW_NATIVE_INCLUDE_NONE) -#if defined(GLFW_EXPOSE_NATIVE_WGL) - /* WGL is declared by windows.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_NSGL) - /* NSGL is declared by Cocoa.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_GLX) - #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_EGL) - #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_OSMESA) - #include -#endif + #if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) + /* This is a workaround for the fact that glfw3.h needs to export APIENTRY (for + * example to allow applications to correctly declare a GL_KHR_debug callback) + * but windows.h assumes no one will define APIENTRY before it does + */ + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif + #include + #endif + + #if defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) + #if defined(__OBJC__) + #import + #else + #include + #include + #endif + #endif + + #if defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) + #include + #include + #endif + + #if defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include + #endif + + #if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ + #endif + #if defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ + #endif + #if defined(GLFW_EXPOSE_NATIVE_GLX) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, glx.h will include gl.h, which will define it unconditionally + */ + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif + #include + #endif + #if defined(GLFW_EXPOSE_NATIVE_EGL) + #include + #endif + #if defined(GLFW_EXPOSE_NATIVE_OSMESA) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, osmesa.h will include gl.h, which will define it unconditionally + */ + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif + #include + #endif + +#endif /*GLFW_NATIVE_INCLUDE_NONE*/ /************************************************************************* @@ -134,6 +169,8 @@ extern "C" { * of the specified monitor, or `NULL` if an [error](@ref error_handling) * occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -149,6 +186,8 @@ GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -163,6 +202,16 @@ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); * @return The `HWND` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -179,6 +228,17 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); * @return The `HGLRC` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -195,6 +255,8 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); * @return The `CGDirectDisplayID` of the specified monitor, or * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -209,6 +271,8 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); * @return The `NSWindow` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -225,6 +289,9 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); * @return The `NSOpenGLContext` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -241,6 +308,8 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); * @return The `Display` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -255,6 +324,8 @@ GLFWAPI Display* glfwGetX11Display(void); * @return The `RRCrtc` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -269,6 +340,8 @@ GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); * @return The `RROutput` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -283,6 +356,8 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); * @return The `Window` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -349,6 +424,9 @@ GLFWAPI const char* glfwGetX11SelectionString(void); * @return The `GLXContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -363,6 +441,9 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); * @return The `GLXWindow` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -379,6 +460,8 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); * @return The `struct wl_display*` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -393,6 +476,8 @@ GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); * @return The `struct wl_output*` of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -407,6 +492,8 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); * @return The main `struct wl_surface*` of the specified window, or `NULL` if * an [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -423,6 +510,11 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark Because EGL is initialized on demand, this function will return + * `EGL_NO_DISPLAY` until the first context has been created via EGL. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -437,6 +529,9 @@ GLFWAPI EGLDisplay glfwGetEGLDisplay(void); * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -451,6 +546,9 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -474,6 +572,9 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -495,6 +596,9 @@ GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -509,6 +613,9 @@ GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height * @return The `OSMesaContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * diff --git a/src/external/glfw/src/CMakeLists.txt b/src/external/glfw/src/CMakeLists.txt index f6e404f2b..01f191c97 100644 --- a/src/external/glfw/src/CMakeLists.txt +++ b/src/external/glfw/src/CMakeLists.txt @@ -1,53 +1,82 @@ -add_library(glfw "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" +add_library(glfw ${GLFW_LIBRARY_TYPE} + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h" - internal.h mappings.h context.c init.c input.c monitor.c - vulkan.c window.c) + internal.h platform.h mappings.h + context.c init.c input.c monitor.c platform.c vulkan.c window.c + egl_context.c osmesa_context.c null_platform.h null_joystick.h + null_init.c null_monitor.c null_window.c null_joystick.c) -if (_GLFW_COCOA) - target_sources(glfw PRIVATE cocoa_platform.h cocoa_joystick.h posix_thread.h - nsgl_context.h egl_context.h osmesa_context.h - cocoa_init.m cocoa_joystick.m cocoa_monitor.m - cocoa_window.m cocoa_time.c posix_thread.c - nsgl_context.m egl_context.c osmesa_context.c) -elseif (_GLFW_WIN32) - target_sources(glfw PRIVATE win32_platform.h win32_joystick.h wgl_context.h - egl_context.h osmesa_context.h win32_init.c - win32_joystick.c win32_monitor.c win32_time.c - win32_thread.c win32_window.c wgl_context.c - egl_context.c osmesa_context.c) -elseif (_GLFW_X11) - target_sources(glfw PRIVATE x11_platform.h xkb_unicode.h posix_time.h - posix_thread.h glx_context.h egl_context.h - osmesa_context.h x11_init.c x11_monitor.c - x11_window.c xkb_unicode.c posix_time.c - posix_thread.c glx_context.c egl_context.c - osmesa_context.c) -elseif (_GLFW_WAYLAND) - target_sources(glfw PRIVATE wl_platform.h posix_time.h posix_thread.h - xkb_unicode.h egl_context.h osmesa_context.h - wl_init.c wl_monitor.c wl_window.c posix_time.c - posix_thread.c xkb_unicode.c egl_context.c - osmesa_context.c) -elseif (_GLFW_OSMESA) - target_sources(glfw PRIVATE null_platform.h null_joystick.h posix_time.h - posix_thread.h osmesa_context.h null_init.c - null_monitor.c null_window.c null_joystick.c - posix_time.c posix_thread.c osmesa_context.c) +# The time, thread and module code is shared between all backends on a given OS, +# including the null backend, which still needs those bits to be functional +if (APPLE) + target_sources(glfw PRIVATE cocoa_time.h cocoa_time.c posix_thread.h + posix_module.c posix_thread.c) +elseif (WIN32) + target_sources(glfw PRIVATE win32_time.h win32_thread.h win32_module.c + win32_time.c win32_thread.c) +else() + target_sources(glfw PRIVATE posix_time.h posix_thread.h posix_module.c + posix_time.c posix_thread.c) endif() -if (_GLFW_X11 OR _GLFW_WAYLAND) +add_custom_target(update_mappings + COMMAND "${CMAKE_COMMAND}" -P "${GLFW_SOURCE_DIR}/CMake/GenerateMappings.cmake" mappings.h.in mappings.h + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Updating gamepad mappings from upstream repository" + SOURCES mappings.h.in "${GLFW_SOURCE_DIR}/CMake/GenerateMappings.cmake" + VERBATIM) + +set_target_properties(update_mappings PROPERTIES FOLDER "GLFW3") + +if (GLFW_BUILD_COCOA) + target_compile_definitions(glfw PRIVATE _GLFW_COCOA) + target_sources(glfw PRIVATE cocoa_platform.h cocoa_joystick.h cocoa_init.m + cocoa_joystick.m cocoa_monitor.m cocoa_window.m + nsgl_context.m) +endif() + +if (GLFW_BUILD_WIN32) + target_compile_definitions(glfw PRIVATE _GLFW_WIN32) + target_sources(glfw PRIVATE win32_platform.h win32_joystick.h win32_init.c + win32_joystick.c win32_monitor.c win32_window.c + wgl_context.c) +endif() + +if (GLFW_BUILD_X11) + target_compile_definitions(glfw PRIVATE _GLFW_X11) + target_sources(glfw PRIVATE x11_platform.h xkb_unicode.h x11_init.c + x11_monitor.c x11_window.c xkb_unicode.c + glx_context.c) +endif() + +if (GLFW_BUILD_WAYLAND) + target_compile_definitions(glfw PRIVATE _GLFW_WAYLAND) + target_sources(glfw PRIVATE wl_platform.h xkb_unicode.h wl_init.c + wl_monitor.c wl_window.c xkb_unicode.c) +endif() + +if (GLFW_BUILD_X11 OR GLFW_BUILD_WAYLAND) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_sources(glfw PRIVATE linux_joystick.h linux_joystick.c) - else() - target_sources(glfw PRIVATE null_joystick.h null_joystick.c) endif() + target_sources(glfw PRIVATE posix_poll.h posix_poll.c) endif() -if (_GLFW_WAYLAND) +if (GLFW_BUILD_WAYLAND) + include(CheckIncludeFiles) + include(CheckFunctionExists) + check_function_exists(memfd_create HAVE_MEMFD_CREATE) + if (HAVE_MEMFD_CREATE) + target_compile_definitions(glfw PRIVATE HAVE_MEMFD_CREATE) + endif() + find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner) + + include(FindPkgConfig) pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols>=1.15) pkg_get_variable(WAYLAND_PROTOCOLS_BASE wayland-protocols pkgdatadir) + pkg_get_variable(WAYLAND_CLIENT_PKGDATADIR wayland-client pkgdatadir) macro(wayland_generate protocol_file output_file) add_custom_command(OUTPUT "${output_file}.h" @@ -55,14 +84,17 @@ if (_GLFW_WAYLAND) DEPENDS "${protocol_file}" VERBATIM) - add_custom_command(OUTPUT "${output_file}.c" - COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c" + add_custom_command(OUTPUT "${output_file}-code.h" + COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}-code.h" DEPENDS "${protocol_file}" VERBATIM) - target_sources(glfw PRIVATE "${output_file}.h" "${output_file}.c") + target_sources(glfw PRIVATE "${output_file}.h" "${output_file}-code.h") endmacro() + wayland_generate( + "${WAYLAND_CLIENT_PKGDATADIR}/wayland.xml" + "${GLFW_BINARY_DIR}/src/wayland-client-protocol") wayland_generate( "${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml" "${GLFW_BINARY_DIR}/src/wayland-xdg-shell-client-protocol") @@ -83,14 +115,17 @@ if (_GLFW_WAYLAND) "${GLFW_BINARY_DIR}/src/wayland-idle-inhibit-unstable-v1-client-protocol") endif() -if (WIN32 AND BUILD_SHARED_LIBS) +if (WIN32 AND GLFW_BUILD_SHARED_LIBRARY) configure_file(glfw.rc.in glfw.rc @ONLY) target_sources(glfw PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/glfw.rc") endif() -configure_file(glfw_config.h.in glfw_config.h @ONLY) -target_compile_definitions(glfw PRIVATE _GLFW_USE_CONFIG_H) -target_sources(glfw PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/glfw_config.h") +if (UNIX AND GLFW_BUILD_SHARED_LIBRARY) + # On Unix-like systems, shared libraries can use the soname system. + set(GLFW_LIB_NAME glfw) +else() + set(GLFW_LIB_NAME glfw3) +endif() set_target_properties(glfw PROPERTIES OUTPUT_NAME ${GLFW_LIB_NAME} @@ -107,28 +142,126 @@ target_include_directories(glfw PUBLIC "$") target_include_directories(glfw PRIVATE "${GLFW_SOURCE_DIR}/src" - "${GLFW_BINARY_DIR}/src" - ${glfw_INCLUDE_DIRS}) -target_link_libraries(glfw PRIVATE Threads::Threads ${glfw_LIBRARIES}) + "${GLFW_BINARY_DIR}/src") +target_link_libraries(glfw PRIVATE Threads::Threads) # Workaround for CMake not knowing about .m files before version 3.16 -if ("${CMAKE_VERSION}" VERSION_LESS "3.16" AND APPLE) +if (CMAKE_VERSION VERSION_LESS "3.16" AND APPLE) set_source_files_properties(cocoa_init.m cocoa_joystick.m cocoa_monitor.m cocoa_window.m nsgl_context.m PROPERTIES LANGUAGE C) endif() +if (GLFW_BUILD_WIN32) + list(APPEND glfw_PKG_LIBS "-lgdi32") +endif() + +if (GLFW_BUILD_COCOA) + target_link_libraries(glfw PRIVATE "-framework Cocoa" + "-framework IOKit" + "-framework CoreFoundation") + + set(glfw_PKG_DEPS "") + set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation") +endif() + +if (GLFW_BUILD_WAYLAND) + pkg_check_modules(Wayland REQUIRED + wayland-client>=0.2.7 + wayland-cursor>=0.2.7 + wayland-egl>=0.2.7 + xkbcommon>=0.5.0) + + target_include_directories(glfw PRIVATE ${Wayland_INCLUDE_DIRS}) + + if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_package(EpollShim) + if (EPOLLSHIM_FOUND) + target_include_directories(glfw PRIVATE ${EPOLLSHIM_INCLUDE_DIRS}) + target_link_libraries(glfw PRIVATE ${EPOLLSHIM_LIBRARIES}) + endif() + endif() +endif() + +if (GLFW_BUILD_X11) + find_package(X11 REQUIRED) + target_include_directories(glfw PRIVATE "${X11_X11_INCLUDE_PATH}") + + # Check for XRandR (modern resolution switching and gamma control) + if (NOT X11_Xrandr_INCLUDE_PATH) + message(FATAL_ERROR "RandR headers not found; install libxrandr development package") + endif() + target_include_directories(glfw PRIVATE "${X11_Xrandr_INCLUDE_PATH}") + + # Check for Xinerama (legacy multi-monitor support) + if (NOT X11_Xinerama_INCLUDE_PATH) + message(FATAL_ERROR "Xinerama headers not found; install libxinerama development package") + endif() + target_include_directories(glfw PRIVATE "${X11_Xinerama_INCLUDE_PATH}") + + # Check for Xkb (X keyboard extension) + if (NOT X11_Xkb_INCLUDE_PATH) + message(FATAL_ERROR "XKB headers not found; install X11 development package") + endif() + target_include_directories(glfw PRIVATE "${X11_Xkb_INCLUDE_PATH}") + + # Check for Xcursor (cursor creation from RGBA images) + if (NOT X11_Xcursor_INCLUDE_PATH) + message(FATAL_ERROR "Xcursor headers not found; install libxcursor development package") + endif() + target_include_directories(glfw PRIVATE "${X11_Xcursor_INCLUDE_PATH}") + + # Check for XInput (modern HID input) + if (NOT X11_Xi_INCLUDE_PATH) + message(FATAL_ERROR "XInput headers not found; install libxi development package") + endif() + target_include_directories(glfw PRIVATE "${X11_Xi_INCLUDE_PATH}") + + # Check for X Shape (custom window input shape) + if (NOT X11_Xshape_INCLUDE_PATH) + message(FATAL_ERROR "X Shape headers not found; install libxext development package") + endif() + target_include_directories(glfw PRIVATE "${X11_Xshape_INCLUDE_PATH}") +endif() + +if (UNIX AND NOT APPLE) + find_library(RT_LIBRARY rt) + mark_as_advanced(RT_LIBRARY) + if (RT_LIBRARY) + target_link_libraries(glfw PRIVATE "${RT_LIBRARY}") + list(APPEND glfw_PKG_LIBS "-lrt") + endif() + + find_library(MATH_LIBRARY m) + mark_as_advanced(MATH_LIBRARY) + if (MATH_LIBRARY) + target_link_libraries(glfw PRIVATE "${MATH_LIBRARY}") + list(APPEND glfw_PKG_LIBS "-lm") + endif() + + if (CMAKE_DL_LIBS) + target_link_libraries(glfw PRIVATE "${CMAKE_DL_LIBS}") + list(APPEND glfw_PKG_LIBS "-l${CMAKE_DL_LIBS}") + endif() +endif() + # Make GCC warn about declarations that VS 2010 and 2012 won't accept for all # source files that VS will build (Clang ignores this because we set -std=c99) if (CMAKE_C_COMPILER_ID STREQUAL "GNU") - set_source_files_properties(context.c init.c input.c monitor.c vulkan.c - window.c win32_init.c win32_joystick.c - win32_monitor.c win32_time.c win32_thread.c - win32_window.c wgl_context.c egl_context.c - osmesa_context.c PROPERTIES + set_source_files_properties(context.c init.c input.c monitor.c platform.c vulkan.c + window.c null_init.c null_joystick.c null_monitor.c + null_window.c win32_init.c win32_joystick.c win32_module.c + win32_monitor.c win32_time.c win32_thread.c win32_window.c + wgl_context.c egl_context.c osmesa_context.c PROPERTIES COMPILE_FLAGS -Wdeclaration-after-statement) endif() +if (WIN32) + if (GLFW_USE_HYBRID_HPG) + target_compile_definitions(glfw PRIVATE _GLFW_USE_HYBRID_HPG) + endif() +endif() + # Enable a reasonable set of warnings # NOTE: The order matters here, Clang-CL matches both MSVC and Clang if (MSVC) @@ -140,7 +273,7 @@ elseif (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR target_compile_options(glfw PRIVATE "-Wall") endif() -if (_GLFW_WIN32) +if (GLFW_BUILD_WIN32) target_compile_definitions(glfw PRIVATE UNICODE _UNICODE) endif() @@ -181,7 +314,14 @@ if (MSVC90) endif() endif() -if (BUILD_SHARED_LIBS) +# Workaround for -std=c99 on Linux disabling _DEFAULT_SOURCE (POSIX 2008 and more) +if (GLFW_BUILD_X11 OR GLFW_BUILD_WAYLAND) + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_compile_definitions(glfw PRIVATE _DEFAULT_SOURCE) + endif() +endif() + +if (GLFW_BUILD_SHARED_LIBRARY) if (WIN32) if (MINGW) # Remove the dependency on the shared version of libgcc @@ -236,6 +376,20 @@ if (BUILD_SHARED_LIBS) endif() endif() +foreach(arg ${glfw_PKG_DEPS}) + string(APPEND deps " ${arg}") +endforeach() +foreach(arg ${glfw_PKG_LIBS}) + string(APPEND libs " ${arg}") +endforeach() + +set(GLFW_PKG_CONFIG_REQUIRES_PRIVATE "${deps}" CACHE INTERNAL + "GLFW pkg-config Requires.private") +set(GLFW_PKG_CONFIG_LIBS_PRIVATE "${libs}" CACHE INTERNAL + "GLFW pkg-config Libs.private") + +configure_file("${GLFW_SOURCE_DIR}/CMake/glfw3.pc.in" glfw3.pc @ONLY) + if (GLFW_INSTALL) install(TARGETS glfw EXPORT glfwTargets diff --git a/src/external/glfw/src/cocoa_init.m b/src/external/glfw/src/cocoa_init.m index 626d95c23..aa369f9c8 100644 --- a/src/external/glfw/src/cocoa_init.m +++ b/src/external/glfw/src/cocoa_init.m @@ -75,7 +75,6 @@ static void changeToResourcesDirectory(void) // static void createMenuBar(void) { - size_t i; NSString* appName = nil; NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary]; NSString* nameKeys[] = @@ -87,7 +86,7 @@ static void createMenuBar(void) // Try to figure out what the calling application is called - for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++) + for (size_t i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++) { id name = bundleInfo[nameKeys[i]]; if (name && @@ -177,8 +176,6 @@ static void createMenuBar(void) // static void createKeyTables(void) { - int scancode; - memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); @@ -297,7 +294,7 @@ static void createKeyTables(void) _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; - for (scancode = 0; scancode < 256; scancode++) + for (int scancode = 0; scancode < 256; scancode++) { // Store the reverse translation for faster key name lookup if (_glfw.ns.keycodes[scancode] >= 0) @@ -307,7 +304,7 @@ static void createKeyTables(void) // Retrieve Unicode data for the current keyboard layout // -static GLFWbool updateUnicodeDataNS(void) +static GLFWbool updateUnicodeData(void) { if (_glfw.ns.inputSource) { @@ -377,7 +374,7 @@ static GLFWbool initializeTIS(void) _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = *kPropertyUnicodeKeyLayoutData; - return updateUnicodeDataNS(); + return updateUnicodeData(); } @interface GLFWHelper : NSObject @@ -387,7 +384,7 @@ static GLFWbool initializeTIS(void) - (void)selectedKeyboardInputSourceChanged:(NSObject* )object { - updateUnicodeDataNS(); + updateUnicodeData(); } - (void)doNothing:(id)object @@ -403,9 +400,7 @@ static GLFWbool initializeTIS(void) - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - _GLFWwindow* window; - - for (window = _glfw.windowListHead; window; window = window->next) + for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) _glfwInputWindowCloseRequest(window); return NSTerminateCancel; @@ -413,15 +408,13 @@ static GLFWbool initializeTIS(void) - (void)applicationDidChangeScreenParameters:(NSNotification *) notification { - _GLFWwindow* window; - - for (window = _glfw.windowListHead; window; window = window->next) + for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) { if (window->context.client != GLFW_NO_API) [window->context.nsgl.object update]; } - _glfwPollMonitorsNS(); + _glfwPollMonitorsCocoa(); } - (void)applicationWillFinishLaunching:(NSNotification *)notification @@ -444,16 +437,14 @@ static GLFWbool initializeTIS(void) - (void)applicationDidFinishLaunching:(NSNotification *)notification { - _glfwPlatformPostEmptyEvent(); + _glfwPostEmptyEventCocoa(); [NSApp stop:nil]; } - (void)applicationDidHide:(NSNotification *)notification { - int i; - - for (i = 0; i < _glfw.monitorCount; i++) - _glfwRestoreVideoModeNS(_glfw.monitors[i]); + for (int i = 0; i < _glfw.monitorCount; i++) + _glfwRestoreVideoModeCocoa(_glfw.monitors[i]); } @end // GLFWApplicationDelegate @@ -463,24 +454,32 @@ static GLFWbool initializeTIS(void) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -void* _glfwLoadLocalVulkanLoaderNS(void) +void* _glfwLoadLocalVulkanLoaderCocoa(void) { CFBundleRef bundle = CFBundleGetMainBundle(); if (!bundle) return NULL; - CFURLRef url = - CFBundleCopyAuxiliaryExecutableURL(bundle, CFSTR("libvulkan.1.dylib")); - if (!url) + CFURLRef frameworksUrl = CFBundleCopyPrivateFrameworksURL(bundle); + if (!frameworksUrl) return NULL; + CFURLRef loaderUrl = CFURLCreateCopyAppendingPathComponent( + kCFAllocatorDefault, frameworksUrl, CFSTR("libvulkan.1.dylib"), false); + if (!loaderUrl) + { + CFRelease(frameworksUrl); + return NULL; + } + char path[PATH_MAX]; void* handle = NULL; - if (CFURLGetFileSystemRepresentation(url, true, (UInt8*) path, sizeof(path) - 1)) - handle = _glfw_dlopen(path); + if (CFURLGetFileSystemRepresentation(loaderUrl, true, (UInt8*) path, sizeof(path) - 1)) + handle = _glfwPlatformLoadModule(path); - CFRelease(url); + CFRelease(loaderUrl); + CFRelease(frameworksUrl); return handle; } @@ -489,7 +488,89 @@ void* _glfwLoadLocalVulkanLoaderNS(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformInit(void) +GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) +{ + const _GLFWplatform cocoa = + { + GLFW_PLATFORM_COCOA, + _glfwInitCocoa, + _glfwTerminateCocoa, + _glfwGetCursorPosCocoa, + _glfwSetCursorPosCocoa, + _glfwSetCursorModeCocoa, + _glfwSetRawMouseMotionCocoa, + _glfwRawMouseMotionSupportedCocoa, + _glfwCreateCursorCocoa, + _glfwCreateStandardCursorCocoa, + _glfwDestroyCursorCocoa, + _glfwSetCursorCocoa, + _glfwGetScancodeNameCocoa, + _glfwGetKeyScancodeCocoa, + _glfwSetClipboardStringCocoa, + _glfwGetClipboardStringCocoa, + _glfwInitJoysticksCocoa, + _glfwTerminateJoysticksCocoa, + _glfwPollJoystickCocoa, + _glfwGetMappingNameCocoa, + _glfwUpdateGamepadGUIDCocoa, + _glfwFreeMonitorCocoa, + _glfwGetMonitorPosCocoa, + _glfwGetMonitorContentScaleCocoa, + _glfwGetMonitorWorkareaCocoa, + _glfwGetVideoModesCocoa, + _glfwGetVideoModeCocoa, + _glfwGetGammaRampCocoa, + _glfwSetGammaRampCocoa, + _glfwCreateWindowCocoa, + _glfwDestroyWindowCocoa, + _glfwSetWindowTitleCocoa, + _glfwSetWindowIconCocoa, + _glfwGetWindowPosCocoa, + _glfwSetWindowPosCocoa, + _glfwGetWindowSizeCocoa, + _glfwSetWindowSizeCocoa, + _glfwSetWindowSizeLimitsCocoa, + _glfwSetWindowAspectRatioCocoa, + _glfwGetFramebufferSizeCocoa, + _glfwGetWindowFrameSizeCocoa, + _glfwGetWindowContentScaleCocoa, + _glfwIconifyWindowCocoa, + _glfwRestoreWindowCocoa, + _glfwMaximizeWindowCocoa, + _glfwShowWindowCocoa, + _glfwHideWindowCocoa, + _glfwRequestWindowAttentionCocoa, + _glfwFocusWindowCocoa, + _glfwSetWindowMonitorCocoa, + _glfwWindowFocusedCocoa, + _glfwWindowIconifiedCocoa, + _glfwWindowVisibleCocoa, + _glfwWindowMaximizedCocoa, + _glfwWindowHoveredCocoa, + _glfwFramebufferTransparentCocoa, + _glfwGetWindowOpacityCocoa, + _glfwSetWindowResizableCocoa, + _glfwSetWindowDecoratedCocoa, + _glfwSetWindowFloatingCocoa, + _glfwSetWindowOpacityCocoa, + _glfwSetWindowMousePassthroughCocoa, + _glfwPollEventsCocoa, + _glfwWaitEventsCocoa, + _glfwWaitEventsTimeoutCocoa, + _glfwPostEmptyEventCocoa, + _glfwGetEGLPlatformCocoa, + _glfwGetEGLNativeDisplayCocoa, + _glfwGetEGLNativeWindowCocoa, + _glfwGetRequiredInstanceExtensionsCocoa, + _glfwGetPhysicalDevicePresentationSupportCocoa, + _glfwCreateWindowSurfaceCocoa, + }; + + *platform = cocoa; + return GLFW_TRUE; +} + +int _glfwInitCocoa(void) { @autoreleasepool { @@ -547,9 +628,7 @@ int _glfwPlatformInit(void) if (!initializeTIS()) return GLFW_FALSE; - _glfwInitTimerNS(); - - _glfwPollMonitorsNS(); + _glfwPollMonitorsCocoa(); if (![[NSRunningApplication currentApplication] isFinishedLaunching]) [NSApp run]; @@ -563,7 +642,7 @@ int _glfwPlatformInit(void) } // autoreleasepool } -void _glfwPlatformTerminate(void) +void _glfwTerminateCocoa(void) { @autoreleasepool { @@ -602,19 +681,12 @@ void _glfwPlatformTerminate(void) if (_glfw.ns.keyUpMonitor) [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; - free(_glfw.ns.clipboardString); + _glfw_free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); + _glfwTerminateEGL(); + _glfwTerminateOSMesa(); } // autoreleasepool } -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " Cocoa NSGL EGL OSMesa" -#if defined(_GLFW_BUILD_DLL) - " dynamic" -#endif - ; -} - diff --git a/src/external/glfw/src/cocoa_joystick.h b/src/external/glfw/src/cocoa_joystick.h index 23d2b86a2..fc7ba7a22 100644 --- a/src/external/glfw/src/cocoa_joystick.h +++ b/src/external/glfw/src/cocoa_joystick.h @@ -26,13 +26,12 @@ #include #include -#include #include -#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyJoystick; } +#define GLFW_COCOA_JOYSTICK_STATE _GLFWjoystickNS ns; +#define GLFW_COCOA_LIBRARY_JOYSTICK_STATE -#define _GLFW_PLATFORM_MAPPING_NAME "Mac OS X" +#define GLFW_BUILD_COCOA_MAPPINGS // Cocoa-specific per-joystick data // @@ -44,3 +43,9 @@ typedef struct _GLFWjoystickNS CFMutableArrayRef hats; } _GLFWjoystickNS; +GLFWbool _glfwInitJoysticksCocoa(void); +void _glfwTerminateJoysticksCocoa(void); +GLFWbool _glfwPollJoystickCocoa(_GLFWjoystick* js, int mode); +const char* _glfwGetMappingNameCocoa(void); +void _glfwUpdateGamepadGUIDCocoa(char* guid); + diff --git a/src/external/glfw/src/cocoa_joystick.m b/src/external/glfw/src/cocoa_joystick.m index 4a64fb09f..ebcf5fdb9 100644 --- a/src/external/glfw/src/cocoa_joystick.m +++ b/src/external/glfw/src/cocoa_joystick.m @@ -96,25 +96,21 @@ static CFComparisonResult compareElements(const void* fp, // static void closeJoystick(_GLFWjoystick* js) { - int i; + _glfwInputJoystick(js, GLFW_DISCONNECTED); - if (!js->present) - return; - - for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); + for (int i = 0; i < CFArrayGetCount(js->ns.axes); i++) + _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); CFRelease(js->ns.axes); - for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); + for (int i = 0; i < CFArrayGetCount(js->ns.buttons); i++) + _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); CFRelease(js->ns.buttons); - for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); + for (int i = 0; i < CFArrayGetCount(js->ns.hats); i++) + _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); CFRelease(js->ns.hats); _glfwFreeJoystick(js); - _glfwInputJoystick(js, GLFW_DISCONNECTED); } // Callback for user-initiated joystick addition @@ -127,7 +123,6 @@ static void matchCallback(void* context, int jid; char name[256]; char guid[33]; - CFIndex i; CFTypeRef property; uint32_t vendor = 0, product = 0, version = 0; _GLFWjoystick* js; @@ -185,7 +180,7 @@ static void matchCallback(void* context, CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); - for (i = 0; i < CFArrayGetCount(elements); i++) + for (CFIndex i = 0; i < CFArrayGetCount(elements); i++) { IOHIDElementRef native = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i); @@ -251,7 +246,7 @@ static void matchCallback(void* context, if (target) { - _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); + _GLFWjoyelementNS* element = _glfw_calloc(1, sizeof(_GLFWjoyelementNS)); element->native = native; element->usage = usage; element->index = (int) CFArrayGetCount(target); @@ -290,13 +285,11 @@ static void removeCallback(void* context, void* sender, IOHIDDeviceRef device) { - int jid; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.joysticks[jid].ns.device == device) + if (_glfw.joysticks[jid].connected && _glfw.joysticks[jid].ns.device == device) { - closeJoystick(_glfw.joysticks + jid); + closeJoystick(&_glfw.joysticks[jid]); break; } } @@ -307,7 +300,7 @@ static void removeCallback(void* context, ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwPlatformInitJoysticks(void) +GLFWbool _glfwInitJoysticksCocoa(void) { CFMutableArrayRef matching; const long usages[] = @@ -384,12 +377,13 @@ GLFWbool _glfwPlatformInitJoysticks(void) return GLFW_TRUE; } -void _glfwPlatformTerminateJoysticks(void) +void _glfwTerminateJoysticksCocoa(void) { - int jid; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - closeJoystick(_glfw.joysticks + jid); + for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.joysticks[jid].connected) + closeJoystick(&_glfw.joysticks[jid]); + } if (_glfw.ns.hidManager) { @@ -399,13 +393,11 @@ void _glfwPlatformTerminateJoysticks(void) } -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +GLFWbool _glfwPollJoystickCocoa(_GLFWjoystick* js, int mode) { if (mode & _GLFW_POLL_AXES) { - CFIndex i; - - for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) + for (CFIndex i = 0; i < CFArrayGetCount(js->ns.axes); i++) { _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.axes, i); @@ -430,9 +422,7 @@ int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) if (mode & _GLFW_POLL_BUTTONS) { - CFIndex i; - - for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) + for (CFIndex i = 0; i < CFArrayGetCount(js->ns.buttons); i++) { _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.buttons, i); @@ -441,7 +431,7 @@ int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) _glfwInputJoystickButton(js, (int) i, state); } - for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) + for (CFIndex i = 0; i < CFArrayGetCount(js->ns.hats); i++) { const int states[9] = { @@ -466,10 +456,15 @@ int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) } } - return js->present; + return js->connected; } -void _glfwPlatformUpdateGamepadGUID(char* guid) +const char* _glfwGetMappingNameCocoa(void) +{ + return "Mac OS X"; +} + +void _glfwUpdateGamepadGUIDCocoa(char* guid) { if ((strncmp(guid + 4, "000000000000", 12) == 0) && (strncmp(guid + 20, "000000000000", 12) == 0)) diff --git a/src/external/glfw/src/cocoa_monitor.m b/src/external/glfw/src/cocoa_monitor.m index 31bf04349..64d9eb2c7 100644 --- a/src/external/glfw/src/cocoa_monitor.m +++ b/src/external/glfw/src/cocoa_monitor.m @@ -58,7 +58,7 @@ static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen) io_service_t service; CFDictionaryRef info; - if (IOServiceGetMatchingServices(kIOMasterPortDefault, + if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("IODisplayConnect"), &it) != 0) { @@ -98,11 +98,7 @@ static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen) IOObjectRelease(it); if (!service) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to find service port for display"); return _glfw_strdup("Display"); - } CFDictionaryRef names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); @@ -120,7 +116,7 @@ static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen) const CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), kCFStringEncodingUTF8); - char* name = calloc(size + 1, 1); + char* name = _glfw_calloc(size + 1, 1); CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8); CFRelease(info); @@ -231,7 +227,7 @@ static double getFallbackRefreshRate(CGDirectDisplayID displayID) io_iterator_t it; io_service_t service; - if (IOServiceGetMatchingServices(kIOMasterPortDefault, + if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("IOFramebuffer"), &it) != 0) { @@ -297,11 +293,11 @@ static double getFallbackRefreshRate(CGDirectDisplayID displayID) // Poll for changes in the set of connected monitors // -void _glfwPollMonitorsNS(void) +void _glfwPollMonitorsCocoa(void) { uint32_t displayCount; CGGetOnlineDisplayList(0, NULL, &displayCount); - CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID)); + CGDirectDisplayID* displays = _glfw_calloc(displayCount, sizeof(CGDirectDisplayID)); CGGetOnlineDisplayList(displayCount, displays, &displayCount); for (int i = 0; i < _glfw.monitorCount; i++) @@ -311,7 +307,7 @@ void _glfwPollMonitorsNS(void) uint32_t disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); @@ -363,7 +359,7 @@ void _glfwPollMonitorsNS(void) monitor->ns.unitNumber = unitNumber; monitor->ns.screen = screen; - free(name); + _glfw_free(name); CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]); if (CGDisplayModeGetRefreshRate(mode) == 0.0) @@ -379,16 +375,16 @@ void _glfwPollMonitorsNS(void) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } - free(disconnected); - free(displays); + _glfw_free(disconnected); + _glfw_free(displays); } // Change the current video mode // -void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) +void _glfwSetVideoModeCocoa(_GLFWmonitor* monitor, const GLFWvidmode* desired) { GLFWvidmode current; - _glfwPlatformGetVideoMode(monitor, ¤t); + _glfwGetVideoModeCocoa(monitor, ¤t); const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); if (_glfwCompareVideoModes(¤t, best) == 0) @@ -428,7 +424,7 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) // Restore the previously saved (original) video mode // -void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) +void _glfwRestoreVideoModeCocoa(_GLFWmonitor* monitor) { if (monitor->ns.previousMode) { @@ -447,11 +443,11 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +void _glfwFreeMonitorCocoa(_GLFWmonitor* monitor) { } -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +void _glfwGetMonitorPosCocoa(_GLFWmonitor* monitor, int* xpos, int* ypos) { @autoreleasepool { @@ -465,8 +461,8 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) } // autoreleasepool } -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) +void _glfwGetMonitorContentScaleCocoa(_GLFWmonitor* monitor, + float* xscale, float* yscale) { @autoreleasepool { @@ -487,9 +483,9 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, } // autoreleasepool } -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, - int* xpos, int* ypos, - int* width, int* height) +void _glfwGetMonitorWorkareaCocoa(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) { @autoreleasepool { @@ -504,7 +500,7 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, if (xpos) *xpos = frameRect.origin.x; if (ypos) - *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1); + *ypos = _glfwTransformYCocoa(frameRect.origin.y + frameRect.size.height - 1); if (width) *width = frameRect.size.width; if (height) @@ -513,7 +509,7 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, } // autoreleasepool } -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +GLFWvidmode* _glfwGetVideoModesCocoa(_GLFWmonitor* monitor, int* count) { @autoreleasepool { @@ -521,7 +517,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); const CFIndex found = CFArrayGetCount(modes); - GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode)); + GLFWvidmode* result = _glfw_calloc(found, sizeof(GLFWvidmode)); for (CFIndex i = 0; i < found; i++) { @@ -553,7 +549,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) } // autoreleasepool } -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) +void _glfwGetVideoModeCocoa(_GLFWmonitor* monitor, GLFWvidmode *mode) { @autoreleasepool { @@ -564,12 +560,12 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) } // autoreleasepool } -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwGetGammaRampCocoa(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { @autoreleasepool { uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID); - CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); + CGGammaValue* values = _glfw_calloc(size * 3, sizeof(CGGammaValue)); CGGetDisplayTransferByTable(monitor->ns.displayID, size, @@ -587,17 +583,17 @@ GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535); } - free(values); + _glfw_free(values); return GLFW_TRUE; } // autoreleasepool } -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +void _glfwSetGammaRampCocoa(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { @autoreleasepool { - CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue)); + CGGammaValue* values = _glfw_calloc(ramp->size * 3, sizeof(CGGammaValue)); for (unsigned int i = 0; i < ramp->size; i++) { @@ -612,7 +608,7 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) values + ramp->size, values + ramp->size * 2); - free(values); + _glfw_free(values); } // autoreleasepool } diff --git a/src/external/glfw/src/cocoa_platform.h b/src/external/glfw/src/cocoa_platform.h index 01dcd87da..9f7d191db 100644 --- a/src/external/glfw/src/cocoa_platform.h +++ b/src/external/glfw/src/cocoa_platform.h @@ -25,13 +25,15 @@ //======================================================================== #include -#include #include +#include // NOTE: All of NSGL was deprecated in the 10.14 SDK // This disables the pointless warnings for every symbol we use +#ifndef GL_SILENCE_DEPRECATION #define GL_SILENCE_DEPRECATION +#endif #if defined(__OBJC__) #import @@ -40,8 +42,15 @@ typedef void* id; #endif // NOTE: Many Cocoa enum values have been renamed and we need to build across -// SDK versions where one is unavailable or the other deprecated -// We use the newer names in code and these macros to handle compatibility +// SDK versions where one is unavailable or deprecated. +// We use the newer names in code and replace them with the older names if +// the base SDK does not provide the newer names. + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 + #define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval + #define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity +#endif + #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat #define NSEventMaskAny NSAnyEventMask @@ -60,6 +69,15 @@ typedef void* id; #define NSWindowStyleMaskTitled NSTitledWindowMask #endif +// NOTE: Many Cocoa dynamically linked constants have been renamed and we need +// to build across SDK versions where one is unavailable or deprecated. +// We use the newer names in code and replace them with the older names if +// the deployment target is older than the newer names. + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + #define NSPasteboardTypeURL NSURLPboardType +#endif + typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; typedef VkFlags VkMetalSurfaceCreateFlagsEXT; @@ -82,19 +100,13 @@ typedef struct VkMetalSurfaceCreateInfoEXT typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMetalSurfaceCreateInfoEXT*,const VkAllocationCallbacks*,VkSurfaceKHR*); -#include "posix_thread.h" -#include "cocoa_joystick.h" -#include "nsgl_context.h" +#define GLFW_COCOA_WINDOW_STATE _GLFWwindowNS ns; +#define GLFW_COCOA_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns; +#define GLFW_COCOA_MONITOR_STATE _GLFWmonitorNS ns; +#define GLFW_COCOA_CURSOR_STATE _GLFWcursorNS ns; -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) - -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns -#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns +#define GLFW_NSGL_CONTEXT_STATE _GLFWcontextNSGL nsgl; +#define GLFW_NSGL_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl; // HIToolbox.framework pointer typedefs #define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData @@ -106,6 +118,22 @@ typedef UInt8 (*PFN_LMGetKbdType)(void); #define LMGetKbdType _glfw.ns.tis.GetKbdType +// NSGL-specific per-context data +// +typedef struct _GLFWcontextNSGL +{ + id pixelFormat; + id object; +} _GLFWcontextNSGL; + +// NSGL-specific global data +// +typedef struct _GLFWlibraryNSGL +{ + // dlopen handle for OpenGL.framework (for glfwGetProcAddress) + CFBundleRef framework; +} _GLFWlibraryNSGL; + // Cocoa-specific per-window data // typedef struct _GLFWwindowNS @@ -128,7 +156,6 @@ typedef struct _GLFWwindowNS // since the last cursor motion event was processed // This is kept to counteract Cocoa doing the same internally double cursorWarpDeltaX, cursorWarpDeltaY; - } _GLFWwindowNS; // Cocoa-specific global data @@ -162,7 +189,6 @@ typedef struct _GLFWlibraryNS PFN_LMGetKbdType GetKbdType; CFStringRef kPropertyUnicodeKeyLayoutData; } tis; - } _GLFWlibraryNS; // Cocoa-specific per-monitor data @@ -174,7 +200,6 @@ typedef struct _GLFWmonitorNS uint32_t unitNumber; id screen; double fallbackRefreshRate; - } _GLFWmonitorNS; // Cocoa-specific per-cursor data @@ -182,25 +207,96 @@ typedef struct _GLFWmonitorNS typedef struct _GLFWcursorNS { id object; - } _GLFWcursorNS; -// Cocoa-specific global timer data -// -typedef struct _GLFWtimerNS -{ - uint64_t frequency; -} _GLFWtimerNS; +GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform); +int _glfwInitCocoa(void); +void _glfwTerminateCocoa(void); +GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); +void _glfwDestroyWindowCocoa(_GLFWwindow* window); +void _glfwSetWindowTitleCocoa(_GLFWwindow* window, const char* title); +void _glfwSetWindowIconCocoa(_GLFWwindow* window, int count, const GLFWimage* images); +void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos); +void _glfwSetWindowPosCocoa(_GLFWwindow* window, int xpos, int ypos); +void _glfwGetWindowSizeCocoa(_GLFWwindow* window, int* width, int* height); +void _glfwSetWindowSizeCocoa(_GLFWwindow* window, int width, int height); +void _glfwSetWindowSizeLimitsCocoa(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); +void _glfwSetWindowAspectRatioCocoa(_GLFWwindow* window, int numer, int denom); +void _glfwGetFramebufferSizeCocoa(_GLFWwindow* window, int* width, int* height); +void _glfwGetWindowFrameSizeCocoa(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); +void _glfwGetWindowContentScaleCocoa(_GLFWwindow* window, float* xscale, float* yscale); +void _glfwIconifyWindowCocoa(_GLFWwindow* window); +void _glfwRestoreWindowCocoa(_GLFWwindow* window); +void _glfwMaximizeWindowCocoa(_GLFWwindow* window); +void _glfwShowWindowCocoa(_GLFWwindow* window); +void _glfwHideWindowCocoa(_GLFWwindow* window); +void _glfwRequestWindowAttentionCocoa(_GLFWwindow* window); +void _glfwFocusWindowCocoa(_GLFWwindow* window); +void _glfwSetWindowMonitorCocoa(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); +GLFWbool _glfwWindowFocusedCocoa(_GLFWwindow* window); +GLFWbool _glfwWindowIconifiedCocoa(_GLFWwindow* window); +GLFWbool _glfwWindowVisibleCocoa(_GLFWwindow* window); +GLFWbool _glfwWindowMaximizedCocoa(_GLFWwindow* window); +GLFWbool _glfwWindowHoveredCocoa(_GLFWwindow* window); +GLFWbool _glfwFramebufferTransparentCocoa(_GLFWwindow* window); +void _glfwSetWindowResizableCocoa(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowDecoratedCocoa(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowFloatingCocoa(_GLFWwindow* window, GLFWbool enabled); +float _glfwGetWindowOpacityCocoa(_GLFWwindow* window); +void _glfwSetWindowOpacityCocoa(_GLFWwindow* window, float opacity); +void _glfwSetWindowMousePassthroughCocoa(_GLFWwindow* window, GLFWbool enabled); -void _glfwInitTimerNS(void); +void _glfwSetRawMouseMotionCocoa(_GLFWwindow *window, GLFWbool enabled); +GLFWbool _glfwRawMouseMotionSupportedCocoa(void); -void _glfwPollMonitorsNS(void); -void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); -void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); +void _glfwPollEventsCocoa(void); +void _glfwWaitEventsCocoa(void); +void _glfwWaitEventsTimeoutCocoa(double timeout); +void _glfwPostEmptyEventCocoa(void); -float _glfwTransformYNS(float y); +void _glfwGetCursorPosCocoa(_GLFWwindow* window, double* xpos, double* ypos); +void _glfwSetCursorPosCocoa(_GLFWwindow* window, double xpos, double ypos); +void _glfwSetCursorModeCocoa(_GLFWwindow* window, int mode); +const char* _glfwGetScancodeNameCocoa(int scancode); +int _glfwGetKeyScancodeCocoa(int key); +GLFWbool _glfwCreateCursorCocoa(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); +GLFWbool _glfwCreateStandardCursorCocoa(_GLFWcursor* cursor, int shape); +void _glfwDestroyCursorCocoa(_GLFWcursor* cursor); +void _glfwSetCursorCocoa(_GLFWwindow* window, _GLFWcursor* cursor); +void _glfwSetClipboardStringCocoa(const char* string); +const char* _glfwGetClipboardStringCocoa(void); -void* _glfwLoadLocalVulkanLoaderNS(void); +EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs); +EGLNativeDisplayType _glfwGetEGLNativeDisplayCocoa(void); +EGLNativeWindowType _glfwGetEGLNativeWindowCocoa(_GLFWwindow* window); + +void _glfwGetRequiredInstanceExtensionsCocoa(char** extensions); +GLFWbool _glfwGetPhysicalDevicePresentationSupportCocoa(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); +VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +void _glfwFreeMonitorCocoa(_GLFWmonitor* monitor); +void _glfwGetMonitorPosCocoa(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwGetMonitorContentScaleCocoa(_GLFWmonitor* monitor, float* xscale, float* yscale); +void _glfwGetMonitorWorkareaCocoa(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); +GLFWvidmode* _glfwGetVideoModesCocoa(_GLFWmonitor* monitor, int* count); +void _glfwGetVideoModeCocoa(_GLFWmonitor* monitor, GLFWvidmode* mode); +GLFWbool _glfwGetGammaRampCocoa(_GLFWmonitor* monitor, GLFWgammaramp* ramp); +void _glfwSetGammaRampCocoa(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +void _glfwPollMonitorsCocoa(void); +void _glfwSetVideoModeCocoa(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwRestoreVideoModeCocoa(_GLFWmonitor* monitor); + +float _glfwTransformYCocoa(float y); + +void* _glfwLoadLocalVulkanLoaderCocoa(void); + +GLFWbool _glfwInitNSGL(void); +void _glfwTerminateNSGL(void); +GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +void _glfwDestroyContextNSGL(_GLFWwindow* window); diff --git a/src/external/glfw/src/cocoa_time.c b/src/external/glfw/src/cocoa_time.c index 4bf646c8b..c2bf8edaa 100644 --- a/src/external/glfw/src/cocoa_time.c +++ b/src/external/glfw/src/cocoa_time.c @@ -32,12 +32,10 @@ ////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// +////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -// Initialise timer -// -void _glfwInitTimerNS(void) +void _glfwPlatformInitTimer(void) { mach_timebase_info_data_t info; mach_timebase_info(&info); @@ -45,11 +43,6 @@ void _glfwInitTimerNS(void) _glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer; } - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - uint64_t _glfwPlatformGetTimerValue(void) { return mach_absolute_time(); diff --git a/src/external/glfw/src/cocoa_time.h b/src/external/glfw/src/cocoa_time.h new file mode 100644 index 000000000..3512e8b6e --- /dev/null +++ b/src/external/glfw/src/cocoa_time.h @@ -0,0 +1,35 @@ +//======================================================================== +// GLFW 3.4 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2021 Camilla Löwy +// +// 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. +// +//======================================================================== + +#define GLFW_COCOA_LIBRARY_TIMER_STATE _GLFWtimerNS ns; + +// Cocoa-specific global timer data +// +typedef struct _GLFWtimerNS +{ + uint64_t frequency; +} _GLFWtimerNS; + diff --git a/src/external/glfw/src/cocoa_window.m b/src/external/glfw/src/cocoa_window.m index b618dccc8..daac39b3b 100644 --- a/src/external/glfw/src/cocoa_window.m +++ b/src/external/glfw/src/cocoa_window.m @@ -31,25 +31,9 @@ #include #include -// Returns the style mask corresponding to the window settings -// -static NSUInteger getStyleMask(_GLFWwindow* window) -{ - NSUInteger styleMask = NSWindowStyleMaskMiniaturizable; - - if (window->monitor || !window->decorated) - styleMask |= NSWindowStyleMaskBorderless; - else - { - styleMask |= NSWindowStyleMaskTitled | - NSWindowStyleMaskClosable; - - if (window->resizable) - styleMask |= NSWindowStyleMaskResizable; - } - - return styleMask; -} +// HACK: This enum value is missing from framework headers on OS X 10.11 despite +// having been (according to documentation) added in Mac OS X 10.7 +#define NSWindowCollectionBehaviorFullScreenNone (1 << 9) // Returns whether the cursor is in the content area of the specified window // @@ -105,19 +89,20 @@ static void updateCursorMode(_GLFWwindow* window) if (window->cursorMode == GLFW_CURSOR_DISABLED) { _glfw.ns.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.ns.restoreCursorPosX, - &_glfw.ns.restoreCursorPosY); + _glfwGetCursorPosCocoa(window, + &_glfw.ns.restoreCursorPosX, + &_glfw.ns.restoreCursorPosY); _glfwCenterCursorInContentArea(window); CGAssociateMouseAndMouseCursorPosition(false); } else if (_glfw.ns.disabledCursorWindow == window) { _glfw.ns.disabledCursorWindow = NULL; - CGAssociateMouseAndMouseCursorPosition(true); - _glfwPlatformSetCursorPos(window, - _glfw.ns.restoreCursorPosX, - _glfw.ns.restoreCursorPosY); + _glfwSetCursorPosCocoa(window, + _glfw.ns.restoreCursorPosX, + _glfw.ns.restoreCursorPosY); + // NOTE: The matching CGAssociateMouseAndMouseCursorPosition call is + // made in _glfwSetCursorPosCocoa as part of a workaround } if (cursorInContentArea(window)) @@ -128,10 +113,10 @@ static void updateCursorMode(_GLFWwindow* window) // static void acquireMonitor(_GLFWwindow* window) { - _glfwSetVideoModeNS(window->monitor, &window->videoMode); + _glfwSetVideoModeCocoa(window->monitor, &window->videoMode); const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID); const NSRect frame = NSMakeRect(bounds.origin.x, - _glfwTransformYNS(bounds.origin.y + bounds.size.height - 1), + _glfwTransformYCocoa(bounds.origin.y + bounds.size.height - 1), bounds.size.width, bounds.size.height); @@ -148,7 +133,7 @@ static void releaseMonitor(_GLFWwindow* window) return; _glfwInputMonitorWindow(window->monitor, NULL); - _glfwRestoreVideoModeNS(window->monitor); + _glfwRestoreVideoModeCocoa(window->monitor); } // Translates macOS key modifiers into GLFW ones @@ -243,7 +228,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)windowDidResize:(NSNotification *)notification { - if (window->context.client != GLFW_NO_API) + if (window->context.source == GLFW_NATIVE_CONTEXT_API) [window->context.nsgl.object update]; if (_glfw.ns.disabledCursorWindow == window) @@ -278,14 +263,14 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)windowDidMove:(NSNotification *)notification { - if (window->context.client != GLFW_NO_API) + if (window->context.source == GLFW_NATIVE_CONTEXT_API) [window->context.nsgl.object update]; if (_glfw.ns.disabledCursorWindow == window) _glfwCenterCursorInContentArea(window); int x, y; - _glfwPlatformGetWindowPos(window, &x, &y); + _glfwGetWindowPosCocoa(window, &x, &y); _glfwInputWindowPos(window, x, y); } @@ -317,7 +302,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)windowDidResignKey:(NSNotification *)notification { if (window->monitor && window->autoIconify) - _glfwPlatformIconifyWindow(window); + _glfwIconifyWindowCocoa(window); _glfwInputWindowFocus(window, GLFW_FALSE); } @@ -360,9 +345,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; markedText = [[NSMutableAttributedString alloc] init]; [self updateTrackingAreas]; - // NOTE: kUTTypeURL corresponds to NSPasteboardTypeURL but is available - // on 10.7 without having been deprecated yet - [self registerForDraggedTypes:@[(__bridge NSString*) kUTTypeURL]]; + [self registerForDraggedTypes:@[NSPasteboardTypeURL]]; } return self; @@ -397,7 +380,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)updateLayer { - if (window->context.client != GLFW_NO_API) + if (window->context.source == GLFW_NATIVE_CONTEXT_API) [window->context.nsgl.object update]; _glfwInputWindowDamage(window); @@ -520,6 +503,18 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; { const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; + const float xscale = fbRect.size.width / contentRect.size.width; + const float yscale = fbRect.size.height / contentRect.size.height; + + if (xscale != window->ns.xscale || yscale != window->ns.yscale) + { + if (window->ns.retina && window->ns.layer) + [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; + + window->ns.xscale = xscale; + window->ns.yscale = yscale; + _glfwInputWindowContentScale(window, xscale, yscale); + } if (fbRect.size.width != window->ns.fbWidth || fbRect.size.height != window->ns.fbHeight) @@ -528,19 +523,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; window->ns.fbHeight = fbRect.size.height; _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); } - - const float xscale = fbRect.size.width / contentRect.size.width; - const float yscale = fbRect.size.height / contentRect.size.height; - - if (xscale != window->ns.xscale || yscale != window->ns.yscale) - { - window->ns.xscale = xscale; - window->ns.yscale = yscale; - _glfwInputWindowContentScale(window, xscale, yscale); - - if (window->ns.retina && window->ns.layer) - [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; - } } - (void)drawRect:(NSRect)rect @@ -647,7 +629,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; const NSUInteger count = [urls count]; if (count) { - char** paths = calloc(count, sizeof(char*)); + char** paths = _glfw_calloc(count, sizeof(char*)); for (NSUInteger i = 0; i < count; i++) paths[i] = _glfw_strdup([urls[i] fileSystemRepresentation]); @@ -655,8 +637,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _glfwInputDrop(window, (int) count, (const char**) paths); for (NSUInteger i = 0; i < count; i++) - free(paths[i]); - free(paths); + _glfw_free(paths[i]); + _glfw_free(paths); } return YES; @@ -803,17 +785,41 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, GLFWvidmode mode; int xpos, ypos; - _glfwPlatformGetVideoMode(window->monitor, &mode); - _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + _glfwGetVideoModeCocoa(window->monitor, &mode); + _glfwGetMonitorPosCocoa(window->monitor, &xpos, &ypos); contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height); } else - contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height); + { + if (wndconfig->xpos == GLFW_ANY_POSITION || + wndconfig->ypos == GLFW_ANY_POSITION) + { + contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height); + } + else + { + const int xpos = wndconfig->xpos; + const int ypos = _glfwTransformYCocoa(wndconfig->ypos + wndconfig->height - 1); + contentRect = NSMakeRect(xpos, ypos, wndconfig->width, wndconfig->height); + } + } + + NSUInteger styleMask = NSWindowStyleMaskMiniaturizable; + + if (window->monitor || !window->decorated) + styleMask |= NSWindowStyleMaskBorderless; + else + { + styleMask |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); + + if (window->resizable) + styleMask |= NSWindowStyleMaskResizable; + } window->ns.object = [[GLFWWindow alloc] initWithContentRect:contentRect - styleMask:getStyleMask(window) + styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]; @@ -827,10 +833,14 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; else { - [(NSWindow*) window->ns.object center]; - _glfw.ns.cascadePoint = - NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint: - NSPointFromCGPoint(_glfw.ns.cascadePoint)]); + if (wndconfig->xpos == GLFW_ANY_POSITION || + wndconfig->ypos == GLFW_ANY_POSITION) + { + [(NSWindow*) window->ns.object center]; + _glfw.ns.cascadePoint = + NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint: + NSPointFromCGPoint(_glfw.ns.cascadePoint)]); + } if (wndconfig->resizable) { @@ -839,6 +849,12 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, NSWindowCollectionBehaviorManaged; [window->ns.object setCollectionBehavior:behavior]; } + else + { + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenNone; + [window->ns.object setCollectionBehavior:behavior]; + } if (wndconfig->floating) [window->ns.object setLevel:NSFloatingWindowLevel]; @@ -872,8 +888,8 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, [window->ns.object setTabbingMode:NSWindowTabbingModeDisallowed]; #endif - _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height); - _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight); + _glfwGetWindowSizeCocoa(window, &window->ns.width, &window->ns.height); + _glfwGetFramebufferSizeCocoa(window, &window->ns.fbWidth, &window->ns.fbHeight); return GLFW_TRUE; } @@ -885,7 +901,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, // Transforms a y-coordinate between the CG display and NS screen spaces // -float _glfwTransformYNS(float y) +float _glfwTransformYCocoa(float y) { return CGDisplayBounds(CGMainDisplayID()).size.height - y - 1; } @@ -895,10 +911,10 @@ float _glfwTransformYNS(float y) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) +GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) { @autoreleasepool { @@ -933,13 +949,31 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughCocoa(window, GLFW_TRUE); + if (window->monitor) { - _glfwPlatformShowWindow(window); - _glfwPlatformFocusWindow(window); + _glfwShowWindowCocoa(window); + _glfwFocusWindowCocoa(window); acquireMonitor(window); + + if (wndconfig->centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig->visible) + { + _glfwShowWindowCocoa(window); + if (wndconfig->focused) + _glfwFocusWindowCocoa(window); + } } return GLFW_TRUE; @@ -947,7 +981,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, } // autoreleasepool } -void _glfwPlatformDestroyWindow(_GLFWwindow* window) +void _glfwDestroyWindowCocoa(_GLFWwindow* window) { @autoreleasepool { @@ -973,12 +1007,12 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) window->ns.object = nil; // HACK: Allow Cocoa to catch up before returning - _glfwPlatformPollEvents(); + _glfwPollEventsCocoa(); } // autoreleasepool } -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +void _glfwSetWindowTitleCocoa(_GLFWwindow* window, const char* title) { @autoreleasepool { NSString* string = @(title); @@ -989,14 +1023,14 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) } // autoreleasepool } -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) +void _glfwSetWindowIconCocoa(_GLFWwindow* window, + int count, const GLFWimage* images) { _glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Cocoa: Regular windows do not have icons on macOS"); } -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos) { @autoreleasepool { @@ -1006,24 +1040,24 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) if (xpos) *xpos = contentRect.origin.x; if (ypos) - *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1); + *ypos = _glfwTransformYCocoa(contentRect.origin.y + contentRect.size.height - 1); } // autoreleasepool } -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y) +void _glfwSetWindowPosCocoa(_GLFWwindow* window, int x, int y) { @autoreleasepool { const NSRect contentRect = [window->ns.view frame]; - const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0); + const NSRect dummyRect = NSMakeRect(x, _glfwTransformYCocoa(y + contentRect.size.height - 1), 0, 0); const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect]; [window->ns.object setFrameOrigin:frameRect.origin]; } // autoreleasepool } -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetWindowSizeCocoa(_GLFWwindow* window, int* width, int* height) { @autoreleasepool { @@ -1037,7 +1071,7 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) } // autoreleasepool } -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +void _glfwSetWindowSizeCocoa(_GLFWwindow* window, int width, int height) { @autoreleasepool { @@ -1059,9 +1093,9 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) } // autoreleasepool } -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) +void _glfwSetWindowSizeLimitsCocoa(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) { @autoreleasepool { @@ -1078,7 +1112,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, } // autoreleasepool } -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +void _glfwSetWindowAspectRatioCocoa(_GLFWwindow* window, int numer, int denom) { @autoreleasepool { if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) @@ -1088,7 +1122,7 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom } // autoreleasepool } -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetFramebufferSizeCocoa(_GLFWwindow* window, int* width, int* height) { @autoreleasepool { @@ -1103,9 +1137,9 @@ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* heigh } // autoreleasepool } -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) +void _glfwGetWindowFrameSizeCocoa(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) { @autoreleasepool { @@ -1126,8 +1160,8 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, } // autoreleasepool } -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) +void _glfwGetWindowContentScaleCocoa(_GLFWwindow* window, + float* xscale, float* yscale) { @autoreleasepool { @@ -1142,14 +1176,14 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, } // autoreleasepool } -void _glfwPlatformIconifyWindow(_GLFWwindow* window) +void _glfwIconifyWindowCocoa(_GLFWwindow* window) { @autoreleasepool { [window->ns.object miniaturize:nil]; } // autoreleasepool } -void _glfwPlatformRestoreWindow(_GLFWwindow* window) +void _glfwRestoreWindowCocoa(_GLFWwindow* window) { @autoreleasepool { if ([window->ns.object isMiniaturized]) @@ -1159,7 +1193,7 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) } // autoreleasepool } -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +void _glfwMaximizeWindowCocoa(_GLFWwindow* window) { @autoreleasepool { if (![window->ns.object isZoomed]) @@ -1167,28 +1201,28 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window) } // autoreleasepool } -void _glfwPlatformShowWindow(_GLFWwindow* window) +void _glfwShowWindowCocoa(_GLFWwindow* window) { @autoreleasepool { [window->ns.object orderFront:nil]; } // autoreleasepool } -void _glfwPlatformHideWindow(_GLFWwindow* window) +void _glfwHideWindowCocoa(_GLFWwindow* window) { @autoreleasepool { [window->ns.object orderOut:nil]; } // autoreleasepool } -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +void _glfwRequestWindowAttentionCocoa(_GLFWwindow* window) { @autoreleasepool { [NSApp requestUserAttention:NSInformationalRequest]; } // autoreleasepool } -void _glfwPlatformFocusWindow(_GLFWwindow* window) +void _glfwFocusWindowCocoa(_GLFWwindow* window) { @autoreleasepool { // Make us the active application @@ -1200,11 +1234,11 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) } // autoreleasepool } -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) +void _glfwSetWindowMonitorCocoa(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) { @autoreleasepool { @@ -1218,10 +1252,11 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else { const NSRect contentRect = - NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height); + NSMakeRect(xpos, _glfwTransformYCocoa(ypos + height - 1), width, height); + const NSUInteger styleMask = [window->ns.object styleMask]; const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect - styleMask:getStyleMask(window)]; + styleMask:styleMask]; [window->ns.object setFrame:frameRect display:YES]; } @@ -1236,9 +1271,29 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, // HACK: Allow the state cached in Cocoa to catch up to reality // TODO: Solve this in a less terrible way - _glfwPlatformPollEvents(); + _glfwPollEventsCocoa(); + + NSUInteger styleMask = [window->ns.object styleMask]; + + if (window->monitor) + { + styleMask &= ~(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); + styleMask |= NSWindowStyleMaskBorderless; + } + else + { + if (window->decorated) + { + styleMask &= ~NSWindowStyleMaskBorderless; + styleMask |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); + } + + if (window->resizable) + styleMask |= NSWindowStyleMaskResizable; + else + styleMask &= ~NSWindowStyleMaskResizable; + } - const NSUInteger styleMask = getStyleMask(window); [window->ns.object setStyleMask:styleMask]; // HACK: Changing the style mask can cause the first responder to be cleared [window->ns.object makeFirstResponder:window->ns.view]; @@ -1252,7 +1307,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, } else { - NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), + NSRect contentRect = NSMakeRect(xpos, _glfwTransformYCocoa(ypos + height - 1), width, height); NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect styleMask:styleMask]; @@ -1284,6 +1339,20 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else [window->ns.object setLevel:NSNormalWindowLevel]; + if (window->resizable) + { + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenPrimary | + NSWindowCollectionBehaviorManaged; + [window->ns.object setCollectionBehavior:behavior]; + } + else + { + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenNone; + [window->ns.object setCollectionBehavior:behavior]; + } + [window->ns.object setHasShadow:YES]; // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window // title property but the miniwindow title property is unaffected @@ -1293,35 +1362,40 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, } // autoreleasepool } -int _glfwPlatformWindowFocused(_GLFWwindow* window) +GLFWbool _glfwWindowFocusedCocoa(_GLFWwindow* window) { @autoreleasepool { return [window->ns.object isKeyWindow]; } // autoreleasepool } -int _glfwPlatformWindowIconified(_GLFWwindow* window) +GLFWbool _glfwWindowIconifiedCocoa(_GLFWwindow* window) { @autoreleasepool { return [window->ns.object isMiniaturized]; } // autoreleasepool } -int _glfwPlatformWindowVisible(_GLFWwindow* window) +GLFWbool _glfwWindowVisibleCocoa(_GLFWwindow* window) { @autoreleasepool { return [window->ns.object isVisible]; } // autoreleasepool } -int _glfwPlatformWindowMaximized(_GLFWwindow* window) +GLFWbool _glfwWindowMaximizedCocoa(_GLFWwindow* window) { @autoreleasepool { - return [window->ns.object isZoomed]; + + if (window->resizable) + return [window->ns.object isZoomed]; + else + return GLFW_FALSE; + } // autoreleasepool } -int _glfwPlatformWindowHovered(_GLFWwindow* window) +GLFWbool _glfwWindowHoveredCocoa(_GLFWwindow* window) { @autoreleasepool { @@ -1339,29 +1413,60 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window) } // autoreleasepool } -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +GLFWbool _glfwFramebufferTransparentCocoa(_GLFWwindow* window) { @autoreleasepool { return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; } // autoreleasepool } -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowResizableCocoa(_GLFWwindow* window, GLFWbool enabled) { @autoreleasepool { - [window->ns.object setStyleMask:getStyleMask(window)]; + + const NSUInteger styleMask = [window->ns.object styleMask]; + if (enabled) + { + [window->ns.object setStyleMask:(styleMask | NSWindowStyleMaskResizable)]; + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenPrimary | + NSWindowCollectionBehaviorManaged; + [window->ns.object setCollectionBehavior:behavior]; + } + else + { + [window->ns.object setStyleMask:(styleMask & ~NSWindowStyleMaskResizable)]; + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenNone; + [window->ns.object setCollectionBehavior:behavior]; + } + } // autoreleasepool } -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowDecoratedCocoa(_GLFWwindow* window, GLFWbool enabled) { @autoreleasepool { - [window->ns.object setStyleMask:getStyleMask(window)]; + + NSUInteger styleMask = [window->ns.object styleMask]; + if (enabled) + { + styleMask |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); + styleMask &= ~NSWindowStyleMaskBorderless; + } + else + { + styleMask |= NSWindowStyleMaskBorderless; + styleMask &= ~(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); + } + + [window->ns.object setStyleMask:styleMask]; [window->ns.object makeFirstResponder:window->ns.view]; + } // autoreleasepool } -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowFloatingCocoa(_GLFWwindow* window, GLFWbool enabled) { @autoreleasepool { if (enabled) @@ -1371,39 +1476,39 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) } // autoreleasepool } -void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowMousePassthroughCocoa(_GLFWwindow* window, GLFWbool enabled) { @autoreleasepool { [window->ns.object setIgnoresMouseEvents:enabled]; } } -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +float _glfwGetWindowOpacityCocoa(_GLFWwindow* window) { @autoreleasepool { return (float) [window->ns.object alphaValue]; } // autoreleasepool } -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +void _glfwSetWindowOpacityCocoa(_GLFWwindow* window, float opacity) { @autoreleasepool { [window->ns.object setAlphaValue:opacity]; } // autoreleasepool } -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +void _glfwSetRawMouseMotionCocoa(_GLFWwindow *window, GLFWbool enabled) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, "Cocoa: Raw mouse motion not yet implemented"); } -GLFWbool _glfwPlatformRawMouseMotionSupported(void) +GLFWbool _glfwRawMouseMotionSupportedCocoa(void) { return GLFW_FALSE; } -void _glfwPlatformPollEvents(void) +void _glfwPollEventsCocoa(void) { @autoreleasepool { @@ -1422,7 +1527,7 @@ void _glfwPlatformPollEvents(void) } // autoreleasepool } -void _glfwPlatformWaitEvents(void) +void _glfwWaitEventsCocoa(void) { @autoreleasepool { @@ -1435,12 +1540,12 @@ void _glfwPlatformWaitEvents(void) dequeue:YES]; [NSApp sendEvent:event]; - _glfwPlatformPollEvents(); + _glfwPollEventsCocoa(); } // autoreleasepool } -void _glfwPlatformWaitEventsTimeout(double timeout) +void _glfwWaitEventsTimeoutCocoa(double timeout) { @autoreleasepool { @@ -1452,12 +1557,12 @@ void _glfwPlatformWaitEventsTimeout(double timeout) if (event) [NSApp sendEvent:event]; - _glfwPlatformPollEvents(); + _glfwPollEventsCocoa(); } // autoreleasepool } -void _glfwPlatformPostEmptyEvent(void) +void _glfwPostEmptyEventCocoa(void) { @autoreleasepool { @@ -1475,7 +1580,7 @@ void _glfwPlatformPostEmptyEvent(void) } // autoreleasepool } -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +void _glfwGetCursorPosCocoa(_GLFWwindow* window, double* xpos, double* ypos) { @autoreleasepool { @@ -1491,7 +1596,7 @@ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) } // autoreleasepool } -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +void _glfwSetCursorPosCocoa(_GLFWwindow* window, double x, double y) { @autoreleasepool { @@ -1516,28 +1621,41 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) const NSPoint globalPoint = globalRect.origin; CGWarpMouseCursorPosition(CGPointMake(globalPoint.x, - _glfwTransformYNS(globalPoint.y))); + _glfwTransformYCocoa(globalPoint.y))); } + // HACK: Calling this right after setting the cursor position prevents macOS + // from freezing the cursor for a fraction of a second afterwards + if (window->cursorMode != GLFW_CURSOR_DISABLED) + CGAssociateMouseAndMouseCursorPosition(true); + } // autoreleasepool } -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +void _glfwSetCursorModeCocoa(_GLFWwindow* window, int mode) { @autoreleasepool { - if (_glfwPlatformWindowFocused(window)) + + if (mode == GLFW_CURSOR_CAPTURED) + { + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, + "Cocoa: Captured cursor mode not yet implemented"); + } + + if (_glfwWindowFocusedCocoa(window)) updateCursorMode(window); + } // autoreleasepool } -const char* _glfwPlatformGetScancodeName(int scancode) +const char* _glfwGetScancodeNameCocoa(int scancode) { @autoreleasepool { if (scancode < 0 || scancode > 0xff || _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN) { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; } @@ -1579,14 +1697,14 @@ const char* _glfwPlatformGetScancodeName(int scancode) } // autoreleasepool } -int _glfwPlatformGetKeyScancode(int key) +int _glfwGetKeyScancodeCocoa(int key) { return _glfw.ns.scancodes[key]; } -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) +GLFWbool _glfwCreateCursorCocoa(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) { @autoreleasepool { @@ -1628,7 +1746,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, } // autoreleasepool } -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +GLFWbool _glfwCreateStandardCursorCocoa(_GLFWcursor* cursor, int shape) { @autoreleasepool { @@ -1702,7 +1820,7 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) } // autoreleasepool } -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +void _glfwDestroyCursorCocoa(_GLFWcursor* cursor) { @autoreleasepool { if (cursor->ns.object) @@ -1710,7 +1828,7 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) } // autoreleasepool } -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +void _glfwSetCursorCocoa(_GLFWwindow* window, _GLFWcursor* cursor) { @autoreleasepool { if (cursorInContentArea(window)) @@ -1718,7 +1836,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } // autoreleasepool } -void _glfwPlatformSetClipboardString(const char* string) +void _glfwSetClipboardStringCocoa(const char* string) { @autoreleasepool { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; @@ -1727,7 +1845,7 @@ void _glfwPlatformSetClipboardString(const char* string) } // autoreleasepool } -const char* _glfwPlatformGetClipboardString(void) +const char* _glfwGetClipboardStringCocoa(void) { @autoreleasepool { @@ -1748,7 +1866,7 @@ const char* _glfwPlatformGetClipboardString(void) return NULL; } - free(_glfw.ns.clipboardString); + _glfw_free(_glfw.ns.clipboardString); _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]); return _glfw.ns.clipboardString; @@ -1756,7 +1874,7 @@ const char* _glfwPlatformGetClipboardString(void) } // autoreleasepool } -EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) +EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) { @@ -1776,7 +1894,7 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) if (type) { - *attribs = calloc(3, sizeof(EGLint)); + *attribs = _glfw_calloc(3, sizeof(EGLint)); (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE; (*attribs)[1] = type; (*attribs)[2] = EGL_NONE; @@ -1787,17 +1905,17 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) return 0; } -EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void) +EGLNativeDisplayType _glfwGetEGLNativeDisplayCocoa(void) { return EGL_DEFAULT_DISPLAY; } -EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window) +EGLNativeWindowType _glfwGetEGLNativeWindowCocoa(_GLFWwindow* window) { return window->ns.layer; } -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +void _glfwGetRequiredInstanceExtensionsCocoa(char** extensions) { if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface) { @@ -1811,17 +1929,17 @@ void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) } } -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) +GLFWbool _glfwGetPhysicalDevicePresentationSupportCocoa(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) { return GLFW_TRUE; } -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) +VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) { @autoreleasepool { @@ -1918,6 +2036,14 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(nil); + + if (_glfw.platform.platformID != GLFW_PLATFORM_COCOA) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, + "Cocoa: Platform not initialized"); + return NULL; + } + return window->ns.object; } diff --git a/src/external/glfw/src/context.c b/src/external/glfw/src/context.c index f6629f554..33b399c9b 100644 --- a/src/external/glfw/src/context.c +++ b/src/external/glfw/src/context.c @@ -48,16 +48,6 @@ // GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) { - if (ctxconfig->share) - { - if (ctxconfig->client == GLFW_NO_API || - ctxconfig->share->context.client == GLFW_NO_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return GLFW_FALSE; - } - } - if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && ctxconfig->source != GLFW_EGL_CONTEXT_API && ctxconfig->source != GLFW_OSMESA_CONTEXT_API) @@ -78,6 +68,23 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) return GLFW_FALSE; } + if (ctxconfig->share) + { + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->share->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return GLFW_FALSE; + } + + if (ctxconfig->source != ctxconfig->share->context.source) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Context creation APIs do not match between contexts"); + return GLFW_FALSE; + } + } + if (ctxconfig->client == GLFW_OPENGL_API) { if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || @@ -609,10 +616,12 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); + _GLFWwindow* previous; _GLFW_REQUIRE_INIT(); + previous = _glfwPlatformGetTls(&_glfw.contextSlot); + if (window && window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, diff --git a/src/external/glfw/src/egl_context.c b/src/external/glfw/src/egl_context.c index 975c67be2..bc5f3e600 100644 --- a/src/external/glfw/src/egl_context.c +++ b/src/external/glfw/src/egl_context.c @@ -103,10 +103,10 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, return GLFW_FALSE; } - nativeConfigs = calloc(nativeCount, sizeof(EGLConfig)); + nativeConfigs = _glfw_calloc(nativeCount, sizeof(EGLConfig)); eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableConfigs = _glfw_calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; for (i = 0; i < nativeCount; i++) @@ -123,6 +123,7 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, continue; #if defined(_GLFW_X11) + if (_glfw.platform.platformID == GLFW_PLATFORM_X11) { XVisualInfo vi = {0}; @@ -172,6 +173,21 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE); u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); +#if defined(_GLFW_WAYLAND) + if (_glfw.platform.platformID == GLFW_PLATFORM_WAYLAND) + { + // NOTE: The wl_surface opaque region is no guarantee that its buffer + // is presented as opaque, if it also has an alpha channel + // HACK: If EGL_EXT_present_opaque is unavailable, ignore any config + // with an alpha channel to ensure the buffer is opaque + if (!_glfw.egl.EXT_present_opaque) + { + if (!desired->transparent && u->alphaBits > 0) + continue; + } + } +#endif // _GLFW_WAYLAND + u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); u->doublebuffer = desired->doublebuffer; @@ -183,8 +199,8 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, if (closest) *result = (EGLConfig) closest->handle; - free(nativeConfigs); - free(usableConfigs); + _glfw_free(nativeConfigs); + _glfw_free(usableConfigs); return closest != NULL; } @@ -230,6 +246,15 @@ static void swapBuffersEGL(_GLFWwindow* window) return; } +#if defined(_GLFW_WAYLAND) + if (_glfw.platform.platformID == GLFW_PLATFORM_WAYLAND) + { + // NOTE: Swapping buffers on a hidden window on Wayland makes it visible + if (!window->wl.visible) + return; + } +#endif + eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); } @@ -256,8 +281,8 @@ static GLFWglproc getProcAddressEGL(const char* procname) if (window->context.egl.client) { - GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, - procname); + GLFWglproc proc = (GLFWglproc) + _glfwPlatformGetModuleSymbol(window->context.egl.client, procname); if (proc) return proc; } @@ -267,15 +292,14 @@ static GLFWglproc getProcAddressEGL(const char* procname) static void destroyContextEGL(_GLFWwindow* window) { -#if defined(_GLFW_X11) // NOTE: Do not unload libGL.so.1 while the X11 display is still open, // as it will make XCloseDisplay segfault - if (window->context.client != GLFW_OPENGL_API) -#endif // _GLFW_X11 + if (_glfw.platform.platformID != GLFW_PLATFORM_X11 || + window->context.client != GLFW_OPENGL_API) { if (window->context.egl.client) { - _glfw_dlclose(window->context.egl.client); + _glfwPlatformFreeModule(window->context.egl.client); window->context.egl.client = NULL; } } @@ -316,6 +340,8 @@ GLFWbool _glfwInitEGL(void) "libEGL.dylib", #elif defined(__CYGWIN__) "libEGL-1.so", +#elif defined(__OpenBSD__) || defined(__NetBSD__) + "libEGL.so", #else "libEGL.so.1", #endif @@ -327,7 +353,7 @@ GLFWbool _glfwInitEGL(void) for (i = 0; sonames[i]; i++) { - _glfw.egl.handle = _glfw_dlopen(sonames[i]); + _glfw.egl.handle = _glfwPlatformLoadModule(sonames[i]); if (_glfw.egl.handle) break; } @@ -341,37 +367,37 @@ GLFWbool _glfwInitEGL(void) _glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0); _glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib) - _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetConfigAttrib"); _glfw.egl.GetConfigs = (PFN_eglGetConfigs) - _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetConfigs"); _glfw.egl.GetDisplay = (PFN_eglGetDisplay) - _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetDisplay"); _glfw.egl.GetError = (PFN_eglGetError) - _glfw_dlsym(_glfw.egl.handle, "eglGetError"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetError"); _glfw.egl.Initialize = (PFN_eglInitialize) - _glfw_dlsym(_glfw.egl.handle, "eglInitialize"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglInitialize"); _glfw.egl.Terminate = (PFN_eglTerminate) - _glfw_dlsym(_glfw.egl.handle, "eglTerminate"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglTerminate"); _glfw.egl.BindAPI = (PFN_eglBindAPI) - _glfw_dlsym(_glfw.egl.handle, "eglBindAPI"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglBindAPI"); _glfw.egl.CreateContext = (PFN_eglCreateContext) - _glfw_dlsym(_glfw.egl.handle, "eglCreateContext"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglCreateContext"); _glfw.egl.DestroySurface = (PFN_eglDestroySurface) - _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglDestroySurface"); _glfw.egl.DestroyContext = (PFN_eglDestroyContext) - _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglDestroyContext"); _glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface) - _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglCreateWindowSurface"); _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) - _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglMakeCurrent"); _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) - _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglSwapBuffers"); _glfw.egl.SwapInterval = (PFN_eglSwapInterval) - _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglSwapInterval"); _glfw.egl.QueryString = (PFN_eglQueryString) - _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglQueryString"); _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) - _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); + _glfwPlatformGetModuleSymbol(_glfw.egl.handle, "eglGetProcAddress"); if (!_glfw.egl.GetConfigAttrib || !_glfw.egl.GetConfigs || @@ -429,18 +455,18 @@ GLFWbool _glfwInitEGL(void) eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"); } - _glfw.egl.platform = _glfwPlatformGetEGLPlatform(&attribs); + _glfw.egl.platform = _glfw.platform.getEGLPlatform(&attribs); if (_glfw.egl.platform) { _glfw.egl.display = eglGetPlatformDisplayEXT(_glfw.egl.platform, - _glfwPlatformGetEGLNativeDisplay(), + _glfw.platform.getEGLNativeDisplay(), attribs); } else - _glfw.egl.display = eglGetDisplay(_glfwPlatformGetEGLNativeDisplay()); + _glfw.egl.display = eglGetDisplay(_glfw.platform.getEGLNativeDisplay()); - free(attribs); + _glfw_free(attribs); if (_glfw.egl.display == EGL_NO_DISPLAY) { @@ -472,6 +498,8 @@ GLFWbool _glfwInitEGL(void) extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); _glfw.egl.KHR_context_flush_control = extensionSupportedEGL("EGL_KHR_context_flush_control"); + _glfw.egl.EXT_present_opaque = + extensionSupportedEGL("EGL_EXT_present_opaque"); return GLFW_TRUE; } @@ -488,12 +516,12 @@ void _glfwTerminateEGL(void) if (_glfw.egl.handle) { - _glfw_dlclose(_glfw.egl.handle); + _glfwPlatformFreeModule(_glfw.egl.handle); _glfw.egl.handle = NULL; } } -#define setAttrib(a, v) \ +#define SET_ATTRIB(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ @@ -571,13 +599,13 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_NO_RESET_NOTIFICATION_KHR); + SET_ATTRIB(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_NO_RESET_NOTIFICATION_KHR); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_LOSE_CONTEXT_ON_RESET_KHR); + SET_ATTRIB(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_LOSE_CONTEXT_ON_RESET_KHR); } flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; @@ -586,42 +614,42 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (ctxconfig->noerror) { if (_glfw.egl.KHR_create_context_no_error) - setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); + SET_ATTRIB(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); } if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); - setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); + SET_ATTRIB(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); + SET_ATTRIB(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); } if (mask) - setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); + SET_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); if (flags) - setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); + SET_ATTRIB(EGL_CONTEXT_FLAGS_KHR, flags); } else { if (ctxconfig->client == GLFW_OPENGL_ES_API) - setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); + SET_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); } if (_glfw.egl.KHR_context_flush_control) { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, - EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); + SET_ATTRIB(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, - EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); + SET_ATTRIB(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); } } - setAttrib(EGL_NONE, EGL_NONE); + SET_ATTRIB(EGL_NONE, EGL_NONE); window->context.egl.handle = eglCreateContext(_glfw.egl.display, config, share, attribs); @@ -640,15 +668,18 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (fbconfig->sRGB) { if (_glfw.egl.KHR_gl_colorspace) - setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); + SET_ATTRIB(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); } if (!fbconfig->doublebuffer) - setAttrib(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); + SET_ATTRIB(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); - setAttrib(EGL_NONE, EGL_NONE); + if (_glfw.egl.EXT_present_opaque) + SET_ATTRIB(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent); - native = _glfwPlatformGetEGLNativeWindow(window); + SET_ATTRIB(EGL_NONE, EGL_NONE); + + native = _glfw.platform.getEGLNativeWindow(window); // HACK: ANGLE does not implement eglCreatePlatformWindowSurfaceEXT // despite reporting EGL_EXT_platform_base if (_glfw.egl.platform && _glfw.egl.platform != EGL_PLATFORM_ANGLE_ANGLE) @@ -686,6 +717,8 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, "libGLES_CM.dll", #elif defined(_GLFW_COCOA) "libGLESv1_CM.dylib", +#elif defined(__OpenBSD__) || defined(__NetBSD__) + "libGLESv1_CM.so", #else "libGLESv1_CM.so.1", "libGLES_CM.so.1", @@ -703,6 +736,8 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, "libGLESv2.dylib", #elif defined(__CYGWIN__) "libGLESv2-2.so", +#elif defined(__OpenBSD__) || defined(__NetBSD__) + "libGLESv2.so", #else "libGLESv2.so.2", #endif @@ -714,7 +749,10 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, _GLFW_OPENGL_LIBRARY, #elif defined(_GLFW_WIN32) #elif defined(_GLFW_COCOA) +#elif defined(__OpenBSD__) || defined(__NetBSD__) + "libGL.so", #else + "libOpenGL.so.0", "libGL.so.1", #endif NULL @@ -737,7 +775,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (_glfw.egl.prefix != (strncmp(sonames[i], "lib", 3) == 0)) continue; - window->context.egl.client = _glfw_dlopen(sonames[i]); + window->context.egl.client = _glfwPlatformLoadModule(sonames[i]); if (window->context.egl.client) break; } @@ -760,7 +798,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, return GLFW_TRUE; } -#undef setAttrib +#undef SET_ATTRIB // Returns the Visual and depth of the chosen EGLConfig // @@ -821,7 +859,7 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); - if (window->context.client == GLFW_NO_API) + if (window->context.source != GLFW_EGL_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return EGL_NO_CONTEXT; @@ -835,7 +873,7 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); - if (window->context.client == GLFW_NO_API) + if (window->context.source != GLFW_EGL_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return EGL_NO_SURFACE; diff --git a/src/external/glfw/src/egl_context.h b/src/external/glfw/src/egl_context.h deleted file mode 100644 index 4c84072ee..000000000 --- a/src/external/glfw/src/egl_context.h +++ /dev/null @@ -1,229 +0,0 @@ -//======================================================================== -// GLFW 3.4 EGL - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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(_GLFW_WIN32) - #define EGLAPIENTRY __stdcall -#else - #define EGLAPIENTRY -#endif - -#define EGL_SUCCESS 0x3000 -#define EGL_NOT_INITIALIZED 0x3001 -#define EGL_BAD_ACCESS 0x3002 -#define EGL_BAD_ALLOC 0x3003 -#define EGL_BAD_ATTRIBUTE 0x3004 -#define EGL_BAD_CONFIG 0x3005 -#define EGL_BAD_CONTEXT 0x3006 -#define EGL_BAD_CURRENT_SURFACE 0x3007 -#define EGL_BAD_DISPLAY 0x3008 -#define EGL_BAD_MATCH 0x3009 -#define EGL_BAD_NATIVE_PIXMAP 0x300a -#define EGL_BAD_NATIVE_WINDOW 0x300b -#define EGL_BAD_PARAMETER 0x300c -#define EGL_BAD_SURFACE 0x300d -#define EGL_CONTEXT_LOST 0x300e -#define EGL_COLOR_BUFFER_TYPE 0x303f -#define EGL_RGB_BUFFER 0x308e -#define EGL_SURFACE_TYPE 0x3033 -#define EGL_WINDOW_BIT 0x0004 -#define EGL_RENDERABLE_TYPE 0x3040 -#define EGL_OPENGL_ES_BIT 0x0001 -#define EGL_OPENGL_ES2_BIT 0x0004 -#define EGL_OPENGL_BIT 0x0008 -#define EGL_ALPHA_SIZE 0x3021 -#define EGL_BLUE_SIZE 0x3022 -#define EGL_GREEN_SIZE 0x3023 -#define EGL_RED_SIZE 0x3024 -#define EGL_DEPTH_SIZE 0x3025 -#define EGL_STENCIL_SIZE 0x3026 -#define EGL_SAMPLES 0x3031 -#define EGL_OPENGL_ES_API 0x30a0 -#define EGL_OPENGL_API 0x30a2 -#define EGL_NONE 0x3038 -#define EGL_RENDER_BUFFER 0x3086 -#define EGL_SINGLE_BUFFER 0x3085 -#define EGL_EXTENSIONS 0x3055 -#define EGL_CONTEXT_CLIENT_VERSION 0x3098 -#define EGL_NATIVE_VISUAL_ID 0x302e -#define EGL_NO_SURFACE ((EGLSurface) 0) -#define EGL_NO_DISPLAY ((EGLDisplay) 0) -#define EGL_NO_CONTEXT ((EGLContext) 0) -#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) - -#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 -#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 -#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 -#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 -#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd -#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be -#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf -#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 -#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 -#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb -#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd -#define EGL_CONTEXT_FLAGS_KHR 0x30fc -#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 -#define EGL_GL_COLORSPACE_KHR 0x309d -#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 -#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 -#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 -#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 -#define EGL_PLATFORM_X11_EXT 0x31d5 -#define EGL_PLATFORM_WAYLAND_EXT 0x31d8 -#define EGL_PLATFORM_ANGLE_ANGLE 0x3202 -#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203 -#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320d -#define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320e -#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207 -#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208 -#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450 -#define EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE 0x3489 -#define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348f - -typedef int EGLint; -typedef unsigned int EGLBoolean; -typedef unsigned int EGLenum; -typedef void* EGLConfig; -typedef void* EGLContext; -typedef void* EGLDisplay; -typedef void* EGLSurface; - -typedef void* EGLNativeDisplayType; -typedef void* EGLNativeWindowType; - -// EGL function pointer typedefs -typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); -typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); -typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); -typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); -typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); -typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); -typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); -typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); -#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib -#define eglGetConfigs _glfw.egl.GetConfigs -#define eglGetDisplay _glfw.egl.GetDisplay -#define eglGetError _glfw.egl.GetError -#define eglInitialize _glfw.egl.Initialize -#define eglTerminate _glfw.egl.Terminate -#define eglBindAPI _glfw.egl.BindAPI -#define eglCreateContext _glfw.egl.CreateContext -#define eglDestroySurface _glfw.egl.DestroySurface -#define eglDestroyContext _glfw.egl.DestroyContext -#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface -#define eglMakeCurrent _glfw.egl.MakeCurrent -#define eglSwapBuffers _glfw.egl.SwapBuffers -#define eglSwapInterval _glfw.egl.SwapInterval -#define eglQueryString _glfw.egl.QueryString -#define eglGetProcAddress _glfw.egl.GetProcAddress - -typedef EGLDisplay (EGLAPIENTRY * PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum,void*,const EGLint*); -typedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay,EGLConfig,void*,const EGLint*); -#define eglGetPlatformDisplayEXT _glfw.egl.GetPlatformDisplayEXT -#define eglCreatePlatformWindowSurfaceEXT _glfw.egl.CreatePlatformWindowSurfaceEXT - -// EGL-specific per-context data -// -typedef struct _GLFWcontextEGL -{ - EGLConfig config; - EGLContext handle; - EGLSurface surface; - - void* client; - -} _GLFWcontextEGL; - -// EGL-specific global data -// -typedef struct _GLFWlibraryEGL -{ - EGLenum platform; - EGLDisplay display; - EGLint major, minor; - GLFWbool prefix; - - GLFWbool KHR_create_context; - GLFWbool KHR_create_context_no_error; - GLFWbool KHR_gl_colorspace; - GLFWbool KHR_get_all_proc_addresses; - GLFWbool KHR_context_flush_control; - GLFWbool EXT_client_extensions; - GLFWbool EXT_platform_base; - GLFWbool EXT_platform_x11; - GLFWbool EXT_platform_wayland; - GLFWbool ANGLE_platform_angle; - GLFWbool ANGLE_platform_angle_opengl; - GLFWbool ANGLE_platform_angle_d3d; - GLFWbool ANGLE_platform_angle_vulkan; - GLFWbool ANGLE_platform_angle_metal; - - void* handle; - - PFN_eglGetConfigAttrib GetConfigAttrib; - PFN_eglGetConfigs GetConfigs; - PFN_eglGetDisplay GetDisplay; - PFN_eglGetError GetError; - PFN_eglInitialize Initialize; - PFN_eglTerminate Terminate; - PFN_eglBindAPI BindAPI; - PFN_eglCreateContext CreateContext; - PFN_eglDestroySurface DestroySurface; - PFN_eglDestroyContext DestroyContext; - PFN_eglCreateWindowSurface CreateWindowSurface; - PFN_eglMakeCurrent MakeCurrent; - PFN_eglSwapBuffers SwapBuffers; - PFN_eglSwapInterval SwapInterval; - PFN_eglQueryString QueryString; - PFN_eglGetProcAddress GetProcAddress; - - PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT; - PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC CreatePlatformWindowSurfaceEXT; - -} _GLFWlibraryEGL; - - -GLFWbool _glfwInitEGL(void); -void _glfwTerminateEGL(void); -GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); -#if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig, - Visual** visual, int* depth); -#endif /*_GLFW_X11*/ - diff --git a/src/external/glfw/src/glfw_config.h.in b/src/external/glfw/src/glfw_config.h.in deleted file mode 100644 index f4876da28..000000000 --- a/src/external/glfw/src/glfw_config.h.in +++ /dev/null @@ -1,58 +0,0 @@ -//======================================================================== -// GLFW 3.4 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2010-2016 Camilla Löwy -// -// 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. -// -//======================================================================== -// As glfw_config.h.in, this file is used by CMake to produce the -// glfw_config.h configuration header file. If you are adding a feature -// requiring conditional compilation, this is where to add the macro. -//======================================================================== -// As glfw_config.h, this file defines compile-time option macros for a -// specific platform and development environment. If you are using the -// GLFW CMake files, modify glfw_config.h.in instead of this file. If you -// are using your own build system, make this file define the appropriate -// macros in whatever way is suitable. -//======================================================================== - -// Define this to 1 if building GLFW for X11 -#cmakedefine _GLFW_X11 -// Define this to 1 if building GLFW for Win32 -#cmakedefine _GLFW_WIN32 -// Define this to 1 if building GLFW for Cocoa -#cmakedefine _GLFW_COCOA -// Define this to 1 if building GLFW for Wayland -#cmakedefine _GLFW_WAYLAND -// Define this to 1 if building GLFW for OSMesa -#cmakedefine _GLFW_OSMESA - -// Define this to 1 to use Vulkan loader linked statically into application -#cmakedefine _GLFW_VULKAN_STATIC - -// Define this to 1 to force use of high-performance GPU on hybrid systems -#cmakedefine _GLFW_USE_HYBRID_HPG - -// Define this to 1 if xkbcommon supports the compose key -#cmakedefine HAVE_XKBCOMMON_COMPOSE_H -// Define this to 1 if the libc supports memfd_create() -#cmakedefine HAVE_MEMFD_CREATE - diff --git a/src/external/glfw/src/glx_context.c b/src/external/glfw/src/glx_context.c index 374c15e06..3c38807f3 100644 --- a/src/external/glfw/src/glx_context.c +++ b/src/external/glfw/src/glx_context.c @@ -55,7 +55,7 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; - int i, nativeCount, usableCount; + int nativeCount, usableCount; const char* vendor; GLFWbool trustWindowBit = GLFW_TRUE; @@ -73,10 +73,10 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, return GLFW_FALSE; } - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableConfigs = _glfw_calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; - for (i = 0; i < nativeCount; i++) + for (int i = 0; i < nativeCount; i++) { const GLXFBConfig n = nativeConfigs[i]; _GLFWfbconfig* u = usableConfigs + usableCount; @@ -138,7 +138,7 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, *result = (GLXFBConfig) closest->handle; XFree(nativeConfigs); - free(usableConfigs); + _glfw_free(usableConfigs); return closest != NULL; } @@ -226,7 +226,10 @@ static GLFWglproc getProcAddressGLX(const char* procname) else if (_glfw.glx.GetProcAddressARB) return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); else - return _glfw_dlsym(_glfw.glx.handle, procname); + { + // NOTE: glvnd provides GLX 1.4, so this can only happen with libGL + return _glfwPlatformGetModuleSymbol(_glfw.glx.handle, procname); + } } static void destroyContextGLX(_GLFWwindow* window) @@ -253,14 +256,16 @@ static void destroyContextGLX(_GLFWwindow* window) // GLFWbool _glfwInitGLX(void) { - int i; const char* sonames[] = { #if defined(_GLFW_GLX_LIBRARY) _GLFW_GLX_LIBRARY, #elif defined(__CYGWIN__) "libGL-1.so", +#elif defined(__OpenBSD__) || defined(__NetBSD__) + "libGL.so", #else + "libGLX.so.0", "libGL.so.1", "libGL.so", #endif @@ -270,9 +275,9 @@ GLFWbool _glfwInitGLX(void) if (_glfw.glx.handle) return GLFW_TRUE; - for (i = 0; sonames[i]; i++) + for (int i = 0; sonames[i]; i++) { - _glfw.glx.handle = _glfw_dlopen(sonames[i]); + _glfw.glx.handle = _glfwPlatformLoadModule(sonames[i]); if (_glfw.glx.handle) break; } @@ -283,36 +288,32 @@ GLFWbool _glfwInitGLX(void) return GLFW_FALSE; } - _glfw.glx.GetFBConfigs = - _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigs"); - _glfw.glx.GetFBConfigAttrib = - _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib"); - _glfw.glx.GetClientString = - _glfw_dlsym(_glfw.glx.handle, "glXGetClientString"); - _glfw.glx.QueryExtension = - _glfw_dlsym(_glfw.glx.handle, "glXQueryExtension"); - _glfw.glx.QueryVersion = - _glfw_dlsym(_glfw.glx.handle, "glXQueryVersion"); - _glfw.glx.DestroyContext = - _glfw_dlsym(_glfw.glx.handle, "glXDestroyContext"); - _glfw.glx.MakeCurrent = - _glfw_dlsym(_glfw.glx.handle, "glXMakeCurrent"); - _glfw.glx.SwapBuffers = - _glfw_dlsym(_glfw.glx.handle, "glXSwapBuffers"); - _glfw.glx.QueryExtensionsString = - _glfw_dlsym(_glfw.glx.handle, "glXQueryExtensionsString"); - _glfw.glx.CreateNewContext = - _glfw_dlsym(_glfw.glx.handle, "glXCreateNewContext"); - _glfw.glx.CreateWindow = - _glfw_dlsym(_glfw.glx.handle, "glXCreateWindow"); - _glfw.glx.DestroyWindow = - _glfw_dlsym(_glfw.glx.handle, "glXDestroyWindow"); - _glfw.glx.GetProcAddress = - _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddress"); - _glfw.glx.GetProcAddressARB = - _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddressARB"); - _glfw.glx.GetVisualFromFBConfig = - _glfw_dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig"); + _glfw.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetFBConfigs"); + _glfw.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetFBConfigAttrib"); + _glfw.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetClientString"); + _glfw.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXQueryExtension"); + _glfw.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXQueryVersion"); + _glfw.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXDestroyContext"); + _glfw.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXMakeCurrent"); + _glfw.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXSwapBuffers"); + _glfw.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXQueryExtensionsString"); + _glfw.glx.CreateNewContext = (PFNGLXCREATENEWCONTEXTPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXCreateNewContext"); + _glfw.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXCreateWindow"); + _glfw.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXDestroyWindow"); + _glfw.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetVisualFromFBConfig"); if (!_glfw.glx.GetFBConfigs || !_glfw.glx.GetFBConfigAttrib || @@ -326,8 +327,6 @@ GLFWbool _glfwInitGLX(void) !_glfw.glx.CreateNewContext || !_glfw.glx.CreateWindow || !_glfw.glx.DestroyWindow || - !_glfw.glx.GetProcAddress || - !_glfw.glx.GetProcAddressARB || !_glfw.glx.GetVisualFromFBConfig) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -335,6 +334,12 @@ GLFWbool _glfwInitGLX(void) return GLFW_FALSE; } + // NOTE: Unlike GLX 1.3 entry points these are not required to be present + _glfw.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetProcAddress"); + _glfw.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) + _glfwPlatformGetModuleSymbol(_glfw.glx.handle, "glXGetProcAddressARB"); + if (!glXQueryExtension(_glfw.x11.display, &_glfw.glx.errorBase, &_glfw.glx.eventBase)) @@ -425,16 +430,16 @@ GLFWbool _glfwInitGLX(void) void _glfwTerminateGLX(void) { // NOTE: This function must not call any X11 functions, as it is called - // after XCloseDisplay (see _glfwPlatformTerminate for details) + // after XCloseDisplay (see _glfwTerminateX11 for details) if (_glfw.glx.handle) { - _glfw_dlclose(_glfw.glx.handle); + _glfwPlatformFreeModule(_glfw.glx.handle); _glfw.glx.handle = NULL; } } -#define setAttrib(a, v) \ +#define SET_ATTRIB(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ @@ -522,13 +527,13 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_NO_RESET_NOTIFICATION_ARB); + SET_ATTRIB(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_LOSE_CONTEXT_ON_RESET_ARB); + SET_ATTRIB(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_LOSE_CONTEXT_ON_RESET_ARB); } flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; @@ -541,13 +546,13 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + SET_ATTRIB(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + SET_ATTRIB(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } @@ -555,7 +560,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (ctxconfig->noerror) { if (_glfw.glx.ARB_create_context_no_error) - setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); + SET_ATTRIB(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); } // NOTE: Only request an explicitly versioned context when necessary, as @@ -563,17 +568,17 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + SET_ATTRIB(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + SET_ATTRIB(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (mask) - setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); + SET_ATTRIB(GLX_CONTEXT_PROFILE_MASK_ARB, mask); if (flags) - setAttrib(GLX_CONTEXT_FLAGS_ARB, flags); + SET_ATTRIB(GLX_CONTEXT_FLAGS_ARB, flags); - setAttrib(None, None); + SET_ATTRIB(None, None); window->context.glx.handle = _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, @@ -630,7 +635,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, return GLFW_TRUE; } -#undef setAttrib +#undef SET_ATTRIB // Returns the Visual and depth of the chosen GLXFBConfig // @@ -674,7 +679,13 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (window->context.client == GLFW_NO_API) + if (_glfw.platform.platformID != GLFW_PLATFORM_X11) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "GLX: Platform not initialized"); + return NULL; + } + + if (window->context.source != GLFW_NATIVE_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; @@ -688,7 +699,13 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(None); - if (window->context.client == GLFW_NO_API) + if (_glfw.platform.platformID != GLFW_PLATFORM_X11) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "GLX: Platform not initialized"); + return None; + } + + if (window->context.source != GLFW_NATIVE_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return None; diff --git a/src/external/glfw/src/glx_context.h b/src/external/glfw/src/glx_context.h deleted file mode 100644 index 94f07e2eb..000000000 --- a/src/external/glfw/src/glx_context.h +++ /dev/null @@ -1,181 +0,0 @@ -//======================================================================== -// GLFW 3.4 GLX - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define GLX_VENDOR 1 -#define GLX_RGBA_BIT 0x00000001 -#define GLX_WINDOW_BIT 0x00000001 -#define GLX_DRAWABLE_TYPE 0x8010 -#define GLX_RENDER_TYPE 0x8011 -#define GLX_RGBA_TYPE 0x8014 -#define GLX_DOUBLEBUFFER 5 -#define GLX_STEREO 6 -#define GLX_AUX_BUFFERS 7 -#define GLX_RED_SIZE 8 -#define GLX_GREEN_SIZE 9 -#define GLX_BLUE_SIZE 10 -#define GLX_ALPHA_SIZE 11 -#define GLX_DEPTH_SIZE 12 -#define GLX_STENCIL_SIZE 13 -#define GLX_ACCUM_RED_SIZE 14 -#define GLX_ACCUM_GREEN_SIZE 15 -#define GLX_ACCUM_BLUE_SIZE 16 -#define GLX_ACCUM_ALPHA_SIZE 17 -#define GLX_SAMPLES 0x186a1 -#define GLX_VISUAL_ID 0x800b - -#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 -#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 -#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 -#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 -#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define GLX_CONTEXT_FLAGS_ARB 0x2094 -#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 -#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 -#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 -#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 -#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 -#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 - -typedef XID GLXWindow; -typedef XID GLXDrawable; -typedef struct __GLXFBConfig* GLXFBConfig; -typedef struct __GLXcontext* GLXContext; -typedef void (*__GLXextproc)(void); - -typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); -typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); -typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); -typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); -typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); -typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); -typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); -typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); -typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); -typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); -typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); -typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); -typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); -typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); -typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); - -typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); -typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); -typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); - -// libGL.so function pointer typedefs -#define glXGetFBConfigs _glfw.glx.GetFBConfigs -#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib -#define glXGetClientString _glfw.glx.GetClientString -#define glXQueryExtension _glfw.glx.QueryExtension -#define glXQueryVersion _glfw.glx.QueryVersion -#define glXDestroyContext _glfw.glx.DestroyContext -#define glXMakeCurrent _glfw.glx.MakeCurrent -#define glXSwapBuffers _glfw.glx.SwapBuffers -#define glXQueryExtensionsString _glfw.glx.QueryExtensionsString -#define glXCreateNewContext _glfw.glx.CreateNewContext -#define glXGetVisualFromFBConfig _glfw.glx.GetVisualFromFBConfig -#define glXCreateWindow _glfw.glx.CreateWindow -#define glXDestroyWindow _glfw.glx.DestroyWindow - -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx - - -// GLX-specific per-context data -// -typedef struct _GLFWcontextGLX -{ - GLXContext handle; - GLXWindow window; - -} _GLFWcontextGLX; - -// GLX-specific global data -// -typedef struct _GLFWlibraryGLX -{ - int major, minor; - int eventBase; - int errorBase; - - // dlopen handle for libGL.so.1 - void* handle; - - // GLX 1.3 functions - PFNGLXGETFBCONFIGSPROC GetFBConfigs; - PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; - PFNGLXGETCLIENTSTRINGPROC GetClientString; - PFNGLXQUERYEXTENSIONPROC QueryExtension; - PFNGLXQUERYVERSIONPROC QueryVersion; - PFNGLXDESTROYCONTEXTPROC DestroyContext; - PFNGLXMAKECURRENTPROC MakeCurrent; - PFNGLXSWAPBUFFERSPROC SwapBuffers; - PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; - PFNGLXCREATENEWCONTEXTPROC CreateNewContext; - PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; - PFNGLXCREATEWINDOWPROC CreateWindow; - PFNGLXDESTROYWINDOWPROC DestroyWindow; - - // GLX 1.4 and extension functions - PFNGLXGETPROCADDRESSPROC GetProcAddress; - PFNGLXGETPROCADDRESSPROC GetProcAddressARB; - PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI; - PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; - PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; - PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; - GLFWbool SGI_swap_control; - GLFWbool EXT_swap_control; - GLFWbool MESA_swap_control; - GLFWbool ARB_multisample; - GLFWbool ARB_framebuffer_sRGB; - GLFWbool EXT_framebuffer_sRGB; - GLFWbool ARB_create_context; - GLFWbool ARB_create_context_profile; - GLFWbool ARB_create_context_robustness; - GLFWbool EXT_create_context_es2_profile; - GLFWbool ARB_create_context_no_error; - GLFWbool ARB_context_flush_control; - -} _GLFWlibraryGLX; - -GLFWbool _glfwInitGLX(void); -void _glfwTerminateGLX(void); -GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); -void _glfwDestroyContextGLX(_GLFWwindow* window); -GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig, - Visual** visual, int* depth); - diff --git a/src/external/glfw/src/init.c b/src/external/glfw/src/init.c index bca59baab..d07a492ec 100644 --- a/src/external/glfw/src/init.c +++ b/src/external/glfw/src/init.c @@ -28,7 +28,6 @@ //======================================================================== #include "internal.h" -#include "mappings.h" #include #include @@ -37,23 +36,25 @@ #include -// The global variables below comprise all mutable global data in GLFW -// -// Any other global variable is a bug +// NOTE: The global variables below comprise all mutable global data in GLFW +// Any other mutable global variable is a bug -// Global state shared between compilation units of GLFW +// This contains all mutable state shared between compilation units of GLFW // _GLFWlibrary _glfw = { GLFW_FALSE }; // These are outside of _glfw so they can be used before initialization and -// after termination +// after termination without special handling when _glfw is cleared to zero // static _GLFWerror _glfwMainThreadError; static GLFWerrorfun _glfwErrorCallback; +static GLFWallocator _glfwInitAllocator; static _GLFWinitconfig _glfwInitHints = { GLFW_TRUE, // hat buttons GLFW_ANGLE_PLATFORM_TYPE_NONE, // ANGLE backend + GLFW_ANY_PLATFORM, // preferred platform + NULL, // vkGetInstanceProcAddr function { GLFW_TRUE, // macOS menu bar GLFW_TRUE // macOS bundle chdir @@ -63,6 +64,27 @@ static _GLFWinitconfig _glfwInitHints = }, }; +// The allocation function used when no custom allocator is set +// +static void* defaultAllocate(size_t size, void* user) +{ + return malloc(size); +} + +// The deallocation function used when no custom allocator is set +// +static void defaultDeallocate(void* block, void* user) +{ + free(block); +} + +// The reallocation function used when no custom allocator is set +// +static void* defaultReallocate(void* block, size_t size, void* user) +{ + return realloc(block, size); +} + // Terminate the library // static void terminate(void) @@ -81,21 +103,21 @@ static void terminate(void) { _GLFWmonitor* monitor = _glfw.monitors[i]; if (monitor->originalRamp.size) - _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp); + _glfw.platform.setGammaRamp(monitor, &monitor->originalRamp); _glfwFreeMonitor(monitor); } - free(_glfw.monitors); + _glfw_free(_glfw.monitors); _glfw.monitors = NULL; _glfw.monitorCount = 0; - free(_glfw.mappings); + _glfw_free(_glfw.mappings); _glfw.mappings = NULL; _glfw.mappingCount = 0; _glfwTerminateVulkan(); - _glfwPlatformTerminateJoysticks(); - _glfwPlatformTerminate(); + _glfw.platform.terminateJoysticks(); + _glfw.platform.terminate(); _glfw.initialized = GLFW_FALSE; @@ -103,7 +125,7 @@ static void terminate(void) { _GLFWerror* error = _glfw.errorListHead; _glfw.errorListHead = error->next; - free(error); + _glfw_free(error); } _glfwPlatformDestroyTls(&_glfw.contextSlot); @@ -118,14 +140,108 @@ static void terminate(void) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Encode a Unicode code point to a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) +// +size_t _glfwEncodeUTF8(char* s, uint32_t codepoint) +{ + size_t count = 0; + + if (codepoint < 0x80) + s[count++] = (char) codepoint; + else if (codepoint < 0x800) + { + s[count++] = (codepoint >> 6) | 0xc0; + s[count++] = (codepoint & 0x3f) | 0x80; + } + else if (codepoint < 0x10000) + { + s[count++] = (codepoint >> 12) | 0xe0; + s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; + s[count++] = (codepoint & 0x3f) | 0x80; + } + else if (codepoint < 0x110000) + { + s[count++] = (codepoint >> 18) | 0xf0; + s[count++] = ((codepoint >> 12) & 0x3f) | 0x80; + s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; + s[count++] = (codepoint & 0x3f) | 0x80; + } + + return count; +} + +// Splits and translates a text/uri-list into separate file paths +// NOTE: This function destroys the provided string +// +char** _glfwParseUriList(char* text, int* count) +{ + const char* prefix = "file://"; + char** paths = NULL; + char* line; + + *count = 0; + + while ((line = strtok(text, "\r\n"))) + { + char* path; + + text = NULL; + + if (line[0] == '#') + continue; + + if (strncmp(line, prefix, strlen(prefix)) == 0) + { + line += strlen(prefix); + // TODO: Validate hostname + while (*line != '/') + line++; + } + + (*count)++; + + path = _glfw_calloc(strlen(line) + 1, 1); + paths = _glfw_realloc(paths, *count * sizeof(char*)); + paths[*count - 1] = path; + + while (*line) + { + if (line[0] == '%' && line[1] && line[2]) + { + const char digits[3] = { line[1], line[2], '\0' }; + *path = (char) strtol(digits, NULL, 16); + line += 2; + } + else + *path = *line; + + path++; + line++; + } + } + + return paths; +} + char* _glfw_strdup(const char* source) { const size_t length = strlen(source); - char* result = calloc(length + 1, 1); + char* result = _glfw_calloc(length + 1, 1); strcpy(result, source); return result; } +int _glfw_min(int a, int b) +{ + return a < b ? a : b; +} + +int _glfw_max(int a, int b) +{ + return a > b ? a : b; +} + float _glfw_fminf(float a, float b) { if (a != a) @@ -150,6 +266,59 @@ float _glfw_fmaxf(float a, float b) return b; } +void* _glfw_calloc(size_t count, size_t size) +{ + if (count && size) + { + void* block; + + if (count > SIZE_MAX / size) + { + _glfwInputError(GLFW_INVALID_VALUE, "Allocation size overflow"); + return NULL; + } + + block = _glfw.allocator.allocate(count * size, _glfw.allocator.user); + if (block) + return memset(block, 0, count * size); + else + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return NULL; + } + } + else + return NULL; +} + +void* _glfw_realloc(void* block, size_t size) +{ + if (block && size) + { + void* resized = _glfw.allocator.reallocate(block, size, _glfw.allocator.user); + if (resized) + return resized; + else + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return NULL; + } + } + else if (block) + { + _glfw_free(block); + return NULL; + } + else + return _glfw_calloc(1, size); +} + +void _glfw_free(void* block) +{ + if (block) + _glfw.allocator.deallocate(block, _glfw.allocator.user); +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// @@ -200,6 +369,8 @@ void _glfwInputError(int code, const char* format, ...) strcpy(description, "The requested feature cannot be implemented for this platform"); else if (code == GLFW_FEATURE_UNIMPLEMENTED) strcpy(description, "The requested feature has not yet been implemented for this platform"); + else if (code == GLFW_PLATFORM_UNAVAILABLE) + strcpy(description, "The requested platform is unavailable"); else strcpy(description, "ERROR: UNKNOWN GLFW ERROR"); } @@ -209,7 +380,7 @@ void _glfwInputError(int code, const char* format, ...) error = _glfwPlatformGetTls(&_glfw.errorSlot); if (!error) { - error = calloc(1, sizeof(_GLFWerror)); + error = _glfw_calloc(1, sizeof(_GLFWerror)); _glfwPlatformSetTls(&_glfw.errorSlot, error); _glfwPlatformLockMutex(&_glfw.errorLock); error->next = _glfw.errorListHead; @@ -240,7 +411,18 @@ GLFWAPI int glfwInit(void) memset(&_glfw, 0, sizeof(_glfw)); _glfw.hints.init = _glfwInitHints; - if (!_glfwPlatformInit()) + _glfw.allocator = _glfwInitAllocator; + if (!_glfw.allocator.allocate) + { + _glfw.allocator.allocate = defaultAllocate; + _glfw.allocator.reallocate = defaultReallocate; + _glfw.allocator.deallocate = defaultDeallocate; + } + + if (!_glfwSelectPlatform(_glfw.hints.init.platformID, &_glfw.platform)) + return GLFW_FALSE; + + if (!_glfw.platform.init()) { terminate(); return GLFW_FALSE; @@ -256,24 +438,14 @@ GLFWAPI int glfwInit(void) _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); - _glfw.initialized = GLFW_TRUE; + _glfwInitGamepadMappings(); + + _glfwPlatformInitTimer(); _glfw.timer.offset = _glfwPlatformGetTimerValue(); + _glfw.initialized = GLFW_TRUE; + glfwDefaultWindowHints(); - - { - int i; - - for (i = 0; _glfwDefaultMappings[i]; i++) - { - if (!glfwUpdateGamepadMappings(_glfwDefaultMappings[i])) - { - terminate(); - return GLFW_FALSE; - } - } - } - return GLFW_TRUE; } @@ -295,6 +467,9 @@ GLFWAPI void glfwInitHint(int hint, int value) case GLFW_ANGLE_PLATFORM_TYPE: _glfwInitHints.angleType = value; return; + case GLFW_PLATFORM: + _glfwInitHints.platformID = value; + return; case GLFW_COCOA_CHDIR_RESOURCES: _glfwInitHints.ns.chdir = value; return; @@ -310,6 +485,24 @@ GLFWAPI void glfwInitHint(int hint, int value) "Invalid init hint 0x%08X", hint); } +GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator) +{ + if (allocator) + { + if (allocator->allocate && allocator->reallocate && allocator->deallocate) + _glfwInitAllocator = *allocator; + else + _glfwInputError(GLFW_INVALID_VALUE, "Missing function in allocator"); + } + else + memset(&_glfwInitAllocator, 0, sizeof(GLFWallocator)); +} + +GLFWAPI void glfwInitVulkanLoader(PFN_vkGetInstanceProcAddr loader) +{ + _glfwInitHints.vulkanLoader = loader; +} + GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) { if (major != NULL) @@ -320,11 +513,6 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) *rev = GLFW_VERSION_REVISION; } -GLFWAPI const char* glfwGetVersionString(void) -{ - return _glfwPlatformGetVersionString(); -} - GLFWAPI int glfwGetError(const char** description) { _GLFWerror* error; @@ -351,7 +539,7 @@ GLFWAPI int glfwGetError(const char** description) GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) { - _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); + _GLFW_SWAP(GLFWerrorfun, _glfwErrorCallback, cbfun); return cbfun; } diff --git a/src/external/glfw/src/input.c b/src/external/glfw/src/input.c index 9cc0c366f..36128e10c 100644 --- a/src/external/glfw/src/input.c +++ b/src/external/glfw/src/input.c @@ -28,6 +28,7 @@ //======================================================================== #include "internal.h" +#include "mappings.h" #include #include @@ -43,15 +44,22 @@ #define _GLFW_JOYSTICK_BUTTON 2 #define _GLFW_JOYSTICK_HATBIT 3 +#define GLFW_MOD_MASK (GLFW_MOD_SHIFT | \ + GLFW_MOD_CONTROL | \ + GLFW_MOD_ALT | \ + GLFW_MOD_SUPER | \ + GLFW_MOD_CAPS_LOCK | \ + GLFW_MOD_NUM_LOCK) + // Initializes the platform joystick API if it has not been already // static GLFWbool initJoysticks(void) { if (!_glfw.joysticksInitialized) { - if (!_glfwPlatformInitJoysticks()) + if (!_glfw.platform.initJoysticks()) { - _glfwPlatformTerminateJoysticks(); + _glfw.platform.terminateJoysticks(); return GLFW_FALSE; } } @@ -101,25 +109,13 @@ static _GLFWmapping* findValidMapping(const _GLFWjoystick* js) for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) { if (!isValidElementForJoystick(mapping->buttons + i, js)) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid button in gamepad mapping %s (%s)", - mapping->guid, - mapping->name); return NULL; - } } for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) { if (!isValidElementForJoystick(mapping->axes + i, js)) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Invalid axis in gamepad mapping %s (%s)", - mapping->guid, - mapping->name); return NULL; - } } } @@ -245,8 +241,9 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) } else { - length = strlen(_GLFW_PLATFORM_MAPPING_NAME); - if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0) + const char* name = _glfw.platform.getMappingName(); + length = strlen(name); + if (strncmp(c, name, length) != 0) return GLFW_FALSE; } @@ -263,7 +260,7 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) mapping->guid[i] += 'a' - 'A'; } - _glfwPlatformUpdateGamepadGUID(mapping->guid); + _glfw.platform.updateGamepadGUID(mapping->guid); return GLFW_TRUE; } @@ -276,6 +273,12 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) // void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods) { + assert(window != NULL); + assert(key >= 0 || key == GLFW_KEY_UNKNOWN); + assert(key <= GLFW_KEY_LAST); + assert(action == GLFW_PRESS || action == GLFW_RELEASE); + assert(mods == (mods & GLFW_MOD_MASK)); + if (key >= 0 && key <= GLFW_KEY_LAST) { GLFWbool repeated = GLFW_FALSE; @@ -305,8 +308,12 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m // Notifies shared code of a Unicode codepoint input event // The 'plain' parameter determines whether to emit a regular character event // -void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain) +void _glfwInputChar(_GLFWwindow* window, uint32_t codepoint, int mods, GLFWbool plain) { + assert(window != NULL); + assert(mods == (mods & GLFW_MOD_MASK)); + assert(plain == GLFW_TRUE || plain == GLFW_FALSE); + if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) return; @@ -327,6 +334,12 @@ void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWb // void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) { + assert(window != NULL); + assert(xoffset > -FLT_MAX); + assert(xoffset < FLT_MAX); + assert(yoffset > -FLT_MAX); + assert(yoffset < FLT_MAX); + if (window->callbacks.scroll) window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset); } @@ -335,6 +348,12 @@ void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) // void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) { + assert(window != NULL); + assert(button >= 0); + assert(button <= GLFW_MOUSE_BUTTON_LAST); + assert(action == GLFW_PRESS || action == GLFW_RELEASE); + assert(mods == (mods & GLFW_MOD_MASK)); + if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) return; @@ -355,6 +374,12 @@ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) // void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) { + assert(window != NULL); + assert(xpos > -FLT_MAX); + assert(xpos < FLT_MAX); + assert(ypos > -FLT_MAX); + assert(ypos < FLT_MAX); + if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos) return; @@ -369,6 +394,9 @@ void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) // void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) { + assert(window != NULL); + assert(entered == GLFW_TRUE || entered == GLFW_FALSE); + if (window->callbacks.cursorEnter) window->callbacks.cursorEnter((GLFWwindow*) window, entered); } @@ -377,6 +405,10 @@ void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) // void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) { + assert(window != NULL); + assert(count > 0); + assert(paths != NULL); + if (window->callbacks.drop) window->callbacks.drop((GLFWwindow*) window, count, paths); } @@ -385,16 +417,26 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) // void _glfwInputJoystick(_GLFWjoystick* js, int event) { - const int jid = (int) (js - _glfw.joysticks); + assert(js != NULL); + assert(event == GLFW_CONNECTED || event == GLFW_DISCONNECTED); + + if (event == GLFW_CONNECTED) + js->connected = GLFW_TRUE; + else if (event == GLFW_DISCONNECTED) + js->connected = GLFW_FALSE; if (_glfw.callbacks.joystick) - _glfw.callbacks.joystick(jid, event); + _glfw.callbacks.joystick((int) (js - _glfw.joysticks), event); } // Notifies shared code of the new value of a joystick axis // void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) { + assert(js != NULL); + assert(axis >= 0); + assert(axis < js->axisCount); + js->axes[axis] = value; } @@ -402,6 +444,11 @@ void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) // void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) { + assert(js != NULL); + assert(button >= 0); + assert(button < js->buttonCount); + assert(value == GLFW_PRESS || value == GLFW_RELEASE); + js->buttons[button] = value; } @@ -409,7 +456,18 @@ void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) // void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) { - const int base = js->buttonCount + hat * 4; + int base; + + assert(js != NULL); + assert(hat >= 0); + assert(hat < js->hatCount); + + // Valid hat values only use the least significant nibble and have at most two bits + // set, which can be considered adjacent plus an arbitrary rotation within the nibble + assert((value & 0xf0) == 0); + assert((value & ((value << 2) | (value >> 2))) == 0); + + base = js->buttonCount + hat * 4; js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; @@ -424,6 +482,21 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Adds the built-in set of gamepad mappings +// +void _glfwInitGamepadMappings(void) +{ + size_t i; + const size_t count = sizeof(_glfwDefaultMappings) / sizeof(char*); + _glfw.mappings = _glfw_calloc(count, sizeof(_GLFWmapping)); + + for (i = 0; i < count; i++) + { + if (parseMapping(&_glfw.mappings[_glfw.mappingCount], _glfwDefaultMappings[i])) + _glfw.mappingCount++; + } +} + // Returns an available joystick object with arrays and name allocated // _GLFWjoystick* _glfwAllocJoystick(const char* name, @@ -437,7 +510,7 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.joysticks[jid].present) + if (!_glfw.joysticks[jid].allocated) break; } @@ -445,10 +518,10 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, return NULL; js = _glfw.joysticks + jid; - js->present = GLFW_TRUE; - js->axes = calloc(axisCount, sizeof(float)); - js->buttons = calloc(buttonCount + (size_t) hatCount * 4, 1); - js->hats = calloc(hatCount, 1); + js->allocated = GLFW_TRUE; + js->axes = _glfw_calloc(axisCount, sizeof(float)); + js->buttons = _glfw_calloc(buttonCount + (size_t) hatCount * 4, 1); + js->hats = _glfw_calloc(hatCount, 1); js->axisCount = axisCount; js->buttonCount = buttonCount; js->hatCount = hatCount; @@ -464,9 +537,9 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, // void _glfwFreeJoystick(_GLFWjoystick* js) { - free(js->axes); - free(js->buttons); - free(js->hats); + _glfw_free(js->axes); + _glfw_free(js->buttons); + _glfw_free(js->hats); memset(js, 0, sizeof(_GLFWjoystick)); } @@ -476,8 +549,8 @@ void _glfwCenterCursorInContentArea(_GLFWwindow* window) { int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); + _glfw.platform.getWindowSize(window, &width, &height); + _glfw.platform.setCursorPos(window, width / 2.0, height / 2.0); } @@ -517,96 +590,109 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) _GLFW_REQUIRE_INIT(); - if (mode == GLFW_CURSOR) + switch (mode) { - if (value != GLFW_CURSOR_NORMAL && - value != GLFW_CURSOR_HIDDEN && - value != GLFW_CURSOR_DISABLED) + case GLFW_CURSOR: { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid cursor mode 0x%08X", - value); - return; - } - - if (window->cursorMode == value) - return; - - window->cursorMode = value; - - _glfwPlatformGetCursorPos(window, - &window->virtualCursorPosX, - &window->virtualCursorPosY); - _glfwPlatformSetCursorMode(window, value); - } - else if (mode == GLFW_STICKY_KEYS) - { - value = value ? GLFW_TRUE : GLFW_FALSE; - if (window->stickyKeys == value) - return; - - if (!value) - { - int i; - - // Release all sticky keys - for (i = 0; i <= GLFW_KEY_LAST; i++) + if (value != GLFW_CURSOR_NORMAL && + value != GLFW_CURSOR_HIDDEN && + value != GLFW_CURSOR_DISABLED && + value != GLFW_CURSOR_CAPTURED) { - if (window->keys[i] == _GLFW_STICK) - window->keys[i] = GLFW_RELEASE; + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid cursor mode 0x%08X", + value); + return; } + + if (window->cursorMode == value) + return; + + window->cursorMode = value; + + _glfw.platform.getCursorPos(window, + &window->virtualCursorPosX, + &window->virtualCursorPosY); + _glfw.platform.setCursorMode(window, value); + return; } - window->stickyKeys = value; - } - else if (mode == GLFW_STICKY_MOUSE_BUTTONS) - { - value = value ? GLFW_TRUE : GLFW_FALSE; - if (window->stickyMouseButtons == value) - return; - - if (!value) + case GLFW_STICKY_KEYS: { - int i; + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyKeys == value) + return; - // Release all sticky mouse buttons - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + if (!value) { - if (window->mouseButtons[i] == _GLFW_STICK) - window->mouseButtons[i] = GLFW_RELEASE; + int i; + + // Release all sticky keys + for (i = 0; i <= GLFW_KEY_LAST; i++) + { + if (window->keys[i] == _GLFW_STICK) + window->keys[i] = GLFW_RELEASE; + } } + + window->stickyKeys = value; + return; } - window->stickyMouseButtons = value; - } - else if (mode == GLFW_LOCK_KEY_MODS) - { - window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE; - } - else if (mode == GLFW_RAW_MOUSE_MOTION) - { - if (!_glfwPlatformRawMouseMotionSupported()) + case GLFW_STICKY_MOUSE_BUTTONS: { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Raw mouse motion is not supported on this system"); + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyMouseButtons == value) + return; + + if (!value) + { + int i; + + // Release all sticky mouse buttons + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == _GLFW_STICK) + window->mouseButtons[i] = GLFW_RELEASE; + } + } + + window->stickyMouseButtons = value; return; } - value = value ? GLFW_TRUE : GLFW_FALSE; - if (window->rawMouseMotion == value) + case GLFW_LOCK_KEY_MODS: + { + window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE; return; + } - window->rawMouseMotion = value; - _glfwPlatformSetRawMouseMotion(window, value); + case GLFW_RAW_MOUSE_MOTION: + { + if (!_glfw.platform.rawMouseMotionSupported()) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Raw mouse motion is not supported on this system"); + return; + } + + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->rawMouseMotion == value) + return; + + window->rawMouseMotion = value; + _glfw.platform.setRawMouseMotion(window, value); + return; + } } - else - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); } GLFWAPI int glfwRawMouseMotionSupported(void) { _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - return _glfwPlatformRawMouseMotionSupported(); + return _glfw.platform.rawMouseMotionSupported(); } GLFWAPI const char* glfwGetKeyName(int key, int scancode) @@ -622,10 +708,10 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode) return NULL; } - scancode = _glfwPlatformGetKeyScancode(key); + scancode = _glfw.platform.getKeyScancode(key); } - return _glfwPlatformGetScancodeName(scancode); + return _glfw.platform.getScancodeName(scancode); } GLFWAPI int glfwGetKeyScancode(int key) @@ -638,7 +724,7 @@ GLFWAPI int glfwGetKeyScancode(int key) return GLFW_RELEASE; } - return _glfwPlatformGetKeyScancode(key); + return _glfw.platform.getKeyScancode(key); } GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) @@ -707,7 +793,7 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) *ypos = window->virtualCursorPosY; } else - _glfwPlatformGetCursorPos(window, xpos, ypos); + _glfw.platform.getCursorPos(window, xpos, ypos); } GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) @@ -726,7 +812,7 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) return; } - if (!_glfwPlatformWindowFocused(window)) + if (!_glfw.platform.windowFocused(window)) return; if (window->cursorMode == GLFW_CURSOR_DISABLED) @@ -738,7 +824,7 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) else { // Update system cursor position - _glfwPlatformSetCursorPos(window, xpos, ypos); + _glfw.platform.setCursorPos(window, xpos, ypos); } } @@ -747,14 +833,21 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) _GLFWcursor* cursor; assert(image != NULL); + assert(image->pixels != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - cursor = calloc(1, sizeof(_GLFWcursor)); + if (image->width <= 0 || image->height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid image dimensions for cursor"); + return NULL; + } + + cursor = _glfw_calloc(1, sizeof(_GLFWcursor)); cursor->next = _glfw.cursorListHead; _glfw.cursorListHead = cursor; - if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot)) + if (!_glfw.platform.createCursor(cursor, image, xhot, yhot)) { glfwDestroyCursor((GLFWcursor*) cursor); return NULL; @@ -784,11 +877,11 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) return NULL; } - cursor = calloc(1, sizeof(_GLFWcursor)); + cursor = _glfw_calloc(1, sizeof(_GLFWcursor)); cursor->next = _glfw.cursorListHead; _glfw.cursorListHead = cursor; - if (!_glfwPlatformCreateStandardCursor(cursor, shape)) + if (!_glfw.platform.createStandardCursor(cursor, shape)) { glfwDestroyCursor((GLFWcursor*) cursor); return NULL; @@ -817,7 +910,7 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) } } - _glfwPlatformDestroyCursor(cursor); + _glfw.platform.destroyCursor(cursor); // Unlink cursor from global linked list { @@ -829,7 +922,7 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) *prev = cursor->next; } - free(cursor); + _glfw_free(cursor); } GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) @@ -842,7 +935,7 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) window->cursor = cursor; - _glfwPlatformSetCursor(window, cursor); + _glfw.platform.setCursor(window, cursor); } GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) @@ -851,7 +944,7 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.key, cbfun); + _GLFW_SWAP(GLFWkeyfun, window->callbacks.key, cbfun); return cbfun; } @@ -861,7 +954,7 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.character, cbfun); + _GLFW_SWAP(GLFWcharfun, window->callbacks.character, cbfun); return cbfun; } @@ -871,7 +964,7 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmods assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun); + _GLFW_SWAP(GLFWcharmodsfun, window->callbacks.charmods, cbfun); return cbfun; } @@ -882,7 +975,7 @@ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun); + _GLFW_SWAP(GLFWmousebuttonfun, window->callbacks.mouseButton, cbfun); return cbfun; } @@ -893,7 +986,7 @@ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun); + _GLFW_SWAP(GLFWcursorposfun, window->callbacks.cursorPos, cbfun); return cbfun; } @@ -904,7 +997,7 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun); + _GLFW_SWAP(GLFWcursorenterfun, window->callbacks.cursorEnter, cbfun); return cbfun; } @@ -915,7 +1008,7 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun); + _GLFW_SWAP(GLFWscrollfun, window->callbacks.scroll, cbfun); return cbfun; } @@ -925,7 +1018,7 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun); + _GLFW_SWAP(GLFWdropfun, window->callbacks.drop, cbfun); return cbfun; } @@ -948,10 +1041,10 @@ GLFWAPI int glfwJoystickPresent(int jid) return GLFW_FALSE; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return GLFW_FALSE; - return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); + return _glfw.platform.pollJoystick(js, _GLFW_POLL_PRESENCE); } GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) @@ -976,10 +1069,10 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) return NULL; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return NULL; - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES)) + if (!_glfw.platform.pollJoystick(js, _GLFW_POLL_AXES)) return NULL; *count = js->axisCount; @@ -1008,10 +1101,10 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) return NULL; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return NULL; - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) + if (!_glfw.platform.pollJoystick(js, _GLFW_POLL_BUTTONS)) return NULL; if (_glfw.hints.init.hatButtons) @@ -1044,10 +1137,10 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) return NULL; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return NULL; - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) + if (!_glfw.platform.pollJoystick(js, _GLFW_POLL_BUTTONS)) return NULL; *count = js->hatCount; @@ -1073,10 +1166,10 @@ GLFWAPI const char* glfwGetJoystickName(int jid) return NULL; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return NULL; - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + if (!_glfw.platform.pollJoystick(js, _GLFW_POLL_PRESENCE)) return NULL; return js->name; @@ -1101,10 +1194,10 @@ GLFWAPI const char* glfwGetJoystickGUID(int jid) return NULL; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return NULL; - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + if (!_glfw.platform.pollJoystick(js, _GLFW_POLL_PRESENCE)) return NULL; return js->guid; @@ -1120,7 +1213,7 @@ GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer) _GLFW_REQUIRE_INIT(); js = _glfw.joysticks + jid; - if (!js->present) + if (!js->allocated) return; js->userPointer = pointer; @@ -1136,7 +1229,7 @@ GLFWAPI void* glfwGetJoystickUserPointer(int jid) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); js = _glfw.joysticks + jid; - if (!js->present) + if (!js->allocated) return NULL; return js->userPointer; @@ -1149,7 +1242,7 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) if (!initJoysticks()) return NULL; - _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun); + _GLFW_SWAP(GLFWjoystickfun, _glfw.callbacks.joystick, cbfun); return cbfun; } @@ -1187,8 +1280,8 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string) { _glfw.mappingCount++; _glfw.mappings = - realloc(_glfw.mappings, - sizeof(_GLFWmapping) * _glfw.mappingCount); + _glfw_realloc(_glfw.mappings, + sizeof(_GLFWmapping) * _glfw.mappingCount); _glfw.mappings[_glfw.mappingCount - 1] = mapping; } } @@ -1206,7 +1299,7 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { _GLFWjoystick* js = _glfw.joysticks + jid; - if (js->present) + if (js->connected) js->mapping = findValidMapping(js); } @@ -1232,10 +1325,10 @@ GLFWAPI int glfwJoystickIsGamepad(int jid) return GLFW_FALSE; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return GLFW_FALSE; - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + if (!_glfw.platform.pollJoystick(js, _GLFW_POLL_PRESENCE)) return GLFW_FALSE; return js->mapping != NULL; @@ -1260,10 +1353,10 @@ GLFWAPI const char* glfwGetGamepadName(int jid) return NULL; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return NULL; - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + if (!_glfw.platform.pollJoystick(js, _GLFW_POLL_PRESENCE)) return NULL; if (!js->mapping) @@ -1295,10 +1388,10 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) return GLFW_FALSE; js = _glfw.joysticks + jid; - if (!js->present) + if (!js->connected) return GLFW_FALSE; - if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL)) + if (!_glfw.platform.pollJoystick(js, _GLFW_POLL_ALL)) return GLFW_FALSE; if (!js->mapping) @@ -1363,13 +1456,13 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) assert(string != NULL); _GLFW_REQUIRE_INIT(); - _glfwPlatformSetClipboardString(string); + _glfw.platform.setClipboardString(string); } GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfwPlatformGetClipboardString(); + return _glfw.platform.getClipboardString(); } GLFWAPI double glfwGetTime(void) @@ -1404,3 +1497,4 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void) _GLFW_REQUIRE_INIT_OR_RETURN(0); return _glfwPlatformGetTimerFrequency(); } + diff --git a/src/external/glfw/src/internal.h b/src/external/glfw/src/internal.h index ce9783f9e..5aa22f595 100644 --- a/src/external/glfw/src/internal.h +++ b/src/external/glfw/src/internal.h @@ -59,6 +59,7 @@ #define _GLFW_MESSAGE_SIZE 1024 typedef int GLFWbool; +typedef void (*GLFWproc)(void); typedef struct _GLFWerror _GLFWerror; typedef struct _GLFWinitconfig _GLFWinitconfig; @@ -67,6 +68,7 @@ typedef struct _GLFWctxconfig _GLFWctxconfig; typedef struct _GLFWfbconfig _GLFWfbconfig; typedef struct _GLFWcontext _GLFWcontext; typedef struct _GLFWwindow _GLFWwindow; +typedef struct _GLFWplatform _GLFWplatform; typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWcursor _GLFWcursor; @@ -76,13 +78,6 @@ typedef struct _GLFWjoystick _GLFWjoystick; typedef struct _GLFWtls _GLFWtls; typedef struct _GLFWmutex _GLFWmutex; -typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); -typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); -typedef void (* _GLFWswapintervalfun)(int); -typedef int (* _GLFWextensionsupportedfun)(const char*); -typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); -typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); - #define GL_VERSION 0x1f02 #define GL_NONE 0 #define GL_COLOR_BUFFER_BIT 0x00004000 @@ -113,6 +108,165 @@ typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum); typedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*); typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint); +#if defined(_GLFW_WIN32) + #define EGLAPIENTRY __stdcall +#else + #define EGLAPIENTRY +#endif + +#define EGL_SUCCESS 0x3000 +#define EGL_NOT_INITIALIZED 0x3001 +#define EGL_BAD_ACCESS 0x3002 +#define EGL_BAD_ALLOC 0x3003 +#define EGL_BAD_ATTRIBUTE 0x3004 +#define EGL_BAD_CONFIG 0x3005 +#define EGL_BAD_CONTEXT 0x3006 +#define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DISPLAY 0x3008 +#define EGL_BAD_MATCH 0x3009 +#define EGL_BAD_NATIVE_PIXMAP 0x300a +#define EGL_BAD_NATIVE_WINDOW 0x300b +#define EGL_BAD_PARAMETER 0x300c +#define EGL_BAD_SURFACE 0x300d +#define EGL_CONTEXT_LOST 0x300e +#define EGL_COLOR_BUFFER_TYPE 0x303f +#define EGL_RGB_BUFFER 0x308e +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_WINDOW_BIT 0x0004 +#define EGL_RENDERABLE_TYPE 0x3040 +#define EGL_OPENGL_ES_BIT 0x0001 +#define EGL_OPENGL_ES2_BIT 0x0004 +#define EGL_OPENGL_BIT 0x0008 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_RED_SIZE 0x3024 +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_STENCIL_SIZE 0x3026 +#define EGL_SAMPLES 0x3031 +#define EGL_OPENGL_ES_API 0x30a0 +#define EGL_OPENGL_API 0x30a2 +#define EGL_NONE 0x3038 +#define EGL_RENDER_BUFFER 0x3086 +#define EGL_SINGLE_BUFFER 0x3085 +#define EGL_EXTENSIONS 0x3055 +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 +#define EGL_NATIVE_VISUAL_ID 0x302e +#define EGL_NO_SURFACE ((EGLSurface) 0) +#define EGL_NO_DISPLAY ((EGLDisplay) 0) +#define EGL_NO_CONTEXT ((EGLContext) 0) +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) + +#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd +#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be +#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf +#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 +#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 +#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb +#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd +#define EGL_CONTEXT_FLAGS_KHR 0x30fc +#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 +#define EGL_GL_COLORSPACE_KHR 0x309d +#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 +#define EGL_PLATFORM_X11_EXT 0x31d5 +#define EGL_PLATFORM_WAYLAND_EXT 0x31d8 +#define EGL_PRESENT_OPAQUE_EXT 0x31df +#define EGL_PLATFORM_ANGLE_ANGLE 0x3202 +#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203 +#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320d +#define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320e +#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207 +#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208 +#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450 +#define EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE 0x3489 +#define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348f + +typedef int EGLint; +typedef unsigned int EGLBoolean; +typedef unsigned int EGLenum; +typedef void* EGLConfig; +typedef void* EGLContext; +typedef void* EGLDisplay; +typedef void* EGLSurface; + +typedef void* EGLNativeDisplayType; +typedef void* EGLNativeWindowType; + +// EGL function pointer typedefs +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); +typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); +typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); +typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); +typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); +typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); +typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); +#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib +#define eglGetConfigs _glfw.egl.GetConfigs +#define eglGetDisplay _glfw.egl.GetDisplay +#define eglGetError _glfw.egl.GetError +#define eglInitialize _glfw.egl.Initialize +#define eglTerminate _glfw.egl.Terminate +#define eglBindAPI _glfw.egl.BindAPI +#define eglCreateContext _glfw.egl.CreateContext +#define eglDestroySurface _glfw.egl.DestroySurface +#define eglDestroyContext _glfw.egl.DestroyContext +#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface +#define eglMakeCurrent _glfw.egl.MakeCurrent +#define eglSwapBuffers _glfw.egl.SwapBuffers +#define eglSwapInterval _glfw.egl.SwapInterval +#define eglQueryString _glfw.egl.QueryString +#define eglGetProcAddress _glfw.egl.GetProcAddress + +typedef EGLDisplay (EGLAPIENTRY * PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum,void*,const EGLint*); +typedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay,EGLConfig,void*,const EGLint*); +#define eglGetPlatformDisplayEXT _glfw.egl.GetPlatformDisplayEXT +#define eglCreatePlatformWindowSurfaceEXT _glfw.egl.CreatePlatformWindowSurfaceEXT + +#define OSMESA_RGBA 0x1908 +#define OSMESA_FORMAT 0x22 +#define OSMESA_DEPTH_BITS 0x30 +#define OSMESA_STENCIL_BITS 0x31 +#define OSMESA_ACCUM_BITS 0x32 +#define OSMESA_PROFILE 0x33 +#define OSMESA_CORE_PROFILE 0x34 +#define OSMESA_COMPAT_PROFILE 0x35 +#define OSMESA_CONTEXT_MAJOR_VERSION 0x36 +#define OSMESA_CONTEXT_MINOR_VERSION 0x37 + +typedef void* OSMesaContext; +typedef void (*OSMESAproc)(void); + +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); +typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); +typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); +typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); +#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt +#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs +#define OSMesaDestroyContext _glfw.osmesa.DestroyContext +#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent +#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer +#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer +#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress + #define VK_NULL_HANDLE 0 typedef void* VkInstance; @@ -170,32 +324,11 @@ typedef struct VkExtensionProperties typedef void (APIENTRY * PFN_vkVoidFunction)(void); -#if defined(_GLFW_VULKAN_STATIC) - PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance,const char*); - VkResult vkEnumerateInstanceExtensionProperties(const char*,uint32_t*,VkExtensionProperties*); -#else - typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*); - typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); - #define vkEnumerateInstanceExtensionProperties _glfw.vk.EnumerateInstanceExtensionProperties - #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr -#endif +typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*); +typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); +#define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr -#if defined(_GLFW_COCOA) - #include "cocoa_platform.h" -#elif defined(_GLFW_WIN32) - #include "win32_platform.h" -#elif defined(_GLFW_X11) - #include "x11_platform.h" -#elif defined(_GLFW_WAYLAND) - #include "wl_platform.h" -#elif defined(_GLFW_OSMESA) - #include "null_platform.h" -#else - #error "No supported window creation API selected" -#endif - -#include "egl_context.h" -#include "osmesa_context.h" +#include "platform.h" // Constructs a version number string from the public header macros #define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r @@ -219,12 +352,12 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); } // Swaps the provided pointers -#define _GLFW_SWAP_POINTERS(x, y) \ - { \ - void* t; \ - t = x; \ - x = y; \ - y = t; \ +#define _GLFW_SWAP(type, x, y) \ + { \ + type t; \ + t = x; \ + x = y; \ + y = t; \ } // Per-thread error structure @@ -244,6 +377,8 @@ struct _GLFWinitconfig { GLFWbool hatButtons; int angleType; + int platformID; + PFN_vkGetInstanceProcAddr vulkanLoader; struct { GLFWbool menubar; GLFWbool chdir; @@ -261,6 +396,8 @@ struct _GLFWinitconfig // struct _GLFWwndconfig { + int xpos; + int ypos; int width; int height; const char* title; @@ -286,6 +423,9 @@ struct _GLFWwndconfig struct { GLFWbool keymenu; } win32; + struct { + char appId[256]; + } wl; }; // Context configuration @@ -357,19 +497,29 @@ struct _GLFWcontext PFNGLGETINTEGERVPROC GetIntegerv; PFNGLGETSTRINGPROC GetString; - _GLFWmakecontextcurrentfun makeCurrent; - _GLFWswapbuffersfun swapBuffers; - _GLFWswapintervalfun swapInterval; - _GLFWextensionsupportedfun extensionSupported; - _GLFWgetprocaddressfun getProcAddress; - _GLFWdestroycontextfun destroy; + void (*makeCurrent)(_GLFWwindow*); + void (*swapBuffers)(_GLFWwindow*); + void (*swapInterval)(int); + int (*extensionSupported)(const char*); + GLFWglproc (*getProcAddress)(const char*); + void (*destroy)(_GLFWwindow*); - // This is defined in the context API's context.h - _GLFW_PLATFORM_CONTEXT_STATE; - // This is defined in egl_context.h - _GLFWcontextEGL egl; - // This is defined in osmesa_context.h - _GLFWcontextOSMesa osmesa; + struct { + EGLConfig config; + EGLContext handle; + EGLSurface surface; + void* client; + } egl; + + struct { + OSMesaContext handle; + int width; + int height; + void* buffer; + } osmesa; + + // This is defined in platform.h + GLFW_PLATFORM_CONTEXT_STATE }; // Window and context structure @@ -428,8 +578,8 @@ struct _GLFWwindow GLFWdropfun drop; } callbacks; - // This is defined in the window API's platform.h - _GLFW_PLATFORM_WINDOW_STATE; + // This is defined in platform.h + GLFW_PLATFORM_WINDOW_STATE }; // Monitor structure @@ -452,8 +602,8 @@ struct _GLFWmonitor GLFWgammaramp originalRamp; GLFWgammaramp currentRamp; - // This is defined in the window API's platform.h - _GLFW_PLATFORM_MONITOR_STATE; + // This is defined in platform.h + GLFW_PLATFORM_MONITOR_STATE }; // Cursor structure @@ -461,9 +611,8 @@ struct _GLFWmonitor struct _GLFWcursor { _GLFWcursor* next; - - // This is defined in the window API's platform.h - _GLFW_PLATFORM_CURSOR_STATE; + // This is defined in platform.h + GLFW_PLATFORM_CURSOR_STATE }; // Gamepad mapping element structure @@ -490,7 +639,8 @@ struct _GLFWmapping // struct _GLFWjoystick { - GLFWbool present; + GLFWbool allocated; + GLFWbool connected; float* axes; int axisCount; unsigned char* buttons; @@ -502,24 +652,108 @@ struct _GLFWjoystick char guid[33]; _GLFWmapping* mapping; - // This is defined in the joystick API's joystick.h - _GLFW_PLATFORM_JOYSTICK_STATE; + // This is defined in platform.h + GLFW_PLATFORM_JOYSTICK_STATE }; // Thread local storage structure // struct _GLFWtls { - // This is defined in the platform's thread.h - _GLFW_PLATFORM_TLS_STATE; + // This is defined in platform.h + GLFW_PLATFORM_TLS_STATE }; // Mutex structure // struct _GLFWmutex { - // This is defined in the platform's thread.h - _GLFW_PLATFORM_MUTEX_STATE; + // This is defined in platform.h + GLFW_PLATFORM_MUTEX_STATE +}; + +// Platform API structure +// +struct _GLFWplatform +{ + int platformID; + // init + GLFWbool (*init)(void); + void (*terminate)(void); + // input + void (*getCursorPos)(_GLFWwindow*,double*,double*); + void (*setCursorPos)(_GLFWwindow*,double,double); + void (*setCursorMode)(_GLFWwindow*,int); + void (*setRawMouseMotion)(_GLFWwindow*,GLFWbool); + GLFWbool (*rawMouseMotionSupported)(void); + GLFWbool (*createCursor)(_GLFWcursor*,const GLFWimage*,int,int); + GLFWbool (*createStandardCursor)(_GLFWcursor*,int); + void (*destroyCursor)(_GLFWcursor*); + void (*setCursor)(_GLFWwindow*,_GLFWcursor*); + const char* (*getScancodeName)(int); + int (*getKeyScancode)(int); + void (*setClipboardString)(const char*); + const char* (*getClipboardString)(void); + GLFWbool (*initJoysticks)(void); + void (*terminateJoysticks)(void); + GLFWbool (*pollJoystick)(_GLFWjoystick*,int); + const char* (*getMappingName)(void); + void (*updateGamepadGUID)(char*); + // monitor + void (*freeMonitor)(_GLFWmonitor*); + void (*getMonitorPos)(_GLFWmonitor*,int*,int*); + void (*getMonitorContentScale)(_GLFWmonitor*,float*,float*); + void (*getMonitorWorkarea)(_GLFWmonitor*,int*,int*,int*,int*); + GLFWvidmode* (*getVideoModes)(_GLFWmonitor*,int*); + void (*getVideoMode)(_GLFWmonitor*,GLFWvidmode*); + GLFWbool (*getGammaRamp)(_GLFWmonitor*,GLFWgammaramp*); + void (*setGammaRamp)(_GLFWmonitor*,const GLFWgammaramp*); + // window + GLFWbool (*createWindow)(_GLFWwindow*,const _GLFWwndconfig*,const _GLFWctxconfig*,const _GLFWfbconfig*); + void (*destroyWindow)(_GLFWwindow*); + void (*setWindowTitle)(_GLFWwindow*,const char*); + void (*setWindowIcon)(_GLFWwindow*,int,const GLFWimage*); + void (*getWindowPos)(_GLFWwindow*,int*,int*); + void (*setWindowPos)(_GLFWwindow*,int,int); + void (*getWindowSize)(_GLFWwindow*,int*,int*); + void (*setWindowSize)(_GLFWwindow*,int,int); + void (*setWindowSizeLimits)(_GLFWwindow*,int,int,int,int); + void (*setWindowAspectRatio)(_GLFWwindow*,int,int); + void (*getFramebufferSize)(_GLFWwindow*,int*,int*); + void (*getWindowFrameSize)(_GLFWwindow*,int*,int*,int*,int*); + void (*getWindowContentScale)(_GLFWwindow*,float*,float*); + void (*iconifyWindow)(_GLFWwindow*); + void (*restoreWindow)(_GLFWwindow*); + void (*maximizeWindow)(_GLFWwindow*); + void (*showWindow)(_GLFWwindow*); + void (*hideWindow)(_GLFWwindow*); + void (*requestWindowAttention)(_GLFWwindow*); + void (*focusWindow)(_GLFWwindow*); + void (*setWindowMonitor)(_GLFWwindow*,_GLFWmonitor*,int,int,int,int,int); + GLFWbool (*windowFocused)(_GLFWwindow*); + GLFWbool (*windowIconified)(_GLFWwindow*); + GLFWbool (*windowVisible)(_GLFWwindow*); + GLFWbool (*windowMaximized)(_GLFWwindow*); + GLFWbool (*windowHovered)(_GLFWwindow*); + GLFWbool (*framebufferTransparent)(_GLFWwindow*); + float (*getWindowOpacity)(_GLFWwindow*); + void (*setWindowResizable)(_GLFWwindow*,GLFWbool); + void (*setWindowDecorated)(_GLFWwindow*,GLFWbool); + void (*setWindowFloating)(_GLFWwindow*,GLFWbool); + void (*setWindowOpacity)(_GLFWwindow*,float); + void (*setWindowMousePassthrough)(_GLFWwindow*,GLFWbool); + void (*pollEvents)(void); + void (*waitEvents)(void); + void (*waitEventsTimeout)(double); + void (*postEmptyEvent)(void); + // EGL + EGLenum (*getEGLPlatform)(EGLint**); + EGLNativeDisplayType (*getEGLNativeDisplay)(void); + EGLNativeWindowType (*getEGLNativeWindow)(_GLFWwindow*); + // vulkan + void (*getRequiredInstanceExtensions)(char**); + GLFWbool (*getPhysicalDevicePresentationSupport)(VkInstance,VkPhysicalDevice,uint32_t); + VkResult (*createWindowSurface)(VkInstance,_GLFWwindow*,const VkAllocationCallbacks*,VkSurfaceKHR*); }; // Library global data @@ -527,6 +761,9 @@ struct _GLFWmutex struct _GLFWlibrary { GLFWbool initialized; + GLFWallocator allocator; + + _GLFWplatform platform; struct { _GLFWinitconfig init; @@ -554,30 +791,80 @@ struct _GLFWlibrary struct { uint64_t offset; - // This is defined in the platform's time.h - _GLFW_PLATFORM_LIBRARY_TIMER_STATE; + // This is defined in platform.h + GLFW_PLATFORM_LIBRARY_TIMER_STATE } timer; + struct { + EGLenum platform; + EGLDisplay display; + EGLint major, minor; + GLFWbool prefix; + + GLFWbool KHR_create_context; + GLFWbool KHR_create_context_no_error; + GLFWbool KHR_gl_colorspace; + GLFWbool KHR_get_all_proc_addresses; + GLFWbool KHR_context_flush_control; + GLFWbool EXT_client_extensions; + GLFWbool EXT_platform_base; + GLFWbool EXT_platform_x11; + GLFWbool EXT_platform_wayland; + GLFWbool EXT_present_opaque; + GLFWbool ANGLE_platform_angle; + GLFWbool ANGLE_platform_angle_opengl; + GLFWbool ANGLE_platform_angle_d3d; + GLFWbool ANGLE_platform_angle_vulkan; + GLFWbool ANGLE_platform_angle_metal; + + void* handle; + + PFN_eglGetConfigAttrib GetConfigAttrib; + PFN_eglGetConfigs GetConfigs; + PFN_eglGetDisplay GetDisplay; + PFN_eglGetError GetError; + PFN_eglInitialize Initialize; + PFN_eglTerminate Terminate; + PFN_eglBindAPI BindAPI; + PFN_eglCreateContext CreateContext; + PFN_eglDestroySurface DestroySurface; + PFN_eglDestroyContext DestroyContext; + PFN_eglCreateWindowSurface CreateWindowSurface; + PFN_eglMakeCurrent MakeCurrent; + PFN_eglSwapBuffers SwapBuffers; + PFN_eglSwapInterval SwapInterval; + PFN_eglQueryString QueryString; + PFN_eglGetProcAddress GetProcAddress; + + PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT; + PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC CreatePlatformWindowSurfaceEXT; + } egl; + + struct { + void* handle; + + PFN_OSMesaCreateContextExt CreateContextExt; + PFN_OSMesaCreateContextAttribs CreateContextAttribs; + PFN_OSMesaDestroyContext DestroyContext; + PFN_OSMesaMakeCurrent MakeCurrent; + PFN_OSMesaGetColorBuffer GetColorBuffer; + PFN_OSMesaGetDepthBuffer GetDepthBuffer; + PFN_OSMesaGetProcAddress GetProcAddress; + + } osmesa; + struct { GLFWbool available; void* handle; char* extensions[2]; -#if !defined(_GLFW_VULKAN_STATIC) - PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; PFN_vkGetInstanceProcAddr GetInstanceProcAddr; -#endif GLFWbool KHR_surface; -#if defined(_GLFW_WIN32) GLFWbool KHR_win32_surface; -#elif defined(_GLFW_COCOA) GLFWbool MVK_macos_surface; GLFWbool EXT_metal_surface; -#elif defined(_GLFW_X11) GLFWbool KHR_xlib_surface; GLFWbool KHR_xcb_surface; -#elif defined(_GLFW_WAYLAND) GLFWbool KHR_wayland_surface; -#endif } vk; struct { @@ -585,16 +872,10 @@ struct _GLFWlibrary GLFWjoystickfun joystick; } callbacks; - // This is defined in the window API's platform.h - _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; - // This is defined in the context API's context.h - _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; - // This is defined in the platform's joystick.h - _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; - // This is defined in egl_context.h - _GLFWlibraryEGL egl; - // This is defined in osmesa_context.h - _GLFWlibraryOSMesa osmesa; + // These are defined in platform.h + GLFW_PLATFORM_LIBRARY_WINDOW_STATE + GLFW_PLATFORM_LIBRARY_CONTEXT_STATE + GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE }; // Global state shared between compilation units of GLFW @@ -606,108 +887,10 @@ extern _GLFWlibrary _glfw; ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformInit(void); -void _glfwPlatformTerminate(void); -const char* _glfwPlatformGetVersionString(void); - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled); -GLFWbool _glfwPlatformRawMouseMotionSupported(void); -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, int xhot, int yhot); -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); - -const char* _glfwPlatformGetScancodeName(int scancode); -int _glfwPlatformGetKeyScancode(int key); - -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor); -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale); -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height); -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); - -void _glfwPlatformSetClipboardString(const char* string); -const char* _glfwPlatformGetClipboardString(void); - -GLFWbool _glfwPlatformInitJoysticks(void); -void _glfwPlatformTerminateJoysticks(void); -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); -void _glfwPlatformUpdateGamepadGUID(char* guid); - +void _glfwPlatformInitTimer(void); uint64_t _glfwPlatformGetTimerValue(void); uint64_t _glfwPlatformGetTimerFrequency(void); -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); -void _glfwPlatformDestroyWindow(_GLFWwindow* window); -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images); -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos); -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos); -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height); -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight); -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom); -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale); -void _glfwPlatformIconifyWindow(_GLFWwindow* window); -void _glfwPlatformRestoreWindow(_GLFWwindow* window); -void _glfwPlatformMaximizeWindow(_GLFWwindow* window); -void _glfwPlatformShowWindow(_GLFWwindow* window); -void _glfwPlatformHideWindow(_GLFWwindow* window); -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window); -void _glfwPlatformFocusWindow(_GLFWwindow* window); -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, - int xpos, int ypos, int width, int height, - int refreshRate); -int _glfwPlatformWindowFocused(_GLFWwindow* window); -int _glfwPlatformWindowIconified(_GLFWwindow* window); -int _glfwPlatformWindowVisible(_GLFWwindow* window); -int _glfwPlatformWindowMaximized(_GLFWwindow* window); -int _glfwPlatformWindowHovered(_GLFWwindow* window); -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); -void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled); -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); - -void _glfwPlatformPollEvents(void); -void _glfwPlatformWaitEvents(void); -void _glfwPlatformWaitEventsTimeout(double timeout); -void _glfwPlatformPostEmptyEvent(void); - -EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs); -EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void); -EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window); - -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily); -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface); - GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls); void _glfwPlatformDestroyTls(_GLFWtls* tls); void* _glfwPlatformGetTls(_GLFWtls* tls); @@ -718,6 +901,10 @@ void _glfwPlatformDestroyMutex(_GLFWmutex* mutex); void _glfwPlatformLockMutex(_GLFWmutex* mutex); void _glfwPlatformUnlockMutex(_GLFWmutex* mutex); +void* _glfwPlatformLoadModule(const char* path); +void _glfwPlatformFreeModule(void* module); +GLFWproc _glfwPlatformGetModuleSymbol(void* module, const char* name); + ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// @@ -738,7 +925,7 @@ void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor); void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods); void _glfwInputChar(_GLFWwindow* window, - unsigned int codepoint, int mods, GLFWbool plain); + uint32_t codepoint, int mods, GLFWbool plain); void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); @@ -764,6 +951,8 @@ void _glfwInputError(int code, const char* format, ...); ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +GLFWbool _glfwSelectPlatform(int platformID, _GLFWplatform* platform); + GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions); const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, const _GLFWfbconfig* alternatives, @@ -781,6 +970,7 @@ void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); void _glfwFreeGammaArrays(GLFWgammaramp* ramp); void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); +void _glfwInitGamepadMappings(void); _GLFWjoystick* _glfwAllocJoystick(const char* name, const char* guid, int axisCount, @@ -789,11 +979,38 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, void _glfwFreeJoystick(_GLFWjoystick* js); void _glfwCenterCursorInContentArea(_GLFWwindow* window); +GLFWbool _glfwInitEGL(void); +void _glfwTerminateEGL(void); +GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +#if defined(_GLFW_X11) +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth); +#endif /*_GLFW_X11*/ + +GLFWbool _glfwInitOSMesa(void); +void _glfwTerminateOSMesa(void); +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + GLFWbool _glfwInitVulkan(int mode); void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); +size_t _glfwEncodeUTF8(char* s, uint32_t codepoint); +char** _glfwParseUriList(char* text, int* count); + char* _glfw_strdup(const char* source); +int _glfw_min(int a, int b); +int _glfw_max(int a, int b); float _glfw_fminf(float a, float b); float _glfw_fmaxf(float a, float b); +void* _glfw_calloc(size_t count, size_t size); +void* _glfw_realloc(void* pointer, size_t size); +void _glfw_free(void* pointer); + diff --git a/src/external/glfw/src/linux_joystick.c b/src/external/glfw/src/linux_joystick.c index 122bc66a9..366bda2df 100644 --- a/src/external/glfw/src/linux_joystick.c +++ b/src/external/glfw/src/linux_joystick.c @@ -128,7 +128,7 @@ static GLFWbool openJoystickDevice(const char* path) { for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.joysticks[jid].present) + if (!_glfw.joysticks[jid].connected) continue; if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) return GLFW_FALSE; @@ -157,7 +157,7 @@ static GLFWbool openJoystickDevice(const char* path) } // Ensure this device supports the events expected of a joystick - if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits)) + if (!isBitSet(EV_ABS, evBits)) { close(linjs.fd); return GLFW_FALSE; @@ -245,9 +245,9 @@ static GLFWbool openJoystickDevice(const char* path) // static void closeJoystick(_GLFWjoystick* js) { + _glfwInputJoystick(js, GLFW_DISCONNECTED); close(js->linjs.fd); _glfwFreeJoystick(js); - _glfwInputJoystick(js, GLFW_DISCONNECTED); } // Lexically compare joysticks by name; used by qsort @@ -307,7 +307,7 @@ void _glfwDetectJoystickConnectionLinux(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwPlatformInitJoysticks(void) +GLFWbool _glfwInitJoysticksLinux(void) { const char* dirname = "/dev/input"; @@ -361,14 +361,12 @@ GLFWbool _glfwPlatformInitJoysticks(void) return GLFW_TRUE; } -void _glfwPlatformTerminateJoysticks(void) +void _glfwTerminateJoysticksLinux(void) { - int jid; - - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { _GLFWjoystick* js = _glfw.joysticks + jid; - if (js->present) + if (js->connected) closeJoystick(js); } @@ -382,7 +380,7 @@ void _glfwPlatformTerminateJoysticks(void) } } -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +GLFWbool _glfwPollJoystickLinux(_GLFWjoystick* js, int mode) { // Read all queued events (non-blocking) for (;;) @@ -419,10 +417,15 @@ int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) handleAbsEvent(js, e.code, e.value); } - return js->present; + return js->connected; } -void _glfwPlatformUpdateGamepadGUID(char* guid) +const char* _glfwGetMappingNameLinux(void) +{ + return "Linux"; +} + +void _glfwUpdateGamepadGUIDLinux(char* guid) { } diff --git a/src/external/glfw/src/linux_joystick.h b/src/external/glfw/src/linux_joystick.h index 05d5488f0..f898b2b9f 100644 --- a/src/external/glfw/src/linux_joystick.h +++ b/src/external/glfw/src/linux_joystick.h @@ -28,10 +28,10 @@ #include #include -#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs +#define GLFW_LINUX_JOYSTICK_STATE _GLFWjoystickLinux linjs; +#define GLFW_LINUX_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs; -#define _GLFW_PLATFORM_MAPPING_NAME "Linux" +#define GLFW_BUILD_LINUX_MAPPINGS // Linux-specific joystick data // @@ -57,3 +57,9 @@ typedef struct _GLFWlibraryLinux void _glfwDetectJoystickConnectionLinux(void); +GLFWbool _glfwInitJoysticksLinux(void); +void _glfwTerminateJoysticksLinux(void); +GLFWbool _glfwPollJoystickLinux(_GLFWjoystick* js, int mode); +const char* _glfwGetMappingNameLinux(void); +void _glfwUpdateGamepadGUIDLinux(char* guid); + diff --git a/src/external/glfw/src/mappings.h b/src/external/glfw/src/mappings.h index df7679fcd..553fe2a28 100644 --- a/src/external/glfw/src/mappings.h +++ b/src/external/glfw/src/mappings.h @@ -31,7 +31,7 @@ // all available in SDL_GameControllerDB. Do not edit this file. Any gamepad // mappings not specific to GLFW should be submitted to SDL_GameControllerDB. // This file can be re-generated from mappings.h.in and the upstream -// gamecontrollerdb.txt with the GenerateMappings.cmake script. +// gamecontrollerdb.txt with the 'update_mappings' CMake target. //======================================================================== // All gamepad mappings not labeled GLFW are copied from the @@ -60,12 +60,9 @@ const char* _glfwDefaultMappings[] = { +#if defined(GLFW_BUILD_WIN32_MAPPINGS) "03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,", -"03000000d0160000600a000000000000,4Play,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,", -"03000000d0160000040d000000000000,4Play,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,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,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,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,", +"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,", @@ -76,16 +73,12 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", "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,", -"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,", -"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,", "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,", -"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:a1,rightshoulder:b7,righttrigger:b9,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,", "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,", @@ -102,7 +95,6 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", "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,", @@ -110,20 +102,12 @@ const char* _glfwDefaultMappings[] = "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,", -"03000000d81d00000e00000000000000,AC02,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,", +"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,", -"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,", "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,", -"030000006d0400000bc2000000000000,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,", -"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,", "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,", -"030000006f0e00001301000000000000,Afterglow,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,", -"030000006f0e00003901000000000000,Afterglow,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,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,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,", -"030000006f0e00001304000000000000,Afterglow,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,", -"03000000ad1b000000f9000000000000,Afterglow,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,", "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,", @@ -131,24 +115,14 @@ const char* _glfwDefaultMappings[] = "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,", -"03000000120c00000288000000000000,AirFlo,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,", "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,", -"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,", -"03000000869800002500000000000000,Astro C40 TR 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,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,", "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,", -"03000000fd0500000230000000000000,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,", "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,", -"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,", "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,x:b0,y:b3,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 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,", @@ -158,120 +132,51 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", -"03000000380700008232000000000000,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,", -"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,x:b0,y:b3,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,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,", +"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,", -"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,Comp Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start: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,", -"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,", -"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,", -"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,", -"03000000380700006352000000000000,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,", "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 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,", -"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,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,", "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,", -"03000000c0160000e105000000000000,Dual,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,", "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,", -"030000007c1800000006000000000000,Dual Compat,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,", -"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 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,", -"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,", "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,", -"03000000317300000100000000000000,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,", "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,", -"030000006e0500000a20000000000000,Elecom DUX60 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,", "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,", -"030000006e0500000520000000000000,Elecom 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,", -"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 (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,", -"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,", "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,", -"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 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,", -"03000000801000000900000000000000,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,", -"03000000008000000210000000000000,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,", -"03000000c82d00001028000000000000,F30,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", -"030000003512000011ab000000000000,F30,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,", -"03000000790000003018000000000000,F300,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,F300 Elite,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,", -"030000006d0400001dc2000000000000,F310,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,", -"030000006d0400001ec2000000000000,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,", -"030000006d0400001fc2000000000000,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,", "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,", -"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,", -"030000004f04000008d0000000000000,Ferrari 150,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,", "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,", -"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,", -"03000000380700002847000000000000,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,", -"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,", -"03000000380700008031000000000000,FightStick,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,", -"03000000380700008731000000000000,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,", -"03000000380700001847000000000000,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,", -"030000003807000038b7000000000000,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,", "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,", -"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:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,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,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows,", -"030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows,", -"03000000ac0500002d02000000000000,G2U,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,", "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,", -"03000000430b00000500000000000000,GameCube,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,", -"03000000341a000005f7000000000000,GameCube,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,", -"03000000790000004718000000000000,GameCube,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,", "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: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: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,", -"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 Bluetooth 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,", "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,", -"03000000b62500000100000000000000,Gametel GT-004-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 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: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,", +"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,", @@ -281,98 +186,50 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", "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,", -"030000007d0400000340000000000000,Gravis G44011 Xterminator,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,rightx:a2,start:b9,x:b3,y:b4,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,", -"03000000fd0500003902000000000000,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,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,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,", "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,", -"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,", -"030000000d0f00000d00000000000000,HFS 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,HFS 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,", -"030000000d0f00002100000000000000,HFS 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,", -"030000000d0f00001000000000000000,HFS3,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,", -"030000000f0d00000010000000000000,HFS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,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,", -"030000000d0f00008600000000000000,Hori Fighting Commander,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,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,", "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,", -"030000000d0f00005100000000000000,Hori Fighting 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:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"030000000d0f00003200000000000000,Hori Fighting Stick 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 Fighting Stick 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,", "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,", -"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,", -"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 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,", -"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,", -"030000000d0f00002301000000000000,Hori Wired 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,", "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,", -"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,", "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,", -"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,", -"030000005b1c00002400000000000000,Horipad Mini,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,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,", -"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,", -"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,", -"03000000ad1b000001f5000000000000,HoripadEXT2,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,", -"030000000d0f00002600000000000000,HRAP 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,HRAP 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,", -"030000000d0f00003d00000000000000,HRAP 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,HRAP 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,", -"030000000d0f0000d800000000000000,HRAP 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,", -"030000000d0f0000aa00000000000000,HRAP 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,", -"030000000d0f0000af00000000000000,HRAP 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,HRAP 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,HRAP VX,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b07,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b08,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,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,", -"030000000d0f00008c00000000000000,HRAP4,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,", -"030000000d0f00006f00000000000000,HRAP4 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,", "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,", -"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 Bluetooth 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,", +"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,", -"03000000830500005130000000000000,InterAct ActionPad,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: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,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,", -"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,", +"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,", -"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,", -"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,", -"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,", "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,", @@ -380,10 +237,7 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", "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,", "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,", @@ -410,69 +264,37 @@ const char* _glfwDefaultMappings[] = "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,", -"0300000079000000ae18000000000000,Mega Drive,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,a:b0,b:b1,leftx:a0,lefty:a1,righttrigger:b2,start:b3,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,", -"030000005e0400002700000000000000,Microsoft SideWinder,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,x:b2,y:b3,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,", -"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,", -"030000005b1c00002500000000000000,Mini,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,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 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,", -"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 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,", -"03000000f70600000100000000000000,N64,a:b1,b:b2,back:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b0,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b8,x:b4,y:b5,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,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,", -"030000006b140000100d000000000000,Nacon Revolution Infinity 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:a3,start:b9,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 USB 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,", -"03000000000f00000100000000000000,NES,a:b1,b:b0,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,", -"03000000571d00002100000000000000,NES,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,", -"03000000921200004346000000000000,NES,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start: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,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,", +"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,", -"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,", +"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,", -"03000000d620000013a7000000000000,NSW 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: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,", -"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 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,", "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,", -"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,", "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,", -"030000006f0e00008501000000000000,PDP Fightpad Pro,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:b1,y:b0,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,", -"03000000d9040000160f000000000000,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,", "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,", -"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,", "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,", @@ -482,88 +304,30 @@ const char* _glfwDefaultMappings[] = "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,", -"03000000250900000088000000000000,PS2 Controllera: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 Controllera: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,", "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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,leftshoulder:b10,leftstick:b1,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,rightx:a2,righty:a3,start:b3,x:b15,y:b12,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,", -"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,", -"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,", "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,", -"03000000120c00000807000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000120c0000aa57000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000160e0000120c000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000120c0000f21c000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000120c0000f41c000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000120c0000f10e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000120c0000150e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000120c0000180e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000120c0000111e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"03000000120c0000181e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", -"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 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,", -"03000000300f00001211000000000000,Qanba JS,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,", "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,", -"030000008a2400006682000000000000,R1,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,", -"030000009b2800002300000000000000,Raphnet Technologies 3DO USB 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 Technologies 3DO USB 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 Technologies Dreamcast USB 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,", -"030000009b2800003200000000000000,Raphnet Technologies 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,Raphnet Technologies 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,", -"030000009b2800001800000000000000,Raphnet Technologies Jaguar USB 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,", -"030000009b2800000200000000000000,Raphnet Technologies NES USB 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,", -"030000009b2800004300000000000000,Raphnet Technologies 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 Technologies 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,", -"030000009b2800000300000000000000,Raphnet Technologies SNES USB 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,", -"030000009b2800005600000000000000,Raphnet Technologies SNES USB Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,", -"030000009b2800005700000000000000,Raphnet Technologies SNES USB Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,", -"030000009b2800001e00000000000000,Raphnet Technologies Vectrex USB 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 Technologies Wii Classic USB Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,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 Technologies Wii Classic USB Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,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,", -"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 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,", @@ -575,185 +339,96 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", -"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,", -"03000000c82d00000290000000000000,Retrobit 64,a:b3,b:b9,back:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b0,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,start:b11,x:b4,y:b8,platform:Windows,", -"03000000c82d00003038000000000000,Retrobit 64,a:b3,b:b9,back:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b0,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,start:b11,x:b4,y:b8,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,", -"03000000bd12000015d0000000000000,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,", "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,", -"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(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,", -"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,", -"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,", -"03000000c6240000fefa000000000000,Rock Candy,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,", "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,", -"03000000c6240000fefa000000010000,Rock Candy Xbox 360 Controller, a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,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,", -"030000004f04000009d0000000000000,Run N Drive,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,", "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,", -"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,", -"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,", "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,", -"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 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,", -"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,", "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,", -"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,", -"03000000bd12000003c0000000000000,Sanwa 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,", -"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,", -"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,", -"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,", -"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,leftx:a0,lefty: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 Bluetooth 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,", -"03000000730700000601000000000000,Saturn,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,Saturn,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,", -"0300000000f000000800000000000000,Saturn,a:b1,b:b2,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,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,", -"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,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,", +"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,", -"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,", -"03000000120c00001c1e000000000000,SnakeByte GamePad 4S 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", -"03000000571d00002000000000000000,SNES Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,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,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,", -"0300000003040000c197000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,", -"0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,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,", -"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,", +"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,", -"03000000de280000fc11000000000000,Steam,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,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,", "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,", -"03000000381000003014000000000000,Stratus Duo,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,", -"03000000381000003114000000000000,Stratus Duo,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,", -"03000000380700003847000000000000,Street Fighter Fighting Stick 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,leftshoulder:b4,leftx:a0,lefty:a1,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,", +"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,", -"030000000d0f0000f600000000000000,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: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,", -"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,", -"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,", -"03000000790000002601000000000000,TGZ,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,", "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,", -"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,", "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,", -"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,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,", -"03000000242f00006e00000000000000,USB Game 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,", -"03000000b50700001503000000000000,USB Game 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,", -"03000000b404000081c6000000000000,USB 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:a5,start:b9,x:b3,y:b0,platform:Windows,", -"03000000666600000188000000000000,USB Game 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,", -"03000000666600000288000000000000,USB Game 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,", -"03000000300f00000701000000000000,USB 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:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", -"03000000bd12000012d0000000000000,USB Game Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", -"030000000b0400003065000000000000,USB 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:a5,start:b9,x:b3,y:b0,platform:Windows,", -"03000000341a00002308000000000000,USB Game 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,", -"030000006b1400000203000000000000,USB Game 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 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:a4,start:b9,x:b3,y:b0,platform:Windows,", -"03000000f0250000c183000000000000,USB Game 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,", -"03000000ff1100004133000000000000,USB Game 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,", +"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,", -"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,WiiU 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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", "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,", -"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,", -"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,", -"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,", -"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,", -"03000000300f00008888000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:b13,dpleft:b10,dpright:b11,dpup:b12,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,", -"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,", -"030000000d0f00006300000000000000,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:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b9,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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", -"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,", "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,", -"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,", "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_BUILD_WIN32_MAPPINGS + +#if defined(GLFW_BUILD_COCOA_MAPPINGS) "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,", @@ -761,15 +436,13 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", "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,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 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,", @@ -779,24 +452,20 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", "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 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,", -"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,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,", -"030000000d0f00008400000000010000,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:Mac OS X,", -"030000000d0f00008500000000010000,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:Mac OS X,", -"03000000151900004000000001000000,Flydigi Vader 2,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:Mac OS X,", -"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,paddle1:b4,paddle2:b5,paddle3:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,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,", @@ -817,6 +486,7 @@ const char* _glfwDefaultMappings[] = "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,", @@ -834,7 +504,7 @@ const char* _glfwDefaultMappings[] = "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,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,", +"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,", @@ -856,7 +526,7 @@ const char* _glfwDefaultMappings[] = "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 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,", +"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,", @@ -877,9 +547,7 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", -"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,", +"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,", @@ -897,8 +565,6 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", -"05000000556e6b6e6f776e2048494400,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,", "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,", @@ -925,7 +591,6 @@ const char* _glfwDefaultMappings[] = "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,", -"030000005e040000200b000011050000,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,", "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,", @@ -933,22 +598,22 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", +#endif // GLFW_BUILD_COCOA_MAPPINGS + +#if defined(GLFW_BUILD_LINUX_MAPPINGS) "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,", -"03000000008000000210000011010000,8BitDo NES30,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,", "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,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,", +"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,", -"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,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,", @@ -986,7 +651,7 @@ const char* _glfwDefaultMappings[] = "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: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,", +"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,", @@ -996,25 +661,21 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", "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 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,", -"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,x:b0,y:b3,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,", -"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,", -"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,", -"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,paddle1:b2,paddle2:b5,paddle4:b17,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: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,", "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,", -"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,", "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,", @@ -1031,9 +692,8 @@ const char* _glfwDefaultMappings[] = "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,", -"03000000300f00001210000010010000,HJC 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,", "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,rightshoulder:b7,start:b9,x:b1,y:b2,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,", @@ -1044,7 +704,6 @@ const char* _glfwDefaultMappings[] = "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,", -"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 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,", @@ -1055,12 +714,14 @@ const char* _glfwDefaultMappings[] = "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 PCU0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,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,", +"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,", @@ -1108,7 +769,6 @@ const char* _glfwDefaultMappings[] = "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,", -"030000005e0400000700000000010000,Microsoft SideWinder Game Pad USB,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,", "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,", @@ -1131,8 +791,7 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", -"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,", +"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,", @@ -1167,20 +826,17 @@ const char* _glfwDefaultMappings[] = "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,", -"03000000c6240000fefa000000010000,Rock Candy Xbox 360 Controller, a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,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,", -"03000000d9040000160f000000010000,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,", "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,", -"03000000d62000000220000001010000,PowerA Wired Controller for Xbox One and Xbox Series S and X,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,", "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,", @@ -1227,8 +883,6 @@ const char* _glfwDefaultMappings[] = "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,", -"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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", -"03000000321500000710000000010000,Razer Raiju Tournament Edition Wired,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:a5,start:b9,x:b0,y:b3,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,", @@ -1236,7 +890,7 @@ const char* _glfwDefaultMappings[] = "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,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,", +"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,", @@ -1255,7 +909,6 @@ const char* _glfwDefaultMappings[] = "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,", -"030000001f08000001e4000010010000,SFC Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,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,", @@ -1310,9 +963,6 @@ const char* _glfwDefaultMappings[] = "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,", -"03000000790000001100000011010000,USB Saturn Controller,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,USB Saturn Controller,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,USB Saturn Pad,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,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,", @@ -1327,16 +977,13 @@ const char* _glfwDefaultMappings[] = "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,", -"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,", "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,", -"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,", "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,", -"060000005e040000120b000007050000,Xbox One Wireless Controller (Model 1914),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,", @@ -1349,202 +996,6 @@ const char* _glfwDefaultMappings[] = "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,", -"38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android,", -"33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android,", -"39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,platform:Android,", -"64653533313537373934323436343563,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:b9,righttrigger:b10,start:b6,x:b2,y:b3,platform:Android,", -"66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,platform:Android,", -"66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android,", -"05000000c82d000006500000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android,", -"05000000c82d000051060000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android,", -"05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,", -"05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,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:Android,", -"050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,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:b3,y:b2,platform:Android,", -"050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,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:b3,y:b2,platform:Android,", -"05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,", -"05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,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:b3,y:b2,platform:Android,", -"38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,platform:Android,", -"61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b31,start:b5,x:b30,y:b2,platform:Android,", -"05000000c82d000012900000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,", -"05000000c82d000062280000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,", -"35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,", -"36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android,", -"38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android,", -"38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,", -"66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,", -"05000000c82d000002600000ffff0f00,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,", -"050000002028000009000000ffff3f00,8BitDo SNES30 Gamepad,a:b1,b:b0,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:b3,y:b2,platform:Android,", -"050000003512000020ab000000780f00,8BitDo SNES30 Gamepad,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android,", -"33666663316164653937326237613331,8BitDo Zero,a:b0,b:b1,back:b15,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android,", -"05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,", -"05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,", -"33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android,", -"34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android,", -"63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,", -"63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android,", -"32333634613735616163326165323731,Amazon Luna Game Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,", -"38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,", -"33323763323132376537376266393366,Dual Strike,a:b24,b:b23,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android,", -"30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,x:b2,y:b3,platform:Android,", -"05000000b404000011240000dfff3f00,Flydigi Vader 2,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,", -"0500000031366332860c44aadfff0f00,GS Gamepad,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,", -"35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,", -"65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b18,platform:Android,", -"31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,", -"0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,platform:Android,", -"64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,platform:Android,", -"65346535636333663931613264643164,Joy Con,a:b21,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,platform:Android,", -"33346566643039343630376565326335,Joy Con (L),a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android,", -"35313531613435623366313835326238,Joy Con (L),a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android,", -"39363561613936303237333537383931,Joy Con (R),a:b0,b:b1,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android,", -"38383665633039363066383334653465,Joy Con (R),a:b0,b:b1,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android,", -"39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,", -"63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android,", -"64623163333561643339623235373232,Logitech F310,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,", -"31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,", -"39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android,", -"32303165626138343962363666346165,Mars,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,", -"31323564663862633234646330373138,Mega Drive,a:b23,b:b22,leftx:a0,lefty:a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android,", -"37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android,", -"64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Android,", -"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,", -"32386235353630393033393135613831,Microsoft Xbox Series 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,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"7573622067616d657061642020202020,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:Android,", -"050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android,", -"34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,a:b0,b:b1,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:Android,", -"050000005509000010720000ffff3f00,NVIDIA Controller v01.03,a:b0,b:b1,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:Android,", -"050000005509000014720000df7f3f00,NVIDIA Controller v01.04,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,", -"61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,platform:Android,", -"32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,platform:Android,", -"61363034663839376638653463633865,PS3,a:b0,b:b1,back:b15,dpdown:a14,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"66366539656564653432353139356536,PS3,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:Android,", -"66383132326164626636313737373037,PS3,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:Android,", -"050000004c05000068020000dfff3f00,PS3 Controller,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:Android,", -"30303839663330346632363232623138,PS4,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,", -"31326235383662333266633463653332,PS4,a:b1,b:b16,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,platform:Android,", -"31663838336334393132303338353963,PS4,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"34613139376634626133336530386430,PS4,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"37626233336235343937333961353732,PS4,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"38393161636261653636653532386639,PS4,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"63313733393535663339656564343962,PS4,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"63393662363836383439353064663939,PS4,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"65366465656364636137653363376531,PS4,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android,", -"66613532303965383534396638613230,PS4,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android,", -"030000004c050000cc09000000006800,PS4 Controller,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:Android,", -"050000004c050000c405000000783f00,PS4 Controller,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:Android,", -"050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,", -"050000004c050000c4050000ffff3f00,PS4 Controller,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:Android,", -"050000004c050000cc090000fffe3f00,PS4 Controller,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:Android,", -"050000004c050000cc090000ffff3f00,PS4 Controller,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:Android,", -"31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,", -"050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,", -"62653335326261303663356263626339,PSX,a:b19,b:b1,back:b17,leftshoulder:b9,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,platform:Android,", -"64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android,", -"36626666353861663864336130363137,Razer Junglecat,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"62653861643333663663383332396665,Razer Kishi,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:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"050000003215000005070000ffff3f00,Razer Raiju Mobile,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:Android,", -"050000003215000007070000ffff3f00,Razer Raiju Mobile,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:Android,", -"050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,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,x:b2,y:b3,platform:Android,", -"61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android,", -"38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b6,platform:Android,", -"33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android,", -"66386565396238363534313863353065,Sanwa Mobile,a:b21,b:b22,leftshoulder:b23,leftx:a0,lefty:a1,rightshoulder:b24,platform:Android,", -"32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android,", -"37316565396364386635383230353365,Saturn,a:b21,b:b22,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android,", -"38613865396530353338373763623431,Saturn,a:b0,b:b1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,platform:Android,", -"61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android,", -"30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android,", -"66386262366536653765333235343634,SG H510,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,", -"66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android,", -"30306461613834333439303734316539,SideWinder Pro,a:b0,b:b1,leftshoulder:b20,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,platform:Android,", -"62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,", -"38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android,", -"32633532643734376632656664383733,Sony DualSense,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android,", -"61303162353165316365336436343139,Sony DualSense,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android,", -"63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android,", -"63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,platform:Android,", -"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,", -"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,", -"0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android,", -"050000004f0400000ed00000fffe3f00,ThrustMaster eSwap PRO Controller,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,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,", -"30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b23,paddle2:b24,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b23,paddle2:b24,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", -"30386438313564306161393537333663,Wii Classic,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android,", -"33333034646336346339646538643633,Wii Classic,a:b23,b:b22,back:b29,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android,", -"30306539356238653637313730656134,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android,", -"30396232393162346330326334636566,Xbox 360,a:b0,b:b1,back:b4,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:Android,", -"38313038323730383864666463383533,Xbox 360,a:b0,b:b1,back:b4,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:Android,", -"65353331386662343338643939643636,Xbox 360,a:b0,b:b1,back:b4,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:Android,", -"65613532386633373963616462363038,Xbox 360,a:b0,b:b1,back:b4,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:Android,", -"050000005e0400008e02000000783f00,Xbox 360 Controller,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:Android,", -"33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,", -"050000005e040000e002000000783f00,Xbox One S Controller,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:Android,", -"050000005e040000ea02000000783f00,Xbox One S Controller,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:Android,", -"050000005e040000fd020000ff7f3f00,Xbox One S Controller,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:Android,", -"050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android,", -"050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,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:Android,", -"050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,", -"050000005e040000130b0000ffff3f00,Xbox Series Controller,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:Android,", -"65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Android,", -"050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,", -"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,", -"36616131643361333337396261666433,Xbox Wireless Controller,a:b0,b:b1,back:b15,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:Android,", -"050000001727000044310000ffff3f00,XiaoMi Game Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,", -"05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,", -"05000000ac050000010000004f066d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,", -"05000000ac05000001000000cf076d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,", -"05000000ac05000001000000df076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", -"05000000ac05000001000000ff076d01,*,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:iOS,", -"05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,", -"05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,", -"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,", -"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,", -"050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", -"050000004c050000cc090000df870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", -"050000004c050000cc090000ff070000,PS4 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:iOS,", -"050000004c050000cc090000ff870001,PS4 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,touchpad:b11,x:b2,y:b3,platform:iOS,", -"050000004c050000cc090000ff876d01,PS4 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,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,", -"050000004c050000e60c0000df870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,touchpad:b10,x:b2,y:b3,platform:iOS,", -"050000004c050000e60c0000ff870000,PS5 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,touchpad:b11,x:b2,y:b3,platform:iOS,", -"05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,", -"05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,", -"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,", -"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,", -"050000005e040000050b0000df070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b10,paddle2:b12,paddle3:b11,paddle4:b13,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", -"050000005e040000050b0000ff070001,Xbox Elite Wireless 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,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,", -"050000005e040000130b0000df870001,Xbox Series X Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b10,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", -"050000005e040000130b0000ff870001,Xbox Series X 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,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,", -"050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,", -"050000005e040000e0020000ff070000,Xbox Wireless 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:iOS,", - -"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,", -NULL +#endif // GLFW_BUILD_LINUX_MAPPINGS }; diff --git a/src/external/glfw/src/mappings.h.in b/src/external/glfw/src/mappings.h.in index 583e98b2f..f2604390a 100644 --- a/src/external/glfw/src/mappings.h.in +++ b/src/external/glfw/src/mappings.h.in @@ -31,7 +31,7 @@ // all available in SDL_GameControllerDB. Do not edit this file. Any gamepad // mappings not specific to GLFW should be submitted to SDL_GameControllerDB. // This file can be re-generated from mappings.h.in and the upstream -// gamecontrollerdb.txt with the GenerateMappings.cmake script. +// gamecontrollerdb.txt with the 'update_mappings' CMake target. //======================================================================== // All gamepad mappings not labeled GLFW are copied from the @@ -60,7 +60,8 @@ const char* _glfwDefaultMappings[] = { -@GLFW_GAMEPAD_MAPPINGS@ +#if defined(GLFW_BUILD_WIN32_MAPPINGS) +@GLFW_WIN32_MAPPINGS@ "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,", @@ -68,6 +69,14 @@ const char* _glfwDefaultMappings[] = "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,", -NULL +#endif // GLFW_BUILD_WIN32_MAPPINGS + +#if defined(GLFW_BUILD_COCOA_MAPPINGS) +@GLFW_COCOA_MAPPINGS@ +#endif // GLFW_BUILD_COCOA_MAPPINGS + +#if defined(GLFW_BUILD_LINUX_MAPPINGS) +@GLFW_LINUX_MAPPINGS@ +#endif // GLFW_BUILD_LINUX_MAPPINGS }; diff --git a/src/external/glfw/src/monitor.c b/src/external/glfw/src/monitor.c index bde930102..6429493bf 100644 --- a/src/external/glfw/src/monitor.c +++ b/src/external/glfw/src/monitor.c @@ -74,13 +74,13 @@ static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) if (monitor->modes) return GLFW_TRUE; - modes = _glfwPlatformGetVideoModes(monitor, &modeCount); + modes = _glfw.platform.getVideoModes(monitor, &modeCount); if (!modes) return GLFW_FALSE; qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); - free(monitor->modes); + _glfw_free(monitor->modes); monitor->modes = modes; monitor->modeCount = modeCount; @@ -96,11 +96,16 @@ static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) // void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) { + assert(monitor != NULL); + assert(action == GLFW_CONNECTED || action == GLFW_DISCONNECTED); + assert(placement == _GLFW_INSERT_FIRST || placement == _GLFW_INSERT_LAST); + if (action == GLFW_CONNECTED) { _glfw.monitorCount++; _glfw.monitors = - realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount); + _glfw_realloc(_glfw.monitors, + sizeof(_GLFWmonitor*) * _glfw.monitorCount); if (placement == _GLFW_INSERT_FIRST) { @@ -122,10 +127,10 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) if (window->monitor == monitor) { int width, height, xoff, yoff; - _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); - _glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL); - _glfwPlatformSetWindowPos(window, xoff, yoff); + _glfw.platform.getWindowSize(window, &width, &height); + _glfw.platform.setWindowMonitor(window, NULL, 0, 0, width, height, 0); + _glfw.platform.getWindowFrameSize(window, &xoff, &yoff, NULL, NULL); + _glfw.platform.setWindowPos(window, xoff, yoff); } } @@ -154,6 +159,7 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) // void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) { + assert(monitor != NULL); monitor->window = window; } @@ -166,7 +172,7 @@ void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) // _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) { - _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); + _GLFWmonitor* monitor = _glfw_calloc(1, sizeof(_GLFWmonitor)); monitor->widthMM = widthMM; monitor->heightMM = heightMM; @@ -182,22 +188,22 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor) if (monitor == NULL) return; - _glfwPlatformFreeMonitor(monitor); + _glfw.platform.freeMonitor(monitor); _glfwFreeGammaArrays(&monitor->originalRamp); _glfwFreeGammaArrays(&monitor->currentRamp); - free(monitor->modes); - free(monitor); + _glfw_free(monitor->modes); + _glfw_free(monitor); } // Allocates red, green and blue value arrays of the specified size // void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) { - ramp->red = calloc(size, sizeof(unsigned short)); - ramp->green = calloc(size, sizeof(unsigned short)); - ramp->blue = calloc(size, sizeof(unsigned short)); + ramp->red = _glfw_calloc(size, sizeof(unsigned short)); + ramp->green = _glfw_calloc(size, sizeof(unsigned short)); + ramp->blue = _glfw_calloc(size, sizeof(unsigned short)); ramp->size = size; } @@ -205,9 +211,9 @@ void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) // void _glfwFreeGammaArrays(GLFWgammaramp* ramp) { - free(ramp->red); - free(ramp->green); - free(ramp->blue); + _glfw_free(ramp->red); + _glfw_free(ramp->green); + _glfw_free(ramp->blue); memset(ramp, 0, sizeof(GLFWgammaramp)); } @@ -331,7 +337,7 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) _GLFW_REQUIRE_INIT(); - _glfwPlatformGetMonitorPos(monitor, xpos, ypos); + _glfw.platform.getMonitorPos(monitor, xpos, ypos); } GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, @@ -352,7 +358,7 @@ GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, _GLFW_REQUIRE_INIT(); - _glfwPlatformGetMonitorWorkarea(monitor, xpos, ypos, width, height); + _glfw.platform.getMonitorWorkarea(monitor, xpos, ypos, width, height); } GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) @@ -385,7 +391,7 @@ GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, *yscale = 0.f; _GLFW_REQUIRE_INIT(); - _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale); + _glfw.platform.getMonitorContentScale(monitor, xscale, yscale); } GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) @@ -418,7 +424,7 @@ GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle) GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(_glfw.callbacks.monitor, cbfun); + _GLFW_SWAP(GLFWmonitorfun, _glfw.callbacks.monitor, cbfun); return cbfun; } @@ -446,7 +452,7 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _glfwPlatformGetVideoMode(monitor, &monitor->currentMode); + _glfw.platform.getVideoMode(monitor, &monitor->currentMode); return &monitor->currentMode; } @@ -472,7 +478,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) if (!original) return; - values = calloc(original->size, sizeof(unsigned short)); + values = _glfw_calloc(original->size, sizeof(unsigned short)); for (i = 0; i < original->size; i++) { @@ -494,7 +500,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) ramp.size = original->size; glfwSetGammaRamp(handle, &ramp); - free(values); + _glfw_free(values); } GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) @@ -505,7 +511,7 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _glfwFreeGammaArrays(&monitor->currentRamp); - if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp)) + if (!_glfw.platform.getGammaRamp(monitor, &monitor->currentRamp)) return NULL; return &monitor->currentRamp; @@ -521,6 +527,8 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) assert(ramp->green != NULL); assert(ramp->blue != NULL); + _GLFW_REQUIRE_INIT(); + if (ramp->size <= 0) { _glfwInputError(GLFW_INVALID_VALUE, @@ -529,14 +537,12 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) return; } - _GLFW_REQUIRE_INIT(); - if (!monitor->originalRamp.size) { - if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp)) + if (!_glfw.platform.getGammaRamp(monitor, &monitor->originalRamp)) return; } - _glfwPlatformSetGammaRamp(monitor, ramp); + _glfw.platform.setGammaRamp(monitor, ramp); } diff --git a/src/external/glfw/src/nsgl_context.h b/src/external/glfw/src/nsgl_context.h deleted file mode 100644 index 9c31436cf..000000000 --- a/src/external/glfw/src/nsgl_context.h +++ /dev/null @@ -1,66 +0,0 @@ -//======================================================================== -// GLFW 3.4 macOS - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2009-2019 Camilla Löwy -// -// 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. -// -//======================================================================== - -// NOTE: Many Cocoa enum values have been renamed and we need to build across -// SDK versions where one is unavailable or the other deprecated -// We use the newer names in code and these macros to handle compatibility -#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 - #define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval - #define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity -#endif - -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl - -#include - - -// NSGL-specific per-context data -// -typedef struct _GLFWcontextNSGL -{ - id pixelFormat; - id object; - -} _GLFWcontextNSGL; - -// NSGL-specific global data -// -typedef struct _GLFWlibraryNSGL -{ - // dlopen handle for OpenGL.framework (for glfwGetProcAddress) - CFBundleRef framework; - -} _GLFWlibraryNSGL; - - -GLFWbool _glfwInitNSGL(void); -void _glfwTerminateNSGL(void); -GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); -void _glfwDestroyContextNSGL(_GLFWwindow* window); - diff --git a/src/external/glfw/src/nsgl_context.m b/src/external/glfw/src/nsgl_context.m index 3f73f7a6b..fc1f75210 100644 --- a/src/external/glfw/src/nsgl_context.m +++ b/src/external/glfw/src/nsgl_context.m @@ -188,45 +188,45 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but // are not a hard constraint, so ignore and continue -#define addAttrib(a) \ +#define ADD_ATTRIB(a) \ { \ assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ } -#define setAttrib(a, v) { addAttrib(a); addAttrib(v); } +#define SET_ATTRIB(a, v) { ADD_ATTRIB(a); ADD_ATTRIB(v); } NSOpenGLPixelFormatAttribute attribs[40]; int index = 0; - addAttrib(NSOpenGLPFAAccelerated); - addAttrib(NSOpenGLPFAClosestPolicy); + ADD_ATTRIB(NSOpenGLPFAAccelerated); + ADD_ATTRIB(NSOpenGLPFAClosestPolicy); if (ctxconfig->nsgl.offline) { - addAttrib(NSOpenGLPFAAllowOfflineRenderers); + ADD_ATTRIB(NSOpenGLPFAAllowOfflineRenderers); // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in // Info.plist for unbundled applications // HACK: This assumes that NSOpenGLPixelFormat will remain // a straightforward wrapper of its CGL counterpart - addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); + ADD_ATTRIB(kCGLPFASupportsAutomaticGraphicsSwitching); } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 if (ctxconfig->major >= 4) { - setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); + SET_ATTRIB(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); } else #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ if (ctxconfig->major >= 3) { - setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); + SET_ATTRIB(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); } if (ctxconfig->major <= 2) { if (fbconfig->auxBuffers != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); + SET_ATTRIB(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); if (fbconfig->accumRedBits != GLFW_DONT_CARE && fbconfig->accumGreenBits != GLFW_DONT_CARE && @@ -238,7 +238,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, fbconfig->accumBlueBits + fbconfig->accumAlphaBits; - setAttrib(NSOpenGLPFAAccumSize, accumBits); + SET_ATTRIB(NSOpenGLPFAAccumSize, accumBits); } } @@ -256,17 +256,17 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, else if (colorBits < 15) colorBits = 15; - setAttrib(NSOpenGLPFAColorSize, colorBits); + SET_ATTRIB(NSOpenGLPFAColorSize, colorBits); } if (fbconfig->alphaBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); + SET_ATTRIB(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); if (fbconfig->depthBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); + SET_ATTRIB(NSOpenGLPFADepthSize, fbconfig->depthBits); if (fbconfig->stencilBits != GLFW_DONT_CARE) - setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); + SET_ATTRIB(NSOpenGLPFAStencilSize, fbconfig->stencilBits); if (fbconfig->stereo) { @@ -275,33 +275,33 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, "NSGL: Stereo rendering is deprecated"); return GLFW_FALSE; #else - addAttrib(NSOpenGLPFAStereo); + ADD_ATTRIB(NSOpenGLPFAStereo); #endif } if (fbconfig->doublebuffer) - addAttrib(NSOpenGLPFADoubleBuffer); + ADD_ATTRIB(NSOpenGLPFADoubleBuffer); if (fbconfig->samples != GLFW_DONT_CARE) { if (fbconfig->samples == 0) { - setAttrib(NSOpenGLPFASampleBuffers, 0); + SET_ATTRIB(NSOpenGLPFASampleBuffers, 0); } else { - setAttrib(NSOpenGLPFASampleBuffers, 1); - setAttrib(NSOpenGLPFASamples, fbconfig->samples); + SET_ATTRIB(NSOpenGLPFASampleBuffers, 1); + SET_ATTRIB(NSOpenGLPFASamples, fbconfig->samples); } } // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB // framebuffer, so there's no need (and no way) to request it - addAttrib(0); + ADD_ATTRIB(0); -#undef addAttrib -#undef setAttrib +#undef ADD_ATTRIB +#undef SET_ATTRIB window->context.nsgl.pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; @@ -358,7 +358,14 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(nil); - if (window->context.client == GLFW_NO_API) + if (_glfw.platform.platformID != GLFW_PLATFORM_COCOA) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, + "NSGL: Platform not initialized"); + return nil; + } + + if (window->context.source != GLFW_NATIVE_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return nil; diff --git a/src/external/glfw/src/null_init.c b/src/external/glfw/src/null_init.c index 57aafd5d3..de4b28f35 100644 --- a/src/external/glfw/src/null_init.c +++ b/src/external/glfw/src/null_init.c @@ -36,22 +36,98 @@ ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformInit(void) +GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) { - _glfwInitTimerPOSIX(); - _glfwPollMonitorsNull(); + const _GLFWplatform null = + { + GLFW_PLATFORM_NULL, + _glfwInitNull, + _glfwTerminateNull, + _glfwGetCursorPosNull, + _glfwSetCursorPosNull, + _glfwSetCursorModeNull, + _glfwSetRawMouseMotionNull, + _glfwRawMouseMotionSupportedNull, + _glfwCreateCursorNull, + _glfwCreateStandardCursorNull, + _glfwDestroyCursorNull, + _glfwSetCursorNull, + _glfwGetScancodeNameNull, + _glfwGetKeyScancodeNull, + _glfwSetClipboardStringNull, + _glfwGetClipboardStringNull, + _glfwInitJoysticksNull, + _glfwTerminateJoysticksNull, + _glfwPollJoystickNull, + _glfwGetMappingNameNull, + _glfwUpdateGamepadGUIDNull, + _glfwFreeMonitorNull, + _glfwGetMonitorPosNull, + _glfwGetMonitorContentScaleNull, + _glfwGetMonitorWorkareaNull, + _glfwGetVideoModesNull, + _glfwGetVideoModeNull, + _glfwGetGammaRampNull, + _glfwSetGammaRampNull, + _glfwCreateWindowNull, + _glfwDestroyWindowNull, + _glfwSetWindowTitleNull, + _glfwSetWindowIconNull, + _glfwGetWindowPosNull, + _glfwSetWindowPosNull, + _glfwGetWindowSizeNull, + _glfwSetWindowSizeNull, + _glfwSetWindowSizeLimitsNull, + _glfwSetWindowAspectRatioNull, + _glfwGetFramebufferSizeNull, + _glfwGetWindowFrameSizeNull, + _glfwGetWindowContentScaleNull, + _glfwIconifyWindowNull, + _glfwRestoreWindowNull, + _glfwMaximizeWindowNull, + _glfwShowWindowNull, + _glfwHideWindowNull, + _glfwRequestWindowAttentionNull, + _glfwFocusWindowNull, + _glfwSetWindowMonitorNull, + _glfwWindowFocusedNull, + _glfwWindowIconifiedNull, + _glfwWindowVisibleNull, + _glfwWindowMaximizedNull, + _glfwWindowHoveredNull, + _glfwFramebufferTransparentNull, + _glfwGetWindowOpacityNull, + _glfwSetWindowResizableNull, + _glfwSetWindowDecoratedNull, + _glfwSetWindowFloatingNull, + _glfwSetWindowOpacityNull, + _glfwSetWindowMousePassthroughNull, + _glfwPollEventsNull, + _glfwWaitEventsNull, + _glfwWaitEventsTimeoutNull, + _glfwPostEmptyEventNull, + _glfwGetEGLPlatformNull, + _glfwGetEGLNativeDisplayNull, + _glfwGetEGLNativeWindowNull, + _glfwGetRequiredInstanceExtensionsNull, + _glfwGetPhysicalDevicePresentationSupportNull, + _glfwCreateWindowSurfaceNull, + }; + *platform = null; return GLFW_TRUE; } -void _glfwPlatformTerminate(void) +int _glfwInitNull(void) +{ + _glfwPollMonitorsNull(); + return GLFW_TRUE; +} + +void _glfwTerminateNull(void) { free(_glfw.null.clipboardString); _glfwTerminateOSMesa(); -} - -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " null OSMesa"; + _glfwTerminateEGL(); } diff --git a/src/external/glfw/src/null_joystick.c b/src/external/glfw/src/null_joystick.c index 27756a619..1fe50721b 100644 --- a/src/external/glfw/src/null_joystick.c +++ b/src/external/glfw/src/null_joystick.c @@ -33,21 +33,26 @@ ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwPlatformInitJoysticks(void) +GLFWbool _glfwInitJoysticksNull(void) { return GLFW_TRUE; } -void _glfwPlatformTerminateJoysticks(void) +void _glfwTerminateJoysticksNull(void) { } -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +GLFWbool _glfwPollJoystickNull(_GLFWjoystick* js, int mode) { return GLFW_FALSE; } -void _glfwPlatformUpdateGamepadGUID(char* guid) +const char* _glfwGetMappingNameNull(void) +{ + return ""; +} + +void _glfwUpdateGamepadGUIDNull(char* guid) { } diff --git a/src/external/glfw/src/null_joystick.h b/src/external/glfw/src/null_joystick.h index 5d19a451b..a2199c560 100644 --- a/src/external/glfw/src/null_joystick.h +++ b/src/external/glfw/src/null_joystick.h @@ -24,8 +24,9 @@ // //======================================================================== -#define _GLFW_PLATFORM_JOYSTICK_STATE struct { int dummyJoystick; } -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } - -#define _GLFW_PLATFORM_MAPPING_NAME "" +GLFWbool _glfwInitJoysticksNull(void); +void _glfwTerminateJoysticksNull(void); +GLFWbool _glfwPollJoystickNull(_GLFWjoystick* js, int mode); +const char* _glfwGetMappingNameNull(void); +void _glfwUpdateGamepadGUIDNull(char* guid); diff --git a/src/external/glfw/src/null_monitor.c b/src/external/glfw/src/null_monitor.c index 8301eb3a3..63a1cd205 100644 --- a/src/external/glfw/src/null_monitor.c +++ b/src/external/glfw/src/null_monitor.c @@ -65,12 +65,12 @@ void _glfwPollMonitorsNull(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +void _glfwFreeMonitorNull(_GLFWmonitor* monitor) { _glfwFreeGammaArrays(&monitor->null.ramp); } -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +void _glfwGetMonitorPosNull(_GLFWmonitor* monitor, int* xpos, int* ypos) { if (xpos) *xpos = 0; @@ -78,8 +78,8 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = 0; } -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) +void _glfwGetMonitorContentScaleNull(_GLFWmonitor* monitor, + float* xscale, float* yscale) { if (xscale) *xscale = 1.f; @@ -87,9 +87,9 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, *yscale = 1.f; } -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, - int* xpos, int* ypos, - int* width, int* height) +void _glfwGetMonitorWorkareaNull(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) { const GLFWvidmode mode = getVideoMode(); @@ -103,26 +103,28 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, *height = mode.height - 10; } -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +GLFWvidmode* _glfwGetVideoModesNull(_GLFWmonitor* monitor, int* found) { - GLFWvidmode* mode = calloc(1, sizeof(GLFWvidmode)); + GLFWvidmode* mode = _glfw_calloc(1, sizeof(GLFWvidmode)); *mode = getVideoMode(); *found = 1; return mode; } -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +void _glfwGetVideoModeNull(_GLFWmonitor* monitor, GLFWvidmode* mode) { *mode = getVideoMode(); } -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwGetGammaRampNull(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { if (!monitor->null.ramp.size) { + unsigned int i; + _glfwAllocGammaArrays(&monitor->null.ramp, 256); - for (unsigned int i = 0; i < monitor->null.ramp.size; i++) + for (i = 0; i < monitor->null.ramp.size; i++) { const float gamma = 2.2f; float value; @@ -143,7 +145,7 @@ GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) return GLFW_TRUE; } -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +void _glfwSetGammaRampNull(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { if (monitor->null.ramp.size != ramp->size) { diff --git a/src/external/glfw/src/null_platform.h b/src/external/glfw/src/null_platform.h index 49436dcca..b646acb30 100644 --- a/src/external/glfw/src/null_platform.h +++ b/src/external/glfw/src/null_platform.h @@ -25,29 +25,14 @@ // //======================================================================== -#include +#define GLFW_NULL_WINDOW_STATE _GLFWwindowNull null; +#define GLFW_NULL_LIBRARY_WINDOW_STATE _GLFWlibraryNull null; +#define GLFW_NULL_MONITOR_STATE _GLFWmonitorNull null; -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNull null -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNull null +#define GLFW_NULL_CONTEXT_STATE +#define GLFW_NULL_CURSOR_STATE +#define GLFW_NULL_LIBRARY_CONTEXT_STATE -#define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } -#define _GLFW_PLATFORM_CURSOR_STATE struct { int dummyCursor; } -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } - -#include "posix_time.h" -#include "posix_thread.h" -#include "null_joystick.h" - -#if defined(_GLFW_WIN32) - #define _glfw_dlopen(name) LoadLibraryA(name) - #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) - #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) -#else - #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) - #define _glfw_dlclose(handle) dlclose(handle) - #define _glfw_dlsym(handle, name) dlsym(handle, name) -#endif // Null-specific per-window data // @@ -87,3 +72,78 @@ typedef struct _GLFWlibraryNull void _glfwPollMonitorsNull(void); +GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform); +int _glfwInitNull(void); +void _glfwTerminateNull(void); + +void _glfwFreeMonitorNull(_GLFWmonitor* monitor); +void _glfwGetMonitorPosNull(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwGetMonitorContentScaleNull(_GLFWmonitor* monitor, float* xscale, float* yscale); +void _glfwGetMonitorWorkareaNull(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); +GLFWvidmode* _glfwGetVideoModesNull(_GLFWmonitor* monitor, int* found); +void _glfwGetVideoModeNull(_GLFWmonitor* monitor, GLFWvidmode* mode); +GLFWbool _glfwGetGammaRampNull(_GLFWmonitor* monitor, GLFWgammaramp* ramp); +void _glfwSetGammaRampNull(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +GLFWbool _glfwCreateWindowNull(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); +void _glfwDestroyWindowNull(_GLFWwindow* window); +void _glfwSetWindowTitleNull(_GLFWwindow* window, const char* title); +void _glfwSetWindowIconNull(_GLFWwindow* window, int count, const GLFWimage* images); +void _glfwSetWindowMonitorNull(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); +void _glfwGetWindowPosNull(_GLFWwindow* window, int* xpos, int* ypos); +void _glfwSetWindowPosNull(_GLFWwindow* window, int xpos, int ypos); +void _glfwGetWindowSizeNull(_GLFWwindow* window, int* width, int* height); +void _glfwSetWindowSizeNull(_GLFWwindow* window, int width, int height); +void _glfwSetWindowSizeLimitsNull(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); +void _glfwSetWindowAspectRatioNull(_GLFWwindow* window, int n, int d); +void _glfwGetFramebufferSizeNull(_GLFWwindow* window, int* width, int* height); +void _glfwGetWindowFrameSizeNull(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); +void _glfwGetWindowContentScaleNull(_GLFWwindow* window, float* xscale, float* yscale); +void _glfwIconifyWindowNull(_GLFWwindow* window); +void _glfwRestoreWindowNull(_GLFWwindow* window); +void _glfwMaximizeWindowNull(_GLFWwindow* window); +GLFWbool _glfwWindowMaximizedNull(_GLFWwindow* window); +GLFWbool _glfwWindowHoveredNull(_GLFWwindow* window); +GLFWbool _glfwFramebufferTransparentNull(_GLFWwindow* window); +void _glfwSetWindowResizableNull(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowDecoratedNull(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowFloatingNull(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowMousePassthroughNull(_GLFWwindow* window, GLFWbool enabled); +float _glfwGetWindowOpacityNull(_GLFWwindow* window); +void _glfwSetWindowOpacityNull(_GLFWwindow* window, float opacity); +void _glfwSetRawMouseMotionNull(_GLFWwindow *window, GLFWbool enabled); +GLFWbool _glfwRawMouseMotionSupportedNull(void); +void _glfwShowWindowNull(_GLFWwindow* window); +void _glfwRequestWindowAttentionNull(_GLFWwindow* window); +void _glfwRequestWindowAttentionNull(_GLFWwindow* window); +void _glfwHideWindowNull(_GLFWwindow* window); +void _glfwFocusWindowNull(_GLFWwindow* window); +GLFWbool _glfwWindowFocusedNull(_GLFWwindow* window); +GLFWbool _glfwWindowIconifiedNull(_GLFWwindow* window); +GLFWbool _glfwWindowVisibleNull(_GLFWwindow* window); +void _glfwPollEventsNull(void); +void _glfwWaitEventsNull(void); +void _glfwWaitEventsTimeoutNull(double timeout); +void _glfwPostEmptyEventNull(void); +void _glfwGetCursorPosNull(_GLFWwindow* window, double* xpos, double* ypos); +void _glfwSetCursorPosNull(_GLFWwindow* window, double x, double y); +void _glfwSetCursorModeNull(_GLFWwindow* window, int mode); +GLFWbool _glfwCreateCursorNull(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); +GLFWbool _glfwCreateStandardCursorNull(_GLFWcursor* cursor, int shape); +void _glfwDestroyCursorNull(_GLFWcursor* cursor); +void _glfwSetCursorNull(_GLFWwindow* window, _GLFWcursor* cursor); +void _glfwSetClipboardStringNull(const char* string); +const char* _glfwGetClipboardStringNull(void); +const char* _glfwGetScancodeNameNull(int scancode); +int _glfwGetKeyScancodeNull(int key); + +EGLenum _glfwGetEGLPlatformNull(EGLint** attribs); +EGLNativeDisplayType _glfwGetEGLNativeDisplayNull(void); +EGLNativeWindowType _glfwGetEGLNativeWindowNull(_GLFWwindow* window); + +void _glfwGetRequiredInstanceExtensionsNull(char** extensions); +GLFWbool _glfwGetPhysicalDevicePresentationSupportNull(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); +VkResult _glfwCreateWindowSurfaceNull(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +void _glfwPollMonitorsNull(void); + diff --git a/src/external/glfw/src/null_window.c b/src/external/glfw/src/null_window.c index 3e4466410..5cdf3e236 100644 --- a/src/external/glfw/src/null_window.c +++ b/src/external/glfw/src/null_window.c @@ -39,24 +39,24 @@ static void applySizeLimits(_GLFWwindow* window, int* width, int* height) *height = (int) (*width / ratio); } - if (window->minwidth != GLFW_DONT_CARE && *width < window->minwidth) - *width = window->minwidth; - else if (window->maxwidth != GLFW_DONT_CARE && *width > window->maxwidth) - *width = window->maxwidth; + if (window->minwidth != GLFW_DONT_CARE) + *width = _glfw_max(*width, window->minwidth); + else if (window->maxwidth != GLFW_DONT_CARE) + *width = _glfw_min(*width, window->maxwidth); - if (window->minheight != GLFW_DONT_CARE && *height < window->minheight) - *height = window->minheight; - else if (window->maxheight != GLFW_DONT_CARE && *height > window->maxheight) - *height = window->maxheight; + if (window->minheight != GLFW_DONT_CARE) + *height = _glfw_min(*height, window->minheight); + else if (window->maxheight != GLFW_DONT_CARE) + *height = _glfw_max(*height, window->maxheight); } static void fitToMonitor(_GLFWwindow* window) { GLFWvidmode mode; - _glfwPlatformGetVideoMode(window->monitor, &mode); - _glfwPlatformGetMonitorPos(window->monitor, - &window->null.xpos, - &window->null.ypos); + _glfwGetVideoModeNull(window->monitor, &mode); + _glfwGetMonitorPosNull(window->monitor, + &window->null.xpos, + &window->null.ypos); window->null.width = mode.width; window->null.height = mode.height; } @@ -82,8 +82,17 @@ static int createNativeWindow(_GLFWwindow* window, fitToMonitor(window); else { - window->null.xpos = 17; - window->null.ypos = 17; + if (wndconfig->xpos == GLFW_ANY_POSITION && wndconfig->ypos == GLFW_ANY_POSITION) + { + window->null.xpos = 17; + window->null.ypos = 17; + } + else + { + window->null.xpos = wndconfig->xpos; + window->null.ypos = wndconfig->ypos; + } + window->null.width = wndconfig->width; window->null.height = wndconfig->height; } @@ -103,10 +112,10 @@ static int createNativeWindow(_GLFWwindow* window, ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) +GLFWbool _glfwCreateWindowNull(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) { if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; @@ -121,24 +130,44 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { - _glfwInputError(GLFW_API_UNAVAILABLE, "Null: EGL not available"); - return GLFW_FALSE; + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughNull(window, GLFW_TRUE); + if (window->monitor) { - _glfwPlatformShowWindow(window); - _glfwPlatformFocusWindow(window); + _glfwShowWindowNull(window); + _glfwFocusWindowNull(window); acquireMonitor(window); + + if (wndconfig->centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig->visible) + { + _glfwShowWindowNull(window); + if (wndconfig->focused) + _glfwFocusWindowNull(window); + } } return GLFW_TRUE; } -void _glfwPlatformDestroyWindow(_GLFWwindow* window) +void _glfwDestroyWindowNull(_GLFWwindow* window) { if (window->monitor) releaseMonitor(window); @@ -150,27 +179,26 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) window->context.destroy(window); } -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +void _glfwSetWindowTitleNull(_GLFWwindow* window, const char* title) { } -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, - const GLFWimage* images) +void _glfwSetWindowIconNull(_GLFWwindow* window, int count, const GLFWimage* images) { } -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) +void _glfwSetWindowMonitorNull(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) { if (window->monitor == monitor) { if (!monitor) { - _glfwPlatformSetWindowPos(window, xpos, ypos); - _glfwPlatformSetWindowSize(window, width, height); + _glfwSetWindowPosNull(window, xpos, ypos); + _glfwSetWindowSizeNull(window, width, height); } return; @@ -189,12 +217,12 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, } else { - _glfwPlatformSetWindowPos(window, xpos, ypos); - _glfwPlatformSetWindowSize(window, width, height); + _glfwSetWindowPosNull(window, xpos, ypos); + _glfwSetWindowSizeNull(window, width, height); } } -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +void _glfwGetWindowPosNull(_GLFWwindow* window, int* xpos, int* ypos) { if (xpos) *xpos = window->null.xpos; @@ -202,7 +230,7 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) *ypos = window->null.ypos; } -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +void _glfwSetWindowPosNull(_GLFWwindow* window, int xpos, int ypos) { if (window->monitor) return; @@ -215,7 +243,7 @@ void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) } } -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetWindowSizeNull(_GLFWwindow* window, int* width, int* height) { if (width) *width = window->null.width; @@ -223,7 +251,7 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) *height = window->null.height; } -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +void _glfwSetWindowSizeNull(_GLFWwindow* window, int width, int height) { if (window->monitor) return; @@ -237,25 +265,25 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) } } -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) +void _glfwSetWindowSizeLimitsNull(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) { int width = window->null.width; int height = window->null.height; applySizeLimits(window, &width, &height); - _glfwPlatformSetWindowSize(window, width, height); + _glfwSetWindowSizeNull(window, width, height); } -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) +void _glfwSetWindowAspectRatioNull(_GLFWwindow* window, int n, int d) { int width = window->null.width; int height = window->null.height; applySizeLimits(window, &width, &height); - _glfwPlatformSetWindowSize(window, width, height); + _glfwSetWindowSizeNull(window, width, height); } -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetFramebufferSizeNull(_GLFWwindow* window, int* width, int* height) { if (width) *width = window->null.width; @@ -263,9 +291,9 @@ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* heigh *height = window->null.height; } -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) +void _glfwGetWindowFrameSizeNull(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) { if (window->null.decorated && !window->monitor) { @@ -291,8 +319,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, } } -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) +void _glfwGetWindowContentScaleNull(_GLFWwindow* window, float* xscale, float* yscale) { if (xscale) *xscale = 1.f; @@ -300,7 +327,7 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, *yscale = 1.f; } -void _glfwPlatformIconifyWindow(_GLFWwindow* window) +void _glfwIconifyWindowNull(_GLFWwindow* window) { if (_glfw.null.focusedWindow == window) { @@ -318,7 +345,7 @@ void _glfwPlatformIconifyWindow(_GLFWwindow* window) } } -void _glfwPlatformRestoreWindow(_GLFWwindow* window) +void _glfwRestoreWindowNull(_GLFWwindow* window) { if (window->null.iconified) { @@ -335,7 +362,7 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) } } -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +void _glfwMaximizeWindowNull(_GLFWwindow* window) { if (!window->null.maximized) { @@ -344,12 +371,12 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window) } } -int _glfwPlatformWindowMaximized(_GLFWwindow* window) +GLFWbool _glfwWindowMaximizedNull(_GLFWwindow* window) { return window->null.maximized; } -int _glfwPlatformWindowHovered(_GLFWwindow* window) +GLFWbool _glfwWindowHoveredNull(_GLFWwindow* window) { return _glfw.null.xcursor >= window->null.xpos && _glfw.null.ycursor >= window->null.ypos && @@ -357,59 +384,59 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window) _glfw.null.ycursor <= window->null.ypos + window->null.height - 1; } -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +GLFWbool _glfwFramebufferTransparentNull(_GLFWwindow* window) { return window->null.transparent; } -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowResizableNull(_GLFWwindow* window, GLFWbool enabled) { window->null.resizable = enabled; } -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowDecoratedNull(_GLFWwindow* window, GLFWbool enabled) { window->null.decorated = enabled; } -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowFloatingNull(_GLFWwindow* window, GLFWbool enabled) { window->null.floating = enabled; } -void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowMousePassthroughNull(_GLFWwindow* window, GLFWbool enabled) { } -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +float _glfwGetWindowOpacityNull(_GLFWwindow* window) { return window->null.opacity; } -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +void _glfwSetWindowOpacityNull(_GLFWwindow* window, float opacity) { window->null.opacity = opacity; } -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +void _glfwSetRawMouseMotionNull(_GLFWwindow *window, GLFWbool enabled) { } -GLFWbool _glfwPlatformRawMouseMotionSupported(void) +GLFWbool _glfwRawMouseMotionSupportedNull(void) { return GLFW_TRUE; } -void _glfwPlatformShowWindow(_GLFWwindow* window) +void _glfwShowWindowNull(_GLFWwindow* window) { window->null.visible = GLFW_TRUE; } -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +void _glfwRequestWindowAttentionNull(_GLFWwindow* window) { } -void _glfwPlatformHideWindow(_GLFWwindow* window) +void _glfwHideWindowNull(_GLFWwindow* window) { if (_glfw.null.focusedWindow == window) { @@ -420,59 +447,61 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) window->null.visible = GLFW_FALSE; } -void _glfwPlatformFocusWindow(_GLFWwindow* window) +void _glfwFocusWindowNull(_GLFWwindow* window) { + _GLFWwindow* previous; + if (_glfw.null.focusedWindow == window) return; if (!window->null.visible) return; - _GLFWwindow* previous = _glfw.null.focusedWindow; + previous = _glfw.null.focusedWindow; _glfw.null.focusedWindow = window; if (previous) { _glfwInputWindowFocus(previous, GLFW_FALSE); if (previous->monitor && previous->autoIconify) - _glfwPlatformIconifyWindow(previous); + _glfwIconifyWindowNull(previous); } _glfwInputWindowFocus(window, GLFW_TRUE); } -int _glfwPlatformWindowFocused(_GLFWwindow* window) +GLFWbool _glfwWindowFocusedNull(_GLFWwindow* window) { return _glfw.null.focusedWindow == window; } -int _glfwPlatformWindowIconified(_GLFWwindow* window) +GLFWbool _glfwWindowIconifiedNull(_GLFWwindow* window) { return window->null.iconified; } -int _glfwPlatformWindowVisible(_GLFWwindow* window) +GLFWbool _glfwWindowVisibleNull(_GLFWwindow* window) { return window->null.visible; } -void _glfwPlatformPollEvents(void) +void _glfwPollEventsNull(void) { } -void _glfwPlatformWaitEvents(void) +void _glfwWaitEventsNull(void) { } -void _glfwPlatformWaitEventsTimeout(double timeout) +void _glfwWaitEventsTimeoutNull(double timeout) { } -void _glfwPlatformPostEmptyEvent(void) +void _glfwPostEmptyEventNull(void) { } -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +void _glfwGetCursorPosNull(_GLFWwindow* window, double* xpos, double* ypos) { if (xpos) *xpos = _glfw.null.xcursor - window->null.xpos; @@ -480,50 +509,71 @@ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) *ypos = _glfw.null.ycursor - window->null.ypos; } -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +void _glfwSetCursorPosNull(_GLFWwindow* window, double x, double y) { _glfw.null.xcursor = window->null.xpos + (int) x; _glfw.null.ycursor = window->null.ypos + (int) y; } -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +void _glfwSetCursorModeNull(_GLFWwindow* window, int mode) { } -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) +GLFWbool _glfwCreateCursorNull(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) { return GLFW_TRUE; } -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +GLFWbool _glfwCreateStandardCursorNull(_GLFWcursor* cursor, int shape) { return GLFW_TRUE; } -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +void _glfwDestroyCursorNull(_GLFWcursor* cursor) { } -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +void _glfwSetCursorNull(_GLFWwindow* window, _GLFWcursor* cursor) { } -void _glfwPlatformSetClipboardString(const char* string) +void _glfwSetClipboardStringNull(const char* string) { char* copy = _glfw_strdup(string); - free(_glfw.null.clipboardString); + _glfw_free(_glfw.null.clipboardString); _glfw.null.clipboardString = copy; } -const char* _glfwPlatformGetClipboardString(void) +const char* _glfwGetClipboardStringNull(void) { return _glfw.null.clipboardString; } -const char* _glfwPlatformGetScancodeName(int scancode) +EGLenum _glfwGetEGLPlatformNull(EGLint** attribs) { + return 0; +} + +EGLNativeDisplayType _glfwGetEGLNativeDisplayNull(void) +{ + return 0; +} + +EGLNativeWindowType _glfwGetEGLNativeWindowNull(_GLFWwindow* window) +{ + return 0; +} + +const char* _glfwGetScancodeNameNull(int scancode) +{ + if (scancode < GLFW_KEY_SPACE || scancode > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); + return NULL; + } + switch (scancode) { case GLFW_KEY_APOSTROPHE: @@ -643,26 +693,26 @@ const char* _glfwPlatformGetScancodeName(int scancode) return NULL; } -int _glfwPlatformGetKeyScancode(int key) +int _glfwGetKeyScancodeNull(int key) { return key; } -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +void _glfwGetRequiredInstanceExtensionsNull(char** extensions) { } -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) +GLFWbool _glfwGetPhysicalDevicePresentationSupportNull(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) { return GLFW_FALSE; } -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) +VkResult _glfwCreateWindowSurfaceNull(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) { // This seems like the most appropriate error to return here return VK_ERROR_EXTENSION_NOT_PRESENT; diff --git a/src/external/glfw/src/osmesa_context.c b/src/external/glfw/src/osmesa_context.c index 70e8675ba..38adabbc0 100644 --- a/src/external/glfw/src/osmesa_context.c +++ b/src/external/glfw/src/osmesa_context.c @@ -39,17 +39,17 @@ static void makeContextCurrentOSMesa(_GLFWwindow* window) if (window) { int width, height; - _glfwPlatformGetFramebufferSize(window, &width, &height); + _glfw.platform.getFramebufferSize(window, &width, &height); // Check to see if we need to allocate a new buffer if ((window->context.osmesa.buffer == NULL) || (width != window->context.osmesa.width) || (height != window->context.osmesa.height)) { - free(window->context.osmesa.buffer); + _glfw_free(window->context.osmesa.buffer); // Allocate the new buffer (width * height * 8-bit RGBA) - window->context.osmesa.buffer = calloc(4, (size_t) width * height); + window->context.osmesa.buffer = _glfw_calloc(4, (size_t) width * height); window->context.osmesa.width = width; window->context.osmesa.height = height; } @@ -83,7 +83,7 @@ static void destroyContextOSMesa(_GLFWwindow* window) if (window->context.osmesa.buffer) { - free(window->context.osmesa.buffer); + _glfw_free(window->context.osmesa.buffer); window->context.osmesa.width = 0; window->context.osmesa.height = 0; } @@ -124,6 +124,8 @@ GLFWbool _glfwInitOSMesa(void) "libOSMesa.8.dylib", #elif defined(__CYGWIN__) "libOSMesa-8.so", +#elif defined(__OpenBSD__) || defined(__NetBSD__) + "libOSMesa.so", #else "libOSMesa.so.8", "libOSMesa.so.6", @@ -136,7 +138,7 @@ GLFWbool _glfwInitOSMesa(void) for (i = 0; sonames[i]; i++) { - _glfw.osmesa.handle = _glfw_dlopen(sonames[i]); + _glfw.osmesa.handle = _glfwPlatformLoadModule(sonames[i]); if (_glfw.osmesa.handle) break; } @@ -148,19 +150,19 @@ GLFWbool _glfwInitOSMesa(void) } _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); + _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaCreateContextExt"); _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); + _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); + _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaDestroyContext"); _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); + _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaMakeCurrent"); _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); + _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); + _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); + _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetProcAddress"); if (!_glfw.osmesa.CreateContextExt || !_glfw.osmesa.DestroyContext || @@ -183,12 +185,12 @@ void _glfwTerminateOSMesa(void) { if (_glfw.osmesa.handle) { - _glfw_dlclose(_glfw.osmesa.handle); + _glfwPlatformFreeModule(_glfw.osmesa.handle); _glfw.osmesa.handle = NULL; } } -#define setAttrib(a, v) \ +#define SET_ATTRIB(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ @@ -219,24 +221,24 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, { int index = 0, attribs[40]; - setAttrib(OSMESA_FORMAT, OSMESA_RGBA); - setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); - setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); - setAttrib(OSMESA_ACCUM_BITS, accumBits); + SET_ATTRIB(OSMESA_FORMAT, OSMESA_RGBA); + SET_ATTRIB(OSMESA_DEPTH_BITS, fbconfig->depthBits); + SET_ATTRIB(OSMESA_STENCIL_BITS, fbconfig->stencilBits); + SET_ATTRIB(OSMESA_ACCUM_BITS, accumBits); if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) { - setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + SET_ATTRIB(OSMESA_PROFILE, OSMESA_CORE_PROFILE); } else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) { - setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + SET_ATTRIB(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); } if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); - setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); + SET_ATTRIB(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); + SET_ATTRIB(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); } if (ctxconfig->forward) @@ -246,7 +248,7 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, return GLFW_FALSE; } - setAttrib(0, 0); + SET_ATTRIB(0, 0); window->context.osmesa.handle = OSMesaCreateContextAttribs(attribs, share); @@ -285,7 +287,7 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, return GLFW_TRUE; } -#undef setAttrib +#undef SET_ATTRIB ////////////////////////////////////////////////////////////////////////// @@ -302,6 +304,12 @@ GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + if (window->context.source != GLFW_OSMESA_CONTEXT_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return GLFW_FALSE; + } + if (!OSMesaGetColorBuffer(window->context.osmesa.handle, &mesaWidth, &mesaHeight, &mesaFormat, &mesaBuffer)) @@ -335,6 +343,12 @@ GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + if (window->context.source != GLFW_OSMESA_CONTEXT_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return GLFW_FALSE; + } + if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, &mesaWidth, &mesaHeight, &mesaBytes, &mesaBuffer)) @@ -361,7 +375,7 @@ GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (window->context.client == GLFW_NO_API) + if (window->context.source != GLFW_OSMESA_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; diff --git a/src/external/glfw/src/osmesa_context.h b/src/external/glfw/src/osmesa_context.h deleted file mode 100644 index ce1f1a294..000000000 --- a/src/external/glfw/src/osmesa_context.h +++ /dev/null @@ -1,90 +0,0 @@ -//======================================================================== -// GLFW 3.4 OSMesa - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2016 Google Inc. -// Copyright (c) 2016-2017 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define OSMESA_RGBA 0x1908 -#define OSMESA_FORMAT 0x22 -#define OSMESA_DEPTH_BITS 0x30 -#define OSMESA_STENCIL_BITS 0x31 -#define OSMESA_ACCUM_BITS 0x32 -#define OSMESA_PROFILE 0x33 -#define OSMESA_CORE_PROFILE 0x34 -#define OSMESA_COMPAT_PROFILE 0x35 -#define OSMESA_CONTEXT_MAJOR_VERSION 0x36 -#define OSMESA_CONTEXT_MINOR_VERSION 0x37 - -typedef void* OSMesaContext; -typedef void (*OSMESAproc)(void); - -typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); -typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); -typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); -typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); -typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); -typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); -typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); -#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt -#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs -#define OSMesaDestroyContext _glfw.osmesa.DestroyContext -#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent -#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer -#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer -#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress - -// OSMesa-specific per-context data -// -typedef struct _GLFWcontextOSMesa -{ - OSMesaContext handle; - int width; - int height; - void* buffer; - -} _GLFWcontextOSMesa; - -// OSMesa-specific global data -// -typedef struct _GLFWlibraryOSMesa -{ - void* handle; - - PFN_OSMesaCreateContextExt CreateContextExt; - PFN_OSMesaCreateContextAttribs CreateContextAttribs; - PFN_OSMesaDestroyContext DestroyContext; - PFN_OSMesaMakeCurrent MakeCurrent; - PFN_OSMesaGetColorBuffer GetColorBuffer; - PFN_OSMesaGetDepthBuffer GetDepthBuffer; - PFN_OSMesaGetProcAddress GetProcAddress; - -} _GLFWlibraryOSMesa; - - -GLFWbool _glfwInitOSMesa(void); -void _glfwTerminateOSMesa(void); -GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); - diff --git a/src/external/glfw/src/platform.c b/src/external/glfw/src/platform.c new file mode 100644 index 000000000..24b6ac9bb --- /dev/null +++ b/src/external/glfw/src/platform.c @@ -0,0 +1,189 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2018 Camilla Löwy +// +// 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. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +static const struct +{ + int ID; + GLFWbool (*connect)(int,_GLFWplatform*); +} supportedPlatforms[] = +{ +#if defined(_GLFW_WIN32) + { GLFW_PLATFORM_WIN32, _glfwConnectWin32 }, +#endif +#if defined(_GLFW_COCOA) + { GLFW_PLATFORM_COCOA, _glfwConnectCocoa }, +#endif +#if defined(_GLFW_X11) + { GLFW_PLATFORM_X11, _glfwConnectX11 }, +#endif +#if defined(_GLFW_WAYLAND) + { GLFW_PLATFORM_WAYLAND, _glfwConnectWayland }, +#endif +}; + +GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform) +{ + const size_t count = sizeof(supportedPlatforms) / sizeof(supportedPlatforms[0]); + size_t i; + + if (desiredID != GLFW_ANY_PLATFORM && + desiredID != GLFW_PLATFORM_WIN32 && + desiredID != GLFW_PLATFORM_COCOA && + desiredID != GLFW_PLATFORM_WAYLAND && + desiredID != GLFW_PLATFORM_X11 && + desiredID != GLFW_PLATFORM_NULL) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid platform ID 0x%08X", desiredID); + return GLFW_FALSE; + } + + // Only allow the Null platform if specifically requested + if (desiredID == GLFW_PLATFORM_NULL) + return GLFW_FALSE; //_glfwConnectNull(desiredID, platform); // @raysan5 + else if (count == 0) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "This binary only supports the Null platform"); + return GLFW_FALSE; + } + + if (desiredID == GLFW_ANY_PLATFORM) + { + // If there is exactly one platform available for auto-selection, let it emit the + // error on failure as the platform-specific error description may be more helpful + if (count == 1) + return supportedPlatforms[0].connect(supportedPlatforms[0].ID, platform); + + for (i = 0; i < count; i++) + { + if (supportedPlatforms[i].connect(desiredID, platform)) + return GLFW_TRUE; + } + + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "Failed to detect any supported platform"); + } + else + { + for (i = 0; i < count; i++) + { + if (supportedPlatforms[i].ID == desiredID) + return supportedPlatforms[i].connect(desiredID, platform); + } + + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "The requested platform is not supported"); + } + + return GLFW_FALSE; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetPlatform(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfw.platform.platformID; +} + +GLFWAPI int glfwPlatformSupported(int platformID) +{ + const size_t count = sizeof(supportedPlatforms) / sizeof(supportedPlatforms[0]); + size_t i; + + if (platformID != GLFW_PLATFORM_WIN32 && + platformID != GLFW_PLATFORM_COCOA && + platformID != GLFW_PLATFORM_WAYLAND && + platformID != GLFW_PLATFORM_X11 && + platformID != GLFW_PLATFORM_NULL) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid platform ID 0x%08X", platformID); + return GLFW_FALSE; + } + + if (platformID == GLFW_PLATFORM_NULL) + return GLFW_TRUE; + + for (i = 0; i < count; i++) + { + if (platformID == supportedPlatforms[i].ID) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +GLFWAPI const char* glfwGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER +#if defined(_GLFW_WIN32) + " Win32 WGL" +#endif +#if defined(_GLFW_COCOA) + " Cocoa NSGL" +#endif +#if defined(_GLFW_WAYLAND) + " Wayland" +#endif +#if defined(_GLFW_X11) + " X11 GLX" +#endif + " Null" + " EGL" + " OSMesa" +#if defined(__MINGW64_VERSION_MAJOR) + " MinGW-w64" +#elif defined(__MINGW32__) + " MinGW" +#elif defined(_MSC_VER) + " VisualC" +#endif +#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) + " hybrid-GPU" +#endif +#if defined(_POSIX_MONOTONIC_CLOCK) + " monotonic" +#endif +#if defined(_GLFW_BUILD_DLL) +#if defined(_WIN32) + " DLL" +#elif defined(__APPLE__) + " dynamic" +#else + " shared" +#endif +#endif + ; +} + diff --git a/src/external/glfw/src/platform.h b/src/external/glfw/src/platform.h new file mode 100644 index 000000000..0c593676c --- /dev/null +++ b/src/external/glfw/src/platform.h @@ -0,0 +1,163 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2018 Camilla Löwy +// +// 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 "null_platform.h" + +#if defined(_GLFW_WIN32) + #include "win32_platform.h" +#else + #define GLFW_WIN32_WINDOW_STATE + #define GLFW_WIN32_MONITOR_STATE + #define GLFW_WIN32_CURSOR_STATE + #define GLFW_WIN32_LIBRARY_WINDOW_STATE + #define GLFW_WGL_CONTEXT_STATE + #define GLFW_WGL_LIBRARY_CONTEXT_STATE +#endif + +#if defined(_GLFW_COCOA) + #include "cocoa_platform.h" +#else + #define GLFW_COCOA_WINDOW_STATE + #define GLFW_COCOA_MONITOR_STATE + #define GLFW_COCOA_CURSOR_STATE + #define GLFW_COCOA_LIBRARY_WINDOW_STATE + #define GLFW_NSGL_CONTEXT_STATE + #define GLFW_NSGL_LIBRARY_CONTEXT_STATE +#endif + +#if defined(_GLFW_WAYLAND) + #include "wl_platform.h" +#else + #define GLFW_WAYLAND_WINDOW_STATE + #define GLFW_WAYLAND_MONITOR_STATE + #define GLFW_WAYLAND_CURSOR_STATE + #define GLFW_WAYLAND_LIBRARY_WINDOW_STATE +#endif + +#if defined(_GLFW_X11) + #include "x11_platform.h" +#else + #define GLFW_X11_WINDOW_STATE + #define GLFW_X11_MONITOR_STATE + #define GLFW_X11_CURSOR_STATE + #define GLFW_X11_LIBRARY_WINDOW_STATE + #define GLFW_GLX_CONTEXT_STATE + #define GLFW_GLX_LIBRARY_CONTEXT_STATE +#endif + +#include "null_joystick.h" + +#if defined(_GLFW_WIN32) + #include "win32_joystick.h" +#else + #define GLFW_WIN32_JOYSTICK_STATE + #define GLFW_WIN32_LIBRARY_JOYSTICK_STATE +#endif + +#if defined(_GLFW_COCOA) + #include "cocoa_joystick.h" +#else + #define GLFW_COCOA_JOYSTICK_STATE + #define GLFW_COCOA_LIBRARY_JOYSTICK_STATE +#endif + +#if (defined(_GLFW_X11) || defined(_GLFW_WAYLAND)) && defined(__linux__) + #include "linux_joystick.h" +#else + #define GLFW_LINUX_JOYSTICK_STATE + #define GLFW_LINUX_LIBRARY_JOYSTICK_STATE +#endif + +#define GLFW_PLATFORM_WINDOW_STATE \ + GLFW_WIN32_WINDOW_STATE \ + GLFW_COCOA_WINDOW_STATE \ + GLFW_WAYLAND_WINDOW_STATE \ + GLFW_X11_WINDOW_STATE \ + GLFW_NULL_WINDOW_STATE \ + +#define GLFW_PLATFORM_MONITOR_STATE \ + GLFW_WIN32_MONITOR_STATE \ + GLFW_COCOA_MONITOR_STATE \ + GLFW_WAYLAND_MONITOR_STATE \ + GLFW_X11_MONITOR_STATE \ + GLFW_NULL_MONITOR_STATE \ + +#define GLFW_PLATFORM_CURSOR_STATE \ + GLFW_WIN32_CURSOR_STATE \ + GLFW_COCOA_CURSOR_STATE \ + GLFW_WAYLAND_CURSOR_STATE \ + GLFW_X11_CURSOR_STATE \ + GLFW_NULL_CURSOR_STATE \ + +#define GLFW_PLATFORM_JOYSTICK_STATE \ + GLFW_WIN32_JOYSTICK_STATE \ + GLFW_COCOA_JOYSTICK_STATE \ + GLFW_LINUX_JOYSTICK_STATE + +#define GLFW_PLATFORM_LIBRARY_WINDOW_STATE \ + GLFW_WIN32_LIBRARY_WINDOW_STATE \ + GLFW_COCOA_LIBRARY_WINDOW_STATE \ + GLFW_WAYLAND_LIBRARY_WINDOW_STATE \ + GLFW_X11_LIBRARY_WINDOW_STATE \ + GLFW_NULL_LIBRARY_WINDOW_STATE \ + +#define GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ + GLFW_WIN32_LIBRARY_JOYSTICK_STATE \ + GLFW_COCOA_LIBRARY_JOYSTICK_STATE \ + GLFW_LINUX_LIBRARY_JOYSTICK_STATE + +#define GLFW_PLATFORM_CONTEXT_STATE \ + GLFW_WGL_CONTEXT_STATE \ + GLFW_NSGL_CONTEXT_STATE \ + GLFW_GLX_CONTEXT_STATE + +#define GLFW_PLATFORM_LIBRARY_CONTEXT_STATE \ + GLFW_WGL_LIBRARY_CONTEXT_STATE \ + GLFW_NSGL_LIBRARY_CONTEXT_STATE \ + GLFW_GLX_LIBRARY_CONTEXT_STATE + +#if defined(_WIN32) + #include "win32_thread.h" + #define GLFW_PLATFORM_TLS_STATE GLFW_WIN32_TLS_STATE + #define GLFW_PLATFORM_MUTEX_STATE GLFW_WIN32_MUTEX_STATE +#else + #include "posix_thread.h" + #define GLFW_PLATFORM_TLS_STATE GLFW_POSIX_TLS_STATE + #define GLFW_PLATFORM_MUTEX_STATE GLFW_POSIX_MUTEX_STATE +#endif + +#if defined(_WIN32) + #include "win32_time.h" + #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_WIN32_LIBRARY_TIMER_STATE +#elif defined(__APPLE__) + #include "cocoa_time.h" + #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_COCOA_LIBRARY_TIMER_STATE +#else + #include "posix_time.h" + #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_POSIX_LIBRARY_TIMER_STATE +#endif + diff --git a/src/external/glfw/src/posix_module.c b/src/external/glfw/src/posix_module.c new file mode 100644 index 000000000..7079e5b45 --- /dev/null +++ b/src/external/glfw/src/posix_module.c @@ -0,0 +1,51 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2021 Camilla Löwy +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void* _glfwPlatformLoadModule(const char* path) +{ + return dlopen(path, RTLD_LAZY | RTLD_LOCAL); +} + +void _glfwPlatformFreeModule(void* module) +{ + dlclose(module); +} + +GLFWproc _glfwPlatformGetModuleSymbol(void* module, const char* name) +{ + return dlsym(module, name); +} + diff --git a/src/external/glfw/src/posix_poll.c b/src/external/glfw/src/posix_poll.c new file mode 100644 index 000000000..676a8a510 --- /dev/null +++ b/src/external/glfw/src/posix_poll.c @@ -0,0 +1,81 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2022 Camilla Löwy +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#define _GNU_SOURCE + +#include "internal.h" + +#include +#include +#include + +GLFWbool _glfwPollPOSIX(struct pollfd* fds, nfds_t count, double* timeout) +{ + for (;;) + { + if (timeout) + { + const uint64_t base = _glfwPlatformGetTimerValue(); + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) + const time_t seconds = (time_t) *timeout; + const long nanoseconds = (long) ((*timeout - seconds) * 1e9); + const struct timespec ts = { seconds, nanoseconds }; + const int result = ppoll(fds, count, &ts, NULL); +#elif defined(__NetBSD__) + const time_t seconds = (time_t) *timeout; + const long nanoseconds = (long) ((*timeout - seconds) * 1e9); + const struct timespec ts = { seconds, nanoseconds }; + const int result = pollts(fds, count, &ts, NULL); +#else + const int milliseconds = (int) (*timeout * 1e3); + const int result = poll(fds, count, milliseconds); +#endif + const int error = errno; // clock_gettime may overwrite our error + + *timeout -= (_glfwPlatformGetTimerValue() - base) / + (double) _glfwPlatformGetTimerFrequency(); + + if (result > 0) + return GLFW_TRUE; + else if (result == -1 && error != EINTR && error != EAGAIN) + return GLFW_FALSE; + else if (*timeout <= 0.0) + return GLFW_FALSE; + } + else + { + const int result = poll(fds, count, -1); + if (result > 0) + return GLFW_TRUE; + else if (result == -1 && errno != EINTR && errno != EAGAIN) + return GLFW_FALSE; + } + } +} + diff --git a/src/external/glfw/src/posix_poll.h b/src/external/glfw/src/posix_poll.h new file mode 100644 index 000000000..1effd1cd3 --- /dev/null +++ b/src/external/glfw/src/posix_poll.h @@ -0,0 +1,32 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2022 Camilla Löwy +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include + +GLFWbool _glfwPollPOSIX(struct pollfd* fds, nfds_t count, double* timeout); + diff --git a/src/external/glfw/src/posix_thread.h b/src/external/glfw/src/posix_thread.h index 85ce596c9..5a5d7b7c3 100644 --- a/src/external/glfw/src/posix_thread.h +++ b/src/external/glfw/src/posix_thread.h @@ -27,8 +27,8 @@ #include -#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix -#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix +#define GLFW_POSIX_TLS_STATE _GLFWtlsPOSIX posix; +#define GLFW_POSIX_MUTEX_STATE _GLFWmutexPOSIX posix; // POSIX-specific thread local storage data @@ -37,7 +37,6 @@ typedef struct _GLFWtlsPOSIX { GLFWbool allocated; pthread_key_t key; - } _GLFWtlsPOSIX; // POSIX-specific mutex data @@ -46,6 +45,5 @@ typedef struct _GLFWmutexPOSIX { GLFWbool allocated; pthread_mutex_t handle; - } _GLFWmutexPOSIX; diff --git a/src/external/glfw/src/posix_time.c b/src/external/glfw/src/posix_time.c index ae3d5c789..f134be47e 100644 --- a/src/external/glfw/src/posix_time.c +++ b/src/external/glfw/src/posix_time.c @@ -27,60 +27,33 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== -#define _POSIX_C_SOURCE 199309L - #include "internal.h" #include #include -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialise timer -// -void _glfwInitTimerPOSIX(void) -{ -#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) - { - _glfw.timer.posix.monotonic = GLFW_TRUE; - _glfw.timer.posix.frequency = 1000000000; - } - else -#endif - { - _glfw.timer.posix.monotonic = GLFW_FALSE; - _glfw.timer.posix.frequency = 1000000; - } -} ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// +void _glfwPlatformInitTimer(void) +{ + _glfw.timer.posix.clock = CLOCK_REALTIME; + _glfw.timer.posix.frequency = 1000000000; + +#if defined(_POSIX_MONOTONIC_CLOCK) + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + _glfw.timer.posix.clock = CLOCK_MONOTONIC; +#endif +} + uint64_t _glfwPlatformGetTimerValue(void) { -#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) - if (_glfw.timer.posix.monotonic) - { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; - } - else -#endif - { - struct timeval tv; - gettimeofday(&tv, NULL); - return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; - } + struct timespec ts; + clock_gettime(_glfw.timer.posix.clock, &ts); + return (uint64_t) ts.tv_sec * _glfw.timer.posix.frequency + (uint64_t) ts.tv_nsec; } uint64_t _glfwPlatformGetTimerFrequency(void) diff --git a/src/external/glfw/src/posix_time.h b/src/external/glfw/src/posix_time.h index 9b59a1879..94374adb8 100644 --- a/src/external/glfw/src/posix_time.h +++ b/src/external/glfw/src/posix_time.h @@ -25,20 +25,17 @@ // //======================================================================== -#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix +#define GLFW_POSIX_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix; #include +#include // POSIX-specific global timer data // typedef struct _GLFWtimerPOSIX { - GLFWbool monotonic; + clockid_t clock; uint64_t frequency; - } _GLFWtimerPOSIX; - -void _glfwInitTimerPOSIX(void); - diff --git a/src/external/glfw/src/vulkan.c b/src/external/glfw/src/vulkan.c index b5340520b..64a4650fc 100644 --- a/src/external/glfw/src/vulkan.c +++ b/src/external/glfw/src/vulkan.c @@ -45,45 +45,52 @@ GLFWbool _glfwInitVulkan(int mode) { VkResult err; VkExtensionProperties* ep; + PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; uint32_t i, count; if (_glfw.vk.available) return GLFW_TRUE; -#if !defined(_GLFW_VULKAN_STATIC) + if (_glfw.hints.init.vulkanLoader) + _glfw.vk.GetInstanceProcAddr = _glfw.hints.init.vulkanLoader; + else + { #if defined(_GLFW_VULKAN_LIBRARY) - _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); + _glfw.vk.handle = _glfwPlatformLoadModule(_GLFW_VULKAN_LIBRARY); #elif defined(_GLFW_WIN32) - _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); + _glfw.vk.handle = _glfwPlatformLoadModule("vulkan-1.dll"); #elif defined(_GLFW_COCOA) - _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib"); - if (!_glfw.vk.handle) - _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS(); + _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.1.dylib"); + if (!_glfw.vk.handle) + _glfw.vk.handle = _glfwLoadLocalVulkanLoaderCocoa(); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.so"); #else - _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); + _glfw.vk.handle = _glfwPlatformLoadModule("libvulkan.so.1"); #endif - if (!_glfw.vk.handle) - { - if (mode == _GLFW_REQUIRE_LOADER) - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); + if (!_glfw.vk.handle) + { + if (mode == _GLFW_REQUIRE_LOADER) + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); - return GLFW_FALSE; + return GLFW_FALSE; + } + + _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) + _glfwPlatformGetModuleSymbol(_glfw.vk.handle, "vkGetInstanceProcAddr"); + if (!_glfw.vk.GetInstanceProcAddr) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Loader does not export vkGetInstanceProcAddr"); + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } } - _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) - _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); - if (!_glfw.vk.GetInstanceProcAddr) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Vulkan: Loader does not export vkGetInstanceProcAddr"); - - _glfwTerminateVulkan(); - return GLFW_FALSE; - } - - _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) + vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); - if (!_glfw.vk.EnumerateInstanceExtensionProperties) + if (!vkEnumerateInstanceExtensionProperties) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties"); @@ -91,7 +98,6 @@ GLFWbool _glfwInitVulkan(int mode) _glfwTerminateVulkan(); return GLFW_FALSE; } -#endif // _GLFW_VULKAN_STATIC err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); if (err) @@ -108,7 +114,7 @@ GLFWbool _glfwInitVulkan(int mode) return GLFW_FALSE; } - ep = calloc(count, sizeof(VkExtensionProperties)); + ep = _glfw_calloc(count, sizeof(VkExtensionProperties)); err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); if (err) @@ -117,7 +123,7 @@ GLFWbool _glfwInitVulkan(int mode) "Vulkan: Failed to query instance extensions: %s", _glfwGetVulkanResultString(err)); - free(ep); + _glfw_free(ep); _glfwTerminateVulkan(); return GLFW_FALSE; } @@ -126,40 +132,33 @@ GLFWbool _glfwInitVulkan(int mode) { if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) _glfw.vk.KHR_surface = GLFW_TRUE; -#if defined(_GLFW_WIN32) else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) _glfw.vk.KHR_win32_surface = GLFW_TRUE; -#elif defined(_GLFW_COCOA) else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) _glfw.vk.MVK_macos_surface = GLFW_TRUE; else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0) _glfw.vk.EXT_metal_surface = GLFW_TRUE; -#elif defined(_GLFW_X11) else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) _glfw.vk.KHR_xlib_surface = GLFW_TRUE; else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) _glfw.vk.KHR_xcb_surface = GLFW_TRUE; -#elif defined(_GLFW_WAYLAND) else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) _glfw.vk.KHR_wayland_surface = GLFW_TRUE; -#endif } - free(ep); + _glfw_free(ep); _glfw.vk.available = GLFW_TRUE; - _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); + _glfw.platform.getRequiredInstanceExtensions(_glfw.vk.extensions); return GLFW_TRUE; } void _glfwTerminateVulkan(void) { -#if !defined(_GLFW_VULKAN_STATIC) if (_glfw.vk.handle) - _glfw_dlclose(_glfw.vk.handle); -#endif + _glfwPlatformFreeModule(_glfw.vk.handle); } const char* _glfwGetVulkanResultString(VkResult result) @@ -257,17 +256,16 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; + // NOTE: Vulkan 1.0 and 1.1 vkGetInstanceProcAddr cannot return itself + if (strcmp(procname, "vkGetInstanceProcAddr") == 0) + return (GLFWvkproc) vkGetInstanceProcAddr; + proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); -#if defined(_GLFW_VULKAN_STATIC) if (!proc) { - if (strcmp(procname, "vkGetInstanceProcAddr") == 0) - return (GLFWvkproc) vkGetInstanceProcAddr; + if (_glfw.vk.handle) + proc = (GLFWvkproc) _glfwPlatformGetModuleSymbol(_glfw.vk.handle, procname); } -#else - if (!proc) - proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); -#endif return proc; } @@ -291,9 +289,9 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, return GLFW_FALSE; } - return _glfwPlatformGetPhysicalDevicePresentationSupport(instance, - device, - queuefamily); + return _glfw.platform.getPhysicalDevicePresentationSupport(instance, + device, + queuefamily); } GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, @@ -327,6 +325,6 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR; } - return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface); + return _glfw.platform.createWindowSurface(instance, window, allocator, surface); } diff --git a/src/external/glfw/src/wgl_context.c b/src/external/glfw/src/wgl_context.c index 5b69f8bce..4a5e77a81 100644 --- a/src/external/glfw/src/wgl_context.c +++ b/src/external/glfw/src/wgl_context.c @@ -30,15 +30,14 @@ #include "internal.h" #include -#include #include // Return the value corresponding to the specified attribute // -static int findPixelFormatAttribValue(const int* attribs, - int attribCount, - const int* values, - int attrib) +static int findPixelFormatAttribValueWGL(const int* attribs, + int attribCount, + const int* values, + int attrib) { int i; @@ -53,19 +52,19 @@ static int findPixelFormatAttribValue(const int* attribs, return 0; } -#define addAttrib(a) \ +#define ADD_ATTRIB(a) \ { \ assert((size_t) attribCount < sizeof(attribs) / sizeof(attribs[0])); \ attribs[attribCount++] = a; \ } -#define findAttribValue(a) \ - findPixelFormatAttribValue(attribs, attribCount, values, a) +#define FIND_ATTRIB_VALUE(a) \ + findPixelFormatAttribValueWGL(attribs, attribCount, values, a) // Return a list of available and usable framebuffer configs // -static int choosePixelFormat(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) +static int choosePixelFormatWGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) { _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; @@ -73,64 +72,52 @@ static int choosePixelFormat(_GLFWwindow* window, int attribs[40]; int values[sizeof(attribs) / sizeof(attribs[0])]; + nativeCount = DescribePixelFormat(window->context.wgl.dc, + 1, + sizeof(PIXELFORMATDESCRIPTOR), + NULL); + if (_glfw.wgl.ARB_pixel_format) { - const int attrib = WGL_NUMBER_PIXEL_FORMATS_ARB; - - if (!wglGetPixelFormatAttribivARB(window->context.wgl.dc, - 1, 0, 1, &attrib, &nativeCount)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve pixel format attribute"); - return 0; - } - - addAttrib(WGL_SUPPORT_OPENGL_ARB); - addAttrib(WGL_DRAW_TO_WINDOW_ARB); - addAttrib(WGL_PIXEL_TYPE_ARB); - addAttrib(WGL_ACCELERATION_ARB); - addAttrib(WGL_RED_BITS_ARB); - addAttrib(WGL_RED_SHIFT_ARB); - addAttrib(WGL_GREEN_BITS_ARB); - addAttrib(WGL_GREEN_SHIFT_ARB); - addAttrib(WGL_BLUE_BITS_ARB); - addAttrib(WGL_BLUE_SHIFT_ARB); - addAttrib(WGL_ALPHA_BITS_ARB); - addAttrib(WGL_ALPHA_SHIFT_ARB); - addAttrib(WGL_DEPTH_BITS_ARB); - addAttrib(WGL_STENCIL_BITS_ARB); - addAttrib(WGL_ACCUM_BITS_ARB); - addAttrib(WGL_ACCUM_RED_BITS_ARB); - addAttrib(WGL_ACCUM_GREEN_BITS_ARB); - addAttrib(WGL_ACCUM_BLUE_BITS_ARB); - addAttrib(WGL_ACCUM_ALPHA_BITS_ARB); - addAttrib(WGL_AUX_BUFFERS_ARB); - addAttrib(WGL_STEREO_ARB); - addAttrib(WGL_DOUBLE_BUFFER_ARB); + ADD_ATTRIB(WGL_SUPPORT_OPENGL_ARB); + ADD_ATTRIB(WGL_DRAW_TO_WINDOW_ARB); + ADD_ATTRIB(WGL_PIXEL_TYPE_ARB); + ADD_ATTRIB(WGL_ACCELERATION_ARB); + ADD_ATTRIB(WGL_RED_BITS_ARB); + ADD_ATTRIB(WGL_RED_SHIFT_ARB); + ADD_ATTRIB(WGL_GREEN_BITS_ARB); + ADD_ATTRIB(WGL_GREEN_SHIFT_ARB); + ADD_ATTRIB(WGL_BLUE_BITS_ARB); + ADD_ATTRIB(WGL_BLUE_SHIFT_ARB); + ADD_ATTRIB(WGL_ALPHA_BITS_ARB); + ADD_ATTRIB(WGL_ALPHA_SHIFT_ARB); + ADD_ATTRIB(WGL_DEPTH_BITS_ARB); + ADD_ATTRIB(WGL_STENCIL_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_RED_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_GREEN_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_BLUE_BITS_ARB); + ADD_ATTRIB(WGL_ACCUM_ALPHA_BITS_ARB); + ADD_ATTRIB(WGL_AUX_BUFFERS_ARB); + ADD_ATTRIB(WGL_STEREO_ARB); + ADD_ATTRIB(WGL_DOUBLE_BUFFER_ARB); if (_glfw.wgl.ARB_multisample) - addAttrib(WGL_SAMPLES_ARB); + ADD_ATTRIB(WGL_SAMPLES_ARB); if (ctxconfig->client == GLFW_OPENGL_API) { if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) - addAttrib(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); + ADD_ATTRIB(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); } else { if (_glfw.wgl.EXT_colorspace) - addAttrib(WGL_COLORSPACE_EXT); + ADD_ATTRIB(WGL_COLORSPACE_EXT); } } - else - { - nativeCount = DescribePixelFormat(window->context.wgl.dc, - 1, - sizeof(PIXELFORMATDESCRIPTOR), - NULL); - } - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableConfigs = _glfw_calloc(nativeCount, sizeof(_GLFWfbconfig)); for (i = 0; i < nativeCount; i++) { @@ -149,52 +136,52 @@ static int choosePixelFormat(_GLFWwindow* window, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to retrieve pixel format attributes"); - free(usableConfigs); + _glfw_free(usableConfigs); return 0; } - if (!findAttribValue(WGL_SUPPORT_OPENGL_ARB) || - !findAttribValue(WGL_DRAW_TO_WINDOW_ARB)) + if (!FIND_ATTRIB_VALUE(WGL_SUPPORT_OPENGL_ARB) || + !FIND_ATTRIB_VALUE(WGL_DRAW_TO_WINDOW_ARB)) { continue; } - if (findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) + if (FIND_ATTRIB_VALUE(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) continue; - if (findAttribValue(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) + if (FIND_ATTRIB_VALUE(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) continue; - if (findAttribValue(WGL_DOUBLE_BUFFER_ARB) != fbconfig->doublebuffer) + if (FIND_ATTRIB_VALUE(WGL_DOUBLE_BUFFER_ARB) != fbconfig->doublebuffer) continue; - u->redBits = findAttribValue(WGL_RED_BITS_ARB); - u->greenBits = findAttribValue(WGL_GREEN_BITS_ARB); - u->blueBits = findAttribValue(WGL_BLUE_BITS_ARB); - u->alphaBits = findAttribValue(WGL_ALPHA_BITS_ARB); + u->redBits = FIND_ATTRIB_VALUE(WGL_RED_BITS_ARB); + u->greenBits = FIND_ATTRIB_VALUE(WGL_GREEN_BITS_ARB); + u->blueBits = FIND_ATTRIB_VALUE(WGL_BLUE_BITS_ARB); + u->alphaBits = FIND_ATTRIB_VALUE(WGL_ALPHA_BITS_ARB); - u->depthBits = findAttribValue(WGL_DEPTH_BITS_ARB); - u->stencilBits = findAttribValue(WGL_STENCIL_BITS_ARB); + u->depthBits = FIND_ATTRIB_VALUE(WGL_DEPTH_BITS_ARB); + u->stencilBits = FIND_ATTRIB_VALUE(WGL_STENCIL_BITS_ARB); - u->accumRedBits = findAttribValue(WGL_ACCUM_RED_BITS_ARB); - u->accumGreenBits = findAttribValue(WGL_ACCUM_GREEN_BITS_ARB); - u->accumBlueBits = findAttribValue(WGL_ACCUM_BLUE_BITS_ARB); - u->accumAlphaBits = findAttribValue(WGL_ACCUM_ALPHA_BITS_ARB); + u->accumRedBits = FIND_ATTRIB_VALUE(WGL_ACCUM_RED_BITS_ARB); + u->accumGreenBits = FIND_ATTRIB_VALUE(WGL_ACCUM_GREEN_BITS_ARB); + u->accumBlueBits = FIND_ATTRIB_VALUE(WGL_ACCUM_BLUE_BITS_ARB); + u->accumAlphaBits = FIND_ATTRIB_VALUE(WGL_ACCUM_ALPHA_BITS_ARB); - u->auxBuffers = findAttribValue(WGL_AUX_BUFFERS_ARB); + u->auxBuffers = FIND_ATTRIB_VALUE(WGL_AUX_BUFFERS_ARB); - if (findAttribValue(WGL_STEREO_ARB)) + if (FIND_ATTRIB_VALUE(WGL_STEREO_ARB)) u->stereo = GLFW_TRUE; if (_glfw.wgl.ARB_multisample) - u->samples = findAttribValue(WGL_SAMPLES_ARB); + u->samples = FIND_ATTRIB_VALUE(WGL_SAMPLES_ARB); if (ctxconfig->client == GLFW_OPENGL_API) { if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) { - if (findAttribValue(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) + if (FIND_ATTRIB_VALUE(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) u->sRGB = GLFW_TRUE; } } @@ -202,7 +189,7 @@ static int choosePixelFormat(_GLFWwindow* window, { if (_glfw.wgl.EXT_colorspace) { - if (findAttribValue(WGL_COLORSPACE_EXT) == WGL_COLORSPACE_SRGB_EXT) + if (FIND_ATTRIB_VALUE(WGL_COLORSPACE_EXT) == WGL_COLORSPACE_SRGB_EXT) u->sRGB = GLFW_TRUE; } } @@ -221,7 +208,7 @@ static int choosePixelFormat(_GLFWwindow* window, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to describe pixel format"); - free(usableConfigs); + _glfw_free(usableConfigs); return 0; } @@ -271,7 +258,7 @@ static int choosePixelFormat(_GLFWwindow* window, _glfwInputError(GLFW_API_UNAVAILABLE, "WGL: The driver does not appear to support OpenGL"); - free(usableConfigs); + _glfw_free(usableConfigs); return 0; } @@ -281,18 +268,18 @@ static int choosePixelFormat(_GLFWwindow* window, _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "WGL: Failed to find a suitable pixel format"); - free(usableConfigs); + _glfw_free(usableConfigs); return 0; } pixelFormat = (int) closest->handle; - free(usableConfigs); + _glfw_free(usableConfigs); return pixelFormat; } -#undef addAttrib -#undef findAttribValue +#undef ADD_ATTRIB +#undef FIND_ATTRIB_VALUE static void makeContextCurrentWGL(_GLFWwindow* window) { @@ -323,14 +310,12 @@ static void swapBuffersWGL(_GLFWwindow* window) { if (!window->monitor) { - if (IsWindowsVistaOrGreater()) + // HACK: Use DwmFlush when desktop composition is enabled on Windows Vista and 7 + if (!IsWindows8OrGreater() && IsWindowsVistaOrGreater()) { - // DWM Composition is always enabled on Win8+ - BOOL enabled = IsWindows8OrGreater(); + BOOL enabled = FALSE; - // HACK: Use DwmFlush when desktop composition is enabled - if (enabled || - (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) { int count = abs(window->context.wgl.interval); while (count--) @@ -350,15 +335,13 @@ static void swapIntervalWGL(int interval) if (!window->monitor) { - if (IsWindowsVistaOrGreater()) + // HACK: Disable WGL swap interval when desktop composition is enabled on Windows + // Vista and 7 to avoid interfering with DWM vsync + if (!IsWindows8OrGreater() && IsWindowsVistaOrGreater()) { - // DWM Composition is always enabled on Win8+ - BOOL enabled = IsWindows8OrGreater(); + BOOL enabled = FALSE; - // HACK: Disable WGL swap interval when desktop composition is enabled to - // avoid interfering with DWM vsync - if (enabled || - (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) interval = 0; } } @@ -388,7 +371,7 @@ static GLFWglproc getProcAddressWGL(const char* procname) if (proc) return proc; - return (GLFWglproc) GetProcAddress(_glfw.wgl.instance, procname); + return (GLFWglproc) _glfwPlatformGetModuleSymbol(_glfw.wgl.instance, procname); } static void destroyContextWGL(_GLFWwindow* window) @@ -400,11 +383,6 @@ static void destroyContextWGL(_GLFWwindow* window) } } - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - // Initialize WGL // GLFWbool _glfwInitWGL(void) @@ -416,7 +394,7 @@ GLFWbool _glfwInitWGL(void) if (_glfw.wgl.instance) return GLFW_TRUE; - _glfw.wgl.instance = LoadLibraryA("opengl32.dll"); + _glfw.wgl.instance = _glfwPlatformLoadModule("opengl32.dll"); if (!_glfw.wgl.instance) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, @@ -425,19 +403,19 @@ GLFWbool _glfwInitWGL(void) } _glfw.wgl.CreateContext = (PFN_wglCreateContext) - GetProcAddress(_glfw.wgl.instance, "wglCreateContext"); + _glfwPlatformGetModuleSymbol(_glfw.wgl.instance, "wglCreateContext"); _glfw.wgl.DeleteContext = (PFN_wglDeleteContext) - GetProcAddress(_glfw.wgl.instance, "wglDeleteContext"); + _glfwPlatformGetModuleSymbol(_glfw.wgl.instance, "wglDeleteContext"); _glfw.wgl.GetProcAddress = (PFN_wglGetProcAddress) - GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress"); + _glfwPlatformGetModuleSymbol(_glfw.wgl.instance, "wglGetProcAddress"); _glfw.wgl.GetCurrentDC = (PFN_wglGetCurrentDC) - GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC"); + _glfwPlatformGetModuleSymbol(_glfw.wgl.instance, "wglGetCurrentDC"); _glfw.wgl.GetCurrentContext = (PFN_wglGetCurrentContext) - GetProcAddress(_glfw.wgl.instance, "wglGetCurrentContext"); + _glfwPlatformGetModuleSymbol(_glfw.wgl.instance, "wglGetCurrentContext"); _glfw.wgl.MakeCurrent = (PFN_wglMakeCurrent) - GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent"); + _glfwPlatformGetModuleSymbol(_glfw.wgl.instance, "wglMakeCurrent"); _glfw.wgl.ShareLists = (PFN_wglShareLists) - GetProcAddress(_glfw.wgl.instance, "wglShareLists"); + _glfwPlatformGetModuleSymbol(_glfw.wgl.instance, "wglShareLists"); // NOTE: A dummy context has to be created for opengl32.dll to load the // OpenGL ICD, from which we can then query WGL extensions @@ -530,10 +508,10 @@ GLFWbool _glfwInitWGL(void) void _glfwTerminateWGL(void) { if (_glfw.wgl.instance) - FreeLibrary(_glfw.wgl.instance); + _glfwPlatformFreeModule(_glfw.wgl.instance); } -#define setAttrib(a, v) \ +#define SET_ATTRIB(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ @@ -562,7 +540,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, return GLFW_FALSE; } - pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig); + pixelFormat = choosePixelFormatWGL(window, ctxconfig, fbconfig); if (!pixelFormat) return GLFW_FALSE; @@ -641,13 +619,13 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_NO_RESET_NOTIFICATION_ARB); + SET_ATTRIB(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_LOSE_CONTEXT_ON_RESET_ARB); + SET_ATTRIB(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_LOSE_CONTEXT_ON_RESET_ARB); } flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; @@ -660,13 +638,13 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + SET_ATTRIB(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + SET_ATTRIB(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } @@ -674,7 +652,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, if (ctxconfig->noerror) { if (_glfw.wgl.ARB_create_context_no_error) - setAttrib(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); + SET_ATTRIB(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); } // NOTE: Only request an explicitly versioned context when necessary, as @@ -682,17 +660,17 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setAttrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setAttrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (flags) - setAttrib(WGL_CONTEXT_FLAGS_ARB, flags); + SET_ATTRIB(WGL_CONTEXT_FLAGS_ARB, flags); if (mask) - setAttrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); + SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, mask); - setAttrib(0, 0); + SET_ATTRIB(0, 0); window->context.wgl.handle = wglCreateContextAttribsARB(window->context.wgl.dc, share, attribs); @@ -775,19 +753,21 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, return GLFW_TRUE; } -#undef setAttrib - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// +#undef SET_ATTRIB GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (window->context.client == GLFW_NO_API) + if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, + "WGL: Platform not initialized"); + return NULL; + } + + if (window->context.source != GLFW_NATIVE_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; diff --git a/src/external/glfw/src/wgl_context.h b/src/external/glfw/src/wgl_context.h deleted file mode 100644 index 2cf7e4e5c..000000000 --- a/src/external/glfw/src/wgl_context.h +++ /dev/null @@ -1,160 +0,0 @@ -//======================================================================== -// GLFW 3.4 WGL - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2018 Camilla Löwy -// -// 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. -// -//======================================================================== - -#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_PIXEL_TYPE_ARB 0x2013 -#define WGL_TYPE_RGBA_ARB 0x202b -#define WGL_ACCELERATION_ARB 0x2003 -#define WGL_NO_ACCELERATION_ARB 0x2025 -#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_STENCIL_BITS_ARB 0x2023 -#define WGL_AUX_BUFFERS_ARB 0x2024 -#define WGL_STEREO_ARB 0x2012 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_SAMPLES_ARB 0x2042 -#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 -#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 -#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 -#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_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 -#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 -#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 -#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 -#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 -#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 -#define WGL_COLORSPACE_EXT 0x309d -#define WGL_COLORSPACE_SRGB_EXT 0x3089 - -#define ERROR_INVALID_VERSION_ARB 0x2095 -#define ERROR_INVALID_PROFILE_ARB 0x2096 -#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 - -// WGL extension pointer typedefs -typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); -typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); -typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); -typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); -typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); -#define wglSwapIntervalEXT _glfw.wgl.SwapIntervalEXT -#define wglGetPixelFormatAttribivARB _glfw.wgl.GetPixelFormatAttribivARB -#define wglGetExtensionsStringEXT _glfw.wgl.GetExtensionsStringEXT -#define wglGetExtensionsStringARB _glfw.wgl.GetExtensionsStringARB -#define wglCreateContextAttribsARB _glfw.wgl.CreateContextAttribsARB - -// opengl32.dll function pointer typedefs -typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); -typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); -typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); -typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); -typedef HGLRC (WINAPI * PFN_wglGetCurrentContext)(void); -typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); -typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); -#define wglCreateContext _glfw.wgl.CreateContext -#define wglDeleteContext _glfw.wgl.DeleteContext -#define wglGetProcAddress _glfw.wgl.GetProcAddress -#define wglGetCurrentDC _glfw.wgl.GetCurrentDC -#define wglGetCurrentContext _glfw.wgl.GetCurrentContext -#define wglMakeCurrent _glfw.wgl.MakeCurrent -#define wglShareLists _glfw.wgl.ShareLists - -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl - - -// WGL-specific per-context data -// -typedef struct _GLFWcontextWGL -{ - HDC dc; - HGLRC handle; - int interval; - -} _GLFWcontextWGL; - -// WGL-specific global data -// -typedef struct _GLFWlibraryWGL -{ - HINSTANCE instance; - PFN_wglCreateContext CreateContext; - PFN_wglDeleteContext DeleteContext; - PFN_wglGetProcAddress GetProcAddress; - PFN_wglGetCurrentDC GetCurrentDC; - PFN_wglGetCurrentContext GetCurrentContext; - PFN_wglMakeCurrent MakeCurrent; - PFN_wglShareLists ShareLists; - - PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; - PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; - PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; - PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; - PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; - GLFWbool EXT_swap_control; - GLFWbool EXT_colorspace; - GLFWbool ARB_multisample; - GLFWbool ARB_framebuffer_sRGB; - GLFWbool EXT_framebuffer_sRGB; - GLFWbool ARB_pixel_format; - GLFWbool ARB_create_context; - GLFWbool ARB_create_context_profile; - GLFWbool EXT_create_context_es2_profile; - GLFWbool ARB_create_context_robustness; - GLFWbool ARB_create_context_no_error; - GLFWbool ARB_context_flush_control; - -} _GLFWlibraryWGL; - - -GLFWbool _glfwInitWGL(void); -void _glfwTerminateWGL(void); -GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig); - diff --git a/src/external/glfw/src/win32_init.c b/src/external/glfw/src/win32_init.c index 970da06ea..8704150c5 100644 --- a/src/external/glfw/src/win32_init.c +++ b/src/external/glfw/src/win32_init.c @@ -30,7 +30,6 @@ #include "internal.h" #include -#include static const GUID _glfw_GUID_DEVINTERFACE_HID = {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; @@ -40,7 +39,7 @@ static const GUID _glfw_GUID_DEVINTERFACE_HID = #if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) #if defined(_GLFW_BUILD_DLL) - #warning "These symbols must be exported by the executable and have no effect in a DLL" + #pragma message("These symbols must be exported by the executable and have no effect in a DLL") #endif // Executables (but not DLLs) exporting this symbol with this value will be @@ -72,18 +71,17 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) // static GLFWbool loadLibraries(void) { - _glfw.win32.winmm.instance = LoadLibraryA("winmm.dll"); - if (!_glfw.win32.winmm.instance) + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (const WCHAR*) &_glfw, + (HMODULE*) &_glfw.win32.instance)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to load winmm.dll"); + "Win32: Failed to retrieve own module handle"); return GLFW_FALSE; } - _glfw.win32.winmm.GetTime = (PFN_timeGetTime) - GetProcAddress(_glfw.win32.winmm.instance, "timeGetTime"); - - _glfw.win32.user32.instance = LoadLibraryA("user32.dll"); + _glfw.win32.user32.instance = _glfwPlatformLoadModule("user32.dll"); if (!_glfw.win32.user32.instance) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, @@ -92,23 +90,25 @@ static GLFWbool loadLibraries(void) } _glfw.win32.user32.SetProcessDPIAware_ = (PFN_SetProcessDPIAware) - GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); + _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "SetProcessDPIAware"); _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) - GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); + _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); _glfw.win32.user32.EnableNonClientDpiScaling_ = (PFN_EnableNonClientDpiScaling) - GetProcAddress(_glfw.win32.user32.instance, "EnableNonClientDpiScaling"); + _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "EnableNonClientDpiScaling"); _glfw.win32.user32.SetProcessDpiAwarenessContext_ = (PFN_SetProcessDpiAwarenessContext) - GetProcAddress(_glfw.win32.user32.instance, "SetProcessDpiAwarenessContext"); + _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "SetProcessDpiAwarenessContext"); _glfw.win32.user32.GetDpiForWindow_ = (PFN_GetDpiForWindow) - GetProcAddress(_glfw.win32.user32.instance, "GetDpiForWindow"); + _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "GetDpiForWindow"); _glfw.win32.user32.AdjustWindowRectExForDpi_ = (PFN_AdjustWindowRectExForDpi) - GetProcAddress(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi"); + _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi"); + _glfw.win32.user32.GetSystemMetricsForDpi_ = (PFN_GetSystemMetricsForDpi) + _glfwPlatformGetModuleSymbol(_glfw.win32.user32.instance, "GetSystemMetricsForDpi"); - _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); + _glfw.win32.dinput8.instance = _glfwPlatformLoadModule("dinput8.dll"); if (_glfw.win32.dinput8.instance) { _glfw.win32.dinput8.Create = (PFN_DirectInput8Create) - GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create"); + _glfwPlatformGetModuleSymbol(_glfw.win32.dinput8.instance, "DirectInput8Create"); } { @@ -125,46 +125,46 @@ static GLFWbool loadLibraries(void) for (i = 0; names[i]; i++) { - _glfw.win32.xinput.instance = LoadLibraryA(names[i]); + _glfw.win32.xinput.instance = _glfwPlatformLoadModule(names[i]); if (_glfw.win32.xinput.instance) { _glfw.win32.xinput.GetCapabilities = (PFN_XInputGetCapabilities) - GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); + _glfwPlatformGetModuleSymbol(_glfw.win32.xinput.instance, "XInputGetCapabilities"); _glfw.win32.xinput.GetState = (PFN_XInputGetState) - GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); + _glfwPlatformGetModuleSymbol(_glfw.win32.xinput.instance, "XInputGetState"); break; } } } - _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); + _glfw.win32.dwmapi.instance = _glfwPlatformLoadModule("dwmapi.dll"); if (_glfw.win32.dwmapi.instance) { _glfw.win32.dwmapi.IsCompositionEnabled = (PFN_DwmIsCompositionEnabled) - GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); + _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); _glfw.win32.dwmapi.Flush = (PFN_DwmFlush) - GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); + _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmFlush"); _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) - GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); + _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); _glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor) - GetProcAddress(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); + _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); } - _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); + _glfw.win32.shcore.instance = _glfwPlatformLoadModule("shcore.dll"); if (_glfw.win32.shcore.instance) { _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) - GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); + _glfwPlatformGetModuleSymbol(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); _glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor) - GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); + _glfwPlatformGetModuleSymbol(_glfw.win32.shcore.instance, "GetDpiForMonitor"); } - _glfw.win32.ntdll.instance = LoadLibraryA("ntdll.dll"); + _glfw.win32.ntdll.instance = _glfwPlatformLoadModule("ntdll.dll"); if (_glfw.win32.ntdll.instance) { _glfw.win32.ntdll.RtlVerifyVersionInfo_ = (PFN_RtlVerifyVersionInfo) - GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); + _glfwPlatformGetModuleSymbol(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); } return GLFW_TRUE; @@ -175,25 +175,22 @@ static GLFWbool loadLibraries(void) static void freeLibraries(void) { if (_glfw.win32.xinput.instance) - FreeLibrary(_glfw.win32.xinput.instance); + _glfwPlatformFreeModule(_glfw.win32.xinput.instance); if (_glfw.win32.dinput8.instance) - FreeLibrary(_glfw.win32.dinput8.instance); - - if (_glfw.win32.winmm.instance) - FreeLibrary(_glfw.win32.winmm.instance); + _glfwPlatformFreeModule(_glfw.win32.dinput8.instance); if (_glfw.win32.user32.instance) - FreeLibrary(_glfw.win32.user32.instance); + _glfwPlatformFreeModule(_glfw.win32.user32.instance); if (_glfw.win32.dwmapi.instance) - FreeLibrary(_glfw.win32.dwmapi.instance); + _glfwPlatformFreeModule(_glfw.win32.dwmapi.instance); if (_glfw.win32.shcore.instance) - FreeLibrary(_glfw.win32.shcore.instance); + _glfwPlatformFreeModule(_glfw.win32.shcore.instance); if (_glfw.win32.ntdll.instance) - FreeLibrary(_glfw.win32.ntdll.instance); + _glfwPlatformFreeModule(_glfw.win32.ntdll.instance); } // Create key code translation tables @@ -266,7 +263,6 @@ static void createKeyTables(void) _glfw.win32.keycodes[0x151] = GLFW_KEY_PAGE_DOWN; _glfw.win32.keycodes[0x149] = GLFW_KEY_PAGE_UP; _glfw.win32.keycodes[0x045] = GLFW_KEY_PAUSE; - _glfw.win32.keycodes[0x146] = GLFW_KEY_PAUSE; _glfw.win32.keycodes[0x039] = GLFW_KEY_SPACE; _glfw.win32.keycodes[0x00F] = GLFW_KEY_TAB; _glfw.win32.keycodes[0x03A] = GLFW_KEY_CAPS_LOCK; @@ -335,20 +331,69 @@ static void createKeyTables(void) } } +// Window procedure for the hidden helper window +// +static LRESULT CALLBACK helperWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_DISPLAYCHANGE: + _glfwPollMonitorsWin32(); + break; + + case WM_DEVICECHANGE: + { + if (!_glfw.joysticksInitialized) + break; + + if (wParam == DBT_DEVICEARRIVAL) + { + DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickConnectionWin32(); + } + else if (wParam == DBT_DEVICEREMOVECOMPLETE) + { + DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickDisconnectionWin32(); + } + + break; + } + } + + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + // Creates a dummy window for behind-the-scenes work // static GLFWbool createHelperWindow(void) { MSG msg; + WNDCLASSEXW wc = { sizeof(wc) }; + + wc.style = CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) helperWindowProc; + wc.hInstance = _glfw.win32.instance; + wc.lpszClassName = L"GLFW3 Helper"; + + _glfw.win32.helperWindowClass = RegisterClassExW(&wc); + if (!_glfw.win32.helperWindowClass) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WIn32: Failed to register helper window class"); + return GLFW_FALSE; + } _glfw.win32.helperWindowHandle = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, - _GLFW_WNDCLASSNAME, + MAKEINTATOM(_glfw.win32.helperWindowClass), L"GLFW message window", WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 1, 1, NULL, NULL, - GetModuleHandleW(NULL), + _glfw.win32.instance, NULL); if (!_glfw.win32.helperWindowHandle) @@ -405,13 +450,13 @@ WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) return NULL; } - target = calloc(count, sizeof(WCHAR)); + target = _glfw_calloc(count, sizeof(WCHAR)); if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string from UTF-8"); - free(target); + _glfw_free(target); return NULL; } @@ -433,13 +478,13 @@ char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) return NULL; } - target = calloc(size, 1); + target = _glfw_calloc(size, 1); if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string to UTF-8"); - free(target); + _glfw_free(target); return NULL; } @@ -498,7 +543,7 @@ void _glfwUpdateKeyNamesWin32(void) vk = vks[key - GLFW_KEY_KP_0]; } else - vk = MapVirtualKey(scancode, MAPVK_VSC_TO_VK); + vk = MapVirtualKeyW(scancode, MAPVK_VSC_TO_VK); length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), @@ -506,6 +551,8 @@ void _glfwUpdateKeyNamesWin32(void) if (length == -1) { + // This is a dead key, so we need a second simulated key press + // to make it output its own character (usually a diacritic) length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), 0); @@ -521,7 +568,8 @@ void _glfwUpdateKeyNamesWin32(void) } } -// Replacement for IsWindowsVersionOrGreater as MinGW lacks versionhelpers.h +// Replacement for IsWindowsVersionOrGreater, as we cannot rely on the +// application having a correct embedded manifest // BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp) { @@ -551,86 +599,129 @@ BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build) return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; } - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void) +GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) { - // To make SetForegroundWindow work as we want, we need to fiddle - // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early - // as possible in the hope of still being the foreground process) - SystemParametersInfoW(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, - &_glfw.win32.foregroundLockTimeout, 0); - SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, UIntToPtr(0), - SPIF_SENDCHANGE); + const _GLFWplatform win32 = + { + GLFW_PLATFORM_WIN32, + _glfwInitWin32, + _glfwTerminateWin32, + _glfwGetCursorPosWin32, + _glfwSetCursorPosWin32, + _glfwSetCursorModeWin32, + _glfwSetRawMouseMotionWin32, + _glfwRawMouseMotionSupportedWin32, + _glfwCreateCursorWin32, + _glfwCreateStandardCursorWin32, + _glfwDestroyCursorWin32, + _glfwSetCursorWin32, + _glfwGetScancodeNameWin32, + _glfwGetKeyScancodeWin32, + _glfwSetClipboardStringWin32, + _glfwGetClipboardStringWin32, + _glfwInitJoysticksWin32, + _glfwTerminateJoysticksWin32, + _glfwPollJoystickWin32, + _glfwGetMappingNameWin32, + _glfwUpdateGamepadGUIDWin32, + _glfwFreeMonitorWin32, + _glfwGetMonitorPosWin32, + _glfwGetMonitorContentScaleWin32, + _glfwGetMonitorWorkareaWin32, + _glfwGetVideoModesWin32, + _glfwGetVideoModeWin32, + _glfwGetGammaRampWin32, + _glfwSetGammaRampWin32, + _glfwCreateWindowWin32, + _glfwDestroyWindowWin32, + _glfwSetWindowTitleWin32, + _glfwSetWindowIconWin32, + _glfwGetWindowPosWin32, + _glfwSetWindowPosWin32, + _glfwGetWindowSizeWin32, + _glfwSetWindowSizeWin32, + _glfwSetWindowSizeLimitsWin32, + _glfwSetWindowAspectRatioWin32, + _glfwGetFramebufferSizeWin32, + _glfwGetWindowFrameSizeWin32, + _glfwGetWindowContentScaleWin32, + _glfwIconifyWindowWin32, + _glfwRestoreWindowWin32, + _glfwMaximizeWindowWin32, + _glfwShowWindowWin32, + _glfwHideWindowWin32, + _glfwRequestWindowAttentionWin32, + _glfwFocusWindowWin32, + _glfwSetWindowMonitorWin32, + _glfwWindowFocusedWin32, + _glfwWindowIconifiedWin32, + _glfwWindowVisibleWin32, + _glfwWindowMaximizedWin32, + _glfwWindowHoveredWin32, + _glfwFramebufferTransparentWin32, + _glfwGetWindowOpacityWin32, + _glfwSetWindowResizableWin32, + _glfwSetWindowDecoratedWin32, + _glfwSetWindowFloatingWin32, + _glfwSetWindowOpacityWin32, + _glfwSetWindowMousePassthroughWin32, + _glfwPollEventsWin32, + _glfwWaitEventsWin32, + _glfwWaitEventsTimeoutWin32, + _glfwPostEmptyEventWin32, + _glfwGetEGLPlatformWin32, + _glfwGetEGLNativeDisplayWin32, + _glfwGetEGLNativeWindowWin32, + _glfwGetRequiredInstanceExtensionsWin32, + _glfwGetPhysicalDevicePresentationSupportWin32, + _glfwCreateWindowSurfaceWin32, + }; + *platform = win32; + return GLFW_TRUE; +} + +int _glfwInitWin32(void) +{ if (!loadLibraries()) return GLFW_FALSE; createKeyTables(); _glfwUpdateKeyNamesWin32(); - if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1703OrGreaterWin32()) SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); else if (IsWindows8Point1OrGreater()) SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); else if (IsWindowsVistaOrGreater()) SetProcessDPIAware(); - if (!_glfwRegisterWindowClassWin32()) - return GLFW_FALSE; - if (!createHelperWindow()) return GLFW_FALSE; - _glfwInitTimerWin32(); - _glfwPollMonitorsWin32(); return GLFW_TRUE; } -void _glfwPlatformTerminate(void) +void _glfwTerminateWin32(void) { if (_glfw.win32.deviceNotificationHandle) UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle); if (_glfw.win32.helperWindowHandle) DestroyWindow(_glfw.win32.helperWindowHandle); + if (_glfw.win32.helperWindowClass) + UnregisterClassW(MAKEINTATOM(_glfw.win32.helperWindowClass), _glfw.win32.instance); + if (_glfw.win32.mainWindowClass) + UnregisterClassW(MAKEINTATOM(_glfw.win32.mainWindowClass), _glfw.win32.instance); - _glfwUnregisterWindowClassWin32(); - - // Restore previous foreground lock timeout system setting - SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, - UIntToPtr(_glfw.win32.foregroundLockTimeout), - SPIF_SENDCHANGE); - - free(_glfw.win32.clipboardString); - free(_glfw.win32.rawInput); + _glfw_free(_glfw.win32.clipboardString); + _glfw_free(_glfw.win32.rawInput); _glfwTerminateWGL(); _glfwTerminateEGL(); + _glfwTerminateOSMesa(); freeLibraries(); } -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " Win32 WGL EGL OSMesa" -#if defined(__MINGW64_VERSION_MAJOR) - " MinGW-w64" -#elif defined(__MINGW32__) - " MinGW" -#elif defined(_MSC_VER) - " VisualC" -#endif -#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) - " hybrid-GPU" -#endif -#if defined(_GLFW_BUILD_DLL) - " DLL" -#endif - ; -} - diff --git a/src/external/glfw/src/win32_joystick.c b/src/external/glfw/src/win32_joystick.c index 9c71d114a..3a28943b0 100644 --- a/src/external/glfw/src/win32_joystick.c +++ b/src/external/glfw/src/win32_joystick.c @@ -199,11 +199,11 @@ static GLFWbool supportsXInput(const GUID* guid) if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) return GLFW_FALSE; - ridl = calloc(count, sizeof(RAWINPUTDEVICELIST)); + ridl = _glfw_calloc(count, sizeof(RAWINPUTDEVICELIST)); if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) { - free(ridl); + _glfw_free(ridl); return GLFW_FALSE; } @@ -248,7 +248,7 @@ static GLFWbool supportsXInput(const GUID* guid) } } - free(ridl); + _glfw_free(ridl); return result; } @@ -256,16 +256,16 @@ static GLFWbool supportsXInput(const GUID* guid) // static void closeJoystick(_GLFWjoystick* js) { + _glfwInputJoystick(js, GLFW_DISCONNECTED); + if (js->win32.device) { IDirectInputDevice8_Unacquire(js->win32.device); IDirectInputDevice8_Release(js->win32.device); } - free(js->win32.objects); - + _glfw_free(js->win32.objects); _glfwFreeJoystick(js); - _glfwInputJoystick(js, GLFW_DISCONNECTED); } // DirectInput device object enumeration callback @@ -357,7 +357,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { js = _glfw.joysticks + jid; - if (js->present) + if (js->connected) { if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0) return DIENUM_CONTINUE; @@ -416,8 +416,8 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) memset(&data, 0, sizeof(data)); data.device = device; - data.objects = calloc(dc.dwAxes + (size_t) dc.dwButtons + dc.dwPOVs, - sizeof(_GLFWjoyobjectWin32)); + data.objects = _glfw_calloc(dc.dwAxes + (size_t) dc.dwButtons + dc.dwPOVs, + sizeof(_GLFWjoyobjectWin32)); if (FAILED(IDirectInputDevice8_EnumObjects(device, deviceObjectCallback, @@ -428,7 +428,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) "Win32: Failed to enumerate device objects"); IDirectInputDevice8_Release(device); - free(data.objects); + _glfw_free(data.objects); return DIENUM_CONTINUE; } @@ -445,7 +445,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) "Win32: Failed to convert joystick name to UTF-8"); IDirectInputDevice8_Release(device); - free(data.objects); + _glfw_free(data.objects); return DIENUM_STOP; } @@ -473,7 +473,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) if (!js) { IDirectInputDevice8_Release(device); - free(data.objects); + _glfw_free(data.objects); return DIENUM_STOP; } @@ -508,7 +508,7 @@ void _glfwDetectJoystickConnectionWin32(void) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.joysticks[jid].present && + if (_glfw.joysticks[jid].connected && _glfw.joysticks[jid].win32.device == NULL && _glfw.joysticks[jid].win32.index == index) { @@ -560,8 +560,8 @@ void _glfwDetectJoystickDisconnectionWin32(void) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { _GLFWjoystick* js = _glfw.joysticks + jid; - if (js->present) - _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); + if (js->connected) + _glfwPollJoystickWin32(js, _GLFW_POLL_PRESENCE); } } @@ -570,11 +570,11 @@ void _glfwDetectJoystickDisconnectionWin32(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwPlatformInitJoysticks(void) +GLFWbool _glfwInitJoysticksWin32(void) { if (_glfw.win32.dinput8.instance) { - if (FAILED(DirectInput8Create(GetModuleHandle(NULL), + if (FAILED(DirectInput8Create(_glfw.win32.instance, DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**) &_glfw.win32.dinput8.api, @@ -590,7 +590,7 @@ GLFWbool _glfwPlatformInitJoysticks(void) return GLFW_TRUE; } -void _glfwPlatformTerminateJoysticks(void) +void _glfwTerminateJoysticksWin32(void) { int jid; @@ -601,13 +601,13 @@ void _glfwPlatformTerminateJoysticks(void) IDirectInput8_Release(_glfw.win32.dinput8.api); } -int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +GLFWbool _glfwPollJoystickWin32(_GLFWjoystick* js, int mode) { if (js->win32.device) { int i, ai = 0, bi = 0, pi = 0; HRESULT result; - DIJOYSTATE state; + DIJOYSTATE state = {0}; IDirectInputDevice8_Poll(js->win32.device); result = IDirectInputDevice8_GetDeviceState(js->win32.device, @@ -740,7 +740,12 @@ int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) return GLFW_TRUE; } -void _glfwPlatformUpdateGamepadGUID(char* guid) +const char* _glfwGetMappingNameWin32(void) +{ + return "Windows"; +} + +void _glfwUpdateGamepadGUIDWin32(char* guid) { if (strcmp(guid + 20, "504944564944") == 0) { diff --git a/src/external/glfw/src/win32_joystick.h b/src/external/glfw/src/win32_joystick.h index b6a7adc36..d7c2bb6f7 100644 --- a/src/external/glfw/src/win32_joystick.h +++ b/src/external/glfw/src/win32_joystick.h @@ -24,10 +24,10 @@ // //======================================================================== -#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } +#define GLFW_WIN32_JOYSTICK_STATE _GLFWjoystickWin32 win32; +#define GLFW_WIN32_LIBRARY_JOYSTICK_STATE -#define _GLFW_PLATFORM_MAPPING_NAME "Windows" +#define GLFW_BUILD_WIN32_MAPPINGS // Joystick element (axis, button or slider) // diff --git a/src/external/glfw/src/win32_module.c b/src/external/glfw/src/win32_module.c new file mode 100644 index 000000000..35bdd71d5 --- /dev/null +++ b/src/external/glfw/src/win32_module.c @@ -0,0 +1,49 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2021 Camilla Löwy +// +// 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. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void* _glfwPlatformLoadModule(const char* path) +{ + return LoadLibraryA(path); +} + +void _glfwPlatformFreeModule(void* module) +{ + FreeLibrary((HMODULE) module); +} + +GLFWproc _glfwPlatformGetModuleSymbol(void* module, const char* name) +{ + return (GLFWproc) GetProcAddress((HMODULE) module, name); +} + diff --git a/src/external/glfw/src/win32_monitor.c b/src/external/glfw/src/win32_monitor.c index b4c53e4c0..57b44af3e 100644 --- a/src/external/glfw/src/win32_monitor.c +++ b/src/external/glfw/src/win32_monitor.c @@ -32,7 +32,6 @@ #include #include #include -#include #include @@ -96,7 +95,7 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DeleteDC(dc); monitor = _glfwAllocMonitor(name, widthMM, heightMM); - free(name); + _glfw_free(name); if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) monitor->win32.modesPruned = GLFW_TRUE; @@ -145,7 +144,7 @@ void _glfwPollMonitorsWin32(void) disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); @@ -197,7 +196,7 @@ void _glfwPollMonitorsWin32(void) monitor = createMonitor(&adapter, &display); if (!monitor) { - free(disconnected); + _glfw_free(disconnected); return; } @@ -227,7 +226,7 @@ void _glfwPollMonitorsWin32(void) monitor = createMonitor(&adapter, NULL); if (!monitor) { - free(disconnected); + _glfw_free(disconnected); return; } @@ -241,7 +240,7 @@ void _glfwPollMonitorsWin32(void) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } - free(disconnected); + _glfw_free(disconnected); } // Change the current video mode @@ -254,7 +253,7 @@ void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) LONG result; best = _glfwChooseVideoMode(monitor, desired); - _glfwPlatformGetVideoMode(monitor, ¤t); + _glfwGetVideoModeWin32(monitor, ¤t); if (_glfwCompareVideoModes(¤t, best) == 0) return; @@ -314,12 +313,23 @@ void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) } } -void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) +void _glfwGetHMONITORContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) { UINT xdpi, ydpi; + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + if (IsWindows8Point1OrGreater()) - GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + { + if (GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi) != S_OK) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query monitor DPI"); + return; + } + } else { const HDC dc = GetDC(NULL); @@ -339,11 +349,11 @@ void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* ysc ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +void _glfwFreeMonitorWin32(_GLFWmonitor* monitor) { } -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +void _glfwGetMonitorPosWin32(_GLFWmonitor* monitor, int* xpos, int* ypos) { DEVMODEW dm; ZeroMemory(&dm, sizeof(dm)); @@ -360,18 +370,18 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = dm.dmPosition.y; } -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) +void _glfwGetMonitorContentScaleWin32(_GLFWmonitor* monitor, + float* xscale, float* yscale) { - _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); + _glfwGetHMONITORContentScaleWin32(monitor->win32.handle, xscale, yscale); } -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, - int* xpos, int* ypos, - int* width, int* height) +void _glfwGetMonitorWorkareaWin32(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) { MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfo(monitor->win32.handle, &mi); + GetMonitorInfoW(monitor->win32.handle, &mi); if (xpos) *xpos = mi.rcWork.left; @@ -383,7 +393,7 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, *height = mi.rcWork.bottom - mi.rcWork.top; } -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +GLFWvidmode* _glfwGetVideoModesWin32(_GLFWmonitor* monitor, int* count) { int modeIndex = 0, size = 0; GLFWvidmode* result = NULL; @@ -442,7 +452,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) if (*count == size) { size += 128; - result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode)); + result = (GLFWvidmode*) _glfw_realloc(result, size * sizeof(GLFWvidmode)); } (*count)++; @@ -452,15 +462,15 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) if (!*count) { // HACK: Report the current mode if no valid modes were found - result = calloc(1, sizeof(GLFWvidmode)); - _glfwPlatformGetVideoMode(monitor, result); + result = _glfw_calloc(1, sizeof(GLFWvidmode)); + _glfwGetVideoModeWin32(monitor, result); *count = 1; } return result; } -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +void _glfwGetVideoModeWin32(_GLFWmonitor* monitor, GLFWvidmode* mode) { DEVMODEW dm; ZeroMemory(&dm, sizeof(dm)); @@ -477,7 +487,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) &mode->blueBits); } -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwGetGammaRampWin32(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { HDC dc; WORD values[3][256]; @@ -495,7 +505,7 @@ GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) return GLFW_TRUE; } -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +void _glfwSetGammaRampWin32(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { HDC dc; WORD values[3][256]; diff --git a/src/external/glfw/src/win32_platform.h b/src/external/glfw/src/win32_platform.h index 6449a7119..82b34bb9d 100644 --- a/src/external/glfw/src/win32_platform.h +++ b/src/external/glfw/src/win32_platform.h @@ -162,7 +162,9 @@ typedef enum #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) -4) #endif /*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ -// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +// Replacement for versionhelpers.h macros, as we cannot rely on the +// application having a correct embedded manifest +// #define IsWindowsVistaOrGreater() \ _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_VISTA), \ LOBYTE(_WIN32_WINNT_VISTA), 0) @@ -176,9 +178,11 @@ typedef enum _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINBLUE), \ LOBYTE(_WIN32_WINNT_WINBLUE), 0) -#define _glfwIsWindows10AnniversaryUpdateOrGreaterWin32() \ +// Windows 10 Anniversary Update +#define _glfwIsWindows10Version1607OrGreaterWin32() \ _glfwIsWindows10BuildOrGreaterWin32(14393) -#define _glfwIsWindows10CreatorsUpdateOrGreaterWin32() \ +// Windows 10 Creators Update +#define _glfwIsWindows10Version1703OrGreaterWin32() \ _glfwIsWindows10BuildOrGreaterWin32(15063) // HACK: Define macros that some xinput.h variants don't @@ -215,9 +219,56 @@ typedef enum #define DIDFT_OPTIONAL 0x80000000 #endif -// winmm.dll function pointer typedefs -typedef DWORD (WINAPI * PFN_timeGetTime)(void); -#define timeGetTime _glfw.win32.winmm.GetTime +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#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_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_STEREO_ARB 0x2012 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_SAMPLES_ARB 0x2042 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#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_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 +#define WGL_COLORSPACE_EXT 0x309d +#define WGL_COLORSPACE_SRGB_EXT 0x3089 + +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 // xinput.dll function pointer typedefs typedef DWORD (WINAPI * PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); @@ -236,12 +287,14 @@ typedef BOOL (WINAPI * PFN_EnableNonClientDpiScaling)(HWND); typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(HANDLE); typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND); typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); +typedef int (WINAPI * PFN_GetSystemMetricsForDpi)(int,UINT); #define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ #define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ #define EnableNonClientDpiScaling _glfw.win32.user32.EnableNonClientDpiScaling_ #define SetProcessDpiAwarenessContext _glfw.win32.user32.SetProcessDpiAwarenessContext_ #define GetDpiForWindow _glfw.win32.user32.GetDpiForWindow_ #define AdjustWindowRectExForDpi _glfw.win32.user32.AdjustWindowRectExForDpi_ +#define GetSystemMetricsForDpi _glfw.win32.user32.GetSystemMetricsForDpi_ // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); @@ -263,6 +316,34 @@ typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*, typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); #define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ +// WGL extension pointer typedefs +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); +#define wglSwapIntervalEXT _glfw.wgl.SwapIntervalEXT +#define wglGetPixelFormatAttribivARB _glfw.wgl.GetPixelFormatAttribivARB +#define wglGetExtensionsStringEXT _glfw.wgl.GetExtensionsStringEXT +#define wglGetExtensionsStringARB _glfw.wgl.GetExtensionsStringARB +#define wglCreateContextAttribsARB _glfw.wgl.CreateContextAttribsARB + +// opengl32.dll function pointer typedefs +typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); +typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); +typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); +typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); +typedef HGLRC (WINAPI * PFN_wglGetCurrentContext)(void); +typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); +typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); +#define wglCreateContext _glfw.wgl.CreateContext +#define wglDeleteContext _glfw.wgl.DeleteContext +#define wglGetProcAddress _glfw.wgl.GetProcAddress +#define wglGetCurrentDC _glfw.wgl.GetCurrentDC +#define wglGetCurrentContext _glfw.wgl.GetCurrentContext +#define wglMakeCurrent _glfw.wgl.MakeCurrent +#define wglShareLists _glfw.wgl.ShareLists + typedef VkFlags VkWin32SurfaceCreateFlagsKHR; typedef struct VkWin32SurfaceCreateInfoKHR @@ -277,25 +358,55 @@ typedef struct VkWin32SurfaceCreateInfoKHR typedef VkResult (APIENTRY *PFN_vkCreateWin32SurfaceKHR)(VkInstance,const VkWin32SurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice,uint32_t); -#include "win32_joystick.h" -#include "wgl_context.h" +#define GLFW_WIN32_WINDOW_STATE _GLFWwindowWin32 win32; +#define GLFW_WIN32_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32; +#define GLFW_WIN32_MONITOR_STATE _GLFWmonitorWin32 win32; +#define GLFW_WIN32_CURSOR_STATE _GLFWcursorWin32 win32; -#if !defined(_GLFW_WNDCLASSNAME) - #define _GLFW_WNDCLASSNAME L"GLFW30" -#endif +#define GLFW_WGL_CONTEXT_STATE _GLFWcontextWGL wgl; +#define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl; -#define _glfw_dlopen(name) LoadLibraryA(name) -#define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) -#define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32 -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 -#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsWin32 win32 -#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexWin32 win32 +// WGL-specific per-context data +// +typedef struct _GLFWcontextWGL +{ + HDC dc; + HGLRC handle; + int interval; +} _GLFWcontextWGL; +// WGL-specific global data +// +typedef struct _GLFWlibraryWGL +{ + HINSTANCE instance; + PFN_wglCreateContext CreateContext; + PFN_wglDeleteContext DeleteContext; + PFN_wglGetProcAddress GetProcAddress; + PFN_wglGetCurrentDC GetCurrentDC; + PFN_wglGetCurrentContext GetCurrentContext; + PFN_wglMakeCurrent MakeCurrent; + PFN_wglShareLists ShareLists; + + PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; + PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; + PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + GLFWbool EXT_swap_control; + GLFWbool EXT_colorspace; + GLFWbool ARB_multisample; + GLFWbool ARB_framebuffer_sRGB; + GLFWbool EXT_framebuffer_sRGB; + GLFWbool ARB_pixel_format; + GLFWbool ARB_create_context; + GLFWbool ARB_create_context_profile; + GLFWbool EXT_create_context_es2_profile; + GLFWbool ARB_create_context_robustness; + GLFWbool ARB_create_context_no_error; + GLFWbool ARB_context_flush_control; +} _GLFWlibraryWGL; // Win32-specific per-window data // @@ -319,18 +430,19 @@ typedef struct _GLFWwindowWin32 // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; - // The last recevied high surrogate when decoding pairs of UTF-16 messages + // The last received high surrogate when decoding pairs of UTF-16 messages WCHAR highSurrogate; - } _GLFWwindowWin32; // Win32-specific global data // typedef struct _GLFWlibraryWin32 { + HINSTANCE instance; HWND helperWindowHandle; + ATOM helperWindowClass; + ATOM mainWindowClass; HDEVNOTIFY deviceNotificationHandle; - DWORD foregroundLockTimeout; int acquiredMonitorCount; char* clipboardString; short int keycodes[512]; @@ -340,15 +452,12 @@ typedef struct _GLFWlibraryWin32 double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active _GLFWwindow* disabledCursorWindow; + // The window the cursor is captured in + _GLFWwindow* capturedCursorWindow; RAWINPUT* rawInput; int rawInputSize; UINT mouseTrailSize; - struct { - HINSTANCE instance; - PFN_timeGetTime GetTime; - } winmm; - struct { HINSTANCE instance; PFN_DirectInput8Create Create; @@ -369,6 +478,7 @@ typedef struct _GLFWlibraryWin32 PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext_; PFN_GetDpiForWindow GetDpiForWindow_; PFN_AdjustWindowRectExForDpi AdjustWindowRectExForDpi_; + PFN_GetSystemMetricsForDpi GetSystemMetricsForDpi_; } user32; struct { @@ -389,7 +499,6 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; } ntdll; - } _GLFWlibraryWin32; // Win32-specific per-monitor data @@ -404,7 +513,6 @@ typedef struct _GLFWmonitorWin32 char publicDisplayName[32]; GLFWbool modesPruned; GLFWbool modeChanged; - } _GLFWmonitorWin32; // Win32-specific per-cursor data @@ -412,39 +520,12 @@ typedef struct _GLFWmonitorWin32 typedef struct _GLFWcursorWin32 { HCURSOR handle; - } _GLFWcursorWin32; -// Win32-specific global timer data -// -typedef struct _GLFWtimerWin32 -{ - GLFWbool hasPC; - uint64_t frequency; -} _GLFWtimerWin32; - -// Win32-specific thread local storage data -// -typedef struct _GLFWtlsWin32 -{ - GLFWbool allocated; - DWORD index; - -} _GLFWtlsWin32; - -// Win32-specific mutex data -// -typedef struct _GLFWmutexWin32 -{ - GLFWbool allocated; - CRITICAL_SECTION section; - -} _GLFWmutexWin32; - - -GLFWbool _glfwRegisterWindowClassWin32(void); -void _glfwUnregisterWindowClassWin32(void); +GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform); +int _glfwInitWin32(void); +void _glfwTerminateWin32(void); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); @@ -453,10 +534,91 @@ BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build); void _glfwInputErrorWin32(int error, const char* description); void _glfwUpdateKeyNamesWin32(void); -void _glfwInitTimerWin32(void); - void _glfwPollMonitorsWin32(void); void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); -void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); +void _glfwGetHMONITORContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); + +GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); +void _glfwDestroyWindowWin32(_GLFWwindow* window); +void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title); +void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images); +void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos); +void _glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos); +void _glfwGetWindowSizeWin32(_GLFWwindow* window, int* width, int* height); +void _glfwSetWindowSizeWin32(_GLFWwindow* window, int width, int height); +void _glfwSetWindowSizeLimitsWin32(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); +void _glfwSetWindowAspectRatioWin32(_GLFWwindow* window, int numer, int denom); +void _glfwGetFramebufferSizeWin32(_GLFWwindow* window, int* width, int* height); +void _glfwGetWindowFrameSizeWin32(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); +void _glfwGetWindowContentScaleWin32(_GLFWwindow* window, float* xscale, float* yscale); +void _glfwIconifyWindowWin32(_GLFWwindow* window); +void _glfwRestoreWindowWin32(_GLFWwindow* window); +void _glfwMaximizeWindowWin32(_GLFWwindow* window); +void _glfwShowWindowWin32(_GLFWwindow* window); +void _glfwHideWindowWin32(_GLFWwindow* window); +void _glfwRequestWindowAttentionWin32(_GLFWwindow* window); +void _glfwFocusWindowWin32(_GLFWwindow* window); +void _glfwSetWindowMonitorWin32(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); +GLFWbool _glfwWindowFocusedWin32(_GLFWwindow* window); +GLFWbool _glfwWindowIconifiedWin32(_GLFWwindow* window); +GLFWbool _glfwWindowVisibleWin32(_GLFWwindow* window); +GLFWbool _glfwWindowMaximizedWin32(_GLFWwindow* window); +GLFWbool _glfwWindowHoveredWin32(_GLFWwindow* window); +GLFWbool _glfwFramebufferTransparentWin32(_GLFWwindow* window); +void _glfwSetWindowResizableWin32(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowDecoratedWin32(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowFloatingWin32(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowMousePassthroughWin32(_GLFWwindow* window, GLFWbool enabled); +float _glfwGetWindowOpacityWin32(_GLFWwindow* window); +void _glfwSetWindowOpacityWin32(_GLFWwindow* window, float opacity); + +void _glfwSetRawMouseMotionWin32(_GLFWwindow *window, GLFWbool enabled); +GLFWbool _glfwRawMouseMotionSupportedWin32(void); + +void _glfwPollEventsWin32(void); +void _glfwWaitEventsWin32(void); +void _glfwWaitEventsTimeoutWin32(double timeout); +void _glfwPostEmptyEventWin32(void); + +void _glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos); +void _glfwSetCursorPosWin32(_GLFWwindow* window, double xpos, double ypos); +void _glfwSetCursorModeWin32(_GLFWwindow* window, int mode); +const char* _glfwGetScancodeNameWin32(int scancode); +int _glfwGetKeyScancodeWin32(int key); +GLFWbool _glfwCreateCursorWin32(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); +GLFWbool _glfwCreateStandardCursorWin32(_GLFWcursor* cursor, int shape); +void _glfwDestroyCursorWin32(_GLFWcursor* cursor); +void _glfwSetCursorWin32(_GLFWwindow* window, _GLFWcursor* cursor); +void _glfwSetClipboardStringWin32(const char* string); +const char* _glfwGetClipboardStringWin32(void); + +EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs); +EGLNativeDisplayType _glfwGetEGLNativeDisplayWin32(void); +EGLNativeWindowType _glfwGetEGLNativeWindowWin32(_GLFWwindow* window); + +void _glfwGetRequiredInstanceExtensionsWin32(char** extensions); +GLFWbool _glfwGetPhysicalDevicePresentationSupportWin32(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); +VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +void _glfwFreeMonitorWin32(_GLFWmonitor* monitor); +void _glfwGetMonitorPosWin32(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwGetMonitorContentScaleWin32(_GLFWmonitor* monitor, float* xscale, float* yscale); +void _glfwGetMonitorWorkareaWin32(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); +GLFWvidmode* _glfwGetVideoModesWin32(_GLFWmonitor* monitor, int* count); +void _glfwGetVideoModeWin32(_GLFWmonitor* monitor, GLFWvidmode* mode); +GLFWbool _glfwGetGammaRampWin32(_GLFWmonitor* monitor, GLFWgammaramp* ramp); +void _glfwSetGammaRampWin32(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +GLFWbool _glfwInitJoysticksWin32(void); +void _glfwTerminateJoysticksWin32(void); +GLFWbool _glfwPollJoystickWin32(_GLFWjoystick* js, int mode); +const char* _glfwGetMappingNameWin32(void); +void _glfwUpdateGamepadGUIDWin32(char* guid); + +GLFWbool _glfwInitWGL(void); +void _glfwTerminateWGL(void); +GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); diff --git a/src/external/glfw/src/win32_thread.c b/src/external/glfw/src/win32_thread.c index 53b34af26..35b8f99eb 100644 --- a/src/external/glfw/src/win32_thread.c +++ b/src/external/glfw/src/win32_thread.c @@ -43,8 +43,7 @@ GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) tls->win32.index = TlsAlloc(); if (tls->win32.index == TLS_OUT_OF_INDEXES) { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate TLS index"); + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to allocate TLS index"); return GLFW_FALSE; } diff --git a/src/external/glfw/src/win32_thread.h b/src/external/glfw/src/win32_thread.h new file mode 100644 index 000000000..4b5a696f8 --- /dev/null +++ b/src/external/glfw/src/win32_thread.h @@ -0,0 +1,48 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// 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 + +#define GLFW_WIN32_TLS_STATE _GLFWtlsWin32 win32; +#define GLFW_WIN32_MUTEX_STATE _GLFWmutexWin32 win32; + +// Win32-specific thread local storage data +// +typedef struct _GLFWtlsWin32 +{ + GLFWbool allocated; + DWORD index; +} _GLFWtlsWin32; + +// Win32-specific mutex data +// +typedef struct _GLFWmutexWin32 +{ + GLFWbool allocated; + CRITICAL_SECTION section; +} _GLFWmutexWin32; + diff --git a/src/external/glfw/src/win32_time.c b/src/external/glfw/src/win32_time.c index 721b0d0df..a1c641419 100644 --- a/src/external/glfw/src/win32_time.c +++ b/src/external/glfw/src/win32_time.c @@ -30,43 +30,20 @@ #include "internal.h" -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Initialise timer -// -void _glfwInitTimerWin32(void) -{ - uint64_t frequency; - - if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) - { - _glfw.timer.win32.hasPC = GLFW_TRUE; - _glfw.timer.win32.frequency = frequency; - } - else - { - _glfw.timer.win32.hasPC = GLFW_FALSE; - _glfw.timer.win32.frequency = 1000; - } -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// +void _glfwPlatformInitTimer(void) +{ + QueryPerformanceFrequency((LARGE_INTEGER*) &_glfw.timer.win32.frequency); +} + uint64_t _glfwPlatformGetTimerValue(void) { - if (_glfw.timer.win32.hasPC) - { - uint64_t value; - QueryPerformanceCounter((LARGE_INTEGER*) &value); - return value; - } - else - return (uint64_t) timeGetTime(); + uint64_t value; + QueryPerformanceCounter((LARGE_INTEGER*) &value); + return value; } uint64_t _glfwPlatformGetTimerFrequency(void) diff --git a/src/external/glfw/src/win32_time.h b/src/external/glfw/src/win32_time.h new file mode 100644 index 000000000..da5afa41e --- /dev/null +++ b/src/external/glfw/src/win32_time.h @@ -0,0 +1,38 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// 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 + +#define GLFW_WIN32_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32; + +// Win32-specific global timer data +// +typedef struct _GLFWtimerWin32 +{ + uint64_t frequency; +} _GLFWtimerWin32; + diff --git a/src/external/glfw/src/win32_window.c b/src/external/glfw/src/win32_window.c index 52a9c6803..69fb64228 100644 --- a/src/external/glfw/src/win32_window.c +++ b/src/external/glfw/src/win32_window.c @@ -31,7 +31,6 @@ #include #include -#include #include #include #include @@ -98,8 +97,7 @@ static const GLFWimage* chooseImage(int count, const GLFWimage* images, // Creates an RGBA icon or cursor // -static HICON createIcon(const GLFWimage* image, - int xhot, int yhot, GLFWbool icon) +static HICON createIcon(const GLFWimage* image, int xhot, int yhot, GLFWbool icon) { int i; HDC dc; @@ -195,7 +193,7 @@ static void getFullWindowSize(DWORD style, DWORD exStyle, { RECT rect = { 0, 0, contentWidth, contentHeight }; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); else AdjustWindowRectEx(&rect, style, FALSE, exStyle); @@ -212,7 +210,7 @@ static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) UINT dpi = USER_DEFAULT_SCREEN_DPI; const float ratio = (float) window->numer / (float) window->denom; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) dpi = GetDpiForWindow(window->win32.handle); getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), @@ -240,7 +238,8 @@ static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) // static void updateCursorImage(_GLFWwindow* window) { - if (window->cursorMode == GLFW_CURSOR_NORMAL) + if (window->cursorMode == GLFW_CURSOR_NORMAL || + window->cursorMode == GLFW_CURSOR_CAPTURED) { if (window->cursor) SetCursor(window->cursor->win32.handle); @@ -251,20 +250,24 @@ static void updateCursorImage(_GLFWwindow* window) SetCursor(NULL); } -// Updates the cursor clip rect +// Sets the cursor clip rect to the window content area // -static void updateClipRect(_GLFWwindow* window) +static void captureCursor(_GLFWwindow* window) { - if (window) - { - RECT clipRect; - GetClientRect(window->win32.handle, &clipRect); - ClientToScreen(window->win32.handle, (POINT*) &clipRect.left); - ClientToScreen(window->win32.handle, (POINT*) &clipRect.right); - ClipCursor(&clipRect); - } - else - ClipCursor(NULL); + RECT clipRect; + GetClientRect(window->win32.handle, &clipRect); + ClientToScreen(window->win32.handle, (POINT*) &clipRect.left); + ClientToScreen(window->win32.handle, (POINT*) &clipRect.right); + ClipCursor(&clipRect); + _glfw.win32.capturedCursorWindow = window; +} + +// Disabled clip cursor +// +static void releaseCursor(void) +{ + ClipCursor(NULL); + _glfw.win32.capturedCursorWindow = NULL; } // Enables WM_INPUT messages for the mouse for the specified window @@ -298,12 +301,12 @@ static void disableRawMouseMotion(_GLFWwindow* window) static void disableCursor(_GLFWwindow* window) { _glfw.win32.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.win32.restoreCursorPosX, - &_glfw.win32.restoreCursorPosY); + _glfwGetCursorPosWin32(window, + &_glfw.win32.restoreCursorPosX, + &_glfw.win32.restoreCursorPosY); updateCursorImage(window); _glfwCenterCursorInContentArea(window); - updateClipRect(window); + captureCursor(window); if (window->rawMouseMotion) enableRawMouseMotion(window); @@ -317,10 +320,10 @@ static void enableCursor(_GLFWwindow* window) disableRawMouseMotion(window); _glfw.win32.disabledCursorWindow = NULL; - updateClipRect(NULL); - _glfwPlatformSetCursorPos(window, - _glfw.win32.restoreCursorPosX, - _glfw.win32.restoreCursorPosY); + releaseCursor(); + _glfwSetCursorPosWin32(window, + _glfw.win32.restoreCursorPosX, + _glfw.win32.restoreCursorPosY); updateCursorImage(window); } @@ -355,7 +358,7 @@ static void updateWindowStyles(const _GLFWwindow* window) GetClientRect(window->win32.handle, &rect); - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, style, FALSE, getWindowExStyle(window), @@ -435,7 +438,7 @@ static int getKeyMods(void) static void fitToMonitor(_GLFWwindow* window) { MONITORINFO mi = { sizeof(mi) }; - GetMonitorInfo(window->monitor->win32.handle, &mi); + GetMonitorInfoW(window->monitor->win32.handle, &mi); SetWindowPos(window->win32.handle, HWND_TOPMOST, mi.rcMonitor.left, mi.rcMonitor.top, @@ -454,8 +457,8 @@ static void acquireMonitor(_GLFWwindow* window) // HACK: When mouse trails are enabled the cursor becomes invisible when // the OpenGL ICD switches to page flipping - SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); - SystemParametersInfo(SPI_SETMOUSETRAILS, 0, 0, 0); + SystemParametersInfoW(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); + SystemParametersInfoW(SPI_SETMOUSETRAILS, 0, 0, 0); } if (!window->monitor->window) @@ -478,67 +481,83 @@ static void releaseMonitor(_GLFWwindow* window) SetThreadExecutionState(ES_CONTINUOUS); // HACK: Restore mouse trail length saved in acquireMonitor - SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); + SystemParametersInfoW(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); } _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeWin32(window->monitor); } -// Window callback function (handles window messages) +// Manually maximize the window, for when SW_MAXIMIZE cannot be used // -static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, - WPARAM wParam, LPARAM lParam) +static void maximizeWindowManually(_GLFWwindow* window) +{ + RECT rect; + DWORD style; + MONITORINFO mi = { sizeof(mi) }; + + GetMonitorInfoW(MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST), &mi); + + rect = mi.rcWork; + + if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) + { + rect.right = _glfw_min(rect.right, rect.left + window->maxwidth); + rect.bottom = _glfw_min(rect.bottom, rect.top + window->maxheight); + } + + style = GetWindowLongW(window->win32.handle, GWL_STYLE); + style |= WS_MAXIMIZE; + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + + if (window->decorated) + { + const DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + + if (_glfwIsWindows10Version1607OrGreaterWin32()) + { + const UINT dpi = GetDpiForWindow(window->win32.handle); + AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); + OffsetRect(&rect, 0, GetSystemMetricsForDpi(SM_CYCAPTION, dpi)); + } + else + { + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + OffsetRect(&rect, 0, GetSystemMetrics(SM_CYCAPTION)); + } + + rect.bottom = _glfw_min(rect.bottom, mi.rcWork.bottom); + } + + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); +} + +// Window procedure for user-created windows +// +static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { _GLFWwindow* window = GetPropW(hWnd, L"GLFW"); if (!window) { - // This is the message handling for the hidden helper window - // and for a regular window during its initial creation - - switch (uMsg) + if (uMsg == WM_NCCREATE) { - case WM_NCCREATE: + if (_glfwIsWindows10Version1607OrGreaterWin32()) { - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - { - const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam; - const _GLFWwndconfig* wndconfig = cs->lpCreateParams; + const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam; + const _GLFWwndconfig* wndconfig = cs->lpCreateParams; - // On per-monitor DPI aware V1 systems, only enable - // non-client scaling for windows that scale the client area - // We need WM_GETDPISCALEDSIZE from V2 to keep the client - // area static when the non-client area is scaled - if (wndconfig && wndconfig->scaleToMonitor) - EnableNonClientDpiScaling(hWnd); - } - - break; - } - - case WM_DISPLAYCHANGE: - _glfwPollMonitorsWin32(); - break; - - case WM_DEVICECHANGE: - { - if (!_glfw.joysticksInitialized) - break; - - if (wParam == DBT_DEVICEARRIVAL) - { - DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickConnectionWin32(); - } - else if (wParam == DBT_DEVICEREMOVECOMPLETE) - { - DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickDisconnectionWin32(); - } - - break; + // On per-monitor DPI aware V1 systems, only enable + // non-client scaling for windows that scale the client area + // We need WM_GETDPISCALEDSIZE from V2 to keep the client + // area static when the non-client area is scaled + if (wndconfig && wndconfig->scaleToMonitor) + EnableNonClientDpiScaling(hWnd); } } @@ -568,6 +587,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); window->win32.frameAction = GLFW_FALSE; } @@ -586,6 +607,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); return 0; } @@ -594,9 +617,11 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + releaseCursor(); if (window->monitor && window->autoIconify) - _glfwPlatformIconifyWindow(window); + _glfwIconifyWindowWin32(window); _glfwInputWindowFocus(window, GLFW_FALSE); return 0; @@ -650,7 +675,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, window->win32.highSurrogate = (WCHAR) wParam; else { - unsigned int codepoint = 0; + uint32_t codepoint = 0; if (wParam >= 0xdc00 && wParam <= 0xdfff) { @@ -684,7 +709,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return TRUE; } - _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), GLFW_TRUE); + _glfwInputChar(window, (uint32_t) wParam, getKeyMods(), GLFW_TRUE); return 0; } @@ -705,6 +730,18 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC); } + // HACK: Alt+PrtSc has a different scancode than just PrtSc + if (scancode == 0x54) + scancode = 0x137; + + // HACK: Ctrl+Pause has a different scancode than just Pause + if (scancode == 0x146) + scancode = 0x45; + + // HACK: CJK IME sets the extended bit for right Shift + if (scancode == 0x136) + scancode = 0x36; + key = _glfw.win32.keycodes[scancode]; // The Ctrl keys require special handling @@ -756,7 +793,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { // HACK: Release both Shift keys on Shift up event, as when both // are pressed the first release does not emit any event - // NOTE: The other half of this is in _glfwPlatformPollEvents + // NOTE: The other half of this is in _glfwPollEventsWin32 _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods); _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods); } @@ -884,8 +921,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); if (size > (UINT) _glfw.win32.rawInputSize) { - free(_glfw.win32.rawInput); - _glfw.win32.rawInput = calloc(size, 1); + _glfw_free(_glfw.win32.rawInput); + _glfw.win32.rawInput = _glfw_calloc(size, 1); _glfw.win32.rawInputSize = size; } @@ -951,6 +988,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // resizing the window or using the window menu if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + releaseCursor(); break; } @@ -965,6 +1004,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // resizing the window or using the menu if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); break; } @@ -978,8 +1019,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, (window->win32.maximized && wParam != SIZE_RESTORED); - if (_glfw.win32.disabledCursorWindow == window) - updateClipRect(window); + if (_glfw.win32.capturedCursorWindow == window) + captureCursor(window); if (window->win32.iconified != iconified) _glfwInputWindowIconify(window, iconified); @@ -1014,8 +1055,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_MOVE: { - if (_glfw.win32.disabledCursorWindow == window) - updateClipRect(window); + if (_glfw.win32.capturedCursorWindow == window) + captureCursor(window); // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as // those macros do not handle negative window positions correctly @@ -1046,7 +1087,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (window->monitor) break; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) dpi = GetDpiForWindow(window->win32.handle); getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), @@ -1074,7 +1115,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); - GetMonitorInfo(mh, &mi); + GetMonitorInfoW(mh, &mi); mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; @@ -1121,7 +1162,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, break; // Adjust the window size to keep the content area size constant - if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1703OrGreaterWin32()) { RECT source = {0}, target = {0}; SIZE* size = (SIZE*) lParam; @@ -1152,7 +1193,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // need it to compensate for non-client area scaling if (!window->monitor && (window->win32.scaleToMonitor || - _glfwIsWindows10CreatorsUpdateOrGreaterWin32())) + _glfwIsWindows10Version1703OrGreaterWin32())) { RECT* suggested = (RECT*) lParam; SetWindowPos(window->win32.handle, HWND_TOP, @@ -1185,7 +1226,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, int i; const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); - char** paths = calloc(count, sizeof(char*)); + char** paths = _glfw_calloc(count, sizeof(char*)); // Move the mouse to the position of the drop DragQueryPoint(drop, &pt); @@ -1194,19 +1235,19 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, for (i = 0; i < count; i++) { const UINT length = DragQueryFileW(drop, i, NULL, 0); - WCHAR* buffer = calloc((size_t) length + 1, sizeof(WCHAR)); + WCHAR* buffer = _glfw_calloc((size_t) length + 1, sizeof(WCHAR)); DragQueryFileW(drop, i, buffer, length + 1); paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); - free(buffer); + _glfw_free(buffer); } _glfwInputDrop(window, count, (const char**) paths); for (i = 0; i < count; i++) - free(paths[i]); - free(paths); + _glfw_free(paths[i]); + _glfw_free(paths); DragFinish(drop); return 0; @@ -1227,31 +1268,75 @@ static int createNativeWindow(_GLFWwindow* window, DWORD style = getWindowStyle(window); DWORD exStyle = getWindowExStyle(window); + if (!_glfw.win32.mainWindowClass) + { + WNDCLASSEXW wc = { sizeof(wc) }; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = windowProc; + wc.hInstance = _glfw.win32.instance; + wc.hCursor = LoadCursorW(NULL, IDC_ARROW); +#if defined(_GLFW_WNDCLASSNAME) + wc.lpszClassName = _GLFW_WNDCLASSNAME; +#else + wc.lpszClassName = L"GLFW30"; +#endif + // Load user-provided icon if available + wc.hIcon = LoadImageW(GetModuleHandleW(NULL), + L"GLFW_ICON", IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE | LR_SHARED); + if (!wc.hIcon) + { + // No user-provided icon found, load default icon + wc.hIcon = LoadImageW(NULL, + IDI_APPLICATION, IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE | LR_SHARED); + } + + _glfw.win32.mainWindowClass = RegisterClassExW(&wc); + if (!_glfw.win32.mainWindowClass) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register window class"); + return GLFW_FALSE; + } + } + if (window->monitor) { - GLFWvidmode mode; + MONITORINFO mi = { sizeof(mi) }; + GetMonitorInfoW(window->monitor->win32.handle, &mi); // NOTE: This window placement is temporary and approximate, as the // correct position and size cannot be known until the monitor // video mode has been picked in _glfwSetVideoModeWin32 - _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); - _glfwPlatformGetVideoMode(window->monitor, &mode); - fullWidth = mode.width; - fullHeight = mode.height; + xpos = mi.rcMonitor.left; + ypos = mi.rcMonitor.top; + fullWidth = mi.rcMonitor.right - mi.rcMonitor.left; + fullHeight = mi.rcMonitor.bottom - mi.rcMonitor.top; } else { - xpos = CW_USEDEFAULT; - ypos = CW_USEDEFAULT; + RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; window->win32.maximized = wndconfig->maximized; if (wndconfig->maximized) style |= WS_MAXIMIZE; - getFullWindowSize(style, exStyle, - wndconfig->width, wndconfig->height, - &fullWidth, &fullHeight, - USER_DEFAULT_SCREEN_DPI); + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + if (wndconfig->xpos == GLFW_ANY_POSITION && wndconfig->ypos == GLFW_ANY_POSITION) + { + xpos = CW_USEDEFAULT; + ypos = CW_USEDEFAULT; + } + else + { + xpos = wndconfig->xpos + rect.left; + ypos = wndconfig->ypos + rect.top; + } + + fullWidth = rect.right - rect.left; + fullHeight = rect.bottom - rect.top; } wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); @@ -1259,17 +1344,17 @@ static int createNativeWindow(_GLFWwindow* window, return GLFW_FALSE; window->win32.handle = CreateWindowExW(exStyle, - _GLFW_WNDCLASSNAME, + MAKEINTATOM(_glfw.win32.mainWindowClass), wideTitle, style, xpos, ypos, fullWidth, fullHeight, NULL, // No parent window NULL, // No window menu - GetModuleHandleW(NULL), + _glfw.win32.instance, (LPVOID) wndconfig); - free(wideTitle); + _glfw_free(wideTitle); if (!window->win32.handle) { @@ -1293,26 +1378,31 @@ static int createNativeWindow(_GLFWwindow* window, window->win32.scaleToMonitor = wndconfig->scaleToMonitor; window->win32.keymenu = wndconfig->win32.keymenu; - // Adjust window rect to account for DPI scaling of the window frame and - // (if enabled) DPI scaling of the content area - // This cannot be done until we know what monitor the window was placed on if (!window->monitor) { RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; WINDOWPLACEMENT wp = { sizeof(wp) }; + const HMONITOR mh = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + + // Adjust window rect to account for DPI scaling of the window frame and + // (if enabled) DPI scaling of the content area + // This cannot be done until we know what monitor the window was placed on + // Only update the restored window rect as the window may be maximized if (wndconfig->scaleToMonitor) { float xscale, yscale; - _glfwPlatformGetWindowContentScale(window, &xscale, &yscale); - rect.right = (int) (rect.right * xscale); - rect.bottom = (int) (rect.bottom * yscale); + _glfwGetHMONITORContentScaleWin32(mh, &xscale, &yscale); + + if (xscale > 0.f && yscale > 0.f) + { + rect.right = (int) (rect.right * xscale); + rect.bottom = (int) (rect.bottom * yscale); + } } - ClientToScreen(window->win32.handle, (POINT*) &rect.left); - ClientToScreen(window->win32.handle, (POINT*) &rect.right); - - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, GetDpiForWindow(window->win32.handle)); @@ -1320,11 +1410,30 @@ static int createNativeWindow(_GLFWwindow* window, else AdjustWindowRectEx(&rect, style, FALSE, exStyle); - // Only update the restored window rect as the window may be maximized GetWindowPlacement(window->win32.handle, &wp); + OffsetRect(&rect, + wp.rcNormalPosition.left - rect.left, + wp.rcNormalPosition.top - rect.top); + wp.rcNormalPosition = rect; wp.showCmd = SW_HIDE; SetWindowPlacement(window->win32.handle, &wp); + + // Adjust rect of maximized undecorated window, because by default Windows will + // make such a window cover the whole monitor instead of its workarea + + if (wndconfig->maximized && !wndconfig->decorated) + { + MONITORINFO mi = { sizeof(mi) }; + GetMonitorInfoW(mh, &mi); + + SetWindowPos(window->win32.handle, HWND_TOP, + mi.rcWork.left, + mi.rcWork.top, + mi.rcWork.right - mi.rcWork.left, + mi.rcWork.bottom - mi.rcWork.top, + SWP_NOACTIVATE | SWP_NOZORDER); + } } DragAcceptFiles(window->win32.handle, TRUE); @@ -1335,68 +1444,15 @@ static int createNativeWindow(_GLFWwindow* window, window->win32.transparent = GLFW_TRUE; } - _glfwPlatformGetWindowSize(window, &window->win32.width, &window->win32.height); + _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height); return GLFW_TRUE; } - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Registers the GLFW window class -// -GLFWbool _glfwRegisterWindowClassWin32(void) -{ - WNDCLASSEXW wc; - - ZeroMemory(&wc, sizeof(wc)); - wc.cbSize = sizeof(wc); - wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wc.lpfnWndProc = (WNDPROC) windowProc; - wc.hInstance = GetModuleHandleW(NULL); - wc.hCursor = LoadCursorW(NULL, IDC_ARROW); - wc.lpszClassName = _GLFW_WNDCLASSNAME; - - // Load user-provided icon if available - wc.hIcon = LoadImageW(GetModuleHandleW(NULL), - L"GLFW_ICON", IMAGE_ICON, - 0, 0, LR_DEFAULTSIZE | LR_SHARED); - if (!wc.hIcon) - { - // No user-provided icon found, load default icon - wc.hIcon = LoadImageW(NULL, - IDI_APPLICATION, IMAGE_ICON, - 0, 0, LR_DEFAULTSIZE | LR_SHARED); - } - - if (!RegisterClassExW(&wc)) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to register window class"); - return GLFW_FALSE; - } - - return GLFW_TRUE; -} - -// Unregisters the GLFW window class -// -void _glfwUnregisterWindowClassWin32(void) -{ - UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) +GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) { if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; @@ -1424,20 +1480,38 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughWin32(window, GLFW_TRUE); + if (window->monitor) { - _glfwPlatformShowWindow(window); - _glfwPlatformFocusWindow(window); + _glfwShowWindowWin32(window); + _glfwFocusWindowWin32(window); acquireMonitor(window); fitToMonitor(window); + + if (wndconfig->centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig->visible) + { + _glfwShowWindowWin32(window); + if (wndconfig->focused) + _glfwFocusWindowWin32(window); + } } return GLFW_TRUE; } -void _glfwPlatformDestroyWindow(_GLFWwindow* window) +void _glfwDestroyWindowWin32(_GLFWwindow* window) { if (window->monitor) releaseMonitor(window); @@ -1446,7 +1520,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) window->context.destroy(window); if (_glfw.win32.disabledCursorWindow == window) - _glfw.win32.disabledCursorWindow = NULL; + enableCursor(window); + + if (_glfw.win32.capturedCursorWindow == window) + releaseCursor(); if (window->win32.handle) { @@ -1462,18 +1539,17 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) DestroyIcon(window->win32.smallIcon); } -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title) { WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); if (!wideTitle) return; SetWindowTextW(window->win32.handle, wideTitle); - free(wideTitle); + _glfw_free(wideTitle); } -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) +void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images) { HICON bigIcon = NULL, smallIcon = NULL; @@ -1495,8 +1571,8 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM); } - SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); - SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); + SendMessageW(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); + SendMessageW(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); if (window->win32.bigIcon) DestroyIcon(window->win32.bigIcon); @@ -1511,7 +1587,7 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, } } -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos) { POINT pos = { 0, 0 }; ClientToScreen(window->win32.handle, &pos); @@ -1522,11 +1598,11 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) *ypos = pos.y; } -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +void _glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos) { RECT rect = { xpos, ypos, xpos, ypos }; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1542,7 +1618,7 @@ void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); } -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetWindowSizeWin32(_GLFWwindow* window, int* width, int* height) { RECT area; GetClientRect(window->win32.handle, &area); @@ -1553,7 +1629,7 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) *height = area.bottom; } -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +void _glfwSetWindowSizeWin32(_GLFWwindow* window, int width, int height) { if (window->monitor) { @@ -1567,7 +1643,7 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { RECT rect = { 0, 0, width, height }; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1585,9 +1661,9 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) } } -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) +void _glfwSetWindowSizeLimitsWin32(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) { RECT area; @@ -1604,7 +1680,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, area.bottom - area.top, TRUE); } -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +void _glfwSetWindowAspectRatioWin32(_GLFWwindow* window, int numer, int denom) { RECT area; @@ -1619,22 +1695,22 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom area.bottom - area.top, TRUE); } -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetFramebufferSizeWin32(_GLFWwindow* window, int* width, int* height) { - _glfwPlatformGetWindowSize(window, width, height); + _glfwGetWindowSizeWin32(window, width, height); } -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) +void _glfwGetWindowFrameSizeWin32(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) { RECT rect; int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); + _glfwGetWindowSizeWin32(window, &width, &height); SetRect(&rect, 0, 0, width, height); - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1656,56 +1732,58 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, *bottom = rect.bottom - height; } -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) +void _glfwGetWindowContentScaleWin32(_GLFWwindow* window, float* xscale, float* yscale) { const HANDLE handle = MonitorFromWindow(window->win32.handle, MONITOR_DEFAULTTONEAREST); - _glfwGetMonitorContentScaleWin32(handle, xscale, yscale); + _glfwGetHMONITORContentScaleWin32(handle, xscale, yscale); } -void _glfwPlatformIconifyWindow(_GLFWwindow* window) +void _glfwIconifyWindowWin32(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_MINIMIZE); } -void _glfwPlatformRestoreWindow(_GLFWwindow* window) +void _glfwRestoreWindowWin32(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_RESTORE); } -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +void _glfwMaximizeWindowWin32(_GLFWwindow* window) { - ShowWindow(window->win32.handle, SW_MAXIMIZE); + if (IsWindowVisible(window->win32.handle)) + ShowWindow(window->win32.handle, SW_MAXIMIZE); + else + maximizeWindowManually(window); } -void _glfwPlatformShowWindow(_GLFWwindow* window) +void _glfwShowWindowWin32(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_SHOWNA); } -void _glfwPlatformHideWindow(_GLFWwindow* window) +void _glfwHideWindowWin32(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_HIDE); } -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +void _glfwRequestWindowAttentionWin32(_GLFWwindow* window) { FlashWindow(window->win32.handle, TRUE); } -void _glfwPlatformFocusWindow(_GLFWwindow* window) +void _glfwFocusWindowWin32(_GLFWwindow* window) { BringWindowToTop(window->win32.handle); SetForegroundWindow(window->win32.handle); SetFocus(window->win32.handle); } -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) +void _glfwSetWindowMonitorWin32(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) { if (window->monitor == monitor) { @@ -1721,7 +1799,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, { RECT rect = { xpos, ypos, xpos + width, ypos + height }; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1763,7 +1841,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, acquireMonitor(window); - GetMonitorInfo(window->monitor->win32.handle, &mi); + GetMonitorInfoW(window->monitor->win32.handle, &mi); SetWindowPos(window->win32.handle, HWND_TOPMOST, mi.rcMonitor.left, mi.rcMonitor.top, @@ -1792,7 +1870,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else after = HWND_NOTOPMOST; - if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + if (_glfwIsWindows10Version1607OrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), @@ -1811,32 +1889,32 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, } } -int _glfwPlatformWindowFocused(_GLFWwindow* window) +GLFWbool _glfwWindowFocusedWin32(_GLFWwindow* window) { return window->win32.handle == GetActiveWindow(); } -int _glfwPlatformWindowIconified(_GLFWwindow* window) +GLFWbool _glfwWindowIconifiedWin32(_GLFWwindow* window) { return IsIconic(window->win32.handle); } -int _glfwPlatformWindowVisible(_GLFWwindow* window) +GLFWbool _glfwWindowVisibleWin32(_GLFWwindow* window) { return IsWindowVisible(window->win32.handle); } -int _glfwPlatformWindowMaximized(_GLFWwindow* window) +GLFWbool _glfwWindowMaximizedWin32(_GLFWwindow* window) { return IsZoomed(window->win32.handle); } -int _glfwPlatformWindowHovered(_GLFWwindow* window) +GLFWbool _glfwWindowHoveredWin32(_GLFWwindow* window) { return cursorInContentArea(window); } -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +GLFWbool _glfwFramebufferTransparentWin32(_GLFWwindow* window) { BOOL composition, opaque; DWORD color; @@ -1863,24 +1941,24 @@ int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) return GLFW_TRUE; } -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowResizableWin32(_GLFWwindow* window, GLFWbool enabled) { updateWindowStyles(window); } -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowDecoratedWin32(_GLFWwindow* window, GLFWbool enabled) { updateWindowStyles(window); } -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowFloatingWin32(_GLFWwindow* window, GLFWbool enabled) { const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; SetWindowPos(window->win32.handle, after, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } -void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowMousePassthroughWin32(_GLFWwindow* window, GLFWbool enabled) { COLORREF key = 0; BYTE alpha = 0; @@ -1910,7 +1988,7 @@ void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enable SetLayeredWindowAttributes(window->win32.handle, key, alpha, flags); } -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +float _glfwGetWindowOpacityWin32(_GLFWwindow* window) { BYTE alpha; DWORD flags; @@ -1925,7 +2003,7 @@ float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) return 1.f; } -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +void _glfwSetWindowOpacityWin32(_GLFWwindow* window, float opacity) { LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); if (opacity < 1.f || (exStyle & WS_EX_TRANSPARENT)) @@ -1946,7 +2024,7 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) } } -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +void _glfwSetRawMouseMotionWin32(_GLFWwindow *window, GLFWbool enabled) { if (_glfw.win32.disabledCursorWindow != window) return; @@ -1957,12 +2035,12 @@ void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) disableRawMouseMotion(window); } -GLFWbool _glfwPlatformRawMouseMotionSupported(void) +GLFWbool _glfwRawMouseMotionSupportedWin32(void) { return GLFW_TRUE; } -void _glfwPlatformPollEvents(void) +void _glfwPollEventsWin32(void) { MSG msg; HWND handle; @@ -2032,38 +2110,38 @@ void _glfwPlatformPollEvents(void) if (window) { int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); + _glfwGetWindowSizeWin32(window, &width, &height); // NOTE: Re-center the cursor only if it has moved since the last call, // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE if (window->win32.lastCursorPosX != width / 2 || window->win32.lastCursorPosY != height / 2) { - _glfwPlatformSetCursorPos(window, width / 2, height / 2); + _glfwSetCursorPosWin32(window, width / 2, height / 2); } } } -void _glfwPlatformWaitEvents(void) +void _glfwWaitEventsWin32(void) { WaitMessage(); - _glfwPlatformPollEvents(); + _glfwPollEventsWin32(); } -void _glfwPlatformWaitEventsTimeout(double timeout) +void _glfwWaitEventsTimeoutWin32(double timeout) { MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS); - _glfwPlatformPollEvents(); + _glfwPollEventsWin32(); } -void _glfwPlatformPostEmptyEvent(void) +void _glfwPostEmptyEventWin32(void) { - PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); + PostMessageW(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); } -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +void _glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos) { POINT pos; @@ -2078,7 +2156,7 @@ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) } } -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) +void _glfwSetCursorPosWin32(_GLFWwindow* window, double xpos, double ypos) { POINT pos = { (int) xpos, (int) ypos }; @@ -2090,39 +2168,65 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) SetCursorPos(pos.x, pos.y); } -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +void _glfwSetCursorModeWin32(_GLFWwindow* window, int mode) { - if (mode == GLFW_CURSOR_DISABLED) + if (_glfwWindowFocusedWin32(window)) { - if (_glfwPlatformWindowFocused(window)) - disableCursor(window); + if (mode == GLFW_CURSOR_DISABLED) + { + _glfwGetCursorPosWin32(window, + &_glfw.win32.restoreCursorPosX, + &_glfw.win32.restoreCursorPosY); + _glfwCenterCursorInContentArea(window); + if (window->rawMouseMotion) + enableRawMouseMotion(window); + } + else if (_glfw.win32.disabledCursorWindow == window) + { + if (window->rawMouseMotion) + disableRawMouseMotion(window); + } + + if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED) + captureCursor(window); + else + releaseCursor(); + + if (mode == GLFW_CURSOR_DISABLED) + _glfw.win32.disabledCursorWindow = window; + else if (_glfw.win32.disabledCursorWindow == window) + { + _glfw.win32.disabledCursorWindow = NULL; + _glfwSetCursorPosWin32(window, + _glfw.win32.restoreCursorPosX, + _glfw.win32.restoreCursorPosY); + } } - else if (_glfw.win32.disabledCursorWindow == window) - enableCursor(window); - else if (cursorInContentArea(window)) + + if (cursorInContentArea(window)) updateCursorImage(window); } -const char* _glfwPlatformGetScancodeName(int scancode) +const char* _glfwGetScancodeNameWin32(int scancode) { if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) || _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN) { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; } return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; } -int _glfwPlatformGetKeyScancode(int key) +int _glfwGetKeyScancodeWin32(int key) { return _glfw.win32.scancodes[key]; } -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) +GLFWbool _glfwCreateCursorWin32(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) { cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE); if (!cursor->win32.handle) @@ -2131,7 +2235,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, return GLFW_TRUE; } -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +GLFWbool _glfwCreateStandardCursorWin32(_GLFWcursor* cursor, int shape) { int id = 0; @@ -2185,19 +2289,19 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) return GLFW_TRUE; } -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +void _glfwDestroyCursorWin32(_GLFWcursor* cursor) { if (cursor->win32.handle) DestroyIcon((HICON) cursor->win32.handle); } -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +void _glfwSetCursorWin32(_GLFWwindow* window, _GLFWcursor* cursor) { if (cursorInContentArea(window)) updateCursorImage(window); } -void _glfwPlatformSetClipboardString(const char* string) +void _glfwSetClipboardStringWin32(const char* string) { int characterCount; HANDLE object; @@ -2240,7 +2344,7 @@ void _glfwPlatformSetClipboardString(const char* string) CloseClipboard(); } -const char* _glfwPlatformGetClipboardString(void) +const char* _glfwGetClipboardStringWin32(void) { HANDLE object; WCHAR* buffer; @@ -2270,7 +2374,7 @@ const char* _glfwPlatformGetClipboardString(void) return NULL; } - free(_glfw.win32.clipboardString); + _glfw_free(_glfw.win32.clipboardString); _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer); GlobalUnlock(object); @@ -2279,7 +2383,7 @@ const char* _glfwPlatformGetClipboardString(void) return _glfw.win32.clipboardString; } -EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) +EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) { @@ -2309,7 +2413,7 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) if (type) { - *attribs = calloc(3, sizeof(EGLint)); + *attribs = _glfw_calloc(3, sizeof(EGLint)); (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE; (*attribs)[1] = type; (*attribs)[2] = EGL_NONE; @@ -2320,17 +2424,17 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) return 0; } -EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void) +EGLNativeDisplayType _glfwGetEGLNativeDisplayWin32(void) { return GetDC(_glfw.win32.helperWindowHandle); } -EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window) +EGLNativeWindowType _glfwGetEGLNativeWindowWin32(_GLFWwindow* window) { return window->win32.handle; } -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +void _glfwGetRequiredInstanceExtensionsWin32(char** extensions) { if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface) return; @@ -2339,9 +2443,9 @@ void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) extensions[1] = "VK_KHR_win32_surface"; } -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) +GLFWbool _glfwGetPhysicalDevicePresentationSupportWin32(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) { PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = @@ -2357,10 +2461,10 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily); } -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) +VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) { VkResult err; VkWin32SurfaceCreateInfoKHR sci; @@ -2377,7 +2481,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - sci.hinstance = GetModuleHandle(NULL); + sci.hinstance = _glfw.win32.instance; sci.hwnd = window->win32.handle; err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); @@ -2391,15 +2495,18 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, return err; } - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, + "Win32: Platform not initialized"); + return NULL; + } + return window->win32.handle; } diff --git a/src/external/glfw/src/window.c b/src/external/glfw/src/window.c index 518b27fd5..1c8519ff7 100644 --- a/src/external/glfw/src/window.c +++ b/src/external/glfw/src/window.c @@ -44,6 +44,9 @@ // void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) { + assert(window != NULL); + assert(focused == GLFW_TRUE || focused == GLFW_FALSE); + if (window->callbacks.focus) window->callbacks.focus((GLFWwindow*) window, focused); @@ -55,7 +58,7 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) { if (window->keys[key] == GLFW_PRESS) { - const int scancode = _glfwPlatformGetKeyScancode(key); + const int scancode = _glfw.platform.getKeyScancode(key); _glfwInputKey(window, key, scancode, GLFW_RELEASE, 0); } } @@ -73,6 +76,8 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) // void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) { + assert(window != NULL); + if (window->callbacks.pos) window->callbacks.pos((GLFWwindow*) window, x, y); } @@ -82,6 +87,10 @@ void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) // void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) { + assert(window != NULL); + assert(width >= 0); + assert(height >= 0); + if (window->callbacks.size) window->callbacks.size((GLFWwindow*) window, width, height); } @@ -90,6 +99,9 @@ void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) // void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) { + assert(window != NULL); + assert(iconified == GLFW_TRUE || iconified == GLFW_FALSE); + if (window->callbacks.iconify) window->callbacks.iconify((GLFWwindow*) window, iconified); } @@ -98,6 +110,9 @@ void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) // void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) { + assert(window != NULL); + assert(maximized == GLFW_TRUE || maximized == GLFW_FALSE); + if (window->callbacks.maximize) window->callbacks.maximize((GLFWwindow*) window, maximized); } @@ -107,6 +122,10 @@ void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) // void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) { + assert(window != NULL); + assert(width >= 0); + assert(height >= 0); + if (window->callbacks.fbsize) window->callbacks.fbsize((GLFWwindow*) window, width, height); } @@ -116,6 +135,12 @@ void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) // void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale) { + assert(window != NULL); + assert(xscale > 0.f); + assert(xscale < FLT_MAX); + assert(yscale > 0.f); + assert(yscale < FLT_MAX); + if (window->callbacks.scale) window->callbacks.scale((GLFWwindow*) window, xscale, yscale); } @@ -124,6 +149,8 @@ void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscal // void _glfwInputWindowDamage(_GLFWwindow* window) { + assert(window != NULL); + if (window->callbacks.refresh) window->callbacks.refresh((GLFWwindow*) window); } @@ -132,6 +159,8 @@ void _glfwInputWindowDamage(_GLFWwindow* window) // void _glfwInputWindowCloseRequest(_GLFWwindow* window) { + assert(window != NULL); + window->shouldClose = GLFW_TRUE; if (window->callbacks.close) @@ -142,6 +171,7 @@ void _glfwInputWindowCloseRequest(_GLFWwindow* window) // void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor) { + assert(window != NULL); window->monitor = monitor; } @@ -186,7 +216,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, if (!_glfwIsValidContextConfig(&ctxconfig)) return NULL; - window = calloc(1, sizeof(_GLFWwindow)); + window = _glfw_calloc(1, sizeof(_GLFWwindow)); window->next = _glfw.windowListHead; _glfw.windowListHead = window; @@ -215,40 +245,12 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->numer = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE; - // Open the actual window and create its context - if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) + if (!_glfw.platform.createWindow(window, &wndconfig, &ctxconfig, &fbconfig)) { glfwDestroyWindow((GLFWwindow*) window); return NULL; } - if (ctxconfig.client != GLFW_NO_API) - { - if (!_glfwRefreshContextAttribs(window, &ctxconfig)) - { - glfwDestroyWindow((GLFWwindow*) window); - return NULL; - } - } - - if (wndconfig.mousePassthrough) - _glfwPlatformSetWindowMousePassthrough(window, GLFW_TRUE); - - if (window->monitor) - { - if (wndconfig.centerCursor) - _glfwCenterCursorInContentArea(window); - } - else - { - if (wndconfig.visible) - { - _glfwPlatformShowWindow(window); - if (wndconfig.focused) - _glfwPlatformFocusWindow(window); - } - } - return (GLFWwindow*) window; } @@ -272,6 +274,8 @@ void glfwDefaultWindowHints(void) _glfw.hints.window.autoIconify = GLFW_TRUE; _glfw.hints.window.centerCursor = GLFW_TRUE; _glfw.hints.window.focusOnShow = GLFW_TRUE; + _glfw.hints.window.xpos = GLFW_ANY_POSITION; + _glfw.hints.window.ypos = GLFW_ANY_POSITION; // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // double buffered @@ -366,6 +370,12 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_VISIBLE: _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; return; + case GLFW_POSITION_X: + _glfw.hints.window.xpos = value; + return; + case GLFW_POSITION_Y: + _glfw.hints.window.ypos = value; + return; case GLFW_COCOA_RETINA_FRAMEBUFFER: _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; return; @@ -445,6 +455,10 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value) strncpy(_glfw.hints.window.x11.instanceName, value, sizeof(_glfw.hints.window.x11.instanceName) - 1); return; + case GLFW_WAYLAND_APP_ID: + strncpy(_glfw.hints.window.wl.appId, value, + sizeof(_glfw.hints.window.wl.appId) - 1); + return; } _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint); @@ -468,7 +482,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) if (window == _glfwPlatformGetTls(&_glfw.contextSlot)) glfwMakeContextCurrent(NULL); - _glfwPlatformDestroyWindow(window); + _glfw.platform.destroyWindow(window); // Unlink window from global linked list { @@ -480,7 +494,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) *prev = window->next; } - free(window); + _glfw_free(window); } GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) @@ -508,19 +522,40 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) assert(title != NULL); _GLFW_REQUIRE_INIT(); - _glfwPlatformSetWindowTitle(window, title); + _glfw.platform.setWindowTitle(window, title); } GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle, int count, const GLFWimage* images) { + int i; _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); assert(count >= 0); assert(count == 0 || images != NULL); _GLFW_REQUIRE_INIT(); - _glfwPlatformSetWindowIcon(window, count, images); + + if (count < 0) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid image count for window icon"); + return; + } + + for (i = 0; i < count; i++) + { + assert(images[i].pixels != NULL); + + if (images[i].width <= 0 || images[i].height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid image dimensions for window icon"); + return; + } + } + + _glfw.platform.setWindowIcon(window, count, images); } GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) @@ -534,7 +569,7 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) *ypos = 0; _GLFW_REQUIRE_INIT(); - _glfwPlatformGetWindowPos(window, xpos, ypos); + _glfw.platform.getWindowPos(window, xpos, ypos); } GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) @@ -547,7 +582,7 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) if (window->monitor) return; - _glfwPlatformSetWindowPos(window, xpos, ypos); + _glfw.platform.setWindowPos(window, xpos, ypos); } GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) @@ -561,7 +596,7 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) *height = 0; _GLFW_REQUIRE_INIT(); - _glfwPlatformGetWindowSize(window, width, height); + _glfw.platform.getWindowSize(window, width, height); } GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) @@ -576,7 +611,7 @@ GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) window->videoMode.width = width; window->videoMode.height = height; - _glfwPlatformSetWindowSize(window, width, height); + _glfw.platform.setWindowSize(window, width, height); } GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, @@ -619,9 +654,9 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, if (window->monitor || !window->resizable) return; - _glfwPlatformSetWindowSizeLimits(window, - minwidth, minheight, - maxwidth, maxheight); + _glfw.platform.setWindowSizeLimits(window, + minwidth, minheight, + maxwidth, maxheight); } GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) @@ -650,7 +685,7 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) if (window->monitor || !window->resizable) return; - _glfwPlatformSetWindowAspectRatio(window, numer, denom); + _glfw.platform.setWindowAspectRatio(window, numer, denom); } GLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height) @@ -664,7 +699,7 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height) *height = 0; _GLFW_REQUIRE_INIT(); - _glfwPlatformGetFramebufferSize(window, width, height); + _glfw.platform.getFramebufferSize(window, width, height); } GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, @@ -684,7 +719,7 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, *bottom = 0; _GLFW_REQUIRE_INIT(); - _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); + _glfw.platform.getWindowFrameSize(window, left, top, right, bottom); } GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, @@ -699,7 +734,7 @@ GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, *yscale = 0.f; _GLFW_REQUIRE_INIT(); - _glfwPlatformGetWindowContentScale(window, xscale, yscale); + _glfw.platform.getWindowContentScale(window, xscale, yscale); } GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle) @@ -708,7 +743,7 @@ GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle) assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(1.f); - return _glfwPlatformGetWindowOpacity(window); + return _glfw.platform.getWindowOpacity(window); } GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity) @@ -727,7 +762,7 @@ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity) return; } - _glfwPlatformSetWindowOpacity(window, opacity); + _glfw.platform.setWindowOpacity(window, opacity); } GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) @@ -736,7 +771,7 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) assert(window != NULL); _GLFW_REQUIRE_INIT(); - _glfwPlatformIconifyWindow(window); + _glfw.platform.iconifyWindow(window); } GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) @@ -745,7 +780,7 @@ GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) assert(window != NULL); _GLFW_REQUIRE_INIT(); - _glfwPlatformRestoreWindow(window); + _glfw.platform.restoreWindow(window); } GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) @@ -758,7 +793,7 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) if (window->monitor) return; - _glfwPlatformMaximizeWindow(window); + _glfw.platform.maximizeWindow(window); } GLFWAPI void glfwShowWindow(GLFWwindow* handle) @@ -771,10 +806,10 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle) if (window->monitor) return; - _glfwPlatformShowWindow(window); + _glfw.platform.showWindow(window); if (window->focusOnShow) - _glfwPlatformFocusWindow(window); + _glfw.platform.focusWindow(window); } GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) @@ -784,7 +819,7 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) _GLFW_REQUIRE_INIT(); - _glfwPlatformRequestWindowAttention(window); + _glfw.platform.requestWindowAttention(window); } GLFWAPI void glfwHideWindow(GLFWwindow* handle) @@ -797,7 +832,7 @@ GLFWAPI void glfwHideWindow(GLFWwindow* handle) if (window->monitor) return; - _glfwPlatformHideWindow(window); + _glfw.platform.hideWindow(window); } GLFWAPI void glfwFocusWindow(GLFWwindow* handle) @@ -807,7 +842,7 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* handle) _GLFW_REQUIRE_INIT(); - _glfwPlatformFocusWindow(window); + _glfw.platform.focusWindow(window); } GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) @@ -820,21 +855,21 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) switch (attrib) { case GLFW_FOCUSED: - return _glfwPlatformWindowFocused(window); + return _glfw.platform.windowFocused(window); case GLFW_ICONIFIED: - return _glfwPlatformWindowIconified(window); + return _glfw.platform.windowIconified(window); case GLFW_VISIBLE: - return _glfwPlatformWindowVisible(window); + return _glfw.platform.windowVisible(window); case GLFW_MAXIMIZED: - return _glfwPlatformWindowMaximized(window); + return _glfw.platform.windowMaximized(window); case GLFW_HOVERED: - return _glfwPlatformWindowHovered(window); + return _glfw.platform.windowHovered(window); case GLFW_FOCUS_ON_SHOW: return window->focusOnShow; case GLFW_MOUSE_PASSTHROUGH: return window->mousePassthrough; case GLFW_TRANSPARENT_FRAMEBUFFER: - return _glfwPlatformFramebufferTransparent(window); + return _glfw.platform.framebufferTransparent(window); case GLFW_RESIZABLE: return window->resizable; case GLFW_DECORATED: @@ -882,35 +917,41 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) value = value ? GLFW_TRUE : GLFW_FALSE; - if (attrib == GLFW_AUTO_ICONIFY) - window->autoIconify = value; - else if (attrib == GLFW_RESIZABLE) + switch (attrib) { - window->resizable = value; - if (!window->monitor) - _glfwPlatformSetWindowResizable(window, value); + case GLFW_AUTO_ICONIFY: + window->autoIconify = value; + return; + + case GLFW_RESIZABLE: + window->resizable = value; + if (!window->monitor) + _glfw.platform.setWindowResizable(window, value); + return; + + case GLFW_DECORATED: + window->decorated = value; + if (!window->monitor) + _glfw.platform.setWindowDecorated(window, value); + return; + + case GLFW_FLOATING: + window->floating = value; + if (!window->monitor) + _glfw.platform.setWindowFloating(window, value); + return; + + case GLFW_FOCUS_ON_SHOW: + window->focusOnShow = value; + return; + + case GLFW_MOUSE_PASSTHROUGH: + window->mousePassthrough = value; + _glfw.platform.setWindowMousePassthrough(window, value); + return; } - else if (attrib == GLFW_DECORATED) - { - window->decorated = value; - if (!window->monitor) - _glfwPlatformSetWindowDecorated(window, value); - } - else if (attrib == GLFW_FLOATING) - { - window->floating = value; - if (!window->monitor) - _glfwPlatformSetWindowFloating(window, value); - } - else if (attrib == GLFW_FOCUS_ON_SHOW) - window->focusOnShow = value; - else if (attrib == GLFW_MOUSE_PASSTHROUGH) - { - window->mousePassthrough = value; - _glfwPlatformSetWindowMousePassthrough(window, value); - } - else - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); } GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) @@ -956,9 +997,9 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, window->videoMode.height = height; window->videoMode.refreshRate = refreshRate; - _glfwPlatformSetWindowMonitor(window, monitor, - xpos, ypos, width, height, - refreshRate); + _glfw.platform.setWindowMonitor(window, monitor, + xpos, ypos, width, height, + refreshRate); } GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) @@ -986,7 +1027,7 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.pos, cbfun); + _GLFW_SWAP(GLFWwindowposfun, window->callbacks.pos, cbfun); return cbfun; } @@ -997,7 +1038,7 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.size, cbfun); + _GLFW_SWAP(GLFWwindowsizefun, window->callbacks.size, cbfun); return cbfun; } @@ -1008,7 +1049,7 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.close, cbfun); + _GLFW_SWAP(GLFWwindowclosefun, window->callbacks.close, cbfun); return cbfun; } @@ -1019,7 +1060,7 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.refresh, cbfun); + _GLFW_SWAP(GLFWwindowrefreshfun, window->callbacks.refresh, cbfun); return cbfun; } @@ -1030,7 +1071,7 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.focus, cbfun); + _GLFW_SWAP(GLFWwindowfocusfun, window->callbacks.focus, cbfun); return cbfun; } @@ -1041,7 +1082,7 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.iconify, cbfun); + _GLFW_SWAP(GLFWwindowiconifyfun, window->callbacks.iconify, cbfun); return cbfun; } @@ -1052,7 +1093,7 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle, assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun); + _GLFW_SWAP(GLFWwindowmaximizefun, window->callbacks.maximize, cbfun); return cbfun; } @@ -1063,7 +1104,7 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.fbsize, cbfun); + _GLFW_SWAP(GLFWframebuffersizefun, window->callbacks.fbsize, cbfun); return cbfun; } @@ -1074,20 +1115,20 @@ GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFW_SWAP_POINTERS(window->callbacks.scale, cbfun); + _GLFW_SWAP(GLFWwindowcontentscalefun, window->callbacks.scale, cbfun); return cbfun; } GLFWAPI void glfwPollEvents(void) { _GLFW_REQUIRE_INIT(); - _glfwPlatformPollEvents(); + _glfw.platform.pollEvents(); } GLFWAPI void glfwWaitEvents(void) { _GLFW_REQUIRE_INIT(); - _glfwPlatformWaitEvents(); + _glfw.platform.waitEvents(); } GLFWAPI void glfwWaitEventsTimeout(double timeout) @@ -1103,12 +1144,12 @@ GLFWAPI void glfwWaitEventsTimeout(double timeout) return; } - _glfwPlatformWaitEventsTimeout(timeout); + _glfw.platform.waitEventsTimeout(timeout); } GLFWAPI void glfwPostEmptyEvent(void) { _GLFW_REQUIRE_INIT(); - _glfwPlatformPostEmptyEvent(); + _glfw.platform.postEmptyEvent(); } diff --git a/src/external/glfw/src/wl_init.c b/src/external/glfw/src/wl_init.c index 21a808aac..8cbcc6e8e 100644 --- a/src/external/glfw/src/wl_init.c +++ b/src/external/glfw/src/wl_init.c @@ -26,11 +26,8 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== -#define _POSIX_C_SOURCE 199309L - #include "internal.h" -#include #include #include #include @@ -41,746 +38,61 @@ #include #include #include -#include +#include "wayland-client-protocol.h" +#include "wayland-xdg-shell-client-protocol.h" +#include "wayland-xdg-decoration-client-protocol.h" +#include "wayland-viewporter-client-protocol.h" +#include "wayland-relative-pointer-unstable-v1-client-protocol.h" +#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" +#include "wayland-idle-inhibit-unstable-v1-client-protocol.h" -static inline int min(int n1, int n2) -{ - return n1 < n2 ? n1 : n2; -} +// NOTE: Versions of wayland-scanner prior to 1.17.91 named every global array of +// wl_interface pointers 'types', making it impossible to combine several unmodified +// private-code files into a single compilation unit +// HACK: We override this name with a macro for each file, allowing them to coexist -static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, - int* which) -{ - int focus; - _GLFWwindow* window = _glfw.windowListHead; - if (!which) - which = &focus; - while (window) - { - if (surface == window->wl.decorations.top.surface) - { - *which = topDecoration; - break; - } - if (surface == window->wl.decorations.left.surface) - { - *which = leftDecoration; - break; - } - if (surface == window->wl.decorations.right.surface) - { - *which = rightDecoration; - break; - } - if (surface == window->wl.decorations.bottom.surface) - { - *which = bottomDecoration; - break; - } - window = window->next; - } - return window; -} +#define types _glfw_wayland_types +#include "wayland-client-protocol-code.h" +#undef types -static void pointerHandleEnter(void* data, - struct wl_pointer* pointer, - uint32_t serial, - struct wl_surface* surface, - wl_fixed_t sx, - wl_fixed_t sy) -{ - // Happens in the case we just destroyed the surface. - if (!surface) - return; +#define types _glfw_xdg_shell_types +#include "wayland-xdg-shell-client-protocol-code.h" +#undef types - int focus = 0; - _GLFWwindow* window = wl_surface_get_user_data(surface); - if (!window) - { - window = findWindowFromDecorationSurface(surface, &focus); - if (!window) - return; - } +#define types _glfw_xdg_decoration_types +#include "wayland-xdg-decoration-client-protocol-code.h" +#undef types - window->wl.decorations.focus = focus; - _glfw.wl.serial = serial; - _glfw.wl.pointerFocus = window; +#define types _glfw_viewporter_types +#include "wayland-viewporter-client-protocol-code.h" +#undef types - window->wl.hovered = GLFW_TRUE; +#define types _glfw_relative_pointer_types +#include "wayland-relative-pointer-unstable-v1-client-protocol-code.h" +#undef types - _glfwPlatformSetCursor(window, window->wl.currentCursor); - _glfwInputCursorEnter(window, GLFW_TRUE); -} +#define types _glfw_pointer_constraints_types +#include "wayland-pointer-constraints-unstable-v1-client-protocol-code.h" +#undef types -static void pointerHandleLeave(void* data, - struct wl_pointer* pointer, - uint32_t serial, - struct wl_surface* surface) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; +#define types _glfw_idle_inhibit_types +#include "wayland-idle-inhibit-unstable-v1-client-protocol-code.h" +#undef types - if (!window) - return; - - window->wl.hovered = GLFW_FALSE; - - _glfw.wl.serial = serial; - _glfw.wl.pointerFocus = NULL; - _glfwInputCursorEnter(window, GLFW_FALSE); - _glfw.wl.cursorPreviousName = NULL; -} - -static void setCursor(_GLFWwindow* window, const char* name) -{ - struct wl_buffer* buffer; - struct wl_cursor* cursor; - struct wl_cursor_image* image; - struct wl_surface* surface = _glfw.wl.cursorSurface; - struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; - int scale = 1; - - if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) - { - // We only support up to scale=2 for now, since libwayland-cursor - // requires us to load a different theme for each size. - scale = 2; - theme = _glfw.wl.cursorThemeHiDPI; - } - - cursor = wl_cursor_theme_get_cursor(theme, name); - if (!cursor) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Standard cursor not found"); - return; - } - // TODO: handle animated cursors too. - image = cursor->images[0]; - - if (!image) - return; - - buffer = wl_cursor_image_get_buffer(image); - if (!buffer) - return; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, - surface, - image->hotspot_x / scale, - image->hotspot_y / scale); - wl_surface_set_buffer_scale(surface, scale); - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - image->width, image->height); - wl_surface_commit(surface); - _glfw.wl.cursorPreviousName = name; -} - -static void pointerHandleMotion(void* data, - struct wl_pointer* pointer, - uint32_t time, - wl_fixed_t sx, - wl_fixed_t sy) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - const char* cursorName = NULL; - double x, y; - - if (!window) - return; - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - return; - x = wl_fixed_to_double(sx); - y = wl_fixed_to_double(sy); - - switch (window->wl.decorations.focus) - { - case mainWindow: - window->wl.cursorPosX = x; - window->wl.cursorPosY = y; - _glfwInputCursorPos(window, x, y); - _glfw.wl.cursorPreviousName = NULL; - return; - case topDecoration: - if (y < _GLFW_DECORATION_WIDTH) - cursorName = "n-resize"; - else - cursorName = "left_ptr"; - break; - case leftDecoration: - if (y < _GLFW_DECORATION_WIDTH) - cursorName = "nw-resize"; - else - cursorName = "w-resize"; - break; - case rightDecoration: - if (y < _GLFW_DECORATION_WIDTH) - cursorName = "ne-resize"; - else - cursorName = "e-resize"; - break; - case bottomDecoration: - if (x < _GLFW_DECORATION_WIDTH) - cursorName = "sw-resize"; - else if (x > window->wl.width + _GLFW_DECORATION_WIDTH) - cursorName = "se-resize"; - else - cursorName = "s-resize"; - break; - default: - assert(0); - } - if (_glfw.wl.cursorPreviousName != cursorName) - setCursor(window, cursorName); -} - -static void pointerHandleButton(void* data, - struct wl_pointer* pointer, - uint32_t serial, - uint32_t time, - uint32_t button, - uint32_t state) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - int glfwButton; - uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; - - if (!window) - return; - if (button == BTN_LEFT) - { - switch (window->wl.decorations.focus) - { - case mainWindow: - break; - case topDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; - else - { - xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); - } - break; - case leftDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; - break; - case rightDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; - break; - case bottomDecoration: - if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH) - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - else - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; - break; - default: - assert(0); - } - if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) - { - xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, - serial, edges); - } - } - else if (button == BTN_RIGHT) - { - if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) - { - xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, - _glfw.wl.seat, serial, - window->wl.cursorPosX, - window->wl.cursorPosY); - return; - } - } - - // Don’t pass the button to the user if it was related to a decoration. - if (window->wl.decorations.focus != mainWindow) - return; - - _glfw.wl.serial = serial; - - /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev - * codes. */ - glfwButton = button - BTN_LEFT; - - _glfwInputMouseClick(window, - glfwButton, - state == WL_POINTER_BUTTON_STATE_PRESSED - ? GLFW_PRESS - : GLFW_RELEASE, - _glfw.wl.xkb.modifiers); -} - -static void pointerHandleAxis(void* data, - struct wl_pointer* pointer, - uint32_t time, - uint32_t axis, - wl_fixed_t value) -{ - _GLFWwindow* window = _glfw.wl.pointerFocus; - double x = 0.0, y = 0.0; - // Wayland scroll events are in pointer motion coordinate space (think two - // finger scroll). The factor 10 is commonly used to convert to "scroll - // step means 1.0. - const double scrollFactor = 1.0 / 10.0; - - if (!window) - return; - - assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || - axis == WL_POINTER_AXIS_VERTICAL_SCROLL); - - if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) - x = -wl_fixed_to_double(value) * scrollFactor; - else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) - y = -wl_fixed_to_double(value) * scrollFactor; - - _glfwInputScroll(window, x, y); -} - -static const struct wl_pointer_listener pointerListener = { - pointerHandleEnter, - pointerHandleLeave, - pointerHandleMotion, - pointerHandleButton, - pointerHandleAxis, -}; - -static void keyboardHandleKeymap(void* data, - struct wl_keyboard* keyboard, - uint32_t format, - int fd, - uint32_t size) -{ - struct xkb_keymap* keymap; - struct xkb_state* state; - -#ifdef HAVE_XKBCOMMON_COMPOSE_H - struct xkb_compose_table* composeTable; - struct xkb_compose_state* composeState; -#endif - - char* mapStr; - const char* locale; - - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) - { - close(fd); - return; - } - - mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (mapStr == MAP_FAILED) { - close(fd); - return; - } - - keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, - mapStr, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); - munmap(mapStr, size); - close(fd); - - if (!keymap) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to compile keymap"); - return; - } - - state = xkb_state_new(keymap); - if (!state) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB state"); - xkb_keymap_unref(keymap); - return; - } - - // Look up the preferred locale, falling back to "C" as default. - locale = getenv("LC_ALL"); - if (!locale) - locale = getenv("LC_CTYPE"); - if (!locale) - locale = getenv("LANG"); - if (!locale) - locale = "C"; - -#ifdef HAVE_XKBCOMMON_COMPOSE_H - composeTable = - xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, - XKB_COMPOSE_COMPILE_NO_FLAGS); - if (composeTable) - { - composeState = - xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); - xkb_compose_table_unref(composeTable); - if (composeState) - _glfw.wl.xkb.composeState = composeState; - else - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB compose state"); - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to create XKB compose table"); - } -#endif - - xkb_keymap_unref(_glfw.wl.xkb.keymap); - xkb_state_unref(_glfw.wl.xkb.state); - _glfw.wl.xkb.keymap = keymap; - _glfw.wl.xkb.state = state; - - _glfw.wl.xkb.controlMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); - _glfw.wl.xkb.altMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); - _glfw.wl.xkb.shiftMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); - _glfw.wl.xkb.superMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); - _glfw.wl.xkb.capsLockMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock"); - _glfw.wl.xkb.numLockMask = - 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); -} - -static void keyboardHandleEnter(void* data, - struct wl_keyboard* keyboard, - uint32_t serial, - struct wl_surface* surface, - struct wl_array* keys) -{ - // Happens in the case we just destroyed the surface. - if (!surface) - return; - - _GLFWwindow* window = wl_surface_get_user_data(surface); - if (!window) - { - window = findWindowFromDecorationSurface(surface, NULL); - if (!window) - return; - } - - _glfw.wl.serial = serial; - _glfw.wl.keyboardFocus = window; - _glfwInputWindowFocus(window, GLFW_TRUE); -} - -static void keyboardHandleLeave(void* data, - struct wl_keyboard* keyboard, - uint32_t serial, - struct wl_surface* surface) -{ - _GLFWwindow* window = _glfw.wl.keyboardFocus; - - if (!window) - return; - - _glfw.wl.serial = serial; - _glfw.wl.keyboardFocus = NULL; - _glfwInputWindowFocus(window, GLFW_FALSE); -} - -static int toGLFWKeyCode(uint32_t key) -{ - if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) - return _glfw.wl.keycodes[key]; - - return GLFW_KEY_UNKNOWN; -} - -#ifdef HAVE_XKBCOMMON_COMPOSE_H -static xkb_keysym_t composeSymbol(xkb_keysym_t sym) -{ - if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) - return sym; - if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) - != XKB_COMPOSE_FEED_ACCEPTED) - return sym; - switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) - { - case XKB_COMPOSE_COMPOSED: - return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); - case XKB_COMPOSE_COMPOSING: - case XKB_COMPOSE_CANCELLED: - return XKB_KEY_NoSymbol; - case XKB_COMPOSE_NOTHING: - default: - return sym; - } -} -#endif - -static GLFWbool inputChar(_GLFWwindow* window, uint32_t key) -{ - uint32_t code, numSyms; - long cp; - const xkb_keysym_t *syms; - xkb_keysym_t sym; - - code = key + 8; - numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); - - if (numSyms == 1) - { -#ifdef HAVE_XKBCOMMON_COMPOSE_H - sym = composeSymbol(syms[0]); -#else - sym = syms[0]; -#endif - cp = _glfwKeySym2Unicode(sym); - if (cp != -1) - { - const int mods = _glfw.wl.xkb.modifiers; - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - _glfwInputChar(window, cp, mods, plain); - } - } - - return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, syms[0]); -} - -static void keyboardHandleKey(void* data, - struct wl_keyboard* keyboard, - uint32_t serial, - uint32_t time, - uint32_t key, - uint32_t state) -{ - int keyCode; - int action; - _GLFWwindow* window = _glfw.wl.keyboardFocus; - GLFWbool shouldRepeat; - struct itimerspec timer = {}; - - if (!window) - return; - - keyCode = toGLFWKeyCode(key); - action = state == WL_KEYBOARD_KEY_STATE_PRESSED - ? GLFW_PRESS : GLFW_RELEASE; - - _glfw.wl.serial = serial; - _glfwInputKey(window, keyCode, key, action, - _glfw.wl.xkb.modifiers); - - if (action == GLFW_PRESS) - { - shouldRepeat = inputChar(window, key); - - if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0) - { - _glfw.wl.keyboardLastKey = keyCode; - _glfw.wl.keyboardLastScancode = key; - if (_glfw.wl.keyboardRepeatRate > 1) - timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate; - else - timer.it_interval.tv_sec = 1; - timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000; - timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000; - } - } - timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); -} - -static void keyboardHandleModifiers(void* data, - struct wl_keyboard* keyboard, - uint32_t serial, - uint32_t modsDepressed, - uint32_t modsLatched, - uint32_t modsLocked, - uint32_t group) -{ - xkb_mod_mask_t mask; - unsigned int modifiers = 0; - - _glfw.wl.serial = serial; - - if (!_glfw.wl.xkb.keymap) - return; - - xkb_state_update_mask(_glfw.wl.xkb.state, - modsDepressed, - modsLatched, - modsLocked, - 0, - 0, - group); - - mask = xkb_state_serialize_mods(_glfw.wl.xkb.state, - XKB_STATE_MODS_DEPRESSED | - XKB_STATE_LAYOUT_DEPRESSED | - XKB_STATE_MODS_LATCHED | - XKB_STATE_LAYOUT_LATCHED); - if (mask & _glfw.wl.xkb.controlMask) - modifiers |= GLFW_MOD_CONTROL; - if (mask & _glfw.wl.xkb.altMask) - modifiers |= GLFW_MOD_ALT; - if (mask & _glfw.wl.xkb.shiftMask) - modifiers |= GLFW_MOD_SHIFT; - if (mask & _glfw.wl.xkb.superMask) - modifiers |= GLFW_MOD_SUPER; - if (mask & _glfw.wl.xkb.capsLockMask) - modifiers |= GLFW_MOD_CAPS_LOCK; - if (mask & _glfw.wl.xkb.numLockMask) - modifiers |= GLFW_MOD_NUM_LOCK; - _glfw.wl.xkb.modifiers = modifiers; -} - -#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION -static void keyboardHandleRepeatInfo(void* data, - struct wl_keyboard* keyboard, - int32_t rate, - int32_t delay) -{ - if (keyboard != _glfw.wl.keyboard) - return; - - _glfw.wl.keyboardRepeatRate = rate; - _glfw.wl.keyboardRepeatDelay = delay; -} -#endif - -static const struct wl_keyboard_listener keyboardListener = { - keyboardHandleKeymap, - keyboardHandleEnter, - keyboardHandleLeave, - keyboardHandleKey, - keyboardHandleModifiers, -#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION - keyboardHandleRepeatInfo, -#endif -}; - -static void seatHandleCapabilities(void* data, - struct wl_seat* seat, - enum wl_seat_capability caps) -{ - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) - { - _glfw.wl.pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) - { - wl_pointer_destroy(_glfw.wl.pointer); - _glfw.wl.pointer = NULL; - } - - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) - { - _glfw.wl.keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); - } - else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) - { - wl_keyboard_destroy(_glfw.wl.keyboard); - _glfw.wl.keyboard = NULL; - } -} - -static void seatHandleName(void* data, - struct wl_seat* seat, - const char* name) -{ -} - -static const struct wl_seat_listener seatListener = { - seatHandleCapabilities, - seatHandleName, -}; - -static void dataOfferHandleOffer(void* data, - struct wl_data_offer* dataOffer, - const char* mimeType) -{ -} - -static const struct wl_data_offer_listener dataOfferListener = { - dataOfferHandleOffer, -}; - -static void dataDeviceHandleDataOffer(void* data, - struct wl_data_device* dataDevice, - struct wl_data_offer* id) -{ - if (_glfw.wl.dataOffer) - wl_data_offer_destroy(_glfw.wl.dataOffer); - - _glfw.wl.dataOffer = id; - wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); -} - -static void dataDeviceHandleEnter(void* data, - struct wl_data_device* dataDevice, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t x, - wl_fixed_t y, - struct wl_data_offer *id) -{ -} - -static void dataDeviceHandleLeave(void* data, - struct wl_data_device* dataDevice) -{ -} - -static void dataDeviceHandleMotion(void* data, - struct wl_data_device* dataDevice, - uint32_t time, - wl_fixed_t x, - wl_fixed_t y) -{ -} - -static void dataDeviceHandleDrop(void* data, - struct wl_data_device* dataDevice) -{ -} - -static void dataDeviceHandleSelection(void* data, - struct wl_data_device* dataDevice, - struct wl_data_offer* id) -{ -} - -static const struct wl_data_device_listener dataDeviceListener = { - dataDeviceHandleDataOffer, - dataDeviceHandleEnter, - dataDeviceHandleLeave, - dataDeviceHandleMotion, - dataDeviceHandleDrop, - dataDeviceHandleSelection, -}; - -static void wmBaseHandlePing(void* data, +static void wmBaseHandlePing(void* userData, struct xdg_wm_base* wmBase, uint32_t serial) { xdg_wm_base_pong(wmBase, serial); } -static const struct xdg_wm_base_listener wmBaseListener = { +static const struct xdg_wm_base_listener wmBaseListener = +{ wmBaseHandlePing }; -static void registryHandleGlobal(void* data, +static void registryHandleGlobal(void* userData, struct wl_registry* registry, uint32_t name, const char* interface, @@ -788,7 +100,7 @@ static void registryHandleGlobal(void* data, { if (strcmp(interface, "wl_compositor") == 0) { - _glfw.wl.compositorVersion = min(3, version); + _glfw.wl.compositorVersion = _glfw_min(3, version); _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, _glfw.wl.compositorVersion); @@ -811,11 +123,11 @@ static void registryHandleGlobal(void* data, { if (!_glfw.wl.seat) { - _glfw.wl.seatVersion = min(4, version); + _glfw.wl.seatVersion = _glfw_min(4, version); _glfw.wl.seat = wl_registry_bind(registry, name, &wl_seat_interface, _glfw.wl.seatVersion); - wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); + _glfwAddSeatListenerWayland(_glfw.wl.seat); } } else if (strcmp(interface, "wl_data_device_manager") == 0) @@ -868,16 +180,13 @@ static void registryHandleGlobal(void* data, } } -static void registryHandleGlobalRemove(void *data, - struct wl_registry *registry, +static void registryHandleGlobalRemove(void* userData, + struct wl_registry* registry, uint32_t name) { - int i; - _GLFWmonitor* monitor; - - for (i = 0; i < _glfw.monitorCount; ++i) + for (int i = 0; i < _glfw.monitorCount; ++i) { - monitor = _glfw.monitors[i]; + _GLFWmonitor* monitor = _glfw.monitors[i]; if (monitor->wl.name == name) { _glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0); @@ -887,7 +196,8 @@ static void registryHandleGlobalRemove(void *data, } -static const struct wl_registry_listener registryListener = { +static const struct wl_registry_listener registryListener = +{ registryHandleGlobal, registryHandleGlobalRemove }; @@ -896,8 +206,6 @@ static const struct wl_registry_listener registryListener = { // static void createKeyTables(void) { - int scancode; - memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes)); memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes)); @@ -959,7 +267,7 @@ static void createKeyTables(void) _glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; _glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; _glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; - _glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU; + _glfw.wl.keycodes[KEY_COMPOSE] = GLFW_KEY_MENU; _glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; _glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; _glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; @@ -1002,7 +310,7 @@ static void createKeyTables(void) _glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23; _glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24; _glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; - _glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; + _glfw.wl.keycodes[KEY_KPASTERISK] = GLFW_KEY_KP_MULTIPLY; _glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; _glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; _glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0; @@ -1015,117 +323,326 @@ static void createKeyTables(void) _glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7; _glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8; _glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9; - _glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; + _glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_DECIMAL; _glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; _glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + _glfw.wl.keycodes[KEY_102ND] = GLFW_KEY_WORLD_2; - for (scancode = 0; scancode < 256; scancode++) + for (int scancode = 0; scancode < 256; scancode++) { if (_glfw.wl.keycodes[scancode] > 0) _glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode; } } +static GLFWbool loadCursorTheme(void) +{ + int cursorSize = 32; + + const char* sizeString = getenv("XCURSOR_SIZE"); + if (sizeString) + { + errno = 0; + const long cursorSizeLong = strtol(sizeString, NULL, 10); + if (errno == 0 && cursorSizeLong > 0 && cursorSizeLong < INT_MAX) + cursorSize = (int) cursorSizeLong; + } + + const char* themeName = getenv("XCURSOR_THEME"); + + _glfw.wl.cursorTheme = wl_cursor_theme_load(themeName, cursorSize, _glfw.wl.shm); + if (!_glfw.wl.cursorTheme) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to load default cursor theme"); + return GLFW_FALSE; + } + + // If this happens to be NULL, we just fallback to the scale=1 version. + _glfw.wl.cursorThemeHiDPI = + wl_cursor_theme_load(themeName, cursorSize * 2, _glfw.wl.shm); + + _glfw.wl.cursorSurface = wl_compositor_create_surface(_glfw.wl.compositor); + _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + return GLFW_TRUE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformInit(void) +GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) { - const char *cursorTheme; - const char *cursorSizeStr; - char *cursorSizeEnd; - long cursorSizeLong; - int cursorSize; + const _GLFWplatform wayland = + { + GLFW_PLATFORM_WAYLAND, + _glfwInitWayland, + _glfwTerminateWayland, + _glfwGetCursorPosWayland, + _glfwSetCursorPosWayland, + _glfwSetCursorModeWayland, + _glfwSetRawMouseMotionWayland, + _glfwRawMouseMotionSupportedWayland, + _glfwCreateCursorWayland, + _glfwCreateStandardCursorWayland, + _glfwDestroyCursorWayland, + _glfwSetCursorWayland, + _glfwGetScancodeNameWayland, + _glfwGetKeyScancodeWayland, + _glfwSetClipboardStringWayland, + _glfwGetClipboardStringWayland, +#if defined(__linux__) + _glfwInitJoysticksLinux, + _glfwTerminateJoysticksLinux, + _glfwPollJoystickLinux, + _glfwGetMappingNameLinux, + _glfwUpdateGamepadGUIDLinux, +#else + _glfwInitJoysticksNull, + _glfwTerminateJoysticksNull, + _glfwPollJoystickNull, + _glfwGetMappingNameNull, + _glfwUpdateGamepadGUIDNull, +#endif + _glfwFreeMonitorWayland, + _glfwGetMonitorPosWayland, + _glfwGetMonitorContentScaleWayland, + _glfwGetMonitorWorkareaWayland, + _glfwGetVideoModesWayland, + _glfwGetVideoModeWayland, + _glfwGetGammaRampWayland, + _glfwSetGammaRampWayland, + _glfwCreateWindowWayland, + _glfwDestroyWindowWayland, + _glfwSetWindowTitleWayland, + _glfwSetWindowIconWayland, + _glfwGetWindowPosWayland, + _glfwSetWindowPosWayland, + _glfwGetWindowSizeWayland, + _glfwSetWindowSizeWayland, + _glfwSetWindowSizeLimitsWayland, + _glfwSetWindowAspectRatioWayland, + _glfwGetFramebufferSizeWayland, + _glfwGetWindowFrameSizeWayland, + _glfwGetWindowContentScaleWayland, + _glfwIconifyWindowWayland, + _glfwRestoreWindowWayland, + _glfwMaximizeWindowWayland, + _glfwShowWindowWayland, + _glfwHideWindowWayland, + _glfwRequestWindowAttentionWayland, + _glfwFocusWindowWayland, + _glfwSetWindowMonitorWayland, + _glfwWindowFocusedWayland, + _glfwWindowIconifiedWayland, + _glfwWindowVisibleWayland, + _glfwWindowMaximizedWayland, + _glfwWindowHoveredWayland, + _glfwFramebufferTransparentWayland, + _glfwGetWindowOpacityWayland, + _glfwSetWindowResizableWayland, + _glfwSetWindowDecoratedWayland, + _glfwSetWindowFloatingWayland, + _glfwSetWindowOpacityWayland, + _glfwSetWindowMousePassthroughWayland, + _glfwPollEventsWayland, + _glfwWaitEventsWayland, + _glfwWaitEventsTimeoutWayland, + _glfwPostEmptyEventWayland, + _glfwGetEGLPlatformWayland, + _glfwGetEGLNativeDisplayWayland, + _glfwGetEGLNativeWindowWayland, + _glfwGetRequiredInstanceExtensionsWayland, + _glfwGetPhysicalDevicePresentationSupportWayland, + _glfwCreateWindowSurfaceWayland, + }; - _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0"); + void* module = _glfwPlatformLoadModule("libwayland-client.so.0"); + if (!module) + { + if (platformID == GLFW_PLATFORM_WAYLAND) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to load libwayland-client"); + } + + return GLFW_FALSE; + } + + PFN_wl_display_connect wl_display_connect = (PFN_wl_display_connect) + _glfwPlatformGetModuleSymbol(module, "wl_display_connect"); + if (!wl_display_connect) + { + if (platformID == GLFW_PLATFORM_WAYLAND) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to load libwayland-client entry point"); + } + + _glfwPlatformFreeModule(module); + return GLFW_FALSE; + } + + struct wl_display* display = wl_display_connect(NULL); + if (!display) + { + if (platformID == GLFW_PLATFORM_WAYLAND) + _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to connect to display"); + + _glfwPlatformFreeModule(module); + return GLFW_FALSE; + } + + _glfw.wl.display = display; + _glfw.wl.client.handle = module; + + *platform = wayland; + return GLFW_TRUE; +} + +int _glfwInitWayland(void) +{ + // These must be set before any failure checks + _glfw.wl.keyRepeatTimerfd = -1; + _glfw.wl.cursorTimerfd = -1; + + _glfw.wl.client.display_flush = (PFN_wl_display_flush) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_flush"); + _glfw.wl.client.display_cancel_read = (PFN_wl_display_cancel_read) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_cancel_read"); + _glfw.wl.client.display_dispatch_pending = (PFN_wl_display_dispatch_pending) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_dispatch_pending"); + _glfw.wl.client.display_read_events = (PFN_wl_display_read_events) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_read_events"); + _glfw.wl.client.display_disconnect = (PFN_wl_display_disconnect) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_disconnect"); + _glfw.wl.client.display_roundtrip = (PFN_wl_display_roundtrip) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_roundtrip"); + _glfw.wl.client.display_get_fd = (PFN_wl_display_get_fd) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_get_fd"); + _glfw.wl.client.display_prepare_read = (PFN_wl_display_prepare_read) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_prepare_read"); + _glfw.wl.client.proxy_marshal = (PFN_wl_proxy_marshal) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_marshal"); + _glfw.wl.client.proxy_add_listener = (PFN_wl_proxy_add_listener) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_add_listener"); + _glfw.wl.client.proxy_destroy = (PFN_wl_proxy_destroy) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_destroy"); + _glfw.wl.client.proxy_marshal_constructor = (PFN_wl_proxy_marshal_constructor) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_marshal_constructor"); + _glfw.wl.client.proxy_marshal_constructor_versioned = (PFN_wl_proxy_marshal_constructor_versioned) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_marshal_constructor_versioned"); + _glfw.wl.client.proxy_get_user_data = (PFN_wl_proxy_get_user_data) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_get_user_data"); + _glfw.wl.client.proxy_set_user_data = (PFN_wl_proxy_set_user_data) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_set_user_data"); + _glfw.wl.client.proxy_get_version = (PFN_wl_proxy_get_version) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_get_version"); + _glfw.wl.client.proxy_marshal_flags = (PFN_wl_proxy_marshal_flags) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_marshal_flags"); + + if (!_glfw.wl.client.display_flush || + !_glfw.wl.client.display_cancel_read || + !_glfw.wl.client.display_dispatch_pending || + !_glfw.wl.client.display_read_events || + !_glfw.wl.client.display_disconnect || + !_glfw.wl.client.display_roundtrip || + !_glfw.wl.client.display_get_fd || + !_glfw.wl.client.display_prepare_read || + !_glfw.wl.client.proxy_marshal || + !_glfw.wl.client.proxy_add_listener || + !_glfw.wl.client.proxy_destroy || + !_glfw.wl.client.proxy_marshal_constructor || + !_glfw.wl.client.proxy_marshal_constructor_versioned || + !_glfw.wl.client.proxy_get_user_data || + !_glfw.wl.client.proxy_set_user_data) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to load libwayland-client entry point"); + return GLFW_FALSE; + } + + _glfw.wl.cursor.handle = _glfwPlatformLoadModule("libwayland-cursor.so.0"); if (!_glfw.wl.cursor.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to open libwayland-cursor"); + "Wayland: Failed to load libwayland-cursor"); return GLFW_FALSE; } _glfw.wl.cursor.theme_load = (PFN_wl_cursor_theme_load) - _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load"); + _glfwPlatformGetModuleSymbol(_glfw.wl.cursor.handle, "wl_cursor_theme_load"); _glfw.wl.cursor.theme_destroy = (PFN_wl_cursor_theme_destroy) - _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy"); + _glfwPlatformGetModuleSymbol(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy"); _glfw.wl.cursor.theme_get_cursor = (PFN_wl_cursor_theme_get_cursor) - _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor"); + _glfwPlatformGetModuleSymbol(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor"); _glfw.wl.cursor.image_get_buffer = (PFN_wl_cursor_image_get_buffer) - _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer"); + _glfwPlatformGetModuleSymbol(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer"); - _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1"); + _glfw.wl.egl.handle = _glfwPlatformLoadModule("libwayland-egl.so.1"); if (!_glfw.wl.egl.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to open libwayland-egl"); + "Wayland: Failed to load libwayland-egl"); return GLFW_FALSE; } _glfw.wl.egl.window_create = (PFN_wl_egl_window_create) - _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create"); + _glfwPlatformGetModuleSymbol(_glfw.wl.egl.handle, "wl_egl_window_create"); _glfw.wl.egl.window_destroy = (PFN_wl_egl_window_destroy) - _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy"); + _glfwPlatformGetModuleSymbol(_glfw.wl.egl.handle, "wl_egl_window_destroy"); _glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize) - _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize"); + _glfwPlatformGetModuleSymbol(_glfw.wl.egl.handle, "wl_egl_window_resize"); - _glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0"); + _glfw.wl.xkb.handle = _glfwPlatformLoadModule("libxkbcommon.so.0"); if (!_glfw.wl.xkb.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to open libxkbcommon"); + "Wayland: Failed to load libxkbcommon"); return GLFW_FALSE; } _glfw.wl.xkb.context_new = (PFN_xkb_context_new) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_context_new"); _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_context_unref"); _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_unref"); _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); _glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats"); + _glfw.wl.xkb.keymap_key_get_syms_by_level = (PFN_xkb_keymap_key_get_syms_by_level) + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_key_get_syms_by_level"); _glfw.wl.xkb.state_new = (PFN_xkb_state_new) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_state_new"); _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_state_unref"); _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); - _glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods"); - -#ifdef HAVE_XKBCOMMON_COMPOSE_H + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_state_update_mask"); + _glfw.wl.xkb.state_key_get_layout = (PFN_xkb_state_key_get_layout) + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_state_key_get_layout"); + _glfw.wl.xkb.state_mod_index_is_active = (PFN_xkb_state_mod_index_is_active) + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_state_mod_index_is_active"); _glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_state_new"); _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) - _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); -#endif - - _glfw.wl.display = wl_display_connect(NULL); - if (!_glfw.wl.display) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to connect to display"); - return GLFW_FALSE; - } + _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); @@ -1146,11 +663,13 @@ int _glfwPlatformInit(void) // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); - _glfwInitTimerPOSIX(); - - _glfw.wl.timerfd = -1; - if (_glfw.wl.seatVersion >= 4) - _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION + if (_glfw.wl.seatVersion >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) + { + _glfw.wl.keyRepeatTimerfd = + timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + } +#endif if (!_glfw.wl.wmBase) { @@ -1159,66 +678,40 @@ int _glfwPlatformInit(void) return GLFW_FALSE; } - if (_glfw.wl.pointer && _glfw.wl.shm) + if (!_glfw.wl.shm) { - cursorTheme = getenv("XCURSOR_THEME"); - cursorSizeStr = getenv("XCURSOR_SIZE"); - cursorSize = 32; - if (cursorSizeStr) - { - errno = 0; - cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10); - if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX) - cursorSize = (int)cursorSizeLong; - } - _glfw.wl.cursorTheme = - wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm); - if (!_glfw.wl.cursorTheme) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unable to load default cursor theme"); - return GLFW_FALSE; - } - // If this happens to be NULL, we just fallback to the scale=1 version. - _glfw.wl.cursorThemeHiDPI = - wl_cursor_theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm); - _glfw.wl.cursorSurface = - wl_compositor_create_surface(_glfw.wl.compositor); - _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to find wl_shm in your compositor"); + return GLFW_FALSE; } + if (!loadCursorTheme()) + return GLFW_FALSE; + if (_glfw.wl.seat && _glfw.wl.dataDeviceManager) { _glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat); - wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); - _glfw.wl.clipboardString = malloc(4096); - if (!_glfw.wl.clipboardString) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unable to allocate clipboard memory"); - return GLFW_FALSE; - } - _glfw.wl.clipboardSize = 4096; + _glfwAddDataDeviceListenerWayland(_glfw.wl.dataDevice); } return GLFW_TRUE; } -void _glfwPlatformTerminate(void) +void _glfwTerminateWayland(void) { _glfwTerminateEGL(); + _glfwTerminateOSMesa(); + if (_glfw.wl.egl.handle) { - _glfw_dlclose(_glfw.wl.egl.handle); + _glfwPlatformFreeModule(_glfw.wl.egl.handle); _glfw.wl.egl.handle = NULL; } -#ifdef HAVE_XKBCOMMON_COMPOSE_H if (_glfw.wl.xkb.composeState) xkb_compose_state_unref(_glfw.wl.xkb.composeState); -#endif if (_glfw.wl.xkb.keymap) xkb_keymap_unref(_glfw.wl.xkb.keymap); if (_glfw.wl.xkb.state) @@ -1227,7 +720,7 @@ void _glfwPlatformTerminate(void) xkb_context_unref(_glfw.wl.xkb.context); if (_glfw.wl.xkb.handle) { - _glfw_dlclose(_glfw.wl.xkb.handle); + _glfwPlatformFreeModule(_glfw.wl.xkb.handle); _glfw.wl.xkb.handle = NULL; } @@ -1237,10 +730,15 @@ void _glfwPlatformTerminate(void) wl_cursor_theme_destroy(_glfw.wl.cursorThemeHiDPI); if (_glfw.wl.cursor.handle) { - _glfw_dlclose(_glfw.wl.cursor.handle); + _glfwPlatformFreeModule(_glfw.wl.cursor.handle); _glfw.wl.cursor.handle = NULL; } + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + wl_data_offer_destroy(_glfw.wl.offers[i].offer); + + _glfw_free(_glfw.wl.offers); + if (_glfw.wl.cursorSurface) wl_surface_destroy(_glfw.wl.cursorSurface); if (_glfw.wl.subcompositor) @@ -1255,12 +753,14 @@ void _glfwPlatformTerminate(void) zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager); if (_glfw.wl.wmBase) xdg_wm_base_destroy(_glfw.wl.wmBase); - if (_glfw.wl.dataSource) - wl_data_source_destroy(_glfw.wl.dataSource); + if (_glfw.wl.selectionOffer) + wl_data_offer_destroy(_glfw.wl.selectionOffer); + if (_glfw.wl.dragOffer) + wl_data_offer_destroy(_glfw.wl.dragOffer); + if (_glfw.wl.selectionSource) + wl_data_source_destroy(_glfw.wl.selectionSource); if (_glfw.wl.dataDevice) wl_data_device_destroy(_glfw.wl.dataDevice); - if (_glfw.wl.dataOffer) - wl_data_offer_destroy(_glfw.wl.dataOffer); if (_glfw.wl.dataDeviceManager) wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); if (_glfw.wl.pointer) @@ -1283,28 +783,11 @@ void _glfwPlatformTerminate(void) wl_display_disconnect(_glfw.wl.display); } - if (_glfw.wl.timerfd >= 0) - close(_glfw.wl.timerfd); + if (_glfw.wl.keyRepeatTimerfd >= 0) + close(_glfw.wl.keyRepeatTimerfd); if (_glfw.wl.cursorTimerfd >= 0) close(_glfw.wl.cursorTimerfd); - if (_glfw.wl.clipboardString) - free(_glfw.wl.clipboardString); - if (_glfw.wl.clipboardSendString) - free(_glfw.wl.clipboardSendString); + _glfw_free(_glfw.wl.clipboardString); } -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " Wayland EGL OSMesa" -#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) - " clock_gettime" -#else - " gettimeofday" -#endif - " evdev" -#if defined(_GLFW_BUILD_DLL) - " shared" -#endif - ; -} diff --git a/src/external/glfw/src/wl_monitor.c b/src/external/glfw/src/wl_monitor.c index 620376188..336681fdc 100644 --- a/src/external/glfw/src/wl_monitor.c +++ b/src/external/glfw/src/wl_monitor.c @@ -34,8 +34,10 @@ #include #include +#include "wayland-client-protocol.h" -static void outputHandleGeometry(void* data, + +static void outputHandleGeometry(void* userData, struct wl_output* output, int32_t x, int32_t y, @@ -46,24 +48,25 @@ static void outputHandleGeometry(void* data, const char* model, int32_t transform) { - struct _GLFWmonitor *monitor = data; + struct _GLFWmonitor* monitor = userData; monitor->wl.x = x; monitor->wl.y = y; monitor->widthMM = physicalWidth; monitor->heightMM = physicalHeight; - snprintf(monitor->name, sizeof(monitor->name), "%s %s", make, model); + if (strlen(monitor->name) == 0) + snprintf(monitor->name, sizeof(monitor->name), "%s %s", make, model); } -static void outputHandleMode(void* data, +static void outputHandleMode(void* userData, struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - struct _GLFWmonitor *monitor = data; + struct _GLFWmonitor* monitor = userData; GLFWvidmode mode; mode.width = width; @@ -75,16 +78,16 @@ static void outputHandleMode(void* data, monitor->modeCount++; monitor->modes = - realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); + _glfw_realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); monitor->modes[monitor->modeCount - 1] = mode; if (flags & WL_OUTPUT_MODE_CURRENT) monitor->wl.currentMode = monitor->modeCount - 1; } -static void outputHandleDone(void* data, struct wl_output* output) +static void outputHandleDone(void* userData, struct wl_output* output) { - struct _GLFWmonitor *monitor = data; + struct _GLFWmonitor* monitor = userData; if (monitor->widthMM <= 0 || monitor->heightMM <= 0) { @@ -94,23 +97,63 @@ static void outputHandleDone(void* data, struct wl_output* output) monitor->heightMM = (int) (mode->height * 25.4f / 96.f); } + for (int i = 0; i < _glfw.monitorCount; i++) + { + if (_glfw.monitors[i] == monitor) + return; + } + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } -static void outputHandleScale(void* data, +static void outputHandleScale(void* userData, struct wl_output* output, int32_t factor) { - struct _GLFWmonitor *monitor = data; + struct _GLFWmonitor* monitor = userData; monitor->wl.scale = factor; + + for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) + { + for (int i = 0; i < window->wl.monitorsCount; i++) + { + if (window->wl.monitors[i] == monitor) + { + _glfwUpdateContentScaleWayland(window); + break; + } + } + } } -static const struct wl_output_listener outputListener = { +#ifdef WL_OUTPUT_NAME_SINCE_VERSION + +void outputHandleName(void* userData, struct wl_output* wl_output, const char* name) +{ + struct _GLFWmonitor* monitor = userData; + + strncpy(monitor->name, name, sizeof(monitor->name) - 1); +} + +void outputHandleDescription(void* userData, + struct wl_output* wl_output, + const char* description) +{ +} + +#endif // WL_OUTPUT_NAME_SINCE_VERSION + +static const struct wl_output_listener outputListener = +{ outputHandleGeometry, outputHandleMode, outputHandleDone, outputHandleScale, +#ifdef WL_OUTPUT_NAME_SINCE_VERSION + outputHandleName, + outputHandleDescription, +#endif }; @@ -120,9 +163,6 @@ static const struct wl_output_listener outputListener = { void _glfwAddOutputWayland(uint32_t name, uint32_t version) { - _GLFWmonitor *monitor; - struct wl_output *output; - if (version < 2) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -130,19 +170,21 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) return; } - // The actual name of this output will be set in the geometry handler. - monitor = _glfwAllocMonitor("", 0, 0); +#ifdef WL_OUTPUT_NAME_SINCE_VERSION + version = _glfw_min(version, WL_OUTPUT_NAME_SINCE_VERSION); +#else + version = 2; +#endif - output = wl_registry_bind(_glfw.wl.registry, - name, - &wl_output_interface, - 2); + struct wl_output* output = wl_registry_bind(_glfw.wl.registry, + name, + &wl_output_interface, + version); if (!output) - { - _glfwFreeMonitor(monitor); return; - } + // The actual name of this output will be set in the geometry handler + _GLFWmonitor* monitor = _glfwAllocMonitor("", 0, 0); monitor->wl.scale = 1; monitor->wl.output = output; monitor->wl.name = name; @@ -155,13 +197,13 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +void _glfwFreeMonitorWayland(_GLFWmonitor* monitor) { if (monitor->wl.output) wl_output_destroy(monitor->wl.output); } -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +void _glfwGetMonitorPosWayland(_GLFWmonitor* monitor, int* xpos, int* ypos) { if (xpos) *xpos = monitor->wl.x; @@ -169,8 +211,8 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = monitor->wl.y; } -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) +void _glfwGetMonitorContentScaleWayland(_GLFWmonitor* monitor, + float* xscale, float* yscale) { if (xscale) *xscale = (float) monitor->wl.scale; @@ -178,9 +220,9 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, *yscale = (float) monitor->wl.scale; } -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, - int* xpos, int* ypos, - int* width, int* height) +void _glfwGetMonitorWorkareaWayland(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) { if (xpos) *xpos = monitor->wl.x; @@ -192,26 +234,25 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, *height = monitor->modes[monitor->wl.currentMode].height; } -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +GLFWvidmode* _glfwGetVideoModesWayland(_GLFWmonitor* monitor, int* found) { *found = monitor->modeCount; return monitor->modes; } -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +void _glfwGetVideoModeWayland(_GLFWmonitor* monitor, GLFWvidmode* mode) { *mode = monitor->modes[monitor->wl.currentMode]; } -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwGetGammaRampWayland(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { _glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Wayland: Gamma ramp access is not available"); return GLFW_FALSE; } -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, - const GLFWgammaramp* ramp) +void _glfwSetGammaRampWayland(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { _glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Wayland: Gamma ramp access is not available"); diff --git a/src/external/glfw/src/wl_platform.h b/src/external/glfw/src/wl_platform.h index 966155fdd..238e1ed40 100644 --- a/src/external/glfw/src/wl_platform.h +++ b/src/external/glfw/src/wl_platform.h @@ -24,12 +24,9 @@ // //======================================================================== -#include +#include #include -#ifdef HAVE_XKBCOMMON_COMPOSE_H #include -#endif -#include typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; @@ -45,33 +42,91 @@ typedef struct VkWaylandSurfaceCreateInfoKHR typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); -#include "posix_thread.h" -#include "posix_time.h" -#ifdef __linux__ -#include "linux_joystick.h" -#else -#include "null_joystick.h" -#endif #include "xkb_unicode.h" +#include "posix_poll.h" -#include "wayland-xdg-shell-client-protocol.h" -#include "wayland-xdg-decoration-client-protocol.h" -#include "wayland-viewporter-client-protocol.h" -#include "wayland-relative-pointer-unstable-v1-client-protocol.h" -#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" -#include "wayland-idle-inhibit-unstable-v1-client-protocol.h" +typedef int (* PFN_wl_display_flush)(struct wl_display* display); +typedef void (* PFN_wl_display_cancel_read)(struct wl_display* display); +typedef int (* PFN_wl_display_dispatch_pending)(struct wl_display* display); +typedef int (* PFN_wl_display_read_events)(struct wl_display* display); +typedef struct wl_display* (* PFN_wl_display_connect)(const char*); +typedef void (* PFN_wl_display_disconnect)(struct wl_display*); +typedef int (* PFN_wl_display_roundtrip)(struct wl_display*); +typedef int (* PFN_wl_display_get_fd)(struct wl_display*); +typedef int (* PFN_wl_display_prepare_read)(struct wl_display*); +typedef void (* PFN_wl_proxy_marshal)(struct wl_proxy*,uint32_t,...); +typedef int (* PFN_wl_proxy_add_listener)(struct wl_proxy*,void(**)(void),void*); +typedef void (* PFN_wl_proxy_destroy)(struct wl_proxy*); +typedef struct wl_proxy* (* PFN_wl_proxy_marshal_constructor)(struct wl_proxy*,uint32_t,const struct wl_interface*,...); +typedef struct wl_proxy* (* PFN_wl_proxy_marshal_constructor_versioned)(struct wl_proxy*,uint32_t,const struct wl_interface*,uint32_t,...); +typedef void* (* PFN_wl_proxy_get_user_data)(struct wl_proxy*); +typedef void (* PFN_wl_proxy_set_user_data)(struct wl_proxy*,void*); +typedef uint32_t (* PFN_wl_proxy_get_version)(struct wl_proxy*); +typedef struct wl_proxy* (* PFN_wl_proxy_marshal_flags)(struct wl_proxy*,uint32_t,const struct wl_interface*,uint32_t,uint32_t,...); +#define wl_display_flush _glfw.wl.client.display_flush +#define wl_display_cancel_read _glfw.wl.client.display_cancel_read +#define wl_display_dispatch_pending _glfw.wl.client.display_dispatch_pending +#define wl_display_read_events _glfw.wl.client.display_read_events +#define wl_display_disconnect _glfw.wl.client.display_disconnect +#define wl_display_roundtrip _glfw.wl.client.display_roundtrip +#define wl_display_get_fd _glfw.wl.client.display_get_fd +#define wl_display_prepare_read _glfw.wl.client.display_prepare_read +#define wl_proxy_marshal _glfw.wl.client.proxy_marshal +#define wl_proxy_add_listener _glfw.wl.client.proxy_add_listener +#define wl_proxy_destroy _glfw.wl.client.proxy_destroy +#define wl_proxy_marshal_constructor _glfw.wl.client.proxy_marshal_constructor +#define wl_proxy_marshal_constructor_versioned _glfw.wl.client.proxy_marshal_constructor_versioned +#define wl_proxy_get_user_data _glfw.wl.client.proxy_get_user_data +#define wl_proxy_set_user_data _glfw.wl.client.proxy_set_user_data +#define wl_proxy_get_version _glfw.wl.client.proxy_get_version +#define wl_proxy_marshal_flags _glfw.wl.client.proxy_marshal_flags -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) +struct wl_shm; -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWayland wl -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWayland wl -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWayland wl +#define wl_display_interface _glfw_wl_display_interface +#define wl_subcompositor_interface _glfw_wl_subcompositor_interface +#define wl_compositor_interface _glfw_wl_compositor_interface +#define wl_shm_interface _glfw_wl_shm_interface +#define wl_data_device_manager_interface _glfw_wl_data_device_manager_interface +#define wl_shell_interface _glfw_wl_shell_interface +#define wl_buffer_interface _glfw_wl_buffer_interface +#define wl_callback_interface _glfw_wl_callback_interface +#define wl_data_device_interface _glfw_wl_data_device_interface +#define wl_data_offer_interface _glfw_wl_data_offer_interface +#define wl_data_source_interface _glfw_wl_data_source_interface +#define wl_keyboard_interface _glfw_wl_keyboard_interface +#define wl_output_interface _glfw_wl_output_interface +#define wl_pointer_interface _glfw_wl_pointer_interface +#define wl_region_interface _glfw_wl_region_interface +#define wl_registry_interface _glfw_wl_registry_interface +#define wl_seat_interface _glfw_wl_seat_interface +#define wl_shell_surface_interface _glfw_wl_shell_surface_interface +#define wl_shm_pool_interface _glfw_wl_shm_pool_interface +#define wl_subsurface_interface _glfw_wl_subsurface_interface +#define wl_surface_interface _glfw_wl_surface_interface +#define wl_touch_interface _glfw_wl_touch_interface +#define zwp_idle_inhibitor_v1_interface _glfw_zwp_idle_inhibitor_v1_interface +#define zwp_idle_inhibit_manager_v1_interface _glfw_zwp_idle_inhibit_manager_v1_interface +#define zwp_confined_pointer_v1_interface _glfw_zwp_confined_pointer_v1_interface +#define zwp_locked_pointer_v1_interface _glfw_zwp_locked_pointer_v1_interface +#define zwp_pointer_constraints_v1_interface _glfw_zwp_pointer_constraints_v1_interface +#define zwp_relative_pointer_v1_interface _glfw_zwp_relative_pointer_v1_interface +#define zwp_relative_pointer_manager_v1_interface _glfw_zwp_relative_pointer_manager_v1_interface +#define wp_viewport_interface _glfw_wp_viewport_interface +#define wp_viewporter_interface _glfw_wp_viewporter_interface +#define xdg_toplevel_interface _glfw_xdg_toplevel_interface +#define zxdg_toplevel_decoration_v1_interface _glfw_zxdg_toplevel_decoration_v1_interface +#define zxdg_decoration_manager_v1_interface _glfw_zxdg_decoration_manager_v1_interface +#define xdg_popup_interface _glfw_xdg_popup_interface +#define xdg_positioner_interface _glfw_xdg_positioner_interface +#define xdg_surface_interface _glfw_xdg_surface_interface +#define xdg_toplevel_interface _glfw_xdg_toplevel_interface +#define xdg_wm_base_interface _glfw_xdg_wm_base_interface -#define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } +#define GLFW_WAYLAND_WINDOW_STATE _GLFWwindowWayland wl; +#define GLFW_WAYLAND_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl; +#define GLFW_WAYLAND_MONITOR_STATE _GLFWmonitorWayland wl; +#define GLFW_WAYLAND_CURSOR_STATE _GLFWcursorWayland wl; struct wl_cursor_image { uint32_t width; @@ -107,24 +162,27 @@ typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*); typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*); typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t); +typedef int (* PFN_xkb_keymap_key_get_syms_by_level)(struct xkb_keymap*,xkb_keycode_t,xkb_layout_index_t,xkb_level_index_t,const xkb_keysym_t**); typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*); typedef void (* PFN_xkb_state_unref)(struct xkb_state*); typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**); typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t); -typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component); +typedef xkb_layout_index_t (* PFN_xkb_state_key_get_layout)(struct xkb_state*,xkb_keycode_t); +typedef int (* PFN_xkb_state_mod_index_is_active)(struct xkb_state*,xkb_mod_index_t,enum xkb_state_component); #define xkb_context_new _glfw.wl.xkb.context_new #define xkb_context_unref _glfw.wl.xkb.context_unref #define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string #define xkb_keymap_unref _glfw.wl.xkb.keymap_unref #define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index #define xkb_keymap_key_repeats _glfw.wl.xkb.keymap_key_repeats +#define xkb_keymap_key_get_syms_by_level _glfw.wl.xkb.keymap_key_get_syms_by_level #define xkb_state_new _glfw.wl.xkb.state_new #define xkb_state_unref _glfw.wl.xkb.state_unref #define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms #define xkb_state_update_mask _glfw.wl.xkb.state_update_mask -#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods +#define xkb_state_key_get_layout _glfw.wl.xkb.state_key_get_layout +#define xkb_state_mod_index_is_active _glfw.wl.xkb.state_mod_index_is_active -#ifdef HAVE_XKBCOMMON_COMPOSE_H typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags); typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*); typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags); @@ -139,12 +197,6 @@ typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_st #define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed #define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status #define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym -#endif - -#define _GLFW_DECORATION_WIDTH 4 -#define _GLFW_DECORATION_TOP 24 -#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH) -#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH) typedef enum _GLFWdecorationSideWayland { @@ -153,7 +205,6 @@ typedef enum _GLFWdecorationSideWayland leftDecoration, rightDecoration, bottomDecoration, - } _GLFWdecorationSideWayland; typedef struct _GLFWdecorationWayland @@ -161,9 +212,15 @@ typedef struct _GLFWdecorationWayland struct wl_surface* surface; struct wl_subsurface* subsurface; struct wp_viewport* viewport; - } _GLFWdecorationWayland; +typedef struct _GLFWofferWayland +{ + struct wl_data_offer* offer; + GLFWbool text_plain_utf8; + GLFWbool text_uri_list; +} _GLFWofferWayland; + // Wayland-specific per-window data // typedef struct _GLFWwindowWayland @@ -171,22 +228,37 @@ typedef struct _GLFWwindowWayland int width, height; GLFWbool visible; GLFWbool maximized; + GLFWbool activated; + GLFWbool fullscreen; GLFWbool hovered; GLFWbool transparent; struct wl_surface* surface; - struct wl_egl_window* native; struct wl_callback* callback; + struct { + struct wl_egl_window* window; + } egl; + + struct { + int width, height; + GLFWbool maximized; + GLFWbool iconified; + GLFWbool activated; + GLFWbool fullscreen; + } pending; + struct { struct xdg_surface* surface; struct xdg_toplevel* toplevel; struct zxdg_toplevel_decoration_v1* decoration; + uint32_t decorationMode; } xdg; _GLFWcursor* currentCursor; double cursorPosX, cursorPosY; char* title; + char* appId; // We need to track the monitors the window spans on to calculate the // optimal scaling factor. @@ -195,22 +267,17 @@ typedef struct _GLFWwindowWayland int monitorsCount; int monitorsSize; - struct { - struct zwp_relative_pointer_v1* relativePointer; - struct zwp_locked_pointer_v1* lockedPointer; - } pointerLock; + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + struct zwp_confined_pointer_v1* confinedPointer; struct zwp_idle_inhibitor_v1* idleInhibitor; - GLFWbool wasFullscreen; - struct { - GLFWbool serverSide; struct wl_buffer* buffer; _GLFWdecorationWayland top, left, right, bottom; - int focus; + _GLFWdecorationSideWayland focus; } decorations; - } _GLFWwindowWayland; // Wayland-specific global data @@ -227,8 +294,6 @@ typedef struct _GLFWlibraryWayland struct wl_keyboard* keyboard; struct wl_data_device_manager* dataDeviceManager; struct wl_data_device* dataDevice; - struct wl_data_offer* dataOffer; - struct wl_data_source* dataSource; struct xdg_wm_base* wmBase; struct zxdg_decoration_manager_v1* decorationManager; struct wp_viewporter* viewporter; @@ -236,6 +301,16 @@ typedef struct _GLFWlibraryWayland struct zwp_pointer_constraints_v1* pointerConstraints; struct zwp_idle_inhibit_manager_v1* idleInhibitManager; + _GLFWofferWayland* offers; + unsigned int offerCount; + + struct wl_data_offer* selectionOffer; + struct wl_data_source* selectionSource; + + struct wl_data_offer* dragOffer; + _GLFWwindow* dragFocus; + uint32_t dragSerial; + int compositorVersion; int seatVersion; @@ -245,18 +320,17 @@ typedef struct _GLFWlibraryWayland const char* cursorPreviousName; int cursorTimerfd; uint32_t serial; + uint32_t pointerEnterSerial; + + int keyRepeatTimerfd; + int32_t keyRepeatRate; + int32_t keyRepeatDelay; + int keyRepeatScancode; - int32_t keyboardRepeatRate; - int32_t keyboardRepeatDelay; - int keyboardLastKey; - int keyboardLastScancode; char* clipboardString; - size_t clipboardSize; - char* clipboardSendString; - size_t clipboardSendSize; - int timerfd; short int keycodes[256]; short int scancodes[GLFW_KEY_LAST + 1]; + char keynames[GLFW_KEY_LAST + 1][5]; struct { void* handle; @@ -264,16 +338,14 @@ typedef struct _GLFWlibraryWayland struct xkb_keymap* keymap; struct xkb_state* state; -#ifdef HAVE_XKBCOMMON_COMPOSE_H struct xkb_compose_state* composeState; -#endif - xkb_mod_mask_t controlMask; - xkb_mod_mask_t altMask; - xkb_mod_mask_t shiftMask; - xkb_mod_mask_t superMask; - xkb_mod_mask_t capsLockMask; - xkb_mod_mask_t numLockMask; + xkb_mod_index_t controlIndex; + xkb_mod_index_t altIndex; + xkb_mod_index_t shiftIndex; + xkb_mod_index_t superIndex; + xkb_mod_index_t capsLockIndex; + xkb_mod_index_t numLockIndex; unsigned int modifiers; PFN_xkb_context_new context_new; @@ -282,13 +354,14 @@ typedef struct _GLFWlibraryWayland PFN_xkb_keymap_unref keymap_unref; PFN_xkb_keymap_mod_get_index keymap_mod_get_index; PFN_xkb_keymap_key_repeats keymap_key_repeats; + PFN_xkb_keymap_key_get_syms_by_level keymap_key_get_syms_by_level; PFN_xkb_state_new state_new; PFN_xkb_state_unref state_unref; PFN_xkb_state_key_get_syms state_key_get_syms; PFN_xkb_state_update_mask state_update_mask; - PFN_xkb_state_serialize_mods state_serialize_mods; + PFN_xkb_state_key_get_layout state_key_get_layout; + PFN_xkb_state_mod_index_is_active state_mod_index_is_active; -#ifdef HAVE_XKBCOMMON_COMPOSE_H PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale; PFN_xkb_compose_table_unref compose_table_unref; PFN_xkb_compose_state_new compose_state_new; @@ -296,12 +369,32 @@ typedef struct _GLFWlibraryWayland PFN_xkb_compose_state_feed compose_state_feed; PFN_xkb_compose_state_get_status compose_state_get_status; PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym; -#endif } xkb; _GLFWwindow* pointerFocus; _GLFWwindow* keyboardFocus; + struct { + void* handle; + PFN_wl_display_flush display_flush; + PFN_wl_display_cancel_read display_cancel_read; + PFN_wl_display_dispatch_pending display_dispatch_pending; + PFN_wl_display_read_events display_read_events; + PFN_wl_display_disconnect display_disconnect; + PFN_wl_display_roundtrip display_roundtrip; + PFN_wl_display_get_fd display_get_fd; + PFN_wl_display_prepare_read display_prepare_read; + PFN_wl_proxy_marshal proxy_marshal; + PFN_wl_proxy_add_listener proxy_add_listener; + PFN_wl_proxy_destroy proxy_destroy; + PFN_wl_proxy_marshal_constructor proxy_marshal_constructor; + PFN_wl_proxy_marshal_constructor_versioned proxy_marshal_constructor_versioned; + PFN_wl_proxy_get_user_data proxy_get_user_data; + PFN_wl_proxy_set_user_data proxy_set_user_data; + PFN_wl_proxy_get_version proxy_get_version; + PFN_wl_proxy_marshal_flags proxy_marshal_flags; + } client; + struct { void* handle; @@ -318,7 +411,6 @@ typedef struct _GLFWlibraryWayland PFN_wl_egl_window_destroy window_destroy; PFN_wl_egl_window_resize window_resize; } egl; - } _GLFWlibraryWayland; // Wayland-specific per-monitor data @@ -332,7 +424,6 @@ typedef struct _GLFWmonitorWayland int x; int y; int scale; - } _GLFWmonitorWayland; // Wayland-specific per-cursor data @@ -347,6 +438,84 @@ typedef struct _GLFWcursorWayland int currentImage; } _GLFWcursorWayland; +GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform); +int _glfwInitWayland(void); +void _glfwTerminateWayland(void); + +GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); +void _glfwDestroyWindowWayland(_GLFWwindow* window); +void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title); +void _glfwSetWindowIconWayland(_GLFWwindow* window, int count, const GLFWimage* images); +void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos); +void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos); +void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height); +void _glfwSetWindowSizeWayland(_GLFWwindow* window, int width, int height); +void _glfwSetWindowSizeLimitsWayland(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); +void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom); +void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height); +void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); +void _glfwGetWindowContentScaleWayland(_GLFWwindow* window, float* xscale, float* yscale); +void _glfwIconifyWindowWayland(_GLFWwindow* window); +void _glfwRestoreWindowWayland(_GLFWwindow* window); +void _glfwMaximizeWindowWayland(_GLFWwindow* window); +void _glfwShowWindowWayland(_GLFWwindow* window); +void _glfwHideWindowWayland(_GLFWwindow* window); +void _glfwRequestWindowAttentionWayland(_GLFWwindow* window); +void _glfwFocusWindowWayland(_GLFWwindow* window); +void _glfwSetWindowMonitorWayland(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); +GLFWbool _glfwWindowFocusedWayland(_GLFWwindow* window); +GLFWbool _glfwWindowIconifiedWayland(_GLFWwindow* window); +GLFWbool _glfwWindowVisibleWayland(_GLFWwindow* window); +GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window); +GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window); +GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window); +void _glfwSetWindowResizableWayland(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowFloatingWayland(_GLFWwindow* window, GLFWbool enabled); +float _glfwGetWindowOpacityWayland(_GLFWwindow* window); +void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity); +void _glfwSetWindowMousePassthroughWayland(_GLFWwindow* window, GLFWbool enabled); + +void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled); +GLFWbool _glfwRawMouseMotionSupportedWayland(void); + +void _glfwPollEventsWayland(void); +void _glfwWaitEventsWayland(void); +void _glfwWaitEventsTimeoutWayland(double timeout); +void _glfwPostEmptyEventWayland(void); + +void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos); +void _glfwSetCursorPosWayland(_GLFWwindow* window, double xpos, double ypos); +void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode); +const char* _glfwGetScancodeNameWayland(int scancode); +int _glfwGetKeyScancodeWayland(int key); +GLFWbool _glfwCreateCursorWayland(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); +GLFWbool _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape); +void _glfwDestroyCursorWayland(_GLFWcursor* cursor); +void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor); +void _glfwSetClipboardStringWayland(const char* string); +const char* _glfwGetClipboardStringWayland(void); + +EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs); +EGLNativeDisplayType _glfwGetEGLNativeDisplayWayland(void); +EGLNativeWindowType _glfwGetEGLNativeWindowWayland(_GLFWwindow* window); + +void _glfwGetRequiredInstanceExtensionsWayland(char** extensions); +GLFWbool _glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); +VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +void _glfwFreeMonitorWayland(_GLFWmonitor* monitor); +void _glfwGetMonitorPosWayland(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwGetMonitorContentScaleWayland(_GLFWmonitor* monitor, float* xscale, float* yscale); +void _glfwGetMonitorWorkareaWayland(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); +GLFWvidmode* _glfwGetVideoModesWayland(_GLFWmonitor* monitor, int* count); +void _glfwGetVideoModeWayland(_GLFWmonitor* monitor, GLFWvidmode* mode); +GLFWbool _glfwGetGammaRampWayland(_GLFWmonitor* monitor, GLFWgammaramp* ramp); +void _glfwSetGammaRampWayland(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); void _glfwAddOutputWayland(uint32_t name, uint32_t version); +void _glfwUpdateContentScaleWayland(_GLFWwindow* window); + +void _glfwAddSeatListenerWayland(struct wl_seat* seat); +void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); diff --git a/src/external/glfw/src/wl_window.c b/src/external/glfw/src/wl_window.c index 939f9c196..76d5f15bc 100644 --- a/src/external/glfw/src/wl_window.c +++ b/src/external/glfw/src/wl_window.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,16 @@ #include #include +#include "wayland-client-protocol.h" +#include "wayland-xdg-shell-client-protocol.h" +#include "wayland-xdg-decoration-client-protocol.h" +#include "wayland-viewporter-client-protocol.h" +#include "wayland-relative-pointer-unstable-v1-client-protocol.h" +#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" +#include "wayland-idle-inhibit-unstable-v1-client-protocol.h" + +#define GLFW_BORDER_SIZE 4 +#define GLFW_CAPTION_HEIGHT 24 static int createTmpfileCloexec(char* tmpname) { @@ -104,12 +115,12 @@ static int createAnonymousFile(off_t size) return -1; } - name = calloc(strlen(path) + sizeof(template), 1); + name = _glfw_calloc(strlen(path) + sizeof(template), 1); strcpy(name, path); strcat(name, template); fd = createTmpfileCloexec(name); - free(name); + _glfw_free(name); if (fd < 0) return -1; } @@ -131,37 +142,34 @@ static int createAnonymousFile(off_t size) static struct wl_buffer* createShmBuffer(const GLFWimage* image) { - struct wl_shm_pool* pool; - struct wl_buffer* buffer; - int stride = image->width * 4; - int length = image->width * image->height * 4; - void* data; - int fd, i; + const int stride = image->width * 4; + const int length = image->width * image->height * 4; - fd = createAnonymousFile(length); + const int fd = createAnonymousFile(length); if (fd < 0) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Creating a buffer file for %d B failed: %s", + "Wayland: Failed to create buffer file of size %d: %s", length, strerror(errno)); return NULL; } - data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + void* data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: mmap failed: %s", strerror(errno)); + "Wayland: Failed to map file: %s", strerror(errno)); close(fd); return NULL; } - pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); + struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); close(fd); + unsigned char* source = (unsigned char*) image->pixels; unsigned char* target = data; - for (i = 0; i < image->width * image->height; i++, source += 4) + for (int i = 0; i < image->width * image->height; i++, source += 4) { unsigned int alpha = source[3]; @@ -171,7 +179,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) *target++ = (unsigned char) alpha; } - buffer = + struct wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, image->width, image->height, @@ -182,14 +190,12 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) return buffer; } -static void createDecoration(_GLFWdecorationWayland* decoration, - struct wl_surface* parent, - struct wl_buffer* buffer, GLFWbool opaque, - int x, int y, - int width, int height) +static void createFallbackDecoration(_GLFWdecorationWayland* decoration, + struct wl_surface* parent, + struct wl_buffer* buffer, + int x, int y, + int width, int height) { - struct wl_region* region; - decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor); decoration->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, @@ -200,25 +206,19 @@ static void createDecoration(_GLFWdecorationWayland* decoration, wp_viewport_set_destination(decoration->viewport, width, height); wl_surface_attach(decoration->surface, buffer, 0, 0); - if (opaque) - { - region = wl_compositor_create_region(_glfw.wl.compositor); - wl_region_add(region, 0, 0, width, height); - wl_surface_set_opaque_region(decoration->surface, region); - wl_surface_commit(decoration->surface); - wl_region_destroy(region); - } - else - wl_surface_commit(decoration->surface); + struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor); + wl_region_add(region, 0, 0, width, height); + wl_surface_set_opaque_region(decoration->surface, region); + wl_surface_commit(decoration->surface); + wl_region_destroy(region); } -static void createDecorations(_GLFWwindow* window) +static void createFallbackDecorations(_GLFWwindow* window) { unsigned char data[] = { 224, 224, 224, 255 }; const GLFWimage image = { 1, 1, data }; - GLFWbool opaque = (data[3] == 255); - if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide) + if (!_glfw.wl.viewporter) return; if (!window->wl.decorations.buffer) @@ -226,25 +226,25 @@ static void createDecorations(_GLFWwindow* window) if (!window->wl.decorations.buffer) return; - createDecoration(&window->wl.decorations.top, window->wl.surface, - window->wl.decorations.buffer, opaque, - 0, -_GLFW_DECORATION_TOP, - window->wl.width, _GLFW_DECORATION_TOP); - createDecoration(&window->wl.decorations.left, window->wl.surface, - window->wl.decorations.buffer, opaque, - -_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP, - _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); - createDecoration(&window->wl.decorations.right, window->wl.surface, - window->wl.decorations.buffer, opaque, - window->wl.width, -_GLFW_DECORATION_TOP, - _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); - createDecoration(&window->wl.decorations.bottom, window->wl.surface, - window->wl.decorations.buffer, opaque, - -_GLFW_DECORATION_WIDTH, window->wl.height, - window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH); + createFallbackDecoration(&window->wl.decorations.top, window->wl.surface, + window->wl.decorations.buffer, + 0, -GLFW_CAPTION_HEIGHT, + window->wl.width, GLFW_CAPTION_HEIGHT); + createFallbackDecoration(&window->wl.decorations.left, window->wl.surface, + window->wl.decorations.buffer, + -GLFW_BORDER_SIZE, -GLFW_CAPTION_HEIGHT, + GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); + createFallbackDecoration(&window->wl.decorations.right, window->wl.surface, + window->wl.decorations.buffer, + window->wl.width, -GLFW_CAPTION_HEIGHT, + GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); + createFallbackDecoration(&window->wl.decorations.bottom, window->wl.surface, + window->wl.decorations.buffer, + -GLFW_BORDER_SIZE, window->wl.height, + window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE); } -static void destroyDecoration(_GLFWdecorationWayland* decoration) +static void destroyFallbackDecoration(_GLFWdecorationWayland* decoration) { if (decoration->subsurface) wl_subsurface_destroy(decoration->subsurface); @@ -257,32 +257,38 @@ static void destroyDecoration(_GLFWdecorationWayland* decoration) decoration->viewport = NULL; } -static void destroyDecorations(_GLFWwindow* window) +static void destroyFallbackDecorations(_GLFWwindow* window) { - destroyDecoration(&window->wl.decorations.top); - destroyDecoration(&window->wl.decorations.left); - destroyDecoration(&window->wl.decorations.right); - destroyDecoration(&window->wl.decorations.bottom); + destroyFallbackDecoration(&window->wl.decorations.top); + destroyFallbackDecoration(&window->wl.decorations.left); + destroyFallbackDecoration(&window->wl.decorations.right); + destroyFallbackDecoration(&window->wl.decorations.bottom); } -static void xdgDecorationHandleConfigure(void* data, +static void xdgDecorationHandleConfigure(void* userData, struct zxdg_toplevel_decoration_v1* decoration, uint32_t mode) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; - window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + window->wl.xdg.decorationMode = mode; - if (!window->wl.decorations.serverSide) - createDecorations(window); + if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) + { + if (window->decorated && !window->monitor) + createFallbackDecorations(window); + } + else + destroyFallbackDecorations(window); } -static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = { +static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = +{ xdgDecorationHandleConfigure, }; // Makes the surface considered as XRGB instead of ARGB. -static void setOpaqueRegion(_GLFWwindow* window) +static void setContentAreaOpaque(_GLFWwindow* window) { struct wl_region* region; @@ -292,7 +298,6 @@ static void setOpaqueRegion(_GLFWwindow* window) wl_region_add(region, 0, 0, window->wl.width, window->wl.height); wl_surface_set_opaque_region(window->wl.surface, region); - wl_surface_commit(window->wl.surface); wl_region_destroy(region); } @@ -302,97 +307,87 @@ static void resizeWindow(_GLFWwindow* window) int scale = window->wl.scale; int scaledWidth = window->wl.width * scale; int scaledHeight = window->wl.height * scale; - wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); + + if (window->wl.egl.window) + wl_egl_window_resize(window->wl.egl.window, scaledWidth, scaledHeight, 0, 0); if (!window->wl.transparent) - setOpaqueRegion(window); + setContentAreaOpaque(window); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); - _glfwInputWindowContentScale(window, scale, scale); if (!window->wl.decorations.top.surface) return; - // Top decoration. wp_viewport_set_destination(window->wl.decorations.top.viewport, - window->wl.width, _GLFW_DECORATION_TOP); + window->wl.width, GLFW_CAPTION_HEIGHT); wl_surface_commit(window->wl.decorations.top.surface); - // Left decoration. wp_viewport_set_destination(window->wl.decorations.left.viewport, - _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); wl_surface_commit(window->wl.decorations.left.surface); - // Right decoration. wl_subsurface_set_position(window->wl.decorations.right.subsurface, - window->wl.width, -_GLFW_DECORATION_TOP); + window->wl.width, -GLFW_CAPTION_HEIGHT); wp_viewport_set_destination(window->wl.decorations.right.viewport, - _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); wl_surface_commit(window->wl.decorations.right.surface); - // Bottom decoration. wl_subsurface_set_position(window->wl.decorations.bottom.subsurface, - -_GLFW_DECORATION_WIDTH, window->wl.height); + -GLFW_BORDER_SIZE, window->wl.height); wp_viewport_set_destination(window->wl.decorations.bottom.viewport, - window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH); + window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE); wl_surface_commit(window->wl.decorations.bottom.surface); } -static void checkScaleChange(_GLFWwindow* window) +void _glfwUpdateContentScaleWayland(_GLFWwindow* window) { - int scale = 1; - int i; - int monitorScale; - - // Check if we will be able to set the buffer scale or not. - if (_glfw.wl.compositorVersion < 3) + if (_glfw.wl.compositorVersion < WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) return; // Get the scale factor from the highest scale monitor. - for (i = 0; i < window->wl.monitorsCount; ++i) - { - monitorScale = window->wl.monitors[i]->wl.scale; - if (scale < monitorScale) - scale = monitorScale; - } + int maxScale = 1; + + for (int i = 0; i < window->wl.monitorsCount; i++) + maxScale = _glfw_max(window->wl.monitors[i]->wl.scale, maxScale); // Only change the framebuffer size if the scale changed. - if (scale != window->wl.scale) + if (window->wl.scale != maxScale) { - window->wl.scale = scale; - wl_surface_set_buffer_scale(window->wl.surface, scale); + window->wl.scale = maxScale; + wl_surface_set_buffer_scale(window->wl.surface, maxScale); + _glfwInputWindowContentScale(window, maxScale, maxScale); resizeWindow(window); } } -static void surfaceHandleEnter(void *data, - struct wl_surface *surface, - struct wl_output *output) +static void surfaceHandleEnter(void* userData, + struct wl_surface* surface, + struct wl_output* output) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; _GLFWmonitor* monitor = wl_output_get_user_data(output); if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) { ++window->wl.monitorsSize; window->wl.monitors = - realloc(window->wl.monitors, - window->wl.monitorsSize * sizeof(_GLFWmonitor*)); + _glfw_realloc(window->wl.monitors, + window->wl.monitorsSize * sizeof(_GLFWmonitor*)); } window->wl.monitors[window->wl.monitorsCount++] = monitor; - checkScaleChange(window); + _glfwUpdateContentScaleWayland(window); } -static void surfaceHandleLeave(void *data, - struct wl_surface *surface, - struct wl_output *output) +static void surfaceHandleLeave(void* userData, + struct wl_surface* surface, + struct wl_output* output) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; _GLFWmonitor* monitor = wl_output_get_user_data(output); - GLFWbool found; - int i; + GLFWbool found = GLFW_FALSE; - for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i) + for (int i = 0; i < window->wl.monitorsCount - 1; ++i) { if (monitor == window->wl.monitors[i]) found = GLFW_TRUE; @@ -401,10 +396,11 @@ static void surfaceHandleLeave(void *data, } window->wl.monitors[--window->wl.monitorsCount] = NULL; - checkScaleChange(window); + _glfwUpdateContentScaleWayland(window); } -static const struct wl_surface_listener surfaceListener = { +static const struct wl_surface_listener surfaceListener = +{ surfaceHandleEnter, surfaceHandleLeave }; @@ -418,7 +414,7 @@ static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) _glfw.wl.idleInhibitManager, window->wl.surface); if (!window->wl.idleInhibitor) _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Idle inhibitor creation failed"); + "Wayland: Failed to create idle inhibitor"); } else if (!enable && window->wl.idleInhibitor) { @@ -427,211 +423,266 @@ static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) } } -static GLFWbool createSurface(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) -{ - window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor); - if (!window->wl.surface) - return GLFW_FALSE; - - wl_surface_add_listener(window->wl.surface, - &surfaceListener, - window); - - wl_surface_set_user_data(window->wl.surface, window); - - window->wl.native = wl_egl_window_create(window->wl.surface, - wndconfig->width, - wndconfig->height); - if (!window->wl.native) - return GLFW_FALSE; - - window->wl.width = wndconfig->width; - window->wl.height = wndconfig->height; - window->wl.scale = 1; - - if (!window->wl.transparent) - setOpaqueRegion(window); - - return GLFW_TRUE; -} - -static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, - int refreshRate) +// Make the specified window and its video mode active on its monitor +// +static void acquireMonitor(_GLFWwindow* window) { if (window->wl.xdg.toplevel) { - xdg_toplevel_set_fullscreen( - window->wl.xdg.toplevel, - monitor->wl.output); + xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, + window->monitor->wl.output); } + setIdleInhibitor(window, GLFW_TRUE); - if (!window->wl.decorations.serverSide) - destroyDecorations(window); + + if (window->wl.decorations.top.surface) + destroyFallbackDecorations(window); } -static void xdgToplevelHandleConfigure(void* data, +// Remove the window and restore the original video mode +// +static void releaseMonitor(_GLFWwindow* window) +{ + if (window->wl.xdg.toplevel) + xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); + + setIdleInhibitor(window, GLFW_FALSE); + + if (window->wl.xdg.decorationMode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) + { + if (window->decorated) + createFallbackDecorations(window); + } +} + +static void xdgToplevelHandleConfigure(void* userData, struct xdg_toplevel* toplevel, int32_t width, int32_t height, struct wl_array* states) { - _GLFWwindow* window = data; - float aspectRatio; - float targetRatio; + _GLFWwindow* window = userData; uint32_t* state; - GLFWbool maximized = GLFW_FALSE; - GLFWbool fullscreen = GLFW_FALSE; - GLFWbool activated = GLFW_FALSE; + + window->wl.pending.activated = GLFW_FALSE; + window->wl.pending.maximized = GLFW_FALSE; + window->wl.pending.fullscreen = GLFW_FALSE; wl_array_for_each(state, states) { switch (*state) { case XDG_TOPLEVEL_STATE_MAXIMIZED: - maximized = GLFW_TRUE; + window->wl.pending.maximized = GLFW_TRUE; break; case XDG_TOPLEVEL_STATE_FULLSCREEN: - fullscreen = GLFW_TRUE; + window->wl.pending.fullscreen = GLFW_TRUE; break; case XDG_TOPLEVEL_STATE_RESIZING: break; case XDG_TOPLEVEL_STATE_ACTIVATED: - activated = GLFW_TRUE; + window->wl.pending.activated = GLFW_TRUE; break; } } - if (width != 0 && height != 0) + if (width && height) { - if (!maximized && !fullscreen) + if (window->wl.decorations.top.surface) { - if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) - { - aspectRatio = (float)width / (float)height; - targetRatio = (float)window->numer / (float)window->denom; - if (aspectRatio < targetRatio) - height = width / targetRatio; - else if (aspectRatio > targetRatio) - width = height * targetRatio; - } + window->wl.pending.width = _glfw_max(0, width - GLFW_BORDER_SIZE * 2); + window->wl.pending.height = + _glfw_max(0, height - GLFW_BORDER_SIZE - GLFW_CAPTION_HEIGHT); } - - _glfwInputWindowSize(window, width, height); - _glfwPlatformSetWindowSize(window, width, height); - _glfwInputWindowDamage(window); - } - - if (window->wl.wasFullscreen && window->autoIconify) - { - if (!activated || !fullscreen) + else { - _glfwPlatformIconifyWindow(window); - window->wl.wasFullscreen = GLFW_FALSE; + window->wl.pending.width = width; + window->wl.pending.height = height; } } - if (fullscreen && activated) - window->wl.wasFullscreen = GLFW_TRUE; - _glfwInputWindowFocus(window, activated); + else + { + window->wl.pending.width = window->wl.width; + window->wl.pending.height = window->wl.height; + } } -static void xdgToplevelHandleClose(void* data, +static void xdgToplevelHandleClose(void* userData, struct xdg_toplevel* toplevel) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; _glfwInputWindowCloseRequest(window); } -static const struct xdg_toplevel_listener xdgToplevelListener = { +static const struct xdg_toplevel_listener xdgToplevelListener = +{ xdgToplevelHandleConfigure, xdgToplevelHandleClose }; -static void xdgSurfaceHandleConfigure(void* data, +static void xdgSurfaceHandleConfigure(void* userData, struct xdg_surface* surface, uint32_t serial) { + _GLFWwindow* window = userData; + xdg_surface_ack_configure(surface, serial); + + if (window->wl.activated != window->wl.pending.activated) + { + window->wl.activated = window->wl.pending.activated; + if (!window->wl.activated) + { + if (window->monitor && window->autoIconify) + xdg_toplevel_set_minimized(window->wl.xdg.toplevel); + } + } + + if (window->wl.maximized != window->wl.pending.maximized) + { + window->wl.maximized = window->wl.pending.maximized; + _glfwInputWindowMaximize(window, window->wl.maximized); + } + + window->wl.fullscreen = window->wl.pending.fullscreen; + + int width = window->wl.pending.width; + int height = window->wl.pending.height; + + if (!window->wl.maximized && !window->wl.fullscreen) + { + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) + { + const float aspectRatio = (float) width / (float) height; + const float targetRatio = (float) window->numer / (float) window->denom; + if (aspectRatio < targetRatio) + height = width / targetRatio; + else if (aspectRatio > targetRatio) + width = height * targetRatio; + } + } + + if (width != window->wl.width || height != window->wl.height) + { + window->wl.width = width; + window->wl.height = height; + resizeWindow(window); + + _glfwInputWindowSize(window, width, height); + + if (window->wl.visible) + _glfwInputWindowDamage(window); + } + + if (!window->wl.visible) + { + // Allow the window to be mapped only if it either has no XDG + // decorations or they have already received a configure event + if (!window->wl.xdg.decoration || window->wl.xdg.decorationMode) + { + window->wl.visible = GLFW_TRUE; + _glfwInputWindowDamage(window); + } + } } -static const struct xdg_surface_listener xdgSurfaceListener = { +static const struct xdg_surface_listener xdgSurfaceListener = +{ xdgSurfaceHandleConfigure }; -static void setXdgDecorations(_GLFWwindow* window) -{ - if (_glfw.wl.decorationManager) - { - window->wl.xdg.decoration = - zxdg_decoration_manager_v1_get_toplevel_decoration( - _glfw.wl.decorationManager, window->wl.xdg.toplevel); - zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, - &xdgDecorationListener, - window); - zxdg_toplevel_decoration_v1_set_mode( - window->wl.xdg.decoration, - ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - } - else - { - window->wl.decorations.serverSide = GLFW_FALSE; - createDecorations(window); - } -} - -static GLFWbool createXdgSurface(_GLFWwindow* window) +static GLFWbool createShellObjects(_GLFWwindow* window) { window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase, window->wl.surface); if (!window->wl.xdg.surface) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: xdg-surface creation failed"); + "Wayland: Failed to create xdg-surface for window"); return GLFW_FALSE; } - xdg_surface_add_listener(window->wl.xdg.surface, - &xdgSurfaceListener, - window); + xdg_surface_add_listener(window->wl.xdg.surface, &xdgSurfaceListener, window); window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface); if (!window->wl.xdg.toplevel) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: xdg-toplevel creation failed"); + "Wayland: Failed to create xdg-toplevel for window"); return GLFW_FALSE; } - xdg_toplevel_add_listener(window->wl.xdg.toplevel, - &xdgToplevelListener, - window); + xdg_toplevel_add_listener(window->wl.xdg.toplevel, &xdgToplevelListener, window); + + if (window->wl.appId) + xdg_toplevel_set_app_id(window->wl.xdg.toplevel, window->wl.appId); if (window->wl.title) xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title); - if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) - xdg_toplevel_set_min_size(window->wl.xdg.toplevel, - window->minwidth, window->minheight); - if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) - xdg_toplevel_set_max_size(window->wl.xdg.toplevel, - window->maxwidth, window->maxheight); - if (window->monitor) { - xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, - window->monitor->wl.output); + xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, window->monitor->wl.output); setIdleInhibitor(window, GLFW_TRUE); } - else if (window->wl.maximized) - { - xdg_toplevel_set_maximized(window->wl.xdg.toplevel); - setIdleInhibitor(window, GLFW_FALSE); - setXdgDecorations(window); - } else { + if (window->wl.maximized) + xdg_toplevel_set_maximized(window->wl.xdg.toplevel); + setIdleInhibitor(window, GLFW_FALSE); - setXdgDecorations(window); + + if (_glfw.wl.decorationManager) + { + window->wl.xdg.decoration = + zxdg_decoration_manager_v1_get_toplevel_decoration( + _glfw.wl.decorationManager, window->wl.xdg.toplevel); + zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, + &xdgDecorationListener, + window); + + uint32_t mode; + + if (window->decorated) + mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; + else + mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; + + zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode); + } + else + { + if (window->decorated) + createFallbackDecorations(window); + } + } + + if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) + { + int minwidth = window->minwidth; + int minheight = window->minheight; + + if (window->wl.decorations.top.surface) + { + minwidth += GLFW_BORDER_SIZE * 2; + minheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE; + } + + xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); + } + + if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) + { + int maxwidth = window->maxwidth; + int maxheight = window->maxheight; + + if (window->wl.decorations.top.surface) + { + maxwidth += GLFW_BORDER_SIZE * 2; + maxheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE; + } + + xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); } wl_surface_commit(window->wl.surface); @@ -640,10 +691,61 @@ static GLFWbool createXdgSurface(_GLFWwindow* window) return GLFW_TRUE; } +static void destroyShellObjects(_GLFWwindow* window) +{ + destroyFallbackDecorations(window); + + if (window->wl.xdg.decoration) + zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); + + if (window->wl.xdg.toplevel) + xdg_toplevel_destroy(window->wl.xdg.toplevel); + + if (window->wl.xdg.surface) + xdg_surface_destroy(window->wl.xdg.surface); + + window->wl.xdg.decoration = NULL; + window->wl.xdg.decorationMode = 0; + window->wl.xdg.toplevel = NULL; + window->wl.xdg.surface = NULL; +} + +static GLFWbool createNativeSurface(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) +{ + window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor); + if (!window->wl.surface) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create window surface"); + return GLFW_FALSE; + } + + wl_surface_add_listener(window->wl.surface, + &surfaceListener, + window); + + wl_surface_set_user_data(window->wl.surface, window); + + window->wl.width = wndconfig->width; + window->wl.height = wndconfig->height; + window->wl.scale = 1; + window->wl.title = _glfw_strdup(wndconfig->title); + window->wl.appId = _glfw_strdup(wndconfig->wl.appId); + + window->wl.maximized = wndconfig->maximized; + + window->wl.transparent = fbconfig->transparent; + if (!window->wl.transparent) + setContentAreaOpaque(window); + + return GLFW_TRUE; +} + static void setCursorImage(_GLFWwindow* window, _GLFWcursorWayland* cursorWayland) { - struct itimerspec timer = {}; + struct itimerspec timer = {0}; struct wl_cursor* wlCursor = cursorWayland->cursor; struct wl_cursor_image* image; struct wl_buffer* buffer; @@ -675,7 +777,7 @@ static void setCursorImage(_GLFWwindow* window, cursorWayland->yhot = image->hotspot_y; } - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, surface, cursorWayland->xhot / scale, cursorWayland->yhot / scale); @@ -702,93 +804,999 @@ static void incrementCursorImage(_GLFWwindow* window) } } -static void handleEvents(int timeout) +static GLFWbool flushDisplay(void) { - struct wl_display* display = _glfw.wl.display; - struct pollfd fds[] = { - { wl_display_get_fd(display), POLLIN }, - { _glfw.wl.timerfd, POLLIN }, - { _glfw.wl.cursorTimerfd, POLLIN }, - }; - ssize_t read_ret; - uint64_t repeats, i; - - while (wl_display_prepare_read(display) != 0) - wl_display_dispatch_pending(display); - - // If an error different from EAGAIN happens, we have likely been - // disconnected from the Wayland session, try to handle that the best we - // can. - if (wl_display_flush(display) < 0 && errno != EAGAIN) + while (wl_display_flush(_glfw.wl.display) == -1) { - _GLFWwindow* window = _glfw.windowListHead; - while (window) + if (errno != EAGAIN) + return GLFW_FALSE; + + struct pollfd fd = { wl_display_get_fd(_glfw.wl.display), POLLOUT }; + + while (poll(&fd, 1, -1) == -1) { - _glfwInputWindowCloseRequest(window); - window = window->next; + if (errno != EINTR && errno != EAGAIN) + return GLFW_FALSE; } - wl_display_cancel_read(display); - return; } - if (poll(fds, 3, timeout) > 0) + return GLFW_TRUE; +} + +static int translateKey(uint32_t scancode) +{ + if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) + return _glfw.wl.keycodes[scancode]; + + return GLFW_KEY_UNKNOWN; +} + +static xkb_keysym_t composeSymbol(xkb_keysym_t sym) +{ + if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) + return sym; + if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) + != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} + +static void inputText(_GLFWwindow* window, uint32_t scancode) +{ + const xkb_keysym_t* keysyms; + const xkb_keycode_t keycode = scancode + 8; + + if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1) + { + const xkb_keysym_t keysym = composeSymbol(keysyms[0]); + const uint32_t codepoint = _glfwKeySym2Unicode(keysym); + if (codepoint != GLFW_INVALID_CODEPOINT) + { + const int mods = _glfw.wl.xkb.modifiers; + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + _glfwInputChar(window, codepoint, mods, plain); + } + } +} + +static void handleEvents(double* timeout) +{ + GLFWbool event = GLFW_FALSE; + struct pollfd fds[] = + { + { wl_display_get_fd(_glfw.wl.display), POLLIN }, + { _glfw.wl.keyRepeatTimerfd, POLLIN }, + { _glfw.wl.cursorTimerfd, POLLIN }, + }; + + while (!event) + { + while (wl_display_prepare_read(_glfw.wl.display) != 0) + wl_display_dispatch_pending(_glfw.wl.display); + + // If an error other than EAGAIN happens, we have likely been disconnected + // from the Wayland session; try to handle that the best we can. + if (!flushDisplay()) + { + wl_display_cancel_read(_glfw.wl.display); + + _GLFWwindow* window = _glfw.windowListHead; + while (window) + { + _glfwInputWindowCloseRequest(window); + window = window->next; + } + + return; + } + + if (!_glfwPollPOSIX(fds, 3, timeout)) + { + wl_display_cancel_read(_glfw.wl.display); + return; + } + if (fds[0].revents & POLLIN) { - wl_display_read_events(display); - wl_display_dispatch_pending(display); + wl_display_read_events(_glfw.wl.display); + if (wl_display_dispatch_pending(_glfw.wl.display) > 0) + event = GLFW_TRUE; } else - { - wl_display_cancel_read(display); - } + wl_display_cancel_read(_glfw.wl.display); if (fds[1].revents & POLLIN) { - read_ret = read(_glfw.wl.timerfd, &repeats, sizeof(repeats)); - if (read_ret != 8) - return; + uint64_t repeats; - if (_glfw.wl.keyboardFocus) + if (read(_glfw.wl.keyRepeatTimerfd, &repeats, sizeof(repeats)) == 8) { - for (i = 0; i < repeats; ++i) + for (uint64_t i = 0; i < repeats; i++) { _glfwInputKey(_glfw.wl.keyboardFocus, - _glfw.wl.keyboardLastKey, - _glfw.wl.keyboardLastScancode, - GLFW_REPEAT, + translateKey(_glfw.wl.keyRepeatScancode), + _glfw.wl.keyRepeatScancode, + GLFW_PRESS, _glfw.wl.xkb.modifiers); + inputText(_glfw.wl.keyboardFocus, _glfw.wl.keyRepeatScancode); } + + event = GLFW_TRUE; } } if (fds[2].revents & POLLIN) { - read_ret = read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)); - if (read_ret != 8) - return; + uint64_t repeats; - incrementCursorImage(_glfw.wl.pointerFocus); + if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8) + { + incrementCursorImage(_glfw.wl.pointerFocus); + event = GLFW_TRUE; + } } } +} + +// Reads the specified data offer as the specified MIME type +// +static char* readDataOfferAsString(struct wl_data_offer* offer, const char* mimeType) +{ + int fds[2]; + + if (pipe2(fds, O_CLOEXEC) == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create pipe for data offer: %s", + strerror(errno)); + return NULL; + } + + wl_data_offer_receive(offer, mimeType, fds[1]); + flushDisplay(); + close(fds[1]); + + char* string = NULL; + size_t size = 0; + size_t length = 0; + + for (;;) + { + const size_t readSize = 4096; + const size_t requiredSize = length + readSize + 1; + if (requiredSize > size) + { + char* longer = _glfw_realloc(string, requiredSize); + if (!longer) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + close(fds[0]); + return NULL; + } + + string = longer; + size = requiredSize; + } + + const ssize_t result = read(fds[0], string + length, readSize); + if (result == 0) + break; + else if (result == -1) + { + if (errno == EINTR) + continue; + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to read from data offer pipe: %s", + strerror(errno)); + close(fds[0]); + return NULL; + } + + length += result; + } + + close(fds[0]); + + string[length] = '\0'; + return string; +} + +static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, + _GLFWdecorationSideWayland* which) +{ + _GLFWdecorationSideWayland focus; + _GLFWwindow* window = _glfw.windowListHead; + if (!which) + which = &focus; + while (window) + { + if (surface == window->wl.decorations.top.surface) + { + *which = topDecoration; + break; + } + if (surface == window->wl.decorations.left.surface) + { + *which = leftDecoration; + break; + } + if (surface == window->wl.decorations.right.surface) + { + *which = rightDecoration; + break; + } + if (surface == window->wl.decorations.bottom.surface) + { + *which = bottomDecoration; + break; + } + window = window->next; + } + return window; +} + +static void pointerHandleEnter(void* userData, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface, + wl_fixed_t sx, + wl_fixed_t sy) +{ + // Happens in the case we just destroyed the surface. + if (!surface) + return; + + _GLFWdecorationSideWayland focus = mainWindow; + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (!window) + { + window = findWindowFromDecorationSurface(surface, &focus); + if (!window) + return; + } + + window->wl.decorations.focus = focus; + _glfw.wl.serial = serial; + _glfw.wl.pointerEnterSerial = serial; + _glfw.wl.pointerFocus = window; + + window->wl.hovered = GLFW_TRUE; + + _glfwSetCursorWayland(window, window->wl.currentCursor); + _glfwInputCursorEnter(window, GLFW_TRUE); +} + +static void pointerHandleLeave(void* userData, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + + if (!window) + return; + + window->wl.hovered = GLFW_FALSE; + + _glfw.wl.serial = serial; + _glfw.wl.pointerFocus = NULL; + _glfw.wl.cursorPreviousName = NULL; + _glfwInputCursorEnter(window, GLFW_FALSE); +} + +static void setCursor(_GLFWwindow* window, const char* name) +{ + struct wl_buffer* buffer; + struct wl_cursor* cursor; + struct wl_cursor_image* image; + struct wl_surface* surface = _glfw.wl.cursorSurface; + struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; + int scale = 1; + + if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) + { + // We only support up to scale=2 for now, since libwayland-cursor + // requires us to load a different theme for each size. + scale = 2; + theme = _glfw.wl.cursorThemeHiDPI; + } + + cursor = wl_cursor_theme_get_cursor(theme, name); + if (!cursor) + { + _glfwInputError(GLFW_CURSOR_UNAVAILABLE, + "Wayland: Standard cursor shape unavailable"); + return; + } + // TODO: handle animated cursors too. + image = cursor->images[0]; + + if (!image) + return; + + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, + surface, + image->hotspot_x / scale, + image->hotspot_y / scale); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + image->width, image->height); + wl_surface_commit(surface); + _glfw.wl.cursorPreviousName = name; +} + +static void pointerHandleMotion(void* userData, + struct wl_pointer* pointer, + uint32_t time, + wl_fixed_t sx, + wl_fixed_t sy) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + const char* cursorName = NULL; + double x, y; + + if (!window) + return; + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + return; + x = wl_fixed_to_double(sx); + y = wl_fixed_to_double(sy); + window->wl.cursorPosX = x; + window->wl.cursorPosY = y; + + switch (window->wl.decorations.focus) + { + case mainWindow: + _glfw.wl.cursorPreviousName = NULL; + _glfwInputCursorPos(window, x, y); + return; + case topDecoration: + if (y < GLFW_BORDER_SIZE) + cursorName = "n-resize"; + else + cursorName = "left_ptr"; + break; + case leftDecoration: + if (y < GLFW_BORDER_SIZE) + cursorName = "nw-resize"; + else + cursorName = "w-resize"; + break; + case rightDecoration: + if (y < GLFW_BORDER_SIZE) + cursorName = "ne-resize"; + else + cursorName = "e-resize"; + break; + case bottomDecoration: + if (x < GLFW_BORDER_SIZE) + cursorName = "sw-resize"; + else if (x > window->wl.width + GLFW_BORDER_SIZE) + cursorName = "se-resize"; + else + cursorName = "s-resize"; + break; + default: + assert(0); + } + if (_glfw.wl.cursorPreviousName != cursorName) + setCursor(window, cursorName); +} + +static void pointerHandleButton(void* userData, + struct wl_pointer* pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + int glfwButton; + uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; + + if (!window) + return; + if (button == BTN_LEFT) + { + switch (window->wl.decorations.focus) + { + case mainWindow: + break; + case topDecoration: + if (window->wl.cursorPosY < GLFW_BORDER_SIZE) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; + else + xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); + break; + case leftDecoration: + if (window->wl.cursorPosY < GLFW_BORDER_SIZE) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; + break; + case rightDecoration: + if (window->wl.cursorPosY < GLFW_BORDER_SIZE) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; + break; + case bottomDecoration: + if (window->wl.cursorPosX < GLFW_BORDER_SIZE) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + else if (window->wl.cursorPosX > window->wl.width + GLFW_BORDER_SIZE) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + break; + default: + assert(0); + } + if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) + { + xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, + serial, edges); + return; + } + } + else if (button == BTN_RIGHT) + { + if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) + { + xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, + _glfw.wl.seat, serial, + window->wl.cursorPosX, + window->wl.cursorPosY); + return; + } + } + + // Don’t pass the button to the user if it was related to a decoration. + if (window->wl.decorations.focus != mainWindow) + return; + + _glfw.wl.serial = serial; + + /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev + * codes. */ + glfwButton = button - BTN_LEFT; + + _glfwInputMouseClick(window, + glfwButton, + state == WL_POINTER_BUTTON_STATE_PRESSED + ? GLFW_PRESS + : GLFW_RELEASE, + _glfw.wl.xkb.modifiers); +} + +static void pointerHandleAxis(void* userData, + struct wl_pointer* pointer, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + double x = 0.0, y = 0.0; + // Wayland scroll events are in pointer motion coordinate space (think two + // finger scroll). The factor 10 is commonly used to convert to "scroll + // step means 1.0. + const double scrollFactor = 1.0 / 10.0; + + if (!window) + return; + + assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || + axis == WL_POINTER_AXIS_VERTICAL_SCROLL); + + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) + x = -wl_fixed_to_double(value) * scrollFactor; + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + y = -wl_fixed_to_double(value) * scrollFactor; + + _glfwInputScroll(window, x, y); +} + +static const struct wl_pointer_listener pointerListener = +{ + pointerHandleEnter, + pointerHandleLeave, + pointerHandleMotion, + pointerHandleButton, + pointerHandleAxis, +}; + +static void keyboardHandleKeymap(void* userData, + struct wl_keyboard* keyboard, + uint32_t format, + int fd, + uint32_t size) +{ + struct xkb_keymap* keymap; + struct xkb_state* state; + struct xkb_compose_table* composeTable; + struct xkb_compose_state* composeState; + + char* mapStr; + const char* locale; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + { + close(fd); + return; + } + + mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (mapStr == MAP_FAILED) { + close(fd); + return; + } + + keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, + mapStr, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); + munmap(mapStr, size); + close(fd); + + if (!keymap) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to compile keymap"); + return; + } + + state = xkb_state_new(keymap); + if (!state) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB state"); + xkb_keymap_unref(keymap); + return; + } + + // Look up the preferred locale, falling back to "C" as default. + locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) + locale = "C"; + + composeTable = + xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (composeTable) + { + composeState = + xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(composeTable); + if (composeState) + _glfw.wl.xkb.composeState = composeState; + else + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose state"); + } else { - wl_display_cancel_read(display); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose table"); + } + + xkb_keymap_unref(_glfw.wl.xkb.keymap); + xkb_state_unref(_glfw.wl.xkb.state); + _glfw.wl.xkb.keymap = keymap; + _glfw.wl.xkb.state = state; + + _glfw.wl.xkb.controlIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); + _glfw.wl.xkb.altIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); + _glfw.wl.xkb.shiftIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); + _glfw.wl.xkb.superIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); + _glfw.wl.xkb.capsLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock"); + _glfw.wl.xkb.numLockIndex = xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); +} + +static void keyboardHandleEnter(void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface, + struct wl_array* keys) +{ + // Happens in the case we just destroyed the surface. + if (!surface) + return; + + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (!window) + { + window = findWindowFromDecorationSurface(surface, NULL); + if (!window) + return; + } + + _glfw.wl.serial = serial; + _glfw.wl.keyboardFocus = window; + _glfwInputWindowFocus(window, GLFW_TRUE); +} + +static void keyboardHandleLeave(void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface) +{ + _GLFWwindow* window = _glfw.wl.keyboardFocus; + + if (!window) + return; + + struct itimerspec timer = {0}; + timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); + + _glfw.wl.serial = serial; + _glfw.wl.keyboardFocus = NULL; + _glfwInputWindowFocus(window, GLFW_FALSE); +} + +static void keyboardHandleKey(void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t time, + uint32_t scancode, + uint32_t state) +{ + _GLFWwindow* window = _glfw.wl.keyboardFocus; + if (!window) + return; + + const int key = translateKey(scancode); + const int action = + state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; + + _glfw.wl.serial = serial; + + struct itimerspec timer = {0}; + + if (action == GLFW_PRESS) + { + const xkb_keycode_t keycode = scancode + 8; + + if (xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode) && + _glfw.wl.keyRepeatRate > 0) + { + _glfw.wl.keyRepeatScancode = scancode; + if (_glfw.wl.keyRepeatRate > 1) + timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyRepeatRate; + else + timer.it_interval.tv_sec = 1; + + timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000; + timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000; + } + } + + timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); + + _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); + + if (action == GLFW_PRESS) + inputText(window, scancode); +} + +static void keyboardHandleModifiers(void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t modsDepressed, + uint32_t modsLatched, + uint32_t modsLocked, + uint32_t group) +{ + _glfw.wl.serial = serial; + + if (!_glfw.wl.xkb.keymap) + return; + + xkb_state_update_mask(_glfw.wl.xkb.state, + modsDepressed, + modsLatched, + modsLocked, + 0, + 0, + group); + + _glfw.wl.xkb.modifiers = 0; + + struct + { + xkb_mod_index_t index; + unsigned int bit; + } modifiers[] = + { + { _glfw.wl.xkb.controlIndex, GLFW_MOD_CONTROL }, + { _glfw.wl.xkb.altIndex, GLFW_MOD_ALT }, + { _glfw.wl.xkb.shiftIndex, GLFW_MOD_SHIFT }, + { _glfw.wl.xkb.superIndex, GLFW_MOD_SUPER }, + { _glfw.wl.xkb.capsLockIndex, GLFW_MOD_CAPS_LOCK }, + { _glfw.wl.xkb.numLockIndex, GLFW_MOD_NUM_LOCK } + }; + + for (size_t i = 0; i < sizeof(modifiers) / sizeof(modifiers[0]); i++) + { + if (xkb_state_mod_index_is_active(_glfw.wl.xkb.state, + modifiers[i].index, + XKB_STATE_MODS_EFFECTIVE) == 1) + { + _glfw.wl.xkb.modifiers |= modifiers[i].bit; + } } } +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION +static void keyboardHandleRepeatInfo(void* userData, + struct wl_keyboard* keyboard, + int32_t rate, + int32_t delay) +{ + if (keyboard != _glfw.wl.keyboard) + return; + + _glfw.wl.keyRepeatRate = rate; + _glfw.wl.keyRepeatDelay = delay; +} +#endif + +static const struct wl_keyboard_listener keyboardListener = +{ + keyboardHandleKeymap, + keyboardHandleEnter, + keyboardHandleLeave, + keyboardHandleKey, + keyboardHandleModifiers, +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION + keyboardHandleRepeatInfo, +#endif +}; + +static void seatHandleCapabilities(void* userData, + struct wl_seat* seat, + enum wl_seat_capability caps) +{ + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) + { + _glfw.wl.pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) + { + wl_pointer_destroy(_glfw.wl.pointer); + _glfw.wl.pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) + { + _glfw.wl.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) + { + wl_keyboard_destroy(_glfw.wl.keyboard); + _glfw.wl.keyboard = NULL; + } +} + +static void seatHandleName(void* userData, + struct wl_seat* seat, + const char* name) +{ +} + +static const struct wl_seat_listener seatListener = +{ + seatHandleCapabilities, + seatHandleName, +}; + +static void dataOfferHandleOffer(void* userData, + struct wl_data_offer* offer, + const char* mimeType) +{ + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + if (strcmp(mimeType, "text/plain;charset=utf-8") == 0) + _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; + else if (strcmp(mimeType, "text/uri-list") == 0) + _glfw.wl.offers[i].text_uri_list = GLFW_TRUE; + + break; + } + } +} + +static const struct wl_data_offer_listener dataOfferListener = +{ + dataOfferHandleOffer +}; + +static void dataDeviceHandleDataOffer(void* userData, + struct wl_data_device* device, + struct wl_data_offer* offer) +{ + _GLFWofferWayland* offers = + _glfw_realloc(_glfw.wl.offers, _glfw.wl.offerCount + 1); + if (!offers) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return; + } + + _glfw.wl.offers = offers; + _glfw.wl.offerCount++; + + _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer }; + wl_data_offer_add_listener(offer, &dataOfferListener, NULL); +} + +static void dataDeviceHandleEnter(void* userData, + struct wl_data_device* device, + uint32_t serial, + struct wl_surface* surface, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer* offer) +{ + if (_glfw.wl.dragOffer) + { + wl_data_offer_destroy(_glfw.wl.dragOffer); + _glfw.wl.dragOffer = NULL; + _glfw.wl.dragFocus = NULL; + } + + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + _GLFWwindow* window = NULL; + + if (surface) + window = wl_surface_get_user_data(surface); + + if (window && _glfw.wl.offers[i].text_uri_list) + { + _glfw.wl.dragOffer = offer; + _glfw.wl.dragFocus = window; + _glfw.wl.dragSerial = serial; + } + + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; + _glfw.wl.offerCount--; + break; + } + } + + if (_glfw.wl.dragOffer) + wl_data_offer_accept(offer, serial, "text/uri-list"); + else + { + wl_data_offer_accept(offer, serial, NULL); + wl_data_offer_destroy(offer); + } +} + +static void dataDeviceHandleLeave(void* userData, + struct wl_data_device* device) +{ + if (_glfw.wl.dragOffer) + { + wl_data_offer_destroy(_glfw.wl.dragOffer); + _glfw.wl.dragOffer = NULL; + _glfw.wl.dragFocus = NULL; + } +} + +static void dataDeviceHandleMotion(void* userData, + struct wl_data_device* device, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void dataDeviceHandleDrop(void* userData, + struct wl_data_device* device) +{ + if (!_glfw.wl.dragOffer) + return; + + char* string = readDataOfferAsString(_glfw.wl.dragOffer, "text/uri-list"); + if (string) + { + int count; + char** paths = _glfwParseUriList(string, &count); + if (paths) + _glfwInputDrop(_glfw.wl.dragFocus, count, (const char**) paths); + + for (int i = 0; i < count; i++) + _glfw_free(paths[i]); + + _glfw_free(paths); + } + + _glfw_free(string); +} + +static void dataDeviceHandleSelection(void* userData, + struct wl_data_device* device, + struct wl_data_offer* offer) +{ + if (_glfw.wl.selectionOffer) + { + wl_data_offer_destroy(_glfw.wl.selectionOffer); + _glfw.wl.selectionOffer = NULL; + } + + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + if (_glfw.wl.offers[i].text_plain_utf8) + _glfw.wl.selectionOffer = offer; + else + wl_data_offer_destroy(offer); + + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; + _glfw.wl.offerCount--; + break; + } + } +} + +const struct wl_data_device_listener dataDeviceListener = +{ + dataDeviceHandleDataOffer, + dataDeviceHandleEnter, + dataDeviceHandleLeave, + dataDeviceHandleMotion, + dataDeviceHandleDrop, + dataDeviceHandleSelection, +}; + +void _glfwAddSeatListenerWayland(struct wl_seat* seat) +{ + wl_seat_add_listener(seat, &seatListener, NULL); +} + +void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device) +{ + wl_data_device_add_listener(device, &dataDeviceListener, NULL); +} + + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) +GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) { - window->wl.transparent = fbconfig->transparent; - - if (!createSurface(window, wndconfig)) + if (!createNativeSurface(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -796,6 +1804,16 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (ctxconfig->source == GLFW_EGL_CONTEXT_API || ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { + window->wl.egl.window = wl_egl_window_create(window->wl.surface, + wndconfig->width, + wndconfig->height); + if (!window->wl.egl.window) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create EGL window"); + return GLFW_FALSE; + } + if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) @@ -808,93 +1826,80 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - } - if (wndconfig->title) - window->wl.title = _glfw_strdup(wndconfig->title); - - if (wndconfig->visible) - { - if (!createXdgSurface(window)) + if (!_glfwRefreshContextAttribs(window, ctxconfig)) return GLFW_FALSE; - - window->wl.visible = GLFW_TRUE; } - else + + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughWayland(window, GLFW_TRUE); + + if (window->monitor || wndconfig->visible) { - window->wl.xdg.surface = NULL; - window->wl.xdg.toplevel = NULL; - window->wl.visible = GLFW_FALSE; + if (!createShellObjects(window)) + return GLFW_FALSE; } - window->wl.currentCursor = NULL; - - window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*)); - window->wl.monitorsCount = 0; - window->wl.monitorsSize = 1; - return GLFW_TRUE; } -void _glfwPlatformDestroyWindow(_GLFWwindow* window) +void _glfwDestroyWindowWayland(_GLFWwindow* window) { if (window == _glfw.wl.pointerFocus) - { _glfw.wl.pointerFocus = NULL; - _glfwInputCursorEnter(window, GLFW_FALSE); - } + if (window == _glfw.wl.keyboardFocus) - { _glfw.wl.keyboardFocus = NULL; - _glfwInputWindowFocus(window, GLFW_FALSE); - } if (window->wl.idleInhibitor) zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); + if (window->wl.relativePointer) + zwp_relative_pointer_v1_destroy(window->wl.relativePointer); + + if (window->wl.lockedPointer) + zwp_locked_pointer_v1_destroy(window->wl.lockedPointer); + + if (window->wl.confinedPointer) + zwp_confined_pointer_v1_destroy(window->wl.confinedPointer); + if (window->context.destroy) window->context.destroy(window); - destroyDecorations(window); - if (window->wl.xdg.decoration) - zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); + destroyShellObjects(window); if (window->wl.decorations.buffer) wl_buffer_destroy(window->wl.decorations.buffer); - if (window->wl.native) - wl_egl_window_destroy(window->wl.native); - - if (window->wl.xdg.toplevel) - xdg_toplevel_destroy(window->wl.xdg.toplevel); - - if (window->wl.xdg.surface) - xdg_surface_destroy(window->wl.xdg.surface); + if (window->wl.egl.window) + wl_egl_window_destroy(window->wl.egl.window); if (window->wl.surface) wl_surface_destroy(window->wl.surface); - free(window->wl.title); - free(window->wl.monitors); + _glfw_free(window->wl.title); + _glfw_free(window->wl.appId); + _glfw_free(window->wl.monitors); } -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title) { - if (window->wl.title) - free(window->wl.title); - window->wl.title = _glfw_strdup(title); + char* copy = _glfw_strdup(title); + _glfw_free(window->wl.title); + window->wl.title = copy; + if (window->wl.xdg.toplevel) xdg_toplevel_set_title(window->wl.xdg.toplevel, title); } -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) +void _glfwSetWindowIconWayland(_GLFWwindow* window, + int count, const GLFWimage* images) { _glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Wayland: The platform does not support setting the window icon"); } -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos) { // A Wayland client is not aware of its position, so just warn and leave it // as (0, 0) @@ -903,7 +1908,7 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) "Wayland: The platform does not provide the window position"); } -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos) { // A Wayland client can not set its position, so just warn @@ -911,7 +1916,7 @@ void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) "Wayland: The platform does not support setting the window position"); } -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height) { if (width) *width = window->wl.width; @@ -919,67 +1924,100 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) *height = window->wl.height; } -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +void _glfwSetWindowSizeWayland(_GLFWwindow* window, int width, int height) { - window->wl.width = width; - window->wl.height = height; - resizeWindow(window); + if (window->monitor) + { + // Video mode setting is not available on Wayland + } + else + { + window->wl.width = width; + window->wl.height = height; + resizeWindow(window); + } } -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) +void _glfwSetWindowSizeLimitsWayland(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) { if (window->wl.xdg.toplevel) { if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) minwidth = minheight = 0; + else + { + if (window->wl.decorations.top.surface) + { + minwidth += GLFW_BORDER_SIZE * 2; + minheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE; + } + } + if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) maxwidth = maxheight = 0; + else + { + if (window->wl.decorations.top.surface) + { + maxwidth += GLFW_BORDER_SIZE * 2; + maxheight += GLFW_CAPTION_HEIGHT + GLFW_BORDER_SIZE; + } + } + xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); wl_surface_commit(window->wl.surface); } } -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, - int numer, int denom) +void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom) { - // TODO: find out how to trigger a resize. - // The actual limits are checked in the xdg_toplevel::configure handler. - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, - "Wayland: Window aspect ratio not yet implemented"); + if (window->wl.maximized || window->wl.fullscreen) + return; + + if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) + { + const float aspectRatio = (float) window->wl.width / (float) window->wl.height; + const float targetRatio = (float) numer / (float) denom; + if (aspectRatio < targetRatio) + window->wl.height = window->wl.width / targetRatio; + else if (aspectRatio > targetRatio) + window->wl.width = window->wl.height * targetRatio; + + resizeWindow(window); + } } -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, - int* width, int* height) +void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height) { - _glfwPlatformGetWindowSize(window, width, height); + _glfwGetWindowSizeWayland(window, width, height); if (width) *width *= window->wl.scale; if (height) *height *= window->wl.scale; } -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) +void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) { - if (window->decorated && !window->monitor && !window->wl.decorations.serverSide) + if (window->decorated && !window->monitor && window->wl.decorations.top.surface) { if (top) - *top = _GLFW_DECORATION_TOP; + *top = GLFW_CAPTION_HEIGHT; if (left) - *left = _GLFW_DECORATION_WIDTH; + *left = GLFW_BORDER_SIZE; if (right) - *right = _GLFW_DECORATION_WIDTH; + *right = GLFW_BORDER_SIZE; if (bottom) - *bottom = _GLFW_DECORATION_WIDTH; + *bottom = GLFW_BORDER_SIZE; } } -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) +void _glfwGetWindowContentScaleWayland(_GLFWwindow* window, + float* xscale, float* yscale) { if (xscale) *xscale = (float) window->wl.scale; @@ -987,149 +2025,169 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, *yscale = (float) window->wl.scale; } -void _glfwPlatformIconifyWindow(_GLFWwindow* window) +void _glfwIconifyWindowWayland(_GLFWwindow* window) { if (window->wl.xdg.toplevel) xdg_toplevel_set_minimized(window->wl.xdg.toplevel); } -void _glfwPlatformRestoreWindow(_GLFWwindow* window) +void _glfwRestoreWindowWayland(_GLFWwindow* window) { - if (window->wl.xdg.toplevel) + if (window->monitor) { - if (window->monitor) - xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); - if (window->wl.maximized) - xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); // There is no way to unset minimized, or even to know if we are // minimized, so there is nothing to do in this case. } - _glfwInputWindowMonitor(window, NULL); - window->wl.maximized = GLFW_FALSE; + else + { + // We assume we are not minimized and act only on maximization + + if (window->wl.maximized) + { + if (window->wl.xdg.toplevel) + xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); + else + window->wl.maximized = GLFW_FALSE; + } + } } -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +void _glfwMaximizeWindowWayland(_GLFWwindow* window) { if (window->wl.xdg.toplevel) - { xdg_toplevel_set_maximized(window->wl.xdg.toplevel); - } - window->wl.maximized = GLFW_TRUE; + else + window->wl.maximized = GLFW_TRUE; } -void _glfwPlatformShowWindow(_GLFWwindow* window) +void _glfwShowWindowWayland(_GLFWwindow* window) { - if (!window->wl.visible) + if (!window->wl.xdg.toplevel) { - createXdgSurface(window); - window->wl.visible = GLFW_TRUE; + // NOTE: The XDG surface and role are created here so command-line applications + // with off-screen windows do not appear in for example the Unity dock + createShellObjects(window); } } -void _glfwPlatformHideWindow(_GLFWwindow* window) +void _glfwHideWindowWayland(_GLFWwindow* window) { - if (window->wl.xdg.toplevel) + if (window->wl.visible) { - xdg_toplevel_destroy(window->wl.xdg.toplevel); - xdg_surface_destroy(window->wl.xdg.surface); - window->wl.xdg.toplevel = NULL; - window->wl.xdg.surface = NULL; + window->wl.visible = GLFW_FALSE; + destroyShellObjects(window); + + wl_surface_attach(window->wl.surface, NULL, 0, 0); + wl_surface_commit(window->wl.surface); } - window->wl.visible = GLFW_FALSE; } -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +void _glfwRequestWindowAttentionWayland(_GLFWwindow* window) { // TODO _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, "Wayland: Window attention request not implemented yet"); } -void _glfwPlatformFocusWindow(_GLFWwindow* window) +void _glfwFocusWindowWayland(_GLFWwindow* window) { _glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Wayland: The platform does not support setting the input focus"); } -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) +void _glfwSetWindowMonitorWayland(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) { - if (monitor) + if (window->monitor == monitor) { - setFullscreen(window, monitor, refreshRate); - } - else - { - if (window->wl.xdg.toplevel) - xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); - setIdleInhibitor(window, GLFW_FALSE); - if (!_glfw.wl.decorationManager) - createDecorations(window); + if (!monitor) + _glfwSetWindowSizeWayland(window, width, height); + + return; } + + if (window->monitor) + releaseMonitor(window); + _glfwInputWindowMonitor(window, monitor); + + if (window->monitor) + acquireMonitor(window); + else + _glfwSetWindowSizeWayland(window, width, height); } -int _glfwPlatformWindowFocused(_GLFWwindow* window) +GLFWbool _glfwWindowFocusedWayland(_GLFWwindow* window) { return _glfw.wl.keyboardFocus == window; } -int _glfwPlatformWindowIconified(_GLFWwindow* window) +GLFWbool _glfwWindowIconifiedWayland(_GLFWwindow* window) { // xdg-shell doesn’t give any way to request whether a surface is // iconified. return GLFW_FALSE; } -int _glfwPlatformWindowVisible(_GLFWwindow* window) +GLFWbool _glfwWindowVisibleWayland(_GLFWwindow* window) { return window->wl.visible; } -int _glfwPlatformWindowMaximized(_GLFWwindow* window) +GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window) { return window->wl.maximized; } -int _glfwPlatformWindowHovered(_GLFWwindow* window) +GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window) { return window->wl.hovered; } -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window) { return window->wl.transparent; } -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowResizableWayland(_GLFWwindow* window, GLFWbool enabled) { // TODO _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, "Wayland: Window attribute setting not implemented yet"); } -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled) { - if (!window->monitor) + if (window->wl.xdg.decoration) + { + uint32_t mode; + + if (enabled) + mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; + else + mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; + + zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode); + } + else { if (enabled) - createDecorations(window); + createFallbackDecorations(window); else - destroyDecorations(window); + destroyFallbackDecorations(window); } } -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowFloatingWayland(_GLFWwindow* window, GLFWbool enabled) { - // TODO - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, - "Wayland: Window attribute setting not implemented yet"); + _glfwInputError(GLFW_FEATURE_UNAVAILABLE, + "Wayland: Platform does not support making a window floating"); } -void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowMousePassthroughWayland(_GLFWwindow* window, GLFWbool enabled) { if (enabled) { @@ -1139,51 +2197,52 @@ void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enable } else wl_surface_set_input_region(window->wl.surface, 0); - wl_surface_commit(window->wl.surface); } -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +float _glfwGetWindowOpacityWayland(_GLFWwindow* window) { return 1.f; } -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +void _glfwSetWindowOpacityWayland(_GLFWwindow* window, float opacity) { _glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Wayland: The platform does not support setting the window opacity"); } -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +void _glfwSetRawMouseMotionWayland(_GLFWwindow* window, GLFWbool enabled) { // This is handled in relativePointerHandleRelativeMotion } -GLFWbool _glfwPlatformRawMouseMotionSupported(void) +GLFWbool _glfwRawMouseMotionSupportedWayland(void) { return GLFW_TRUE; } -void _glfwPlatformPollEvents(void) +void _glfwPollEventsWayland(void) { - handleEvents(0); + double timeout = 0.0; + handleEvents(&timeout); } -void _glfwPlatformWaitEvents(void) +void _glfwWaitEventsWayland(void) { - handleEvents(-1); + handleEvents(NULL); } -void _glfwPlatformWaitEventsTimeout(double timeout) +void _glfwWaitEventsTimeoutWayland(double timeout) { - handleEvents((int) (timeout * 1e3)); + handleEvents(&timeout); } -void _glfwPlatformPostEmptyEvent(void) +void _glfwPostEmptyEventWayland(void) { wl_display_sync(_glfw.wl.display); + flushDisplay(); } -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos) { if (xpos) *xpos = window->wl.cursorPosX; @@ -1191,40 +2250,80 @@ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) *ypos = window->wl.cursorPosY; } -static GLFWbool isPointerLocked(_GLFWwindow* window); - -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y) { - if (isPointerLocked(window)) + _glfwInputError(GLFW_FEATURE_UNAVAILABLE, + "Wayland: The platform does not support setting the cursor position"); +} + +void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode) +{ + _glfwSetCursorWayland(window, window->wl.currentCursor); +} + +const char* _glfwGetScancodeNameWayland(int scancode) +{ + if (scancode < 0 || scancode > 255 || + _glfw.wl.keycodes[scancode] == GLFW_KEY_UNKNOWN) { - zwp_locked_pointer_v1_set_cursor_position_hint( - window->wl.pointerLock.lockedPointer, - wl_fixed_from_double(x), wl_fixed_from_double(y)); - wl_surface_commit(window->wl.surface); + _glfwInputError(GLFW_INVALID_VALUE, + "Wayland: Invalid scancode %i", + scancode); + return NULL; } + + const int key = _glfw.wl.keycodes[scancode]; + const xkb_keycode_t keycode = scancode + 8; + const xkb_layout_index_t layout = + xkb_state_key_get_layout(_glfw.wl.xkb.state, keycode); + if (layout == XKB_LAYOUT_INVALID) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to retrieve layout for key name"); + return NULL; + } + + const xkb_keysym_t* keysyms = NULL; + xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap, + keycode, + layout, + 0, + &keysyms); + if (keysyms == NULL) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to retrieve keysym for key name"); + return NULL; + } + + const uint32_t codepoint = _glfwKeySym2Unicode(keysyms[0]); + if (codepoint == GLFW_INVALID_CODEPOINT) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to retrieve codepoint for key name"); + return NULL; + } + + const size_t count = _glfwEncodeUTF8(_glfw.wl.keynames[key], codepoint); + if (count == 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to encode codepoint for key name"); + return NULL; + } + + _glfw.wl.keynames[key][count] = '\0'; + return _glfw.wl.keynames[key]; } -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) -{ - _glfwPlatformSetCursor(window, window->wl.currentCursor); -} - -const char* _glfwPlatformGetScancodeName(int scancode) -{ - // TODO - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, - "Wayland: Key names not yet implemented"); - return NULL; -} - -int _glfwPlatformGetKeyScancode(int key) +int _glfwGetKeyScancodeWayland(int key) { return _glfw.wl.scancodes[key]; } -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) +GLFWbool _glfwCreateCursorWayland(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) { cursor->wl.buffer = createShmBuffer(image); if (!cursor->wl.buffer) @@ -1237,7 +2336,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, return GLFW_TRUE; } -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +GLFWbool _glfwCreateStandardCursorWayland(_GLFWcursor* cursor, int shape) { const char* name = NULL; @@ -1291,18 +2390,25 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { case GLFW_ARROW_CURSOR: name = "left_ptr"; + break; case GLFW_IBEAM_CURSOR: name = "xterm"; + break; case GLFW_CROSSHAIR_CURSOR: name = "crosshair"; + break; case GLFW_POINTING_HAND_CURSOR: name = "hand2"; + break; case GLFW_RESIZE_EW_CURSOR: name = "sb_h_double_arrow"; + break; case GLFW_RESIZE_NS_CURSOR: name = "sb_v_double_arrow"; + break; case GLFW_RESIZE_ALL_CURSOR: name = "fleur"; + break; default: _glfwInputError(GLFW_CURSOR_UNAVAILABLE, "Wayland: Standard cursor shape unavailable"); @@ -1312,7 +2418,7 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name); if (!cursor->wl.cursor) { - _glfwInputError(GLFW_PLATFORM_ERROR, + _glfwInputError(GLFW_CURSOR_UNAVAILABLE, "Wayland: Failed to create standard cursor \"%s\"", name); return GLFW_FALSE; @@ -1331,7 +2437,7 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) return GLFW_TRUE; } -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +void _glfwDestroyCursorWayland(_GLFWcursor* cursor) { // If it's a standard cursor we don't need to do anything here if (cursor->wl.cursor) @@ -1341,7 +2447,7 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) wl_buffer_destroy(cursor->wl.buffer); } -static void relativePointerHandleRelativeMotion(void* data, +static void relativePointerHandleRelativeMotion(void* userData, struct zwp_relative_pointer_v1* pointer, uint32_t timeHi, uint32_t timeLo, @@ -1350,7 +2456,7 @@ static void relativePointerHandleRelativeMotion(void* data, wl_fixed_t dxUnaccel, wl_fixed_t dyUnaccel) { - _GLFWwindow* window = data; + _GLFWwindow* window = userData; double xpos = window->virtualCursorPosX; double ypos = window->virtualCursorPosY; @@ -1371,89 +2477,104 @@ static void relativePointerHandleRelativeMotion(void* data, _glfwInputCursorPos(window, xpos, ypos); } -static const struct zwp_relative_pointer_v1_listener relativePointerListener = { +static const struct zwp_relative_pointer_v1_listener relativePointerListener = +{ relativePointerHandleRelativeMotion }; -static void lockedPointerHandleLocked(void* data, +static void lockedPointerHandleLocked(void* userData, struct zwp_locked_pointer_v1* lockedPointer) { } -static void unlockPointer(_GLFWwindow* window) -{ - struct zwp_relative_pointer_v1* relativePointer = - window->wl.pointerLock.relativePointer; - struct zwp_locked_pointer_v1* lockedPointer = - window->wl.pointerLock.lockedPointer; - - zwp_relative_pointer_v1_destroy(relativePointer); - zwp_locked_pointer_v1_destroy(lockedPointer); - - window->wl.pointerLock.relativePointer = NULL; - window->wl.pointerLock.lockedPointer = NULL; -} - -static void lockPointer(_GLFWwindow* window); - -static void lockedPointerHandleUnlocked(void* data, +static void lockedPointerHandleUnlocked(void* userData, struct zwp_locked_pointer_v1* lockedPointer) { } -static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { +static const struct zwp_locked_pointer_v1_listener lockedPointerListener = +{ lockedPointerHandleLocked, lockedPointerHandleUnlocked }; static void lockPointer(_GLFWwindow* window) { - struct zwp_relative_pointer_v1* relativePointer; - struct zwp_locked_pointer_v1* lockedPointer; - if (!_glfw.wl.relativePointerManager) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: no relative pointer manager"); + _glfwInputError(GLFW_FEATURE_UNAVAILABLE, + "Wayland: The compositor does not support pointer locking"); return; } - relativePointer = + window->wl.relativePointer = zwp_relative_pointer_manager_v1_get_relative_pointer( _glfw.wl.relativePointerManager, _glfw.wl.pointer); - zwp_relative_pointer_v1_add_listener(relativePointer, + zwp_relative_pointer_v1_add_listener(window->wl.relativePointer, &relativePointerListener, window); - lockedPointer = + window->wl.lockedPointer = zwp_pointer_constraints_v1_lock_pointer( _glfw.wl.pointerConstraints, window->wl.surface, _glfw.wl.pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); - zwp_locked_pointer_v1_add_listener(lockedPointer, + zwp_locked_pointer_v1_add_listener(window->wl.lockedPointer, &lockedPointerListener, window); - - window->wl.pointerLock.relativePointer = relativePointer; - window->wl.pointerLock.lockedPointer = lockedPointer; - - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, - NULL, 0, 0); } -static GLFWbool isPointerLocked(_GLFWwindow* window) +static void unlockPointer(_GLFWwindow* window) { - return window->wl.pointerLock.lockedPointer != NULL; + zwp_relative_pointer_v1_destroy(window->wl.relativePointer); + window->wl.relativePointer = NULL; + + zwp_locked_pointer_v1_destroy(window->wl.lockedPointer); + window->wl.lockedPointer = NULL; } -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +static void confinedPointerHandleConfined(void* userData, + struct zwp_confined_pointer_v1* confinedPointer) { - struct wl_cursor* defaultCursor; - struct wl_cursor* defaultCursorHiDPI = NULL; +} +static void confinedPointerHandleUnconfined(void* userData, + struct zwp_confined_pointer_v1* confinedPointer) +{ +} + +static const struct zwp_confined_pointer_v1_listener confinedPointerListener = +{ + confinedPointerHandleConfined, + confinedPointerHandleUnconfined +}; + +static void confinePointer(_GLFWwindow* window) +{ + window->wl.confinedPointer = + zwp_pointer_constraints_v1_confine_pointer( + _glfw.wl.pointerConstraints, + window->wl.surface, + _glfw.wl.pointer, + NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + + zwp_confined_pointer_v1_add_listener(window->wl.confinedPointer, + &confinedPointerListener, + window); +} + +static void unconfinePointer(_GLFWwindow* window) +{ + zwp_confined_pointer_v1_destroy(window->wl.confinedPointer); + window->wl.confinedPointer = NULL; +} + +void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor) +{ if (!_glfw.wl.pointer) return; @@ -1464,29 +2585,55 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) return; - // Unlock possible pointer lock if no longer disabled. - if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) - unlockPointer(window); + // Update pointer lock to match cursor mode + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (window->wl.confinedPointer) + unconfinePointer(window); + if (!window->wl.lockedPointer) + lockPointer(window); + } + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + { + if (window->wl.lockedPointer) + unlockPointer(window); + if (!window->wl.confinedPointer) + confinePointer(window); + } + else if (window->cursorMode == GLFW_CURSOR_NORMAL || + window->cursorMode == GLFW_CURSOR_HIDDEN) + { + if (window->wl.lockedPointer) + unlockPointer(window); + else if (window->wl.confinedPointer) + unconfinePointer(window); + } - if (window->cursorMode == GLFW_CURSOR_NORMAL) + if (window->cursorMode == GLFW_CURSOR_NORMAL || + window->cursorMode == GLFW_CURSOR_CAPTURED) { if (cursor) setCursorImage(window, &cursor->wl); else { - defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, - "left_ptr"); + struct wl_cursor* defaultCursor = + wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, "left_ptr"); if (!defaultCursor) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Standard cursor not found"); return; } + + struct wl_cursor* defaultCursorHiDPI = NULL; if (_glfw.wl.cursorThemeHiDPI) + { defaultCursorHiDPI = - wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, - "left_ptr"); - _GLFWcursorWayland cursorWayland = { + wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, "left_ptr"); + } + + _GLFWcursorWayland cursorWayland = + { defaultCursor, defaultCursorHiDPI, NULL, @@ -1494,25 +2641,22 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) 0, 0, 0 }; + setCursorImage(window, &cursorWayland); } } - else if (window->cursorMode == GLFW_CURSOR_DISABLED) + else if (window->cursorMode == GLFW_CURSOR_HIDDEN || + window->cursorMode == GLFW_CURSOR_DISABLED) { - if (!isPointerLocked(window)) - lockPointer(window); - } - else if (window->cursorMode == GLFW_CURSOR_HIDDEN) - { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0); } } -static void dataSourceHandleTarget(void* data, - struct wl_data_source* dataSource, +static void dataSourceHandleTarget(void* userData, + struct wl_data_source* source, const char* mimeType) { - if (_glfw.wl.dataSource != dataSource) + if (_glfw.wl.selectionSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); @@ -1520,200 +2664,115 @@ static void dataSourceHandleTarget(void* data, } } -static void dataSourceHandleSend(void* data, - struct wl_data_source* dataSource, +static void dataSourceHandleSend(void* userData, + struct wl_data_source* source, const char* mimeType, int fd) { - const char* string = _glfw.wl.clipboardSendString; - size_t len = _glfw.wl.clipboardSendSize; - int ret; - - if (_glfw.wl.dataSource != dataSource) + // Ignore it if this is an outdated or invalid request + if (_glfw.wl.selectionSource != source || + strcmp(mimeType, "text/plain;charset=utf-8") != 0) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unknown clipboard data source"); - return; - } - - if (!string) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Copy requested from an invalid string"); - return; - } - - if (strcmp(mimeType, "text/plain;charset=utf-8") != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Wrong MIME type asked from clipboard"); close(fd); return; } - while (len > 0) + char* string = _glfw.wl.clipboardString; + size_t length = strlen(string); + + while (length > 0) { - ret = write(fd, string, len); - if (ret == -1 && errno == EINTR) - continue; - if (ret == -1) + const ssize_t result = write(fd, string, length); + if (result == -1) { - // TODO: also report errno maybe. + if (errno == EINTR) + continue; + _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Error while writing the clipboard"); - close(fd); - return; + "Wayland: Error while writing the clipboard: %s", + strerror(errno)); + break; } - len -= ret; + + length -= result; + string += result; } + close(fd); } -static void dataSourceHandleCancelled(void* data, - struct wl_data_source* dataSource) +static void dataSourceHandleCancelled(void* userData, + struct wl_data_source* source) { - wl_data_source_destroy(dataSource); + wl_data_source_destroy(source); - if (_glfw.wl.dataSource != dataSource) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Unknown clipboard data source"); + if (_glfw.wl.selectionSource != source) return; - } - _glfw.wl.dataSource = NULL; + _glfw.wl.selectionSource = NULL; } -static const struct wl_data_source_listener dataSourceListener = { +static const struct wl_data_source_listener dataSourceListener = +{ dataSourceHandleTarget, dataSourceHandleSend, dataSourceHandleCancelled, }; -void _glfwPlatformSetClipboardString(const char* string) +void _glfwSetClipboardStringWayland(const char* string) { - if (_glfw.wl.dataSource) + if (_glfw.wl.selectionSource) { - wl_data_source_destroy(_glfw.wl.dataSource); - _glfw.wl.dataSource = NULL; + wl_data_source_destroy(_glfw.wl.selectionSource); + _glfw.wl.selectionSource = NULL; } - if (_glfw.wl.clipboardSendString) + char* copy = _glfw_strdup(string); + if (!copy) { - free(_glfw.wl.clipboardSendString); - _glfw.wl.clipboardSendString = NULL; - } - - _glfw.wl.clipboardSendString = strdup(string); - if (!_glfw.wl.clipboardSendString) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to allocate clipboard string"); + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); return; } - _glfw.wl.clipboardSendSize = strlen(string); - _glfw.wl.dataSource = + + _glfw_free(_glfw.wl.clipboardString); + _glfw.wl.clipboardString = copy; + + _glfw.wl.selectionSource = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); - if (!_glfw.wl.dataSource) + if (!_glfw.wl.selectionSource) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to create clipboard source"); - free(_glfw.wl.clipboardSendString); + "Wayland: Failed to create clipboard data source"); return; } - wl_data_source_add_listener(_glfw.wl.dataSource, + wl_data_source_add_listener(_glfw.wl.selectionSource, &dataSourceListener, NULL); - wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8"); + wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8"); wl_data_device_set_selection(_glfw.wl.dataDevice, - _glfw.wl.dataSource, + _glfw.wl.selectionSource, _glfw.wl.serial); } -static GLFWbool growClipboardString(void) +const char* _glfwGetClipboardStringWayland(void) { - char* clipboard = _glfw.wl.clipboardString; - - clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2); - if (!clipboard) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to grow clipboard string"); - return GLFW_FALSE; - } - _glfw.wl.clipboardString = clipboard; - _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2; - return GLFW_TRUE; -} - -const char* _glfwPlatformGetClipboardString(void) -{ - int fds[2]; - int ret; - size_t len = 0; - - if (!_glfw.wl.dataOffer) + if (!_glfw.wl.selectionOffer) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "No clipboard data has been sent yet"); + "Wayland: No clipboard data available"); return NULL; } - ret = pipe2(fds, O_CLOEXEC); - if (ret < 0) - { - // TODO: also report errno maybe? - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to create clipboard pipe fds"); - return NULL; - } + if (_glfw.wl.selectionSource) + return _glfw.wl.clipboardString; - wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); - close(fds[1]); - - // XXX: this is a huge hack, this function shouldn’t be synchronous! - handleEvents(-1); - - while (1) - { - // Grow the clipboard if we need to paste something bigger, there is no - // shrink operation yet. - if (len + 4096 > _glfw.wl.clipboardSize) - { - if (!growClipboardString()) - { - close(fds[0]); - return NULL; - } - } - - // Then read from the fd to the clipboard, handling all known errors. - ret = read(fds[0], _glfw.wl.clipboardString + len, 4096); - if (ret == 0) - break; - if (ret == -1 && errno == EINTR) - continue; - if (ret == -1) - { - // TODO: also report errno maybe. - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Impossible to read from clipboard fd"); - close(fds[0]); - return NULL; - } - len += ret; - } - close(fds[0]); - if (len + 1 > _glfw.wl.clipboardSize) - { - if (!growClipboardString()) - return NULL; - } - _glfw.wl.clipboardString[len] = '\0'; + _glfw_free(_glfw.wl.clipboardString); + _glfw.wl.clipboardString = + readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8"); return _glfw.wl.clipboardString; } -EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) +EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs) { if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland) return EGL_PLATFORM_WAYLAND_EXT; @@ -1721,17 +2780,17 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) return 0; } -EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void) +EGLNativeDisplayType _glfwGetEGLNativeDisplayWayland(void) { return _glfw.wl.display; } -EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window) +EGLNativeWindowType _glfwGetEGLNativeWindowWayland(_GLFWwindow* window) { - return window->wl.native; + return window->wl.egl.window; } -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +void _glfwGetRequiredInstanceExtensionsWayland(char** extensions) { if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) return; @@ -1740,9 +2799,9 @@ void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) extensions[1] = "VK_KHR_wayland_surface"; } -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) +GLFWbool _glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) { PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = @@ -1760,10 +2819,10 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, _glfw.wl.display); } -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) +VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) { VkResult err; VkWaylandSurfaceCreateInfoKHR sci; @@ -1802,6 +2861,14 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, GLFWAPI struct wl_display* glfwGetWaylandDisplay(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, + "Wayland: Platform not initialized"); + return NULL; + } + return _glfw.wl.display; } @@ -1809,6 +2876,14 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (_glfw.platform.platformID != GLFW_PLATFORM_WAYLAND) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, + "Wayland: Platform not initialized"); + return NULL; + } + return window->wl.surface; } diff --git a/src/external/glfw/src/x11_init.c b/src/external/glfw/src/x11_init.c index fc9ac427b..11aeb9e52 100644 --- a/src/external/glfw/src/x11_init.c +++ b/src/external/glfw/src/x11_init.c @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include // Translate the X11 KeySyms for a key to a GLFW key code @@ -209,7 +212,7 @@ static int translateKeySyms(const KeySym* keysyms, int width) // static void createKeyTables(void) { - int scancode, scancodeMin, scancodeMax; + int scancodeMin, scancodeMax; memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); @@ -355,7 +358,7 @@ static void createKeyTables(void) }; // Find the X11 key code -> GLFW key code mapping - for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) + for (int scancode = scancodeMin; scancode <= scancodeMax; scancode++) { int key = GLFW_KEY_UNKNOWN; @@ -414,7 +417,7 @@ static void createKeyTables(void) scancodeMax - scancodeMin + 1, &width); - for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) + for (int scancode = scancodeMin; scancode <= scancodeMax; scancode++) { // Translate the un-translated key codes using traditional X11 KeySym // lookups @@ -601,17 +604,21 @@ static void detectEWMH(void) // static GLFWbool initExtensions(void) { - _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so.1"); +#if defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.x11.vidmode.handle = _glfwPlatformLoadModule("libXxf86vm.so"); +#else + _glfw.x11.vidmode.handle = _glfwPlatformLoadModule("libXxf86vm.so.1"); +#endif if (_glfw.x11.vidmode.handle) { _glfw.x11.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension) - _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension"); + _glfwPlatformGetModuleSymbol(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension"); _glfw.x11.vidmode.GetGammaRamp = (PFN_XF86VidModeGetGammaRamp) - _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp"); + _glfwPlatformGetModuleSymbol(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp"); _glfw.x11.vidmode.SetGammaRamp = (PFN_XF86VidModeSetGammaRamp) - _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp"); + _glfwPlatformGetModuleSymbol(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp"); _glfw.x11.vidmode.GetGammaRampSize = (PFN_XF86VidModeGetGammaRampSize) - _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize"); + _glfwPlatformGetModuleSymbol(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize"); _glfw.x11.vidmode.available = XF86VidModeQueryExtension(_glfw.x11.display, @@ -620,16 +627,18 @@ static GLFWbool initExtensions(void) } #if defined(__CYGWIN__) - _glfw.x11.xi.handle = _glfw_dlopen("libXi-6.so"); + _glfw.x11.xi.handle = _glfwPlatformLoadModule("libXi-6.so"); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.x11.xi.handle = _glfwPlatformLoadModule("libXi.so"); #else - _glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6"); + _glfw.x11.xi.handle = _glfwPlatformLoadModule("libXi.so.6"); #endif if (_glfw.x11.xi.handle) { _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) - _glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xi.handle, "XIQueryVersion"); _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) - _glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xi.handle, "XISelectEvents"); if (XQueryExtension(_glfw.x11.display, "XInputExtension", @@ -650,48 +659,50 @@ static GLFWbool initExtensions(void) } #if defined(__CYGWIN__) - _glfw.x11.randr.handle = _glfw_dlopen("libXrandr-2.so"); + _glfw.x11.randr.handle = _glfwPlatformLoadModule("libXrandr-2.so"); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.x11.randr.handle = _glfwPlatformLoadModule("libXrandr.so"); #else - _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2"); + _glfw.x11.randr.handle = _glfwPlatformLoadModule("libXrandr.so.2"); #endif if (_glfw.x11.randr.handle) { _glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRAllocGamma"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRAllocGamma"); _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRFreeGamma"); _glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRFreeCrtcInfo"); _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRFreeGamma"); _glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRFreeOutputInfo"); _glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRFreeScreenResources"); _glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRGetCrtcGamma"); _glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize"); _glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRGetCrtcInfo"); _glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRGetOutputInfo"); _glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRGetOutputPrimary"); _glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent"); _glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryExtension"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRQueryExtension"); _glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryVersion"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRQueryVersion"); _glfw.x11.randr.SelectInput = (PFN_XRRSelectInput) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRSelectInput"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRSelectInput"); _glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRSetCrtcConfig"); _glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRSetCrtcGamma"); _glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration) - _glfw_dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration"); + _glfwPlatformGetModuleSymbol(_glfw.x11.randr.handle, "XRRUpdateConfiguration"); if (XRRQueryExtension(_glfw.x11.display, &_glfw.x11.randr.eventBase, @@ -742,39 +753,43 @@ static GLFWbool initExtensions(void) } #if defined(__CYGWIN__) - _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so"); + _glfw.x11.xcursor.handle = _glfwPlatformLoadModule("libXcursor-1.so"); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.x11.xcursor.handle = _glfwPlatformLoadModule("libXcursor.so"); #else - _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1"); + _glfw.x11.xcursor.handle = _glfwPlatformLoadModule("libXcursor.so.1"); #endif if (_glfw.x11.xcursor.handle) { _glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xcursor.handle, "XcursorImageCreate"); _glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); _glfw.x11.xcursor.GetTheme = (PFN_XcursorGetTheme) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetTheme"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xcursor.handle, "XcursorGetTheme"); _glfw.x11.xcursor.GetDefaultSize = (PFN_XcursorGetDefaultSize) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetDefaultSize"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xcursor.handle, "XcursorGetDefaultSize"); _glfw.x11.xcursor.LibraryLoadImage = (PFN_XcursorLibraryLoadImage) - _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorLibraryLoadImage"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xcursor.handle, "XcursorLibraryLoadImage"); } #if defined(__CYGWIN__) - _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama-1.so"); + _glfw.x11.xinerama.handle = _glfwPlatformLoadModule("libXinerama-1.so"); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.x11.xinerama.handle = _glfwPlatformLoadModule("libXinerama.so"); #else - _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1"); + _glfw.x11.xinerama.handle = _glfwPlatformLoadModule("libXinerama.so.1"); #endif if (_glfw.x11.xinerama.handle) { _glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive) - _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xinerama.handle, "XineramaIsActive"); _glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension) - _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xinerama.handle, "XineramaQueryExtension"); _glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens) - _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xinerama.handle, "XineramaQueryScreens"); if (XineramaQueryExtension(_glfw.x11.display, &_glfw.x11.xinerama.major, @@ -816,31 +831,35 @@ static GLFWbool initExtensions(void) if (_glfw.hints.init.x11.xcbVulkanSurface) { #if defined(__CYGWIN__) - _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb-1.so"); + _glfw.x11.x11xcb.handle = _glfwPlatformLoadModule("libX11-xcb-1.so"); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.x11.x11xcb.handle = _glfwPlatformLoadModule("libX11-xcb.so"); #else - _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1"); + _glfw.x11.x11xcb.handle = _glfwPlatformLoadModule("libX11-xcb.so.1"); #endif } if (_glfw.x11.x11xcb.handle) { _glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection) - _glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); + _glfwPlatformGetModuleSymbol(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); } #if defined(__CYGWIN__) - _glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so"); + _glfw.x11.xrender.handle = _glfwPlatformLoadModule("libXrender-1.so"); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.x11.xrender.handle = _glfwPlatformLoadModule("libXrender.so"); #else - _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1"); + _glfw.x11.xrender.handle = _glfwPlatformLoadModule("libXrender.so.1"); #endif if (_glfw.x11.xrender.handle) { _glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension) - _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xrender.handle, "XRenderQueryExtension"); _glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion) - _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xrender.handle, "XRenderQueryVersion"); _glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) - _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); if (XRenderQueryExtension(_glfw.x11.display, &_glfw.x11.xrender.errorBase, @@ -856,20 +875,22 @@ static GLFWbool initExtensions(void) } #if defined(__CYGWIN__) - _glfw.x11.xshape.handle = _glfw_dlopen("libXext-6.so"); + _glfw.x11.xshape.handle = _glfwPlatformLoadModule("libXext-6.so"); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + _glfw.x11.xshape.handle = _glfwPlatformLoadModule("libXext.so"); #else - _glfw.x11.xshape.handle = _glfw_dlopen("libXext.so.6"); + _glfw.x11.xshape.handle = _glfwPlatformLoadModule("libXext.so.6"); #endif if (_glfw.x11.xshape.handle) { _glfw.x11.xshape.QueryExtension = (PFN_XShapeQueryExtension) - _glfw_dlsym(_glfw.x11.xshape.handle, "XShapeQueryExtension"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xshape.handle, "XShapeQueryExtension"); _glfw.x11.xshape.ShapeCombineRegion = (PFN_XShapeCombineRegion) - _glfw_dlsym(_glfw.x11.xshape.handle, "XShapeCombineRegion"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xshape.handle, "XShapeCombineRegion"); _glfw.x11.xshape.QueryVersion = (PFN_XShapeQueryVersion) - _glfw_dlsym(_glfw.x11.xshape.handle, "XShapeQueryVersion"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xshape.handle, "XShapeQueryVersion"); _glfw.x11.xshape.ShapeCombineMask = (PFN_XShapeCombineMask) - _glfw_dlsym(_glfw.x11.xshape.handle, "XShapeCombineMask"); + _glfwPlatformGetModuleSymbol(_glfw.x11.xshape.handle, "XShapeCombineMask"); if (XShapeQueryExtension(_glfw.x11.display, &_glfw.x11.xshape.errorBase, @@ -1007,7 +1028,7 @@ static Cursor createHiddenCursor(void) { unsigned char pixels[16 * 16 * 4] = { 0 }; GLFWimage image = { 16, 16, pixels }; - return _glfwCreateCursorX11(&image, 0, 0); + return _glfwCreateNativeCursorX11(&image, 0, 0); } // Create a helper window for IPC @@ -1024,6 +1045,37 @@ static Window createHelperWindow(void) CWEventMask, &wa); } +// Create the pipe for empty events without assumuing the OS has pipe2(2) +// +static GLFWbool createEmptyEventPipe(void) +{ + if (pipe(_glfw.x11.emptyEventPipe) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create empty event pipe: %s", + strerror(errno)); + return GLFW_FALSE; + } + + for (int i = 0; i < 2; i++) + { + const int sf = fcntl(_glfw.x11.emptyEventPipe[i], F_GETFL, 0); + const int df = fcntl(_glfw.x11.emptyEventPipe[i], F_GETFD, 0); + + if (sf == -1 || df == -1 || + fcntl(_glfw.x11.emptyEventPipe[i], F_SETFL, sf | O_NONBLOCK) == -1 || + fcntl(_glfw.x11.emptyEventPipe[i], F_SETFD, df | FD_CLOEXEC) == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to set flags for empty event pipe: %s", + strerror(errno)); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + // X error handler // static int errorHandler(Display *display, XErrorEvent* event) @@ -1044,8 +1096,9 @@ static int errorHandler(Display *display, XErrorEvent* event) // void _glfwGrabErrorHandlerX11(void) { + assert(_glfw.x11.errorHandler == NULL); _glfw.x11.errorCode = Success; - XSetErrorHandler(errorHandler); + _glfw.x11.errorHandler = XSetErrorHandler(errorHandler); } // Clears the X error handler callback @@ -1054,7 +1107,8 @@ void _glfwReleaseErrorHandlerX11(void) { // Synchronize to make sure all commands are processed XSync(_glfw.x11.display, False); - XSetErrorHandler(NULL); + XSetErrorHandler(_glfw.x11.errorHandler); + _glfw.x11.errorHandler = NULL; } // Reports the specified error, appending information about the last X error @@ -1070,9 +1124,8 @@ void _glfwInputErrorX11(int error, const char* message) // Creates a native cursor object from the specified image and hotspot // -Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) +Cursor _glfwCreateNativeCursorX11(const GLFWimage* image, int xhot, int yhot) { - int i; Cursor cursor; if (!_glfw.x11.xcursor.handle) @@ -1088,7 +1141,7 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) unsigned char* source = (unsigned char*) image->pixels; XcursorPixel* target = native->pixels; - for (i = 0; i < image->width * image->height; i++, target++, source += 4) + for (int i = 0; i < image->width * image->height; i++, target++, source += 4) { unsigned int alpha = source[3]; @@ -1109,8 +1162,92 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformInit(void) +GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) { + const _GLFWplatform x11 = + { + GLFW_PLATFORM_X11, + _glfwInitX11, + _glfwTerminateX11, + _glfwGetCursorPosX11, + _glfwSetCursorPosX11, + _glfwSetCursorModeX11, + _glfwSetRawMouseMotionX11, + _glfwRawMouseMotionSupportedX11, + _glfwCreateCursorX11, + _glfwCreateStandardCursorX11, + _glfwDestroyCursorX11, + _glfwSetCursorX11, + _glfwGetScancodeNameX11, + _glfwGetKeyScancodeX11, + _glfwSetClipboardStringX11, + _glfwGetClipboardStringX11, +#if defined(__linux__) + _glfwInitJoysticksLinux, + _glfwTerminateJoysticksLinux, + _glfwPollJoystickLinux, + _glfwGetMappingNameLinux, + _glfwUpdateGamepadGUIDLinux, +#else + _glfwInitJoysticksNull, + _glfwTerminateJoysticksNull, + _glfwPollJoystickNull, + _glfwGetMappingNameNull, + _glfwUpdateGamepadGUIDNull, +#endif + _glfwFreeMonitorX11, + _glfwGetMonitorPosX11, + _glfwGetMonitorContentScaleX11, + _glfwGetMonitorWorkareaX11, + _glfwGetVideoModesX11, + _glfwGetVideoModeX11, + _glfwGetGammaRampX11, + _glfwSetGammaRampX11, + _glfwCreateWindowX11, + _glfwDestroyWindowX11, + _glfwSetWindowTitleX11, + _glfwSetWindowIconX11, + _glfwGetWindowPosX11, + _glfwSetWindowPosX11, + _glfwGetWindowSizeX11, + _glfwSetWindowSizeX11, + _glfwSetWindowSizeLimitsX11, + _glfwSetWindowAspectRatioX11, + _glfwGetFramebufferSizeX11, + _glfwGetWindowFrameSizeX11, + _glfwGetWindowContentScaleX11, + _glfwIconifyWindowX11, + _glfwRestoreWindowX11, + _glfwMaximizeWindowX11, + _glfwShowWindowX11, + _glfwHideWindowX11, + _glfwRequestWindowAttentionX11, + _glfwFocusWindowX11, + _glfwSetWindowMonitorX11, + _glfwWindowFocusedX11, + _glfwWindowIconifiedX11, + _glfwWindowVisibleX11, + _glfwWindowMaximizedX11, + _glfwWindowHoveredX11, + _glfwFramebufferTransparentX11, + _glfwGetWindowOpacityX11, + _glfwSetWindowResizableX11, + _glfwSetWindowDecoratedX11, + _glfwSetWindowFloatingX11, + _glfwSetWindowOpacityX11, + _glfwSetWindowMousePassthroughX11, + _glfwPollEventsX11, + _glfwWaitEventsX11, + _glfwWaitEventsTimeoutX11, + _glfwPostEmptyEventX11, + _glfwGetEGLPlatformX11, + _glfwGetEGLNativeDisplayX11, + _glfwGetEGLNativeWindowX11, + _glfwGetRequiredInstanceExtensionsX11, + _glfwGetPhysicalDevicePresentationSupportX11, + _glfwCreateWindowSurfaceX11, + }; + // HACK: If the application has left the locale as "C" then both wide // character text input and explicit UTF-8 input via XIM will break // This sets the CTYPE part of the current locale from the environment @@ -1119,251 +1256,280 @@ int _glfwPlatformInit(void) setlocale(LC_CTYPE, ""); #if defined(__CYGWIN__) - _glfw.x11.xlib.handle = _glfw_dlopen("libX11-6.so"); + void* module = _glfwPlatformLoadModule("libX11-6.so"); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + void* module = _glfwPlatformLoadModule("libX11.so"); #else - _glfw.x11.xlib.handle = _glfw_dlopen("libX11.so.6"); + void* module = _glfwPlatformLoadModule("libX11.so.6"); #endif - if (!_glfw.x11.xlib.handle) + if (!module) { - _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to load Xlib"); + if (platformID == GLFW_PLATFORM_X11) + _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to load Xlib"); + return GLFW_FALSE; } - _glfw.x11.xlib.AllocClassHint = (PFN_XAllocClassHint) - _glfw_dlsym(_glfw.x11.xlib.handle, "XAllocClassHint"); - _glfw.x11.xlib.AllocSizeHints = (PFN_XAllocSizeHints) - _glfw_dlsym(_glfw.x11.xlib.handle, "XAllocSizeHints"); - _glfw.x11.xlib.AllocWMHints = (PFN_XAllocWMHints) - _glfw_dlsym(_glfw.x11.xlib.handle, "XAllocWMHints"); - _glfw.x11.xlib.ChangeProperty = (PFN_XChangeProperty) - _glfw_dlsym(_glfw.x11.xlib.handle, "XChangeProperty"); - _glfw.x11.xlib.ChangeWindowAttributes = (PFN_XChangeWindowAttributes) - _glfw_dlsym(_glfw.x11.xlib.handle, "XChangeWindowAttributes"); - _glfw.x11.xlib.CheckIfEvent = (PFN_XCheckIfEvent) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCheckIfEvent"); - _glfw.x11.xlib.CheckTypedWindowEvent = (PFN_XCheckTypedWindowEvent) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCheckTypedWindowEvent"); - _glfw.x11.xlib.CloseDisplay = (PFN_XCloseDisplay) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCloseDisplay"); - _glfw.x11.xlib.CloseIM = (PFN_XCloseIM) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCloseIM"); - _glfw.x11.xlib.ConvertSelection = (PFN_XConvertSelection) - _glfw_dlsym(_glfw.x11.xlib.handle, "XConvertSelection"); - _glfw.x11.xlib.CreateColormap = (PFN_XCreateColormap) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateColormap"); - _glfw.x11.xlib.CreateFontCursor = (PFN_XCreateFontCursor) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateFontCursor"); - _glfw.x11.xlib.CreateIC = (PFN_XCreateIC) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateIC"); - _glfw.x11.xlib.CreateRegion = (PFN_XCreateRegion) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateRegion"); - _glfw.x11.xlib.CreateWindow = (PFN_XCreateWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateWindow"); - _glfw.x11.xlib.DefineCursor = (PFN_XDefineCursor) - _glfw_dlsym(_glfw.x11.xlib.handle, "XDefineCursor"); - _glfw.x11.xlib.DeleteContext = (PFN_XDeleteContext) - _glfw_dlsym(_glfw.x11.xlib.handle, "XDeleteContext"); - _glfw.x11.xlib.DeleteProperty = (PFN_XDeleteProperty) - _glfw_dlsym(_glfw.x11.xlib.handle, "XDeleteProperty"); - _glfw.x11.xlib.DestroyIC = (PFN_XDestroyIC) - _glfw_dlsym(_glfw.x11.xlib.handle, "XDestroyIC"); - _glfw.x11.xlib.DestroyRegion = (PFN_XDestroyRegion) - _glfw_dlsym(_glfw.x11.xlib.handle, "XDestroyRegion"); - _glfw.x11.xlib.DestroyWindow = (PFN_XDestroyWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XDestroyWindow"); - _glfw.x11.xlib.DisplayKeycodes = (PFN_XDisplayKeycodes) - _glfw_dlsym(_glfw.x11.xlib.handle, "XDisplayKeycodes"); - _glfw.x11.xlib.EventsQueued = (PFN_XEventsQueued) - _glfw_dlsym(_glfw.x11.xlib.handle, "XEventsQueued"); - _glfw.x11.xlib.FilterEvent = (PFN_XFilterEvent) - _glfw_dlsym(_glfw.x11.xlib.handle, "XFilterEvent"); - _glfw.x11.xlib.FindContext = (PFN_XFindContext) - _glfw_dlsym(_glfw.x11.xlib.handle, "XFindContext"); - _glfw.x11.xlib.Flush = (PFN_XFlush) - _glfw_dlsym(_glfw.x11.xlib.handle, "XFlush"); - _glfw.x11.xlib.Free = (PFN_XFree) - _glfw_dlsym(_glfw.x11.xlib.handle, "XFree"); - _glfw.x11.xlib.FreeColormap = (PFN_XFreeColormap) - _glfw_dlsym(_glfw.x11.xlib.handle, "XFreeColormap"); - _glfw.x11.xlib.FreeCursor = (PFN_XFreeCursor) - _glfw_dlsym(_glfw.x11.xlib.handle, "XFreeCursor"); - _glfw.x11.xlib.FreeEventData = (PFN_XFreeEventData) - _glfw_dlsym(_glfw.x11.xlib.handle, "XFreeEventData"); - _glfw.x11.xlib.GetErrorText = (PFN_XGetErrorText) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetErrorText"); - _glfw.x11.xlib.GetEventData = (PFN_XGetEventData) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetEventData"); - _glfw.x11.xlib.GetICValues = (PFN_XGetICValues) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetICValues"); - _glfw.x11.xlib.GetIMValues = (PFN_XGetIMValues) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetIMValues"); - _glfw.x11.xlib.GetInputFocus = (PFN_XGetInputFocus) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetInputFocus"); - _glfw.x11.xlib.GetKeyboardMapping = (PFN_XGetKeyboardMapping) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetKeyboardMapping"); - _glfw.x11.xlib.GetScreenSaver = (PFN_XGetScreenSaver) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetScreenSaver"); - _glfw.x11.xlib.GetSelectionOwner = (PFN_XGetSelectionOwner) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetSelectionOwner"); - _glfw.x11.xlib.GetVisualInfo = (PFN_XGetVisualInfo) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetVisualInfo"); - _glfw.x11.xlib.GetWMNormalHints = (PFN_XGetWMNormalHints) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetWMNormalHints"); - _glfw.x11.xlib.GetWindowAttributes = (PFN_XGetWindowAttributes) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetWindowAttributes"); - _glfw.x11.xlib.GetWindowProperty = (PFN_XGetWindowProperty) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGetWindowProperty"); - _glfw.x11.xlib.GrabPointer = (PFN_XGrabPointer) - _glfw_dlsym(_glfw.x11.xlib.handle, "XGrabPointer"); - _glfw.x11.xlib.IconifyWindow = (PFN_XIconifyWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XIconifyWindow"); - _glfw.x11.xlib.InitThreads = (PFN_XInitThreads) - _glfw_dlsym(_glfw.x11.xlib.handle, "XInitThreads"); - _glfw.x11.xlib.InternAtom = (PFN_XInternAtom) - _glfw_dlsym(_glfw.x11.xlib.handle, "XInternAtom"); - _glfw.x11.xlib.LookupString = (PFN_XLookupString) - _glfw_dlsym(_glfw.x11.xlib.handle, "XLookupString"); - _glfw.x11.xlib.MapRaised = (PFN_XMapRaised) - _glfw_dlsym(_glfw.x11.xlib.handle, "XMapRaised"); - _glfw.x11.xlib.MapWindow = (PFN_XMapWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XMapWindow"); - _glfw.x11.xlib.MoveResizeWindow = (PFN_XMoveResizeWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XMoveResizeWindow"); - _glfw.x11.xlib.MoveWindow = (PFN_XMoveWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XMoveWindow"); - _glfw.x11.xlib.NextEvent = (PFN_XNextEvent) - _glfw_dlsym(_glfw.x11.xlib.handle, "XNextEvent"); - _glfw.x11.xlib.OpenDisplay = (PFN_XOpenDisplay) - _glfw_dlsym(_glfw.x11.xlib.handle, "XOpenDisplay"); - _glfw.x11.xlib.OpenIM = (PFN_XOpenIM) - _glfw_dlsym(_glfw.x11.xlib.handle, "XOpenIM"); - _glfw.x11.xlib.PeekEvent = (PFN_XPeekEvent) - _glfw_dlsym(_glfw.x11.xlib.handle, "XPeekEvent"); - _glfw.x11.xlib.Pending = (PFN_XPending) - _glfw_dlsym(_glfw.x11.xlib.handle, "XPending"); - _glfw.x11.xlib.QueryExtension = (PFN_XQueryExtension) - _glfw_dlsym(_glfw.x11.xlib.handle, "XQueryExtension"); - _glfw.x11.xlib.QueryPointer = (PFN_XQueryPointer) - _glfw_dlsym(_glfw.x11.xlib.handle, "XQueryPointer"); - _glfw.x11.xlib.RaiseWindow = (PFN_XRaiseWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XRaiseWindow"); - _glfw.x11.xlib.RegisterIMInstantiateCallback = (PFN_XRegisterIMInstantiateCallback) - _glfw_dlsym(_glfw.x11.xlib.handle, "XRegisterIMInstantiateCallback"); - _glfw.x11.xlib.ResizeWindow = (PFN_XResizeWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XResizeWindow"); - _glfw.x11.xlib.ResourceManagerString = (PFN_XResourceManagerString) - _glfw_dlsym(_glfw.x11.xlib.handle, "XResourceManagerString"); - _glfw.x11.xlib.SaveContext = (PFN_XSaveContext) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSaveContext"); - _glfw.x11.xlib.SelectInput = (PFN_XSelectInput) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSelectInput"); - _glfw.x11.xlib.SendEvent = (PFN_XSendEvent) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSendEvent"); - _glfw.x11.xlib.SetClassHint = (PFN_XSetClassHint) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetClassHint"); - _glfw.x11.xlib.SetErrorHandler = (PFN_XSetErrorHandler) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetErrorHandler"); - _glfw.x11.xlib.SetICFocus = (PFN_XSetICFocus) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetICFocus"); - _glfw.x11.xlib.SetIMValues = (PFN_XSetIMValues) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetIMValues"); - _glfw.x11.xlib.SetInputFocus = (PFN_XSetInputFocus) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetInputFocus"); - _glfw.x11.xlib.SetLocaleModifiers = (PFN_XSetLocaleModifiers) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetLocaleModifiers"); - _glfw.x11.xlib.SetScreenSaver = (PFN_XSetScreenSaver) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetScreenSaver"); - _glfw.x11.xlib.SetSelectionOwner = (PFN_XSetSelectionOwner) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetSelectionOwner"); - _glfw.x11.xlib.SetWMHints = (PFN_XSetWMHints) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetWMHints"); - _glfw.x11.xlib.SetWMNormalHints = (PFN_XSetWMNormalHints) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetWMNormalHints"); - _glfw.x11.xlib.SetWMProtocols = (PFN_XSetWMProtocols) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSetWMProtocols"); - _glfw.x11.xlib.SupportsLocale = (PFN_XSupportsLocale) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSupportsLocale"); - _glfw.x11.xlib.Sync = (PFN_XSync) - _glfw_dlsym(_glfw.x11.xlib.handle, "XSync"); - _glfw.x11.xlib.TranslateCoordinates = (PFN_XTranslateCoordinates) - _glfw_dlsym(_glfw.x11.xlib.handle, "XTranslateCoordinates"); - _glfw.x11.xlib.UndefineCursor = (PFN_XUndefineCursor) - _glfw_dlsym(_glfw.x11.xlib.handle, "XUndefineCursor"); - _glfw.x11.xlib.UngrabPointer = (PFN_XUngrabPointer) - _glfw_dlsym(_glfw.x11.xlib.handle, "XUngrabPointer"); - _glfw.x11.xlib.UnmapWindow = (PFN_XUnmapWindow) - _glfw_dlsym(_glfw.x11.xlib.handle, "XUnmapWindow"); - _glfw.x11.xlib.UnsetICFocus = (PFN_XUnsetICFocus) - _glfw_dlsym(_glfw.x11.xlib.handle, "XUnsetICFocus"); - _glfw.x11.xlib.VisualIDFromVisual = (PFN_XVisualIDFromVisual) - _glfw_dlsym(_glfw.x11.xlib.handle, "XVisualIDFromVisual"); - _glfw.x11.xlib.WarpPointer = (PFN_XWarpPointer) - _glfw_dlsym(_glfw.x11.xlib.handle, "XWarpPointer"); - _glfw.x11.xkb.FreeKeyboard = (PFN_XkbFreeKeyboard) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbFreeKeyboard"); - _glfw.x11.xkb.FreeNames = (PFN_XkbFreeNames) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbFreeNames"); - _glfw.x11.xkb.GetMap = (PFN_XkbGetMap) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbGetMap"); - _glfw.x11.xkb.GetNames = (PFN_XkbGetNames) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbGetNames"); - _glfw.x11.xkb.GetState = (PFN_XkbGetState) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbGetState"); - _glfw.x11.xkb.KeycodeToKeysym = (PFN_XkbKeycodeToKeysym) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbKeycodeToKeysym"); - _glfw.x11.xkb.QueryExtension = (PFN_XkbQueryExtension) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbQueryExtension"); - _glfw.x11.xkb.SelectEventDetails = (PFN_XkbSelectEventDetails) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbSelectEventDetails"); - _glfw.x11.xkb.SetDetectableAutoRepeat = (PFN_XkbSetDetectableAutoRepeat) - _glfw_dlsym(_glfw.x11.xlib.handle, "XkbSetDetectableAutoRepeat"); - _glfw.x11.xrm.DestroyDatabase = (PFN_XrmDestroyDatabase) - _glfw_dlsym(_glfw.x11.xlib.handle, "XrmDestroyDatabase"); - _glfw.x11.xrm.GetResource = (PFN_XrmGetResource) - _glfw_dlsym(_glfw.x11.xlib.handle, "XrmGetResource"); - _glfw.x11.xrm.GetStringDatabase = (PFN_XrmGetStringDatabase) - _glfw_dlsym(_glfw.x11.xlib.handle, "XrmGetStringDatabase"); - _glfw.x11.xrm.Initialize = (PFN_XrmInitialize) - _glfw_dlsym(_glfw.x11.xlib.handle, "XrmInitialize"); - _glfw.x11.xrm.UniqueQuark = (PFN_XrmUniqueQuark) - _glfw_dlsym(_glfw.x11.xlib.handle, "XrmUniqueQuark"); - _glfw.x11.xlib.UnregisterIMInstantiateCallback = (PFN_XUnregisterIMInstantiateCallback) - _glfw_dlsym(_glfw.x11.xlib.handle, "XUnregisterIMInstantiateCallback"); - _glfw.x11.xlib.utf8LookupString = (PFN_Xutf8LookupString) - _glfw_dlsym(_glfw.x11.xlib.handle, "Xutf8LookupString"); - _glfw.x11.xlib.utf8SetWMProperties = (PFN_Xutf8SetWMProperties) - _glfw_dlsym(_glfw.x11.xlib.handle, "Xutf8SetWMProperties"); + PFN_XInitThreads XInitThreads = (PFN_XInitThreads) + _glfwPlatformGetModuleSymbol(module, "XInitThreads"); + PFN_XrmInitialize XrmInitialize = (PFN_XrmInitialize) + _glfwPlatformGetModuleSymbol(module, "XrmInitialize"); + PFN_XOpenDisplay XOpenDisplay = (PFN_XOpenDisplay) + _glfwPlatformGetModuleSymbol(module, "XOpenDisplay"); + if (!XInitThreads || !XrmInitialize || !XOpenDisplay) + { + if (platformID == GLFW_PLATFORM_X11) + _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to load Xlib entry point"); - if (_glfw.x11.xlib.utf8LookupString && _glfw.x11.xlib.utf8SetWMProperties) - _glfw.x11.xlib.utf8 = GLFW_TRUE; + _glfwPlatformFreeModule(module); + return GLFW_FALSE; + } XInitThreads(); XrmInitialize(); - _glfw.x11.display = XOpenDisplay(NULL); - if (!_glfw.x11.display) + Display* display = XOpenDisplay(NULL); + if (!display) { - const char* display = getenv("DISPLAY"); - if (display) + if (platformID == GLFW_PLATFORM_X11) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to open display %s", display); - } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: The DISPLAY environment variable is missing"); + const char* name = getenv("DISPLAY"); + if (name) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, + "X11: Failed to open display %s", name); + } + else + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, + "X11: The DISPLAY environment variable is missing"); + } } + _glfwPlatformFreeModule(module); return GLFW_FALSE; } + _glfw.x11.display = display; + _glfw.x11.xlib.handle = module; + + *platform = x11; + return GLFW_TRUE; +} + +int _glfwInitX11(void) +{ + _glfw.x11.xlib.AllocClassHint = (PFN_XAllocClassHint) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XAllocClassHint"); + _glfw.x11.xlib.AllocSizeHints = (PFN_XAllocSizeHints) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XAllocSizeHints"); + _glfw.x11.xlib.AllocWMHints = (PFN_XAllocWMHints) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XAllocWMHints"); + _glfw.x11.xlib.ChangeProperty = (PFN_XChangeProperty) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XChangeProperty"); + _glfw.x11.xlib.ChangeWindowAttributes = (PFN_XChangeWindowAttributes) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XChangeWindowAttributes"); + _glfw.x11.xlib.CheckIfEvent = (PFN_XCheckIfEvent) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCheckIfEvent"); + _glfw.x11.xlib.CheckTypedWindowEvent = (PFN_XCheckTypedWindowEvent) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCheckTypedWindowEvent"); + _glfw.x11.xlib.CloseDisplay = (PFN_XCloseDisplay) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCloseDisplay"); + _glfw.x11.xlib.CloseIM = (PFN_XCloseIM) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCloseIM"); + _glfw.x11.xlib.ConvertSelection = (PFN_XConvertSelection) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XConvertSelection"); + _glfw.x11.xlib.CreateColormap = (PFN_XCreateColormap) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCreateColormap"); + _glfw.x11.xlib.CreateFontCursor = (PFN_XCreateFontCursor) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCreateFontCursor"); + _glfw.x11.xlib.CreateIC = (PFN_XCreateIC) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCreateIC"); + _glfw.x11.xlib.CreateRegion = (PFN_XCreateRegion) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCreateRegion"); + _glfw.x11.xlib.CreateWindow = (PFN_XCreateWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XCreateWindow"); + _glfw.x11.xlib.DefineCursor = (PFN_XDefineCursor) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XDefineCursor"); + _glfw.x11.xlib.DeleteContext = (PFN_XDeleteContext) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XDeleteContext"); + _glfw.x11.xlib.DeleteProperty = (PFN_XDeleteProperty) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XDeleteProperty"); + _glfw.x11.xlib.DestroyIC = (PFN_XDestroyIC) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XDestroyIC"); + _glfw.x11.xlib.DestroyRegion = (PFN_XDestroyRegion) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XDestroyRegion"); + _glfw.x11.xlib.DestroyWindow = (PFN_XDestroyWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XDestroyWindow"); + _glfw.x11.xlib.DisplayKeycodes = (PFN_XDisplayKeycodes) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XDisplayKeycodes"); + _glfw.x11.xlib.EventsQueued = (PFN_XEventsQueued) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XEventsQueued"); + _glfw.x11.xlib.FilterEvent = (PFN_XFilterEvent) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XFilterEvent"); + _glfw.x11.xlib.FindContext = (PFN_XFindContext) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XFindContext"); + _glfw.x11.xlib.Flush = (PFN_XFlush) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XFlush"); + _glfw.x11.xlib.Free = (PFN_XFree) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XFree"); + _glfw.x11.xlib.FreeColormap = (PFN_XFreeColormap) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XFreeColormap"); + _glfw.x11.xlib.FreeCursor = (PFN_XFreeCursor) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XFreeCursor"); + _glfw.x11.xlib.FreeEventData = (PFN_XFreeEventData) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XFreeEventData"); + _glfw.x11.xlib.GetErrorText = (PFN_XGetErrorText) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetErrorText"); + _glfw.x11.xlib.GetEventData = (PFN_XGetEventData) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetEventData"); + _glfw.x11.xlib.GetICValues = (PFN_XGetICValues) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetICValues"); + _glfw.x11.xlib.GetIMValues = (PFN_XGetIMValues) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetIMValues"); + _glfw.x11.xlib.GetInputFocus = (PFN_XGetInputFocus) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetInputFocus"); + _glfw.x11.xlib.GetKeyboardMapping = (PFN_XGetKeyboardMapping) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetKeyboardMapping"); + _glfw.x11.xlib.GetScreenSaver = (PFN_XGetScreenSaver) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetScreenSaver"); + _glfw.x11.xlib.GetSelectionOwner = (PFN_XGetSelectionOwner) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetSelectionOwner"); + _glfw.x11.xlib.GetVisualInfo = (PFN_XGetVisualInfo) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetVisualInfo"); + _glfw.x11.xlib.GetWMNormalHints = (PFN_XGetWMNormalHints) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetWMNormalHints"); + _glfw.x11.xlib.GetWindowAttributes = (PFN_XGetWindowAttributes) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetWindowAttributes"); + _glfw.x11.xlib.GetWindowProperty = (PFN_XGetWindowProperty) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetWindowProperty"); + _glfw.x11.xlib.GrabPointer = (PFN_XGrabPointer) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGrabPointer"); + _glfw.x11.xlib.IconifyWindow = (PFN_XIconifyWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XIconifyWindow"); + _glfw.x11.xlib.InternAtom = (PFN_XInternAtom) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XInternAtom"); + _glfw.x11.xlib.LookupString = (PFN_XLookupString) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XLookupString"); + _glfw.x11.xlib.MapRaised = (PFN_XMapRaised) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XMapRaised"); + _glfw.x11.xlib.MapWindow = (PFN_XMapWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XMapWindow"); + _glfw.x11.xlib.MoveResizeWindow = (PFN_XMoveResizeWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XMoveResizeWindow"); + _glfw.x11.xlib.MoveWindow = (PFN_XMoveWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XMoveWindow"); + _glfw.x11.xlib.NextEvent = (PFN_XNextEvent) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XNextEvent"); + _glfw.x11.xlib.OpenIM = (PFN_XOpenIM) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XOpenIM"); + _glfw.x11.xlib.PeekEvent = (PFN_XPeekEvent) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XPeekEvent"); + _glfw.x11.xlib.Pending = (PFN_XPending) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XPending"); + _glfw.x11.xlib.QueryExtension = (PFN_XQueryExtension) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XQueryExtension"); + _glfw.x11.xlib.QueryPointer = (PFN_XQueryPointer) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XQueryPointer"); + _glfw.x11.xlib.RaiseWindow = (PFN_XRaiseWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XRaiseWindow"); + _glfw.x11.xlib.RegisterIMInstantiateCallback = (PFN_XRegisterIMInstantiateCallback) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XRegisterIMInstantiateCallback"); + _glfw.x11.xlib.ResizeWindow = (PFN_XResizeWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XResizeWindow"); + _glfw.x11.xlib.ResourceManagerString = (PFN_XResourceManagerString) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XResourceManagerString"); + _glfw.x11.xlib.SaveContext = (PFN_XSaveContext) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSaveContext"); + _glfw.x11.xlib.SelectInput = (PFN_XSelectInput) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSelectInput"); + _glfw.x11.xlib.SendEvent = (PFN_XSendEvent) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSendEvent"); + _glfw.x11.xlib.SetClassHint = (PFN_XSetClassHint) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetClassHint"); + _glfw.x11.xlib.SetErrorHandler = (PFN_XSetErrorHandler) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetErrorHandler"); + _glfw.x11.xlib.SetICFocus = (PFN_XSetICFocus) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetICFocus"); + _glfw.x11.xlib.SetIMValues = (PFN_XSetIMValues) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetIMValues"); + _glfw.x11.xlib.SetInputFocus = (PFN_XSetInputFocus) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetInputFocus"); + _glfw.x11.xlib.SetLocaleModifiers = (PFN_XSetLocaleModifiers) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetLocaleModifiers"); + _glfw.x11.xlib.SetScreenSaver = (PFN_XSetScreenSaver) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetScreenSaver"); + _glfw.x11.xlib.SetSelectionOwner = (PFN_XSetSelectionOwner) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetSelectionOwner"); + _glfw.x11.xlib.SetWMHints = (PFN_XSetWMHints) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetWMHints"); + _glfw.x11.xlib.SetWMNormalHints = (PFN_XSetWMNormalHints) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetWMNormalHints"); + _glfw.x11.xlib.SetWMProtocols = (PFN_XSetWMProtocols) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSetWMProtocols"); + _glfw.x11.xlib.SupportsLocale = (PFN_XSupportsLocale) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSupportsLocale"); + _glfw.x11.xlib.Sync = (PFN_XSync) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XSync"); + _glfw.x11.xlib.TranslateCoordinates = (PFN_XTranslateCoordinates) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XTranslateCoordinates"); + _glfw.x11.xlib.UndefineCursor = (PFN_XUndefineCursor) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUndefineCursor"); + _glfw.x11.xlib.UngrabPointer = (PFN_XUngrabPointer) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUngrabPointer"); + _glfw.x11.xlib.UnmapWindow = (PFN_XUnmapWindow) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnmapWindow"); + _glfw.x11.xlib.UnsetICFocus = (PFN_XUnsetICFocus) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnsetICFocus"); + _glfw.x11.xlib.VisualIDFromVisual = (PFN_XVisualIDFromVisual) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XVisualIDFromVisual"); + _glfw.x11.xlib.WarpPointer = (PFN_XWarpPointer) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XWarpPointer"); + _glfw.x11.xkb.FreeKeyboard = (PFN_XkbFreeKeyboard) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbFreeKeyboard"); + _glfw.x11.xkb.FreeNames = (PFN_XkbFreeNames) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbFreeNames"); + _glfw.x11.xkb.GetMap = (PFN_XkbGetMap) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbGetMap"); + _glfw.x11.xkb.GetNames = (PFN_XkbGetNames) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbGetNames"); + _glfw.x11.xkb.GetState = (PFN_XkbGetState) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbGetState"); + _glfw.x11.xkb.KeycodeToKeysym = (PFN_XkbKeycodeToKeysym) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbKeycodeToKeysym"); + _glfw.x11.xkb.QueryExtension = (PFN_XkbQueryExtension) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbQueryExtension"); + _glfw.x11.xkb.SelectEventDetails = (PFN_XkbSelectEventDetails) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbSelectEventDetails"); + _glfw.x11.xkb.SetDetectableAutoRepeat = (PFN_XkbSetDetectableAutoRepeat) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XkbSetDetectableAutoRepeat"); + _glfw.x11.xrm.DestroyDatabase = (PFN_XrmDestroyDatabase) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XrmDestroyDatabase"); + _glfw.x11.xrm.GetResource = (PFN_XrmGetResource) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XrmGetResource"); + _glfw.x11.xrm.GetStringDatabase = (PFN_XrmGetStringDatabase) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XrmGetStringDatabase"); + _glfw.x11.xrm.UniqueQuark = (PFN_XrmUniqueQuark) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XrmUniqueQuark"); + _glfw.x11.xlib.UnregisterIMInstantiateCallback = (PFN_XUnregisterIMInstantiateCallback) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XUnregisterIMInstantiateCallback"); + _glfw.x11.xlib.utf8LookupString = (PFN_Xutf8LookupString) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "Xutf8LookupString"); + _glfw.x11.xlib.utf8SetWMProperties = (PFN_Xutf8SetWMProperties) + _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "Xutf8SetWMProperties"); + + if (_glfw.x11.xlib.utf8LookupString && _glfw.x11.xlib.utf8SetWMProperties) + _glfw.x11.xlib.utf8 = GLFW_TRUE; + _glfw.x11.screen = DefaultScreen(_glfw.x11.display); _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.context = XUniqueContext(); getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); + if (!createEmptyEventPipe()) + return GLFW_FALSE; + if (!initExtensions()) return GLFW_FALSE; @@ -1381,13 +1547,11 @@ int _glfwPlatformInit(void) NULL); } - _glfwInitTimerPOSIX(); - _glfwPollMonitorsX11(); return GLFW_TRUE; } -void _glfwPlatformTerminate(void) +void _glfwTerminateX11(void) { if (_glfw.x11.helperWindowHandle) { @@ -1407,8 +1571,8 @@ void _glfwPlatformTerminate(void) _glfw.x11.hiddenCursorHandle = (Cursor) 0; } - free(_glfw.x11.primarySelectionString); - free(_glfw.x11.clipboardString); + _glfw_free(_glfw.x11.primarySelectionString); + _glfw_free(_glfw.x11.clipboardString); XUnregisterIMInstantiateCallback(_glfw.x11.display, NULL, NULL, NULL, @@ -1429,46 +1593,47 @@ void _glfwPlatformTerminate(void) if (_glfw.x11.x11xcb.handle) { - _glfw_dlclose(_glfw.x11.x11xcb.handle); + _glfwPlatformFreeModule(_glfw.x11.x11xcb.handle); _glfw.x11.x11xcb.handle = NULL; } if (_glfw.x11.xcursor.handle) { - _glfw_dlclose(_glfw.x11.xcursor.handle); + _glfwPlatformFreeModule(_glfw.x11.xcursor.handle); _glfw.x11.xcursor.handle = NULL; } if (_glfw.x11.randr.handle) { - _glfw_dlclose(_glfw.x11.randr.handle); + _glfwPlatformFreeModule(_glfw.x11.randr.handle); _glfw.x11.randr.handle = NULL; } if (_glfw.x11.xinerama.handle) { - _glfw_dlclose(_glfw.x11.xinerama.handle); + _glfwPlatformFreeModule(_glfw.x11.xinerama.handle); _glfw.x11.xinerama.handle = NULL; } if (_glfw.x11.xrender.handle) { - _glfw_dlclose(_glfw.x11.xrender.handle); + _glfwPlatformFreeModule(_glfw.x11.xrender.handle); _glfw.x11.xrender.handle = NULL; } if (_glfw.x11.vidmode.handle) { - _glfw_dlclose(_glfw.x11.vidmode.handle); + _glfwPlatformFreeModule(_glfw.x11.vidmode.handle); _glfw.x11.vidmode.handle = NULL; } if (_glfw.x11.xi.handle) { - _glfw_dlclose(_glfw.x11.xi.handle); + _glfwPlatformFreeModule(_glfw.x11.xi.handle); _glfw.x11.xi.handle = NULL; } + _glfwTerminateOSMesa(); // NOTE: These need to be unloaded after XCloseDisplay, as they register // cleanup callbacks that get called by that function _glfwTerminateEGL(); @@ -1476,25 +1641,14 @@ void _glfwPlatformTerminate(void) if (_glfw.x11.xlib.handle) { - _glfw_dlclose(_glfw.x11.xlib.handle); + _glfwPlatformFreeModule(_glfw.x11.xlib.handle); _glfw.x11.xlib.handle = NULL; } + + if (_glfw.x11.emptyEventPipe[0] || _glfw.x11.emptyEventPipe[1]) + { + close(_glfw.x11.emptyEventPipe[0]); + close(_glfw.x11.emptyEventPipe[1]); + } } -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " X11 GLX EGL OSMesa" -#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) - " clock_gettime" -#else - " gettimeofday" -#endif -#if defined(__linux__) - " evdev" -#endif -#if defined(_GLFW_BUILD_DLL) - " shared" -#endif - ; -} - diff --git a/src/external/glfw/src/x11_monitor.c b/src/external/glfw/src/x11_monitor.c index 8de865960..b031c83c0 100644 --- a/src/external/glfw/src/x11_monitor.c +++ b/src/external/glfw/src/x11_monitor.c @@ -116,7 +116,7 @@ void _glfwPollMonitorsX11(void) disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); @@ -209,7 +209,7 @@ void _glfwPollMonitorsX11(void) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } - free(disconnected); + _glfw_free(disconnected); } else { @@ -232,7 +232,7 @@ void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) RRMode native = None; const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); - _glfwPlatformGetVideoMode(monitor, ¤t); + _glfwGetVideoModeX11(monitor, ¤t); if (_glfwCompareVideoModes(¤t, best) == 0) return; @@ -310,11 +310,11 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +void _glfwFreeMonitorX11(_GLFWmonitor* monitor) { } -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +void _glfwGetMonitorPosX11(_GLFWmonitor* monitor, int* xpos, int* ypos) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { @@ -336,8 +336,8 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) } } -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) +void _glfwGetMonitorContentScaleX11(_GLFWmonitor* monitor, + float* xscale, float* yscale) { if (xscale) *xscale = _glfw.x11.contentScaleX; @@ -345,7 +345,9 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, *yscale = _glfw.x11.contentScaleY; } -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) +void _glfwGetMonitorWorkareaX11(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) { int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; @@ -437,7 +439,7 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos *height = areaHeight; } -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +GLFWvidmode* _glfwGetVideoModesX11(_GLFWmonitor* monitor, int* count) { GLFWvidmode* result; @@ -450,7 +452,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); - result = calloc(oi->nmode, sizeof(GLFWvidmode)); + result = _glfw_calloc(oi->nmode, sizeof(GLFWvidmode)); for (int i = 0; i < oi->nmode; i++) { @@ -482,14 +484,14 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) else { *count = 1; - result = calloc(1, sizeof(GLFWvidmode)); - _glfwPlatformGetVideoMode(monitor, result); + result = _glfw_calloc(1, sizeof(GLFWvidmode)); + _glfwGetVideoModeX11(monitor, result); } return result; } -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +void _glfwGetVideoModeX11(_GLFWmonitor* monitor, GLFWvidmode* mode) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { @@ -519,7 +521,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) } } -GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwGetGammaRampX11(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) { @@ -557,7 +559,7 @@ GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) } } -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +void _glfwSetGammaRampX11(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) { diff --git a/src/external/glfw/src/x11_platform.h b/src/external/glfw/src/x11_platform.h index c5137bc33..cdea39574 100644 --- a/src/external/glfw/src/x11_platform.h +++ b/src/external/glfw/src/x11_platform.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -51,6 +50,53 @@ // The Shape extension provides custom window shapes #include +#define GLX_VENDOR 1 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_RGBA_TYPE 0x8014 +#define GLX_DOUBLEBUFFER 5 +#define GLX_STEREO 6 +#define GLX_AUX_BUFFERS 7 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_ACCUM_RED_SIZE 14 +#define GLX_ACCUM_GREEN_SIZE 15 +#define GLX_ACCUM_BLUE_SIZE 16 +#define GLX_ACCUM_ALPHA_SIZE 17 +#define GLX_SAMPLES 0x186a1 +#define GLX_VISUAL_ID 0x800b + +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 + +typedef XID GLXWindow; +typedef XID GLXDrawable; +typedef struct __GLXFBConfig* GLXFBConfig; +typedef struct __GLXcontext* GLXContext; +typedef void (*__GLXextproc)(void); + typedef XClassHint* (* PFN_XAllocClassHint)(void); typedef XSizeHints* (* PFN_XAllocSizeHints)(void); typedef XWMHints* (* PFN_XAllocWMHints)(void); @@ -197,7 +243,6 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XGetWindowProperty _glfw.x11.xlib.GetWindowProperty #define XGrabPointer _glfw.x11.xlib.GrabPointer #define XIconifyWindow _glfw.x11.xlib.IconifyWindow -#define XInitThreads _glfw.x11.xlib.InitThreads #define XInternAtom _glfw.x11.xlib.InternAtom #define XLookupString _glfw.x11.xlib.LookupString #define XMapRaised _glfw.x11.xlib.MapRaised @@ -205,7 +250,6 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XMoveResizeWindow _glfw.x11.xlib.MoveResizeWindow #define XMoveWindow _glfw.x11.xlib.MoveWindow #define XNextEvent _glfw.x11.xlib.NextEvent -#define XOpenDisplay _glfw.x11.xlib.OpenDisplay #define XOpenIM _glfw.x11.xlib.OpenIM #define XPeekEvent _glfw.x11.xlib.PeekEvent #define XPending _glfw.x11.xlib.Pending @@ -250,7 +294,6 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char #define XrmDestroyDatabase _glfw.x11.xrm.DestroyDatabase #define XrmGetResource _glfw.x11.xrm.GetResource #define XrmGetStringDatabase _glfw.x11.xrm.GetStringDatabase -#define XrmInitialize _glfw.x11.xrm.Initialize #define XrmUniqueQuark _glfw.x11.xrm.UniqueQuark #define XUnregisterIMInstantiateCallback _glfw.x11.xlib.UnregisterIMInstantiateCallback #define Xutf8LookupString _glfw.x11.xlib.utf8LookupString @@ -348,6 +391,41 @@ typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); #define XShapeCombineRegion _glfw.x11.xshape.ShapeCombineRegion #define XShapeCombineMask _glfw.x11.xshape.ShapeCombineMask +typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); +typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); +typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); +typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); +typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); +typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); +typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); +typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); +typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); +typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); +typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); +typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); +typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); +typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); + +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + +// libGL.so function pointer typedefs +#define glXGetFBConfigs _glfw.glx.GetFBConfigs +#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib +#define glXGetClientString _glfw.glx.GetClientString +#define glXQueryExtension _glfw.glx.QueryExtension +#define glXQueryVersion _glfw.glx.QueryVersion +#define glXDestroyContext _glfw.glx.DestroyContext +#define glXMakeCurrent _glfw.glx.MakeCurrent +#define glXSwapBuffers _glfw.glx.SwapBuffers +#define glXQueryExtensionsString _glfw.glx.QueryExtensionsString +#define glXCreateNewContext _glfw.glx.CreateNewContext +#define glXGetVisualFromFBConfig _glfw.glx.GetVisualFromFBConfig +#define glXCreateWindow _glfw.glx.CreateWindow +#define glXDestroyWindow _glfw.glx.DestroyWindow + typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR; @@ -374,26 +452,72 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(V typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); -#include "posix_thread.h" -#include "posix_time.h" #include "xkb_unicode.h" -#include "glx_context.h" -#if defined(__linux__) -#include "linux_joystick.h" -#else -#include "null_joystick.h" -#endif +#include "posix_poll.h" -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) +#define GLFW_X11_WINDOW_STATE _GLFWwindowX11 x11; +#define GLFW_X11_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11; +#define GLFW_X11_MONITOR_STATE _GLFWmonitorX11 x11; +#define GLFW_X11_CURSOR_STATE _GLFWcursorX11 x11; -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11 +#define GLFW_GLX_CONTEXT_STATE _GLFWcontextGLX glx; +#define GLFW_GLX_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx; +// GLX-specific per-context data +// +typedef struct _GLFWcontextGLX +{ + GLXContext handle; + GLXWindow window; +} _GLFWcontextGLX; + +// GLX-specific global data +// +typedef struct _GLFWlibraryGLX +{ + int major, minor; + int eventBase; + int errorBase; + + void* handle; + + // GLX 1.3 functions + PFNGLXGETFBCONFIGSPROC GetFBConfigs; + PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; + PFNGLXGETCLIENTSTRINGPROC GetClientString; + PFNGLXQUERYEXTENSIONPROC QueryExtension; + PFNGLXQUERYVERSIONPROC QueryVersion; + PFNGLXDESTROYCONTEXTPROC DestroyContext; + PFNGLXMAKECURRENTPROC MakeCurrent; + PFNGLXSWAPBUFFERSPROC SwapBuffers; + PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; + PFNGLXCREATENEWCONTEXTPROC CreateNewContext; + PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC CreateWindow; + PFNGLXDESTROYWINDOWPROC DestroyWindow; + + // GLX 1.4 and extension functions + PFNGLXGETPROCADDRESSPROC GetProcAddress; + PFNGLXGETPROCADDRESSPROC GetProcAddressARB; + PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI; + PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + GLFWbool SGI_swap_control; + GLFWbool EXT_swap_control; + GLFWbool MESA_swap_control; + GLFWbool ARB_multisample; + GLFWbool ARB_framebuffer_sRGB; + GLFWbool EXT_framebuffer_sRGB; + GLFWbool ARB_create_context; + GLFWbool ARB_create_context_profile; + GLFWbool ARB_create_context_robustness; + GLFWbool EXT_create_context_es2_profile; + GLFWbool ARB_create_context_no_error; + GLFWbool ARB_context_flush_control; +} _GLFWlibraryGLX; + // X11-specific per-window data // typedef struct _GLFWwindowX11 @@ -422,7 +546,6 @@ typedef struct _GLFWwindowX11 // The time of the last KeyPress event per keycode, for discarding // duplicate key events generated for some keys by ibus Time keyPressTimes[256]; - } _GLFWwindowX11; // X11-specific global data @@ -443,6 +566,8 @@ typedef struct _GLFWlibraryX11 XContext context; // XIM input method XIM im; + // The previous X error handler, to be restored later + XErrorHandler errorHandler; // Most recent error code received by X error handler int errorCode; // Primary selection string (while the primary selection is owned) @@ -459,6 +584,7 @@ typedef struct _GLFWlibraryX11 double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active _GLFWwindow* disabledCursorWindow; + int emptyEventPipe[2]; // Window manager atoms Atom NET_SUPPORTED; @@ -563,7 +689,6 @@ typedef struct _GLFWlibraryX11 PFN_XGetWindowProperty GetWindowProperty; PFN_XGrabPointer GrabPointer; PFN_XIconifyWindow IconifyWindow; - PFN_XInitThreads InitThreads; PFN_XInternAtom InternAtom; PFN_XLookupString LookupString; PFN_XMapRaised MapRaised; @@ -571,7 +696,6 @@ typedef struct _GLFWlibraryX11 PFN_XMoveResizeWindow MoveResizeWindow; PFN_XMoveWindow MoveWindow; PFN_XNextEvent NextEvent; - PFN_XOpenDisplay OpenDisplay; PFN_XOpenIM OpenIM; PFN_XPeekEvent PeekEvent; PFN_XPending Pending; @@ -613,7 +737,6 @@ typedef struct _GLFWlibraryX11 PFN_XrmDestroyDatabase DestroyDatabase; PFN_XrmGetResource GetResource; PFN_XrmGetStringDatabase GetStringDatabase; - PFN_XrmInitialize Initialize; PFN_XrmUniqueQuark UniqueQuark; } xrm; @@ -751,7 +874,6 @@ typedef struct _GLFWlibraryX11 PFN_XShapeQueryVersion QueryVersion; PFN_XShapeCombineMask ShapeCombineMask; } xshape; - } _GLFWlibraryX11; // X11-specific per-monitor data @@ -765,7 +887,6 @@ typedef struct _GLFWmonitorX11 // Index of corresponding Xinerama screen, // for EWMH full screen window placement int index; - } _GLFWmonitorX11; // X11-specific per-cursor data @@ -773,15 +894,89 @@ typedef struct _GLFWmonitorX11 typedef struct _GLFWcursorX11 { Cursor handle; - } _GLFWcursorX11; +GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform); +int _glfwInitX11(void); +void _glfwTerminateX11(void); + +GLFWbool _glfwCreateWindowX11(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); +void _glfwDestroyWindowX11(_GLFWwindow* window); +void _glfwSetWindowTitleX11(_GLFWwindow* window, const char* title); +void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* images); +void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos); +void _glfwSetWindowPosX11(_GLFWwindow* window, int xpos, int ypos); +void _glfwGetWindowSizeX11(_GLFWwindow* window, int* width, int* height); +void _glfwSetWindowSizeX11(_GLFWwindow* window, int width, int height); +void _glfwSetWindowSizeLimitsX11(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); +void _glfwSetWindowAspectRatioX11(_GLFWwindow* window, int numer, int denom); +void _glfwGetFramebufferSizeX11(_GLFWwindow* window, int* width, int* height); +void _glfwGetWindowFrameSizeX11(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); +void _glfwGetWindowContentScaleX11(_GLFWwindow* window, float* xscale, float* yscale); +void _glfwIconifyWindowX11(_GLFWwindow* window); +void _glfwRestoreWindowX11(_GLFWwindow* window); +void _glfwMaximizeWindowX11(_GLFWwindow* window); +void _glfwShowWindowX11(_GLFWwindow* window); +void _glfwHideWindowX11(_GLFWwindow* window); +void _glfwRequestWindowAttentionX11(_GLFWwindow* window); +void _glfwFocusWindowX11(_GLFWwindow* window); +void _glfwSetWindowMonitorX11(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); +GLFWbool _glfwWindowFocusedX11(_GLFWwindow* window); +GLFWbool _glfwWindowIconifiedX11(_GLFWwindow* window); +GLFWbool _glfwWindowVisibleX11(_GLFWwindow* window); +GLFWbool _glfwWindowMaximizedX11(_GLFWwindow* window); +GLFWbool _glfwWindowHoveredX11(_GLFWwindow* window); +GLFWbool _glfwFramebufferTransparentX11(_GLFWwindow* window); +void _glfwSetWindowResizableX11(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowDecoratedX11(_GLFWwindow* window, GLFWbool enabled); +void _glfwSetWindowFloatingX11(_GLFWwindow* window, GLFWbool enabled); +float _glfwGetWindowOpacityX11(_GLFWwindow* window); +void _glfwSetWindowOpacityX11(_GLFWwindow* window, float opacity); +void _glfwSetWindowMousePassthroughX11(_GLFWwindow* window, GLFWbool enabled); + +void _glfwSetRawMouseMotionX11(_GLFWwindow *window, GLFWbool enabled); +GLFWbool _glfwRawMouseMotionSupportedX11(void); + +void _glfwPollEventsX11(void); +void _glfwWaitEventsX11(void); +void _glfwWaitEventsTimeoutX11(double timeout); +void _glfwPostEmptyEventX11(void); + +void _glfwGetCursorPosX11(_GLFWwindow* window, double* xpos, double* ypos); +void _glfwSetCursorPosX11(_GLFWwindow* window, double xpos, double ypos); +void _glfwSetCursorModeX11(_GLFWwindow* window, int mode); +const char* _glfwGetScancodeNameX11(int scancode); +int _glfwGetKeyScancodeX11(int key); +GLFWbool _glfwCreateCursorX11(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); +GLFWbool _glfwCreateStandardCursorX11(_GLFWcursor* cursor, int shape); +void _glfwDestroyCursorX11(_GLFWcursor* cursor); +void _glfwSetCursorX11(_GLFWwindow* window, _GLFWcursor* cursor); +void _glfwSetClipboardStringX11(const char* string); +const char* _glfwGetClipboardStringX11(void); + +EGLenum _glfwGetEGLPlatformX11(EGLint** attribs); +EGLNativeDisplayType _glfwGetEGLNativeDisplayX11(void); +EGLNativeWindowType _glfwGetEGLNativeWindowX11(_GLFWwindow* window); + +void _glfwGetRequiredInstanceExtensionsX11(char** extensions); +GLFWbool _glfwGetPhysicalDevicePresentationSupportX11(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); +VkResult _glfwCreateWindowSurfaceX11(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +void _glfwFreeMonitorX11(_GLFWmonitor* monitor); +void _glfwGetMonitorPosX11(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwGetMonitorContentScaleX11(_GLFWmonitor* monitor, float* xscale, float* yscale); +void _glfwGetMonitorWorkareaX11(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); +GLFWvidmode* _glfwGetVideoModesX11(_GLFWmonitor* monitor, int* count); +void _glfwGetVideoModeX11(_GLFWmonitor* monitor, GLFWvidmode* mode); +GLFWbool _glfwGetGammaRampX11(_GLFWmonitor* monitor, GLFWgammaramp* ramp); +void _glfwSetGammaRampX11(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); + void _glfwPollMonitorsX11(void); void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor); -Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot); +Cursor _glfwCreateNativeCursorX11(const GLFWimage* image, int xhot, int yhot); unsigned long _glfwGetWindowPropertyX11(Window window, Atom property, @@ -796,3 +991,14 @@ void _glfwInputErrorX11(int error, const char* message); void _glfwPushSelectionToManagerX11(void); void _glfwCreateInputContextX11(_GLFWwindow* window); +GLFWbool _glfwInitGLX(void); +void _glfwTerminateGLX(void); +GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +void _glfwDestroyContextGLX(_GLFWwindow* window); +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth); + diff --git a/src/external/glfw/src/x11_window.c b/src/external/glfw/src/x11_window.c index 3f2277d60..8a689ed10 100644 --- a/src/external/glfw/src/x11_window.c +++ b/src/external/glfw/src/x11_window.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include @@ -56,50 +56,79 @@ #define _GLFW_XDND_VERSION 5 - -// Wait for data to arrive using select +// Wait for event data to arrive on the X11 display socket // This avoids blocking other threads via the per-display Xlib lock that also // covers GLX functions // -static GLFWbool waitForEvent(double* timeout) +static GLFWbool waitForX11Event(double* timeout) { - fd_set fds; - const int fd = ConnectionNumber(_glfw.x11.display); - int count = fd + 1; + struct pollfd fd = { ConnectionNumber(_glfw.x11.display), POLLIN }; + + while (!XPending(_glfw.x11.display)) + { + if (!_glfwPollPOSIX(&fd, 1, timeout)) + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Wait for event data to arrive on any event file descriptor +// This avoids blocking other threads via the per-display Xlib lock that also +// covers GLX functions +// +static GLFWbool waitForAnyEvent(double* timeout) +{ + nfds_t count = 2; + struct pollfd fds[3] = + { + { ConnectionNumber(_glfw.x11.display), POLLIN }, + { _glfw.x11.emptyEventPipe[0], POLLIN } + }; #if defined(__linux__) - if (_glfw.linjs.inotify > fd) - count = _glfw.linjs.inotify + 1; + if (_glfw.joysticksInitialized) + fds[count++] = (struct pollfd) { _glfw.linjs.inotify, POLLIN }; #endif + + while (!XPending(_glfw.x11.display)) + { + if (!_glfwPollPOSIX(fds, count, timeout)) + return GLFW_FALSE; + + for (int i = 1; i < count; i++) + { + if (fds[i].revents & POLLIN) + return GLFW_TRUE; + } + } + + return GLFW_TRUE; +} + +// Writes a byte to the empty event pipe +// +static void writeEmptyEvent(void) +{ for (;;) { - FD_ZERO(&fds); - FD_SET(fd, &fds); -#if defined(__linux__) - if (_glfw.linjs.inotify > 0) - FD_SET(_glfw.linjs.inotify, &fds); -#endif + const char byte = 0; + const ssize_t result = write(_glfw.x11.emptyEventPipe[1], &byte, 1); + if (result == 1 || (result == -1 && errno != EINTR)) + break; + } +} - if (timeout) - { - const long seconds = (long) *timeout; - const long microseconds = (long) ((*timeout - seconds) * 1e6); - struct timeval tv = { seconds, microseconds }; - const uint64_t base = _glfwPlatformGetTimerValue(); - - const int result = select(count, &fds, NULL, NULL, &tv); - const int error = errno; - - *timeout -= (_glfwPlatformGetTimerValue() - base) / - (double) _glfwPlatformGetTimerFrequency(); - - if (result > 0) - return GLFW_TRUE; - if ((result == -1 && error == EINTR) || *timeout <= 0.0) - return GLFW_FALSE; - } - else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR) - return GLFW_TRUE; +// Drains available data from the empty event pipe +// +static void drainEmptyEvents(void) +{ + for (;;) + { + char dummy[64]; + const ssize_t result = read(_glfw.x11.emptyEventPipe[0], dummy, sizeof(dummy)); + if (result == -1 && errno != EINTR) + break; } } @@ -116,7 +145,7 @@ static GLFWbool waitForVisibilityNotify(_GLFWwindow* window) VisibilityNotify, &dummy)) { - if (!waitForEvent(&timeout)) + if (!waitForX11Event(&timeout)) return GLFW_FALSE; } @@ -241,6 +270,11 @@ static void updateNormalHints(_GLFWwindow* window, int width, int height) { XSizeHints* hints = XAllocSizeHints(); + long supplied; + XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied); + + hints->flags &= ~(PMinSize | PMaxSize | PAspect); + if (!window->monitor) { if (window->resizable) @@ -277,9 +311,6 @@ static void updateNormalHints(_GLFWwindow* window, int width, int height) } } - hints->flags |= PWinGravity; - hints->win_gravity = StaticGravity; - XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); XFree(hints); } @@ -378,95 +409,13 @@ static void updateWindowMode(_GLFWwindow* window) } } -// Splits and translates a text/uri-list into separate file paths -// NOTE: This function destroys the provided string -// -static char** parseUriList(char* text, int* count) -{ - const char* prefix = "file://"; - char** paths = NULL; - char* line; - - *count = 0; - - while ((line = strtok(text, "\r\n"))) - { - text = NULL; - - if (line[0] == '#') - continue; - - if (strncmp(line, prefix, strlen(prefix)) == 0) - { - line += strlen(prefix); - // TODO: Validate hostname - while (*line != '/') - line++; - } - - (*count)++; - - char* path = calloc(strlen(line) + 1, 1); - paths = realloc(paths, *count * sizeof(char*)); - paths[*count - 1] = path; - - while (*line) - { - if (line[0] == '%' && line[1] && line[2]) - { - const char digits[3] = { line[1], line[2], '\0' }; - *path = strtol(digits, NULL, 16); - line += 2; - } - else - *path = *line; - - path++; - line++; - } - } - - return paths; -} - -// Encode a Unicode code point to a UTF-8 stream -// Based on cutef8 by Jeff Bezanson (Public Domain) -// -static size_t encodeUTF8(char* s, unsigned int ch) -{ - size_t count = 0; - - if (ch < 0x80) - s[count++] = (char) ch; - else if (ch < 0x800) - { - s[count++] = (ch >> 6) | 0xc0; - s[count++] = (ch & 0x3f) | 0x80; - } - else if (ch < 0x10000) - { - s[count++] = (ch >> 12) | 0xe0; - s[count++] = ((ch >> 6) & 0x3f) | 0x80; - s[count++] = (ch & 0x3f) | 0x80; - } - else if (ch < 0x110000) - { - s[count++] = (ch >> 18) | 0xf0; - s[count++] = ((ch >> 12) & 0x3f) | 0x80; - s[count++] = ((ch >> 6) & 0x3f) | 0x80; - s[count++] = (ch & 0x3f) | 0x80; - } - - return count; -} - // Decode a Unicode code point from a UTF-8 stream // Based on cutef8 by Jeff Bezanson (Public Domain) // -static unsigned int decodeUTF8(const char** s) +static uint32_t decodeUTF8(const char** s) { - unsigned int ch = 0, count = 0; - static const unsigned int offsets[] = + uint32_t codepoint = 0, count = 0; + static const uint32_t offsets[] = { 0x00000000u, 0x00003080u, 0x000e2080u, 0x03c82080u, 0xfa082080u, 0x82082080u @@ -474,13 +423,13 @@ static unsigned int decodeUTF8(const char** s) do { - ch = (ch << 6) + (unsigned char) **s; + codepoint = (codepoint << 6) + (unsigned char) **s; (*s)++; count++; } while ((**s & 0xc0) == 0x80); assert(count <= 6); - return ch - offsets[count - 1]; + return codepoint - offsets[count - 1]; } // Convert the specified Latin-1 string to UTF-8 @@ -493,11 +442,11 @@ static char* convertLatin1toUTF8(const char* source) for (sp = source; *sp; sp++) size += (*sp & 0x80) ? 2 : 1; - char* target = calloc(size, 1); + char* target = _glfw_calloc(size, 1); char* tp = target; for (sp = source; *sp; sp++) - tp += encodeUTF8(tp, *sp); + tp += _glfwEncodeUTF8(tp, *sp); return target; } @@ -506,7 +455,8 @@ static char* convertLatin1toUTF8(const char* source) // static void updateCursorImage(_GLFWwindow* window) { - if (window->cursorMode == GLFW_CURSOR_NORMAL) + if (window->cursorMode == GLFW_CURSOR_NORMAL || + window->cursorMode == GLFW_CURSOR_CAPTURED) { if (window->cursor) { @@ -523,6 +473,25 @@ static void updateCursorImage(_GLFWwindow* window) } } +// Grabs the cursor and confines it to the window +// +static void captureCursor(_GLFWwindow* window) +{ + XGrabPointer(_glfw.x11.display, window->x11.handle, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, + window->x11.handle, + None, + CurrentTime); +} + +// Ungrabs the cursor +// +static void releaseCursor(void) +{ + XUngrabPointer(_glfw.x11.display, CurrentTime); +} + // Enable XI2 raw mouse motion events // static void enableRawMouseMotion(_GLFWwindow* window) @@ -560,17 +529,12 @@ static void disableCursor(_GLFWwindow* window) enableRawMouseMotion(window); _glfw.x11.disabledCursorWindow = window; - _glfwPlatformGetCursorPos(window, - &_glfw.x11.restoreCursorPosX, - &_glfw.x11.restoreCursorPosY); + _glfwGetCursorPosX11(window, + &_glfw.x11.restoreCursorPosX, + &_glfw.x11.restoreCursorPosY); updateCursorImage(window); _glfwCenterCursorInContentArea(window); - XGrabPointer(_glfw.x11.display, window->x11.handle, True, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, - GrabModeAsync, GrabModeAsync, - window->x11.handle, - _glfw.x11.hiddenCursorHandle, - CurrentTime); + captureCursor(window); } // Exit disabled cursor mode for the specified window @@ -581,10 +545,10 @@ static void enableCursor(_GLFWwindow* window) disableRawMouseMotion(window); _glfw.x11.disabledCursorWindow = NULL; - XUngrabPointer(_glfw.x11.display, CurrentTime); - _glfwPlatformSetCursorPos(window, - _glfw.x11.restoreCursorPosX, - _glfw.x11.restoreCursorPosY); + releaseCursor(); + _glfwSetCursorPosX11(window, + _glfw.x11.restoreCursorPosX, + _glfw.x11.restoreCursorPosY); updateCursorImage(window); } @@ -611,6 +575,14 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, height *= _glfw.x11.contentScaleY; } + int xpos = 0, ypos = 0; + + if (wndconfig->xpos != GLFW_ANY_POSITION && wndconfig->ypos != GLFW_ANY_POSITION) + { + xpos = wndconfig->xpos; + ypos = wndconfig->ypos; + } + // Create a colormap based on the visual used by the current context window->x11.colormap = XCreateColormap(_glfw.x11.display, _glfw.x11.root, @@ -631,7 +603,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, window->x11.parent = _glfw.x11.root; window->x11.handle = XCreateWindow(_glfw.x11.display, _glfw.x11.root, - 0, 0, // Position + xpos, ypos, width, height, 0, // Border width depth, // Color depth @@ -655,7 +627,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, (XPointer) window); if (!wndconfig->decorated) - _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); + _glfwSetWindowDecoratedX11(window, GLFW_FALSE); if (_glfw.x11.NET_WM_STATE && !window->monitor) { @@ -734,7 +706,37 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, XFree(hints); } - updateNormalHints(window, width, height); + // Set ICCCM WM_NORMAL_HINTS property + { + XSizeHints* hints = XAllocSizeHints(); + if (!hints) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate size hints"); + return GLFW_FALSE; + } + + if (!wndconfig->resizable) + { + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = width; + hints->min_height = hints->max_height = height; + } + + // HACK: Explicitly setting PPosition to any value causes some WMs, notably + // Compiz and Metacity, to honor the position of unmapped windows + if (wndconfig->xpos != GLFW_ANY_POSITION && wndconfig->ypos != GLFW_ANY_POSITION) + { + hints->flags |= PPosition; + hints->x = 0; + hints->y = 0; + } + + hints->flags |= PWinGravity; + hints->win_gravity = StaticGravity; + + XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); + XFree(hints); + } // Set ICCCM WM_CLASS property { @@ -777,9 +779,9 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, if (_glfw.x11.im) _glfwCreateInputContextX11(window); - _glfwPlatformSetWindowTitle(window, wndconfig->title); - _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); - _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); + _glfwSetWindowTitleX11(window, wndconfig->title); + _glfwGetWindowPosX11(window, &window->x11.xpos, &window->x11.ypos); + _glfwGetWindowSizeX11(window, &window->x11.width, &window->x11.height); return GLFW_TRUE; } @@ -788,7 +790,6 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, // static Atom writeTargetToProperty(const XSelectionRequestEvent* request) { - int i; char* selectionString = NULL; const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING }; const int formatCount = sizeof(formats) / sizeof(formats[0]); @@ -831,14 +832,13 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) // Multiple conversions were requested Atom* targets; - unsigned long i, count; + const unsigned long count = + _glfwGetWindowPropertyX11(request->requestor, + request->property, + _glfw.x11.ATOM_PAIR, + (unsigned char**) &targets); - count = _glfwGetWindowPropertyX11(request->requestor, - request->property, - _glfw.x11.ATOM_PAIR, - (unsigned char**) &targets); - - for (i = 0; i < count; i += 2) + for (unsigned long i = 0; i < count; i += 2) { int j; @@ -896,7 +896,7 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) // Conversion to a data target was requested - for (i = 0; i < formatCount; i++) + for (int i = 0; i < formatCount; i++) { if (request->target == formats[i]) { @@ -920,20 +920,6 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) return None; } -static void handleSelectionClear(XEvent* event) -{ - if (event->xselectionclear.selection == _glfw.x11.PRIMARY) - { - free(_glfw.x11.primarySelectionString); - _glfw.x11.primarySelectionString = NULL; - } - else - { - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = NULL; - } -} - static void handleSelectionRequest(XEvent* event) { const XSelectionRequestEvent* request = &event->xselectionrequest; @@ -968,7 +954,7 @@ static const char* getSelectionString(Atom selection) return *selectionString; } - free(*selectionString); + _glfw_free(*selectionString); *selectionString = NULL; for (size_t i = 0; i < targetCount; i++) @@ -991,7 +977,7 @@ static const char* getSelectionString(Atom selection) SelectionNotify, ¬ification)) { - waitForEvent(NULL); + waitForX11Event(NULL); } if (notification.xselection.property == None) @@ -1027,7 +1013,7 @@ static const char* getSelectionString(Atom selection) isSelPropNewValueNotify, (XPointer) ¬ification)) { - waitForEvent(NULL); + waitForX11Event(NULL); } XFree(data); @@ -1047,20 +1033,23 @@ static const char* getSelectionString(Atom selection) if (itemCount) { size += itemCount; - string = realloc(string, size); + string = _glfw_realloc(string, size); string[size - itemCount - 1] = '\0'; strcat(string, data); } if (!itemCount) { - if (targets[i] == XA_STRING) + if (string) { - *selectionString = convertLatin1toUTF8(string); - free(string); + if (targets[i] == XA_STRING) + { + *selectionString = convertLatin1toUTF8(string); + _glfw_free(string); + } + else + *selectionString = string; } - else - *selectionString = string; break; } @@ -1118,8 +1107,8 @@ static void acquireMonitor(_GLFWwindow* window) GLFWvidmode mode; // Manually position the window over its monitor - _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); - _glfwPlatformGetVideoMode(window->monitor, &mode); + _glfwGetMonitorPosX11(window->monitor, &xpos, &ypos); + _glfwGetVideoModeX11(window->monitor, &mode); XMoveResizeWindow(_glfw.x11.display, window->x11.handle, xpos, ypos, mode.width, mode.height); @@ -1226,12 +1215,7 @@ static void processEvent(XEvent *event) return; } - if (event->type == SelectionClear) - { - handleSelectionClear(event); - return; - } - else if (event->type == SelectionRequest) + if (event->type == SelectionRequest) { handleSelectionRequest(event); return; @@ -1271,7 +1255,7 @@ static void processEvent(XEvent *event) // (the server never sends a timestamp of zero) // NOTE: Timestamp difference is compared to handle wrap-around Time diff = event->xkey.time - window->x11.keyPressTimes[keycode]; - if (diff == event->xkey.time || (diff > 0 && diff < (1 << 31))) + if (diff == event->xkey.time || (diff > 0 && diff < ((Time)1 << 31))) { if (keycode) _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); @@ -1293,7 +1277,7 @@ static void processEvent(XEvent *event) if (status == XBufferOverflow) { - chars = calloc(count + 1, 1); + chars = _glfw_calloc(count + 1, 1); count = Xutf8LookupString(window->x11.ic, &event->xkey, chars, count, @@ -1309,7 +1293,7 @@ static void processEvent(XEvent *event) } if (chars != buffer) - free(chars); + _glfw_free(chars); } } else @@ -1319,9 +1303,9 @@ static void processEvent(XEvent *event) _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); - const long character = _glfwKeySym2Unicode(keysym); - if (character != -1) - _glfwInputChar(window, character, mods, plain); + const uint32_t codepoint = _glfwKeySym2Unicode(keysym); + if (codepoint != GLFW_INVALID_CODEPOINT) + _glfwInputChar(window, codepoint, mods, plain); } return; @@ -1588,7 +1572,7 @@ static void processEvent(XEvent *event) else if (event->xclient.message_type == _glfw.x11.XdndEnter) { // A drag operation has entered the window - unsigned long i, count; + unsigned long count; Atom* formats = NULL; const GLFWbool list = event->xclient.data.l[1] & 1; @@ -1612,7 +1596,7 @@ static void processEvent(XEvent *event) formats = (Atom*) event->xclient.data.l + 2; } - for (i = 0; i < count; i++) + for (unsigned int i = 0; i < count; i++) { if (formats[i] == _glfw.x11.text_uri_list) { @@ -1718,14 +1702,14 @@ static void processEvent(XEvent *event) if (result) { - int i, count; - char** paths = parseUriList(data, &count); + int count; + char** paths = _glfwParseUriList(data, &count); _glfwInputDrop(window, count, (const char**) paths); - for (i = 0; i < count; i++) - free(paths[i]); - free(paths); + for (int i = 0; i < count; i++) + _glfw_free(paths[i]); + _glfw_free(paths); } if (data) @@ -1762,6 +1746,8 @@ static void processEvent(XEvent *event) if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); if (window->x11.ic) XSetICFocus(window->x11.ic); @@ -1782,12 +1768,14 @@ static void processEvent(XEvent *event) if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + releaseCursor(); if (window->x11.ic) XUnsetICFocus(window->x11.ic); if (window->monitor && window->autoIconify) - _glfwPlatformIconifyWindow(window); + _glfwIconifyWindowX11(window); _glfwInputWindowFocus(window, GLFW_FALSE); return; @@ -1827,7 +1815,7 @@ static void processEvent(XEvent *event) } else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) { - const GLFWbool maximized = _glfwPlatformWindowMaximized(window); + const GLFWbool maximized = _glfwWindowMaximizedX11(window); if (window->x11.maximized != maximized) { window->x11.maximized = maximized; @@ -1908,10 +1896,6 @@ void _glfwPushSelectionToManagerX11(void) handleSelectionRequest(&event); break; - case SelectionClear: - handleSelectionClear(&event); - break; - case SelectionNotify: { if (event.xselection.target == _glfw.x11.SAVE_TARGETS) @@ -1929,7 +1913,7 @@ void _glfwPushSelectionToManagerX11(void) } } - waitForEvent(NULL); + waitForX11Event(NULL); } } @@ -1970,7 +1954,7 @@ void _glfwCreateInputContextX11(_GLFWwindow* window) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformCreateWindow(_GLFWwindow* window, +GLFWbool _glfwCreateWindowX11(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) @@ -2027,23 +2011,41 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + + if (!_glfwRefreshContextAttribs(window, ctxconfig)) + return GLFW_FALSE; } + if (wndconfig->mousePassthrough) + _glfwSetWindowMousePassthroughX11(window, GLFW_TRUE); + if (window->monitor) { - _glfwPlatformShowWindow(window); + _glfwShowWindowX11(window); updateWindowMode(window); acquireMonitor(window); + + if (wndconfig->centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig->visible) + { + _glfwShowWindowX11(window); + if (wndconfig->focused) + _glfwFocusWindowX11(window); + } } XFlush(_glfw.x11.display); return GLFW_TRUE; } -void _glfwPlatformDestroyWindow(_GLFWwindow* window) +void _glfwDestroyWindowX11(_GLFWwindow* window) { if (_glfw.x11.disabledCursorWindow == window) - _glfw.x11.disabledCursorWindow = NULL; + enableCursor(window); if (window->monitor) releaseMonitor(window); @@ -2074,7 +2076,7 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +void _glfwSetWindowTitleX11(_GLFWwindow* window, const char* title) { if (_glfw.x11.xlib.utf8) { @@ -2098,33 +2100,38 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) XFlush(_glfw.x11.display); } -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) +void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* images) { if (count) { - int i, j, longCount = 0; + int longCount = 0; - for (i = 0; i < count; i++) + for (int i = 0; i < count; i++) longCount += 2 + images[i].width * images[i].height; - long* icon = calloc(longCount, sizeof(long)); - long* target = icon; + unsigned long* icon = _glfw_calloc(longCount, sizeof(unsigned long)); + unsigned long* target = icon; - for (i = 0; i < count; i++) + for (int i = 0; i < count; i++) { *target++ = images[i].width; *target++ = images[i].height; - for (j = 0; j < images[i].width * images[i].height; j++) + for (int j = 0; j < images[i].width * images[i].height; j++) { - *target++ = (images[i].pixels[j * 4 + 0] << 16) | - (images[i].pixels[j * 4 + 1] << 8) | - (images[i].pixels[j * 4 + 2] << 0) | - (images[i].pixels[j * 4 + 3] << 24); + *target++ = (((unsigned long) images[i].pixels[j * 4 + 0]) << 16) | + (((unsigned long) images[i].pixels[j * 4 + 1]) << 8) | + (((unsigned long) images[i].pixels[j * 4 + 2]) << 0) | + (((unsigned long) images[i].pixels[j * 4 + 3]) << 24); } } + // NOTE: XChangeProperty expects 32-bit values like the image data above to be + // placed in the 32 least significant bits of individual longs. This is + // true even if long is 64-bit and a WM protocol calls for "packed" data. + // This is because of a historical mistake that then became part of the Xlib + // ABI. Xlib will pack these values into a regular array of 32-bit values + // before sending it over the wire. XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_ICON, XA_CARDINAL, 32, @@ -2132,7 +2139,7 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, (unsigned char*) icon, longCount); - free(icon); + _glfw_free(icon); } else { @@ -2143,7 +2150,7 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, XFlush(_glfw.x11.display); } -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos) { Window dummy; int x, y; @@ -2157,11 +2164,11 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) *ypos = y; } -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +void _glfwSetWindowPosX11(_GLFWwindow* window, int xpos, int ypos) { // HACK: Explicitly setting PPosition to any value causes some WMs, notably // Compiz and Metacity, to honor the position of unmapped windows - if (!_glfwPlatformWindowVisible(window)) + if (!_glfwWindowVisibleX11(window)) { long supplied; XSizeHints* hints = XAllocSizeHints(); @@ -2181,7 +2188,7 @@ void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) XFlush(_glfw.x11.display); } -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetWindowSizeX11(_GLFWwindow* window, int* width, int* height) { XWindowAttributes attribs; XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); @@ -2192,7 +2199,7 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) *height = attribs.height; } -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +void _glfwSetWindowSizeX11(_GLFWwindow* window, int width, int height) { if (window->monitor) { @@ -2210,32 +2217,32 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) XFlush(_glfw.x11.display); } -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) +void _glfwSetWindowSizeLimitsX11(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) { int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); + _glfwGetWindowSizeX11(window, &width, &height); updateNormalHints(window, width, height); XFlush(_glfw.x11.display); } -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +void _glfwSetWindowAspectRatioX11(_GLFWwindow* window, int numer, int denom) { int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); + _glfwGetWindowSizeX11(window, &width, &height); updateNormalHints(window, width, height); XFlush(_glfw.x11.display); } -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +void _glfwGetFramebufferSizeX11(_GLFWwindow* window, int* width, int* height) { - _glfwPlatformGetWindowSize(window, width, height); + _glfwGetWindowSizeX11(window, width, height); } -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) +void _glfwGetWindowFrameSizeX11(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) { long* extents = NULL; @@ -2245,7 +2252,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, if (_glfw.x11.NET_FRAME_EXTENTS == None) return; - if (!_glfwPlatformWindowVisible(window) && + if (!_glfwWindowVisibleX11(window) && _glfw.x11.NET_REQUEST_FRAME_EXTENTS) { XEvent event; @@ -2266,7 +2273,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, isFrameExtentsEvent, (XPointer) window)) { - if (!waitForEvent(&timeout)) + if (!waitForX11Event(&timeout)) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); @@ -2294,8 +2301,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, XFree(extents); } -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) +void _glfwGetWindowContentScaleX11(_GLFWwindow* window, float* xscale, float* yscale) { if (xscale) *xscale = _glfw.x11.contentScaleX; @@ -2303,7 +2309,7 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, *yscale = _glfw.x11.contentScaleY; } -void _glfwPlatformIconifyWindow(_GLFWwindow* window) +void _glfwIconifyWindowX11(_GLFWwindow* window) { if (window->x11.overrideRedirect) { @@ -2318,7 +2324,7 @@ void _glfwPlatformIconifyWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } -void _glfwPlatformRestoreWindow(_GLFWwindow* window) +void _glfwRestoreWindowX11(_GLFWwindow* window) { if (window->x11.overrideRedirect) { @@ -2329,12 +2335,12 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) return; } - if (_glfwPlatformWindowIconified(window)) + if (_glfwWindowIconifiedX11(window)) { XMapWindow(_glfw.x11.display, window->x11.handle); waitForVisibilityNotify(window); } - else if (_glfwPlatformWindowVisible(window)) + else if (_glfwWindowVisibleX11(window)) { if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && @@ -2352,7 +2358,7 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +void _glfwMaximizeWindowX11(_GLFWwindow* window) { if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || @@ -2361,7 +2367,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window) return; } - if (_glfwPlatformWindowVisible(window)) + if (_glfwWindowVisibleX11(window)) { sendEventToWM(window, _glfw.x11.NET_WM_STATE, @@ -2417,22 +2423,22 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } -void _glfwPlatformShowWindow(_GLFWwindow* window) +void _glfwShowWindowX11(_GLFWwindow* window) { - if (_glfwPlatformWindowVisible(window)) + if (_glfwWindowVisibleX11(window)) return; XMapWindow(_glfw.x11.display, window->x11.handle); waitForVisibilityNotify(window); } -void _glfwPlatformHideWindow(_GLFWwindow* window) +void _glfwHideWindowX11(_GLFWwindow* window) { XUnmapWindow(_glfw.x11.display, window->x11.handle); XFlush(_glfw.x11.display); } -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +void _glfwRequestWindowAttentionX11(_GLFWwindow* window) { if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION) return; @@ -2444,11 +2450,11 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) 0, 1, 0); } -void _glfwPlatformFocusWindow(_GLFWwindow* window) +void _glfwFocusWindowX11(_GLFWwindow* window) { if (_glfw.x11.NET_ACTIVE_WINDOW) sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); - else if (_glfwPlatformWindowVisible(window)) + else if (_glfwWindowVisibleX11(window)) { XRaiseWindow(_glfw.x11.display, window->x11.handle); XSetInputFocus(_glfw.x11.display, window->x11.handle, @@ -2458,11 +2464,11 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) +void _glfwSetWindowMonitorX11(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) { if (window->monitor == monitor) { @@ -2486,8 +2492,8 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) { - _glfwPlatformSetWindowDecorated(window, window->decorated); - _glfwPlatformSetWindowFloating(window, window->floating); + _glfwSetWindowDecoratedX11(window, window->decorated); + _glfwSetWindowFloatingX11(window, window->floating); releaseMonitor(window); } @@ -2496,7 +2502,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) { - if (!_glfwPlatformWindowVisible(window)) + if (!_glfwWindowVisibleX11(window)) { XMapRaised(_glfw.x11.display, window->x11.handle); waitForVisibilityNotify(window); @@ -2515,7 +2521,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, XFlush(_glfw.x11.display); } -int _glfwPlatformWindowFocused(_GLFWwindow* window) +GLFWbool _glfwWindowFocusedX11(_GLFWwindow* window) { Window focused; int state; @@ -2524,22 +2530,21 @@ int _glfwPlatformWindowFocused(_GLFWwindow* window) return window->x11.handle == focused; } -int _glfwPlatformWindowIconified(_GLFWwindow* window) +GLFWbool _glfwWindowIconifiedX11(_GLFWwindow* window) { return getWindowState(window) == IconicState; } -int _glfwPlatformWindowVisible(_GLFWwindow* window) +GLFWbool _glfwWindowVisibleX11(_GLFWwindow* window) { XWindowAttributes wa; XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa); return wa.map_state == IsViewable; } -int _glfwPlatformWindowMaximized(_GLFWwindow* window) +GLFWbool _glfwWindowMaximizedX11(_GLFWwindow* window) { Atom* states; - unsigned long i; GLFWbool maximized = GLFW_FALSE; if (!_glfw.x11.NET_WM_STATE || @@ -2555,7 +2560,7 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) XA_ATOM, (unsigned char**) &states); - for (i = 0; i < count; i++) + for (unsigned long i = 0; i < count; i++) { if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) @@ -2571,7 +2576,7 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return maximized; } -int _glfwPlatformWindowHovered(_GLFWwindow* window) +GLFWbool _glfwWindowHoveredX11(_GLFWwindow* window) { Window w = _glfw.x11.root; while (w) @@ -2599,7 +2604,7 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window) return GLFW_FALSE; } -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +GLFWbool _glfwFramebufferTransparentX11(_GLFWwindow* window) { if (!window->x11.transparent) return GLFW_FALSE; @@ -2607,14 +2612,14 @@ int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None; } -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowResizableX11(_GLFWwindow* window, GLFWbool enabled) { int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); + _glfwGetWindowSizeX11(window, &width, &height); updateNormalHints(window, width, height); } -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowDecoratedX11(_GLFWwindow* window, GLFWbool enabled) { struct { @@ -2636,12 +2641,12 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) sizeof(hints) / sizeof(long)); } -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowFloatingX11(_GLFWwindow* window, GLFWbool enabled) { if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) return; - if (_glfwPlatformWindowVisible(window)) + if (_glfwWindowVisibleX11(window)) { const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; sendEventToWM(window, @@ -2653,35 +2658,19 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) else { Atom* states = NULL; - unsigned long i, count; - - count = _glfwGetWindowPropertyX11(window->x11.handle, - _glfw.x11.NET_WM_STATE, - XA_ATOM, - (unsigned char**) &states); + const unsigned long count = + _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); // NOTE: We don't check for failure as this property may not exist yet // and that's fine (and we'll create it implicitly with append) if (enabled) { - for (i = 0; i < count; i++) - { - if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) - break; - } + unsigned long i; - if (i < count) - return; - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeAppend, - (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, - 1); - } - else if (states) - { for (i = 0; i < count; i++) { if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) @@ -2689,14 +2678,27 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) } if (i == count) - return; - - states[i] = states[count - 1]; - count--; - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) states, count); + { + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, + 1); + } + } + else if (states) + { + for (unsigned long i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + { + states[i] = states[count - 1]; + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) states, count - 1); + break; + } + } } if (states) @@ -2706,7 +2708,7 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) XFlush(_glfw.x11.display); } -void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled) +void _glfwSetWindowMousePassthroughX11(_GLFWwindow* window, GLFWbool enabled) { if (!_glfw.x11.xshape.available) return; @@ -2725,7 +2727,7 @@ void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enable } } -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +float _glfwGetWindowOpacityX11(_GLFWwindow* window) { float opacity = 1.f; @@ -2748,7 +2750,7 @@ float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) return opacity; } -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +void _glfwSetWindowOpacityX11(_GLFWwindow* window, float opacity) { const CARD32 value = (CARD32) (0xffffffffu * (double) opacity); XChangeProperty(_glfw.x11.display, window->x11.handle, @@ -2756,7 +2758,7 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) PropModeReplace, (unsigned char*) &value, 1); } -void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +void _glfwSetRawMouseMotionX11(_GLFWwindow *window, GLFWbool enabled) { if (!_glfw.x11.xi.available) return; @@ -2770,14 +2772,14 @@ void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) disableRawMouseMotion(window); } -GLFWbool _glfwPlatformRawMouseMotionSupported(void) +GLFWbool _glfwRawMouseMotionSupportedX11(void) { return _glfw.x11.xi.available; } -void _glfwPlatformPollEvents(void) +void _glfwPollEventsX11(void) { - _GLFWwindow* window; + drainEmptyEvents(); #if defined(__linux__) if (_glfw.joysticksInitialized) @@ -2792,55 +2794,42 @@ void _glfwPlatformPollEvents(void) processEvent(&event); } - window = _glfw.x11.disabledCursorWindow; + _GLFWwindow* window = _glfw.x11.disabledCursorWindow; if (window) { int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); + _glfwGetWindowSizeX11(window, &width, &height); // NOTE: Re-center the cursor only if it has moved since the last call, // to avoid breaking glfwWaitEvents with MotionNotify if (window->x11.lastCursorPosX != width / 2 || window->x11.lastCursorPosY != height / 2) { - _glfwPlatformSetCursorPos(window, width / 2, height / 2); + _glfwSetCursorPosX11(window, width / 2, height / 2); } } XFlush(_glfw.x11.display); } -void _glfwPlatformWaitEvents(void) +void _glfwWaitEventsX11(void) { - while (!XPending(_glfw.x11.display)) - waitForEvent(NULL); - - _glfwPlatformPollEvents(); + waitForAnyEvent(NULL); + _glfwPollEventsX11(); } -void _glfwPlatformWaitEventsTimeout(double timeout) +void _glfwWaitEventsTimeoutX11(double timeout) { - while (!XPending(_glfw.x11.display)) - { - if (!waitForEvent(&timeout)) - break; - } - - _glfwPlatformPollEvents(); + waitForAnyEvent(&timeout); + _glfwPollEventsX11(); } -void _glfwPlatformPostEmptyEvent(void) +void _glfwPostEmptyEventX11(void) { - XEvent event = { ClientMessage }; - event.xclient.window = _glfw.x11.helperWindowHandle; - event.xclient.format = 32; // Data is 32-bit longs - event.xclient.message_type = _glfw.x11.NULL_; - - XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); - XFlush(_glfw.x11.display); + writeEmptyEvent(); } -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +void _glfwGetCursorPosX11(_GLFWwindow* window, double* xpos, double* ypos) { Window root, child; int rootX, rootY, childX, childY; @@ -2857,7 +2846,7 @@ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) *ypos = childY; } -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +void _glfwSetCursorPosX11(_GLFWwindow* window, double x, double y) { // Store the new position so it can be recognized later window->x11.warpCursorPosX = (int) x; @@ -2868,22 +2857,46 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) XFlush(_glfw.x11.display); } -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +void _glfwSetCursorModeX11(_GLFWwindow* window, int mode) { - if (mode == GLFW_CURSOR_DISABLED) + if (_glfwWindowFocusedX11(window)) { - if (_glfwPlatformWindowFocused(window)) - disableCursor(window); - } - else if (_glfw.x11.disabledCursorWindow == window) - enableCursor(window); - else - updateCursorImage(window); + if (mode == GLFW_CURSOR_DISABLED) + { + _glfwGetCursorPosX11(window, + &_glfw.x11.restoreCursorPosX, + &_glfw.x11.restoreCursorPosY); + _glfwCenterCursorInContentArea(window); + if (window->rawMouseMotion) + enableRawMouseMotion(window); + } + else if (_glfw.x11.disabledCursorWindow == window) + { + if (window->rawMouseMotion) + disableRawMouseMotion(window); + } + if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED) + captureCursor(window); + else + releaseCursor(); + + if (mode == GLFW_CURSOR_DISABLED) + _glfw.x11.disabledCursorWindow = window; + else if (_glfw.x11.disabledCursorWindow == window) + { + _glfw.x11.disabledCursorWindow = NULL; + _glfwSetCursorPosX11(window, + _glfw.x11.restoreCursorPosX, + _glfw.x11.restoreCursorPosY); + } + } + + updateCursorImage(window); XFlush(_glfw.x11.display); } -const char* _glfwPlatformGetScancodeName(int scancode) +const char* _glfwGetScancodeNameX11(int scancode) { if (!_glfw.x11.xkb.available) return NULL; @@ -2891,7 +2904,7 @@ const char* _glfwPlatformGetScancodeName(int scancode) if (scancode < 0 || scancode > 0xff || _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN) { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; } @@ -2901,11 +2914,11 @@ const char* _glfwPlatformGetScancodeName(int scancode) if (keysym == NoSymbol) return NULL; - const long ch = _glfwKeySym2Unicode(keysym); - if (ch == -1) + const uint32_t codepoint = _glfwKeySym2Unicode(keysym); + if (codepoint == GLFW_INVALID_CODEPOINT) return NULL; - const size_t count = encodeUTF8(_glfw.x11.keynames[key], (unsigned int) ch); + const size_t count = _glfwEncodeUTF8(_glfw.x11.keynames[key], codepoint); if (count == 0) return NULL; @@ -2913,23 +2926,23 @@ const char* _glfwPlatformGetScancodeName(int scancode) return _glfw.x11.keynames[key]; } -int _glfwPlatformGetKeyScancode(int key) +int _glfwGetKeyScancodeX11(int key) { return _glfw.x11.scancodes[key]; } -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, +GLFWbool _glfwCreateCursorX11(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { - cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot); + cursor->x11.handle = _glfwCreateNativeCursorX11(image, xhot, yhot); if (!cursor->x11.handle) return GLFW_FALSE; return GLFW_TRUE; } -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +GLFWbool _glfwCreateStandardCursorX11(_GLFWcursor* cursor, int shape) { if (_glfw.x11.xcursor.handle) { @@ -3027,25 +3040,26 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) return GLFW_TRUE; } -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +void _glfwDestroyCursorX11(_GLFWcursor* cursor) { if (cursor->x11.handle) XFreeCursor(_glfw.x11.display, cursor->x11.handle); } -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +void _glfwSetCursorX11(_GLFWwindow* window, _GLFWcursor* cursor) { - if (window->cursorMode == GLFW_CURSOR_NORMAL) + if (window->cursorMode == GLFW_CURSOR_NORMAL || + window->cursorMode == GLFW_CURSOR_CAPTURED) { updateCursorImage(window); XFlush(_glfw.x11.display); } } -void _glfwPlatformSetClipboardString(const char* string) +void _glfwSetClipboardStringX11(const char* string) { char* copy = _glfw_strdup(string); - free(_glfw.x11.clipboardString); + _glfw_free(_glfw.x11.clipboardString); _glfw.x11.clipboardString = copy; XSetSelectionOwner(_glfw.x11.display, @@ -3061,12 +3075,12 @@ void _glfwPlatformSetClipboardString(const char* string) } } -const char* _glfwPlatformGetClipboardString(void) +const char* _glfwGetClipboardStringX11(void) { return getSelectionString(_glfw.x11.CLIPBOARD); } -EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) +EGLenum _glfwGetEGLPlatformX11(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) { @@ -3086,7 +3100,7 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) if (type) { - *attribs = calloc(5, sizeof(EGLint)); + *attribs = _glfw_calloc(5, sizeof(EGLint)); (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE; (*attribs)[1] = type; (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE; @@ -3102,12 +3116,12 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) return 0; } -EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void) +EGLNativeDisplayType _glfwGetEGLNativeDisplayX11(void) { return _glfw.x11.display; } -EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window) +EGLNativeWindowType _glfwGetEGLNativeWindowX11(_GLFWwindow* window) { if (_glfw.egl.platform) return &window->x11.handle; @@ -3115,7 +3129,7 @@ EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window) return (EGLNativeWindowType) window->x11.handle; } -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +void _glfwGetRequiredInstanceExtensionsX11(char** extensions) { if (!_glfw.vk.KHR_surface) return; @@ -3136,7 +3150,7 @@ void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) extensions[1] = "VK_KHR_xlib_surface"; } -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, +GLFWbool _glfwGetPhysicalDevicePresentationSupportX11(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { @@ -3189,10 +3203,10 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, } } -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) +VkResult _glfwCreateWindowSurfaceX11(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) { if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) { @@ -3272,6 +3286,13 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, GLFWAPI Display* glfwGetX11Display(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (_glfw.platform.platformID != GLFW_PLATFORM_X11) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized"); + return NULL; + } + return _glfw.x11.display; } @@ -3279,6 +3300,13 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(None); + + if (_glfw.platform.platformID != GLFW_PLATFORM_X11) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized"); + return None; + } + return window->x11.handle; } @@ -3286,7 +3314,13 @@ GLFWAPI void glfwSetX11SelectionString(const char* string) { _GLFW_REQUIRE_INIT(); - free(_glfw.x11.primarySelectionString); + if (_glfw.platform.platformID != GLFW_PLATFORM_X11) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized"); + return; + } + + _glfw_free(_glfw.x11.primarySelectionString); _glfw.x11.primarySelectionString = _glfw_strdup(string); XSetSelectionOwner(_glfw.x11.display, @@ -3305,6 +3339,13 @@ GLFWAPI void glfwSetX11SelectionString(const char* string) GLFWAPI const char* glfwGetX11SelectionString(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (_glfw.platform.platformID != GLFW_PLATFORM_X11) + { + _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized"); + return NULL; + } + return getSelectionString(_glfw.x11.PRIMARY); } diff --git a/src/external/glfw/src/xkb_unicode.c b/src/external/glfw/src/xkb_unicode.c index 2772ea098..1b2482cdf 100644 --- a/src/external/glfw/src/xkb_unicode.c +++ b/src/external/glfw/src/xkb_unicode.c @@ -907,7 +907,7 @@ static const struct codepair { // Convert XKB KeySym to Unicode // -long _glfwKeySym2Unicode(unsigned int keysym) +uint32_t _glfwKeySym2Unicode(unsigned int keysym) { int min = 0; int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; @@ -937,6 +937,6 @@ long _glfwKeySym2Unicode(unsigned int keysym) } // No matching Unicode value found - return -1; + return GLFW_INVALID_CODEPOINT; } diff --git a/src/external/glfw/src/xkb_unicode.h b/src/external/glfw/src/xkb_unicode.h index 76d83ffd1..b07408f61 100644 --- a/src/external/glfw/src/xkb_unicode.h +++ b/src/external/glfw/src/xkb_unicode.h @@ -24,5 +24,7 @@ // //======================================================================== -long _glfwKeySym2Unicode(unsigned int keysym); +#define GLFW_INVALID_CODEPOINT 0xffffffffu + +uint32_t _glfwKeySym2Unicode(unsigned int keysym); diff --git a/src/rglfw.c b/src/rglfw.c index cbb19ad71..83450b2db 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -58,69 +58,77 @@ #endif // Common modules to all platforms -#include "external/glfw/src/context.c" #include "external/glfw/src/init.c" -#include "external/glfw/src/input.c" +#include "external/glfw/src/platform.c" +#include "external/glfw/src/context.c" #include "external/glfw/src/monitor.c" -#include "external/glfw/src/vulkan.c" #include "external/glfw/src/window.c" +#include "external/glfw/src/input.c" +#include "external/glfw/src/vulkan.c" #if defined(_WIN32) || defined(__CYGWIN__) #include "external/glfw/src/win32_init.c" - #include "external/glfw/src/win32_joystick.c" + #include "external/glfw/src/win32_module.c" #include "external/glfw/src/win32_monitor.c" + #include "external/glfw/src/win32_window.c" + #include "external/glfw/src/win32_joystick.c" #include "external/glfw/src/win32_time.c" #include "external/glfw/src/win32_thread.c" - #include "external/glfw/src/win32_window.c" #include "external/glfw/src/wgl_context.c" + #include "external/glfw/src/egl_context.c" #include "external/glfw/src/osmesa_context.c" #endif #if defined(__linux__) + #include "external/glfw/src/posix_module.c" + #include "external/glfw/src/posix_thread.c" + #include "external/glfw/src/posix_time.c" + #include "external/glfw/src/linux_joystick.c" + #include "external/glfw/src/xkb_unicode.c" + + #include "external/glfw/src/egl_context.c" + #include "external/glfw/src/osmesa_context.c" + #if defined(_GLFW_WAYLAND) #include "external/glfw/src/wl_init.c" #include "external/glfw/src/wl_monitor.c" #include "external/glfw/src/wl_window.c" - #include "external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c" - #include "external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c" - #endif + #endif #if defined(_GLFW_X11) #include "external/glfw/src/x11_init.c" #include "external/glfw/src/x11_monitor.c" #include "external/glfw/src/x11_window.c" #include "external/glfw/src/glx_context.c" #endif - - #include "external/glfw/src/linux_joystick.c" - #include "external/glfw/src/posix_thread.c" - #include "external/glfw/src/posix_time.c" - #include "external/glfw/src/xkb_unicode.c" - #include "external/glfw/src/egl_context.c" - #include "external/glfw/src/osmesa_context.c" #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__) || defined(__DragonFly__) + #include "external/glfw/src/posix_module.c" + #include "external/glfw/src/posix_thread.c" + #include "external/glfw/src/posix_time.c" + #include "external/glfw/src/null_joystick.c" + #include "external/glfw/src/xkb_unicode.c" + #include "external/glfw/src/x11_init.c" #include "external/glfw/src/x11_monitor.c" #include "external/glfw/src/x11_window.c" - #include "external/glfw/src/xkb_unicode.c" - #include "external/glfw/src/null_joystick.c" - #include "external/glfw/src/posix_time.c" - #include "external/glfw/src/posix_thread.c" #include "external/glfw/src/glx_context.c" + #include "external/glfw/src/egl_context.c" #include "external/glfw/src/osmesa_context.c" #endif #if defined(__APPLE__) + #include "external/glfw/src/posix_module.c" + #include "external/glfw/src/posix_thread.c" #include "external/glfw/src/cocoa_init.m" #include "external/glfw/src/cocoa_joystick.m" #include "external/glfw/src/cocoa_monitor.m" #include "external/glfw/src/cocoa_window.m" #include "external/glfw/src/cocoa_time.c" - #include "external/glfw/src/posix_thread.c" #include "external/glfw/src/nsgl_context.m" + #include "external/glfw/src/egl_context.c" #include "external/glfw/src/osmesa_context.c" #endif From 2236197d492edfb4cf1b211bf68837dfb3ac7058 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Aug 2022 15:20:16 +0200 Subject: [PATCH 0019/1710] Update rglfw.c --- src/rglfw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rglfw.c b/src/rglfw.c index 83450b2db..61922604c 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -84,6 +84,7 @@ #include "external/glfw/src/posix_module.c" #include "external/glfw/src/posix_thread.c" #include "external/glfw/src/posix_time.c" + #include "external/glfw/src/posix_poll.c" #include "external/glfw/src/linux_joystick.c" #include "external/glfw/src/xkb_unicode.c" From 10ae54379af2d1e251b1c16170689afc274a8b8b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Aug 2022 15:25:34 +0200 Subject: [PATCH 0020/1710] Update posix_poll.c --- src/external/glfw/src/posix_poll.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/external/glfw/src/posix_poll.c b/src/external/glfw/src/posix_poll.c index 676a8a510..f96f14106 100644 --- a/src/external/glfw/src/posix_poll.c +++ b/src/external/glfw/src/posix_poll.c @@ -29,6 +29,7 @@ #define _GNU_SOURCE #include "internal.h" +#include "posix_poll.h" #include #include From 482dbfc52ea57b9241677c8a2e103526f0be926f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Aug 2022 15:28:57 +0200 Subject: [PATCH 0021/1710] Avoid error on `implicit-function-declaration` --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index ea3376046..df8f9d0fd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -348,7 +348,7 @@ endif # -Wstrict-prototypes warn if a function is declared or defined without specifying the argument types # -Werror=implicit-function-declaration catch function calls without prior declaration ifeq ($(PLATFORM),PLATFORM_DESKTOP) - CFLAGS += -Werror=implicit-function-declaration + #CFLAGS += -Werror=implicit-function-declaration endif ifeq ($(PLATFORM),PLATFORM_WEB) # -Os # size optimization From 568fe42cb116bef9262066e9b8b7e2f462f8451a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Aug 2022 15:37:09 +0200 Subject: [PATCH 0022/1710] Reviewed GLFW issue with `ppoll()` function --- src/Makefile | 2 +- src/external/glfw/src/posix_poll.c | 2 +- src/external/glfw/src/posix_poll.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index df8f9d0fd..ea3376046 100644 --- a/src/Makefile +++ b/src/Makefile @@ -348,7 +348,7 @@ endif # -Wstrict-prototypes warn if a function is declared or defined without specifying the argument types # -Werror=implicit-function-declaration catch function calls without prior declaration ifeq ($(PLATFORM),PLATFORM_DESKTOP) - #CFLAGS += -Werror=implicit-function-declaration + CFLAGS += -Werror=implicit-function-declaration endif ifeq ($(PLATFORM),PLATFORM_WEB) # -Os # size optimization diff --git a/src/external/glfw/src/posix_poll.c b/src/external/glfw/src/posix_poll.c index f96f14106..d309292e1 100644 --- a/src/external/glfw/src/posix_poll.c +++ b/src/external/glfw/src/posix_poll.c @@ -29,7 +29,7 @@ #define _GNU_SOURCE #include "internal.h" -#include "posix_poll.h" +#include "posix_poll.h" // @raysan5: Required for poll(), ppoll() #include #include diff --git a/src/external/glfw/src/posix_poll.h b/src/external/glfw/src/posix_poll.h index 1effd1cd3..860f108da 100644 --- a/src/external/glfw/src/posix_poll.h +++ b/src/external/glfw/src/posix_poll.h @@ -26,6 +26,7 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== +#define _GNU_SOURCE // @raysan5: Required for ppoll() #include GLFWbool _glfwPollPOSIX(struct pollfd* fds, nfds_t count, double* timeout); From be2328f848d96a788f9f40f1ed302c64335d47df Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Aug 2022 15:39:22 +0200 Subject: [PATCH 0023/1710] Update Makefile --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index ea3376046..a6f380b6f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -309,7 +309,7 @@ endif # -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec # -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers # -fno-strict-aliasing jar_xm.h does shady stuff (breaks strict aliasing) -CFLAGS = -Wall -D_DEFAULT_SOURCE -D$(PLATFORM) -D$(GRAPHICS) -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing $(CUSTOM_CFLAGS) +CFLAGS = -Wall -D_GNU_SOURCE -D$(PLATFORM) -D$(GRAPHICS) -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing $(CUSTOM_CFLAGS) ifneq ($(RAYLIB_CONFIG_FLAGS), NONE) CFLAGS += -DEXTERNAL_CONFIG_FLAGS $(RAYLIB_CONFIG_FLAGS) From 7f5567eec0f996fd849876c74508aa63b3c2e3fc Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Aug 2022 15:48:12 +0200 Subject: [PATCH 0024/1710] Reviewed GLFW compilation requirements on Linux: `_GNU_SOURCE` Reasons to NOT define `_GNU_SOURCE`: - access to lots of nonstandard GNU/Linux extension functions - access to traditional functions which were omitted from the POSIX standard (often for good reason, such as being replaced with better alternatives, or being tied to particular legacy implementations) - access to low-level functions that cannot be portable, but that you sometimes need for implementing system utilities like mount, ifconfig, etc. - broken behavior for lots of POSIX-specified functions, where the GNU folks disagreed with the standards committee on how the functions should behave and decided to do their own thing. --- src/Makefile | 1 + src/external/glfw/src/posix_poll.c | 1 - src/external/glfw/src/posix_poll.h | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index a6f380b6f..291508edc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -307,6 +307,7 @@ endif # -Wno-missing-braces ignore invalid warning (GCC bug 53119) # -Wno-unused-value ignore unused return values of some functions (i.e. fread()) # -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec +# -D_GNU_SOURCE access to lots of nonstandard GNU/Linux extension functions # -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers # -fno-strict-aliasing jar_xm.h does shady stuff (breaks strict aliasing) CFLAGS = -Wall -D_GNU_SOURCE -D$(PLATFORM) -D$(GRAPHICS) -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing $(CUSTOM_CFLAGS) diff --git a/src/external/glfw/src/posix_poll.c b/src/external/glfw/src/posix_poll.c index d309292e1..676a8a510 100644 --- a/src/external/glfw/src/posix_poll.c +++ b/src/external/glfw/src/posix_poll.c @@ -29,7 +29,6 @@ #define _GNU_SOURCE #include "internal.h" -#include "posix_poll.h" // @raysan5: Required for poll(), ppoll() #include #include diff --git a/src/external/glfw/src/posix_poll.h b/src/external/glfw/src/posix_poll.h index 860f108da..1effd1cd3 100644 --- a/src/external/glfw/src/posix_poll.h +++ b/src/external/glfw/src/posix_poll.h @@ -26,7 +26,6 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== -#define _GNU_SOURCE // @raysan5: Required for ppoll() #include GLFWbool _glfwPollPOSIX(struct pollfd* fds, nfds_t count, double* timeout); From d66692149bb0ba507be969971408fc82a6843194 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Aug 2022 18:04:47 +0200 Subject: [PATCH 0025/1710] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ee41bd53f..9db359194 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,9 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
-[![GitHub Contributors](https://img.shields.io/github/contributors/raysan5/raylib)](https://github.com/raysan5/raylib/graphs/contributors) [![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases) [![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers) -[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.0.0)](https://github.com/raysan5/raylib/commits/master) +[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.2.0)](https://github.com/raysan5/raylib/commits/master) [![Packaging Status](https://repology.org/badge/tiny-repos/raylib.svg)](https://repology.org/project/raylib/versions) [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE) From 4b76aa09dd342b32979df6a0926cf309c406532e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 29 Aug 2022 14:36:07 +0200 Subject: [PATCH 0026/1710] ADDED: `lighting.fs` for GLSL120 Fix #2651 --- .../resources/shaders/glsl120/lighting.fs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl120/lighting.fs diff --git a/examples/shaders/resources/shaders/glsl120/lighting.fs b/examples/shaders/resources/shaders/glsl120/lighting.fs new file mode 100644 index 000000000..d9cfb4472 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/lighting.fs @@ -0,0 +1,79 @@ +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec4 fragColor; +varying vec3 fragNormal; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// NOTE: Add here your custom variables + +#define MAX_LIGHTS 4 +#define LIGHT_DIRECTIONAL 0 +#define LIGHT_POINT 1 + +struct MaterialProperty { + vec3 color; + int useSampler; + sampler2D sampler; +}; + +struct Light { + int enabled; + int type; + vec3 position; + vec3 target; + vec4 color; +}; + +// Input lighting values +uniform Light lights[MAX_LIGHTS]; +uniform vec4 ambient; +uniform vec3 viewPos; + +void main() +{ + // Texel color fetching from texture sampler + vec4 texelColor = texture2D(texture0, fragTexCoord); + vec3 lightDot = vec3(0.0); + vec3 normal = normalize(fragNormal); + vec3 viewD = normalize(viewPos - fragPosition); + vec3 specular = vec3(0.0); + + // NOTE: Implement here your fragment shader code + + for (int i = 0; i < MAX_LIGHTS; i++) + { + if (lights[i].enabled == 1) + { + vec3 light = vec3(0.0); + + if (lights[i].type == LIGHT_DIRECTIONAL) + { + light = -normalize(lights[i].target - lights[i].position); + } + + if (lights[i].type == LIGHT_POINT) + { + light = normalize(lights[i].position - fragPosition); + } + + float NdotL = max(dot(normal, light), 0.0); + lightDot += lights[i].color.rgb*NdotL; + + float specCo = 0.0; + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewD, reflect(-(light), normal))), 16.0); // 16 refers to shine + specular += specCo; + } + } + + vec4 finalColor = (texelColor*((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0))); + finalColor += texelColor*(ambient/10.0); + + // Gamma correction + gl_FragColor = pow(finalColor, vec4(1.0/2.2)); +} \ No newline at end of file From 0c7ba773ece7a3015dab0a4308829d1e428cdb71 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 10:14:45 +0200 Subject: [PATCH 0027/1710] Fixed issue with `LoadIQM()` #2676 --- src/rmodels.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rmodels.c b/src/rmodels.c index ec9e7adf8..d1ead9373 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4302,6 +4302,7 @@ static Model LoadIQM(const char *fileName) RL_FREE(blendi); RL_FREE(blendw); RL_FREE(ijoint); + RL_FREE(color); return model; } From 23cc39a265d42c693f08c23b54a043d1648b8266 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 10:27:16 +0200 Subject: [PATCH 0028/1710] Implemented latest .M3D improvements #2648 --- examples/models/models_loading_m3d.c | 27 +++++++++++++++++++------ examples/models/models_loading_m3d.png | Bin 0 -> 16992 bytes src/rmodels.c | 18 +++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 examples/models/models_loading_m3d.png diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index ca77d360a..f67ba5e57 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -18,13 +18,14 @@ #include "raylib.h" #include +#include //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ int main(int argc, char **argv) { - char *model_fn = argc > 1 ? argv[1] : "resources/models/m3d/suzanne.m3d"; + char *model_fn = argc > 1 ? argv[1] : "resources/models/m3d/seagull.m3d"; // Initialization //-------------------------------------------------------------------------------------- @@ -46,7 +47,7 @@ int main(int argc, char **argv) Model model = LoadModel(model_fn); // Load the animated model mesh and basic data // Load animation data - unsigned int animsCount = 0; + unsigned int animsCount = 0, animsSkel = 1, animsMesh = 1; ModelAnimation *anims = LoadModelAnimations(model_fn, &animsCount); int animFrameCounter = 0, animId = 0; @@ -71,6 +72,15 @@ int main(int argc, char **argv) animFrameCounter++; UpdateModelAnimation(model, anims[animId], animFrameCounter); if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0; + //printf("anim %u, frame %u / %u\n",animId,animFrameCounter,anims[animId].frameCount); + } + if (IsKeyDown(KEY_S)) + { + animsSkel ^= 1; + } + if (IsKeyDown(KEY_M)) + { + animsMesh ^= 1; } // Select animation on mouse click @@ -92,11 +102,16 @@ int main(int argc, char **argv) BeginMode3D(camera); - DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture - if(anims) + if (animsMesh) + DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture + + if (anims && animsSkel) for (int i = 0; i < model.boneCount; i++) { - DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED); + DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED); + if (anims[animId].bones[i].parent >= 0) + DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation, + anims[animId].framePoses[animFrameCounter][anims[animId].bones[i].parent].translation, RED); } DrawGrid(10, 1.0f); // Draw a grid @@ -105,7 +120,7 @@ int main(int argc, char **argv) DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 30, 10, MAROON); DrawText("MOUSE LEFT BUTTON to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 20, 10, DARKGRAY); - DrawText("(c) Suzanne 3D model by blender", screenWidth - 200, screenHeight - 20, 10, GRAY); + DrawText("(c) Seagull model by Scorched3D", screenWidth - 200, screenHeight - 20, 10, GRAY); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_m3d.png b/examples/models/models_loading_m3d.png new file mode 100644 index 0000000000000000000000000000000000000000..80c859fee02548c70a84f42fd75584cf341d36e5 GIT binary patch literal 16992 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 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); + } } } @@ -5463,6 +5472,15 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int animations[a].framePoses[i][j].rotation.w = m3d->vertex[pose[j].ori].w; animations[a].framePoses[i][j].rotation = QuaternionNormalize(animations[a].framePoses[i][j].rotation); animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f; + + // Child bones are stored in parent bone relative space, convert that into model space + if (animations[a].bones[j].parent >= 0) + { + animations[a].framePoses[i][j].rotation = QuaternionMultiply(animations[a].framePoses[i][animations[a].bones[j].parent].rotation, animations[a].framePoses[i][j].rotation); + animations[a].framePoses[i][j].translation = Vector3RotateByQuaternion(animations[a].framePoses[i][j].translation, animations[a].framePoses[i][animations[a].bones[j].parent].rotation); + animations[a].framePoses[i][j].translation = Vector3Add(animations[a].framePoses[i][j].translation, animations[a].framePoses[i][animations[a].bones[j].parent].translation); + animations[a].framePoses[i][j].scale = Vector3Multiply(animations[a].framePoses[i][j].scale, animations[a].framePoses[i][animations[a].bones[j].parent].scale); + } } RL_FREE(pose); } From bfab101ac2955865975780bcfd466ebf20dd0694 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 10:42:11 +0200 Subject: [PATCH 0029/1710] Update windows.yml --- .github/workflows/windows.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index fb216cbf4..b687c83ea 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -84,13 +84,15 @@ jobs: cd src set PATH=%PATH%;${{ matrix.COMPILER_PATH }} ${{ matrix.ARCH }}-w64-mingw32-gcc.exe --version - ${{ matrix.COMPILER_PATH }}/windres.exe --version + ${{ matrix.COMPILER_PATH }}\windres.exe --version + dir C:\msys64\mingw64\bin make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" - ${{ matrix.COMPILER_PATH }}/windres.exe -i raylib.dll.rc -o raylib.dll.rc.data -O coff --target=${{ matrix.WINDRES_ARCH }} + ${{ matrix.COMPILER_PATH }}\windres.exe -i raylib.dll.rc -o raylib.dll.rc.data -O coff --target=${{ matrix.WINDRES_ARCH }} make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. shell: cmd - if: matrix.compiler == 'mingw-w64' + # NOTE: The MinGW-w64 compiler provided by the host only supports 64bit compilation + if: matrix.compiler == 'mingw-w64' && bits == '64' - name: Build Library (MSVC16) run: | From cabaa53302f44462a265fd543da5223fa5dee293 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 10:44:25 +0200 Subject: [PATCH 0030/1710] Update windows.yml --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b687c83ea..46cf08244 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -79,6 +79,7 @@ jobs: uses: microsoft/setup-msbuild@v1.0.2 if: matrix.compiler == 'msvc16' + # NOTE: The MinGW-w64 compiler provided by the host only supports 64bit compilation - name: Build Library (MinGW-w64) run: | cd src @@ -91,7 +92,6 @@ jobs: make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. shell: cmd - # NOTE: The MinGW-w64 compiler provided by the host only supports 64bit compilation if: matrix.compiler == 'mingw-w64' && bits == '64' - name: Build Library (MSVC16) From 4938966e768acfdb4963e33e76ad26ba4298f218 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 10:45:30 +0200 Subject: [PATCH 0031/1710] Update windows.yml --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 46cf08244..4653b3a4e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -92,7 +92,7 @@ jobs: make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. shell: cmd - if: matrix.compiler == 'mingw-w64' && bits == '64' + if: matrix.compiler == 'mingw-w64' && matrix.bits == '64' - name: Build Library (MSVC16) run: | From bb4d9297b5118e4039bd08d41e51dee2ab18e312 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 10:52:03 +0200 Subject: [PATCH 0032/1710] Update windows.yml --- .github/workflows/windows.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4653b3a4e..3927a1b52 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -33,12 +33,10 @@ jobs: - compiler: mingw-w64 bits: 32 ARCH: "i686" - COMPILER_PATH: "C:\\msys64\\mingw32\\bin" WINDRES_ARCH: pe-i386 - compiler: mingw-w64 bits: 64 ARCH: "x86_64" - COMPILER_PATH: "C:\\msys64\\mingw64\\bin" WINDRES_ARCH: pe-x86-64 - compiler: msvc16 bits: 32 @@ -83,12 +81,11 @@ jobs: - name: Build Library (MinGW-w64) run: | cd src - set PATH=%PATH%;${{ matrix.COMPILER_PATH }} ${{ matrix.ARCH }}-w64-mingw32-gcc.exe --version - ${{ matrix.COMPILER_PATH }}\windres.exe --version + windres.exe --version dir C:\msys64\mingw64\bin make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" - ${{ matrix.COMPILER_PATH }}\windres.exe -i raylib.dll.rc -o raylib.dll.rc.data -O coff --target=${{ matrix.WINDRES_ARCH }} + windres.exe -i raylib.dll.rc -o raylib.dll.rc.data -O coff --target=${{ matrix.WINDRES_ARCH }} make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. shell: cmd From a598754b5bf4fbc5b606ba161fe243666ae4f758 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 11:04:10 +0200 Subject: [PATCH 0033/1710] Update windows.yml --- .github/workflows/windows.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3927a1b52..bdbe06887 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -77,7 +77,6 @@ jobs: uses: microsoft/setup-msbuild@v1.0.2 if: matrix.compiler == 'msvc16' - # NOTE: The MinGW-w64 compiler provided by the host only supports 64bit compilation - name: Build Library (MinGW-w64) run: | cd src @@ -89,7 +88,7 @@ jobs: make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. shell: cmd - if: matrix.compiler == 'mingw-w64' && matrix.bits == '64' + if: matrix.compiler == 'mingw-w64' - name: Build Library (MSVC16) run: | From 64cca24526d8d2f82e87039a1d492dcbe07b994d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 11:18:22 +0200 Subject: [PATCH 0034/1710] ADDED: `RL_TEXTURE_MIPMAP_BIAS_RATIO` support to `rlTextureParameters()` for OpenGL 3.3 #2674 --- src/rlgl.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index 123791a02..363b45bd3 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -243,6 +243,7 @@ #define RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST 0x2701 // GL_LINEAR_MIPMAP_NEAREST #define RL_TEXTURE_FILTER_MIP_LINEAR 0x2703 // GL_LINEAR_MIPMAP_LINEAR #define RL_TEXTURE_FILTER_ANISOTROPIC 0x3000 // Anisotropic filter (custom identifier) +#define RL_TEXTURE_MIPMAP_BIAS_RATIO 0x4000 // Texture mipmap bias (percentage ratio) #define RL_TEXTURE_WRAP_REPEAT 0x2901 // GL_REPEAT #define RL_TEXTURE_WRAP_CLAMP 0x812F // GL_CLAMP_TO_EDGE @@ -1551,6 +1552,9 @@ void rlTextureParameters(unsigned int id, int param, int value) else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); #endif } break; +#if defined(GRAPHICS_API_OPENGL_33) + case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, value/100.0f); +#endif default: break; } From fb1037a2417e516d4be02d962b113e5d55cd5855 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Sep 2022 20:46:06 +0200 Subject: [PATCH 0035/1710] ADDED: Complete support for M3D animations! #2648 --- examples/README.md | 255 +++++++++--------- examples/models/models_loading_m3d.c | 139 ++++++---- examples/models/models_loading_m3d.png | Bin 16992 -> 23682 bytes .../models/resources/models/m3d/CesiumMan.m3d | Bin 0 -> 73844 bytes .../models/resources/models/m3d/seagull.m3d | Bin 0 -> 10263 bytes .../models/resources/models/m3d/suzanne.m3d | Bin 0 -> 11645 bytes src/rmodels.c | 63 ++++- 7 files changed, 263 insertions(+), 194 deletions(-) create mode 100644 examples/models/resources/models/m3d/CesiumMan.m3d create mode 100644 examples/models/resources/models/m3d/seagull.m3d create mode 100644 examples/models/resources/models/m3d/suzanne.m3d diff --git a/examples/README.md b/examples/README.md index 158f81df4..7a3e72f87 100644 --- a/examples/README.md +++ b/examples/README.md @@ -24,36 +24,36 @@ Examples using raylib core platform functionality like window creation, inputs, | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 01 | [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 02 | [core_input_keys](core/core_input_keys.c) | core_input_keys | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 03 | [core_input_mouse](core/core_input_mouse.c) | core_input_mouse | ⭐️☆☆☆ | 1.0 | **4.0** | [Ray](https://github.com/raysan5) | -| 04 | [core_input_mouse_wheel](core/core_input_mouse_wheel.c) | core_input_mouse_wheel | ⭐️☆☆☆ | 1.1 | 1.3 | [Ray](https://github.com/raysan5) | -| 05 | [core_input_gamepad](core/core_input_gamepad.c) | core_input_gamepad | ⭐️☆☆☆ | 1.1 | **4.2** | [Ray](https://github.com/raysan5) | -| 06 | [core_input_multitouch](core/core_input_multitouch.c) | core_input_multitouch | ⭐️☆☆☆ | 2.1 | 2.5 | [Berni](https://github.com/Berni8k) | -| 07 | [core_input_gestures](core/core_input_gestures.c) | core_input_gestures | ⭐️⭐️☆☆ | 1.4 | **4.2** | [Ray](https://github.com/raysan5) | -| 08 | [core_2d_camera](core/core_2d_camera.c) | core_2d_camera | ⭐️⭐️☆☆ | 1.5 | 3.0 | [Ray](https://github.com/raysan5) | -| 09 | [core_2d_camera_mouse_zoom](core/core_2d_camera_mouse_zoom.c) | core_2d_camera_mouse_zoom | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Jeffery Myers](https://github.com/JeffM2501) | -| 10 | [core_2d_camera_platformer](core/core_2d_camera_platformer.c) | core_2d_camera_platformer | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [avyy](https://github.com/avyy) | -| 11 | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 12 | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 13 | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 14 | [core_3d_picking](core/core_3d_picking.c) | core_3d_picking | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 15 | [core_world_screen](core/core_world_screen.c) | core_world_screen | ⭐️⭐️☆☆ | 1.3 | 1.4 | [Ray](https://github.com/raysan5) | -| 16 | [core_custom_logging](core/core_custom_logging.c) | core_custom_logging | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Pablo Marcos Oltra](https://github.com/pamarcos) | -| 17 | [core_window_flags](core/core_window_flags.c) | core_window_flags | ⭐️⭐️⭐️☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 18 | [core_window_letterbox](core/core_window_letterbox.c) | core_window_letterbox | ⭐️⭐️☆☆ | 2.5 | **4.0** | [Anata](https://github.com/anatagawa) | -| 19 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️☆☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | -| 20 | [core_drop_files](core/core_drop_files.c) | core_drop_files | ⭐️⭐️☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 21 | [core_random_values](core/core_random_values.c) | core_random_values | ⭐️☆☆☆ | 1.1 | 1.1 | [Ray](https://github.com/raysan5) | -| 22 | [core_storage_values](core/core_storage_values.c) | core_storage_values | ⭐️⭐️☆☆ | 1.4 | **4.2** | [Ray](https://github.com/raysan5) | -| 23 | [core_vr_simulator](core/core_vr_simulator.c) | core_vr_simulator | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | -| 24 | [core_loading_thread](core/core_loading_thread.c) | core_loading_thread | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Ray](https://github.com/raysan5) | -| 25 | [core_scissor_test](core/core_scissor_test.c) | core_scissor_test | ⭐️☆☆☆ | 2.5 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | -| 26 | [core_basic_screen_manager](core/core_basic_screen_manager.c) | core_basic_screen_manager | ⭐️☆☆☆ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | -| 27 | [core_custom_frame_control](core/core_custom_frame_control.c) | core_custom_frame_control | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | -| 28 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | **4.0** | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | -| 29 | [core_split_screen](core/core_split_screen.c) | core_split_screen | ⭐️⭐️⭐️⭐️ | 3.7 | **4.0** | [Jeffery Myers](https://github.com/JeffM2501) | -| 30 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | +| 01 | [core_basic_window](core/core_basic_window.c) | core_basic_window | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 02 | [core_input_keys](core/core_input_keys.c) | core_input_keys | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 03 | [core_input_mouse](core/core_input_mouse.c) | core_input_mouse | ⭐️☆☆☆ | 1.0 | **4.0** | [Ray](https://github.com/raysan5) | +| 04 | [core_input_mouse_wheel](core/core_input_mouse_wheel.c) | core_input_mouse_wheel | ⭐️☆☆☆ | 1.1 | 1.3 | [Ray](https://github.com/raysan5) | +| 05 | [core_input_gamepad](core/core_input_gamepad.c) | core_input_gamepad | ⭐️☆☆☆ | 1.1 | **4.2** | [Ray](https://github.com/raysan5) | +| 06 | [core_input_multitouch](core/core_input_multitouch.c) | core_input_multitouch | ⭐️☆☆☆ | 2.1 | 2.5 | [Berni](https://github.com/Berni8k) | +| 07 | [core_input_gestures](core/core_input_gestures.c) | core_input_gestures | ⭐️⭐️☆☆ | 1.4 | **4.2** | [Ray](https://github.com/raysan5) | +| 08 | [core_2d_camera](core/core_2d_camera.c) | core_2d_camera | ⭐️⭐️☆☆ | 1.5 | 3.0 | [Ray](https://github.com/raysan5) | +| 09 | [core_2d_camera_mouse_zoom](core/core_2d_camera_mouse_zoom.c) | core_2d_camera_mouse_zoom | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Jeffery Myers](https://github.com/JeffM2501) | +| 10 | [core_2d_camera_platformer](core/core_2d_camera_platformer.c) | core_2d_camera_platformer | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [avyy](https://github.com/avyy) | +| 11 | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 12 | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 13 | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 14 | [core_3d_picking](core/core_3d_picking.c) | core_3d_picking | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 15 | [core_world_screen](core/core_world_screen.c) | core_world_screen | ⭐️⭐️☆☆ | 1.3 | 1.4 | [Ray](https://github.com/raysan5) | +| 16 | [core_custom_logging](core/core_custom_logging.c) | core_custom_logging | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Pablo Marcos Oltra](https://github.com/pamarcos) | +| 17 | [core_window_flags](core/core_window_flags.c) | core_window_flags | ⭐️⭐️⭐️☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 18 | [core_window_letterbox](core/core_window_letterbox.c) | core_window_letterbox | ⭐️⭐️☆☆ | 2.5 | **4.0** | [Anata](https://github.com/anatagawa) | +| 19 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️☆☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | +| 20 | [core_drop_files](core/core_drop_files.c) | core_drop_files | ⭐️⭐️☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 21 | [core_random_values](core/core_random_values.c) | core_random_values | ⭐️☆☆☆ | 1.1 | 1.1 | [Ray](https://github.com/raysan5) | +| 22 | [core_storage_values](core/core_storage_values.c) | core_storage_values | ⭐️⭐️☆☆ | 1.4 | **4.2** | [Ray](https://github.com/raysan5) | +| 23 | [core_vr_simulator](core/core_vr_simulator.c) | core_vr_simulator | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | +| 24 | [core_loading_thread](core/core_loading_thread.c) | core_loading_thread | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Ray](https://github.com/raysan5) | +| 25 | [core_scissor_test](core/core_scissor_test.c) | core_scissor_test | ⭐️☆☆☆ | 2.5 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | +| 26 | [core_basic_screen_manager](core/core_basic_screen_manager.c) | core_basic_screen_manager | ⭐️☆☆☆ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | +| 27 | [core_custom_frame_control](core/core_custom_frame_control.c) | core_custom_frame_control | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | +| 28 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | **4.0** | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | +| 29 | [core_split_screen](core/core_split_screen.c) | core_split_screen | ⭐️⭐️⭐️⭐️ | 3.7 | **4.0** | [Jeffery Myers](https://github.com/JeffM2501) | +| 30 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | ### category: shapes @@ -61,22 +61,22 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 31 | [shapes_basic_shapes](shapes/shapes_basic_shapes.c) | shapes_basic_shapes | ⭐️☆☆☆ | 1.0 | **4.0** | [Ray](https://github.com/raysan5) | -| 32 | [shapes_bouncing_ball](shapes/shapes_bouncing_ball.c) | shapes_bouncing_ball | ⭐️☆☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 33 | [shapes_colors_palette](shapes/shapes_colors_palette.c) | shapes_colors_palette | ⭐️⭐️☆☆ | 1.0 | 2.5 | [Ray](https://github.com/raysan5) | -| 34 | [shapes_logo_raylib](shapes/shapes_logo_raylib.c) | shapes_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 35 | [shapes_logo_raylib_anim](shapes/shapes_logo_raylib_anim.c) | shapes_logo_raylib_anim | ⭐️⭐️☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | -| 36 | [shapes_rectangle_scaling](shapes/shapes_rectangle_scaling.c) | shapes_rectangle_scaling | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | -| 37 | [shapes_lines_bezier](shapes/shapes_lines_bezier.c) | shapes_lines_bezier | ⭐️☆☆☆ | 1.7 | 1.7 | [Ray](https://github.com/raysan5) | -| 38 | [shapes_collision_area](shapes/shapes_collision_area.c) | shapes_collision_area | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 39 | [shapes_following_eyes](shapes/shapes_following_eyes.c) | shapes_following_eyes | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 40 | [shapes_easings_ball_anim](shapes/shapes_easings_ball_anim.c) | shapes_easings_ball_anim | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 41 | [shapes_easings_box_anim](shapes/shapes_easings_box_anim.c) | shapes_easings_box_anim | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 42 | [shapes_easings_rectangle_array](shapes/shapes_easings_rectangle_array.c) | shapes_easings_rectangle_array | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 43 | [shapes_draw_ring](shapes/shapes_draw_ring.c) | shapes_draw_ring | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | -| 44 | [shapes_draw_circle_sector](shapes/shapes_draw_circle_sector.c) | shapes_draw_circle_sector | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | -| 45 | [shapes_draw_rectangle_rounded](shapes/shapes_draw_rectangle_rounded.c) | shapes_draw_rectangle_rounded | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | -| 46 | [shapes_top_down_lights](shapes/shapes_top_down_lights.c) | shapes_top_down_lights | ⭐️⭐️⭐️⭐️ | **4.2** | **4.2** | [Jeffery Myers](https://github.com/JeffM2501) | +| 31 | [shapes_basic_shapes](shapes/shapes_basic_shapes.c) | shapes_basic_shapes | ⭐️☆☆☆ | 1.0 | **4.0** | [Ray](https://github.com/raysan5) | +| 32 | [shapes_bouncing_ball](shapes/shapes_bouncing_ball.c) | shapes_bouncing_ball | ⭐️☆☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 33 | [shapes_colors_palette](shapes/shapes_colors_palette.c) | shapes_colors_palette | ⭐️⭐️☆☆ | 1.0 | 2.5 | [Ray](https://github.com/raysan5) | +| 34 | [shapes_logo_raylib](shapes/shapes_logo_raylib.c) | shapes_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 35 | [shapes_logo_raylib_anim](shapes/shapes_logo_raylib_anim.c) | shapes_logo_raylib_anim | ⭐️⭐️☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | +| 36 | [shapes_rectangle_scaling](shapes/shapes_rectangle_scaling.c) | shapes_rectangle_scaling | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | +| 37 | [shapes_lines_bezier](shapes/shapes_lines_bezier.c) | shapes_lines_bezier | ⭐️☆☆☆ | 1.7 | 1.7 | [Ray](https://github.com/raysan5) | +| 38 | [shapes_collision_area](shapes/shapes_collision_area.c) | shapes_collision_area | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 39 | [shapes_following_eyes](shapes/shapes_following_eyes.c) | shapes_following_eyes | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 40 | [shapes_easings_ball_anim](shapes/shapes_easings_ball_anim.c) | shapes_easings_ball_anim | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 41 | [shapes_easings_box_anim](shapes/shapes_easings_box_anim.c) | shapes_easings_box_anim | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 42 | [shapes_easings_rectangle_array](shapes/shapes_easings_rectangle_array.c) | shapes_easings_rectangle_array | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 43 | [shapes_draw_ring](shapes/shapes_draw_ring.c) | shapes_draw_ring | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | +| 44 | [shapes_draw_circle_sector](shapes/shapes_draw_circle_sector.c) | shapes_draw_circle_sector | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | +| 45 | [shapes_draw_rectangle_rounded](shapes/shapes_draw_rectangle_rounded.c) | shapes_draw_rectangle_rounded | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Vlad Adrian](https://github.com/demizdor) | +| 46 | [shapes_top_down_lights](shapes/shapes_top_down_lights.c) | shapes_top_down_lights | ⭐️⭐️⭐️⭐️ | **4.2** | **4.2** | [Jeffery Myers](https://github.com/JeffM2501) | ### category: textures @@ -84,28 +84,28 @@ Examples using raylib textures functionality, including image/textures loading/g | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 47 | [textures_logo_raylib](textures/textures_logo_raylib.c) | textures_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 48 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | textures_srcrec_dstrec | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 49 | [textures_image_drawing](textures/textures_image_drawing.c) | textures_image_drawing | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | -| 50 | [textures_image_generation](textures/textures_image_generation.c) | textures_image_generation | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Ray](https://github.com/raysan5) | -| 51 | [textures_image_loading](textures/textures_image_loading.c) | textures_image_loading | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 52 | [textures_image_processing](textures/textures_image_processing.c) | textures_image_processing | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) | -| 53 | [textures_image_text](textures/textures_image_text.c) | textures_image_text | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | -| 54 | [textures_to_image](textures/textures_to_image.c) | textures_to_image | ⭐️☆☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 55 | [textures_raw_data](textures/textures_raw_data.c) | textures_raw_data | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 56 | [textures_particles_blending](textures/textures_particles_blending.c) | textures_particles_blending | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | -| 57 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | textures_npatch_drawing | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) | -| 58 | [textures_background_scrolling](textures/textures_background_scrolling.c) | textures_background_scrolling | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) | -| 59 | [textures_sprite_anim](textures/textures_sprite_anim.c) | textures_sprite_anim | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 60 | [textures_sprite_button](textures/textures_sprite_button.c) | textures_sprite_button | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | -| 61 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | textures_sprite_explosion | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 62 | [textures_bunnymark](textures/textures_bunnymark.c) | textures_bunnymark | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) | -| 63 | [textures_mouse_painting](textures/textures_mouse_painting.c) | textures_mouse_painting | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | -| 64 | [textures_blend_modes](textures/textures_blend_modes.c) | textures_blend_modes | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) | -| 65 | [textures_draw_tiled](textures/textures_draw_tiled.c) | textures_draw_tiled | ⭐️⭐️⭐️☆ | 3.0 | **4.2** | [Vlad Adrian](https://github.com/demizdor) | -| 66 | [textures_polygon](textures/textures_polygon.c) | textures_polygon | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/codifies) | -| 67 | [textures_fog_of_war](textures/textures_fog_of_war.c) | textures_fog_of_war | ⭐️⭐️⭐️☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | -| 68 | [textures_gif_player](textures/textures_gif_player.c) | textures_gif_player | ⭐️⭐️⭐️☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | +| 47 | [textures_logo_raylib](textures/textures_logo_raylib.c) | textures_logo_raylib | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 48 | [textures_srcrec_dstrec](textures/textures_srcrec_dstrec.c) | textures_srcrec_dstrec | ⭐️⭐️⭐️☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 49 | [textures_image_drawing](textures/textures_image_drawing.c) | textures_image_drawing | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | +| 50 | [textures_image_generation](textures/textures_image_generation.c) | textures_image_generation | ⭐️⭐️☆☆ | 1.8 | 1.8 | [Ray](https://github.com/raysan5) | +| 51 | [textures_image_loading](textures/textures_image_loading.c) | textures_image_loading | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 52 | [textures_image_processing](textures/textures_image_processing.c) | textures_image_processing | ⭐️⭐️⭐️☆ | 1.4 | 3.5 | [Ray](https://github.com/raysan5) | +| 53 | [textures_image_text](textures/textures_image_text.c) | textures_image_text | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | +| 54 | [textures_to_image](textures/textures_to_image.c) | textures_to_image | ⭐️☆☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 55 | [textures_raw_data](textures/textures_raw_data.c) | textures_raw_data | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 56 | [textures_particles_blending](textures/textures_particles_blending.c) | textures_particles_blending | ⭐️☆☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | +| 57 | [textures_npatch_drawing](textures/textures_npatch_drawing.c) | textures_npatch_drawing | ⭐️⭐️⭐️☆ | 2.0 | 2.5 | [Jorge A. Gomes](https://github.com/overdev) | +| 58 | [textures_background_scrolling](textures/textures_background_scrolling.c) | textures_background_scrolling | ⭐️☆☆☆ | 2.0 | 2.5 | [Ray](https://github.com/raysan5) | +| 59 | [textures_sprite_anim](textures/textures_sprite_anim.c) | textures_sprite_anim | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 60 | [textures_sprite_button](textures/textures_sprite_button.c) | textures_sprite_button | ⭐️⭐️☆☆ | 2.5 | 2.5 | [Ray](https://github.com/raysan5) | +| 61 | [textures_sprite_explosion](textures/textures_sprite_explosion.c) | textures_sprite_explosion | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 62 | [textures_bunnymark](textures/textures_bunnymark.c) | textures_bunnymark | ⭐️⭐️⭐️☆ | 1.6 | 2.5 | [Ray](https://github.com/raysan5) | +| 63 | [textures_mouse_painting](textures/textures_mouse_painting.c) | textures_mouse_painting | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | +| 64 | [textures_blend_modes](textures/textures_blend_modes.c) | textures_blend_modes | ⭐️☆☆☆ | 3.5 | 3.5 | [Karlo Licudine](https://github.com/accidentalrebel) | +| 65 | [textures_draw_tiled](textures/textures_draw_tiled.c) | textures_draw_tiled | ⭐️⭐️⭐️☆ | 3.0 | **4.2** | [Vlad Adrian](https://github.com/demizdor) | +| 66 | [textures_polygon](textures/textures_polygon.c) | textures_polygon | ⭐️☆☆☆ | 3.7 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 67 | [textures_fog_of_war](textures/textures_fog_of_war.c) | textures_fog_of_war | ⭐️⭐️⭐️☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | +| 68 | [textures_gif_player](textures/textures_gif_player.c) | textures_gif_player | ⭐️⭐️⭐️☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | ### category: text @@ -113,42 +113,43 @@ Examples using raylib text functionality, including sprite fonts loading/generat | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 69 | [text_raylib_fonts](text/text_raylib_fonts.c) | text_raylib_fonts | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 70 | [text_font_spritefont](text/text_font_spritefont.c) | text_font_spritefont | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 71 | [text_font_filters](text/text_font_filters.c) | text_font_filters | ⭐️⭐️☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 72 | [text_font_loading](text/text_font_loading.c) | text_font_loading | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) | -| 73 | [text_font_sdf](text/text_font_sdf.c) | text_font_sdf | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 74 | [text_format_text](text/text_format_text.c) | text_format_text | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) | -| 75 | [text_input_box](text/text_input_box.c) | text_input_box | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | -| 76 | [text_writing_anim](text/text_writing_anim.c) | text_writing_anim | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | -| 77 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | text_rectangle_bounds | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Vlad Adrian](https://github.com/demizdor) | -| 78 | [text_unicode](text/text_unicode.c) | text_unicode | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Vlad Adrian](https://github.com/demizdor) | -| 79 | [text_draw_3d](text/text_draw_3d.c) | text_draw_3d | ⭐️⭐️⭐️⭐️ | 3.5 | **4.0** | [Vlad Adrian](https://github.com/demizdor) | -| 80 | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐️⭐️⭐️☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | - +| 69 | [text_raylib_fonts](text/text_raylib_fonts.c) | text_raylib_fonts | ⭐️☆☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | +| 70 | [text_font_spritefont](text/text_font_spritefont.c) | text_font_spritefont | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 71 | [text_font_filters](text/text_font_filters.c) | text_font_filters | ⭐️⭐️☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 72 | [text_font_loading](text/text_font_loading.c) | text_font_loading | ⭐️☆☆☆ | 1.4 | 3.0 | [Ray](https://github.com/raysan5) | +| 73 | [text_font_sdf](text/text_font_sdf.c) | text_font_sdf | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 74 | [text_format_text](text/text_format_text.c) | text_format_text | ⭐️☆☆☆ | 1.1 | 3.0 | [Ray](https://github.com/raysan5) | +| 75 | [text_input_box](text/text_input_box.c) | text_input_box | ⭐️⭐️☆☆ | 1.7 | 3.5 | [Ray](https://github.com/raysan5) | +| 76 | [text_writing_anim](text/text_writing_anim.c) | text_writing_anim | ⭐️⭐️☆☆ | 1.4 | 1.4 | [Ray](https://github.com/raysan5) | +| 77 | [text_rectangle_bounds](text/text_rectangle_bounds.c) | text_rectangle_bounds | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Vlad Adrian](https://github.com/demizdor) | +| 78 | [text_unicode](text/text_unicode.c) | text_unicode | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Vlad Adrian](https://github.com/demizdor) | +| 79 | [text_draw_3d](text/text_draw_3d.c) | text_draw_3d | ⭐️⭐️⭐️⭐️ | 3.5 | **4.0** | [Vlad Adrian](https://github.com/demizdor) | +| 80 | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐️⭐️⭐️☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | + ### category: models Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/models.c) module. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 81 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [culacant](https://github.com/culacant) | -| 82 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 83 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 84 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 85 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 86 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 87 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | -| 88 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | **4.0** | [Joel Davis](https://github.com/joeld42) | -| 89 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | -| 90 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | **4.2** | [Ray](https://github.com/raysan5) | -| 91 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) | -| 92 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | -| 93 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | -| 94 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) | -| 95 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) | -| 96 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 97 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | +| 81 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [culacant](https://github.com/culacant) | +| 82 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 83 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 84 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | +| 85 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 86 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | +| 87 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | +| 88 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | **4.0** | [Joel Davis](https://github.com/joeld42) | +| 89 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | +| 90 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | **4.2** | [Ray](https://github.com/raysan5) | +| 91 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) | +| 92 | [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐️☆☆☆ | **4.2** | **4.2** | [bzt](https://bztsrc.gitlab.io/model3d) | +| 93 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | +| 94 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | +| 95 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) | +| 96 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) | +| 97 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | +| 98 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | ### category: shaders @@ -156,36 +157,36 @@ Examples using raylib shaders functionality, including shaders loading, paramete | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 98 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) | -| 99 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | -| 100 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 101 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 102 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 103 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | -| 104 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) | -| 105 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) | -| 106 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) | -| 107 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | -| 108 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) | -| 109 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) | -| 110 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | -| 111 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | -| 112 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 113 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) | -| 114 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 115 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | - +| 99 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) | +| 100 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | +| 101 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | +| 102 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 103 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 104 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | +| 105 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) | +| 106 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) | +| 107 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) | +| 108 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | +| 109 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) | +| 110 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) | +| 111 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 112 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 113 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | +| 114 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) | +| 115 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 116 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | + ### category: audio 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, check [raudio_standalone](others/raudio_standalone.c) example. | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 116 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 117 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 118 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | -| 119 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | -| 120 | [audio_multichannel_sound](audio/audio_multichannel_sound.c) | audio_multichannel_sound | ⭐️☆☆☆ | 3.0 | 3.5 | [Chris Camacho](https://github.com/codifies) | +| 117 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 118 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 119 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | +| 120 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 121 | [audio_multichannel_sound](audio/audio_multichannel_sound.c) | audio_multichannel_sound | ⭐️☆☆☆ | 3.0 | 3.5 | [Chris Camacho](https://github.com/codifies) | ### category: others @@ -193,11 +194,11 @@ Examples showing raylib misc functionality that does not fit in other categories | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 121 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) | -| 122 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) | -| 123 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | -| 124 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) | -| 125 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | +| 122 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) | +| 123 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) | +| 124 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | +| 125 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) | +| 126 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | As always contributions are welcome, feel free to send new examples! Here it is an [examples template](examples_template.c) to start with! diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index f67ba5e57..d11c3a005 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -1,97 +1,101 @@ /******************************************************************************************* * -* raylib [models] example - Load M3D model (with optional animations) and play them +* raylib [models] example - Load models M3D * -* Example static mesh Suzanne from Blender -* Example animated seagull model from Scorched 3D, licensed GPLv2 +* Example originally created with raylib 4.5-dev, last time updated with raylib 4.5-dev +* +* Example contributed by bzt (@bztsrc) and reviewed by Ramon Santamaria (@raysan5) +* +* NOTES: +* - Model3D (M3D) fileformat specs: https://gitlab.com/bztsrc/model3d +* - Bender M3D exported: https://gitlab.com/bztsrc/model3d/-/tree/master/blender +* WARNING: Make sure to add "(action)" markers to the timeline if you want multiple animations. +* +* 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-2022 Culacant (@culacant) and Ramon Santamaria (@raysan5) * Copyright (c) 2022 bzt (@bztsrc) * -******************************************************************************************** -* -* NOTE: To export a model from blender, just use https://gitlab.com/bztsrc/model3d/-/tree/master/blender -* and make sure to add "(action)" markers to the timeline if you want multiple animations. -* ********************************************************************************************/ #include "raylib.h" -#include -#include - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ -int main(int argc, char **argv) +int main(void) { - char *model_fn = argc > 1 ? argv[1] : "resources/models/m3d/seagull.m3d"; - // Initialization //-------------------------------------------------------------------------------------- const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - M3D model"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - M3D model 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.position = (Vector3){ 1.5f, 1.5f, 1.5f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.4f, 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 Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + + char modelFileName[128] = "resources/models/m3d/CesiumMan.m3d"; + bool drawMesh = 1; + bool drawSkeleton = 1; + bool animPlaying = false; // Store anim state, what to draw // Load model - Model model = LoadModel(model_fn); // Load the animated model mesh and basic data + Model model = LoadModel(modelFileName); // Load the bind-pose model mesh and basic data - // Load animation data - unsigned int animsCount = 0, animsSkel = 1, animsMesh = 1; - ModelAnimation *anims = LoadModelAnimations(model_fn, &animsCount); + // Load animations + unsigned int animsCount = 0; int animFrameCounter = 0, animId = 0; + ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetCameraMode(camera, CAMERA_FREE); // Set free camera mode + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera); - // Play animation when spacebar is held down if (animsCount) { - if (IsKeyDown(KEY_SPACE)) + // Play animation when spacebar is held down (or step one frame with N) + if (IsKeyDown(KEY_SPACE) || IsKeyPressed(KEY_N)) { animFrameCounter++; - UpdateModelAnimation(model, anims[animId], animFrameCounter); + if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0; - //printf("anim %u, frame %u / %u\n",animId,animFrameCounter,anims[animId].frameCount); + + UpdateModelAnimation(model, anims[animId], animFrameCounter); + animPlaying = true; } - if (IsKeyDown(KEY_S)) - { - animsSkel ^= 1; - } - if (IsKeyDown(KEY_M)) - { - animsMesh ^= 1; - } - - // Select animation on mouse click - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) + + // Select animation by pressing A + if (IsKeyPressed(KEY_A)) { animFrameCounter = 0; animId++; + if (animId >= animsCount) animId = 0; UpdateModelAnimation(model, anims[animId], 0); + animPlaying = true; } } + + // Toggle skeleton drawing + if (IsKeyPressed(KEY_S)) drawSkeleton ^= 1; + + // Toggle mesh drawing + if (IsKeyPressed(KEY_M)) drawMesh ^= 1; //---------------------------------------------------------------------------------- // Draw @@ -102,25 +106,53 @@ int main(int argc, char **argv) BeginMode3D(camera); - if (animsMesh) - DrawModel(model, position, 1.0f, WHITE); // Draw 3d model with texture + // Draw 3d model with texture + if (drawMesh) DrawModel(model, position, 1.0f, WHITE); - if (anims && animsSkel) - for (int i = 0; i < model.boneCount; i++) + // Draw the animated skeleton + if (drawSkeleton) + { + // Loop to (boneCount - 1) because the last one is a special "no bone" bone, + // needed to workaround buggy models + // without a -1, we would always draw a cube at the origin + for (int i = 0; i < model.boneCount - 1; i++) { - DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED); - if (anims[animId].bones[i].parent >= 0) - DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation, - anims[animId].framePoses[animFrameCounter][anims[animId].bones[i].parent].translation, RED); + // 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) + { + // Display the bind-pose skeleton + DrawCube(model.bindPose[i].translation, 0.04f, 0.04f, 0.04f, RED); + + if (model.bones[i].parent >= 0) + { + DrawLine3D(model.bindPose[i].translation, + model.bindPose[model.bones[i].parent].translation, RED); + } + } + else + { + // Display the frame-pose skeleton + DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED); + + if (anims[animId].bones[i].parent >= 0) + { + DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation, + anims[animId].framePoses[animFrameCounter][anims[animId].bones[i].parent].translation, RED); + } + } } + } DrawGrid(10, 1.0f); // Draw a grid EndMode3D(); - DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 30, 10, MAROON); - DrawText("MOUSE LEFT BUTTON to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 20, 10, DARKGRAY); - DrawText("(c) Seagull model by Scorched3D", screenWidth - 200, screenHeight - 20, 10, GRAY); + DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 60, 10, MAROON); + DrawText("PRESS A to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY); + DrawText("PRESS M to toggle MESH, S to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY); + DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -130,8 +162,7 @@ int main(int argc, char **argv) //-------------------------------------------------------------------------------------- // Unload model animations data - for (unsigned int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); - RL_FREE(anims); + UnloadModelAnimations(anims, animsCount); UnloadModel(model); // Unload model diff --git a/examples/models/models_loading_m3d.png b/examples/models/models_loading_m3d.png index 80c859fee02548c70a84f42fd75584cf341d36e5..2214b8e0152194319b3cb88f763d85df9019e5a0 100644 GIT binary patch literal 23682 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
9ss0R$N< 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 Y5DY3V&l!;|4(5h}8O-mDE=tt?59F};=l}o! literal 0 HcmV?d00001 diff --git a/examples/models/resources/models/m3d/suzanne.m3d b/examples/models/resources/models/m3d/suzanne.m3d new file mode 100644 index 0000000000000000000000000000000000000000..9bc64d7d787a4cd7c135af96c71cc0c6d8c463f5 GIT binary patch literal 11645 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 W?QqGf)qqRRMyLE2$&$JCoc{se_CU4( literal 0 HcmV?d00001 diff --git a/src/rmodels.c b/src/rmodels.c index d6e73c07f..710cfabad 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5264,18 +5264,28 @@ static Model LoadM3D(const char *fileName) } // Add skin (vertex / bone weight pairs) - if (m3d->numbone && m3d->numskin) { - for (n = 0; n < 3; n++) { + if (m3d->numbone && m3d->numskin) + { + for (n = 0; n < 3; n++) + { int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid; - // check if there's a skin for this mesh, should be, just failsafe + + // Check if there is a skin for this mesh, should be, just failsafe if (skinid != M3D_UNDEF && skinid < 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]; + 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; + } } } } @@ -5352,11 +5362,12 @@ static Model LoadM3D(const char *fileName) } // Load bones - if(m3d->numbone) + if (m3d->numbone) { - model.boneCount = m3d->numbone; - model.bones = RL_MALLOC(m3d->numbone*sizeof(BoneInfo)); - model.bindPose = RL_MALLOC(m3d->numbone*sizeof(Transform)); + 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 < m3d->numbone; i++) { model.bones[i].parent = m3d->bone[i].parent; @@ -5381,6 +5392,18 @@ static Model LoadM3D(const char *fileName) 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 @@ -5440,8 +5463,8 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int for (unsigned int a = 0; a < m3d->numaction; a++) { animations[a].frameCount = m3d->action[a].durationmsec / M3D_ANIMDELAY; - animations[a].boneCount = m3d->numbone; - animations[a].bones = RL_MALLOC(m3d->numbone*sizeof(BoneInfo)); + 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)); TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount); @@ -5452,11 +5475,15 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int 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*sizeof(Transform)); + animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); m3db_t *pose = m3d_pose(m3d, a, i * M3D_ANIMDELAY); if (pose != NULL) @@ -5473,7 +5500,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int animations[a].framePoses[i][j].rotation = QuaternionNormalize(animations[a].framePoses[i][j].rotation); animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f; - // Child bones are stored in parent bone relative space, convert that into model space + // Child bones are stored in parent bone relative space, convert that into model space if (animations[a].bones[j].parent >= 0) { animations[a].framePoses[i][j].rotation = QuaternionMultiply(animations[a].framePoses[i][animations[a].bones[j].parent].rotation, animations[a].framePoses[i][j].rotation); @@ -5482,6 +5509,16 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int animations[a].framePoses[i][j].scale = Vector3Multiply(animations[a].framePoses[i][j].scale, animations[a].framePoses[i][animations[a].bones[j].parent].scale); } } + + // Default transform for the "no bone" bone + animations[a].framePoses[i][j].translation.x = 0.0f; + animations[a].framePoses[i][j].translation.y = 0.0f; + animations[a].framePoses[i][j].translation.z = 0.0f; + animations[a].framePoses[i][j].rotation.x = 0.0f; + animations[a].framePoses[i][j].rotation.y = 0.0f; + animations[a].framePoses[i][j].rotation.z = 0.0f; + animations[a].framePoses[i][j].rotation.w = 1.0f; + animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f; RL_FREE(pose); } } From 234576da71757e1f02911993175e22762923749b Mon Sep 17 00:00:00 2001 From: Stan <109335133+disketteman@users.noreply.github.com> Date: Fri, 2 Sep 2022 09:13:35 +0200 Subject: [PATCH 0036/1710] Removed raylib_opengl_interop.c from PLATFORM=Web build (#2682) --- examples/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 416f76e9c..3bcdc19f0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -85,7 +85,7 @@ if (${PLATFORM} MATCHES "Android") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_obj_viewer.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_animation.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_first_person_maze.c) - list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_magicavoxel_loading.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/models/models_magicavoxel_loading.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/shaders/shaders_custom_uniform.c) @@ -102,6 +102,8 @@ elseif (${PLATFORM} MATCHES "Web") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --shell-file ${CMAKE_SOURCE_DIR}/src/shell.html") set(CMAKE_EXECUTABLE_SUFFIX ".html") + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/raylib_opengl_interop.c) + # Remove the -rdynamic flag because otherwise emscripten # does not generate HTML+JS+WASM files, only a non-working # and fat HTML From 2a798d64a2040979a5cc6e8f40282f66edb23169 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 2 Sep 2022 21:28:19 +0200 Subject: [PATCH 0037/1710] Export `raylibVersion` symbol. Fixes #2671 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index c15022230..fc57db58c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -505,7 +505,7 @@ typedef struct CoreData { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -const char *raylibVersion = RAYLIB_VERSION; // raylib version symbol, it could be required for some bindings +RLAPI const char *raylibVersion = RAYLIB_VERSION; // raylib version exported symbol, it could be required for some bindings static CoreData CORE = { 0 }; // Global CORE state context From de968e3623e773b245a20136c5e2e0207511059d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 2 Sep 2022 21:32:58 +0200 Subject: [PATCH 0038/1710] WARNING: RENAMED symbol `raylibVersion` to `raylib_version` I want to note this is a special symbol exported --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index fc57db58c..200e490ae 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -505,7 +505,7 @@ typedef struct CoreData { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -RLAPI const char *raylibVersion = RAYLIB_VERSION; // raylib version exported symbol, it could be required for some bindings +RLAPI const char *RAYLIB_VERSION = RAYLIB_VERSION; // raylib version exported symbol, it could be required for some bindings static CoreData CORE = { 0 }; // Global CORE state context From 082920eb800d7d7612d500a4bbc46b21ff232424 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 2 Sep 2022 21:35:08 +0200 Subject: [PATCH 0039/1710] WARNING: RENAMED exported symbol to `raylib_version` #2671 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 200e490ae..b0f28ccc3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -505,7 +505,7 @@ typedef struct CoreData { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -RLAPI const char *RAYLIB_VERSION = RAYLIB_VERSION; // raylib version exported symbol, it could be required for some bindings +RLAPI const char *raylib_version = RAYLIB_VERSION; // raylib version exported symbol, required for some bindings static CoreData CORE = { 0 }; // Global CORE state context From aff98d7f2af466f4b066ac9a019b8ca1e450c5cd Mon Sep 17 00:00:00 2001 From: Jacek Date: Sun, 4 Sep 2022 10:39:03 +0200 Subject: [PATCH 0040/1710] Check collision point polygon (#2685) * Update raylib.h * CheckCollisionPointPolygon() * typo --- src/raylib.h | 1 + src/rshapes.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 0746ca2a3..1f0ee8f32 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1213,6 +1213,7 @@ RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); 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 CheckCollisionPointPolygon(Vector2 point, Vector2* vertices, int verticesCount); // 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 Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision diff --git a/src/rshapes.c b/src/rshapes.c index 955f056d4..8018bbdef 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1631,6 +1631,28 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 return collision; } +// Check if point is within a polygon described by array of vertices +bool CheckCollisionPointPolygon(Vector2 point, Vector2* vertices, int verticesCount) +{ + // http://jeffreythompson.org/collision-detection/poly-point.php + + bool collision = false; + + if (verticesCount > 2) + { + for (int i = 0; i < verticesCount; i++) + { + Vector2 vc = vertices[i]; + Vector2 vn = vertices[i + 1]; + + if ((((vc.y >= point.y) && (vn.y < point.y)) || ((vc.y < point.y) && (vn.y >= point.y))) && + (point.x < ((vn.x - vc.x)*(point.y - vc.y)/(vn.y - vc.y) + vc.x))) collision = !collision; + } + } + + return collision; +} + // Check collision between two rectangles bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2) { From f4b4054de570742845fb9a7b80c579ba37d98fbd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 4 Sep 2022 10:45:01 +0200 Subject: [PATCH 0041/1710] REVIEWED: `CheckCollisionPointPoly()` --- src/raylib.h | 2 +- src/rshapes.c | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 1f0ee8f32..3f03effe6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1213,7 +1213,7 @@ RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); 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 CheckCollisionPointPolygon(Vector2 point, Vector2* vertices, int verticesCount); // Check if point is within a polygon described by array of vertices +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 Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision diff --git a/src/rshapes.c b/src/rshapes.c index 8018bbdef..5e79d893f 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1632,18 +1632,17 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 } // Check if point is within a polygon described by array of vertices -bool CheckCollisionPointPolygon(Vector2 point, Vector2* vertices, int verticesCount) +// NOTE: Based on http://jeffreythompson.org/collision-detection/poly-point.php +bool CheckCollisionPointPoly(Vector2 point, Vector2 *points, int pointCount) { - // http://jeffreythompson.org/collision-detection/poly-point.php - bool collision = false; - if (verticesCount > 2) + if (pointCount > 2) { - for (int i = 0; i < verticesCount; i++) + for (int i = 0; i < pointCount; i++) { - Vector2 vc = vertices[i]; - Vector2 vn = vertices[i + 1]; + Vector2 vc = points[i]; + Vector2 vn = points[i + 1]; if ((((vc.y >= point.y) && (vn.y < point.y)) || ((vc.y < point.y) && (vn.y >= point.y))) && (point.x < ((vn.x - vc.x)*(point.y - vc.y)/(vn.y - vc.y) + vc.x))) collision = !collision; From ee5a87826d22ba14f0ad3d3938c92ca42d217146 Mon Sep 17 00:00:00 2001 From: skylar <89084411+skylar779@users.noreply.github.com> Date: Sun, 4 Sep 2022 11:48:33 +0200 Subject: [PATCH 0042/1710] Fixed a bug in the 2d camera platformer example (#2687) canJump used to alternate between true and false when on ground --- examples/core/core_2d_camera_platformer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_2d_camera_platformer.c b/examples/core/core_2d_camera_platformer.c index 59e7b318a..5750e7a80 100644 --- a/examples/core/core_2d_camera_platformer.c +++ b/examples/core/core_2d_camera_platformer.c @@ -177,7 +177,7 @@ void UpdatePlayer(Player *player, EnvItem *envItems, int envItemsLength, float d ei->rect.x <= p->x && ei->rect.x + ei->rect.width >= p->x && ei->rect.y >= p->y && - ei->rect.y < p->y + player->speed*delta) + ei->rect.y <= p->y + player->speed*delta) { hitObstacle = 1; player->speed = 0.0f; From ac3420faac3b0a19403a944eebb1e110034eb4d7 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 4 Sep 2022 12:32:40 +0200 Subject: [PATCH 0043/1710] Update core_custom_frame_control.c --- examples/core/core_custom_frame_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_custom_frame_control.c b/examples/core/core_custom_frame_control.c index d69481b01..016c6c448 100644 --- a/examples/core/core_custom_frame_control.c +++ b/examples/core/core_custom_frame_control.c @@ -110,7 +110,7 @@ int main(void) waitTime = (1.0f/(float)targetFPS) - updateDrawTime; if (waitTime > 0.0) { - WaitTime((float)waitTime*1000.0f); + WaitTime((float)waitTime); currentTime = GetTime(); deltaTime = (float)(currentTime - previousTime); } From 0917290e95898ede3ae54c01988a302e0a743793 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 4 Sep 2022 18:49:54 +0200 Subject: [PATCH 0044/1710] REVIEWED: M3D model loading #2688 --- src/rmodels.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 710cfabad..3160ecf8b 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5372,9 +5372,9 @@ static Model LoadM3D(const char *fileName) { 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; - model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y; - model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z; + 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; @@ -5490,9 +5490,9 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int { for (j = 0; j < m3d->numbone; j++) { - animations[a].framePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x; - animations[a].framePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y; - animations[a].framePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z; + animations[a].framePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x*m3d->scale; + animations[a].framePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y*m3d->scale; + animations[a].framePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z*m3d->scale; animations[a].framePoses[i][j].rotation.x = m3d->vertex[pose[j].ori].x; animations[a].framePoses[i][j].rotation.y = m3d->vertex[pose[j].ori].y; animations[a].framePoses[i][j].rotation.z = m3d->vertex[pose[j].ori].z; From ad5ffd78d616aafabf18458a25aa78c0850b3e3d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 5 Sep 2022 11:15:28 +0200 Subject: [PATCH 0045/1710] REVIEWED: rlgl enums and comments --- src/rcore.c | 10 +-- src/rlgl.h | 254 +++++++++++++++++++++++++++------------------------- 2 files changed, 135 insertions(+), 129 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b0f28ccc3..40581415f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2339,7 +2339,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) { VrStereoConfig config = { 0 }; - if ((rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20)) + if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() == RL_OPENGL_ES_20)) { // Compute aspect ratio float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution; @@ -4006,12 +4006,12 @@ static bool InitGraphicsDevice(int width, int height) // For example, if using OpenGL 1.1, driver can provide a 4.3 context forward compatible. // Check selection OpenGL version - if (rlGetVersion() == OPENGL_21) + if (rlGetVersion() == RL_OPENGL_21) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) } - else if (rlGetVersion() == OPENGL_33) + 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) @@ -4024,7 +4024,7 @@ static bool InitGraphicsDevice(int width, int height) #endif //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context } - else if (rlGetVersion() == OPENGL_43) + else if (rlGetVersion() == RL_OPENGL_43) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) @@ -4034,7 +4034,7 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context #endif } - else if (rlGetVersion() == OPENGL_ES_20) // Request OpenGL ES 2.0 context + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); diff --git a/src/rlgl.h b/src/rlgl.h index 363b45bd3..057c42b5e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -243,7 +243,7 @@ #define RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST 0x2701 // GL_LINEAR_MIPMAP_NEAREST #define RL_TEXTURE_FILTER_MIP_LINEAR 0x2703 // GL_LINEAR_MIPMAP_LINEAR #define RL_TEXTURE_FILTER_ANISOTROPIC 0x3000 // Anisotropic filter (custom identifier) -#define RL_TEXTURE_MIPMAP_BIAS_RATIO 0x4000 // Texture mipmap bias (percentage ratio) +#define RL_TEXTURE_MIPMAP_BIAS_RATIO 0x4000 // Texture mipmap bias, percentage ratio (custom identifier) #define RL_TEXTURE_WRAP_REPEAT 0x2901 // GL_REPEAT #define RL_TEXTURE_WRAP_CLAMP 0x812F // GL_CLAMP_TO_EDGE @@ -283,37 +283,23 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef enum { - OPENGL_11 = 1, - OPENGL_21, - OPENGL_33, - OPENGL_43, - OPENGL_ES_20 -} rlGlVersion; +#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) + #include +#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE) + // Boolean type +typedef enum bool { false = 0, true = !false } bool; +#endif -typedef enum { - RL_ATTACHMENT_COLOR_CHANNEL0 = 0, - RL_ATTACHMENT_COLOR_CHANNEL1, - RL_ATTACHMENT_COLOR_CHANNEL2, - RL_ATTACHMENT_COLOR_CHANNEL3, - RL_ATTACHMENT_COLOR_CHANNEL4, - RL_ATTACHMENT_COLOR_CHANNEL5, - RL_ATTACHMENT_COLOR_CHANNEL6, - RL_ATTACHMENT_COLOR_CHANNEL7, - RL_ATTACHMENT_DEPTH = 100, - RL_ATTACHMENT_STENCIL = 200, -} rlFramebufferAttachType; - -typedef enum { - RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, - RL_ATTACHMENT_CUBEMAP_NEGATIVE_X, - RL_ATTACHMENT_CUBEMAP_POSITIVE_Y, - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y, - RL_ATTACHMENT_CUBEMAP_POSITIVE_Z, - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, - RL_ATTACHMENT_TEXTURE2D = 100, - RL_ATTACHMENT_RENDERBUFFER = 200, -} rlFramebufferAttachTextureType; +#if !defined(RL_MATRIX_TYPE) +// Matrix, 4x4 components, column major, OpenGL style, right handed +typedef struct Matrix { + float m0, m4, m8, m12; // Matrix first row (4 components) + float m1, m5, m9, m13; // Matrix second row (4 components) + float m2, m6, m10, m14; // Matrix third row (4 components) + float m3, m7, m11, m15; // Matrix fourth row (4 components) +} Matrix; +#define RL_MATRIX_TYPE +#endif // Dynamic vertex buffers (position + texcoords + colors + indices arrays) typedef struct rlVertexBuffer { @@ -344,8 +330,8 @@ typedef struct rlDrawCall { //unsigned int shaderId; // Shader id to be used on the draw -> Using RLGL.currentShaderId unsigned int textureId; // Texture id to be used on the draw -> Use to create new draw call if changes - //Matrix projection; // Projection matrix for this draw -> Using RLGL.projection by default - //Matrix modelview; // Modelview matrix for this draw -> Using RLGL.modelview by default + //Matrix projection; // Projection matrix for this draw -> Using RLGL.projection by default + //Matrix modelview; // Modelview matrix for this draw -> Using RLGL.modelview by default } rlDrawCall; // rlRenderBatch type @@ -359,38 +345,31 @@ typedef struct rlRenderBatch { float currentDepth; // Current depth value for next draw } rlRenderBatch; -#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) - #include -#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE) - // Boolean type -typedef enum bool { false = 0, true = !false } bool; -#endif -#if !defined(RL_MATRIX_TYPE) -// Matrix, 4x4 components, column major, OpenGL style, right handed -typedef struct Matrix { - float m0, m4, m8, m12; // Matrix first row (4 components) - float m1, m5, m9, m13; // Matrix second row (4 components) - float m2, m6, m10, m14; // Matrix third row (4 components) - float m3, m7, m11, m15; // Matrix fourth row (4 components) -} Matrix; -#define RL_MATRIX_TYPE -#endif +// OpenGL version +typedef enum { + RL_OPENGL_11 = 1, // 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) + RL_OPENGL_ES_20 // OpenGL ES 2.0 (GLSL 100) +} rlGlVersion; // Trace log level // NOTE: Organized by priority level typedef enum { - RL_LOG_ALL = 0, // Display all logs - RL_LOG_TRACE, // Trace logging, intended for internal use only - RL_LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds - RL_LOG_INFO, // Info logging, used for program execution info - RL_LOG_WARNING, // Warning logging, used on recoverable failures - RL_LOG_ERROR, // Error logging, used on unrecoverable failures - RL_LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) - RL_LOG_NONE // Disable logging + RL_LOG_ALL = 0, // Display all logs + RL_LOG_TRACE, // Trace logging, intended for internal use only + RL_LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + RL_LOG_INFO, // Info logging, used for program execution info + RL_LOG_WARNING, // Warning logging, used on recoverable failures + RL_LOG_ERROR, // Error logging, used on unrecoverable failures + RL_LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) + RL_LOG_NONE // Disable logging } rlTraceLogLevel; -// Texture formats (support depends on OpenGL version) +// Texture pixel formats +// NOTE: Support depends on OpenGL version typedef enum { RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) @@ -419,79 +398,106 @@ typedef enum { // NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 2: Filter is accordingly set for minification and magnification typedef enum { - RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation - RL_TEXTURE_FILTER_BILINEAR, // Linear filtering - RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) - RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x - RL_TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x - RL_TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x + RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation + RL_TEXTURE_FILTER_BILINEAR, // Linear filtering + RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) + RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x + RL_TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x + RL_TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x } rlTextureFilter; // Color blending modes (pre-defined) typedef enum { - RL_BLEND_ALPHA = 0, // Blend textures considering alpha (default) - RL_BLEND_ADDITIVE, // Blend textures adding colors - RL_BLEND_MULTIPLIED, // Blend textures multiplying colors - RL_BLEND_ADD_COLORS, // Blend textures adding colors (alternative) - RL_BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) - RL_BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha - RL_BLEND_CUSTOM // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + RL_BLEND_ALPHA = 0, // Blend textures considering alpha (default) + RL_BLEND_ADDITIVE, // Blend textures adding colors + RL_BLEND_MULTIPLIED, // Blend textures multiplying colors + RL_BLEND_ADD_COLORS, // Blend textures adding colors (alternative) + RL_BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) + RL_BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha + RL_BLEND_CUSTOM // Blend textures using custom src/dst factors (use rlSetBlendFactors()) } rlBlendMode; // Shader location point type typedef enum { - RL_SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position - RL_SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 - RL_SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 - RL_SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal - RL_SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent - RL_SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color - RL_SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection - RL_SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) - RL_SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection - RL_SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) - RL_SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal - RL_SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view - RL_SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color - RL_SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color - RL_SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color - RL_SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: RL_SHADER_LOC_MAP_DIFFUSE) - RL_SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: RL_SHADER_LOC_MAP_SPECULAR) - RL_SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal - RL_SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness - RL_SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion - RL_SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission - RL_SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height - RL_SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap - RL_SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance - RL_SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter - RL_SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf -} rlShaderLocationIndex; - -#define RL_SHADER_LOC_MAP_DIFFUSE RL_SHADER_LOC_MAP_ALBEDO -#define RL_SHADER_LOC_MAP_SPECULAR RL_SHADER_LOC_MAP_METALNESS - -// Shader uniform data type -typedef enum { - RL_SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float - RL_SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) - RL_SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) - RL_SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) - RL_SHADER_UNIFORM_INT, // Shader uniform type: int - RL_SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) - RL_SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) - RL_SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) - RL_SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d + RL_SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position + RL_SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 + RL_SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 + RL_SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal + RL_SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent + RL_SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color + RL_SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection + RL_SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) + RL_SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection + RL_SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) + RL_SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal + RL_SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view + RL_SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color + RL_SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color + RL_SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color + RL_SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: RL_SHADER_LOC_MAP_DIFFUSE) + RL_SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: RL_SHADER_LOC_MAP_SPECULAR) + RL_SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal + RL_SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness + RL_SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion + RL_SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission + RL_SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height + RL_SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap + RL_SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance + RL_SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter + RL_SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf +} rlShaderLocationIndex; + +#define RL_SHADER_LOC_MAP_DIFFUSE RL_SHADER_LOC_MAP_ALBEDO +#define RL_SHADER_LOC_MAP_SPECULAR RL_SHADER_LOC_MAP_METALNESS + +// Shader uniform data type +typedef enum { + RL_SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float + RL_SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) + RL_SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) + RL_SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) + RL_SHADER_UNIFORM_INT, // Shader uniform type: int + RL_SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) + RL_SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) + RL_SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) + RL_SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d } rlShaderUniformDataType; // Shader attribute data types typedef enum { - RL_SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float - RL_SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) - RL_SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) - RL_SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) + RL_SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float + RL_SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) + RL_SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) + RL_SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) } rlShaderAttributeDataType; +// Framebuffer attachment type +// NOTE: By default up to 8 color channels defined but it can be more +typedef enum { + RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachmment type: color 0 + RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachmment type: color 1 + RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachmment type: color 2 + RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachmment type: color 3 + RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachmment type: color 4 + RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachmment type: color 5 + RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachmment type: color 6 + RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachmment type: color 7 + RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachmment type: depth + RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachmment type: stencil +} rlFramebufferAttachType; + +// Framebuffer texture attachment type +typedef enum { + RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side + RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d + RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer +} rlFramebufferAttachTextureType; + //------------------------------------------------------------------------------------ // Functions Declaration - Matrix operations //------------------------------------------------------------------------------------ @@ -617,7 +623,7 @@ RLAPI void rlDrawRenderBatch(rlRenderBatch *batch); // D RLAPI void rlSetRenderBatchActive(rlRenderBatch *batch); // Set the active render batch for rlgl (NULL for default internal) RLAPI void rlDrawRenderBatchActive(void); // Update and draw internal render batch RLAPI bool rlCheckRenderBatchLimit(int vCount); // Check internal buffer overflow for a given number of vertex -RLAPI void rlSetTexture(unsigned int id); // Set current texture for render batch and check buffers limits +RLAPI void rlSetTexture(unsigned int id); // Set current texture for render batch and check buffers limits //------------------------------------------------------------------------------------------------------------------------ @@ -2211,22 +2217,22 @@ int rlGetVersion(void) { int glVersion = 0; #if defined(GRAPHICS_API_OPENGL_11) - glVersion = OPENGL_11; + glVersion = RL_OPENGL_11; #endif #if defined(GRAPHICS_API_OPENGL_21) #if defined(__APPLE__) - glVersion = OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX + glVersion = RL_OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX #else - glVersion = OPENGL_21; + glVersion = RL_OPENGL_21; #endif #elif defined(GRAPHICS_API_OPENGL_33) - glVersion = OPENGL_33; + glVersion = RL_OPENGL_33; #endif #if defined(GRAPHICS_API_OPENGL_43) - glVersion = OPENGL_43; + glVersion = RL_OPENGL_43; #endif #if defined(GRAPHICS_API_OPENGL_ES2) - glVersion = OPENGL_ES_20; + glVersion = RL_OPENGL_ES_20; #endif return glVersion; } From 9996e328cb036b9bc63249bab77de5499ad2e4e8 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 5 Sep 2022 13:20:09 +0200 Subject: [PATCH 0046/1710] WARNING: BREAKING: Removed `rlCheckRenderBatchLimit()` requirement Updated version to `rlgl 4.2` --- src/rlgl.h | 110 ++++++++++++++++++---------------- src/rmodels.c | 153 +++++++++++++++++------------------------------- src/rshapes.c | 69 ---------------------- src/rtextures.c | 6 -- 4 files changed, 115 insertions(+), 223 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 057c42b5e..2c83d08a4 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rlgl v4.0 - A multi-OpenGL abstraction layer with an immediate-mode style API +* rlgl v4.2 - A multi-OpenGL abstraction layer with an immediate-mode style API * * An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) * that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) @@ -107,7 +107,7 @@ #ifndef RLGL_H #define RLGL_H -#define RLGL_VERSION "4.0" +#define RLGL_VERSION "4.2" // 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 @@ -345,7 +345,6 @@ typedef struct rlRenderBatch { float currentDepth; // Current depth value for next draw } rlRenderBatch; - // OpenGL version typedef enum { RL_OPENGL_11 = 1, // OpenGL 1.1 @@ -601,18 +600,18 @@ RLAPI void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation); // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ // rlgl initialization functions -RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) -RLAPI void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures) -RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required) -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 -RLAPI void rlSetFramebufferHeight(int height); // Set current framebuffer height -RLAPI int rlGetFramebufferHeight(void); // Get default framebuffer height +RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) +RLAPI void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures) +RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required) +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 +RLAPI void rlSetFramebufferHeight(int height); // Set current framebuffer height +RLAPI int rlGetFramebufferHeight(void); // Get default framebuffer height -RLAPI unsigned int rlGetTextureIdDefault(void); // Get default texture id -RLAPI unsigned int rlGetShaderIdDefault(void); // Get default shader id -RLAPI int *rlGetShaderLocsDefault(void); // Get default shader locations +RLAPI unsigned int rlGetTextureIdDefault(void); // Get default texture id +RLAPI unsigned int rlGetShaderIdDefault(void); // Get default shader id +RLAPI int *rlGetShaderLocsDefault(void); // Get default shader locations // Render batch management // NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode @@ -623,6 +622,7 @@ RLAPI void rlDrawRenderBatch(rlRenderBatch *batch); // D RLAPI void rlSetRenderBatchActive(rlRenderBatch *batch); // Set the active render batch for rlgl (NULL for default internal) RLAPI void rlDrawRenderBatchActive(void); // Update and draw internal render batch RLAPI bool rlCheckRenderBatchLimit(int vCount); // Check internal buffer overflow for a given number of vertex + RLAPI void rlSetTexture(unsigned int id); // Set current texture for render batch and check buffers limits //------------------------------------------------------------------------------------------------------------------------ @@ -1313,17 +1313,6 @@ void rlEnd(void) // as well as depth buffer bit-depth (16bit or 24bit or 32bit) // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) RLGL.currentBatch->currentDepth += (1.0f/20000.0f); - - // Verify internal buffers limits - // NOTE: This check is combined with usage of rlCheckRenderBatchLimit() - if (RLGL.State.vertexCounter >= (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4 - 4)) - { - // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlDrawRenderBatch(), - // we need to call rlPopMatrix() before to recover *RLGL.State.currentMatrix (RLGL.State.modelview) for the next forced draw call! - // If we have multiple matrix pushed, it will require "RLGL.State.stackCounter" pops before launching the draw - for (int i = RLGL.State.stackCounter; i >= 0; i--) rlPopMatrix(); - rlDrawRenderBatch(RLGL.currentBatch); - } } // Define one vertex (position) @@ -1342,32 +1331,51 @@ void rlVertex3f(float x, float y, float z) tz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z + RLGL.State.transform.m14; } - // Verify that current vertex buffer elements limit has not been reached - if (RLGL.State.vertexCounter < (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4)) + // 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)) { - // Add vertices - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter] = tx; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 1] = ty; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 2] = tz; - - // Add current texcoord - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy; - - // TODO: Add current normal - // By default rlVertexBuffer type does not store normals - - // Add current color - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 1] = RLGL.State.colorg; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 2] = RLGL.State.colorb; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 3] = RLGL.State.colora; - - RLGL.State.vertexCounter++; - - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount++; + if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%2 == 0)) + { + // Reached the maximum number of vertices for RL_LINES drawing + // Launch a draw call but keep current state for next vertices comming + // NOTE: We add +1 vertex to the check for security + rlCheckRenderBatchLimit(2 + 1); + } + else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%3 == 0)) + { + rlCheckRenderBatchLimit(3 + 1); + } + else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_QUADS) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4 == 0)) + { + rlCheckRenderBatchLimit(4 + 1); + } } - else TRACELOG(RL_LOG_ERROR, "RLGL: Batch elements overflow"); + + // Add vertices + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter] = tx; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 1] = ty; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 2] = tz; + + // Add current texcoord + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy; + + // TODO: Add current normal + // By default rlVertexBuffer type does not store normals + + // Add current color + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 1] = RLGL.State.colorg; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 2] = RLGL.State.colorb; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 3] = RLGL.State.colora; + + RLGL.State.vertexCounter++; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount++; } // Define one vertex (position) @@ -2702,10 +2710,12 @@ bool rlCheckRenderBatchLimit(int vCount) if ((RLGL.State.vertexCounter + vCount) >= (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4)) { + overflow = true; + + // Store current primitive drawing mode and texture id int currentMode = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode; int currentTexture = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId; - overflow = true; rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside // Restore state of last batch so we can continue adding vertices diff --git a/src/rmodels.c b/src/rmodels.c index 3160ecf8b..cb6551eb7 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -163,11 +163,6 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int // Draw a line in 3D world space void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color) { - // WARNING: Be careful with internal buffer vertex alignment - // when using RL_LINES or RL_TRIANGLES, data is aligned to fit - // lines-triangles-quads in the same indexed buffers!!! - rlCheckRenderBatchLimit(8); - rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); rlVertex3f(startPos.x, startPos.y, startPos.z); @@ -178,8 +173,6 @@ void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color) // Draw a point in 3D space, actually a small line void DrawPoint3D(Vector3 position, Color color) { - rlCheckRenderBatchLimit(8); - rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); rlBegin(RL_LINES); @@ -193,8 +186,6 @@ void DrawPoint3D(Vector3 position, Color color) // Draw a circle in 3D world space void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color) { - rlCheckRenderBatchLimit(2*36); - rlPushMatrix(); rlTranslatef(center.x, center.y, center.z); rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z); @@ -214,8 +205,6 @@ void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rota // Draw a color-filled triangle (vertex in counter-clockwise order!) void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color) { - rlCheckRenderBatchLimit(8); - rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); rlVertex3f(v1.x, v1.y, v1.z); @@ -227,30 +216,27 @@ void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color) // Draw a triangle strip defined by points void DrawTriangleStrip3D(Vector3 *points, int pointCount, Color color) { - if (pointCount >= 3) - { - rlCheckRenderBatchLimit(3*(pointCount - 2)); + if (pointCount < 3) return; - rlBegin(RL_TRIANGLES); - rlColor4ub(color.r, color.g, color.b, color.a); + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); - for (int i = 2; i < pointCount; i++) + for (int i = 2; i < pointCount; i++) + { + if ((i%2) == 0) { - 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); - } + 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); } - rlEnd(); - } + 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 @@ -261,8 +247,6 @@ void DrawCube(Vector3 position, float width, float height, float length, Color c float y = 0.0f; float z = 0.0f; - rlCheckRenderBatchLimit(36); - rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate) rlTranslatef(position.x, position.y, position.z); @@ -342,65 +326,67 @@ void DrawCubeWires(Vector3 position, float width, float height, float length, Co float y = 0.0f; float z = 0.0f; - rlCheckRenderBatchLimit(36); - rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - // Front face ----------------------------------------------------- + // 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 + 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 + 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 + 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 + 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 ------------------------------------------------------ + // 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 + 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 + 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 + 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 + 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 ------------------------------------------------------- + // 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 + 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 + 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 --------------------------------------------------- + // 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 + 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 + 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(); } @@ -419,8 +405,6 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei float y = position.y; float z = position.z; - rlCheckRenderBatchLimit(36); - rlSetTexture(texture.id); //rlPushMatrix(); @@ -432,37 +416,37 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei 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 + 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 + 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 + 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 + 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 + 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 + 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 @@ -482,8 +466,6 @@ void DrawCubeTextureRec(Texture2D texture, Rectangle source, Vector3 position, f float texWidth = (float)texture.width; float texHeight = (float)texture.height; - rlCheckRenderBatchLimit(36); - rlSetTexture(texture.id); rlBegin(RL_QUADS); @@ -569,9 +551,6 @@ void DrawSphere(Vector3 centerPos, float radius, Color color) // Draw sphere with extended parameters void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) { - int numVertex = (rings + 2)*slices*6; - rlCheckRenderBatchLimit(numVertex); - rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -612,9 +591,6 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color // Draw sphere wires void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color) { - int numVertex = (rings + 2)*slices*6; - rlCheckRenderBatchLimit(numVertex); - rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -659,9 +635,6 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h { if (sides < 3) sides = 3; - int numVertex = sides*6; - rlCheckRenderBatchLimit(numVertex); - rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); @@ -718,9 +691,6 @@ void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float e { if (sides < 3) sides = 3; - int numVertex = sides*6; - rlCheckRenderBatchLimit(numVertex); - 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; @@ -777,9 +747,6 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl { if (sides < 3) sides = 3; - int numVertex = sides*8; - rlCheckRenderBatchLimit(numVertex); - rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); @@ -811,9 +778,6 @@ void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, fl { if (sides < 3) sides = 3; - int numVertex = sides*6; - rlCheckRenderBatchLimit(numVertex); - 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; @@ -853,12 +817,9 @@ void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, fl rlEnd(); } - // Draw a plane void DrawPlane(Vector3 centerPos, Vector2 size, Color color) { - rlCheckRenderBatchLimit(4); - // NOTE: Plane is always created on XZ ground rlPushMatrix(); rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -895,8 +856,6 @@ void DrawGrid(int slices, float spacing) { int halfSlices = slices/2; - rlCheckRenderBatchLimit((slices + 2)*4); - rlBegin(RL_LINES); for (int i = -halfSlices; i <= halfSlices; i++) { @@ -3421,8 +3380,6 @@ void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector bottomRight = Vector3Add(bottomRight, position); bottomLeft = Vector3Add(bottomLeft, position); - rlCheckRenderBatchLimit(8); - rlSetTexture(texture.id); rlBegin(RL_QUADS); diff --git a/src/rshapes.c b/src/rshapes.c index 5e79d893f..458569a5f 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -238,8 +238,6 @@ void DrawLineStrip(Vector2 *points, int pointCount, Color color) { if (pointCount >= 2) { - rlCheckRenderBatchLimit(pointCount); - rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -287,8 +285,6 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA float angle = startAngle; #if defined(SUPPORT_QUADS_DRAW_MODE) - rlCheckRenderBatchLimit(4*segments/2); - rlSetTexture(texShapes.id); rlBegin(RL_QUADS); @@ -333,8 +329,6 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlSetTexture(0); #else - rlCheckRenderBatchLimit(3*segments); - rlBegin(RL_TRIANGLES); for (int i = 0; i < segments; i++) { @@ -377,13 +371,7 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float float stepLength = (endAngle - startAngle)/(float)segments; float angle = startAngle; - - // Hide the cap lines when the circle is full bool showCapLines = true; - int limit = 2*(segments + 2); - if ((int)(endAngle - startAngle)%360 == 0) { limit = 2*segments; showCapLines = false; } - - rlCheckRenderBatchLimit(limit); rlBegin(RL_LINES); if (showCapLines) @@ -416,8 +404,6 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float // NOTE: Gradient goes from center (color1) to border (color2) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) { - rlCheckRenderBatchLimit(3*36); - rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) { @@ -441,8 +427,6 @@ void DrawCircleV(Vector2 center, float radius, Color color) // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) { - rlCheckRenderBatchLimit(2*36); - rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -458,8 +442,6 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) // Draw ellipse void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color) { - rlCheckRenderBatchLimit(3*36); - rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) { @@ -474,8 +456,6 @@ void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color c // Draw ellipse outline void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color) { - rlCheckRenderBatchLimit(2*36); - rlBegin(RL_LINES); for (int i = 0; i < 360; i += 10) { @@ -532,8 +512,6 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA float angle = startAngle; #if defined(SUPPORT_QUADS_DRAW_MODE) - rlCheckRenderBatchLimit(4*segments); - rlSetTexture(texShapes.id); rlBegin(RL_QUADS); @@ -559,8 +537,6 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA rlSetTexture(0); #else - rlCheckRenderBatchLimit(6*segments); - rlBegin(RL_TRIANGLES); for (int i = 0; i < segments; i++) { @@ -623,12 +599,7 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s float stepLength = (endAngle - startAngle)/(float)segments; float angle = startAngle; - bool showCapLines = true; - int limit = 4*(segments + 1); - if ((int)(endAngle - startAngle)%360 == 0) { limit = 4*segments; showCapLines = false; } - - rlCheckRenderBatchLimit(limit); rlBegin(RL_LINES); if (showCapLines) @@ -720,8 +691,6 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color } #if defined(SUPPORT_QUADS_DRAW_MODE) - rlCheckRenderBatchLimit(4); - rlSetTexture(texShapes.id); rlBegin(RL_QUADS); @@ -745,8 +714,6 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color rlSetTexture(0); #else - rlCheckRenderBatchLimit(6); - rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -781,8 +748,6 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color col // NOTE: Colors refer to corners, starting at top-lef corner and counter-clockwise void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4) { - rlCheckRenderBatchLimit(4); - rlSetTexture(texShapes.id); rlBegin(RL_QUADS); @@ -923,8 +888,6 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; #if defined(SUPPORT_QUADS_DRAW_MODE) - rlCheckRenderBatchLimit(16*segments/2 + 5*4); - rlSetTexture(texShapes.id); rlBegin(RL_QUADS); @@ -1022,8 +985,6 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co rlEnd(); rlSetTexture(0); #else - rlCheckRenderBatchLimit(12*segments + 5*6); // 4 corners with 3 vertices per segment + 5 rectangles with 6 vertices each - 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 @@ -1155,8 +1116,6 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo if (lineThick > 1) { #if defined(SUPPORT_QUADS_DRAW_MODE) - rlCheckRenderBatchLimit(4*4*segments + 4*4); // 4 corners with 4 vertices for each segment + 4 rectangles with 4 vertices each - rlSetTexture(texShapes.id); rlBegin(RL_QUADS); @@ -1229,8 +1188,6 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo rlEnd(); rlSetTexture(0); #else - rlCheckRenderBatchLimit(4*6*segments + 4*6); // 4 corners with 6(2*3) vertices for each segment + 4 rectangles with 6 vertices each - rlBegin(RL_TRIANGLES); // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner @@ -1296,8 +1253,6 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo else { // Use LINES to draw the outline - rlCheckRenderBatchLimit(8*segments + 4*2); // 4 corners with 2 vertices for each segment + 4 rectangles with 2 vertices each - rlBegin(RL_LINES); // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner @@ -1332,8 +1287,6 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { #if defined(SUPPORT_QUADS_DRAW_MODE) - rlCheckRenderBatchLimit(4); - rlSetTexture(texShapes.id); rlBegin(RL_QUADS); @@ -1354,8 +1307,6 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) rlSetTexture(0); #else - rlCheckRenderBatchLimit(3); - rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(v1.x, v1.y); @@ -1369,8 +1320,6 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) // NOTE: Vertex must be provided in counter-clockwise order void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { - rlCheckRenderBatchLimit(6); - rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(v1.x, v1.y); @@ -1391,8 +1340,6 @@ void DrawTriangleFan(Vector2 *points, int pointCount, Color color) { if (pointCount >= 3) { - rlCheckRenderBatchLimit((pointCount - 2)*4); - rlSetTexture(texShapes.id); rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); @@ -1422,8 +1369,6 @@ void DrawTriangleStrip(Vector2 *points, int pointCount, Color color) { if (pointCount >= 3) { - rlCheckRenderBatchLimit(3*(pointCount - 2)); - rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -1452,12 +1397,6 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col if (sides < 3) sides = 3; float centralAngle = 0.0f; -#if defined(SUPPORT_QUADS_DRAW_MODE) - rlCheckRenderBatchLimit(4*sides); // Each side is a quad -#else - rlCheckRenderBatchLimit(3*sides); -#endif - rlPushMatrix(); rlTranslatef(center.x, center.y, 0.0f); rlRotatef(rotation, 0.0f, 0.0f, 1.0f); @@ -1508,8 +1447,6 @@ void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Colo if (sides < 3) sides = 3; float centralAngle = 0.0f; - rlCheckRenderBatchLimit(2*sides); - rlPushMatrix(); rlTranslatef(center.x, center.y, 0.0f); rlRotatef(rotation, 0.0f, 0.0f, 1.0f); @@ -1534,12 +1471,6 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl float exteriorAngle = 360.0f/(float)sides; float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f)); -#if defined(SUPPORT_QUADS_DRAW_MODE) - rlCheckRenderBatchLimit(4*sides); -#else - rlCheckRenderBatchLimit(6*sides); -#endif - rlPushMatrix(); rlTranslatef(center.x, center.y, 0.0f); rlRotatef(rotation, 0.0f, 0.0f, 1.0f); diff --git a/src/rtextures.c b/src/rtextures.c index 2490d1d10..24fbafd2a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3362,8 +3362,6 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 bottomRight.y = y + (dx + dest.width)*sinRotation + (dy + dest.height)*cosRotation; } - rlCheckRenderBatchLimit(4); // Make sure there is enough free space on the batch buffer - rlSetTexture(texture.id); rlBegin(RL_QUADS); @@ -3494,8 +3492,6 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, coordD.x = (nPatchInfo.source.x + nPatchInfo.source.width)/width; coordD.y = (nPatchInfo.source.y + nPatchInfo.source.height)/height; - rlCheckRenderBatchLimit(9 * 3 * 2); // Maxium number of verts that could happen - rlSetTexture(texture.id); rlPushMatrix(); @@ -3639,8 +3635,6 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, // without crossing perimeter, points must be in anticlockwise order void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint) { - rlCheckRenderBatchLimit((pointCount - 1)*4); - rlSetTexture(texture.id); // Texturing is only supported on RL_QUADS From e59442bfabfb33de3cdee0907ce34ea3b3b92819 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 5 Sep 2022 18:23:25 +0200 Subject: [PATCH 0047/1710] REMOVED: `rlPushMatrix()`/`rlPopMatrix()` from `rshapes` This simplification will allow the usage of `rshapes` as STANDALONE mode in a future. Only a small set of `rlgl` functions are required and they can be "more" easely replaced if no `rlPushMatrix()`/`rlPopMatrix()` are involved. More simplification planned for the future, maybe the textures dependencies. --- examples/shapes/shapes_basic_shapes.c | 23 ++-- src/rshapes.c | 155 ++++++++++++-------------- 2 files changed, 83 insertions(+), 95 deletions(-) diff --git a/examples/shapes/shapes_basic_shapes.c b/examples/shapes/shapes_basic_shapes.c index 5b02a5498..d06c2ee15 100644 --- a/examples/shapes/shapes_basic_shapes.c +++ b/examples/shapes/shapes_basic_shapes.c @@ -2,7 +2,7 @@ * * raylib [shapes] example - Draw basic shapes 2d (rectangle, circle, line...) * -* Example originally created with raylib 1.0, last time updated with raylib 4.0 +* 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 @@ -25,6 +25,8 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - basic shapes drawing"); + float rotation = 0.0f; + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -33,7 +35,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - // TODO: Update your variables here + rotation += 0.2f; //---------------------------------------------------------------------------------- // Draw @@ -55,17 +57,18 @@ int main(void) DrawRectangleLines(screenWidth/4*2 - 40, 320, 80, 60, ORANGE); // NOTE: Uses QUADS internally, not lines // Triangle shapes and lines - DrawTriangle((Vector2){screenWidth/4.0f *3.0f, 80.0f}, - (Vector2){screenWidth/4.0f *3.0f - 60.0f, 150.0f}, - (Vector2){screenWidth/4.0f *3.0f + 60.0f, 150.0f}, VIOLET); + DrawTriangle((Vector2){ screenWidth/4.0f *3.0f, 80.0f }, + (Vector2){ screenWidth/4.0f *3.0f - 60.0f, 150.0f }, + (Vector2){ screenWidth/4.0f *3.0f + 60.0f, 150.0f }, VIOLET); - DrawTriangleLines((Vector2){screenWidth/4.0f*3.0f, 160.0f}, - (Vector2){screenWidth/4.0f*3.0f - 20.0f, 230.0f}, - (Vector2){screenWidth/4.0f*3.0f + 20.0f, 230.0f}, DARKBLUE); + DrawTriangleLines((Vector2){ screenWidth/4.0f*3.0f, 160.0f }, + (Vector2){ screenWidth/4.0f*3.0f - 20.0f, 230.0f }, + (Vector2){ screenWidth/4.0f*3.0f + 20.0f, 230.0f }, DARKBLUE); // Polygon shapes and lines - DrawPoly((Vector2){screenWidth/4.0f*3, 320}, 6, 80, 0, BROWN); - DrawPolyLinesEx((Vector2){screenWidth/4.0f*3, 320}, 6, 80, 0, 6, BEIGE); + DrawPoly((Vector2){ screenWidth/4.0f*3, 330 }, 6, 80, rotation, BROWN); + DrawPolyLines((Vector2){ screenWidth/4.0f*3, 330 }, 6, 90, rotation, BROWN); + DrawPolyLinesEx((Vector2){ screenWidth/4.0f*3, 330 }, 6, 85, rotation, 6, BEIGE); // NOTE: We draw all LINES based shapes together to optimize internal drawing, // this way, all LINES are rendered in a single draw pass diff --git a/src/rshapes.c b/src/rshapes.c index 458569a5f..d6e4da22a 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1395,129 +1395,114 @@ void DrawTriangleStrip(Vector2 *points, int pointCount, Color color) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) { if (sides < 3) sides = 3; - float centralAngle = 0.0f; - - rlPushMatrix(); - rlTranslatef(center.x, center.y, 0.0f); - rlRotatef(rotation, 0.0f, 0.0f, 1.0f); + float centralAngle = rotation; #if defined(SUPPORT_QUADS_DRAW_MODE) - rlSetTexture(texShapes.id); + rlSetTexture(texShapes.id); - rlBegin(RL_QUADS); - for (int i = 0; i < sides; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); + rlBegin(RL_QUADS); + for (int i = 0; i < sides; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); - rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(0, 0); + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); - } - rlEnd(); - rlSetTexture(0); + centralAngle += 360.0f/(float)sides; + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + } + rlEnd(); + rlSetTexture(0); #else - rlBegin(RL_TRIANGLES); - for (int i = 0; i < sides; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); + rlBegin(RL_TRIANGLES); + for (int i = 0; i < sides; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(0, 0); - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); - } - rlEnd(); + centralAngle += 360.0f/(float)sides; + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + } + rlEnd(); #endif - rlPopMatrix(); } // Draw a polygon outline of n sides void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color) { if (sides < 3) sides = 3; - float centralAngle = 0.0f; + float centralAngle = rotation; - rlPushMatrix(); - rlTranslatef(center.x, center.y, 0.0f); - rlRotatef(rotation, 0.0f, 0.0f, 1.0f); + rlBegin(RL_LINES); + for (int i = 0; i < sides; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); - rlBegin(RL_LINES); - for (int i = 0; i < sides; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); - - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); - } - rlEnd(); - rlPopMatrix(); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + centralAngle += 360.0f/(float)sides; + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + } + rlEnd(); } void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color) { if (sides < 3) sides = 3; - float centralAngle = 0.0f; + float centralAngle = rotation; float exteriorAngle = 360.0f/(float)sides; float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f)); - rlPushMatrix(); - rlTranslatef(center.x, center.y, 0.0f); - rlRotatef(rotation, 0.0f, 0.0f, 1.0f); - #if defined(SUPPORT_QUADS_DRAW_MODE) - rlSetTexture(texShapes.id); + rlSetTexture(texShapes.id); - rlBegin(RL_QUADS); - for (int i = 0; i < sides; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); + rlBegin(RL_QUADS); + for (int i = 0; i < sides; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); - rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(sinf(DEG2RAD*centralAngle)*innerRadius, cosf(DEG2RAD*centralAngle)*innerRadius); + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - centralAngle += exteriorAngle; - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); + centralAngle += exteriorAngle; + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(sinf(DEG2RAD*centralAngle)*innerRadius, cosf(DEG2RAD*centralAngle)*innerRadius); - } - rlEnd(); - rlSetTexture(0); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); + } + rlEnd(); + rlSetTexture(0); #else - rlBegin(RL_TRIANGLES); - for (int i = 0; i < sides; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); - float nextAngle = centralAngle + exteriorAngle; + rlBegin(RL_TRIANGLES); + for (int i = 0; i < sides; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + float nextAngle = centralAngle + exteriorAngle; - rlVertex2f(sinf(DEG2RAD*centralAngle)*radius, cosf(DEG2RAD*centralAngle)*radius); - rlVertex2f(sinf(DEG2RAD*centralAngle)*innerRadius, cosf(DEG2RAD*centralAngle)*innerRadius); - rlVertex2f(sinf(DEG2RAD*nextAngle)*radius, cosf(DEG2RAD*nextAngle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*radius, center.y + cosf(DEG2RAD*nextAngle)*radius); - rlVertex2f(sinf(DEG2RAD*centralAngle)*innerRadius, cosf(DEG2RAD*centralAngle)*innerRadius); - rlVertex2f(sinf(DEG2RAD*nextAngle)*radius, cosf(DEG2RAD*nextAngle)*radius); - rlVertex2f(sinf(DEG2RAD*nextAngle)*innerRadius, cosf(DEG2RAD*nextAngle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); + rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*radius, center.y + cosf(DEG2RAD*nextAngle)*radius); + rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*innerRadius, center.y + cosf(DEG2RAD*nextAngle)*innerRadius); - centralAngle = nextAngle; - } - rlEnd(); + centralAngle = nextAngle; + } + rlEnd(); #endif - rlPopMatrix(); } //---------------------------------------------------------------------------------- From b09725fa84f2dfc2c766cdcf4e9bc8aff097048d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 5 Sep 2022 18:45:33 +0200 Subject: [PATCH 0048/1710] REPLACED: `rlVertex2i()` by `rlVertex2f()` --- src/rshapes.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index d6e4da22a..12ed247f6 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -106,8 +106,8 @@ void DrawPixel(int posX, int posY, Color color) { rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2i(posX, posY); - rlVertex2i(posX + 1, posY + 1); + rlVertex2f(posX, posY); + rlVertex2f(posX + 1, posY + 1); rlEnd(); } @@ -126,8 +126,8 @@ void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color colo { rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2i(startPosX, startPosY); - rlVertex2i(endPosX, endPosY); + rlVertex2f(startPosX, startPosY); + rlVertex2f(endPosX, endPosY); rlEnd(); } @@ -786,17 +786,17 @@ void DrawRectangleLines(int posX, int posY, int width, int height, Color color) #else rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2i(posX + 1, posY + 1); - rlVertex2i(posX + width, posY + 1); + rlVertex2f(posX + 1, posY + 1); + rlVertex2f(posX + width, posY + 1); - rlVertex2i(posX + width, posY + 1); - rlVertex2i(posX + width, posY + height); + rlVertex2f(posX + width, posY + 1); + rlVertex2f(posX + width, posY + height); - rlVertex2i(posX + width, posY + height); - rlVertex2i(posX + 1, posY + height); + rlVertex2f(posX + width, posY + height); + rlVertex2f(posX + 1, posY + height); - rlVertex2i(posX + 1, posY + height); - rlVertex2i(posX + 1, posY + 1); + rlVertex2f(posX + 1, posY + height); + rlVertex2f(posX + 1, posY + 1); rlEnd(); #endif } From ac1ffbad1d80dc0fcebe4b105347764733b78ff3 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 7 Sep 2022 00:39:38 +0200 Subject: [PATCH 0049/1710] REVIEWED: Data type to unsigned --- src/raylib.h | 4 ++-- src/utils.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 3f03effe6..693da1409 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1039,8 +1039,8 @@ RLAPI void SetConfigFlags(unsigned int flags); // Setup init 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(int size); // Internal memory allocator -RLAPI void *MemRealloc(void *ptr, int size); // Internal memory reallocator +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 RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) diff --git a/src/utils.c b/src/utils.c index 9809e904a..208a483a7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -160,14 +160,14 @@ void TraceLog(int logType, const char *text, ...) // Internal memory allocator // NOTE: Initializes to zero by default -void *MemAlloc(int size) +void *MemAlloc(unsigned int size) { void *ptr = RL_CALLOC(size, 1); return ptr; } // Internal memory reallocator -void *MemRealloc(void *ptr, int size) +void *MemRealloc(void *ptr, unsigned int size) { void *ret = RL_REALLOC(ptr, size); return ret; From c1b4881e8a3ea86a3af6c3d5cb15f0a8322dd4b2 Mon Sep 17 00:00:00 2001 From: hartmannathan <59230071+hartmannathan@users.noreply.github.com> Date: Thu, 8 Sep 2022 10:10:15 -0400 Subject: [PATCH 0050/1710] examples/core/core_custom_logging.c: Fix typo (#2692) --- examples/core/core_custom_logging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_custom_logging.c b/examples/core/core_custom_logging.c index 94a9c6582..9794d14b9 100644 --- a/examples/core/core_custom_logging.c +++ b/examples/core/core_custom_logging.c @@ -18,7 +18,7 @@ #include // Required for: fopen(), fclose(), fputc(), fwrite(), printf(), fprintf(), funopen() #include // Required for: time_t, tm, time(), localtime(), strftime() -// Custom logging funtion +// Custom logging function void CustomLog(int msgType, const char *text, va_list args) { char timeStr[64] = { 0 }; From b478364914c64df9b963955da0d864b2616c0fe9 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Sep 2022 00:04:08 +0200 Subject: [PATCH 0051/1710] REVIEWED: Removed comment, fixes #2691 --- examples/models/models_loading_m3d.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index d11c3a005..819684073 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -9,8 +9,7 @@ * NOTES: * - Model3D (M3D) fileformat specs: https://gitlab.com/bztsrc/model3d * - Bender M3D exported: https://gitlab.com/bztsrc/model3d/-/tree/master/blender -* WARNING: Make sure to add "(action)" markers to the timeline if you want multiple animations. -* +* * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * From cf24c021a36a483c0ad04b43edd9ba248eaa9cc0 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Sep 2022 00:26:47 +0200 Subject: [PATCH 0052/1710] WARNING: BREAKING: Reviewed SSBO usage to avoid `long long` raylib library tries to avoid `long long` usage. Several SSBO functions have been reviewed (including some renames for consistency) to minimize `long long` type usage. --- examples/others/rlgl_compute_shader.c | 4 ++-- src/rlgl.h | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/others/rlgl_compute_shader.c b/examples/others/rlgl_compute_shader.c index 5e1debd7b..b74ed932f 100644 --- a/examples/others/rlgl_compute_shader.c +++ b/examples/others/rlgl_compute_shader.c @@ -104,9 +104,9 @@ int main(void) else if (transfertBuffer.count > 0) // Process transfert buffer { // Send SSBO buffer to GPU - rlUpdateShaderBufferElements(ssboTransfert, &transfertBuffer, sizeof(GolUpdateSSBO), 0); + rlUpdateShaderBuffer(ssboTransfert, &transfertBuffer, sizeof(GolUpdateSSBO), 0); - // Process ssbo commands on GPU + // Process SSBO commands on GPU rlEnableShader(golTransfertProgram); rlBindShaderBuffer(ssboA, 1); rlBindShaderBuffer(ssboTransfert, 3); diff --git a/src/rlgl.h b/src/rlgl.h index 2c83d08a4..5848deb29 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -678,15 +678,15 @@ RLAPI unsigned int rlLoadComputeShaderProgram(unsigned int shaderId); RLAPI void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ); // Dispatch compute shader (equivalent to *draw* for graphics pilepine) // Shader buffer storage object management (ssbo) -RLAPI unsigned int rlLoadShaderBuffer(unsigned long long size, const void *data, int usageHint); // Load shader storage buffer object (SSBO) +RLAPI unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint); // Load shader storage buffer object (SSBO) RLAPI void rlUnloadShaderBuffer(unsigned int ssboId); // Unload shader storage buffer object (SSBO) -RLAPI void rlUpdateShaderBufferElements(unsigned int id, const void *data, unsigned long long dataSize, unsigned long long offset); // Update SSBO buffer data -RLAPI unsigned long long rlGetShaderBufferSize(unsigned int id); // Get SSBO buffer size -RLAPI void rlReadShaderBufferElements(unsigned int id, void *dest, unsigned long long count, unsigned long long offset); // Bind SSBO buffer -RLAPI void rlBindShaderBuffer(unsigned int id, unsigned int index); // Copy SSBO buffer data +RLAPI void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSize, unsigned int offset); // Update SSBO buffer data +RLAPI void rlBindShaderBuffer(unsigned int id, unsigned int index); // Bind SSBO buffer +RLAPI void rlReadShaderBuffer(unsigned int id, void *dest, unsigned int count, unsigned int offset); // Read SSBO buffer data (GPU->CPU) +RLAPI void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int destOffset, unsigned int srcOffset, unsigned int count); // Copy SSBO data between buffers +RLAPI unsigned int rlGetShaderBufferSize(unsigned int id); // Get SSBO buffer size // Buffer management -RLAPI void rlCopyBuffersElements(unsigned int destId, unsigned int srcId, unsigned long long destOffset, unsigned long long srcOffset, unsigned long long count); // Copy SSBO buffer data RLAPI void rlBindImageTexture(unsigned int id, unsigned int index, unsigned int format, int readonly); // Bind image texture // Matrix state management @@ -3954,7 +3954,7 @@ void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned } // Load shader storage buffer object (SSBO) -unsigned int rlLoadShaderBuffer(unsigned long long size, const void *data, int usageHint) +unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint) { unsigned int ssbo = 0; @@ -3978,7 +3978,7 @@ void rlUnloadShaderBuffer(unsigned int ssboId) } // Update SSBO buffer data -void rlUpdateShaderBufferElements(unsigned int id, const void *data, unsigned long long dataSize, unsigned long long offset) +void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSize, unsigned int offset) { #if defined(GRAPHICS_API_OPENGL_43) glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); @@ -3987,7 +3987,7 @@ void rlUpdateShaderBufferElements(unsigned int id, const void *data, unsigned lo } // Get SSBO buffer size -unsigned long long rlGetShaderBufferSize(unsigned int id) +unsigned int rlGetShaderBufferSize(unsigned int id) { long long size = 0; @@ -3996,11 +3996,11 @@ unsigned long long rlGetShaderBufferSize(unsigned int id) glGetInteger64v(GL_SHADER_STORAGE_BUFFER_SIZE, &size); #endif - return (size > 0)? size : 0; + return (size > 0)? (unsigned int)size : 0; } -// Read SSBO buffer data -void rlReadShaderBufferElements(unsigned int id, void *dest, unsigned long long count, unsigned long long offset) +// Read SSBO buffer data (GPU->CPU) +void rlReadShaderBuffer(unsigned int id, void *dest, unsigned int count, unsigned int offset) { #if defined(GRAPHICS_API_OPENGL_43) glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); @@ -4017,7 +4017,7 @@ void rlBindShaderBuffer(unsigned int id, unsigned int index) } // Copy SSBO buffer data -void rlCopyBuffersElements(unsigned int destId, unsigned int srcId, unsigned long long destOffset, unsigned long long srcOffset, unsigned long long count) +void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int destOffset, unsigned int srcOffset, unsigned int count) { #if defined(GRAPHICS_API_OPENGL_43) glBindBuffer(GL_COPY_READ_BUFFER, srcId); From cf76d234769be4773230fc1674acee837525ad40 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Sep 2022 10:23:38 +0200 Subject: [PATCH 0053/1710] Minor format tweaks --- examples/text/text_codepoints_loading.c | 1 - examples/text/text_unicode.c | 7 ++++--- examples/textures/textures_draw_tiled.c | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index 921b0e752..3b6e11609 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -24,7 +24,6 @@ static char *text = "いろはにほへと ちりぬるを\nわかよたれそ // Remove codepoint duplicates if requested static int *CodepointRemoveDuplicates(int *codepoints, int codepointCount, int *codepointResultCount); - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index fb6fec87c..4af542655 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -153,7 +153,7 @@ static int hovered = -1, selected = -1; //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ -int main(int argc, char **argv) +int main(void) { // Initialization //-------------------------------------------------------------------------------------- @@ -267,6 +267,7 @@ int main(int argc, char **argv) a = b; b = tmp; } + if (msgRect.x + msgRect.width > screenWidth) msgRect.x -= (msgRect.x + msgRect.width) - screenWidth + 10; // Draw chat bubble @@ -286,11 +287,11 @@ int main(int argc, char **argv) DrawText(info, (int)pos.x, (int)pos.y, 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(); //---------------------------------------------------------------------------------- } diff --git a/examples/textures/textures_draw_tiled.c b/examples/textures/textures_draw_tiled.c index 517fabc2d..908bf238d 100644 --- a/examples/textures/textures_draw_tiled.c +++ b/examples/textures/textures_draw_tiled.c @@ -12,6 +12,7 @@ * Copyright (c) 2020-2022 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ + #include "raylib.h" #define SIZEOF(A) (sizeof(A)/sizeof(A[0])) @@ -22,7 +23,7 @@ //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ -int main(int argc, char **argv) +int main(void) { // Initialization //-------------------------------------------------------------------------------------- From 853c66baedb342968c818e62422eb043ba4fb0c7 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Sep 2022 23:56:52 +0200 Subject: [PATCH 0054/1710] REVIEWED: `CheckCollisionPointPoly()` --- src/rshapes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rshapes.c b/src/rshapes.c index 12ed247f6..0a016eabb 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1555,7 +1555,7 @@ bool CheckCollisionPointPoly(Vector2 point, Vector2 *points, int pointCount) if (pointCount > 2) { - for (int i = 0; i < pointCount; i++) + for (int i = 0; i < pointCount - 1; i++) { Vector2 vc = points[i]; Vector2 vn = points[i + 1]; From a12ddacb7bfbc6e552e6145456f2fe6dfdfbe1c7 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Mon, 12 Sep 2022 12:21:41 +0200 Subject: [PATCH 0055/1710] Enable DXT compression on __APPLE__ targets (#2694) --- src/rlgl.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 5848deb29..6a0d715eb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2029,13 +2029,16 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.texAnisoFilter = true; RLGL.ExtSupported.texMirrorClamp = true; #if defined(GRAPHICS_API_OPENGL_43) - if (GLAD_GL_ARB_compute_shader) RLGL.ExtSupported.computeShader = true; - if (GLAD_GL_ARB_shader_storage_buffer_object) RLGL.ExtSupported.ssbo = true; + RLGL.ExtSupported.computeShader = GLAD_GL_ARB_compute_shader; + RLGL.ExtSupported.ssbo = GLAD_GL_ARB_shader_storage_buffer_object; #endif - #if !defined(__APPLE__) + #if defined(__APPLE__) + // Apple provides its own extension macros + RLGL.ExtSupported.texCompDXT = GL_EXT_texture_compression_s3tc; + #else // NOTE: With GLAD, we can check if an extension is supported using the GLAD_GL_xxx booleans - if (GLAD_GL_EXT_texture_compression_s3tc) RLGL.ExtSupported.texCompDXT = true; // Texture compression: DXT - if (GLAD_GL_ARB_ES3_compatibility) RLGL.ExtSupported.texCompETC2 = true; // Texture compression: ETC2/EAC + RLGL.ExtSupported.texCompDXT = GLAD_GL_EXT_texture_compression_s3tc; // Texture compression: DXT + RLGL.ExtSupported.texCompETC2 = GLAD_GL_ARB_ES3_compatibility; // Texture compression: ETC2/EAC #endif #endif // GRAPHICS_API_OPENGL_33 From eaa0b9102b3751617b9856d3a60eed26354c4140 Mon Sep 17 00:00:00 2001 From: murilluhenrique <90881619+murilluhenrique@users.noreply.github.com> Date: Thu, 15 Sep 2022 19:06:25 -0300 Subject: [PATCH 0056/1710] Fix typo (#2696) --- examples/core/core_loading_thread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 614adb690..09ec76460 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [cpre] example - loading thread +* raylib [core] example - loading thread * * NOTE: This example requires linking with pthreads library on MinGW, * it can be accomplished passing -static parameter to compiler From cb9b8f73c08e720ef6b8762192c4efe9521a1185 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Sep 2022 13:39:49 +0200 Subject: [PATCH 0057/1710] ADDED: `GenImagePerlinNoise()` --- src/external/stb_perlin.h | 428 ++++++++++++++++++++++++++++++++++++++ src/raylib.h | 1 + src/rtextures.c | 51 ++++- 3 files changed, 475 insertions(+), 5 deletions(-) create mode 100644 src/external/stb_perlin.h diff --git a/src/external/stb_perlin.h b/src/external/stb_perlin.h new file mode 100644 index 000000000..47cb9a437 --- /dev/null +++ b/src/external/stb_perlin.h @@ -0,0 +1,428 @@ +// stb_perlin.h - v0.5 - perlin noise +// public domain single-file C implementation by Sean Barrett +// +// LICENSE +// +// See end of file. +// +// +// to create the implementation, +// #define STB_PERLIN_IMPLEMENTATION +// in *one* C/CPP file that includes this file. +// +// +// Documentation: +// +// float stb_perlin_noise3( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0) +// +// This function computes a random value at the coordinate (x,y,z). +// Adjacent random values are continuous but the noise fluctuates +// its randomness with period 1, i.e. takes on wholly unrelated values +// at integer points. Specifically, this implements Ken Perlin's +// revised noise function from 2002. +// +// The "wrap" parameters can be used to create wraparound noise that +// wraps at powers of two. The numbers MUST be powers of two. Specify +// 0 to mean "don't care". (The noise always wraps every 256 due +// details of the implementation, even if you ask for larger or no +// wrapping.) +// +// float stb_perlin_noise3_seed( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0, +// int seed) +// +// As above, but 'seed' selects from multiple different variations of the +// noise function. The current implementation only uses the bottom 8 bits +// of 'seed', but possibly in the future more bits will be used. +// +// +// Fractal Noise: +// +// Three common fractal noise functions are included, which produce +// a wide variety of nice effects depending on the parameters +// provided. Note that each function will call stb_perlin_noise3 +// 'octaves' times, so this parameter will affect runtime. +// +// float stb_perlin_ridge_noise3(float x, float y, float z, +// float lacunarity, float gain, float offset, int octaves) +// +// float stb_perlin_fbm_noise3(float x, float y, float z, +// float lacunarity, float gain, int octaves) +// +// float stb_perlin_turbulence_noise3(float x, float y, float z, +// float lacunarity, float gain, int octaves) +// +// Typical values to start playing with: +// octaves = 6 -- number of "octaves" of noise3() to sum +// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) +// gain = 0.5 -- relative weighting applied to each successive octave +// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure +// +// +// Contributors: +// Jack Mott - additional noise functions +// Jordan Peck - seeded noise +// + + +#ifdef __cplusplus +extern "C" { +#endif +extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap); +extern float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed); +extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves); +extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); +extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves); +extern float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed); +#ifdef __cplusplus +} +#endif + +#ifdef STB_PERLIN_IMPLEMENTATION + +#include // fabs() + +// not same permutation table as Perlin's reference to avoid copyright issues; +// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/ +static unsigned char stb__perlin_randtab[512] = +{ + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, + + // and a second copy so we don't need an extra mask or static initializer + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, +}; + + +// perlin's gradient has 12 cases so some get used 1/16th of the time +// and some 2/16ths. We reduce bias by changing those fractions +// to 5/64ths and 6/64ths + +// this array is designed to match the previous implementation +// of gradient hash: indices[stb__perlin_randtab[i]&63] +static unsigned char stb__perlin_randtab_grad_idx[512] = +{ + 7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7, + 8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8, + 7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8, + 8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5, + 5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1, + 2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4, + 9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11, + 1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6, + 10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0, + 6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2, + 4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3, + 11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11, + 10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1, + 3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10, + 11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7, + 9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5, + + // and a second copy so we don't need an extra mask or static initializer + 7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7, + 8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8, + 7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8, + 8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5, + 5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1, + 2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4, + 9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11, + 1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6, + 10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0, + 6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2, + 4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3, + 11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11, + 10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1, + 3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10, + 11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7, + 9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5, +}; + +static float stb__perlin_lerp(float a, float b, float t) +{ + return a + (b-a) * t; +} + +static int stb__perlin_fastfloor(float a) +{ + int ai = (int) a; + return (a < ai) ? ai-1 : ai; +} + +// different grad function from Perlin's, but easy to modify to match reference +static float stb__perlin_grad(int grad_idx, float x, float y, float z) +{ + static float basis[12][4] = + { + { 1, 1, 0 }, + { -1, 1, 0 }, + { 1,-1, 0 }, + { -1,-1, 0 }, + { 1, 0, 1 }, + { -1, 0, 1 }, + { 1, 0,-1 }, + { -1, 0,-1 }, + { 0, 1, 1 }, + { 0,-1, 1 }, + { 0, 1,-1 }, + { 0,-1,-1 }, + }; + + float *grad = basis[grad_idx]; + return grad[0]*x + grad[1]*y + grad[2]*z; +} + +float stb_perlin_noise3_internal(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + unsigned int x_mask = (x_wrap-1) & 255; + unsigned int y_mask = (y_wrap-1) & 255; + unsigned int z_mask = (z_wrap-1) & 255; + int px = stb__perlin_fastfloor(x); + int py = stb__perlin_fastfloor(y); + int pz = stb__perlin_fastfloor(z); + int x0 = px & x_mask, x1 = (px+1) & x_mask; + int y0 = py & y_mask, y1 = (py+1) & y_mask; + int z0 = pz & z_mask, z1 = (pz+1) & z_mask; + int r0,r1, r00,r01,r10,r11; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0+seed]; + r1 = stb__perlin_randtab[x1+seed]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} + +float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap) +{ + return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap,0); +} + +float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed) +{ + return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap, (unsigned char) seed); +} + +float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves) +{ + int i; + float frequency = 1.0f; + float prev = 1.0f; + float amplitude = 0.5f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i); + r = offset - (float) fabs(r); + r = r*r; + sum += r*amplitude*prev; + prev = r; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves) +{ + int i; + float frequency = 1.0f; + float amplitude = 1.0f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + sum += stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves) +{ + int i; + float frequency = 1.0f; + float amplitude = 1.0f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude; + sum += (float) fabs(r); + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + int px = stb__perlin_fastfloor(x); + int py = stb__perlin_fastfloor(y); + int pz = stb__perlin_fastfloor(z); + int x_wrap2 = (x_wrap ? x_wrap : 256); + int y_wrap2 = (y_wrap ? y_wrap : 256); + int z_wrap2 = (z_wrap ? z_wrap : 256); + int x0 = px % x_wrap2, x1; + int y0 = py % y_wrap2, y1; + int z0 = pz % z_wrap2, z1; + int r0,r1, r00,r01,r10,r11; + + if (x0 < 0) x0 += x_wrap2; + if (y0 < 0) y0 += y_wrap2; + if (z0 < 0) z0 += z_wrap2; + x1 = (x0+1) % x_wrap2; + y1 = (y0+1) % y_wrap2; + z1 = (z0+1) % z_wrap2; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0]; + r0 = stb__perlin_randtab[r0+seed]; + r1 = stb__perlin_randtab[x1]; + r1 = stb__perlin_randtab[r1+seed]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} +#endif // STB_PERLIN_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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/src/raylib.h b/src/raylib.h index 693da1409..ac5d6e6af 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1241,6 +1241,7 @@ RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise +RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells // Image manipulation functions diff --git a/src/rtextures.c b/src/rtextures.c index 24fbafd2a..d602081ba 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -70,12 +70,12 @@ #if defined(SUPPORT_MODULE_RTEXTURES) -#include "utils.h" // Required for: TRACELOG() and fopen() Android mapping +#include "utils.h" // Required for: TRACELOG() #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2 #include // Required for: malloc(), free() #include // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()] -#include // Required for: fabsf() +#include // Required for: fabsf() [Used in DrawTextureRec()] #include // Required for: sprintf() [Used in ExportImageAsCode()] // Support only desired texture formats on stb_image @@ -147,6 +147,11 @@ #include "external/stb_image_write.h" // Required for: stbi_write_*() #endif +#if defined(SUPPORT_IMAGE_GENERATION) + #define STB_PERLIN_IMPLEMENTATION + #include "external/stb_perlin.h" // Required for: stb_perlin_fbm_noise3 +#endif + #if defined(SUPPORT_IMAGE_MANIPULATION) #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size)) #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr)) @@ -760,6 +765,42 @@ Image GenImageWhiteNoise(int width, int height, float factor) return image; } +// Generate image: perlin noise +Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale) +{ + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + float nx = (float)(x + offsetX)*scale/(float)width; + float ny = (float)(y + offsetY)*scale/(float)height; + + // Typical values to start playing with: + // lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) + // gain = 0.5 -- relative weighting applied to each successive octave + // octaves = 6 -- number of "octaves" of noise3() to sum + + // NOTE: We need to translate the data from [-1..1] to [0..1] + float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6) + 1.0f)/2.0f; + + int intensity = (int)(p*255.0f); + pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 }; + } + } + + Image image = { + .data = pixels, + .width = width, + .height = height, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, + .mipmaps = 1 + }; + + return image; +} + // Generate image: cellular algorithm. Bigger tileSize means bigger cells Image GenImageCellular(int width, int height, int tileSize) { @@ -3181,19 +3222,19 @@ void SetTextureWrap(Texture2D texture, int wrap) //------------------------------------------------------------------------------------ // Texture drawing functions //------------------------------------------------------------------------------------ -// Draw a Texture2D +// Draw a texture void DrawTexture(Texture2D texture, int posX, int posY, Color tint) { DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0.0f, 1.0f, tint); } -// Draw a Texture2D with position defined as Vector2 +// Draw a texture with position defined as Vector2 void DrawTextureV(Texture2D texture, Vector2 position, Color tint) { DrawTextureEx(texture, position, 0, 1.0f, tint); } -// Draw a Texture2D with extended parameters +// Draw a texture with extended parameters void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint) { Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height }; From c328f09efccce43293644c8fad0ea0bd7e7c94a9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Sep 2022 19:26:23 +0200 Subject: [PATCH 0058/1710] Move compressed textures loading to a separate self-contained library --- src/external/rl_gputex.h | 818 +++++++++++++++++++++++++++++++++ src/rtextures.c | 945 +++++++-------------------------------- 2 files changed, 969 insertions(+), 794 deletions(-) create mode 100644 src/external/rl_gputex.h diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h new file mode 100644 index 000000000..22bb72816 --- /dev/null +++ b/src/external/rl_gputex.h @@ -0,0 +1,818 @@ +/********************************************************************************************** +* +* rl_gputex - 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; + 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); + 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); + 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); + 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); + 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); + 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*2; + 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, width = width, height = height; i < mipmaps; i++) + { + data_size += get_pixel_data_size(width, height, format); + width /= 2; height /= 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 diff --git a/src/rtextures.c b/src/rtextures.c index d602081ba..12ba4923b 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -104,6 +104,22 @@ #define STBI_NO_HDR #endif +#if defined(SUPPORT_FILEFORMAT_DDS) + #define RL_GPUTEX_SUPPORT_DDS +#endif +#if defined(SUPPORT_FILEFORMAT_PKM) + #define RL_GPUTEX_SUPPORT_PKM +#endif +#if defined(SUPPORT_FILEFORMAT_KTX) + #define RL_GPUTEX_SUPPORT_KTX +#endif +#if defined(SUPPORT_FILEFORMAT_PVR) + #define RL_GPUTEX_SUPPORT_PVR +#endif +#if defined(SUPPORT_FILEFORMAT_ASTC) + #define RL_GPUTEX_SUPPORT_ASTC +#endif + // Image fileformats not supported by default #define STBI_NO_PIC #define STBI_NO_PNM // Image format .ppm and .pgm @@ -130,6 +146,17 @@ // NOTE: Used to read image data (multiple formats support) #endif +#if (defined(SUPPORT_FILEFORMAT_DDS) || \ + defined(SUPPORT_FILEFORMAT_PKM) || \ + defined(SUPPORT_FILEFORMAT_KTX) || \ + defined(SUPPORT_FILEFORMAT_PVR) || \ + defined(SUPPORT_FILEFORMAT_ASTC)) + + #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) +#endif + #if defined(SUPPORT_FILEFORMAT_QOI) #define QOI_MALLOC RL_MALLOC #define QOI_FREE RL_FREE @@ -185,23 +212,6 @@ //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -#if defined(SUPPORT_FILEFORMAT_DDS) -static Image LoadDDS(const unsigned char *fileData, unsigned int fileSize); // Load DDS file data -#endif -#if defined(SUPPORT_FILEFORMAT_PKM) -static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize); // Load PKM file data -#endif -#if defined(SUPPORT_FILEFORMAT_KTX) -static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize); // Load KTX file data -static int SaveKTX(Image image, const char *fileName); // Save image data as KTX file -#endif -#if defined(SUPPORT_FILEFORMAT_PVR) -static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize); // Load PVR file data -#endif -#if defined(SUPPORT_FILEFORMAT_ASTC) -static Image LoadASTC(const unsigned char *fileData, unsigned int fileSize); // Load ASTC file data -#endif - static Vector4 *LoadImageDataNormalized(Image image); // Load pixel data from image as Vector4 array (float normalized) //---------------------------------------------------------------------------------- @@ -333,7 +343,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #if defined(SUPPORT_FILEFORMAT_PSD) || (strcmp(fileType, ".psd") == 0) #endif - ) + ) { #if defined(STBI_REQUIRED) // NOTE: Using stb_image to load images (Supports multiple image formats) @@ -390,19 +400,35 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } #endif #if defined(SUPPORT_FILEFORMAT_DDS) - else if (strcmp(fileType, ".dds") == 0) image = LoadDDS(fileData, dataSize); + else if (strcmp(fileType, ".dds") == 0) + { + int format = 0; + image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif #if defined(SUPPORT_FILEFORMAT_PKM) - else if (strcmp(fileType, ".pkm") == 0) image = LoadPKM(fileData, dataSize); + else if (strcmp(fileType, ".pkm") == 0) + { + image.data = rl_load_pkm_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif #if defined(SUPPORT_FILEFORMAT_KTX) - else if (strcmp(fileType, ".ktx") == 0) image = LoadKTX(fileData, dataSize); + else if (strcmp(fileType, ".ktx") == 0) + { + image.data = rl_load_ktx_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif #if defined(SUPPORT_FILEFORMAT_PVR) - else if (strcmp(fileType, ".pvr") == 0) image = LoadPVR(fileData, dataSize); + else if (strcmp(fileType, ".pvr") == 0) + { + image.data = rl_load_pvr_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif #if defined(SUPPORT_FILEFORMAT_ASTC) - else if (strcmp(fileType, ".astc") == 0) image = LoadASTC(fileData, dataSize); + else if (strcmp(fileType, ".astc") == 0) + { + image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); + } #endif else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported"); @@ -528,7 +554,10 @@ bool ExportImage(Image image, const char *fileName) } #endif #if defined(SUPPORT_FILEFORMAT_KTX) - else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName); + else if (IsFileExtension(fileName, ".ktx")) + { + success = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); + } #endif else if (IsFileExtension(fileName, ".raw")) { @@ -3253,104 +3282,6 @@ void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color DrawTexturePro(texture, source, dest, origin, 0.0f, tint); } -// Draw texture quad with tiling and offset parameters -// NOTE: Tiling and offset should be provided considering normalized texture values [0..1] -// i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center -void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint) -{ - // WARNING: This solution only works if TEXTURE_WRAP_REPEAT is supported, - // NPOT textures supported is required and OpenGL ES 2.0 could not support it - Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height }; - Vector2 origin = { 0.0f, 0.0f }; - - DrawTexturePro(texture, source, quad, origin, 0.0f, tint); -} - -// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. -// NOTE: For tilling a whole texture DrawTextureQuad() is better -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); - } - } - } -} - // Draw a part of a texture (defined by a rectangle) with 'pro' parameters // NOTE: origin is relative to destination rectangle size void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint) @@ -3474,6 +3405,104 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 } } +// Draw texture quad with tiling and offset parameters +// NOTE: Tiling and offset should be provided considering normalized texture values [0..1] +// i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center +void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint) +{ + // WARNING: This solution only works if TEXTURE_WRAP_REPEAT is supported, + // NPOT textures supported is required and OpenGL ES 2.0 could not support it + Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height }; + Vector2 origin = { 0.0f, 0.0f }; + + DrawTexturePro(texture, source, quad, origin, 0.0f, tint); +} + +// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. +// NOTE: For tilling a whole texture DrawTextureQuad() is better +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); + } + } + } +} + // Draws a texture (or part of it) that stretches or shrinks nicely using n-patch info void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint) { @@ -4106,678 +4135,6 @@ int GetPixelDataSize(int width, int height, int format) //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- -#if defined(SUPPORT_FILEFORMAT_DDS) -// Loading DDS image data (compressed or uncompressed) -static Image LoadDDS(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // 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 rgbBitCount; - unsigned int rBitMask; - unsigned int gBitMask; - unsigned int bBitMask; - unsigned int aBitMask; - } DDSPixelFormat; - - // DDS Header (124 bytes) - typedef struct { - unsigned int size; - unsigned int flags; - unsigned int height; - unsigned int width; - unsigned int pitchOrLinearSize; - unsigned int depth; - unsigned int mipmapCount; - unsigned int reserved1[11]; - DDSPixelFormat ddspf; - unsigned int caps; - unsigned int caps2; - unsigned int caps3; - unsigned int caps4; - unsigned int reserved2; - } DDSHeader; - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - // Verify the type of file - unsigned char *ddsHeaderId = fileDataPtr; - fileDataPtr += 4; - - if ((ddsHeaderId[0] != 'D') || (ddsHeaderId[1] != 'D') || (ddsHeaderId[2] != 'S') || (ddsHeaderId[3] != ' ')) - { - TRACELOG(LOG_WARNING, "IMAGE: DDS file data not valid"); - } - else - { - DDSHeader *ddsHeader = (DDSHeader *)fileDataPtr; - - TRACELOGD("IMAGE: DDS file data info:"); - TRACELOGD(" > Header size: %i", sizeof(DDSHeader)); - TRACELOGD(" > Pixel format size: %i", ddsHeader->ddspf.size); - TRACELOGD(" > Pixel format flags: 0x%x", ddsHeader->ddspf.flags); - TRACELOGD(" > File format: 0x%x", ddsHeader->ddspf.fourCC); - TRACELOGD(" > File bit count: 0x%x", ddsHeader->ddspf.rgbBitCount); - - fileDataPtr += sizeof(DDSHeader); // Skip header - - image.width = ddsHeader->width; - image.height = ddsHeader->height; - - if (ddsHeader->mipmapCount == 0) image.mipmaps = 1; // Parameter not used - else image.mipmaps = ddsHeader->mipmapCount; - - if (ddsHeader->ddspf.rgbBitCount == 16) // 16bit mode, no compressed - { - if (ddsHeader->ddspf.flags == 0x40) // no alpha channel - { - int dataSize = image.width*image.height*sizeof(unsigned short); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - image.format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; - } - else if (ddsHeader->ddspf.flags == 0x41) // with alpha channel - { - if (ddsHeader->ddspf.aBitMask == 0x8000) // 1bit alpha - { - int dataSize = image.width*image.height*sizeof(unsigned short); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - unsigned char alpha = 0; - - // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1 - for (int i = 0; i < image.width*image.height; 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; - } - - image.format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; - } - else if (ddsHeader->ddspf.aBitMask == 0xf000) // 4bit alpha - { - int dataSize = image.width*image.height*sizeof(unsigned short); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - unsigned char alpha = 0; - - // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4 - for (int i = 0; i < image.width*image.height; 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; - } - - image.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; - } - } - } - else if (ddsHeader->ddspf.flags == 0x40 && ddsHeader->ddspf.rgbBitCount == 24) // DDS_RGB, no compressed - { - int dataSize = image.width*image.height*3*sizeof(unsigned char); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; - } - else if (ddsHeader->ddspf.flags == 0x41 && ddsHeader->ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed - { - int dataSize = image.width*image.height*4*sizeof(unsigned char); - image.data = (unsigned short *)RL_MALLOC(dataSize); - - memcpy(image.data, fileDataPtr, dataSize); - - 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.width*image.height*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; - } - - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - } - else if (((ddsHeader->ddspf.flags == 0x04) || (ddsHeader->ddspf.flags == 0x05)) && (ddsHeader->ddspf.fourCC > 0)) // Compressed - { - int dataSize = 0; - - // Calculate data size, including all mipmaps - if (ddsHeader->mipmapCount > 1) dataSize = ddsHeader->pitchOrLinearSize*2; - else dataSize = ddsHeader->pitchOrLinearSize; - - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - - switch (ddsHeader->ddspf.fourCC) - { - case FOURCC_DXT1: - { - if (ddsHeader->ddspf.flags == 0x04) image.format = PIXELFORMAT_COMPRESSED_DXT1_RGB; - else image.format = PIXELFORMAT_COMPRESSED_DXT1_RGBA; - } break; - case FOURCC_DXT3: image.format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break; - case FOURCC_DXT5: image.format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break; - default: break; - } - } - } - } - - return image; -} -#endif - -#if defined(SUPPORT_FILEFORMAT_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) -static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // 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) (origWidth rounded to multiple of 4) - unsigned short height; // Texture height (big-endian) (origHeight rounded to multiple of 4) - unsigned short origWidth; // Original width (big-endian) - unsigned short origHeight; // Original height (big-endian) - } PKMHeader; - - // 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) - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - PKMHeader *pkmHeader = (PKMHeader *)fileDataPtr; - - if ((pkmHeader->id[0] != 'P') || (pkmHeader->id[1] != 'K') || (pkmHeader->id[2] != 'M') || (pkmHeader->id[3] != ' ')) - { - TRACELOG(LOG_WARNING, "IMAGE: PKM file data not valid"); - } - else - { - fileDataPtr += sizeof(PKMHeader); // Skip header - - // NOTE: format, width and height come as big-endian, data must be swapped to little-endian - pkmHeader->format = ((pkmHeader->format & 0x00FF) << 8) | ((pkmHeader->format & 0xFF00) >> 8); - pkmHeader->width = ((pkmHeader->width & 0x00FF) << 8) | ((pkmHeader->width & 0xFF00) >> 8); - pkmHeader->height = ((pkmHeader->height & 0x00FF) << 8) | ((pkmHeader->height & 0xFF00) >> 8); - - TRACELOGD("IMAGE: PKM file data info:"); - TRACELOGD(" > Image width: %i", pkmHeader->width); - TRACELOGD(" > Image height: %i", pkmHeader->height); - TRACELOGD(" > Image format: %i", pkmHeader->format); - - image.width = pkmHeader->width; - image.height = pkmHeader->height; - image.mipmaps = 1; - - int bpp = 4; - if (pkmHeader->format == 3) bpp = 8; - - int dataSize = image.width*image.height*bpp/8; // Total data size in bytes - - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - - if (pkmHeader->format == 0) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB; - else if (pkmHeader->format == 1) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB; - else if (pkmHeader->format == 3) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; - } - } - - return image; -} -#endif - -#if defined(SUPPORT_FILEFORMAT_KTX) -// Load KTX compressed image data (ETC1/ETC2 compression) -// TODO: Review KTX loading, many things changed! -static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // 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/ - - // 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 glType; // For compressed textures, glType must equal 0 - unsigned int glTypeSize; // For compressed texture data, usually 1 - unsigned int glFormat; // For compressed textures is 0 - unsigned int glInternalFormat; // Compressed internal format - unsigned int glBaseInternalFormat; // 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 mipmapLevels; // Non-mipmapped textures = 1 - unsigned int keyValueDataSize; // Used to encode any arbitrary data... - } KTXHeader; - - // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - KTXHeader *ktxHeader = (KTXHeader *)fileDataPtr; - - if ((ktxHeader->id[1] != 'K') || (ktxHeader->id[2] != 'T') || (ktxHeader->id[3] != 'X') || - (ktxHeader->id[4] != ' ') || (ktxHeader->id[5] != '1') || (ktxHeader->id[6] != '1')) - { - TRACELOG(LOG_WARNING, "IMAGE: KTX file data not valid"); - } - else - { - fileDataPtr += sizeof(KTXHeader); // Move file data pointer - - image.width = ktxHeader->width; - image.height = ktxHeader->height; - image.mipmaps = ktxHeader->mipmapLevels; - - TRACELOGD("IMAGE: KTX file data info:"); - TRACELOGD(" > Image width: %i", ktxHeader->width); - TRACELOGD(" > Image height: %i", ktxHeader->height); - TRACELOGD(" > Image format: 0x%x", ktxHeader->glInternalFormat); - - fileDataPtr += ktxHeader->keyValueDataSize; // Skip value data size - - int dataSize = ((int *)fileDataPtr)[0]; - fileDataPtr += sizeof(int); - - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - - if (ktxHeader->glInternalFormat == 0x8D64) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB; - else if (ktxHeader->glInternalFormat == 0x9274) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB; - else if (ktxHeader->glInternalFormat == 0x9278) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; - - // TODO: Support uncompressed data formats? Right now it returns format = 0! - } - } - - return image; -} - -// 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! -static int SaveKTX(Image image, const char *fileName) -{ - // 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 glType; // For compressed textures, glType must equal 0 - unsigned int glTypeSize; // For compressed texture data, usually 1 - unsigned int glFormat; // For compressed textures is 0 - unsigned int glInternalFormat; // Compressed internal format - unsigned int glBaseInternalFormat; // 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 mipmapLevels; // Non-mipmapped textures = 1 - unsigned int keyValueDataSize; // 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... - } KTXHeader; - - // Calculate file dataSize required - int dataSize = sizeof(KTXHeader); - - for (int i = 0, width = image.width, height = image.height; i < image.mipmaps; i++) - { - dataSize += GetPixelDataSize(width, height, image.format); - width /= 2; height /= 2; - } - - unsigned char *fileData = RL_CALLOC(dataSize, 1); - unsigned char *fileDataPtr = fileData; - - KTXHeader ktxHeader = { 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 ktxIdentifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' }; - - // Get the image header - memcpy(ktxHeader.id, ktxIdentifier, 12); // KTX 1.1 signature - ktxHeader.endianness = 0; - ktxHeader.glType = 0; // Obtained from image.format - ktxHeader.glTypeSize = 1; - ktxHeader.glFormat = 0; // Obtained from image.format - ktxHeader.glInternalFormat = 0; // Obtained from image.format - ktxHeader.glBaseInternalFormat = 0; - ktxHeader.width = image.width; - ktxHeader.height = image.height; - ktxHeader.depth = 0; - ktxHeader.elements = 0; - ktxHeader.faces = 1; - ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) - ktxHeader.keyValueDataSize = 0; // No extra data after the header - - rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function - ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // 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 (ktxHeader.glFormat == -1) TRACELOG(LOG_WARNING, "IMAGE: GL format not supported for KTX export (%i)", ktxHeader.glFormat); - else - { - memcpy(fileDataPtr, &ktxHeader, sizeof(KTXHeader)); - fileDataPtr += sizeof(KTXHeader); - - int width = image.width; - int height = image.height; - int dataOffset = 0; - - // Save all mipmaps data - for (int i = 0; i < image.mipmaps; i++) - { - unsigned int dataSize = GetPixelDataSize(width, height, image.format); - - memcpy(fileDataPtr, &dataSize, sizeof(unsigned int)); - memcpy(fileDataPtr + 4, (unsigned char *)image.data + dataOffset, dataSize); - - width /= 2; - height /= 2; - dataOffset += dataSize; - fileDataPtr += (4 + dataSize); - } - } - - int success = SaveFileData(fileName, fileData, dataSize); - - RL_FREE(fileData); // Free file data buffer - - // If all data has been written correctly to file, success = 1 - return success; -} -#endif - -#if defined(SUPPORT_FILEFORMAT_PVR) -// Loading PVR image data (uncompressed or PVRT compression) -// NOTE: PVR v2 not supported, use PVR v3 instead -static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // 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 channelDepth[4]; // pixelFormat low part - unsigned int colorSpace; - unsigned int channelType; - unsigned int height; - unsigned int width; - unsigned int depth; - unsigned int numSurfaces; - unsigned int numFaces; - unsigned int numMipmaps; - unsigned int metaDataSize; - } PVRHeaderV3; - -#if 0 // Not used... - // Metadata (usually 15 bytes) - typedef struct { - unsigned int devFOURCC; - unsigned int key; - unsigned int dataSize; // Not used? - unsigned char *data; // Not used? - } PVRMetadata; -#endif - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - // Check PVR image version - unsigned char pvrVersion = fileDataPtr[0]; - - // Load different PVR data formats - if (pvrVersion == 0x50) - { - PVRHeaderV3 *pvrHeader = (PVRHeaderV3 *)fileDataPtr; - - if ((pvrHeader->id[0] != 'P') || (pvrHeader->id[1] != 'V') || (pvrHeader->id[2] != 'R') || (pvrHeader->id[3] != 3)) - { - TRACELOG(LOG_WARNING, "IMAGE: PVR file data not valid"); - } - else - { - fileDataPtr += sizeof(PVRHeaderV3); // Skip header - - image.width = pvrHeader->width; - image.height = pvrHeader->height; - image.mipmaps = pvrHeader->numMipmaps; - - // Check data format - if (((pvrHeader->channels[0] == 'l') && (pvrHeader->channels[1] == 0)) && (pvrHeader->channelDepth[0] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; - else if (((pvrHeader->channels[0] == 'l') && (pvrHeader->channels[1] == 'a')) && ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8))) image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; - else if ((pvrHeader->channels[0] == 'r') && (pvrHeader->channels[1] == 'g') && (pvrHeader->channels[2] == 'b')) - { - if (pvrHeader->channels[3] == 'a') - { - if ((pvrHeader->channelDepth[0] == 5) && (pvrHeader->channelDepth[1] == 5) && (pvrHeader->channelDepth[2] == 5) && (pvrHeader->channelDepth[3] == 1)) image.format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; - else if ((pvrHeader->channelDepth[0] == 4) && (pvrHeader->channelDepth[1] == 4) && (pvrHeader->channelDepth[2] == 4) && (pvrHeader->channelDepth[3] == 4)) image.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; - else if ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8) && (pvrHeader->channelDepth[2] == 8) && (pvrHeader->channelDepth[3] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - } - else if (pvrHeader->channels[3] == 0) - { - if ((pvrHeader->channelDepth[0] == 5) && (pvrHeader->channelDepth[1] == 6) && (pvrHeader->channelDepth[2] == 5)) image.format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; - else if ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8) && (pvrHeader->channelDepth[2] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; - } - } - else if (pvrHeader->channels[0] == 2) image.format = PIXELFORMAT_COMPRESSED_PVRT_RGB; - else if (pvrHeader->channels[0] == 3) image.format = PIXELFORMAT_COMPRESSED_PVRT_RGBA; - - fileDataPtr += pvrHeader->metaDataSize; // Skip meta data header - - // Calculate data size (depends on format) - int bpp = 0; - switch (image.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 dataSize = image.width*image.height*bpp/8; // Total data size in bytes - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - } - } - else if (pvrVersion == 52) TRACELOG(LOG_INFO, "IMAGE: PVRv2 format not supported, update your files to PVRv3"); - } - - return image; -} -#endif - -#if defined(SUPPORT_FILEFORMAT_ASTC) -// Load ASTC compressed image data (ASTC compression) -static Image LoadASTC(const unsigned char *fileData, unsigned int fileSize) -{ - unsigned char *fileDataPtr = (unsigned char *)fileData; - - // 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]; // Image width in pixels (24bit value) - unsigned char height[3]; // Image height in pixels (24bit value) - unsigned char length[3]; // Image Z-size (1 for 2D images) - } ASTCHeader; - - Image image = { 0 }; - - if (fileDataPtr != NULL) - { - ASTCHeader *astcHeader = (ASTCHeader *)fileDataPtr; - - if ((astcHeader->id[3] != 0x5c) || (astcHeader->id[2] != 0xa1) || (astcHeader->id[1] != 0xab) || (astcHeader->id[0] != 0x13)) - { - TRACELOG(LOG_WARNING, "IMAGE: ASTC file data not valid"); - } - else - { - fileDataPtr += sizeof(ASTCHeader); // Skip header - - // NOTE: Assuming Little Endian (could it be wrong?) - image.width = 0x00000000 | ((int)astcHeader->width[2] << 16) | ((int)astcHeader->width[1] << 8) | ((int)astcHeader->width[0]); - image.height = 0x00000000 | ((int)astcHeader->height[2] << 16) | ((int)astcHeader->height[1] << 8) | ((int)astcHeader->height[0]); - - TRACELOGD("IMAGE: ASTC file data info:"); - TRACELOGD(" > Image width: %i", image.width); - TRACELOGD(" > Image height: %i", image.height); - TRACELOGD(" > Image blocks: %ix%i", astcHeader->blockX, astcHeader->blockY); - - image.mipmaps = 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/(astcHeader->blockX*astcHeader->blockY); - - // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8 - if ((bpp == 8) || (bpp == 2)) - { - int dataSize = image.width*image.height*bpp/8; // Data size in bytes - - image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char)); - - memcpy(image.data, fileDataPtr, dataSize); - - if (bpp == 8) image.format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; - else if (bpp == 2) image.format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; - } - else TRACELOG(LOG_WARNING, "IMAGE: ASTC block size configuration not supported"); - } - } - - return image; -} -#endif - // Get pixel data from image as Vector4 array (float normalized) static Vector4 *LoadImageDataNormalized(Image image) { From 4c3d577ddbe0c36412c146072b0dc813ea5d079c Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 17 Sep 2022 17:12:57 -0400 Subject: [PATCH 0059/1710] CMake: Project template fix to easily target raylib version (#2700) --- projects/CMake/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index 351b4809f..cfd924fba 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -5,12 +5,13 @@ project(example) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Dependencies -find_package(raylib 4.2.0 QUIET) # QUIET or REQUIRED +set(RAYLIB_VERSION 4.2.0) +find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED if (NOT raylib_FOUND) # If there's none, fetch and build raylib include(FetchContent) FetchContent_Declare( raylib - URL https://github.com/raysan5/raylib/archive/refs/tags/4.0.0.tar.gz + URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz ) FetchContent_GetProperties(raylib) if (NOT raylib_POPULATED) # Have we downloaded raylib yet? From 494a581339721f5693654281b19d2b2e69dfd9df Mon Sep 17 00:00:00 2001 From: AQuantumPotato <113799046+AQuantumPotato@users.noreply.github.com> Date: Sat, 17 Sep 2022 21:14:00 +0000 Subject: [PATCH 0060/1710] Update BINDINGS.md (#2701) Updated the list of projects that support 4.2 --- BINDINGS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index e6d167596..fe15740fa 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -6,7 +6,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | name | raylib version | language | license | repo | |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| -| raylib | **4.0** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | +| raylib | **4.2** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | | Raylib-cs | **4.0** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | **4.0** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | @@ -15,7 +15,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | **4.0** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/xdrie/dray | -| raylib-d | **4.0** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | +| raylib-d | **4.2** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | @@ -29,7 +29,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | **4.2** | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | -| raylib-lua | **4.0** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | +| raylib-lua | **4.2** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | **4.0** | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | @@ -54,7 +54,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | | raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | -| raylib-zig | **4.0** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | +| raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | | raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | | hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | From 8e57c8ace389135d1f4b92ced0bfb79920ed359e Mon Sep 17 00:00:00 2001 From: Denis Pobedrya Date: Sun, 18 Sep 2022 00:15:11 +0300 Subject: [PATCH 0061/1710] Fix touchscreen input related functions on Android (#2702) Fix display -> screen coordinate conversion for android platform and move it to the platform event handling code, simplifying GetTouchPosition() function implementation. Co-authored-by: Denis Pobedrya --- src/rcore.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 40581415f..daf39a93d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3835,22 +3835,7 @@ Vector2 GetTouchPosition(int index) // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages if (index == 0) position = GetMousePosition(); #endif -#if defined(PLATFORM_ANDROID) - 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); - - if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) - { - position.x = position.x*((float)CORE.Window.screen.width/(float)(CORE.Window.display.width - CORE.Window.renderOffset.x)) - CORE.Window.renderOffset.x/2; - position.y = position.y*((float)CORE.Window.screen.height/(float)(CORE.Window.display.height - CORE.Window.renderOffset.y)) - CORE.Window.renderOffset.y/2; - } - else - { - position.x = position.x*((float)CORE.Window.render.width/(float)CORE.Window.display.width) - CORE.Window.renderOffset.x/2; - position.y = position.y*((float)CORE.Window.render.height/(float)CORE.Window.display.height) - CORE.Window.renderOffset.y/2; - } -#endif -#if defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) 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); #endif @@ -5675,9 +5660,11 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) // Register touch points position CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; - // Normalize CORE.Input.Touch.position[x] for screenWidth and screenHeight - CORE.Input.Touch.position[i].x /= (float)GetScreenWidth(); - CORE.Input.Touch.position[i].y /= (float)GetScreenHeight(); + // 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; } int32_t action = AMotionEvent_getAction(event); From ff25402f689113da07f0077354f32073fc070c2e Mon Sep 17 00:00:00 2001 From: Denis Pobedrya Date: Sun, 18 Sep 2022 12:28:55 +0300 Subject: [PATCH 0062/1710] Fix viewport scaling bug on android after context rebind (#2703) On android after rebinding context (which happens when you minimize and navigate back to an app, or when you turn a screen off and back on) there's a bug that viewport has a wrong scale and part of it is off screen. The change fixes it on devices I tried, but the solution feels hacky to me. However when I attempted instead to call SetupViewport() again which feels like a more proper solution, it didn't fix it. --- src/rcore.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index daf39a93d..f94687074 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5484,7 +5484,14 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Reset screen scaling to full display size EGLint displayFormat = 0; eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - ANativeWindow_setBuffersGeometry(app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + + // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the + // context rebinding if the screen is scaled unless offsets are added. There's probably a more + // appropriate way to fix this + ANativeWindow_setBuffersGeometry(app->window, + CORE.Window.render.width + CORE.Window.renderOffset.x, + CORE.Window.render.height + CORE.Window.renderOffset.y, + displayFormat); // Recreate display surface and re-attach OpenGL context CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); From 8f597b3cc379258215cb6cbdedada7e8389ad1df Mon Sep 17 00:00:00 2001 From: Denis Pobedrya Date: Sun, 18 Sep 2022 20:58:52 +0300 Subject: [PATCH 0063/1710] Basic gamepad support for Android (#2709) Currently assumes a single gamepad, has no code specific to gamepad detection (gamepad is "detected" when an event related to gamepad arrives). Also assumes that all gamepads look roughly like an xbox/ps controller. Both assumptions are not strictly true, but an implementation like that probably covers 85% of usecases. Also it doesn't update previousButtonState so functions IsGamepadButtonPressed() and IsGamepadButtonReleased() don't work, but they didn't work previously on Android anyway, and they are flaky on desktop as they are now, so the mechanism for these two functions probably should be reworked anyway. It's certainly an improvement compared to the previous android gamepad handling code, which put gamepad events into touch related structs. --- src/rcore.c | 108 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index f94687074..e46751d27 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5580,6 +5580,32 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } } +static GamepadButton AndroidTranslateGamepadButton(int button) +{ + switch (button) + { + case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN; + case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; + case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT; + case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP; + case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1; + case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1; + case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2; + case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB; + case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB; + case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT; + case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT; + case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE; + // On some (most?) gamepads dpad events are reported as axis motion instead + case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN; + case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT; + case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT; + case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP; + default: return GAMEPAD_BUTTON_UNKNOWN; + } +} + // ANDROID: Get input events static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { @@ -5595,26 +5621,59 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) { - // Get first touch position - CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0); - CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0); + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; - // Get second touch position - CORE.Input.Touch.position[1].x = AMotionEvent_getX(event, 1); - CORE.Input.Touch.position[1].y = AMotionEvent_getY(event, 1); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_X, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Y, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Z, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_RZ, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; - int32_t keycode = AKeyEvent_getKeyCode(event); - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + // dpad is reported as an axis on android + float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); + float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); + + if (dpadX == 1.0f) { - CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; - CORE.Input.Keyboard.keyPressedQueueCount++; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; + } + else if (dpadX == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; } - else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up - // Stop processing gamepad buttons - return 1; + if (dpadY == 1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + else if (dpadY == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + + return 1; // Handled gamepad axis motion } } else if (type == AINPUT_EVENT_TYPE_KEY) @@ -5622,6 +5681,25 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) int32_t keycode = AKeyEvent_getKeyCode(event); //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)) + { + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; + + GamepadButton button = AndroidTranslateGamepadButton(keycode); + if (button == GAMEPAD_BUTTON_UNKNOWN) + return 1; + + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Gamepad.currentButtonState[0][button] = 1; + } + else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up + return 1; // Handled gamepad button + } + // Save current button and its state // NOTE: Android key action is 0 for down and 1 for up if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) From 42ecd72547b699c0f3008d49831e504e4eb3dfb2 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Sun, 18 Sep 2022 21:08:51 +0200 Subject: [PATCH 0064/1710] [rlgl] Check for extensions before enabling them (#2706) * [rlgl] Check for extensions before enabling them * Shift to glad on macOS * #undef CORE_OPENGL_33 * Remove version hack and fix ASTC compression assumption * Remove loader from glad * Use GLAD_MALLOC/FREE instead of malloc/free * More explicit extension checking --- src/external/glad.h | 170 +++++++++++++++++++++++++++----------------- src/rcore.c | 6 +- src/rlgl.h | 62 ++++++++-------- 3 files changed, 139 insertions(+), 99 deletions(-) diff --git a/src/external/glad.h b/src/external/glad.h index 5ca3ead32..5bb79d40d 100644 --- a/src/external/glad.h +++ b/src/external/glad.h @@ -1,9 +1,9 @@ /** - * Loader generated by glad 2.0.0-beta on Sun Oct 17 18:39:09 2021 + * Loader generated by glad 2.0.0-beta on Sun Sep 18 18:12:12 2022 * * Generator: C/C++ * Specification: gl - * Extensions: 110 + * Extensions: 116 * * APIs: * - gl:core=4.3 @@ -12,16 +12,16 @@ * - ALIAS = False * - DEBUG = False * - HEADER_ONLY = True - * - LOADER = True + * - LOADER = False * - MX = False * - MX_GLOBAL = False * - ON_DEMAND = False * * Commandline: - * --api='gl:core=4.3' --extensions='GL_ARB_ES2_compatibility,GL_ARB_ES3_1_compatibility,GL_ARB_ES3_2_compatibility,GL_ARB_ES3_compatibility,GL_ARB_blend_func_extended,GL_ARB_buffer_storage,GL_ARB_clear_buffer_object,GL_ARB_clear_texture,GL_ARB_color_buffer_float,GL_ARB_compatibility,GL_ARB_compressed_texture_pixel_storage,GL_ARB_compute_shader,GL_ARB_compute_variable_group_size,GL_ARB_copy_buffer,GL_ARB_copy_image,GL_ARB_debug_output,GL_ARB_depth_buffer_float,GL_ARB_depth_clamp,GL_ARB_depth_texture,GL_ARB_direct_state_access,GL_ARB_draw_buffers,GL_ARB_draw_buffers_blend,GL_ARB_draw_elements_base_vertex,GL_ARB_draw_indirect,GL_ARB_draw_instanced,GL_ARB_enhanced_layouts,GL_ARB_explicit_attrib_location,GL_ARB_explicit_uniform_location,GL_ARB_fragment_coord_conventions,GL_ARB_fragment_layer_viewport,GL_ARB_fragment_program,GL_ARB_fragment_program_shadow,GL_ARB_fragment_shader,GL_ARB_fragment_shader_interlock,GL_ARB_framebuffer_no_attachments,GL_ARB_framebuffer_object,GL_ARB_framebuffer_sRGB,GL_ARB_geometry_shader4,GL_ARB_get_program_binary,GL_ARB_get_texture_sub_image,GL_ARB_gl_spirv,GL_ARB_gpu_shader5,GL_ARB_gpu_shader_fp64,GL_ARB_gpu_shader_int64,GL_ARB_half_float_pixel,GL_ARB_half_float_vertex,GL_ARB_instanced_arrays,GL_ARB_internalformat_query,GL_ARB_internalformat_query2,GL_ARB_map_buffer_range,GL_ARB_multi_bind,GL_ARB_multi_draw_indirect,GL_ARB_multisample,GL_ARB_multitexture,GL_ARB_occlusion_query,GL_ARB_occlusion_query2,GL_ARB_pipeline_statistics_query,GL_ARB_query_buffer_object,GL_ARB_sample_locations,GL_ARB_sample_shading,GL_ARB_seamless_cube_map,GL_ARB_seamless_cubemap_per_texture,GL_ARB_shader_atomic_counter_ops,GL_ARB_shader_atomic_counters,GL_ARB_shader_bit_encoding,GL_ARB_shader_clock,GL_ARB_shader_image_load_store,GL_ARB_shader_image_size,GL_ARB_shader_objects,GL_ARB_shader_storage_buffer_object,GL_ARB_shader_texture_lod,GL_ARB_shading_language_100,GL_ARB_shading_language_420pack,GL_ARB_shading_language_include,GL_ARB_shading_language_packing,GL_ARB_spirv_extensions,GL_ARB_tessellation_shader,GL_ARB_texture_border_clamp,GL_ARB_texture_buffer_object_rgb32,GL_ARB_texture_compression,GL_ARB_texture_cube_map,GL_ARB_texture_cube_map_array,GL_ARB_texture_env_add,GL_ARB_texture_filter_anisotropic,GL_ARB_texture_filter_minmax,GL_ARB_texture_float,GL_ARB_texture_mirror_clamp_to_edge,GL_ARB_texture_mirrored_repeat,GL_ARB_texture_multisample,GL_ARB_texture_non_power_of_two,GL_ARB_texture_rg,GL_ARB_texture_storage,GL_ARB_texture_swizzle,GL_ARB_texture_view,GL_ARB_timer_query,GL_ARB_transpose_matrix,GL_ARB_uniform_buffer_object,GL_ARB_vertex_array_bgra,GL_ARB_vertex_array_object,GL_ARB_vertex_attrib_binding,GL_ARB_vertex_buffer_object,GL_ARB_vertex_program,GL_ARB_vertex_shader,GL_EXT_fog_coord,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_EXT_framebuffer_sRGB,GL_OES_compressed_paletted_texture,GL_OES_fixed_point' c --header-only --loader + * --api='gl:core=4.3' --extensions='GL_ARB_ES2_compatibility,GL_ARB_ES3_1_compatibility,GL_ARB_ES3_2_compatibility,GL_ARB_ES3_compatibility,GL_ARB_blend_func_extended,GL_ARB_buffer_storage,GL_ARB_clear_buffer_object,GL_ARB_clear_texture,GL_ARB_color_buffer_float,GL_ARB_compatibility,GL_ARB_compressed_texture_pixel_storage,GL_ARB_compute_shader,GL_ARB_compute_variable_group_size,GL_ARB_copy_buffer,GL_ARB_copy_image,GL_ARB_debug_output,GL_ARB_depth_buffer_float,GL_ARB_depth_clamp,GL_ARB_depth_texture,GL_ARB_direct_state_access,GL_ARB_draw_buffers,GL_ARB_draw_buffers_blend,GL_ARB_draw_elements_base_vertex,GL_ARB_draw_indirect,GL_ARB_draw_instanced,GL_ARB_enhanced_layouts,GL_ARB_explicit_attrib_location,GL_ARB_explicit_uniform_location,GL_ARB_fragment_coord_conventions,GL_ARB_fragment_layer_viewport,GL_ARB_fragment_program,GL_ARB_fragment_program_shadow,GL_ARB_fragment_shader,GL_ARB_fragment_shader_interlock,GL_ARB_framebuffer_no_attachments,GL_ARB_framebuffer_object,GL_ARB_framebuffer_sRGB,GL_ARB_geometry_shader4,GL_ARB_get_program_binary,GL_ARB_get_texture_sub_image,GL_ARB_gl_spirv,GL_ARB_gpu_shader5,GL_ARB_gpu_shader_fp64,GL_ARB_gpu_shader_int64,GL_ARB_half_float_pixel,GL_ARB_half_float_vertex,GL_ARB_instanced_arrays,GL_ARB_internalformat_query,GL_ARB_internalformat_query2,GL_ARB_map_buffer_range,GL_ARB_multi_bind,GL_ARB_multi_draw_indirect,GL_ARB_multisample,GL_ARB_multitexture,GL_ARB_occlusion_query,GL_ARB_occlusion_query2,GL_ARB_pipeline_statistics_query,GL_ARB_query_buffer_object,GL_ARB_sample_locations,GL_ARB_sample_shading,GL_ARB_seamless_cube_map,GL_ARB_seamless_cubemap_per_texture,GL_ARB_shader_atomic_counter_ops,GL_ARB_shader_atomic_counters,GL_ARB_shader_bit_encoding,GL_ARB_shader_clock,GL_ARB_shader_image_load_store,GL_ARB_shader_image_size,GL_ARB_shader_objects,GL_ARB_shader_storage_buffer_object,GL_ARB_shader_texture_lod,GL_ARB_shading_language_100,GL_ARB_shading_language_420pack,GL_ARB_shading_language_include,GL_ARB_shading_language_packing,GL_ARB_spirv_extensions,GL_ARB_tessellation_shader,GL_ARB_texture_border_clamp,GL_ARB_texture_buffer_object_rgb32,GL_ARB_texture_compression,GL_ARB_texture_cube_map,GL_ARB_texture_cube_map_array,GL_ARB_texture_env_add,GL_ARB_texture_filter_anisotropic,GL_ARB_texture_filter_minmax,GL_ARB_texture_float,GL_ARB_texture_mirror_clamp_to_edge,GL_ARB_texture_mirrored_repeat,GL_ARB_texture_multisample,GL_ARB_texture_non_power_of_two,GL_ARB_texture_rg,GL_ARB_texture_storage,GL_ARB_texture_swizzle,GL_ARB_texture_view,GL_ARB_timer_query,GL_ARB_transpose_matrix,GL_ARB_uniform_buffer_object,GL_ARB_vertex_array_bgra,GL_ARB_vertex_array_object,GL_ARB_vertex_attrib_binding,GL_ARB_vertex_buffer_object,GL_ARB_vertex_program,GL_ARB_vertex_shader,GL_EXT_draw_instanced,GL_EXT_fog_coord,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_EXT_framebuffer_sRGB,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_mirror_clamp,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_OES_compressed_paletted_texture,GL_OES_fixed_point' c --header-only * * Online: - * http://glad.sh/#api=gl%3Acore%3D4.3&generator=c&options=HEADER_ONLY%2CLOADER + * http://glad.sh/#api=gl%3Acore%3D4.3&generator=c&options=HEADER_ONLY * */ @@ -54,7 +54,6 @@ #define GLAD_GL #define GLAD_OPTION_GL_HEADER_ONLY -#define GLAD_OPTION_GL_LOADER #ifdef __cplusplus extern "C" { @@ -381,11 +380,25 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_COMPRESSED_RGBA 0x84EE #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 #define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 #define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C -#define GL_COMPRESSED_RGB_ARB 0x84ED #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#define GL_COMPRESSED_RGB_ARB 0x84ED #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 @@ -395,6 +408,20 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 #define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE #define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 #define GL_COMPRESSED_SRGB8_ETC2 0x9275 #define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 @@ -1067,6 +1094,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD #define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 #define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 @@ -1113,7 +1141,10 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_MIPMAP 0x8293 #define GL_MIRRORED_REPEAT 0x8370 #define GL_MIRRORED_REPEAT_ARB 0x8370 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 #define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 #define GL_MULTISAMPLE 0x809D #define GL_MULTISAMPLE_ARB 0x809D #define GL_MULTISAMPLE_BIT_ARB 0x20000000 @@ -1749,6 +1780,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_TEXTURE_MAX_LOD 0x813B #define GL_TEXTURE_MIN_FILTER 0x2801 @@ -2159,6 +2191,20 @@ typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif #elif defined(__VMS ) || defined(__sgi) @@ -2241,14 +2287,21 @@ typedef unsigned short int khronos_uint16_t; * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears * to be the only LLP64 architecture in current use. */ -#ifdef _WIN64 +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; -typedef signed long long int khronos_ssize_t; -typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif @@ -2294,113 +2347,70 @@ typedef enum { } khronos_boolean_enum_t; #endif /* __khrplatform_h_ */ - typedef unsigned int GLenum; - typedef unsigned char GLboolean; - typedef unsigned int GLbitfield; - typedef void GLvoid; - typedef khronos_int8_t GLbyte; - typedef khronos_uint8_t GLubyte; - typedef khronos_int16_t GLshort; - typedef khronos_uint16_t GLushort; - typedef int GLint; - typedef unsigned int GLuint; - typedef khronos_int32_t GLclampx; - typedef int GLsizei; - typedef khronos_float_t GLfloat; - typedef khronos_float_t GLclampf; - typedef double GLdouble; - typedef double GLclampd; - typedef void *GLeglClientBufferEXT; - typedef void *GLeglImageOES; - typedef char GLchar; - typedef char GLcharARB; - #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif - typedef khronos_uint16_t GLhalf; - typedef khronos_uint16_t GLhalfARB; - typedef khronos_int32_t GLfixed; - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptr; #else typedef khronos_intptr_t GLintptr; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptrARB; #else typedef khronos_intptr_t GLintptrARB; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptr; #else typedef khronos_ssize_t GLsizeiptr; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptrARB; #else typedef khronos_ssize_t GLsizeiptrARB; #endif - typedef khronos_int64_t GLint64; - typedef khronos_int64_t GLint64EXT; - typedef khronos_uint64_t GLuint64; - typedef khronos_uint64_t GLuint64EXT; - typedef struct __GLsync *GLsync; - struct _cl_context; - struct _cl_event; - typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); - typedef unsigned short GLhalfNV; - typedef GLintptr GLvdpauSurfaceNV; - typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); - #define GL_VERSION_1_0 1 GLAD_API_CALL int GLAD_GL_VERSION_1_0; #define GL_VERSION_1_1 1 @@ -2593,8 +2603,6 @@ GLAD_API_CALL int GLAD_GL_ARB_texture_border_clamp; GLAD_API_CALL int GLAD_GL_ARB_texture_buffer_object_rgb32; #define GL_ARB_texture_compression 1 GLAD_API_CALL int GLAD_GL_ARB_texture_compression; -#define GL_EXT_texture_compression_s3tc 1 -GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc; #define GL_ARB_texture_cube_map 1 GLAD_API_CALL int GLAD_GL_ARB_texture_cube_map; #define GL_ARB_texture_cube_map_array 1 @@ -2641,6 +2649,8 @@ GLAD_API_CALL int GLAD_GL_ARB_vertex_buffer_object; GLAD_API_CALL int GLAD_GL_ARB_vertex_program; #define GL_ARB_vertex_shader 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_shader; +#define GL_EXT_draw_instanced 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_instanced; #define GL_EXT_fog_coord 1 GLAD_API_CALL int GLAD_GL_EXT_fog_coord; #define GL_EXT_framebuffer_blit 1 @@ -2651,6 +2661,16 @@ GLAD_API_CALL int GLAD_GL_EXT_framebuffer_multisample; GLAD_API_CALL int GLAD_GL_EXT_framebuffer_object; #define GL_EXT_framebuffer_sRGB 1 GLAD_API_CALL int GLAD_GL_EXT_framebuffer_sRGB; +#define GL_EXT_texture_compression_s3tc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc; +#define GL_EXT_texture_filter_anisotropic 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_filter_anisotropic; +#define GL_EXT_texture_mirror_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_mirror_clamp; +#define GL_KHR_texture_compression_astc_hdr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_hdr; +#define GL_KHR_texture_compression_astc_ldr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_ldr; #define GL_OES_compressed_paletted_texture 1 GLAD_API_CALL int GLAD_GL_OES_compressed_paletted_texture; #define GL_OES_fixed_point 1 @@ -2848,6 +2868,7 @@ typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINDIRECTPROC)(GLenum mode, const void typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDARBPROC)(GLenum mode, GLint first, GLsizei count, GLsizei primcount); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDEXTPROC)(GLenum mode, GLint start, GLsizei count, GLsizei primcount); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSARBPROC)(GLsizei n, const GLenum * bufs); @@ -2859,6 +2880,7 @@ typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDARBPROC)(GLenum mode, GLsi typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLuint baseinstance); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKPROC)(GLenum mode, GLuint id); @@ -4045,6 +4067,8 @@ GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB; #define glDrawArraysInstancedARB glad_glDrawArraysInstancedARB GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance; #define glDrawArraysInstancedBaseInstance glad_glDrawArraysInstancedBaseInstance +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT; +#define glDrawArraysInstancedEXT glad_glDrawArraysInstancedEXT GLAD_API_CALL PFNGLDRAWBUFFERPROC glad_glDrawBuffer; #define glDrawBuffer glad_glDrawBuffer GLAD_API_CALL PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; @@ -4067,6 +4091,8 @@ GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstan #define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance; #define glDrawElementsInstancedBaseVertexBaseInstance glad_glDrawElementsInstancedBaseVertexBaseInstance +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT; +#define glDrawElementsInstancedEXT glad_glDrawElementsInstancedEXT GLAD_API_CALL PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; #define glDrawRangeElements glad_glDrawRangeElements GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; @@ -5682,12 +5708,6 @@ GLAD_API_CALL int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr); GLAD_API_CALL int gladLoadGL( GLADloadfunc load); -#ifdef GLAD_GL - -GLAD_API_CALL int gladLoaderLoadGL(void); -GLAD_API_CALL void gladLoaderUnloadGL(void); - -#endif #ifdef __cplusplus } @@ -5813,7 +5833,6 @@ int GLAD_GL_ARB_tessellation_shader = 0; int GLAD_GL_ARB_texture_border_clamp = 0; int GLAD_GL_ARB_texture_buffer_object_rgb32 = 0; int GLAD_GL_ARB_texture_compression = 0; -int GLAD_GL_EXT_texture_compression_s3tc = 0; int GLAD_GL_ARB_texture_cube_map = 0; int GLAD_GL_ARB_texture_cube_map_array = 0; int GLAD_GL_ARB_texture_env_add = 0; @@ -5837,11 +5856,17 @@ int GLAD_GL_ARB_vertex_attrib_binding = 0; int GLAD_GL_ARB_vertex_buffer_object = 0; int GLAD_GL_ARB_vertex_program = 0; int GLAD_GL_ARB_vertex_shader = 0; +int GLAD_GL_EXT_draw_instanced = 0; int GLAD_GL_EXT_fog_coord = 0; int GLAD_GL_EXT_framebuffer_blit = 0; int GLAD_GL_EXT_framebuffer_multisample = 0; int GLAD_GL_EXT_framebuffer_object = 0; int GLAD_GL_EXT_framebuffer_sRGB = 0; +int GLAD_GL_EXT_texture_compression_s3tc = 0; +int GLAD_GL_EXT_texture_filter_anisotropic = 0; +int GLAD_GL_EXT_texture_mirror_clamp = 0; +int GLAD_GL_KHR_texture_compression_astc_hdr = 0; +int GLAD_GL_KHR_texture_compression_astc_ldr = 0; int GLAD_GL_OES_compressed_paletted_texture = 0; int GLAD_GL_OES_fixed_point = 0; @@ -6038,6 +6063,7 @@ PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect = NULL; PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB = NULL; PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance = NULL; +PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT = NULL; PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; PFNGLDRAWBUFFERSARBPROC glad_glDrawBuffersARB = NULL; @@ -6049,6 +6075,7 @@ PFNGLDRAWELEMENTSINSTANCEDARBPROC glad_glDrawElementsInstancedARB = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance = NULL; +PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT = NULL; PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; PFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback = NULL; @@ -8122,6 +8149,11 @@ static void glad_gl_load_GL_ARB_vertex_shader( GLADuserptrloadfunc load, void* u glad_glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) load(userptr, "glVertexAttrib4usvARB"); glad_glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) load(userptr, "glVertexAttribPointerARB"); } +static void glad_gl_load_GL_EXT_draw_instanced( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_instanced) return; + glad_glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC) load(userptr, "glDrawArraysInstancedEXT"); + glad_glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC) load(userptr, "glDrawElementsInstancedEXT"); +} static void glad_gl_load_GL_EXT_fog_coord( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_EXT_fog_coord) return; glad_glFogCoordPointerEXT = (PFNGLFOGCOORDPOINTEREXTPROC) load(userptr, "glFogCoordPointerEXT"); @@ -8451,8 +8483,7 @@ static int glad_gl_find_extensions_gl( int version) { GLAD_GL_ARB_texture_border_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_border_clamp"); GLAD_GL_ARB_texture_buffer_object_rgb32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_buffer_object_rgb32"); GLAD_GL_ARB_texture_compression = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_compression"); - GLAD_GL_EXT_texture_compression_s3tc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc"); - GLAD_GL_ARB_texture_cube_map = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map"); + GLAD_GL_ARB_texture_cube_map = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map"); GLAD_GL_ARB_texture_cube_map_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map_array"); GLAD_GL_ARB_texture_env_add = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_env_add"); GLAD_GL_ARB_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_filter_anisotropic"); @@ -8475,11 +8506,17 @@ static int glad_gl_find_extensions_gl( int version) { GLAD_GL_ARB_vertex_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_buffer_object"); GLAD_GL_ARB_vertex_program = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_program"); GLAD_GL_ARB_vertex_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_shader"); + GLAD_GL_EXT_draw_instanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_instanced"); GLAD_GL_EXT_fog_coord = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_fog_coord"); GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_blit"); GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_multisample"); GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_object"); GLAD_GL_EXT_framebuffer_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_sRGB"); + GLAD_GL_EXT_texture_compression_s3tc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc"); + GLAD_GL_EXT_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_filter_anisotropic"); + GLAD_GL_EXT_texture_mirror_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_mirror_clamp"); + GLAD_GL_KHR_texture_compression_astc_hdr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_hdr"); + GLAD_GL_KHR_texture_compression_astc_ldr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_ldr"); GLAD_GL_OES_compressed_paletted_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_paletted_texture"); GLAD_GL_OES_fixed_point = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_fixed_point"); @@ -8615,6 +8652,7 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { glad_gl_load_GL_ARB_vertex_buffer_object(load, userptr); glad_gl_load_GL_ARB_vertex_program(load, userptr); glad_gl_load_GL_ARB_vertex_shader(load, userptr); + glad_gl_load_GL_EXT_draw_instanced(load, userptr); glad_gl_load_GL_EXT_fog_coord(load, userptr); glad_gl_load_GL_EXT_framebuffer_blit(load, userptr); glad_gl_load_GL_EXT_framebuffer_multisample(load, userptr); @@ -8632,6 +8670,10 @@ int gladLoadGL( GLADloadfunc load) { } + + + + #ifdef __cplusplus } #endif diff --git a/src/rcore.c b/src/rcore.c index e46751d27..5605a75f5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3986,9 +3986,9 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 } - // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version - // with forward compatibility to older OpenGL versions. - // For example, if using OpenGL 1.1, driver can provide a 4.3 context forward compatible. + // 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. // Check selection OpenGL version if (rlGetVersion() == RL_OPENGL_21) diff --git a/src/rlgl.h b/src/rlgl.h index 6a0d715eb..1efd1106a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -444,13 +444,13 @@ typedef enum { RL_SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance RL_SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter RL_SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf -} rlShaderLocationIndex; - +} rlShaderLocationIndex; + #define RL_SHADER_LOC_MAP_DIFFUSE RL_SHADER_LOC_MAP_ALBEDO #define RL_SHADER_LOC_MAP_SPECULAR RL_SHADER_LOC_MAP_METALNESS - + // Shader uniform data type -typedef enum { +typedef enum { RL_SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float RL_SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) RL_SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) @@ -741,16 +741,11 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #endif #if defined(GRAPHICS_API_OPENGL_33) - #if defined(__APPLE__) - #include // OpenGL 3 library for OSX - #include // OpenGL 3 extensions library for OSX - #else - #define GLAD_MALLOC RL_MALLOC - #define GLAD_FREE RL_FREE + #define GLAD_MALLOC RL_MALLOC + #define GLAD_FREE RL_FREE - #define GLAD_GL_IMPLEMENTATION - #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers - #endif + #define GLAD_GL_IMPLEMENTATION + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers #endif #if defined(GRAPHICS_API_OPENGL_ES2) @@ -2001,10 +1996,8 @@ void rlLoadExtensions(void *loader) { #if defined(GRAPHICS_API_OPENGL_33) // Also defined for GRAPHICS_API_OPENGL_21 // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) - #if !defined(__APPLE__) - if (gladLoadGL((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); - else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); - #endif + if (gladLoadGL((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); // Get number of supported extensions GLint numExt = 0; @@ -2018,6 +2011,18 @@ void rlLoadExtensions(void *loader) for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", glGetStringi(GL_EXTENSIONS, i)); #endif +#if defined(GRAPHICS_API_OPENGL_21) + // Register supported extensions flags + // Optional OpenGL 2.1 extensions + RLGL.ExtSupported.vao = GLAD_GL_ARB_vertex_array_object; + RLGL.ExtSupported.instancing = (GLAD_GL_EXT_draw_instanced && GLAD_GL_ARB_instanced_arrays); + RLGL.ExtSupported.texNPOT = GLAD_GL_ARB_texture_non_power_of_two; + RLGL.ExtSupported.texFloat32 = GLAD_GL_ARB_texture_float; + RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture; + RLGL.ExtSupported.maxDepthBits = 32; + RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic; + RLGL.ExtSupported.texMirrorClamp = GLAD_GL_EXT_texture_mirror_clamp; +#else // Register supported extensions flags // OpenGL 3.3 extensions supported by default (core) RLGL.ExtSupported.vao = true; @@ -2028,18 +2033,17 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.maxDepthBits = 32; RLGL.ExtSupported.texAnisoFilter = true; RLGL.ExtSupported.texMirrorClamp = true; +#endif + + // Optional OpenGL 3.3 extensions + RLGL.ExtSupported.texCompASTC = GLAD_GL_KHR_texture_compression_astc_hdr && GLAD_GL_KHR_texture_compression_astc_ldr; + RLGL.ExtSupported.texCompDXT = GLAD_GL_EXT_texture_compression_s3tc; // Texture compression: DXT + RLGL.ExtSupported.texCompETC2 = GLAD_GL_ARB_ES3_compatibility; // Texture compression: ETC2/EAC #if defined(GRAPHICS_API_OPENGL_43) RLGL.ExtSupported.computeShader = GLAD_GL_ARB_compute_shader; RLGL.ExtSupported.ssbo = GLAD_GL_ARB_shader_storage_buffer_object; #endif - #if defined(__APPLE__) - // Apple provides its own extension macros - RLGL.ExtSupported.texCompDXT = GL_EXT_texture_compression_s3tc; - #else - // NOTE: With GLAD, we can check if an extension is supported using the GLAD_GL_xxx booleans - RLGL.ExtSupported.texCompDXT = GLAD_GL_EXT_texture_compression_s3tc; // Texture compression: DXT - RLGL.ExtSupported.texCompETC2 = GLAD_GL_ARB_ES3_compatibility; // Texture compression: ETC2/EAC - #endif + #endif // GRAPHICS_API_OPENGL_33 #if defined(GRAPHICS_API_OPENGL_ES2) @@ -2205,12 +2209,10 @@ void rlLoadExtensions(void *loader) #else // RLGL_SHOW_GL_DETAILS_INFO // Show some basic info about GL supported features - #if defined(GRAPHICS_API_OPENGL_ES2) if (RLGL.ExtSupported.vao) TRACELOG(RL_LOG_INFO, "GL: VAO extension detected, VAO functions loaded successfully"); else TRACELOG(RL_LOG_WARNING, "GL: VAO extension not found, VAO not supported"); if (RLGL.ExtSupported.texNPOT) TRACELOG(RL_LOG_INFO, "GL: NPOT textures extension detected, full NPOT textures supported"); else TRACELOG(RL_LOG_WARNING, "GL: NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); - #endif if (RLGL.ExtSupported.texCompDXT) TRACELOG(RL_LOG_INFO, "GL: DXT compressed textures supported"); if (RLGL.ExtSupported.texCompETC1) TRACELOG(RL_LOG_INFO, "GL: ETC1 compressed textures supported"); if (RLGL.ExtSupported.texCompETC2) TRACELOG(RL_LOG_INFO, "GL: ETC2/EAC compressed textures supported"); @@ -2231,11 +2233,7 @@ int rlGetVersion(void) glVersion = RL_OPENGL_11; #endif #if defined(GRAPHICS_API_OPENGL_21) - #if defined(__APPLE__) - glVersion = RL_OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX - #else - glVersion = RL_OPENGL_21; - #endif + glVersion = RL_OPENGL_21; #elif defined(GRAPHICS_API_OPENGL_33) glVersion = RL_OPENGL_33; #endif From 7e3a50b4acea0bbedbf121aad4f36431c2e09907 Mon Sep 17 00:00:00 2001 From: Gunko Vadim Date: Mon, 19 Sep 2022 01:36:59 +0500 Subject: [PATCH 0065/1710] Update BINDINGS.md (#2710) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index fe15740fa..ec8e9d885 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -39,7 +39,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | | raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.0** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | -| Ray4Laz | **4.2** | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language))| Zlib | https://github.com/GuvaCode/Ray4Laz | +| Ray4Laz | **4.2** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | | raylib-python-cffi | **4.2** | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | From 27781a4767afdb60a9bea452a910fa4d53d85461 Mon Sep 17 00:00:00 2001 From: Denis Pobedrya Date: Mon, 19 Sep 2022 00:01:10 +0300 Subject: [PATCH 0066/1710] Remove touch points on touch up events on Android (#2711) Fixes #2683 Remove elements from touch point related arrays when touch up and similar events are processed. This makes GetTouchPointCount() always report the actual count of touch points, and all positions returned by GetTouchPosition() correspond to positions of currently happening touches. --- src/rcore.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 5605a75f5..1b00b14f1 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5755,9 +5755,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; - if (flags == AMOTION_EVENT_ACTION_DOWN || flags == AMOTION_EVENT_ACTION_MOVE) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; - else if (flags == AMOTION_EVENT_ACTION_UP) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; - #if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID GestureEvent gestureEvent = { 0 }; @@ -5779,6 +5776,25 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) ProcessGestureEvent(gestureEvent); #endif + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + + 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++) + { + CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; + CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; + } + CORE.Input.Touch.pointCount--; + } + + // When all touchpoints are tapped and released really quickly, this event is generated + if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; + + if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; + else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + return 0; } #endif From 12e8cef9cfe7ba99696b1eed5454e58767d0a3d3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 10:31:55 +0200 Subject: [PATCH 0067/1710] REMOVED: Mipmaps software generation for OpenGL 1.1 As generation is done in software, it's up to the user to do it. `ImageMipmaps()` is already provided for reference. --- src/rlgl.h | 175 ++--------------------------------------------------- 1 file changed, 4 insertions(+), 171 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 1efd1106a..da4882540 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -990,14 +990,12 @@ static void rlUnloadShaderDefault(void); // Unload default shader static char *rlGetCompressedFormatName(int format); // Get compressed format official GL identifier name #endif // RLGL_SHOW_GL_DETAILS_INFO #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -#if defined(GRAPHICS_API_OPENGL_11) -static int rlGenTextureMipmapsData(unsigned char *data, int baseWidth, int baseHeight); // Generate mipmaps data on CPU side -static unsigned char *rlGenNextMipmapData(unsigned char *srcData, int srcWidth, int srcHeight); // Generate next mipmap level on CPU side -#endif + static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) + // Auxiliar matrix math functions -static Matrix rlMatrixIdentity(void); // Get identity matrix -static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices +static Matrix rlMatrixIdentity(void); // Get identity matrix +static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations @@ -3094,45 +3092,6 @@ void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; -#if defined(GRAPHICS_API_OPENGL_11) - if (texIsPOT) - { - // WARNING: Manual mipmap generation only works for RGBA 32bit textures! - if (format == RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - // Retrieve texture data from VRAM - void *texData = rlReadTexturePixels(id, width, height, format); - - // NOTE: Texture data size is reallocated to fit mipmaps data - // NOTE: CPU mipmap generation only supports RGBA 32bit data - int mipmapCount = rlGenTextureMipmapsData(texData, width, height); - - int size = width*height*4; - int offset = size; - - int mipWidth = width/2; - int mipHeight = height/2; - - // Load the mipmaps - for (int level = 1; level < mipmapCount; level++) - { - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)texData + offset); - - size = mipWidth*mipHeight*4; - offset += size; - - mipWidth /= 2; - mipHeight /= 2; - } - - *mipmaps = mipmapCount + 1; - RL_FREE(texData); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data - - TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Mipmaps generated manually on CPU side, total: %i", id, *mipmaps); - } - else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps for provided texture format", id); - } -#endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) { @@ -4522,132 +4481,6 @@ static char *rlGetCompressedFormatName(int format) #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -#if defined(GRAPHICS_API_OPENGL_11) -// Mipmaps data is generated after image data -// NOTE: Only works with RGBA (4 bytes) data! -static int rlGenTextureMipmapsData(unsigned char *data, int baseWidth, int baseHeight) -{ - int mipmapCount = 1; // Required mipmap levels count (including base level) - int width = baseWidth; - int height = baseHeight; - int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...), RGBA only - - // Count mipmap levels required - while ((width != 1) && (height != 1)) - { - width /= 2; - height /= 2; - - TRACELOGD("TEXTURE: Next mipmap size: %i x %i", width, height); - - mipmapCount++; - - size += (width*height*4); // Add mipmap size (in bytes) - } - - TRACELOGD("TEXTURE: Total mipmaps required: %i", mipmapCount); - TRACELOGD("TEXTURE: Total size of data required: %i", size); - - unsigned char *temp = RL_REALLOC(data, size); - - if (temp != NULL) data = temp; - else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to re-allocate required mipmaps memory"); - - width = baseWidth; - height = baseHeight; - size = (width*height*4); // RGBA: 4 bytes - - // Generate mipmaps - // NOTE: Every mipmap data is stored after data (RGBA - 4 bytes) - unsigned char *image = (unsigned char *)RL_MALLOC(width*height*4); - unsigned char *mipmap = NULL; - int offset = 0; - - for (int i = 0; i < size; i += 4) - { - image[i] = data[i]; - image[i + 1] = data[i + 1]; - image[i + 2] = data[i + 2]; - image[i + 3] = data[i + 3]; - } - - TRACELOGD("TEXTURE: Mipmap base size (%ix%i)", width, height); - - for (int mip = 1; mip < mipmapCount; mip++) - { - mipmap = rlGenNextMipmapData(image, width, height); - - offset += (width*height*4); // Size of last mipmap - - width /= 2; - height /= 2; - size = (width*height*4); // Mipmap size to store after offset - - // Add mipmap to data - for (int i = 0; i < size; i += 4) - { - data[offset + i] = mipmap[i]; - data[offset + i + 1] = mipmap[i + 1]; - data[offset + i + 2] = mipmap[i + 2]; - data[offset + i + 3] = mipmap[i + 3]; - } - - RL_FREE(image); - - image = mipmap; - mipmap = NULL; - } - - RL_FREE(mipmap); // free mipmap data - - return mipmapCount; -} - -// Manual mipmap generation (basic scaling algorithm) -static unsigned char *rlGenNextMipmapData(unsigned char *srcData, int srcWidth, int srcHeight) -{ - int x2 = 0; - int y2 = 0; - unsigned char prow[4] = { 0 }; - unsigned char pcol[4] = { 0 }; - - int width = srcWidth/2; - int height = srcHeight/2; - - unsigned char *mipmap = (unsigned char *)RL_MALLOC(width*height*4); - - // Scaling algorithm works perfectly (box-filter) - for (int y = 0; y < height; y++) - { - y2 = 2*y; - - for (int x = 0; x < width; x++) - { - x2 = 2*x; - - prow[0] = (srcData[(y2*srcWidth + x2)*4 + 0] + srcData[(y2*srcWidth + x2 + 1)*4 + 0])/2; - prow[1] = (srcData[(y2*srcWidth + x2)*4 + 1] + srcData[(y2*srcWidth + x2 + 1)*4 + 1])/2; - prow[2] = (srcData[(y2*srcWidth + x2)*4 + 2] + srcData[(y2*srcWidth + x2 + 1)*4 + 2])/2; - prow[3] = (srcData[(y2*srcWidth + x2)*4 + 3] + srcData[(y2*srcWidth + x2 + 1)*4 + 3])/2; - - pcol[0] = (srcData[((y2 + 1)*srcWidth + x2)*4 + 0] + srcData[((y2 + 1)*srcWidth + x2 + 1)*4 + 0])/2; - pcol[1] = (srcData[((y2 + 1)*srcWidth + x2)*4 + 1] + srcData[((y2 + 1)*srcWidth + x2 + 1)*4 + 1])/2; - pcol[2] = (srcData[((y2 + 1)*srcWidth + x2)*4 + 2] + srcData[((y2 + 1)*srcWidth + x2 + 1)*4 + 2])/2; - pcol[3] = (srcData[((y2 + 1)*srcWidth + x2)*4 + 3] + srcData[((y2 + 1)*srcWidth + x2 + 1)*4 + 3])/2; - - mipmap[(y*width + x)*4 + 0] = (prow[0] + pcol[0])/2; - mipmap[(y*width + x)*4 + 1] = (prow[1] + pcol[1])/2; - mipmap[(y*width + x)*4 + 2] = (prow[2] + pcol[2])/2; - mipmap[(y*width + x)*4 + 3] = (prow[3] + pcol[3])/2; - } - } - - TRACELOGD("TEXTURE: Mipmap generated successfully (%ix%i)", width, height); - - return mipmap; -} -#endif // GRAPHICS_API_OPENGL_11 - // Get pixel data size in bytes (image or texture) // NOTE: Size depends on pixel format static int rlGetPixelDataSize(int width, int height, int format) From 6e8f3b0f45c73005634e8c4cfe8fa0d61560fbb5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 10:35:34 +0200 Subject: [PATCH 0068/1710] Update rlgl.h --- src/rlgl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rlgl.h b/src/rlgl.h index da4882540..ee3c3d91a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3082,6 +3082,7 @@ void rlUnloadTexture(unsigned int id) } // Generate mipmap data for selected texture +// NOTE: Only supports GPU mipmap generation void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps) { glBindTexture(GL_TEXTURE_2D, id); From 5530a3ceb88962066affa5db8e13b00b64444b37 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 18:20:36 +0200 Subject: [PATCH 0069/1710] REVIEWED: `ScanDirectoryFilesRecursively()`, fix #2704 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 1b00b14f1..8d5b4589d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5146,7 +5146,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const // Scan all files and directories recursively from a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter) { - static char path[MAX_FILEPATH_LENGTH] = { 0 }; + char path[MAX_FILEPATH_LENGTH] = { 0 }; memset(path, 0, MAX_FILEPATH_LENGTH); struct dirent *dp = NULL; From 0e5cd442be3b90c041a5874fbec52659f214827e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 18:29:08 +0200 Subject: [PATCH 0070/1710] REVIEWED: Renamed some shaders, fix #2707 --- .../resources/shaders/glsl100/{base_lighting.vs => lighting.vs} | 0 .../{base_lighting_instanced.vs => lighting_instanced.vs} | 0 .../resources/shaders/glsl120/{base_lighting.vs => lighting.vs} | 0 .../resources/shaders/glsl330/{base_lighting.vs => lighting.vs} | 0 examples/shaders/shaders_basic_lighting.c | 2 +- examples/shaders/shaders_fog.c | 2 +- 6 files changed, 2 insertions(+), 2 deletions(-) rename examples/shaders/resources/shaders/glsl100/{base_lighting.vs => lighting.vs} (100%) rename examples/shaders/resources/shaders/glsl100/{base_lighting_instanced.vs => lighting_instanced.vs} (100%) rename examples/shaders/resources/shaders/glsl120/{base_lighting.vs => lighting.vs} (100%) rename examples/shaders/resources/shaders/glsl330/{base_lighting.vs => lighting.vs} (100%) diff --git a/examples/shaders/resources/shaders/glsl100/base_lighting.vs b/examples/shaders/resources/shaders/glsl100/lighting.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl100/base_lighting.vs rename to examples/shaders/resources/shaders/glsl100/lighting.vs diff --git a/examples/shaders/resources/shaders/glsl100/base_lighting_instanced.vs b/examples/shaders/resources/shaders/glsl100/lighting_instanced.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl100/base_lighting_instanced.vs rename to examples/shaders/resources/shaders/glsl100/lighting_instanced.vs diff --git a/examples/shaders/resources/shaders/glsl120/base_lighting.vs b/examples/shaders/resources/shaders/glsl120/lighting.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl120/base_lighting.vs rename to examples/shaders/resources/shaders/glsl120/lighting.vs diff --git a/examples/shaders/resources/shaders/glsl330/base_lighting.vs b/examples/shaders/resources/shaders/glsl330/lighting.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl330/base_lighting.vs rename to examples/shaders/resources/shaders/glsl330/lighting.vs diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 42910a516..c557ecf73 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -57,7 +57,7 @@ int main(void) Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 4.0f, 2.0f)); // Load basic lighting shader - Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/base_lighting.vs", GLSL_VERSION), + Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lighting.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/lighting.fs", GLSL_VERSION)); // Get some required shader locations shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos"); diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index 5cd68d2c5..de5c23df5 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -63,7 +63,7 @@ int main(void) modelC.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Load shader and set up some uniforms - Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/base_lighting.vs", GLSL_VERSION), + 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"); From 2093fdcc536e68a131c15e196460e338609e1a76 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Mon, 19 Sep 2022 12:41:17 -0400 Subject: [PATCH 0071/1710] Added: `ImageDrawCircleLines`, `ImageDrawCircleLinesV` (#2713) This adds `ImageDrawCircleLines()` and `ImageDrawCircleLinesV()` to draw outlines of circles, and updates `ImageDrawCircle()` draw a filled circle to match the effect of `DrawCircle()` and `DrawCircleLines()`. --- examples/textures/textures_image_drawing.c | 2 +- src/raylib.h | 2 ++ src/rtextures.c | 37 +++++++++++++++++++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/examples/textures/textures_image_drawing.c b/examples/textures/textures_image_drawing.c index 203b3cd38..190fb8594 100644 --- a/examples/textures/textures_image_drawing.c +++ b/examples/textures/textures_image_drawing.c @@ -42,7 +42,7 @@ int main(void) // Draw on the image with a few image draw methods ImageDrawPixel(&parrots, 10, 10, RAYWHITE); - ImageDrawCircle(&parrots, 10, 10, 5, RAYWHITE); + ImageDrawCircleLines(&parrots, 10, 10, 5, RAYWHITE); ImageDrawRectangle(&parrots, 5, 20, 10, 10, RAYWHITE); UnloadImage(cat); // Unload image from RAM diff --git a/src/raylib.h b/src/raylib.h index ac5d6e6af..7e4fdf3fb 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1287,6 +1287,8 @@ RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color); // Draw line within an image (Vector version) RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle within an image RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw 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 +RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version) RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image 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 diff --git a/src/rtextures.c b/src/rtextures.c index 12ba4923b..5a77ac7b2 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2716,7 +2716,36 @@ void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color) } // Draw circle within an image -void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color) +void ImageDrawCircle(Image* dst, int centerX, int centerY, int radius, Color color) +{ + int x = 0, y = radius; + int decesionParameter = 3 - 2 * radius; + + while (y >= x) + { + ImageDrawRectangle(dst, centerX - x, centerY + y, x * 2, 1, color); + ImageDrawRectangle(dst, centerX - x, centerY - y, x * 2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY + x, y * 2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY - x, y * 2, 1, color); + x++; + + if (decesionParameter > 0) { + y--; + decesionParameter = decesionParameter + 4 * (x - y) + 10; + } else { + decesionParameter = decesionParameter + 4 * x + 6; + } + } +} + +// Draw circle within an image (Vector version) +void ImageDrawCircleV(Image* dst, Vector2 center, int radius, Color color) +{ + ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color); +} + +// Draw circle outline within an image +void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color) { int x = 0, y = radius; int decesionParameter = 3 - 2*radius; @@ -2742,10 +2771,10 @@ void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color col } } -// Draw circle within an image (Vector version) -void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color) +// Draw circle outline within an image (Vector version) +void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color) { - ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color); + ImageDrawCircleLines(dst, (int)center.x, (int)center.y, radius, color); } // Draw rectangle within an image From 4311ffc9e13c4de3c519ce9b1afd2f52fc618914 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 19 Sep 2022 18:47:16 +0200 Subject: [PATCH 0072/1710] REVIEWED: New functions coding conventions --- src/rtextures.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 5a77ac7b2..3badc349d 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2718,23 +2718,24 @@ void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color) // Draw circle within an image void ImageDrawCircle(Image* dst, int centerX, int centerY, int radius, Color color) { - int x = 0, y = radius; - int decesionParameter = 3 - 2 * radius; + int x = 0; + int y = radius; + int decesionParameter = 3 - 2*radius; while (y >= x) { - ImageDrawRectangle(dst, centerX - x, centerY + y, x * 2, 1, color); - ImageDrawRectangle(dst, centerX - x, centerY - y, x * 2, 1, color); - ImageDrawRectangle(dst, centerX - y, centerY + x, y * 2, 1, color); - ImageDrawRectangle(dst, centerX - y, centerY - x, y * 2, 1, color); + ImageDrawRectangle(dst, centerX - x, centerY + y, x*2, 1, color); + ImageDrawRectangle(dst, centerX - x, centerY - y, x*2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY + x, y*2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY - x, y*2, 1, color); x++; - if (decesionParameter > 0) { + if (decesionParameter > 0) + { y--; - decesionParameter = decesionParameter + 4 * (x - y) + 10; - } else { - decesionParameter = decesionParameter + 4 * x + 6; - } + decesionParameter = decesionParameter + 4*(x - y) + 10; + } + else decesionParameter = decesionParameter + 4*x + 6; } } @@ -2747,7 +2748,8 @@ void ImageDrawCircleV(Image* dst, Vector2 center, int radius, Color color) // Draw circle outline within an image void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color) { - int x = 0, y = radius; + int x = 0; + int y = radius; int decesionParameter = 3 - 2*radius; while (y >= x) From 907e9e1fe2943efdb253103995d1c10a4adc5c9c Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 22 Sep 2022 20:29:54 +0200 Subject: [PATCH 0073/1710] Update text_codepoints_loading.c --- examples/text/text_codepoints_loading.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index 3b6e11609..c68544030 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -57,6 +57,10 @@ int main(void) bool showFontAtlas = false; + int codepointSize = 0; + int codepoint = 0; + char *ptr = text; + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -66,6 +70,20 @@ int main(void) // Update //---------------------------------------------------------------------------------- if (IsKeyPressed(KEY_SPACE)) showFontAtlas = !showFontAtlas; + + // Testing code: getting next and previous codepoints on provided text + if (IsKeyPressed(KEY_RIGHT)) + { + // Get next codepoint in string and move pointer + codepoint = GetCodepointNext(ptr, &codepointSize); + ptr += codepointSize; + } + else if (IsKeyPressed(KEY_LEFT)) + { + // Get previous codepoint in string and move pointer + codepoint = GetCodepointPrevious(ptr, &codepointSize); + ptr -= codepointSize; + } //---------------------------------------------------------------------------------- // Draw From 4b1d4b4f6b669b133fab34d9f91ad2f5b898e97a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 22 Sep 2022 20:30:02 +0200 Subject: [PATCH 0074/1710] Update rcore.c --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8d5b4589d..490ac4a10 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1934,7 +1934,7 @@ void EnableEventWaiting(void) } // Disable waiting for events on EndDrawing(), automatic events polling -RLAPI void DisableEventWaiting(void) +void DisableEventWaiting(void) { CORE.Window.eventWaiting = false; } @@ -2435,7 +2435,7 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) } // Load shader from code strings and bind default locations -RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) +Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) { Shader shader = { 0 }; shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); From 810a0330abdf13c49843692407e2f77da08f8bdc Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 22 Sep 2022 20:35:55 +0200 Subject: [PATCH 0075/1710] WARNING: Several changes on UTF-8/Codepoints API - ADDED: `GetCodepointPrevious()` - RENAMED: `GetCodepoint()` -> `GetCodepointNext()`, actually, reimplemented - `GetCodepoint()` has been kept for the moment, for compatibility and also because implementation is different - RENAMED: `TextCodepointsToUTF8()` to `LoadUTF8()`, simpler name and more aligned with raylib conventions (functions loading memory start with Load*()), parameters should be descriptive of functionailty. - ADDED: `UnloadUTF8()`, aligned with `LoadUTF8()` to avoid allocators issues. --- src/raylib.h | 15 ++-- src/rtext.c | 219 +++++++++++++++++++++++++++++++----------------- src/rtextures.c | 4 +- 3 files changed, 151 insertions(+), 87 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 7e4fdf3fb..84a18ebe2 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1370,12 +1370,15 @@ RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); 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 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 *bytesProcessed); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) -RLAPI char *TextCodepointsToUTF8(const int *codepoints, int length); // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!) +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! diff --git a/src/rtext.c b/src/rtext.c index ef5debd5b..324ebe95a 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1043,7 +1043,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f { // Get next codepoint from byte string and glyph index in font int codepointByteCount = 0; - int codepoint = GetCodepoint(&text[i], &codepointByteCount); + int codepoint = GetCodepointNext(&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) @@ -1185,7 +1185,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing byteCounter++; int next = 0; - letter = GetCodepoint(&text[i], &next); + letter = GetCodepointNext(&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) @@ -1627,7 +1627,7 @@ const char *TextToPascal(const char *text) // Encode text codepoint into UTF-8 text // REQUIRES: memcpy() // WARNING: Allocated memory must be manually freed -char *TextCodepointsToUTF8(const int *codepoints, int length) +char *LoadUTF8(const int *codepoints, int length) { // We allocate enough memory fo fit all possible codepoints // NOTE: 5 bytes for every codepoint should be enough @@ -1650,9 +1650,68 @@ char *TextCodepointsToUTF8(const int *codepoints, int length) return text; } +// Unload UTF-8 text encoded from codepoints array +void UnloadUTF8(char *text) +{ + RL_FREE(text); +} + +// 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 codepointCount = 0; + + // Allocate a big enough buffer to store as many codepoints as text bytes + int *codepoints = RL_CALLOC(textLength, sizeof(int)); + + for (int i = 0; i < textLength; codepointCount++) + { + codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize); + i += codepointSize; + } + + // Re-allocate buffer to the actual number of codepoints loaded + void *temp = RL_REALLOC(codepoints, codepointCount*sizeof(int)); + if (temp != NULL) codepoints = temp; + + *count = codepointCount; + + return codepoints; +} + +// Unload codepoints data from memory +void UnloadCodepoints(int *codepoints) +{ + RL_FREE(codepoints); +} + +// Get total number of characters(codepoints) in a UTF-8 encoded text, until '\0' is found +// NOTE: If an invalid UTF-8 sequence is encountered a '?'(0x3f) codepoint is counted instead +int GetCodepointCount(const char *text) +{ + unsigned int length = 0; + char *ptr = (char *)&text[0]; + + while (*ptr != '\0') + { + int next = 0; + int letter = GetCodepointNext(ptr, &next); + + if (letter == 0x3f) ptr += 1; + else ptr += next; + + length++; + } + + return length; +} + // Encode codepoint into utf8 text (char array length returned as parameter) // NOTE: It uses a static array to store UTF-8 bytes -RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize) +const char *CodepointToUTF8(int codepoint, int *utf8Size) { static char utf8[6] = { 0 }; int size = 0; // Byte size of codepoint @@ -1684,63 +1743,10 @@ RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize) size = 4; } - *byteSize = size; + *utf8Size = size; return utf8; } - -// 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 bytesProcessed = 0; - int codepointCount = 0; - - // Allocate a big enough buffer to store as many codepoints as text bytes - int *codepoints = RL_CALLOC(textLength, sizeof(int)); - - for (int i = 0; i < textLength; codepointCount++) - { - codepoints[codepointCount] = GetCodepoint(text + i, &bytesProcessed); - i += bytesProcessed; - } - - // Re-allocate buffer to the actual number of codepoints loaded - void *temp = RL_REALLOC(codepoints, codepointCount*sizeof(int)); - if (temp != NULL) codepoints = temp; - - *count = codepointCount; - - return codepoints; -} - -// Unload codepoints data from memory -void UnloadCodepoints(int *codepoints) -{ - RL_FREE(codepoints); -} - -// Get total number of characters(codepoints) in a UTF-8 encoded text, until '\0' is found -// NOTE: If an invalid UTF-8 sequence is encountered a '?'(0x3f) codepoint is counted instead -int GetCodepointCount(const char *text) -{ - unsigned int length = 0; - char *ptr = (char *)&text[0]; - - while (*ptr != '\0') - { - int next = 0; - int letter = GetCodepoint(ptr, &next); - - if (letter == 0x3f) ptr += 1; - else ptr += next; - - length++; - } - - return length; -} #endif // SUPPORT_TEXT_MANIPULATION // Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found @@ -1748,7 +1754,7 @@ int GetCodepointCount(const char *text) // 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 -int GetCodepoint(const char *text, int *bytesProcessed) +int GetCodepoint(const char *text, int *codepointSize) { /* UTF-8 specs from https://www.ietf.org/rfc/rfc3629.txt @@ -1763,14 +1769,14 @@ int GetCodepoint(const char *text, int *bytesProcessed) */ // NOTE: on decode errors we return as soon as possible - int code = 0x3f; // Codepoint (defaults to '?') + int codepoint = 0x3f; // Codepoint (defaults to '?') int octet = (unsigned char)(text[0]); // The first UTF8 octet - *bytesProcessed = 1; + *codepointSize = 1; if (octet <= 0x7f) { // Only one octet (ASCII range x00-7F) - code = text[0]; + codepoint = text[0]; } else if ((octet & 0xe0) == 0xc0) { @@ -1779,12 +1785,12 @@ int GetCodepoint(const char *text, int *bytesProcessed) // [0]xC2-DF [1]UTF8-tail(x80-BF) unsigned char octet1 = text[1]; - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence if ((octet >= 0xc2) && (octet <= 0xdf)) { - code = ((octet & 0x1f) << 6) | (octet1 & 0x3f); - *bytesProcessed = 2; + codepoint = ((octet & 0x1f) << 6) | (octet1 & 0x3f); + *codepointSize = 2; } } else if ((octet & 0xf0) == 0xe0) @@ -1793,11 +1799,11 @@ int GetCodepoint(const char *text, int *bytesProcessed) unsigned char octet1 = text[1]; unsigned char octet2 = '\0'; - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence octet2 = text[2]; - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence + if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *codepointSize = 3; return codepoint; } // Unexpected sequence // [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) // [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) @@ -1805,50 +1811,105 @@ int GetCodepoint(const char *text, int *bytesProcessed) // [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF) if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) || - ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; } + ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *codepointSize = 2; return codepoint; } if ((octet >= 0xe0) && (octet <= 0xef)) { - code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); - *bytesProcessed = 3; + codepoint = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); + *codepointSize = 3; } } else if ((octet & 0xf8) == 0xf0) { // Four octets - if (octet > 0xf4) return code; + if (octet > 0xf4) return codepoint; unsigned char octet1 = text[1]; unsigned char octet2 = '\0'; unsigned char octet3 = '\0'; - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence + if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence octet2 = text[2]; - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence + if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *codepointSize = 3; return codepoint; } // Unexpected sequence octet3 = text[3]; - if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence + if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *codepointSize = 4; return codepoint; } // Unexpected sequence // [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail // [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail // [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) || - ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence + ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *codepointSize = 2; return codepoint; } // Unexpected sequence if (octet >= 0xf0) { - code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); - *bytesProcessed = 4; + codepoint = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); + *codepointSize = 4; } } - if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid + if (codepoint > 0x10ffff) codepoint = 0x3f; // Codepoints after U+10ffff are invalid - return code; + return codepoint; +} + +// Get next codepoint in a byte sequence and bytes processed +int GetCodepointNext(const char *text, int *codepointSize) +{ + const char *ptr = text; + int codepoint = 0x3f; // Codepoint (defaults to '?') + *codepointSize = 0; + + // Get current codepoint and bytes processed + if (0xf0 == (0xf8 & ptr[0])) + { + // 4 byte UTF-8 codepoint + 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 */ + 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 + codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); + *codepointSize = 2; + } + else + { + // 1 byte UTF-8 codepoint + codepoint = ptr[0]; + *codepointSize = 1; + } + + return codepoint; +} + +// Get previous codepoint in a byte sequence and bytes processed +int GetCodepointPrevious(const char *text, int *codepointSize) +{ + const char *ptr = text; + int codepoint = 0x3f; // Codepoint (defaults to '?') + int cpSize = 0; + *codepointSize = 0; + + // Move to previous codepoint + do ptr--; + while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) == 0x80)); + + codepoint = GetCodepointNext(ptr, &cpSize); + + if (codepoint != 0) *codepointSize = cpSize; + + return codepoint; } //---------------------------------------------------------------------------------- diff --git a/src/rtextures.c b/src/rtextures.c index 3badc349d..41bd3e184 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1238,8 +1238,8 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co { // Get next codepoint from byte string and glyph index in font int codepointByteCount = 0; - int codepoint = GetCodepoint(&text[i], &codepointByteCount); // WARNING: Module required: rtext - int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext + int codepoint = GetCodepointNext(&text[i], &codepointByteCount); // WARNING: Module required: rtext + int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext // 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 From ea87491a82a0fc778056d1538d37ec851e8e0fbe Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 25 Sep 2022 00:14:59 +0200 Subject: [PATCH 0076/1710] ADDED: Support CAPS/NUM lock keys registering if locked --- src/rcore.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 490ac4a10..214b9509b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4136,6 +4136,8 @@ static bool InitGraphicsDevice(int width, int height) glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + glfwMakeContextCurrent(CORE.Window.handle); #if !defined(PLATFORM_WEB) @@ -5267,6 +5269,9 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; else CORE.Input.Keyboard.currentKeyState[key] = 1; + // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys + if (((mods & GLFW_MOD_CAPS_LOCK) > 0) || ((mods & GLFW_MOD_NUM_LOCK) > 0)) 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)) { From 2ef0b064e50279f84531fd265990f29327775dbf Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 25 Sep 2022 14:09:28 +0200 Subject: [PATCH 0077/1710] Fix isssue #2718 --- src/rcore.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 214b9509b..c7986dfb7 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1952,8 +1952,6 @@ const char *GetClipboardText(void) return NULL; } - - // Show mouse cursor void ShowCursor(void) { @@ -5270,7 +5268,8 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i else CORE.Input.Keyboard.currentKeyState[key] = 1; // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys - if (((mods & GLFW_MOD_CAPS_LOCK) > 0) || ((mods & GLFW_MOD_NUM_LOCK) > 0)) CORE.Input.Keyboard.currentKeyState[key] = 1; + 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; // Check if there is space available in the key queue if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) From 7ab056b6efb0764967c80439c15eed828b6ae1c4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 25 Sep 2022 15:41:49 +0200 Subject: [PATCH 0078/1710] REVIEWED: `GeneshHeightmap()`, fix #2716 --- src/rmodels.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index cb6551eb7..342ba33b6 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2630,7 +2630,7 @@ Mesh GenMeshKnot(float radius, float size, int radSeg, int sides) // NOTE: Vertex data is uploaded to GPU Mesh GenMeshHeightmap(Image heightmap, Vector3 size) { - #define GRAY_VALUE(c) ((c.r+c.g+c.b)/3.0f) + #define GRAY_VALUE(c) ((float)(c.r + c.g + c.b)/3.0f) Mesh mesh = { 0 }; @@ -2640,7 +2640,7 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size) Color *pixels = LoadImageColors(heightmap); // NOTE: One vertex per pixel - mesh.triangleCount = (mapX-1)*(mapZ-1)*2; // One quad every four pixels + mesh.triangleCount = (mapX - 1)*(mapZ - 1)*2; // One quad every four pixels mesh.vertexCount = mesh.triangleCount*3; @@ -2653,7 +2653,7 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size) 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, size.y/255.0f, size.z/mapZ }; + Vector3 scaleFactor = { size.x/(mapX - 1), size.y/255.0f, size.z/(mapZ - 1) }; Vector3 vA = { 0 }; Vector3 vB = { 0 }; @@ -2680,7 +2680,7 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size) 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 + // 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]; From e9ca38fafa603cf146e385456e9ccfcb6f15df70 Mon Sep 17 00:00:00 2001 From: bXi Date: Mon, 26 Sep 2022 12:24:10 +0200 Subject: [PATCH 0079/1710] Clarified working of ImageDrawCircle and ImageDrawCircleV (#2719) --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 84a18ebe2..df59ffefa 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1285,8 +1285,8 @@ RLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color); 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 ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle within an image -RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw circle within an image (Vector version) +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 RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version) RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image From 8f88c61bdfe1e6e009dd6f182bc7a2f5c8b38f15 Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Tue, 27 Sep 2022 01:27:22 +0200 Subject: [PATCH 0080/1710] update build.zig (#2720) zig `master` now enforces to use addIncludePath instead of addIncludeDir --- src/build.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/build.zig b/src/build.zig index 7834ab249..44a3c1714 100644 --- a/src/build.zig +++ b/src/build.zig @@ -17,7 +17,7 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. raylib.setBuildMode(mode); raylib.linkLibC(); - raylib.addIncludeDir(srcdir ++ "/external/glfw/include"); + raylib.addIncludePath(srcdir ++ "/external/glfw/include"); raylib.addCSourceFiles(&.{ srcdir ++ "/raudio.c", @@ -35,7 +35,7 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); - raylib.addIncludeDir("external/glfw/deps/mingw"); + raylib.addIncludePath("external/glfw/deps/mingw"); }, .linux => { raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); From 5c404c79da4ad1507b49557a2fba1fdbe5b8809e Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Wed, 28 Sep 2022 21:04:41 +0300 Subject: [PATCH 0081/1710] Update raylib.h (#2726) --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index df59ffefa..4d0270d2b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -663,7 +663,7 @@ typedef enum { MOUSE_BUTTON_MIDDLE = 2, // Mouse button middle (pressed wheel) MOUSE_BUTTON_SIDE = 3, // Mouse button side (advanced mouse device) MOUSE_BUTTON_EXTRA = 4, // Mouse button extra (advanced mouse device) - MOUSE_BUTTON_FORWARD = 5, // Mouse button fordward (advanced mouse device) + MOUSE_BUTTON_FORWARD = 5, // Mouse button forward (advanced mouse device) MOUSE_BUTTON_BACK = 6, // Mouse button back (advanced mouse device) } MouseButton; From 587e61def9450dcf64648177e1158acc9762f2a2 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Wed, 28 Sep 2022 21:04:59 +0300 Subject: [PATCH 0082/1710] fixing new typoes (#2727) --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 4d0270d2b..4f0483f15 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -305,7 +305,7 @@ typedef struct Camera3D { 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 apperture 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 width in orthographic int projection; // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; @@ -967,7 +967,7 @@ RLAPI void DisableEventWaiting(void); // Disable wai // Custom frame control functions // NOTE: Those functions are intended for advance users that want full control over the frame processing -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents() +// 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 From 0ced04f0df789fef5a7e3ff7ff89c67d3e4bdd3a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 29 Sep 2022 11:08:00 +0200 Subject: [PATCH 0083/1710] Fix #2722 --- src/rcore.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index c7986dfb7..7bb71f18c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4134,11 +4134,11 @@ static bool InitGraphicsDevice(int width, int height) glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); - glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) - glfwMakeContextCurrent(CORE.Window.handle); #if !defined(PLATFORM_WEB) + glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + glfwSwapInterval(0); // No V-Sync by default #endif @@ -5267,9 +5267,11 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; else CORE.Input.Keyboard.currentKeyState[key] = 1; +#if !defined(PLATFORM_WEB) // 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; +#endif // Check if there is space available in the key queue if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) From 4fe6f885f75bd007c19a23498795caf8b61805e8 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 29 Sep 2022 11:23:25 +0200 Subject: [PATCH 0084/1710] Update gamepad mappings with latest gamecontrollerdb, fix #2725 --- src/external/glfw/src/mappings.h | 1668 ++++++++++++++++++++---------- 1 file changed, 1114 insertions(+), 554 deletions(-) diff --git a/src/external/glfw/src/mappings.h b/src/external/glfw/src/mappings.h index 553fe2a28..3444d3f6a 100644 --- a/src/external/glfw/src/mappings.h +++ b/src/external/glfw/src/mappings.h @@ -61,53 +61,81 @@ const char* _glfwDefaultMappings[] = { #if defined(GLFW_BUILD_WIN32_MAPPINGS) -"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,", +"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,", +"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,", -"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,", +"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,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,", "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,", -"03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,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,", -"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,", +"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,", +"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,", -"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,", +"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,", -"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,", +"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,", -"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,", +"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,", "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,", +"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:b8,leftx:a0,lefty:a1,paddle1:b23,paddle2:b19,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,", -"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,", +"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,", "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,", @@ -115,509 +143,946 @@ const char* _glfwDefaultMappings[] = "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,", +"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,", -"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,", +"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,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,", +"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,", +"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,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 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,", +"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,", "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,", +"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,", +"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,", -"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,", +"03000000120c0000200e000000000000,Brook Mars 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:a3,start:b9,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,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,", +"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,", -"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,", +"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,", -"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,", +"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,", -"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,", +"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,", "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,", +"03000000317300000100000000000000,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,", +"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,", +"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,", +"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,", +"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 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,", +"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,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,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,", +"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:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,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,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows,", +"03000000b40400001224000000000000,Flydigi Vader 2 Pro,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,paddle1:b15,paddle2:b16,paddle3:b17,paddle4:b18,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,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,", -"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,", +"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,", +"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,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,", +"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 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,", +"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,", -"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,", +"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,", +"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,", -"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,", +"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,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,", +"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,", +"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,", +"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,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,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,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,", -"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,", +"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,", "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,", +"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,", -"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,", +"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,", +"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,", +"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,", +"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,", "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,", +"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,", +"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,", "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,", +"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,", -"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,", +"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,", -"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,", +"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,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,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,", -"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,", +"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,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,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 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,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,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,", +"03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,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,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,", +"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,", -"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,", +"030000008f0e00001030000000000000,Mayflash Sega Saturn Adapter,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 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: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,", +"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,", "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,", +"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,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,", +"030000006b140000100d000000000000,Nacon Revolution Infinity 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:a3,start:b9,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,", -"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,", +"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,", -"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,", +"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,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,", +"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,", -"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,", +"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:b13,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,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:b1,y:b0,platform:Windows,", +"030000006f0e00000901000000000000,PDP 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,", +"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,", +"03000000e30500009605000000000000,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,", "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,", +"03000000632500002306000000000000,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,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,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,", "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,", +"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,", +"03000000d620000013a7000000000000,PowerA 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,", +"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,", -"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,", +"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,", "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,", +"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.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,", +"03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,leftshoulder:b10,leftstick:b1,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,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,", -"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,", +"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,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,", +"03000000120c0000111e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", +"03000000120c0000130e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", +"03000000120c0000180e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", +"03000000120c0000191e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", +"03000000120c0000a957000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", +"03000000120c0000f21c000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", +"03000000120c0000f41c000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", +"03000000120c0000f70e000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", +"03000000160e0000120c000000000000,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,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,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,", "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,", +"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,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,", +"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,", +"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,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,", +"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,", +"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,", +"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,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,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,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,", +"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,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,", -"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,", +"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,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,", +"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,", +"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,", +"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(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,", +"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,", "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,", +"030000006f0e00008701000000000000,Rock Candy Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,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,", +"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,", "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,", +"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game,a:b1,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 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,", +"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,", -"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,", +"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,", -"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,", +"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,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,", +"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,", +"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,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,", +"0300000003040000c197000000000000,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,", +"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,", +"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,", +"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,", "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,", +"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,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,", "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,", -"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,", +"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,", +"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,", +"03000000d620000011a7000000000000,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,", +"030000000d0f0000f600000000000000,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: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,", +"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,", +"03000000790000002601000000000000,TGZ,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,", "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,", +"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 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,", +"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,", -"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,", +"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:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b10,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,", -"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,", +"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,", "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,", +"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,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,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,", +"03000000300f00008888000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:b13,dpleft:b10,dpright:b11,dpup:b12,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,", +"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,", +"030000000d0f00006300000000000000,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:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b9,rightx:a3,righty:a4,start:b7,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 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,", +"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,", +"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,", "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,", +"xinput,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,", +"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,", +"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,", #endif // GLFW_BUILD_WIN32_MAPPINGS #if defined(GLFW_BUILD_COCOA_MAPPINGS) -"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,", +"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,", +"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,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,", +"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,", +"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,", -"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,", +"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,", +"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+,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,", +"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,", -"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,", +"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,", "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,", +"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,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,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 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,", +"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 Premiere 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:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000151900004000000001000000,Flydigi Vader 2,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:Mac OS X,", +"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,paddle1:b4,paddle2:b5,paddle3:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,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,", +"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,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,", +"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,", +"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,", -"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,", +"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,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,", +"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,", +"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,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,", +"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 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,", "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,", +"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 Gamepad,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 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,", +"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 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,", +"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 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,", +"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+,a:b1,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,x:b0,y:b3,platform:Mac OS X,", +"03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S+,a: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,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,", +"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,", -"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,", +"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,", +"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,", +"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,", +"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,", +"03000000d620000011a7000000020000,Nintendo Switch Core Plus 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 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,", +"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,", +"030000006f0e00000901000002010000,PDP 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,", +"030000004c050000da0c000000010000,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,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,", +"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,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,", +"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,", +"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,", +"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,", "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,", +"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,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,", +"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,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,", +"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,", +"030000000d0f0000c100000072050000,Retro Bit Sega Genesis 6B Controller,a:b2,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b8,rightshoulder:b6,righttrigger:b7,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,", "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,", +"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,", +"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 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,", +"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,", -"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,", +"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,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,", +"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,", +"050000004e696d6275732b008b000000,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,", +"05000000556e6b6e6f776e2048494400,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,", -"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,", +"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,", "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,", +"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,", +"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,", -"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,", +"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,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,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,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,", +"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,", +"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 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,", +"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,", +"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: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,", +"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,", +"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,", -"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,", +"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,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,", +"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_BUILD_COCOA_MAPPINGS #if defined(GLFW_BUILD_LINUX_MAPPINGS) +"030000005e0400008e02000020010000,8BitDo 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,", +"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,", +"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,", +"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,", +"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,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,", +"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,", +"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,", -"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,", +"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,", +"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,", +"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,", "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,", +"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,", @@ -626,219 +1091,272 @@ const char* _glfwDefaultMappings[] = "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,", +"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,", +"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,", +"050000005e040000e002000030110000,8BitDo Zero 2,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,", "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,", +"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,", "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,", +"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,", "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,", +"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,", -"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,", +"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 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 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 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 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 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 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 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 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 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 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,", +"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,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,", +"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,", "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,", +"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,", +"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 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,", +"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,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,", +"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,", +"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,", -"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,", +"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,", +"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,paddle1:b2,paddle2:b5,paddle4:b17,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: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,", +"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,", +"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,", "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,", +"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,", -"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,", +"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,", "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,", +"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,", -"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,", +"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,", +"03000000632500002605000010010000,HJDX,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,rightshoulder:b7,start:b9,x:b1,y:b2,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,", +"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,", +"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,", +"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,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,", -"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,", +"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,", +"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,", +"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,", +"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 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: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,", +"030000000d0f0000c100000011010000,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,", +"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,", "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,", +"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,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,", +"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 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,", +"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,", +"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,", +"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,", -"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,", +"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,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,", +"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,", +"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,", -"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,", +"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,", -"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,", +"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,", +"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,", +"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,", +"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,", "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,", +"030000005e0400000700000000010000,Microsoft SideWinder Gamepad,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: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,", +"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,", +"030000005e040000120b00000b050000,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,", +"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,", +"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,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000dd02000003020000,Microsoft Xbox One 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,", +"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,", +"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,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,", +"030000005e040000ea02000008040000,Microsoft Xbox One S,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,", +"030000005e0400008902000021010000,Microsoft Xbox pad v2,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,", "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,", +"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,", +"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 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,", +"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,", +"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,", "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,", +"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,", -"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,", +"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,", +"03000000d620000013a7000011010000,Nintendo Switch PowerA 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,", +"03000000d620000011a7000011010000,Nintendo Switch PowerA Core Plus 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,", +"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:b4,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,", +"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,", +"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,", +"050000007e0500001720000001000000,NSO SNES 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,", "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,", +"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,", "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,", +"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,", +"03000000790000001c18000011010000,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,", +"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,", -"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,", +"030000006f0e0000d702000006640000,PDP Black Camo Wired Xbox Series X 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,", +"030000006f0e00008001000011010000,PDP Faceoff Nintendo Switch 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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,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,", +"030000006f0e00008501000011010000,PDP Nintendo Switch Fightpad 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,", +"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,", +"030000006f0e00008701000011010000,PDP Rock Nintendo Switch Candy Controller,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,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 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,", +"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,", +"03000000d9040000160f000000010000,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,", "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,", +"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,", +"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,", "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,", +"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,", "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,", @@ -846,6 +1364,7 @@ const char* _glfwDefaultMappings[] = "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,", @@ -868,134 +1387,175 @@ const char* _glfwDefaultMappings[] = "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,", +"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,", +"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,", +"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,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,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,", +"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,", "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,", +"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 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,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,", +"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,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,", +"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,", "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,", "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,", +"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,", -"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,", +"03000000a306000023f6000011010000,Saitek Cyborg V1 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 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,", +"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,", +"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 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,", +"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,", "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,", +"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,", +"030000001f08000001e4000010010000,SFC 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,", +"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,", +"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,", +"03000000632500007505000010010000,Shanwan PS3 PC,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 ,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,", +"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,", +"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,x:b0,y:b3,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,", +"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,", "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,", +"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:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,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: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,", +"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,", "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 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,", +"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,", "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,", +"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,", +"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,", +"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,", +"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,", -"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,", +"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,", -"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,", +"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,", -"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,", +"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,", +"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,", "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,", +"03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,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,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,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,", +"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,", +"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,", +"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,", +"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,", +"0000000058626f782047616d65706100,Xbox 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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,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,", +"030000005e040000120b000009050000,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,", "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,", +"060000005e040000120b000007050000,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,", -"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,", +"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,", "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,", +"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,", "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,", +"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,", +"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,", +"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,", +"030000005e040000120b000007050000,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,", +"050000005e040000130b000011050000,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,", +"050000005e040000130b000007050000,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,", +"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,", +"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,", +"xinput,XInput 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,", +"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,", #endif // GLFW_BUILD_LINUX_MAPPINGS }; From 45da03df236db2090ac9a53aac572b0b91278a64 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 29 Sep 2022 12:00:20 +0200 Subject: [PATCH 0085/1710] Reviewed monitor checking order --- src/rcore.c | 155 ++++++++++++++++++++++++++++------------------------ 1 file changed, 85 insertions(+), 70 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 7bb71f18c..9f4af1daa 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1695,49 +1695,58 @@ int GetMonitorCount(void) // Get number of monitors int GetCurrentMonitor(void) { + int index = 0; + #if defined(PLATFORM_DESKTOP) int monitorCount; - GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); - GLFWmonitor* monitor = NULL; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor *monitor = NULL; - if (monitorCount == 1) // easy out - return 0; - - if (IsWindowFullscreen()) + if (monitorCount > 1) { - monitor = glfwGetWindowMonitor(CORE.Window.handle); - for (int i = 0; i < monitorCount; i++) + if (IsWindowFullscreen()) { - if (monitors[i] == monitor) - return i; + // Get the handle of the monitor that the specified window is in full screen on + monitor = glfwGetWindowMonitor(CORE.Window.handle); + + for (int i = 0; i < monitorCount; i++) + { + if (monitors[i] == monitor) + { + index = i; + break; + } + } } - return 0; - } - else - { - int x = 0; - int y = 0; - - glfwGetWindowPos(CORE.Window.handle, &x, &y); - - for (int i = 0; i < monitorCount; i++) + else { - int mx = 0; - int my = 0; + int x = 0; + int y = 0; - int width = 0; - int height = 0; + glfwGetWindowPos(CORE.Window.handle, &x, &y); - monitor = monitors[i]; - glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); - if (x >= mx && x <= (mx + width) && y >= my && y <= (my + height)) - return i; + for (int i = 0; i < monitorCount; i++) + { + int mx = 0; + int my = 0; + + int width = 0; + int height = 0; + + monitor = monitors[i]; + glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); + + if (x >= mx && x <= (mx + width) && y >= my && y <= (my + height)) + { + index = i; + break; + } + } } } - return 0; -#else - return 0; #endif + + return index; } // Get selected monitor position @@ -1745,7 +1754,7 @@ Vector2 GetMonitorPosition(int monitor) { #if defined(PLATFORM_DESKTOP) int monitorCount; - GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); if ((monitor >= 0) && (monitor < monitorCount)) { @@ -1815,7 +1824,7 @@ int GetMonitorPhysicalWidth(int monitor) return 0; } -// Get primary monitor physical height in millimetres +// Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { #if defined(PLATFORM_DESKTOP) @@ -1833,6 +1842,7 @@ int GetMonitorPhysicalHeight(int monitor) return 0; } +// Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { #if defined(PLATFORM_DESKTOP) @@ -1866,7 +1876,7 @@ Vector2 GetWindowPosition(void) return (Vector2){ (float)x, (float)y }; } -// Get window scale DPI factor +// Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { Vector2 scale = { 1.0f, 1.0f }; @@ -1900,7 +1910,7 @@ Vector2 GetWindowScaleDPI(void) return scale; } -// Get the human-readable, UTF-8 encoded name of the primary monitor +// Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { #if defined(PLATFORM_DESKTOP) @@ -3897,30 +3907,6 @@ static bool InitGraphicsDevice(int width, int height) return false; } - // NOTE: Getting video modes is not implemented in emscripten GLFW3 version -#if defined(PLATFORM_DESKTOP) - // Find monitor resolution - GLFWmonitor *monitor = glfwGetPrimaryMonitor(); - if (!monitor) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); - return false; - } - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - - 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; -#endif // PLATFORM_DESKTOP - -#if defined(PLATFORM_WEB) - CORE.Window.display.width = CORE.Window.screen.width; - CORE.Window.display.height = CORE.Window.screen.height; -#endif // PLATFORM_WEB - glfwDefaultWindowHints(); // Set default windows hints //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits @@ -4037,6 +4023,31 @@ static bool InitGraphicsDevice(int width, int height) if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); #endif +#if defined(PLATFORM_DESKTOP) + // Find monitor resolution + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + if (!monitor) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); + return false; + } + + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + + 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; +#endif // PLATFORM_DESKTOP + +#if defined(PLATFORM_WEB) + // 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; +#endif // PLATFORM_WEB + if (CORE.Window.fullscreen) { // remember center for switchinging from fullscreen to window @@ -4126,6 +4137,7 @@ static bool InitGraphicsDevice(int width, int height) glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + // Set input callback events glfwSetKeyCallback(CORE.Window.handle, KeyCallback); glfwSetCharCallback(CORE.Window.handle, CharCallback); @@ -4258,6 +4270,7 @@ static bool InitGraphicsDevice(int width, int height) drmModeFreeConnector(con); } } + if (!CORE.Window.connector) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); @@ -4303,18 +4316,20 @@ static bool InitGraphicsDevice(int width, int 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 + + // Try to find an exact matching mode CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); - // if nothing found, try to find a nearly matching mode - if (CORE.Window.modeIndex < 0) - CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); - // if nothing found, try to find an exactly matching mode including interlaced - if (CORE.Window.modeIndex < 0) - CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); - // if nothing found, try to find a nearly matching mode including interlaced - if (CORE.Window.modeIndex < 0) - CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); - // if nothing found, there is no suitable mode + + // If nothing found, try to find a nearly matching mode + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + + // If nothing found, try to find an exactly matching mode including interlaced + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + + // If nothing found, try to find a nearly matching mode including interlaced + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + + // If nothing found, there is no suitable mode if (CORE.Window.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); From f14955512f4c47e9b77cd5e17b969a43543ec705 Mon Sep 17 00:00:00 2001 From: Random <35673979+RandomErrorMessage@users.noreply.github.com> Date: Fri, 30 Sep 2022 01:09:49 -0700 Subject: [PATCH 0086/1710] fix issue #2728 (#2731) * fix issue #2728 * updated gamecontrollerdb: fixes GLFW warning due to invalid entry --- src/external/glfw/src/mappings.h | 9 +++++++-- src/rcore.c | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/external/glfw/src/mappings.h b/src/external/glfw/src/mappings.h index 3444d3f6a..f3a778baa 100644 --- a/src/external/glfw/src/mappings.h +++ b/src/external/glfw/src/mappings.h @@ -803,11 +803,17 @@ const char* _glfwDefaultMappings[] = "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,", "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,", -"xinput,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,", "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,", "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_BUILD_WIN32_MAPPINGS #if defined(GLFW_BUILD_COCOA_MAPPINGS) @@ -1553,7 +1559,6 @@ const char* _glfwDefaultMappings[] = "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,", -"xinput,XInput 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,", "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,", #endif // GLFW_BUILD_LINUX_MAPPINGS diff --git a/src/rcore.c b/src/rcore.c index 9f4af1daa..ef261dc2c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4109,8 +4109,10 @@ static bool InitGraphicsDevice(int width, int height) { #if defined(PLATFORM_DESKTOP) // Center window on screen - int windowPosX = CORE.Window.display.width/2 - CORE.Window.screen.width/2; - int windowPosY = CORE.Window.display.height/2 - CORE.Window.screen.height/2; + int windowPosX, windowPosY; + glfwGetMonitorPos(monitor, &windowPosX, &windowPosY); + windowPosX += CORE.Window.display.width/2 - CORE.Window.screen.width/2; + windowPosY += CORE.Window.display.height/2 - CORE.Window.screen.height/2; if (windowPosX < 0) windowPosX = 0; if (windowPosY < 0) windowPosY = 0; From 178a356cb4130f942f051f5669f46ebcb7f438bc Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 30 Sep 2022 23:07:29 +0200 Subject: [PATCH 0087/1710] minor tweaks --- src/external/rl_gputex.h | 44 ++++++++++++++++++++-------------------- src/rtextures.c | 1 - 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 22bb72816..6d2e97b27 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -778,28 +778,28 @@ static int get_pixel_data_size(int width, int height, int format) 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; + 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 diff --git a/src/rtextures.c b/src/rtextures.c index 41bd3e184..ceca1a305 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -402,7 +402,6 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #if defined(SUPPORT_FILEFORMAT_DDS) else if (strcmp(fileType, ".dds") == 0) { - int format = 0; image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif From 03f5fce6729bfec2bde7885e40d36737754f935e Mon Sep 17 00:00:00 2001 From: Random <35673979+RandomErrorMessage@users.noreply.github.com> Date: Sun, 2 Oct 2022 01:46:33 -0700 Subject: [PATCH 0088/1710] removed glfwSetWindowPos on InitWindow (#2732) * removed glfwSetWindowPos on InitWindow * removed execute permission from CMakeLists --- CMakeLists.txt | 0 src/rcore.c | 40 ++++++++++++++-------------------------- 2 files changed, 14 insertions(+), 26 deletions(-) mode change 100755 => 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/src/rcore.c b/src/rcore.c index ef261dc2c..d5823ad0b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -241,7 +241,7 @@ //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() #endif - + // TODO: HACK: Added flag if not provided by GLFW when using external library // Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev #if !defined(GLFW_MOUSE_PASSTHROUGH) @@ -1708,12 +1708,12 @@ int GetCurrentMonitor(void) { // Get the handle of the monitor that the specified window is in full screen on monitor = glfwGetWindowMonitor(CORE.Window.handle); - + for (int i = 0; i < monitorCount; i++) { if (monitors[i] == monitor) - { - index = i; + { + index = i; break; } } @@ -1735,7 +1735,7 @@ int GetCurrentMonitor(void) monitor = monitors[i]; glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); - + if (x >= mx && x <= (mx + width) && y >= my && y <= (my + height)) { index = i; @@ -3790,7 +3790,7 @@ float GetMouseWheelMove(void) Vector2 GetMouseWheelMoveV(void) { Vector2 result = { 0 }; - + result = CORE.Input.Mouse.currentWheelMove; return result; @@ -4107,18 +4107,6 @@ static bool InitGraphicsDevice(int width, int height) if (CORE.Window.handle) { -#if defined(PLATFORM_DESKTOP) - // Center window on screen - int windowPosX, windowPosY; - glfwGetMonitorPos(monitor, &windowPosX, &windowPosY); - windowPosX += CORE.Window.display.width/2 - CORE.Window.screen.width/2; - windowPosY += CORE.Window.display.height/2 - CORE.Window.screen.height/2; - - if (windowPosX < 0) windowPosX = 0; - if (windowPosY < 0) windowPosY = 0; - - glfwSetWindowPos(CORE.Window.handle, windowPosX, windowPosY); -#endif CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; } @@ -4139,7 +4127,7 @@ static bool InitGraphicsDevice(int width, int height) glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); - + // Set input callback events glfwSetKeyCallback(CORE.Window.handle, KeyCallback); glfwSetCharCallback(CORE.Window.handle, CharCallback); @@ -4272,7 +4260,7 @@ static bool InitGraphicsDevice(int width, int height) drmModeFreeConnector(con); } } - + if (!CORE.Window.connector) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); @@ -4318,19 +4306,19 @@ static bool InitGraphicsDevice(int width, int 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 CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); - + // If nothing found, try to find a nearly matching mode if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); - + // If nothing found, try to find an exactly matching mode including interlaced if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); - + // If nothing found, try to find a nearly matching mode including interlaced if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); - + // If nothing found, there is no suitable mode if (CORE.Window.modeIndex < 0) { @@ -5278,7 +5266,7 @@ static void WindowFocusCallback(GLFWwindow *window, int focused) static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { if (key < 0) return; // Security check, macOS fn key generates -1 - + // 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; From 0daaaddeef7668cb2bea02fccfb504c8939fd44c Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 2 Oct 2022 04:47:17 -0400 Subject: [PATCH 0089/1710] Fix Gestures to use GetTime() if it's available (#2733) --- src/rgestures.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rgestures.h b/src/rgestures.h index e33e52ef9..1703dbd0d 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -151,6 +151,7 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang #if defined(GESTURES_IMPLEMENTATION) +#if defined(GESTURES_STANDALONE) #if defined(_WIN32) #if defined(__cplusplus) extern "C" { // Prevents name mangling of functions @@ -175,6 +176,7 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang #include // Required for: clock_get_time() #include // Required for: mach_timespec_t #endif +#endif //---------------------------------------------------------------------------------- // Defines and Macros @@ -526,6 +528,9 @@ static double rgGetCurrentTime(void) { double time = 0; +#if !defined(GESTURES_STANDALONE) + time = GetTime(); +#else #if defined(_WIN32) unsigned long long int clockFrequency, currentTime; @@ -558,6 +563,7 @@ static double rgGetCurrentTime(void) unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds time = ((double)nowTime/1000000.0); // Time in miliseconds +#endif #endif return time; From 33e7f7cc59df39021f2c289f44edc25843c8bb75 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Oct 2022 11:11:13 +0200 Subject: [PATCH 0090/1710] WARNING: `DrawLineBezier()` implementation needs review #2721 --- src/rshapes.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rshapes.c b/src/rshapes.c index 0a016eabb..f8d9db71f 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -175,6 +175,8 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)BEZIER_LINE_DIVISIONS); current.x = previous.x + (endPos.x - startPos.x)/ (float)BEZIER_LINE_DIVISIONS; + // TODO: Avoid drawing the line by pieces, it generates gaps for big thicks, + // Custom "triangle-strip" implementation should be used, check DrawTriangleStrip() for reference DrawLineEx(previous, current, thick, color); previous = current; @@ -201,6 +203,8 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl current.y = a*startPos.y + b*controlPos.y + c*endPos.y; current.x = a*startPos.x + b*controlPos.x + c*endPos.x; + // TODO: Avoid drawing the line by pieces, it generates gaps for big thicks, + // Custom "triangle-strip" implementation should be used, check DrawTriangleStrip() for reference DrawLineEx(previous, current, thick, color); previous = current; @@ -227,6 +231,8 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y; current.x = a*startPos.x + b*startControlPos.x + c*endControlPos.x + d*endPos.x; + // TODO: Avoid drawing the line by pieces, it generates gaps for big thicks, + // Custom "triangle-strip" implementation should be used, check DrawTriangleStrip() for reference DrawLineEx(previous, current, thick, color); previous = current; From 2872b2fff5c897b09029b3eaa6556621a8a0f189 Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Sun, 2 Oct 2022 22:30:26 +0500 Subject: [PATCH 0091/1710] Clear PCM buffer state when closing audio device (#2736) Fix for #2714 --- src/raudio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 78d3ebd34..d1b47f64a 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -509,7 +509,9 @@ void CloseAudioDevice(void) AUDIO.System.isReady = false; RL_FREE(AUDIO.System.pcmBuffer); - + AUDIO.System.pcmBuffer = NULL; + AUDIO.System.pcmBufferSize = 0; + TRACELOG(LOG_INFO, "AUDIO: Device closed successfully"); } else TRACELOG(LOG_WARNING, "AUDIO: Device could not be closed, not currently initialized"); From 62d228346bff31fe5836985f52f6ece59f9fbbf3 Mon Sep 17 00:00:00 2001 From: _Tradam Date: Sun, 2 Oct 2022 14:29:48 -0400 Subject: [PATCH 0092/1710] Update build.zig to work with last GLFW update (#2737) --- src/build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build.zig b/src/build.zig index 44a3c1714..8194e1e3c 100644 --- a/src/build.zig +++ b/src/build.zig @@ -8,6 +8,7 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. const raylib_flags = &[_][]const u8{ "-std=gnu99", "-DPLATFORM_DESKTOP", + "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; From 7459d906de4e6a605ac4eecf4401273aa2bc5aeb Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 3 Oct 2022 00:07:22 +0200 Subject: [PATCH 0093/1710] Update rtext.c --- src/rtext.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 324ebe95a..62996be6a 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -58,7 +58,7 @@ #if defined(SUPPORT_MODULE_RTEXT) -#include "utils.h" // Required for: LoadFileText() +#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 // Required for: malloc(), free() @@ -359,7 +359,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCou // Loading font from memory data font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, fontChars, glyphCount); - RL_FREE(fileData); + UnloadFileData(fileData); } else font = GetFontDefault(); From 2d88958d35346ab35c6ad6f11c75f8a1fdc2f799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=8C=E6=BA=A2=E7=8B=90?= <78730559+pure01fx@users.noreply.github.com> Date: Wed, 5 Oct 2022 19:05:44 +0800 Subject: [PATCH 0094/1710] Add rlSetBlendFactorsSeparate and custom blend mode modification checks (#2741) --- src/rlgl.h | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index ee3c3d91a..5f78cde01 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -413,7 +413,8 @@ typedef enum { RL_BLEND_ADD_COLORS, // Blend textures adding colors (alternative) RL_BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) RL_BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha - RL_BLEND_CUSTOM // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + RL_BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + RL_BLEND_CUSTOM_SEPARATE // Blend textures using custom src/dst factors (use rlSetBlendFactorsSeparate()) } rlBlendMode; // Shader location point type @@ -595,6 +596,7 @@ RLAPI void rlClearScreenBuffers(void); // Clear used screen buf RLAPI void rlCheckErrors(void); // Check and log OpenGL error codes RLAPI void rlSetBlendMode(int mode); // Set blending mode RLAPI void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation); // Set blending mode factor and equation (using OpenGL factors) +RLAPI void rlSetBlendFactorsSeparate(int srgb, int drgb, int salpha, int dalpha, int ergb, int ealpha); // Set blending mode factors and equations separately (using OpenGL factors) //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality @@ -930,6 +932,13 @@ typedef struct rlglData { int glBlendSrcFactor; // Blending source factor int glBlendDstFactor; // Blending destination factor int glBlendEquation; // Blending equation + int glBlendSrcFactorRGB; // Blending source RGB factor + int glBlendDestFactorRGB; // Blending destination RGB factor + int glBlendSrcFactorAlpha; // Blending source alpha factor + int glBlendDestFactorAlpha; // Blending destination alpha factor + int glBlendEquationRGB; // Blending equation for RGB + int glBlendEquationAlpha; // Blending equation for alpha + bool glCustomBlendModeModified; // Custom blending factor and equation modification status int framebufferWidth; // Current framebuffer width int framebufferHeight; // Current framebuffer height @@ -1788,7 +1797,7 @@ void rlCheckErrors() void rlSetBlendMode(int mode) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.State.currentBlendMode != mode) + if (RLGL.State.currentBlendMode != mode || ((mode == RL_BLEND_CUSTOM || mode == RL_BLEND_CUSTOM_SEPARATE) && RLGL.State.glCustomBlendModeModified)) { rlDrawRenderBatch(RLGL.currentBatch); @@ -1805,21 +1814,52 @@ void rlSetBlendMode(int mode) // 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; } RLGL.State.currentBlendMode = mode; + RLGL.State.glCustomBlendModeModified = false; } #endif } +// Set blending mode factor and equation used by glBlendFuncSeparate and glBlendEquationSeparate +void rlSetBlendFactorsSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha, int modeRGB, int modeAlpha) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.State.glBlendSrcFactorRGB == srcRGB + && RLGL.State.glBlendDestFactorRGB == dstRGB + && RLGL.State.glBlendSrcFactorAlpha == srcAlpha + && RLGL.State.glBlendDestFactorAlpha == destAlpha + && RLGL.State.glBlendEquationRGB == modeRGB + && RLGL.State.glBlendEquationAlpha == modeAlpha) return; + RLGL.State.glBlendSrcFactorRGB = srcRGB; + RLGL.State.glBlendDestFactorRGB = destRGB; + RLGL.State.glBlendSrcFactorAlpha = srcAlpha; + RLGL.State.glBlendDestFactorAlpha = dstAlpha; + RLGL.State.glBlendEquationRGB = modeRGB; + RLGL.State.glBlendEquationAlpha = modeAlpha; + RLGL.State.glCustomBlendModeModified = true; +#endif +} + // Set blending mode factor and equation void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.State.glBlendSrcFactor == glSrcFactor + && RLGL.State.glBlendDstFactor == glDstFactor + && RLGL.State.glBlendEquation == glEquation) return; RLGL.State.glBlendSrcFactor = glSrcFactor; RLGL.State.glBlendDstFactor = glDstFactor; RLGL.State.glBlendEquation = glEquation; + RLGL.State.glCustomBlendModeModified = true; #endif } @@ -1969,6 +2009,9 @@ void rlglInit(int width, int height) //---------------------------------------------------------- #endif + // Init state: custom blend factor and equation modification flag + RLGL.State.glCustomBlendModeModified = false; + // 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) From 9017be32596c1ac3847e04e750fcd482d3b2d9fc Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Oct 2022 13:29:34 +0200 Subject: [PATCH 0095/1710] Reviewed latest PR formating and variables naming #2741 --- src/rlgl.h | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 5f78cde01..326204875 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -596,7 +596,7 @@ RLAPI void rlClearScreenBuffers(void); // Clear used screen buf RLAPI void rlCheckErrors(void); // Check and log OpenGL error codes RLAPI void rlSetBlendMode(int mode); // Set blending mode RLAPI void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation); // Set blending mode factor and equation (using OpenGL factors) -RLAPI void rlSetBlendFactorsSeparate(int srgb, int drgb, int salpha, int dalpha, int ergb, int ealpha); // Set blending mode factors and equations separately (using OpenGL factors) +RLAPI void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha); // Set blending mode factors and equations separately (using OpenGL factors) //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality @@ -928,6 +928,7 @@ typedef struct rlglData { Matrix projectionStereo[2]; // VR stereo rendering eyes projection matrices Matrix viewOffsetStereo[2]; // VR stereo rendering eyes view offset matrices + // Blending variables int currentBlendMode; // Blending mode active int glBlendSrcFactor; // Blending source factor int glBlendDstFactor; // Blending destination factor @@ -1813,14 +1814,15 @@ void rlSetBlendMode(int mode) { // 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; - } + + } break; default: break; } @@ -1831,20 +1833,23 @@ void rlSetBlendMode(int mode) } // Set blending mode factor and equation used by glBlendFuncSeparate and glBlendEquationSeparate -void rlSetBlendFactorsSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha, int modeRGB, int modeAlpha) { +void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha) +{ #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.State.glBlendSrcFactorRGB == srcRGB - && RLGL.State.glBlendDestFactorRGB == dstRGB - && RLGL.State.glBlendSrcFactorAlpha == srcAlpha - && RLGL.State.glBlendDestFactorAlpha == destAlpha - && RLGL.State.glBlendEquationRGB == modeRGB - && RLGL.State.glBlendEquationAlpha == modeAlpha) return; - RLGL.State.glBlendSrcFactorRGB = srcRGB; - RLGL.State.glBlendDestFactorRGB = destRGB; - RLGL.State.glBlendSrcFactorAlpha = srcAlpha; - RLGL.State.glBlendDestFactorAlpha = dstAlpha; - RLGL.State.glBlendEquationRGB = modeRGB; - RLGL.State.glBlendEquationAlpha = modeAlpha; + if ((RLGL.State.glBlendSrcFactorRGB == glSrcRGB) && + (RLGL.State.glBlendDestFactorRGB == glDstRGB) && + (RLGL.State.glBlendSrcFactorAlpha == glSrcAlpha) && + (RLGL.State.glBlendDestFactorAlpha == glDstAlpha) && + (RLGL.State.glBlendEquationRGB == glEqRGB) && + (RLGL.State.glBlendEquationAlpha == glEqAlpha)) return; + + RLGL.State.glBlendSrcFactorRGB = glSrcRGB; + RLGL.State.glBlendDestFactorRGB = glDstRGB; + RLGL.State.glBlendSrcFactorAlpha = glSrcAlpha; + RLGL.State.glBlendDestFactorAlpha = glDstAlpha; + RLGL.State.glBlendEquationRGB = glEqRGB; + RLGL.State.glBlendEquationAlpha = glEqAlpha; + RLGL.State.glCustomBlendModeModified = true; #endif } @@ -1853,12 +1858,14 @@ void rlSetBlendFactorsSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlph void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.State.glBlendSrcFactor == glSrcFactor - && RLGL.State.glBlendDstFactor == glDstFactor - && RLGL.State.glBlendEquation == glEquation) return; + if ((RLGL.State.glBlendSrcFactor == glSrcFactor) && + (RLGL.State.glBlendDstFactor == glDstFactor) && + (RLGL.State.glBlendEquation == glEquation)) return; + RLGL.State.glBlendSrcFactor = glSrcFactor; RLGL.State.glBlendDstFactor = glDstFactor; RLGL.State.glBlendEquation = glEquation; + RLGL.State.glCustomBlendModeModified = true; #endif } @@ -2009,9 +2016,6 @@ void rlglInit(int width, int height) //---------------------------------------------------------- #endif - // Init state: custom blend factor and equation modification flag - RLGL.State.glCustomBlendModeModified = false; - // 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) From 25d846aa9aeb195ebe1128174ff30a7d5396ed32 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Oct 2022 13:34:19 +0200 Subject: [PATCH 0096/1710] Avoid early return calls --- src/rlgl.h | 62 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 326204875..455330c73 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1832,41 +1832,43 @@ void rlSetBlendMode(int mode) #endif } -// Set blending mode factor and equation used by glBlendFuncSeparate and glBlendEquationSeparate -void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((RLGL.State.glBlendSrcFactorRGB == glSrcRGB) && - (RLGL.State.glBlendDestFactorRGB == glDstRGB) && - (RLGL.State.glBlendSrcFactorAlpha == glSrcAlpha) && - (RLGL.State.glBlendDestFactorAlpha == glDstAlpha) && - (RLGL.State.glBlendEquationRGB == glEqRGB) && - (RLGL.State.glBlendEquationAlpha == glEqAlpha)) return; - - RLGL.State.glBlendSrcFactorRGB = glSrcRGB; - RLGL.State.glBlendDestFactorRGB = glDstRGB; - RLGL.State.glBlendSrcFactorAlpha = glSrcAlpha; - RLGL.State.glBlendDestFactorAlpha = glDstAlpha; - RLGL.State.glBlendEquationRGB = glEqRGB; - RLGL.State.glBlendEquationAlpha = glEqAlpha; - - RLGL.State.glCustomBlendModeModified = true; -#endif -} - // Set blending mode factor and equation void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((RLGL.State.glBlendSrcFactor == glSrcFactor) && - (RLGL.State.glBlendDstFactor == glDstFactor) && - (RLGL.State.glBlendEquation == glEquation)) return; + if ((RLGL.State.glBlendSrcFactor != glSrcFactor) || + (RLGL.State.glBlendDstFactor != glDstFactor) || + (RLGL.State.glBlendEquation != glEquation)) + { + RLGL.State.glBlendSrcFactor = glSrcFactor; + RLGL.State.glBlendDstFactor = glDstFactor; + RLGL.State.glBlendEquation = glEquation; + + RLGL.State.glCustomBlendModeModified = true; + } +#endif +} - RLGL.State.glBlendSrcFactor = glSrcFactor; - RLGL.State.glBlendDstFactor = glDstFactor; - RLGL.State.glBlendEquation = glEquation; - - RLGL.State.glCustomBlendModeModified = true; +// Set blending mode factor and equation separately for RGB and alpha +void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.glBlendSrcFactorRGB != glSrcRGB) || + (RLGL.State.glBlendDestFactorRGB != glDstRGB) || + (RLGL.State.glBlendSrcFactorAlpha != glSrcAlpha) || + (RLGL.State.glBlendDestFactorAlpha != glDstAlpha) || + (RLGL.State.glBlendEquationRGB != glEqRGB) || + (RLGL.State.glBlendEquationAlpha != glEqAlpha)) + { + RLGL.State.glBlendSrcFactorRGB = glSrcRGB; + RLGL.State.glBlendDestFactorRGB = glDstRGB; + RLGL.State.glBlendSrcFactorAlpha = glSrcAlpha; + RLGL.State.glBlendDestFactorAlpha = glDstAlpha; + RLGL.State.glBlendEquationRGB = glEqRGB; + RLGL.State.glBlendEquationAlpha = glEqAlpha; + + RLGL.State.glCustomBlendModeModified = true; + } #endif } From 26969c2c38cfea301a570714a13ebd5fd6764708 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Oct 2022 13:57:38 +0200 Subject: [PATCH 0097/1710] Added `BLEND_CUSTOM_SEPARATE` #2741 --- src/raylib.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 4f0483f15..3494adaa9 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -857,7 +857,8 @@ typedef enum { BLEND_ADD_COLORS, // Blend textures adding colors (alternative) BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha - BLEND_CUSTOM // Blend textures using custom src/dst factors (use rlSetBlendMode()) + BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendMode()) + BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendModeSeparate()) } BlendMode; // Gesture From 7a15861d442fa8db6c9ab783e1f8eba50e2954ef Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Oct 2022 00:56:18 +0200 Subject: [PATCH 0098/1710] Update rlgl.h --- src/rlgl.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 455330c73..79c661cd9 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -450,7 +450,7 @@ typedef enum { #define RL_SHADER_LOC_MAP_DIFFUSE RL_SHADER_LOC_MAP_ALBEDO #define RL_SHADER_LOC_MAP_SPECULAR RL_SHADER_LOC_MAP_METALNESS -// Shader uniform data type +// Shader uniform data type typedef enum { RL_SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float RL_SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) @@ -1814,14 +1814,14 @@ void rlSetBlendMode(int mode) { // 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; } @@ -1843,7 +1843,7 @@ void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) RLGL.State.glBlendSrcFactor = glSrcFactor; RLGL.State.glBlendDstFactor = glDstFactor; RLGL.State.glBlendEquation = glEquation; - + RLGL.State.glCustomBlendModeModified = true; } #endif @@ -1866,7 +1866,7 @@ void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int g RLGL.State.glBlendDestFactorAlpha = glDstAlpha; RLGL.State.glBlendEquationRGB = glEqRGB; RLGL.State.glBlendEquationAlpha = glEqAlpha; - + RLGL.State.glCustomBlendModeModified = true; } #endif From 38025362ee8e0d11c98174390e4c1ec1cc30b701 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 7 Oct 2022 16:22:44 +0200 Subject: [PATCH 0099/1710] Update version to `raylib 4.5-dev` to avoid confusions with 4.2 --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 3494adaa9..b83e6760b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib v4.2 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v4.5-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 @@ -81,7 +81,7 @@ #include // Required for: va_list - Only used by TraceLogCallback -#define RAYLIB_VERSION "4.2" +#define RAYLIB_VERSION "4.5-dev" // 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 From cee0fc5d7863da06d59a17f6a87b6965a3fa4967 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 7 Oct 2022 16:33:39 +0200 Subject: [PATCH 0100/1710] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9db359194..030cc93b0 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) [![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases) [![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers) [![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.2.0)](https://github.com/raysan5/raylib/commits/master) +[![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5) [![Packaging Status](https://repology.org/badge/tiny-repos/raylib.svg)](https://repology.org/project/raylib/versions) [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE) From cb085a1b50324315ec77f134be3447107c52cf2d Mon Sep 17 00:00:00 2001 From: JupiterRider <60042618+JupiterRider@users.noreply.github.com> Date: Fri, 7 Oct 2022 19:03:08 +0200 Subject: [PATCH 0101/1710] Update BINDINGS.md (#2745) --- BINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index ec8e9d885..86316aa3c 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -8,8 +8,8 @@ Some people ported raylib to other languages in form of bindings or wrappers to |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| | raylib | **4.2** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | -| Raylib-cs | **4.0** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | -| Raylib-CsLo | **4.0** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | +| Raylib-cs | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | +| Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | **4.0** | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | | raylib-cr | **4.0** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | From 8025b052b3de1fbdeaa07cdea0cdea335db22ac6 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Tue, 11 Oct 2022 12:20:29 +0300 Subject: [PATCH 0102/1710] fixing typo (#2748) --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index b83e6760b..4ec44b483 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -364,7 +364,7 @@ typedef struct Material { float params[4]; // Material generic parameters (if required) } Material; -// Transform, vectex transformation data +// Transform, vertex transformation data typedef struct Transform { Vector3 translation; // Translation Quaternion rotation; // Rotation From 0d04ceafbf4892d5a11bc9f642232c331785f903 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Tue, 11 Oct 2022 13:14:01 +0300 Subject: [PATCH 0103/1710] build raylib_api without the 'vectex' tyops (#2749) --- parser/output/raylib_api.json | 196 +++++++++-- parser/output/raylib_api.lua | 118 +++++-- parser/output/raylib_api.txt | 627 ++++++++++++++++++---------------- parser/output/raylib_api.xml | 73 +++- 4 files changed, 660 insertions(+), 354 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 11288bf1b..d9a548128 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -9,7 +9,7 @@ { "name": "RAYLIB_VERSION", "type": "STRING", - "value": "4.2", + "value": "4.5-dev", "description": "" }, { @@ -729,7 +729,7 @@ { "type": "float", "name": "fovy", - "description": "Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic" + "description": "Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic" }, { "type": "int", @@ -905,7 +905,7 @@ }, { "name": "Transform", - "description": "Transform, vectex transformation data", + "description": "Transform, vertex transformation data", "fields": [ { "type": "Vector3", @@ -2051,7 +2051,7 @@ { "name": "MOUSE_BUTTON_FORWARD", "value": 5, - "description": "Mouse button fordward (advanced mouse device)" + "description": "Mouse button forward (advanced mouse device)" }, { "name": "MOUSE_BUTTON_BACK", @@ -2795,6 +2795,11 @@ "name": "BLEND_CUSTOM", "value": 6, "description": "Blend textures using custom src/dst factors (use rlSetBlendMode())" + }, + { + "name": "BLEND_CUSTOM_SEPARATE", + "value": 7, + "description": "Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendModeSeparate())" } ] }, @@ -3971,7 +3976,7 @@ "returnType": "void *", "params": [ { - "type": "int", + "type": "unsigned int", "name": "size" } ] @@ -3986,7 +3991,7 @@ "name": "ptr" }, { - "type": "int", + "type": "unsigned int", "name": "size" } ] @@ -5995,6 +6000,25 @@ } ] }, + { + "name": "CheckCollisionPointPoly", + "description": "Check if point is within a polygon described by array of vertices", + "returnType": "bool", + "params": [ + { + "type": "Vector2", + "name": "point" + }, + { + "type": "Vector2 *", + "name": "points" + }, + { + "type": "int", + "name": "pointCount" + } + ] + }, { "name": "CheckCollisionLines", "description": "Check the collision between two lines defined by two points each, returns collision point by reference", @@ -6331,6 +6355,33 @@ } ] }, + { + "name": "GenImagePerlinNoise", + "description": "Generate image: perlin noise", + "returnType": "Image", + "params": [ + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + }, + { + "type": "int", + "name": "offsetX" + }, + { + "type": "int", + "name": "offsetY" + }, + { + "type": "float", + "name": "scale" + } + ] + }, { "name": "GenImageCellular", "description": "Generate image: cellular algorithm, bigger tileSize means bigger cells", @@ -6963,7 +7014,7 @@ }, { "name": "ImageDrawCircle", - "description": "Draw circle within an image", + "description": "Draw a filled circle within an image", "returnType": "void", "params": [ { @@ -6990,7 +7041,57 @@ }, { "name": "ImageDrawCircleV", - "description": "Draw circle within an image (Vector version)", + "description": "Draw a filled circle within an image (Vector version)", + "returnType": "void", + "params": [ + { + "type": "Image *", + "name": "dst" + }, + { + "type": "Vector2", + "name": "center" + }, + { + "type": "int", + "name": "radius" + }, + { + "type": "Color", + "name": "color" + } + ] + }, + { + "name": "ImageDrawCircleLines", + "description": "Draw circle outline within an image", + "returnType": "void", + "params": [ + { + "type": "Image *", + "name": "dst" + }, + { + "type": "int", + "name": "centerX" + }, + { + "type": "int", + "name": "centerY" + }, + { + "type": "int", + "name": "radius" + }, + { + "type": "Color", + "name": "color" + } + ] + }, + { + "name": "ImageDrawCircleLinesV", + "description": "Draw circle outline within an image (Vector version)", "returnType": "void", "params": [ { @@ -8221,6 +8322,32 @@ } ] }, + { + "name": "LoadUTF8", + "description": "Load UTF-8 text encoded from codepoints array", + "returnType": "char *", + "params": [ + { + "type": "const int *", + "name": "codepoints" + }, + { + "type": "int", + "name": "length" + } + ] + }, + { + "name": "UnloadUTF8", + "description": "Unload UTF-8 text encoded from codepoints array", + "returnType": "void", + "params": [ + { + "type": "char *", + "name": "text" + } + ] + }, { "name": "LoadCodepoints", "description": "Load all codepoints from a UTF-8 text string, codepoints count returned by parameter", @@ -8269,7 +8396,37 @@ }, { "type": "int *", - "name": "bytesProcessed" + "name": "codepointSize" + } + ] + }, + { + "name": "GetCodepointNext", + "description": "Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure", + "returnType": "int", + "params": [ + { + "type": "const char *", + "name": "text" + }, + { + "type": "int *", + "name": "codepointSize" + } + ] + }, + { + "name": "GetCodepointPrevious", + "description": "Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure", + "returnType": "int", + "params": [ + { + "type": "const char *", + "name": "text" + }, + { + "type": "int *", + "name": "codepointSize" } ] }, @@ -8284,22 +8441,7 @@ }, { "type": "int *", - "name": "byteSize" - } - ] - }, - { - "name": "TextCodepointsToUTF8", - "description": "Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!)", - "returnType": "char *", - "params": [ - { - "type": "const int *", - "name": "codepoints" - }, - { - "type": "int", - "name": "length" + "name": "utf8Size" } ] }, @@ -10685,7 +10827,7 @@ }, { "name": "AttachAudioStreamProcessor", - "description": "", + "description": "Attach audio stream processor to stream", "returnType": "void", "params": [ { @@ -10700,7 +10842,7 @@ }, { "name": "DetachAudioStreamProcessor", - "description": "", + "description": "Detach audio stream processor from stream", "returnType": "void", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 1d80216de..a7b68f1c3 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -9,7 +9,7 @@ return { { name = "RAYLIB_VERSION", type = "STRING", - value = "4.2", + value = "4.5-dev", description = "" }, { @@ -729,7 +729,7 @@ return { { type = "float", name = "fovy", - description = "Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic" + description = "Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic" }, { type = "int", @@ -905,7 +905,7 @@ return { }, { name = "Transform", - description = "Transform, vectex transformation data", + description = "Transform, vertex transformation data", fields = { { type = "Vector3", @@ -2051,7 +2051,7 @@ return { { name = "MOUSE_BUTTON_FORWARD", value = 5, - description = "Mouse button fordward (advanced mouse device)" + description = "Mouse button forward (advanced mouse device)" }, { name = "MOUSE_BUTTON_BACK", @@ -2795,6 +2795,11 @@ return { name = "BLEND_CUSTOM", value = 6, description = "Blend textures using custom src/dst factors (use rlSetBlendMode())" + }, + { + name = "BLEND_CUSTOM_SEPARATE", + value = 7, + description = "Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendModeSeparate())" } } }, @@ -3673,7 +3678,7 @@ return { description = "Internal memory allocator", returnType = "void *", params = { - {type = "int", name = "size"} + {type = "unsigned int", name = "size"} } }, { @@ -3682,7 +3687,7 @@ return { returnType = "void *", params = { {type = "void *", name = "ptr"}, - {type = "int", name = "size"} + {type = "unsigned int", name = "size"} } }, { @@ -4846,6 +4851,16 @@ return { {type = "Vector2", name = "p3"} } }, + { + name = "CheckCollisionPointPoly", + description = "Check if point is within a polygon described by array of vertices", + returnType = "bool", + params = { + {type = "Vector2", name = "point"}, + {type = "Vector2 *", name = "points"}, + {type = "int", name = "pointCount"} + } + }, { name = "CheckCollisionLines", description = "Check the collision between two lines defined by two points each, returns collision point by reference", @@ -5023,6 +5038,18 @@ return { {type = "float", name = "factor"} } }, + { + name = "GenImagePerlinNoise", + description = "Generate image: perlin noise", + returnType = "Image", + params = { + {type = "int", name = "width"}, + {type = "int", name = "height"}, + {type = "int", name = "offsetX"}, + {type = "int", name = "offsetY"}, + {type = "float", name = "scale"} + } + }, { name = "GenImageCellular", description = "Generate image: cellular algorithm, bigger tileSize means bigger cells", @@ -5382,7 +5409,7 @@ return { }, { name = "ImageDrawCircle", - description = "Draw circle within an image", + description = "Draw a filled circle within an image", returnType = "void", params = { {type = "Image *", name = "dst"}, @@ -5394,7 +5421,30 @@ return { }, { name = "ImageDrawCircleV", - description = "Draw circle within an image (Vector version)", + description = "Draw a filled circle within an image (Vector version)", + returnType = "void", + params = { + {type = "Image *", name = "dst"}, + {type = "Vector2", name = "center"}, + {type = "int", name = "radius"}, + {type = "Color", name = "color"} + } + }, + { + name = "ImageDrawCircleLines", + description = "Draw circle outline within an image", + returnType = "void", + params = { + {type = "Image *", name = "dst"}, + {type = "int", name = "centerX"}, + {type = "int", name = "centerY"}, + {type = "int", name = "radius"}, + {type = "Color", name = "color"} + } + }, + { + name = "ImageDrawCircleLinesV", + description = "Draw circle outline within an image (Vector version)", returnType = "void", params = { {type = "Image *", name = "dst"}, @@ -6019,6 +6069,23 @@ return { {type = "int", name = "codepoint"} } }, + { + name = "LoadUTF8", + description = "Load UTF-8 text encoded from codepoints array", + returnType = "char *", + params = { + {type = "const int *", name = "codepoints"}, + {type = "int", name = "length"} + } + }, + { + name = "UnloadUTF8", + description = "Unload UTF-8 text encoded from codepoints array", + returnType = "void", + params = { + {type = "char *", name = "text"} + } + }, { name = "LoadCodepoints", description = "Load all codepoints from a UTF-8 text string, codepoints count returned by parameter", @@ -6050,7 +6117,25 @@ return { returnType = "int", params = { {type = "const char *", name = "text"}, - {type = "int *", name = "bytesProcessed"} + {type = "int *", name = "codepointSize"} + } + }, + { + name = "GetCodepointNext", + description = "Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure", + returnType = "int", + params = { + {type = "const char *", name = "text"}, + {type = "int *", name = "codepointSize"} + } + }, + { + name = "GetCodepointPrevious", + description = "Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure", + returnType = "int", + params = { + {type = "const char *", name = "text"}, + {type = "int *", name = "codepointSize"} } }, { @@ -6059,16 +6144,7 @@ return { returnType = "const char *", params = { {type = "int", name = "codepoint"}, - {type = "int *", name = "byteSize"} - } - }, - { - name = "TextCodepointsToUTF8", - description = "Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!)", - returnType = "char *", - params = { - {type = "const int *", name = "codepoints"}, - {type = "int", name = "length"} + {type = "int *", name = "utf8Size"} } }, { @@ -7415,7 +7491,7 @@ return { }, { name = "AttachAudioStreamProcessor", - description = "", + description = "Attach audio stream processor to stream", returnType = "void", params = { {type = "AudioStream", name = "stream"}, @@ -7424,7 +7500,7 @@ return { }, { name = "DetachAudioStreamProcessor", - description = "", + description = "Detach audio stream processor from stream", returnType = "void", params = { {type = "AudioStream", name = "stream"}, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index d45d8ce63..c53e292d0 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -9,7 +9,7 @@ Define 001: RAYLIB_H Define 002: RAYLIB_VERSION Name: RAYLIB_VERSION Type: STRING - Value: "4.2" + Value: "4.5-dev" Description: Define 003: __declspec(x) Name: __declspec(x) @@ -374,7 +374,7 @@ Struct 13: Camera3D (5 fields) Field[1]: Vector3 position // Camera position Field[2]: Vector3 target // Camera target it looks-at Field[3]: Vector3 up // Camera up vector (rotation over its axis) - Field[4]: float fovy // Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic + Field[4]: float fovy // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic Field[5]: int projection // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC Struct 14: Camera2D (4 fields) Name: Camera2D @@ -420,7 +420,7 @@ Struct 18: Material (3 fields) Field[3]: float[4] params // Material generic parameters (if required) Struct 19: Transform (3 fields) Name: Transform - Description: Transform, vectex transformation data + Description: Transform, vertex transformation data Field[1]: Vector3 translation // Translation Field[2]: Quaternion rotation // Rotation Field[3]: Vector3 scale // Scale @@ -863,7 +863,7 @@ Enum 16: FontType (3 values) Value[FONT_DEFAULT]: 0 Value[FONT_BITMAP]: 1 Value[FONT_SDF]: 2 -Enum 17: BlendMode (7 values) +Enum 17: BlendMode (8 values) Name: BlendMode Description: Color blending modes (pre-defined) Value[BLEND_ALPHA]: 0 @@ -873,6 +873,7 @@ Enum 17: BlendMode (7 values) Value[BLEND_SUBTRACT_COLORS]: 4 Value[BLEND_ALPHA_PREMULTIPLY]: 5 Value[BLEND_CUSTOM]: 6 + Value[BLEND_CUSTOM_SEPARATE]: 7 Enum 18: Gesture (11 values) Name: Gesture Description: Gesture @@ -947,7 +948,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 502 +Functions found: 509 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -1476,13 +1477,13 @@ Function 099: MemAlloc() (1 input parameters) Name: MemAlloc Return type: void * Description: Internal memory allocator - Param[1]: size (type: int) + Param[1]: size (type: unsigned int) Function 100: MemRealloc() (2 input parameters) Name: MemRealloc Return type: void * Description: Internal memory reallocator Param[1]: ptr (type: void *) - Param[2]: size (type: int) + Param[2]: size (type: unsigned int) Function 101: MemFree() (1 input parameters) Name: MemFree Return type: void @@ -2314,7 +2315,14 @@ Function 232: CheckCollisionPointTriangle() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: p3 (type: Vector2) -Function 233: CheckCollisionLines() (5 input parameters) +Function 233: CheckCollisionPointPoly() (3 input parameters) + Name: CheckCollisionPointPoly + Return type: bool + Description: Check if point is within a polygon described by array of vertices + Param[1]: point (type: Vector2) + Param[2]: points (type: Vector2 *) + Param[3]: pointCount (type: int) +Function 234: CheckCollisionLines() (5 input parameters) Name: CheckCollisionLines Return type: bool Description: Check the collision between two lines defined by two points each, returns collision point by reference @@ -2323,7 +2331,7 @@ Function 233: CheckCollisionLines() (5 input parameters) Param[3]: startPos2 (type: Vector2) Param[4]: endPos2 (type: Vector2) Param[5]: collisionPoint (type: Vector2 *) -Function 234: CheckCollisionPointLine() (4 input parameters) +Function 235: CheckCollisionPointLine() (4 input parameters) Name: CheckCollisionPointLine Return type: bool Description: Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] @@ -2331,18 +2339,18 @@ Function 234: CheckCollisionPointLine() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: threshold (type: int) -Function 235: GetCollisionRec() (2 input parameters) +Function 236: GetCollisionRec() (2 input parameters) Name: GetCollisionRec Return type: Rectangle Description: Get collision rectangle for two rectangles collision Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 236: LoadImage() (1 input parameters) +Function 237: LoadImage() (1 input parameters) Name: LoadImage Return type: Image Description: Load image from file into CPU memory (RAM) Param[1]: fileName (type: const char *) -Function 237: LoadImageRaw() (5 input parameters) +Function 238: LoadImageRaw() (5 input parameters) Name: LoadImageRaw Return type: Image Description: Load image from RAW file data @@ -2351,54 +2359,54 @@ Function 237: LoadImageRaw() (5 input parameters) Param[3]: height (type: int) Param[4]: format (type: int) Param[5]: headerSize (type: int) -Function 238: LoadImageAnim() (2 input parameters) +Function 239: LoadImageAnim() (2 input parameters) Name: LoadImageAnim Return type: Image Description: Load image sequence from file (frames appended to image.data) Param[1]: fileName (type: const char *) Param[2]: frames (type: int *) -Function 239: LoadImageFromMemory() (3 input parameters) +Function 240: LoadImageFromMemory() (3 input parameters) Name: LoadImageFromMemory Return type: Image Description: Load image from memory buffer, fileType refers to extension: i.e. '.png' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 240: LoadImageFromTexture() (1 input parameters) +Function 241: LoadImageFromTexture() (1 input parameters) Name: LoadImageFromTexture Return type: Image Description: Load image from GPU texture data Param[1]: texture (type: Texture2D) -Function 241: LoadImageFromScreen() (0 input parameters) +Function 242: LoadImageFromScreen() (0 input parameters) Name: LoadImageFromScreen Return type: Image Description: Load image from screen buffer and (screenshot) No input parameters -Function 242: UnloadImage() (1 input parameters) +Function 243: UnloadImage() (1 input parameters) Name: UnloadImage Return type: void Description: Unload image from CPU memory (RAM) Param[1]: image (type: Image) -Function 243: ExportImage() (2 input parameters) +Function 244: ExportImage() (2 input parameters) Name: ExportImage Return type: bool Description: Export image data to file, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 244: ExportImageAsCode() (2 input parameters) +Function 245: ExportImageAsCode() (2 input parameters) Name: ExportImageAsCode Return type: bool Description: Export image as code file defining an array of bytes, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 245: GenImageColor() (3 input parameters) +Function 246: GenImageColor() (3 input parameters) Name: GenImageColor Return type: Image Description: Generate image: plain color Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 246: GenImageGradientV() (4 input parameters) +Function 247: GenImageGradientV() (4 input parameters) Name: GenImageGradientV Return type: Image Description: Generate image: vertical gradient @@ -2406,7 +2414,7 @@ Function 246: GenImageGradientV() (4 input parameters) Param[2]: height (type: int) Param[3]: top (type: Color) Param[4]: bottom (type: Color) -Function 247: GenImageGradientH() (4 input parameters) +Function 248: GenImageGradientH() (4 input parameters) Name: GenImageGradientH Return type: Image Description: Generate image: horizontal gradient @@ -2414,7 +2422,7 @@ Function 247: GenImageGradientH() (4 input parameters) Param[2]: height (type: int) Param[3]: left (type: Color) Param[4]: right (type: Color) -Function 248: GenImageGradientRadial() (5 input parameters) +Function 249: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2423,7 +2431,7 @@ Function 248: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 249: GenImageChecked() (6 input parameters) +Function 250: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2433,39 +2441,48 @@ Function 249: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 250: GenImageWhiteNoise() (3 input parameters) +Function 251: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 251: GenImageCellular() (3 input parameters) +Function 252: GenImagePerlinNoise() (5 input parameters) + Name: GenImagePerlinNoise + Return type: Image + Description: Generate image: perlin noise + Param[1]: width (type: int) + Param[2]: height (type: int) + Param[3]: offsetX (type: int) + Param[4]: offsetY (type: int) + Param[5]: scale (type: float) +Function 253: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 252: ImageCopy() (1 input parameters) +Function 254: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 253: ImageFromImage() (2 input parameters) +Function 255: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 254: ImageText() (3 input parameters) +Function 256: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 255: ImageTextEx() (5 input parameters) +Function 257: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2474,63 +2491,63 @@ Function 255: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 256: ImageFormat() (2 input parameters) +Function 258: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 257: ImageToPOT() (2 input parameters) +Function 259: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 258: ImageCrop() (2 input parameters) +Function 260: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 259: ImageAlphaCrop() (2 input parameters) +Function 261: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 260: ImageAlphaClear() (3 input parameters) +Function 262: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 261: ImageAlphaMask() (2 input parameters) +Function 263: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 262: ImageAlphaPremultiply() (1 input parameters) +Function 264: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 263: ImageResize() (3 input parameters) +Function 265: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 264: ImageResizeNN() (3 input parameters) +Function 266: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 265: ImageResizeCanvas() (6 input parameters) +Function 267: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2540,12 +2557,12 @@ Function 265: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 266: ImageMipmaps() (1 input parameters) +Function 268: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 267: ImageDither() (5 input parameters) +Function 269: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2554,103 +2571,103 @@ Function 267: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 268: ImageFlipVertical() (1 input parameters) +Function 270: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 269: ImageFlipHorizontal() (1 input parameters) +Function 271: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 270: ImageRotateCW() (1 input parameters) +Function 272: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 271: ImageRotateCCW() (1 input parameters) +Function 273: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 272: ImageColorTint() (2 input parameters) +Function 274: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 273: ImageColorInvert() (1 input parameters) +Function 275: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 274: ImageColorGrayscale() (1 input parameters) +Function 276: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 275: ImageColorContrast() (2 input parameters) +Function 277: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 276: ImageColorBrightness() (2 input parameters) +Function 278: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 277: ImageColorReplace() (3 input parameters) +Function 279: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 278: LoadImageColors() (1 input parameters) +Function 280: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 279: LoadImagePalette() (3 input parameters) +Function 281: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 280: UnloadImageColors() (1 input parameters) +Function 282: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 281: UnloadImagePalette() (1 input parameters) +Function 283: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 282: GetImageAlphaBorder() (2 input parameters) +Function 284: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 283: GetImageColor() (3 input parameters) +Function 285: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 284: ImageClearBackground() (2 input parameters) +Function 286: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 285: ImageDrawPixel() (4 input parameters) +Function 287: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2658,14 +2675,14 @@ Function 285: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 286: ImageDrawPixelV() (3 input parameters) +Function 288: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 287: ImageDrawLine() (6 input parameters) +Function 289: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2675,7 +2692,7 @@ Function 287: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 288: ImageDrawLineV() (4 input parameters) +Function 290: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2683,24 +2700,41 @@ Function 288: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 289: ImageDrawCircle() (5 input parameters) +Function 291: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void - Description: Draw circle within an image + Description: Draw a filled circle within an image Param[1]: dst (type: Image *) Param[2]: centerX (type: int) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 290: ImageDrawCircleV() (4 input parameters) +Function 292: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void - Description: Draw circle within an image (Vector version) + Description: Draw a filled circle within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 291: ImageDrawRectangle() (6 input parameters) +Function 293: ImageDrawCircleLines() (5 input parameters) + Name: ImageDrawCircleLines + Return type: void + Description: Draw circle outline within an image + Param[1]: dst (type: Image *) + Param[2]: centerX (type: int) + Param[3]: centerY (type: int) + Param[4]: radius (type: int) + Param[5]: color (type: Color) +Function 294: ImageDrawCircleLinesV() (4 input parameters) + Name: ImageDrawCircleLinesV + Return type: void + Description: Draw circle outline within an image (Vector version) + Param[1]: dst (type: Image *) + Param[2]: center (type: Vector2) + Param[3]: radius (type: int) + Param[4]: color (type: Color) +Function 295: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2710,7 +2744,7 @@ Function 291: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 292: ImageDrawRectangleV() (4 input parameters) +Function 296: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2718,14 +2752,14 @@ Function 292: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 293: ImageDrawRectangleRec() (3 input parameters) +Function 297: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 294: ImageDrawRectangleLines() (4 input parameters) +Function 298: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2733,7 +2767,7 @@ Function 294: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 295: ImageDraw() (5 input parameters) +Function 299: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2742,7 +2776,7 @@ Function 295: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 296: ImageDrawText() (6 input parameters) +Function 300: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2752,7 +2786,7 @@ Function 296: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 297: ImageDrawTextEx() (7 input parameters) +Function 301: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2763,69 +2797,69 @@ Function 297: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 298: LoadTexture() (1 input parameters) +Function 302: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 299: LoadTextureFromImage() (1 input parameters) +Function 303: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 300: LoadTextureCubemap() (2 input parameters) +Function 304: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 301: LoadRenderTexture() (2 input parameters) +Function 305: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 302: UnloadTexture() (1 input parameters) +Function 306: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 303: UnloadRenderTexture() (1 input parameters) +Function 307: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 304: UpdateTexture() (2 input parameters) +Function 308: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 305: UpdateTextureRec() (3 input parameters) +Function 309: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 306: GenTextureMipmaps() (1 input parameters) +Function 310: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 307: SetTextureFilter() (2 input parameters) +Function 311: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 308: SetTextureWrap() (2 input parameters) +Function 312: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 309: DrawTexture() (4 input parameters) +Function 313: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2833,14 +2867,14 @@ Function 309: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 310: DrawTextureV() (3 input parameters) +Function 314: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 311: DrawTextureEx() (5 input parameters) +Function 315: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2849,7 +2883,7 @@ Function 311: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 312: DrawTextureRec() (4 input parameters) +Function 316: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2857,7 +2891,7 @@ Function 312: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 313: DrawTextureQuad() (5 input parameters) +Function 317: DrawTextureQuad() (5 input parameters) Name: DrawTextureQuad Return type: void Description: Draw texture quad with tiling and offset parameters @@ -2866,7 +2900,7 @@ Function 313: DrawTextureQuad() (5 input parameters) Param[3]: offset (type: Vector2) Param[4]: quad (type: Rectangle) Param[5]: tint (type: Color) -Function 314: DrawTextureTiled() (7 input parameters) +Function 318: DrawTextureTiled() (7 input parameters) Name: DrawTextureTiled Return type: void Description: Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. @@ -2877,7 +2911,7 @@ Function 314: DrawTextureTiled() (7 input parameters) Param[5]: rotation (type: float) Param[6]: scale (type: float) Param[7]: tint (type: Color) -Function 315: DrawTexturePro() (6 input parameters) +Function 319: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2887,7 +2921,7 @@ Function 315: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 316: DrawTextureNPatch() (6 input parameters) +Function 320: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2897,7 +2931,7 @@ Function 316: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 317: DrawTexturePoly() (6 input parameters) +Function 321: DrawTexturePoly() (6 input parameters) Name: DrawTexturePoly Return type: void Description: Draw a textured polygon @@ -2907,88 +2941,88 @@ Function 317: DrawTexturePoly() (6 input parameters) Param[4]: texcoords (type: Vector2 *) Param[5]: pointCount (type: int) Param[6]: tint (type: Color) -Function 318: Fade() (2 input parameters) +Function 322: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 319: ColorToInt() (1 input parameters) +Function 323: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 320: ColorNormalize() (1 input parameters) +Function 324: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 321: ColorFromNormalized() (1 input parameters) +Function 325: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 322: ColorToHSV() (1 input parameters) +Function 326: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 323: ColorFromHSV() (3 input parameters) +Function 327: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 324: ColorAlpha() (2 input parameters) +Function 328: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 325: ColorAlphaBlend() (3 input parameters) +Function 329: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 326: GetColor() (1 input parameters) +Function 330: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 327: GetPixelColor() (2 input parameters) +Function 331: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 328: SetPixelColor() (3 input parameters) +Function 332: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 329: GetPixelDataSize() (3 input parameters) +Function 333: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 330: GetFontDefault() (0 input parameters) +Function 334: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 331: LoadFont() (1 input parameters) +Function 335: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 332: LoadFontEx() (4 input parameters) +Function 336: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set @@ -2996,14 +3030,14 @@ Function 332: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: fontChars (type: int *) Param[4]: glyphCount (type: int) -Function 333: LoadFontFromImage() (3 input parameters) +Function 337: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 334: LoadFontFromMemory() (6 input parameters) +Function 338: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3013,7 +3047,7 @@ Function 334: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: fontChars (type: int *) Param[6]: glyphCount (type: int) -Function 335: LoadFontData() (6 input parameters) +Function 339: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3023,7 +3057,7 @@ Function 335: LoadFontData() (6 input parameters) Param[4]: fontChars (type: int *) Param[5]: glyphCount (type: int) Param[6]: type (type: int) -Function 336: GenImageFontAtlas() (6 input parameters) +Function 340: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3033,30 +3067,30 @@ Function 336: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 337: UnloadFontData() (2 input parameters) +Function 341: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: chars (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 338: UnloadFont() (1 input parameters) +Function 342: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 339: ExportFontAsCode() (2 input parameters) +Function 343: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 340: DrawFPS() (2 input parameters) +Function 344: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 341: DrawText() (5 input parameters) +Function 345: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3065,7 +3099,7 @@ Function 341: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 342: DrawTextEx() (6 input parameters) +Function 346: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3075,7 +3109,7 @@ Function 342: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 343: DrawTextPro() (8 input parameters) +Function 347: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3087,7 +3121,7 @@ Function 343: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 344: DrawTextCodepoint() (5 input parameters) +Function 348: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3096,7 +3130,7 @@ Function 344: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 345: DrawTextCodepoints() (7 input parameters) +Function 349: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3107,13 +3141,13 @@ Function 345: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 346: MeasureText() (2 input parameters) +Function 350: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 347: MeasureTextEx() (4 input parameters) +Function 351: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3121,163 +3155,180 @@ Function 347: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 348: GetGlyphIndex() (2 input parameters) +Function 352: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 349: GetGlyphInfo() (2 input parameters) +Function 353: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 350: GetGlyphAtlasRec() (2 input parameters) +Function 354: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 351: LoadCodepoints() (2 input parameters) +Function 355: LoadUTF8() (2 input parameters) + Name: LoadUTF8 + Return type: char * + Description: Load UTF-8 text encoded from codepoints array + Param[1]: codepoints (type: const int *) + Param[2]: length (type: int) +Function 356: UnloadUTF8() (1 input parameters) + Name: UnloadUTF8 + Return type: void + Description: Unload UTF-8 text encoded from codepoints array + Param[1]: text (type: char *) +Function 357: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 352: UnloadCodepoints() (1 input parameters) +Function 358: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 353: GetCodepointCount() (1 input parameters) +Function 359: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 354: GetCodepoint() (2 input parameters) +Function 360: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) - Param[2]: bytesProcessed (type: int *) -Function 355: CodepointToUTF8() (2 input parameters) + Param[2]: codepointSize (type: int *) +Function 361: GetCodepointNext() (2 input parameters) + Name: GetCodepointNext + Return type: int + Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure + Param[1]: text (type: const char *) + Param[2]: codepointSize (type: int *) +Function 362: GetCodepointPrevious() (2 input parameters) + Name: GetCodepointPrevious + Return type: int + Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure + Param[1]: text (type: const char *) + Param[2]: codepointSize (type: int *) +Function 363: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) - Param[2]: byteSize (type: int *) -Function 356: TextCodepointsToUTF8() (2 input parameters) - Name: TextCodepointsToUTF8 - Return type: char * - Description: Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!) - Param[1]: codepoints (type: const int *) - Param[2]: length (type: int) -Function 357: TextCopy() (2 input parameters) + Param[2]: utf8Size (type: int *) +Function 364: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 358: TextIsEqual() (2 input parameters) +Function 365: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 359: TextLength() (1 input parameters) +Function 366: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 360: TextFormat() (2 input parameters) +Function 367: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 361: TextSubtext() (3 input parameters) +Function 368: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 362: TextReplace() (3 input parameters) +Function 369: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 363: TextInsert() (3 input parameters) +Function 370: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 364: TextJoin() (3 input parameters) +Function 371: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 365: TextSplit() (3 input parameters) +Function 372: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 366: TextAppend() (3 input parameters) +Function 373: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 367: TextFindIndex() (2 input parameters) +Function 374: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 368: TextToUpper() (1 input parameters) +Function 375: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 369: TextToLower() (1 input parameters) +Function 376: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 370: TextToPascal() (1 input parameters) +Function 377: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 371: TextToInteger() (1 input parameters) +Function 378: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 372: DrawLine3D() (3 input parameters) +Function 379: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 373: DrawPoint3D() (2 input parameters) +Function 380: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 374: DrawCircle3D() (5 input parameters) +Function 381: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3286,7 +3337,7 @@ Function 374: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 375: DrawTriangle3D() (4 input parameters) +Function 382: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3294,14 +3345,14 @@ Function 375: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 376: DrawTriangleStrip3D() (3 input parameters) +Function 383: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 377: DrawCube() (5 input parameters) +Function 384: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3310,14 +3361,14 @@ Function 377: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 378: DrawCubeV() (3 input parameters) +Function 385: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 379: DrawCubeWires() (5 input parameters) +Function 386: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3326,14 +3377,14 @@ Function 379: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 380: DrawCubeWiresV() (3 input parameters) +Function 387: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 381: DrawCubeTexture() (6 input parameters) +Function 388: DrawCubeTexture() (6 input parameters) Name: DrawCubeTexture Return type: void Description: Draw cube textured @@ -3343,7 +3394,7 @@ Function 381: DrawCubeTexture() (6 input parameters) Param[4]: height (type: float) Param[5]: length (type: float) Param[6]: color (type: Color) -Function 382: DrawCubeTextureRec() (7 input parameters) +Function 389: DrawCubeTextureRec() (7 input parameters) Name: DrawCubeTextureRec Return type: void Description: Draw cube with a region of a texture @@ -3354,14 +3405,14 @@ Function 382: DrawCubeTextureRec() (7 input parameters) Param[5]: height (type: float) Param[6]: length (type: float) Param[7]: color (type: Color) -Function 383: DrawSphere() (3 input parameters) +Function 390: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 384: DrawSphereEx() (5 input parameters) +Function 391: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3370,7 +3421,7 @@ Function 384: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 385: DrawSphereWires() (5 input parameters) +Function 392: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3379,7 +3430,7 @@ Function 385: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 386: DrawCylinder() (6 input parameters) +Function 393: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3389,7 +3440,7 @@ Function 386: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 387: DrawCylinderEx() (6 input parameters) +Function 394: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3399,7 +3450,7 @@ Function 387: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 388: DrawCylinderWires() (6 input parameters) +Function 395: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3409,7 +3460,7 @@ Function 388: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 389: DrawCylinderWiresEx() (6 input parameters) +Function 396: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3419,51 +3470,51 @@ Function 389: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 390: DrawPlane() (3 input parameters) +Function 397: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 391: DrawRay() (2 input parameters) +Function 398: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 392: DrawGrid() (2 input parameters) +Function 399: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 393: LoadModel() (1 input parameters) +Function 400: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 394: LoadModelFromMesh() (1 input parameters) +Function 401: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 395: UnloadModel() (1 input parameters) +Function 402: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 396: UnloadModelKeepMeshes() (1 input parameters) +Function 403: UnloadModelKeepMeshes() (1 input parameters) Name: UnloadModelKeepMeshes Return type: void Description: Unload model (but not meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 397: GetModelBoundingBox() (1 input parameters) +Function 404: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 398: DrawModel() (4 input parameters) +Function 405: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3471,7 +3522,7 @@ Function 398: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 399: DrawModelEx() (6 input parameters) +Function 406: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3481,7 +3532,7 @@ Function 399: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 400: DrawModelWires() (4 input parameters) +Function 407: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3489,7 +3540,7 @@ Function 400: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 401: DrawModelWiresEx() (6 input parameters) +Function 408: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3499,13 +3550,13 @@ Function 401: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 402: DrawBoundingBox() (2 input parameters) +Function 409: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 403: DrawBillboard() (5 input parameters) +Function 410: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3514,7 +3565,7 @@ Function 403: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 404: DrawBillboardRec() (6 input parameters) +Function 411: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3524,7 +3575,7 @@ Function 404: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 405: DrawBillboardPro() (9 input parameters) +Function 412: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3537,13 +3588,13 @@ Function 405: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 406: UploadMesh() (2 input parameters) +Function 413: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 407: UpdateMeshBuffer() (5 input parameters) +Function 414: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3552,19 +3603,19 @@ Function 407: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 408: UnloadMesh() (1 input parameters) +Function 415: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 409: DrawMesh() (3 input parameters) +Function 416: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 410: DrawMeshInstanced() (4 input parameters) +Function 417: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3572,29 +3623,29 @@ Function 410: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 411: ExportMesh() (2 input parameters) +Function 418: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 412: GetMeshBoundingBox() (1 input parameters) +Function 419: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 413: GenMeshTangents() (1 input parameters) +Function 420: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 414: GenMeshPoly() (2 input parameters) +Function 421: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 415: GenMeshPlane() (4 input parameters) +Function 422: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3602,42 +3653,42 @@ Function 415: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 416: GenMeshCube() (3 input parameters) +Function 423: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 417: GenMeshSphere() (3 input parameters) +Function 424: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 418: GenMeshHemiSphere() (3 input parameters) +Function 425: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 419: GenMeshCylinder() (3 input parameters) +Function 426: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 420: GenMeshCone() (3 input parameters) +Function 427: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 421: GenMeshTorus() (4 input parameters) +Function 428: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3645,7 +3696,7 @@ Function 421: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 422: GenMeshKnot() (4 input parameters) +Function 429: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3653,79 +3704,79 @@ Function 422: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 423: GenMeshHeightmap() (2 input parameters) +Function 430: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 424: GenMeshCubicmap() (2 input parameters) +Function 431: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 425: LoadMaterials() (2 input parameters) +Function 432: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 426: LoadMaterialDefault() (0 input parameters) +Function 433: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 427: UnloadMaterial() (1 input parameters) +Function 434: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 428: SetMaterialTexture() (3 input parameters) +Function 435: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 429: SetModelMeshMaterial() (3 input parameters) +Function 436: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 430: LoadModelAnimations() (2 input parameters) +Function 437: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: unsigned int *) -Function 431: UpdateModelAnimation() (3 input parameters) +Function 438: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 432: UnloadModelAnimation() (1 input parameters) +Function 439: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 433: UnloadModelAnimations() (2 input parameters) +Function 440: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: count (type: unsigned int) -Function 434: IsModelAnimationValid() (2 input parameters) +Function 441: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 435: CheckCollisionSpheres() (4 input parameters) +Function 442: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3733,40 +3784,40 @@ Function 435: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 436: CheckCollisionBoxes() (2 input parameters) +Function 443: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 437: CheckCollisionBoxSphere() (3 input parameters) +Function 444: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 438: GetRayCollisionSphere() (3 input parameters) +Function 445: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 439: GetRayCollisionBox() (2 input parameters) +Function 446: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 440: GetRayCollisionMesh() (3 input parameters) +Function 447: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 441: GetRayCollisionTriangle() (4 input parameters) +Function 448: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3774,7 +3825,7 @@ Function 441: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 442: GetRayCollisionQuad() (5 input parameters) +Function 449: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3783,148 +3834,148 @@ Function 442: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 443: InitAudioDevice() (0 input parameters) +Function 450: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 444: CloseAudioDevice() (0 input parameters) +Function 451: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 445: IsAudioDeviceReady() (0 input parameters) +Function 452: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 446: SetMasterVolume() (1 input parameters) +Function 453: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 447: LoadWave() (1 input parameters) +Function 454: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 448: LoadWaveFromMemory() (3 input parameters) +Function 455: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 449: LoadSound() (1 input parameters) +Function 456: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 450: LoadSoundFromWave() (1 input parameters) +Function 457: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 451: UpdateSound() (3 input parameters) +Function 458: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 452: UnloadWave() (1 input parameters) +Function 459: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 453: UnloadSound() (1 input parameters) +Function 460: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 454: ExportWave() (2 input parameters) +Function 461: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 455: ExportWaveAsCode() (2 input parameters) +Function 462: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 456: PlaySound() (1 input parameters) +Function 463: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 457: StopSound() (1 input parameters) +Function 464: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 458: PauseSound() (1 input parameters) +Function 465: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 459: ResumeSound() (1 input parameters) +Function 466: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 460: PlaySoundMulti() (1 input parameters) +Function 467: PlaySoundMulti() (1 input parameters) Name: PlaySoundMulti Return type: void Description: Play a sound (using multichannel buffer pool) Param[1]: sound (type: Sound) -Function 461: StopSoundMulti() (0 input parameters) +Function 468: StopSoundMulti() (0 input parameters) Name: StopSoundMulti Return type: void Description: Stop any sound playing (using multichannel buffer pool) No input parameters -Function 462: GetSoundsPlaying() (0 input parameters) +Function 469: GetSoundsPlaying() (0 input parameters) Name: GetSoundsPlaying Return type: int Description: Get number of sounds playing in the multichannel No input parameters -Function 463: IsSoundPlaying() (1 input parameters) +Function 470: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 464: SetSoundVolume() (2 input parameters) +Function 471: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 465: SetSoundPitch() (2 input parameters) +Function 472: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 466: SetSoundPan() (2 input parameters) +Function 473: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 467: WaveCopy() (1 input parameters) +Function 474: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 468: WaveCrop() (3 input parameters) +Function 475: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 469: WaveFormat() (4 input parameters) +Function 476: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -3932,184 +3983,184 @@ Function 469: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 470: LoadWaveSamples() (1 input parameters) +Function 477: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 471: UnloadWaveSamples() (1 input parameters) +Function 478: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 472: LoadMusicStream() (1 input parameters) +Function 479: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 473: LoadMusicStreamFromMemory() (3 input parameters) +Function 480: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 474: UnloadMusicStream() (1 input parameters) +Function 481: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 475: PlayMusicStream() (1 input parameters) +Function 482: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 476: IsMusicStreamPlaying() (1 input parameters) +Function 483: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 477: UpdateMusicStream() (1 input parameters) +Function 484: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 478: StopMusicStream() (1 input parameters) +Function 485: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 479: PauseMusicStream() (1 input parameters) +Function 486: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 480: ResumeMusicStream() (1 input parameters) +Function 487: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 481: SeekMusicStream() (2 input parameters) +Function 488: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 482: SetMusicVolume() (2 input parameters) +Function 489: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 483: SetMusicPitch() (2 input parameters) +Function 490: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 484: SetMusicPan() (2 input parameters) +Function 491: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 485: GetMusicTimeLength() (1 input parameters) +Function 492: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 486: GetMusicTimePlayed() (1 input parameters) +Function 493: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 487: LoadAudioStream() (3 input parameters) +Function 494: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 488: UnloadAudioStream() (1 input parameters) +Function 495: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 489: UpdateAudioStream() (3 input parameters) +Function 496: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 490: IsAudioStreamProcessed() (1 input parameters) +Function 497: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 491: PlayAudioStream() (1 input parameters) +Function 498: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 492: PauseAudioStream() (1 input parameters) +Function 499: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 493: ResumeAudioStream() (1 input parameters) +Function 500: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 494: IsAudioStreamPlaying() (1 input parameters) +Function 501: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 495: StopAudioStream() (1 input parameters) +Function 502: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 496: SetAudioStreamVolume() (2 input parameters) +Function 503: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 497: SetAudioStreamPitch() (2 input parameters) +Function 504: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 498: SetAudioStreamPan() (2 input parameters) +Function 505: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 499: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 506: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 500: SetAudioStreamCallback() (2 input parameters) +Function 507: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 501: AttachAudioStreamProcessor() (2 input parameters) +Function 508: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void - Description: + Description: Attach audio stream processor to stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 502: DetachAudioStreamProcessor() (2 input parameters) +Function 509: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void - Description: + Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index bbd99e86e..a28cd779e 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -2,7 +2,7 @@ - + @@ -147,7 +147,7 @@ - + @@ -187,7 +187,7 @@ - + @@ -433,7 +433,7 @@ - + @@ -585,7 +585,7 @@ - + @@ -593,6 +593,7 @@ + @@ -651,7 +652,7 @@ - + @@ -935,11 +936,11 @@ - + - + @@ -1486,6 +1487,11 @@ + + + + + @@ -1575,6 +1581,13 @@ + + + + + + + @@ -1742,14 +1755,27 @@ - + - + + + + + + + + + + + + + + @@ -2073,6 +2099,13 @@ + + + + + + + @@ -2085,15 +2118,19 @@ - + + + + + + + + + - - - - - + @@ -2729,11 +2766,11 @@ - + - + From 4cca234f46fcdac4541ceb26b753ca5f73e8a819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szieberth=20=C3=81d=C3=A1m?= Date: Tue, 11 Oct 2022 12:14:40 +0200 Subject: [PATCH 0104/1710] avoid leading spaces in `text_rectangle_bounds` (#2746) --- examples/text/text_rectangle_bounds.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/text/text_rectangle_bounds.c b/examples/text/text_rectangle_bounds.c index d2c992dfd..9ae2ba938 100644 --- a/examples/text/text_rectangle_bounds.c +++ b/examples/text/text_rectangle_bounds.c @@ -263,6 +263,6 @@ static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, } } - textOffsetX += glyphWidth; + if ((textOffsetX != 0) || (codepoint != ' ')) textOffsetX += glyphWidth; // avoid leading spaces } -} \ No newline at end of file +} From 8ebe62b4dd89cf3f6228d6a5c339c35b0ac25c31 Mon Sep 17 00:00:00 2001 From: hkc Date: Tue, 11 Oct 2022 19:45:34 +0300 Subject: [PATCH 0105/1710] Use RL_QUADS/RL_TRIANGLES for single-pixel drawing (#2750) Addresses problem mentioned in https://github.com/raysan5/raylib/issues/2744#issuecomment-1273568263 (in short: when drawing pixels using DrawPixel{,V} in camera mode, upscaled pixel becomes a line instead of bigger pixel) --- src/rshapes.c | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index f8d9db71f..7e4cad74b 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -104,21 +104,50 @@ void SetShapesTexture(Texture2D texture, Rectangle source) // Draw a pixel void DrawPixel(int posX, int posY, Color color) { - rlBegin(RL_LINES); - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(posX, posY); - rlVertex2f(posX + 1, posY + 1); - rlEnd(); + DrawPixelV((Vector2){ posX, posY }, color); } // Draw a pixel (Vector version) void DrawPixelV(Vector2 position, Color color) { - rlBegin(RL_LINES); +#if defined(SUPPORT_QUADS_DRAW_MODE) + rlSetTexture(texShapes.id); + + rlBegin(RL_QUADS); + + rlNormal3f(0.0f, 0.0f, 1.0f); rlColor4ub(color.r, color.g, color.b, color.a); + + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(position.x, position.y); - rlVertex2f(position.x + 1.0f, position.y + 1.0f); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(position.x, position.y + 1); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(position.x + 1, position.y + 1); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(position.x + 1, position.y); + rlEnd(); + + rlSetTexture(0); +#else + rlBegin(RL_TRIANGLES); + + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(position.x, position.y); + rlVertex2f(position.x, position.y + 1); + rlVertex2f(position.x + 1, position.y); + + rlVertex2f(position.x + 1, position.y); + rlVertex2f(position.x, position.y + 1); + rlVertex2f(position.x + 1, position.y + 1); + + rlEnd(); +#endif } // Draw a line From 07bbfe86b9cb750d224bc185a9ddcefba6d8bedb Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 11 Oct 2022 22:28:40 +0200 Subject: [PATCH 0106/1710] Update core_basic_window.c --- examples/core/core_basic_window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_basic_window.c b/examples/core/core_basic_window.c index 25937e4c3..be7a449ab 100644 --- a/examples/core/core_basic_window.c +++ b/examples/core/core_basic_window.c @@ -13,7 +13,7 @@ * Enjoy using raylib. :) * * 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 * From f080367a0ce1f9f420a7a28b48ed2d01a6cb3827 Mon Sep 17 00:00:00 2001 From: hartmannathan <59230071+hartmannathan@users.noreply.github.com> Date: Wed, 12 Oct 2022 11:12:28 -0400 Subject: [PATCH 0107/1710] examples/core/core_custom_logging.c: Fix typo (#2751) From aa67f7c39a77b195ffdbd0b9ba9291e65ccf7131 Mon Sep 17 00:00:00 2001 From: CrezyDud <84625713+CrezyDud@users.noreply.github.com> Date: Wed, 12 Oct 2022 17:14:18 +0200 Subject: [PATCH 0108/1710] Fix & Simplify .vox signature check (#2752) and make version check be only 150 not over 150 --- src/external/vox_loader.h | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/external/vox_loader.h b/src/external/vox_loader.h index a7a161c7f..c1b26e6f3 100644 --- a/src/external/vox_loader.h +++ b/src/external/vox_loader.h @@ -67,7 +67,7 @@ revision history: #define VOX_SUCCESS (0) #define VOX_ERROR_FILE_NOT_FOUND (-1) #define VOX_ERROR_INVALID_FORMAT (-2) -#define VOX_ERROR_FILE_VERSION_TOO_OLD (-3) +#define VOX_ERROR_FILE_VERSION_NOT_MATCH (-3) // VoxColor, 4 components, R8G8B8A8 (32bit) typedef struct { @@ -538,31 +538,26 @@ int Vox_LoadFromMemory(unsigned char* pvoxData, unsigned int voxDataSize, VoxArr // @raysan5: Reviewed (unsigned long) -> (unsigned int), possible issue with Ubuntu 18.04 64bit // @raysan5: reviewed signature loading - unsigned char signature[4] = { 0 }; unsigned char* fileData = pvoxData; unsigned char* fileDataPtr = fileData; unsigned char* endfileDataPtr = fileData + voxDataSize; - signature[0] = fileDataPtr[0]; - signature[1] = fileDataPtr[1]; - signature[2] = fileDataPtr[2]; - signature[3] = fileDataPtr[3]; - fileDataPtr += 4; - - if ((signature[0] != 'V') && (signature[0] != 'O') && (signature[0] != 'X') && (signature[0] != ' ')) + if (strncmp((char*)fileDataPtr, "VOX ", 4) != 0) { return VOX_ERROR_INVALID_FORMAT; //"Not an MagicaVoxel File format" } + fileDataPtr += 4; + // @raysan5: reviewed version loading unsigned int version = 0; version = ((unsigned int*)fileDataPtr)[0]; fileDataPtr += 4; - if (version < 150) + if (version != 150) { - return VOX_ERROR_FILE_VERSION_TOO_OLD; //"MagicaVoxel version too old" + return VOX_ERROR_FILE_VERSION_NOT_MATCH; //"MagicaVoxel version doesn't match" } From 11fd883ee443d3ea478e750d0116611d72d5708d Mon Sep 17 00:00:00 2001 From: Anut-py <72886192+Anut-py@users.noreply.github.com> Date: Wed, 12 Oct 2022 16:04:11 -0400 Subject: [PATCH 0109/1710] Add Haskell bindings to BINDINGS.md (#2753) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 86316aa3c..a783194c0 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -23,6 +23,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | +| h-raylib | 4.5-dev | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | | raylib-hx | 4.0 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | From ccd4f8b5ae7e9c40cb9ad4b1ef8a8f5c1ab458d9 Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Thu, 13 Oct 2022 22:00:53 +0100 Subject: [PATCH 0110/1710] Add C3 binding to BINDINGS.md (#2757) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index a783194c0..85e5f9295 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -12,6 +12,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | **4.0** | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | | raylib-cr | **4.0** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | +| raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | **4.0** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/xdrie/dray | From e61639f6fc46856ee3e66b28796a58ecb42278b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 14 Oct 2022 10:51:43 +0200 Subject: [PATCH 0111/1710] ADDED: `GenImageText()` Probably useless but interesting for education. It generated a grayscale image directly from text data. --- src/raylib.h | 1 + src/rtextures.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 4ec44b483..34b8cce4c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1244,6 +1244,7 @@ RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Col RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells +RLAPI Image GenImageText(int width, int height, const char *text); // Generate image: grayscale image from text data // Image manipulation functions RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) diff --git a/src/rtextures.c b/src/rtextures.c index ceca1a305..45a96a169 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -893,6 +893,25 @@ Image GenImageCellular(int width, int height, int tileSize) return image; } + +// Generate image: grayscale image from text data +Image GenImageText(int width, int height, const char *text) +{ + Image image = { 0 }; + + int textLength = TextLength(text); + int imageViewSize = width*height; + + image.width = width; + image.height = height; + image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + image.data = RL_CALLOC(imageViewSize, 1); + image.mipmaps = 1; + + memcpy(image.data, text, (textLength > imageViewSize)? imageViewSize : textLength); + + return image; +} #endif // SUPPORT_IMAGE_GENERATION //------------------------------------------------------------------------------------ From 0b69bc28c6dc671970b9cbe9dcf2bcdf1d8506d7 Mon Sep 17 00:00:00 2001 From: hkc Date: Fri, 14 Oct 2022 18:43:12 +0300 Subject: [PATCH 0112/1710] Fix ImageTextEx and ImageDrawTextEx scaling (#2756) * Use RL_QUADS/RL_TRIANGLES for single-pixel drawing Addresses problem mentioned in https://github.com/raysan5/raylib/issues/2744#issuecomment-1273568263 (in short: when drawing pixels using DrawPixel{,V} in camera mode, upscaled pixel becomes a line instead of bigger pixel) * [rtextures] Fixed scaling down in ImageTextEx Closes #2755 --- src/rtextures.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 45a96a169..569832ac2 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1248,6 +1248,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // NOTE: Text image is generated at font base size, later scaled to desired font size Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); // WARNING: Module required: rtext + Vector2 textSize = MeasureTextEx(font, text, fontSize, spacing); // Create image to store text imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); @@ -1286,9 +1287,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co } // Scale image depending on text size - if (fontSize > imSize.y) + if (textSize.y != imSize.y) { - float scaleFactor = fontSize/imSize.y; + float scaleFactor = textSize.y / imSize.y; TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor); // Using nearest-neighbor scaling algorithm for default font From c5e89241c515d12a739a0d0bacb6a05395bf2a95 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 15 Oct 2022 18:25:52 -0400 Subject: [PATCH 0113/1710] BINDINGS: Add Umka bindings to BINDINGS.md (#2760) [raylib-umka](https://github.com/robloach/raylib-umka) provides [Umka scripting language](https://github.com/vtereshkov/umka-lang) bindings to raylib. --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 85e5f9295..b2bced133 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -54,6 +54,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-swift | **4.0** | [Swift](https://swift.org/) | MIT | https://github.com/STREGAsGate/Raylib | | raylib-scopes | auto | [Scopes](http://scopes.rocks) | MIT | https://github.com/salotz/raylib-scopes | | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | +| raylib-umka | **4.2** | [Umka](https://github.com/vtereshkov/umka-lang) | Zlib | https://github.com/robloach/raylib-umka | | raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | | raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | From 7e7939e1ad1b9576dc518e0fadbf4a3c1eb989df Mon Sep 17 00:00:00 2001 From: ianband <32182222+IanBand@users.noreply.github.com> Date: Mon, 17 Oct 2022 02:36:53 -0700 Subject: [PATCH 0114/1710] Add DrawCapsule(Wires) (#2761) * Add DrawCapsule & DrawCapsuleWires * Add DrawCapsule & DrawCapsuleWires to example Co-authored-by: Ian Band --- examples/models/models_geometric_shapes.c | 3 + examples/models/models_geometric_shapes.png | Bin 33973 -> 22450 bytes src/raylib.h | 2 + src/rmodels.c | 278 ++++++++++++++++++++ 4 files changed, 283 insertions(+) diff --git a/examples/models/models_geometric_shapes.c b/examples/models/models_geometric_shapes.c index 294c88252..90261ad21 100644 --- a/examples/models/models_geometric_shapes.c +++ b/examples/models/models_geometric_shapes.c @@ -66,6 +66,9 @@ int main(void) DrawCylinder((Vector3){1.0f, 0.0f, -4.0f}, 0.0f, 1.5f, 3.0f, 8, GOLD); DrawCylinderWires((Vector3){1.0f, 0.0f, -4.0f}, 0.0f, 1.5f, 3.0f, 8, PINK); + DrawCapsule ((Vector3){-3.0f, 1.5f, -4.0f}, (Vector3){-4.0f, -1.0f, -4.0f}, 1.2f, 8, 8, VIOLET); + DrawCapsuleWires((Vector3){-3.0f, 1.5f, -4.0f}, (Vector3){-4.0f, -1.0f, -4.0f}, 1.2f, 8, 8, PURPLE); + DrawGrid(10, 1.0f); // Draw a grid EndMode3D(); diff --git a/examples/models/models_geometric_shapes.png b/examples/models/models_geometric_shapes.png index 6076b429714060a4653e7d77970932bcd8874dc0..765abe1979f097640f6fa909ba61312a41585ae0 100644 GIT binary patch literal 22450 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 literal 33973 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 Date: Thu, 20 Oct 2022 16:29:03 +0100 Subject: [PATCH 0115/1710] Review parser Makefile (#2765) * parser: Fail gracefully if a nonexistent file is passed on the command line Before, if a nonexistent file was passed to LoadFileText(), it would return NULL, and the parser would happily dereference it. * parser: Refactor Makefile and update the path to easings.h (now reasings.h) Before, the `make all` target would simply segfault, see 0a679d79 Now, if a file in the `make all` target doesn't exist, make will write an error. Individual API files can be generated likeso, provided the header file the target depends on exists: FORMAT=JSON EXTENSION=json make raygui_api.json In order for the `make all` target to succeed, raygui.h, physac.h and rmem.h need to be added to the correct directory. --- parser/Makefile | 54 +++++++++++++++++++++++++++++------------- parser/raylib_parser.c | 6 +++++ 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/parser/Makefile b/parser/Makefile index e222f4e79..165192463 100644 --- a/parser/Makefile +++ b/parser/Makefile @@ -1,28 +1,50 @@ EXTENSION?=txt FORMAT?=DEFAULT +.PHONY: all parse clean raylib_api -raylib_api: - cc raylib_parser.c -o raylib_parser - ./raylib_parser -i ../src/raylib.h -o raylib_api.txt -f DEFAULT -d RLAPI - ./raylib_parser -i ../src/raylib.h -o raylib_api.json -f JSON -d RLAPI - ./raylib_parser -i ../src/raylib.h -o raylib_api.xml -f XML -d RLAPI - ./raylib_parser -i ../src/raylib.h -o raylib_api.lua -f LUA -d RLAPI -all: +raylib_parser: raylib_parser.c cc raylib_parser.c -o raylib_parser + +raylib_api: ../src/raylib.h raylib_parser + FORMAT=DEFAULT EXTENSION=txt $(MAKE) raylib_api.txt + FORMAT=JSON EXTENSION=json $(MAKE) raylib_api.json + FORMAT=XML EXTENSION=xml $(MAKE) raylib_api.xml + FORMAT=LUA EXTENSION=lua $(MAKE) raylib_api.lua + +raylib_api.$(EXTENSION): ../src/raylib.h raylib_parser + ./raylib_parser -i ../src/raylib.h -o raylib_api.$(EXTENSION) -f $(FORMAT) -d RLAPI + +raymath_api.$(EXTENSION): ../src/raymath.h raylib_parser + ./raylib_parser -i ../src/raymath.h -o raymath_api.$(EXTENSION) -f $(FORMAT) -d RMAPI + +rlgl_api.$(EXTENSION): ../src/rlgl.h raylib_parser + ./raylib_parser -i ../src/rlgl.h -o rlgl_api.$(EXTENSION) -f $(FORMAT) -d RLAPI -t "RLGL IMPLEMENTATION" + +reasings_api.$(EXTENSION): ../examples/others/reasings.h raylib_parser + ./raylib_parser -i ../examples/others/reasings.h -o reasings_api.$(EXTENSION) -f $(FORMAT) -d EASEDEF + +rmem_api.$(EXTENSION): ../rmem.h raylib_parser + ./raylib_parser -i ../rmem.h -o rmem_api.$(EXTENSION) -f $(FORMAT) -d RMEMAPI -t "RMEM IMPLEMENTATION" + +physac_api.$(EXTENSION): ../physac.h raylib_parser + ./raylib_parser -i ../physac.h -o physac_api.$(EXTENSION) -f $(FORMAT) -d PHYSACDEF -t "PHYSAC IMPLEMENTATION" + +raygui_api.$(EXTENSION): ../raygui.h raylib_parser + ./raylib_parser -i ../raygui.h -o raygui_api.$(EXTENSION) -f $(FORMAT) -d RAYGUIAPI -t "RAYGUI IMPLEMENTATION" + +parse: raylib_api.$(EXTENSION) raymath_api.$(EXTENSION) rlgl_api.$(EXTENSION) rmem_api.$(EXTENSION) physac_api.$(EXTENSION) raygui_api.$(EXTENSION) + + +# `make parse` (and therefore `make all) requires +# rmem.h, physac.h and raygui.h to exist in the correct directory +# API files for individual headers can be created likeso, provided the relevant header exists: +# FORMAT=JSON EXTENSION=json make raygui_api.json +all: raylib_parser FORMAT=DEFAULT EXTENSION=txt $(MAKE) parse FORMAT=JSON EXTENSION=json $(MAKE) parse FORMAT=XML EXTENSION=xml $(MAKE) parse FORMAT=LUA EXTENSION=lua $(MAKE) parse -parse: - ./raylib_parser -i ../src/raylib.h -o raylib_api.$(EXTENSION) -f $(FORMAT) -d RLAPI - ./raylib_parser -i ../src/raymath.h -o raymath_api.$(EXTENSION) -f $(FORMAT) -d RMAPI - ./raylib_parser -i ../src/rlgl.h -o rlgl_api.$(EXTENSION) -f $(FORMAT) -d RLAPI -t "RLGL IMPLEMENTATION" - ./raylib_parser -i ../src/easings.h -o easings_api.$(EXTENSION) -f $(FORMAT) -d EASEDEF - ./raylib_parser -i ../src/rmem.h -o rmem_api.$(EXTENSION) -f $(FORMAT) -d RMEMAPI -t "RMEM IMPLEMENTATION" - ./raylib_parser -i ../physac.h -o physac_api.$(EXTENSION) -f $(FORMAT) -d PHYSACDEF -t "PHYSAC IMPLEMENTATION" - ./raylib_parser -i ../raygui.h -o raygui_api.$(EXTENSION) -f $(FORMAT) -d RAYGUIAPI -t "RAYGUI IMPLEMENTATION" - clean: rm -f raylib_parser *.json *.txt *.xml *.lua diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 49b43f768..5ca186d71 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -207,6 +207,12 @@ int main(int argc, char* argv[]) 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; From 40cf84e7e527aab27ae01861c887a0498949a328 Mon Sep 17 00:00:00 2001 From: InKryption <59504965+InKryption@users.noreply.github.com> Date: Thu, 20 Oct 2022 18:33:12 +0200 Subject: [PATCH 0116/1710] build.zig: let user decide how to set build mode + fix linker warning (#2763) * build.zig: let user decide how to set build mode This should delegate the responsibility of calling `standardReleaseOptions` and setting the build mode of the `*LibExeObjStep` step to the caller, especially since this might not be the process by which one wants to determine the build mode. Also changes hides `getSrcDir` to enforce usage of `srcdir`, and asserts that the file is in fact inside a directory. * build.zig: set root_src param to `null` Supplying the header file as the root source here appears to cause a linker warning of the form: ``` LLD Link... warning(link): unexpected LLD stderr: ld.lld: warning: {build_root}/zig-cache/o/{hash}/libraylib.a: archive member '{build_root}/zig-cache/o/{hash}/raylib.o' is neither ET_REL nor LLVM bitcode ``` Passing `null` instead fixes it. --- src/build.zig | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/build.zig b/src/build.zig index 8194e1e3c..6a4c07e7e 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,10 +1,6 @@ const std = @import("std"); pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.LibExeObjStep { - // Standard release options allow the person running `zig build` to select - // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. - const mode = b.standardReleaseOptions(); - const raylib_flags = &[_][]const u8{ "-std=gnu99", "-DPLATFORM_DESKTOP", @@ -13,9 +9,8 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; - const raylib = b.addStaticLibrary("raylib", srcdir ++ "/raylib.h"); + const raylib = b.addStaticLibrary("raylib", null); raylib.setTarget(target); - raylib.setBuildMode(mode); raylib.linkLibC(); raylib.addIncludePath(srcdir ++ "/external/glfw/include"); @@ -90,8 +85,8 @@ pub fn build(b: *std.build.Builder) void { lib.install(); } -const srcdir = getSrcDir(); - -fn getSrcDir() []const u8 { - return std.fs.path.dirname(@src().file) orelse "."; -} +const srcdir = struct{ + fn getSrcDir() []const u8 { + return std.fs.path.dirname(@src().file).?; + } +}.getSrcDir(); From d9f434afb9d1b2eb761a7f79125895d97ab02839 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 20 Oct 2022 20:07:29 +0200 Subject: [PATCH 0117/1710] Added `-s EXPORTED_RUNTIME_METHODS=ccall` to examples web build #2739 --- examples/Makefile | 3 ++- examples/Makefile.Web | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index b0bf42524..51aae0361 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -279,11 +279,12 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # -s ASYNCIFY # lets synchronous C/C++ code interact with asynchronous JS # -s FORCE_FILESYSTEM=1 # force filesystem to load/save files data # -s ASSERTIONS=1 # enable runtime checks for common memory allocation errors (-O1 and above turn it off) + # -s EXPORTED_RUNTIME_METHODS=ccall # require exporting some LEGACY_RUNTIME functions, ccall() is required by miniaudio # --profiling # include information for code profiling # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map - LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 --preload-file $(dir $<)resources@resources + LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 -s EXPORTED_RUNTIME_METHODS=ccall --preload-file $(dir $<)resources@resources # NOTE: Simple raylib examples are compiled to be interpreter with asyncify, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger diff --git a/examples/Makefile.Web b/examples/Makefile.Web index d2dc0d5c4..3a83b7bb7 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -283,7 +283,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map - LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY + LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s EXPORTED_RUNTIME_METHODS=ccall # NOTE: Simple raylib examples are compiled to be interpreter with asyncify, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger From 483f10397ee37ba551e57bad563a846f2dc5bb5b Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 20 Oct 2022 20:09:54 +0200 Subject: [PATCH 0118/1710] review note --- examples/Makefile | 2 +- examples/Makefile.Web | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 51aae0361..e1c3a9983 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -327,7 +327,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lc endif - # TODO: On ARM 32bit arch, miniaudio requires atomics library + # NOTE: On ARM 32bit arch, miniaudio requires atomics library LDLIBS += -latomic endif ifeq ($(PLATFORM_OS),OSX) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 3a83b7bb7..d7e640862 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -279,6 +279,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # -s ASYNCIFY # lets synchronous C/C++ code interact with asynchronous JS # -s FORCE_FILESYSTEM=1 # force filesystem to load/save files data # -s ASSERTIONS=1 # enable runtime checks for common memory allocation errors (-O1 and above turn it off) + # -s EXPORTED_RUNTIME_METHODS=ccall # require exporting some LEGACY_RUNTIME functions, ccall() is required by miniaudio # --profiling # include information for code profiling # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) # --preload-file resources # specify a resources folder for data compilation @@ -328,6 +329,9 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(RAYLIB_LIBTYPE),SHARED) LDLIBS += -lc endif + + # NOTE: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic endif ifeq ($(PLATFORM_OS),OSX) # Libraries for OSX 10.9 desktop compiling @@ -466,6 +470,7 @@ MODELS = \ models/models_loading \ models/models_loading_vox \ models/models_loading_gltf \ + models/models_loading_m3d \ models/models_orthographic_projection \ models/models_rlgl_solar_system \ models/models_skybox \ From d71505bdc65161a1b29b3d1e178c29231dca2a0a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 24 Oct 2022 13:37:08 +0200 Subject: [PATCH 0119/1710] Update Makefile --- examples/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index e1c3a9983..26d8b43ea 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -279,12 +279,11 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # -s ASYNCIFY # lets synchronous C/C++ code interact with asynchronous JS # -s FORCE_FILESYSTEM=1 # force filesystem to load/save files data # -s ASSERTIONS=1 # enable runtime checks for common memory allocation errors (-O1 and above turn it off) - # -s EXPORTED_RUNTIME_METHODS=ccall # require exporting some LEGACY_RUNTIME functions, ccall() is required by miniaudio # --profiling # include information for code profiling # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map - LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 -s EXPORTED_RUNTIME_METHODS=ccall --preload-file $(dir $<)resources@resources + LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 -s --preload-file $(dir $<)resources@resources # NOTE: Simple raylib examples are compiled to be interpreter with asyncify, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger From d5cd5ebd80ad02a3a9129ecf080007fce7efe4d6 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 24 Oct 2022 13:37:20 +0200 Subject: [PATCH 0120/1710] Update to latest miniaudio (dev) --- src/external/miniaudio.h | 1414 +++++++++++++++++++++++++++++--------- 1 file changed, 1090 insertions(+), 324 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index f774f0d5f..d454c4978 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.9 - 2022-04-20 +miniaudio - v0.11.11 - TBD David Reid - mackron@gmail.com @@ -386,7 +386,7 @@ Sounds should be uninitialized with `ma_sound_uninit()`. Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with `ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use -`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting +`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound the be started and/or stopped at a specific time. This can be done with the following functions: @@ -627,10 +627,29 @@ You cannot use `-std=c*` compiler flags, nor `-ansi`. | | and `ma_device` APIs. This is useful if you only want to use | | | miniaudio's data conversion and/or decoding APIs. | +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | + | | also disable the following functions: | + | | | + | | ``` | + | | ma_sound_init_from_file() | + | | ma_sound_init_from_file_w() | + | | ma_sound_init_copy() | + | | ma_engine_play_sound_ex() | + | | ma_engine_play_sound() | + | | ``` | + | | | + | | The only way to initialize a `ma_sound` object is to initialize it | + | | from a data source. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | + | | because it depends on the node graph. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_ENGINE | Disables the engine API. | + +----------------------------------+--------------------------------------------------------------------+ | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | | | `ma_event` APIs. This option is useful if you only need to use | | | miniaudio for data conversion, decoding and/or encoding. Some | - | | families of APIsrequire threading which means the following | + | | families of APIs require threading which means the following | | | options must also be set: | | | | | | ``` | @@ -1429,7 +1448,7 @@ source, mainly for convenience: Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do not have any notion of a data source, anything relating to a data source is unavailable. -Internally, sound data is loaded via the `ma_decoder` API which means by default in only supports +Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports file formats that have built-in support in miniaudio. You can extend this to support any kind of file format through the use of custom decoders. To do this you'll need to use a self-managed resource manager and configure it appropriately. See the "Resource Management" section below for @@ -1444,7 +1463,7 @@ streaming. This is supported by miniaudio via the `ma_resource_manager` API. The resource manager is mainly responsible for the following: * Loading of sound files into memory with reference counting. - * Streaming of sound data + * Streaming of sound data. When loading a sound file, the resource manager will give you back a `ma_data_source` compatible object called `ma_resource_manager_data_source`. This object can be passed into any @@ -1539,7 +1558,7 @@ need to retrieve a job using `ma_resource_manager_next_job()` and then process i ma_job job; ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); if (result != MA_SUCCESS) { - if (result == MA_NOT_DATA_AVAILABLE) { + if (result == MA_NO_DATA_AVAILABLE) { // No jobs are available. Keep going. Will only get this if the resource manager was initialized // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. continue; @@ -1578,7 +1597,7 @@ default. This can be done by setting `pVFS` member of the resource manager's con This is particularly useful in programs like games where you want to read straight from an archive rather than the normal file system. If you do not specify a custom VFS, the resource manager will -use the operating system's normal file operations. This is default. +use the operating system's normal file operations. To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When loading a sound you need to specify the file path and options for how the sounds should be loaded. @@ -1933,7 +1952,7 @@ miniaudio's routing infrastructure follows a node graph paradigm. The idea is th node whose outputs are attached to inputs of another node, thereby creating a graph. There are different types of nodes, with each node in the graph processing input data to produce output, which is then fed through the chain. Each node in the graph can apply their own custom effects. At -the start of the graph will usually be one or more data source nodes which have no inputs, but +the start of the graph will usually be one or more data source nodes which have no inputs and instead pull their data from a data source. At the end of the graph is an endpoint which represents the end of the chain and is where the final output is ultimately extracted from. @@ -1959,7 +1978,7 @@ splitter node. It's at this point that the two data sources are mixed. After mix performs it's processing routine and produces two outputs which is simply a duplication of the input stream. One output is attached to a low pass filter, whereas the other output is attached to a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and -since they're both connected to the same input but, they'll be mixed. +since they're both connected to the same input bus, they'll be mixed. Each input bus must be configured to accept the same number of channels, but the number of channels used by input buses can be different to the number of channels for output buses in which case @@ -2074,7 +2093,7 @@ pointer to the processing function and the number of input and output buses. Exa static ma_node_vtable my_custom_node_vtable = { - my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. + my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. 2, // 2 input buses. 1, // 1 output bus. @@ -2204,7 +2223,7 @@ called `ma_splitter_node`. This takes has 1 input bus and splits the stream into You can use it like this: ```c - ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut); + ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); ma_splitter_node splitterNode; result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); @@ -3514,7 +3533,12 @@ producer thread. 15. Backends ============ -The following backends are supported by miniaudio. +The following backends are supported by miniaudio. These are listed in order of default priority. +When no backend is specified when initializing a context or device, miniaudio will attempt to use +each of these backends in the order listed in the table below. + +Note that backends that are not usable by the build target will not be included in the build. For +example, ALSA, which is specific to Linux, will not be included in the Windows build. +-------------+-----------------------+--------------------------------------------------------+ | Name | Enum Name | Supported Operating Systems | @@ -3523,12 +3547,12 @@ The following backends are supported by miniaudio. | DirectSound | ma_backend_dsound | Windows XP+ | | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | | sndio | ma_backend_sndio | OpenBSD | | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | | OSS | ma_backend_oss | FreeBSD | + | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | + | ALSA | ma_backend_alsa | Linux | + | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | | AAudio | ma_backend_aaudio | Android 8+ | | OpenSL ES | ma_backend_opensl | Android (API level 16+) | | Web Audio | ma_backend_webaudio | Web (via Emscripten) | @@ -3640,7 +3664,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 9 +#define MA_VERSION_REVISION 11 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3737,7 +3761,7 @@ typedef ma_uint16 wchar_t; /* Platform/backend detection. */ #ifdef _WIN32 #define MA_WIN32 - #if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)) + #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) #define MA_WIN32_UWP #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) #define MA_WIN32_GDK @@ -3890,7 +3914,7 @@ implications. Where supported by the compiler, alignment will be used, but other architecture does not require it, it will simply leave it unaligned. This is the case with old versions of Visual Studio, which I've confirmed with at least VC6. */ -#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) #include #define MA_ATOMIC(alignment, type) alignas(alignment) type #else @@ -4123,7 +4147,7 @@ typedef enum { ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ - ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */ + ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular } ma_channel_mix_mode; @@ -4748,7 +4772,7 @@ typedef struct { ma_delay_config config; ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ - ma_uint32 bufferSizeInFrames; /* The maximum of config.startDelayInFrames and config.feedbackDelayInFrames. */ + ma_uint32 bufferSizeInFrames; float* pBuffer; } ma_delay; @@ -5264,6 +5288,7 @@ typedef struct const ma_channel* pChannelMapIn; const ma_channel* pChannelMapOut; ma_channel_mix_mode mixingMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ } ma_channel_converter_config; @@ -5316,6 +5341,7 @@ typedef struct ma_channel* pChannelMapOut; ma_dither_mode ditherMode; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ ma_bool32 allowDynamicSampleRate; ma_resampler_config resampling; @@ -5496,6 +5522,28 @@ The channel map buffer must have a capacity of at least `channels`. */ MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); +/* +Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The +index of the channel is output to `pChannelIndex`. + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); + +/* +Generates a string representing the given channel map. + +This is for printing and debugging purposes, not serialization/deserialization. + +Returns the length of the string, not including the null terminator. +*/ +MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); + +/* +Retrieves a human readable version of a channel position. +*/ +MA_API const char* ma_channel_position_to_string(ma_channel channel); + /************************************************************************************************************************************************************ @@ -6103,10 +6151,6 @@ This section contains the APIs for device playback and capture. Here is where yo #define MA_SUPPORT_PULSEAUDIO #define MA_SUPPORT_JACK #endif - #if defined(MA_ANDROID) - #define MA_SUPPORT_AAUDIO - #define MA_SUPPORT_OPENSL - #endif #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ #endif @@ -6117,6 +6161,10 @@ This section contains the APIs for device playback and capture. Here is where yo #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ #endif #endif +#if defined(MA_ANDROID) + #define MA_SUPPORT_AAUDIO + #define MA_SUPPORT_OPENSL +#endif #if defined(MA_APPLE) #define MA_SUPPORT_COREAUDIO #endif @@ -6412,7 +6460,7 @@ typedef enum /* iOS/tvOS/watchOS session categories. */ typedef enum { - ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */ + ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ ma_ios_session_category_none, /* Leave the session category unchanged. */ ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ @@ -6457,36 +6505,44 @@ typedef enum ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ } ma_opensl_recording_preset; +/* WASAPI audio thread priority characteristics. */ +typedef enum +{ + ma_wasapi_usage_default = 0, + ma_wasapi_usage_games, + ma_wasapi_usage_pro_audio, +} ma_wasapi_usage; + /* AAudio usage types. */ typedef enum { ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ - ma_aaudio_usage_announcement, /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ - ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ - ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ - ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ + ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ + ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ + ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ + ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ + ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ + ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ - ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ - ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ - ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ - ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ - ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ - ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ - ma_aaudio_usage_voice_communication_signalling /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ + ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ + ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ + ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ + ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ + ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ } ma_aaudio_usage; /* AAudio content types. */ typedef enum { ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ - ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ + ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ - ma_aaudio_content_type_sonification, /* AAUDIO_CONTENT_TYPE_SONIFICATION */ - ma_aaudio_content_type_speech /* AAUDIO_CONTENT_TYPE_SPEECH */ + ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ + ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ } ma_aaudio_content_type; /* AAudio input presets. */ @@ -6495,9 +6551,9 @@ typedef enum ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ - ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ + ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ } ma_aaudio_input_preset; @@ -6584,6 +6640,7 @@ struct ma_device_config ma_uint32 channels; ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ ma_share_mode shareMode; } playback; struct @@ -6593,15 +6650,19 @@ struct ma_device_config ma_uint32 channels; ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ ma_share_mode shareMode; } capture; struct { - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ + ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ + ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ + ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ + ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ } wasapi; struct { @@ -6714,7 +6775,7 @@ sample rate. For the channel map, the default should be used when `ma_channel_ma `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the -sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_data_format` +sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses @@ -6844,6 +6905,11 @@ struct ma_context ma_uint32 commandIndex; ma_uint32 commandCount; ma_context_command__wasapi commands[4]; + ma_handle hAvrt; + ma_proc AvSetMmThreadCharacteristicsW; + ma_proc AvRevertMmThreadcharacteristics; + ma_handle hMMDevapi; + ma_proc ActivateAudioInterfaceAsync; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND @@ -7278,6 +7344,7 @@ struct ma_device ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; ma_data_converter converter; void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ ma_uint32 intermediaryBufferCap; @@ -7303,6 +7370,7 @@ struct ma_device ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; ma_data_converter converter; void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ ma_uint32 intermediaryBufferCap; @@ -7338,6 +7406,8 @@ struct ma_device ma_uint32 mappedBufferPlaybackLen; MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_uint32 loopbackProcessID; + ma_bool8 loopbackProcessExclude; ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ ma_bool8 noHardwareOffloading; @@ -7345,6 +7415,8 @@ struct ma_device ma_bool8 allowPlaybackAutoStreamRouting; ma_bool8 isDetachedPlayback; ma_bool8 isDetachedCapture; + ma_wasapi_usage usage; + void *hAvrtHandle; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND @@ -8146,9 +8218,9 @@ then be set directly on the structure. Below are the members of the `ma_device_c By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. noFixedSizedCallback - Allows miniaudio to fire the data callback with any frame count. When this is set to true, the data callback will be fired with a consistent frame - count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to false, miniaudio will fire the callback with whatever the - backend requests, which could be anything. + Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a + consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with + whatever the backend requests, which could be anything. dataCallback The callback to fire whenever data is ready to be delivered to or from the device. @@ -10441,6 +10513,7 @@ typedef struct { ma_node_config nodeConfig; ma_uint32 channels; + ma_uint32 outputBusCount; } ma_splitter_node_config; MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); @@ -10665,6 +10738,7 @@ MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); #endif /* MA_NO_NODE_GRAPH */ +/* SECTION: miniaudio_engine.h */ /************************************************************************************************************************************************************ Engine @@ -10706,6 +10780,7 @@ typedef struct ma_uint32 channelsIn; ma_uint32 channelsOut; ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ + ma_mono_expansion_mode monoExpansionMode; ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ @@ -10720,6 +10795,7 @@ typedef struct ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ + ma_mono_expansion_mode monoExpansionMode; ma_fader fader; ma_linear_resampler resampler; /* For pitch shift. */ ma_spatializer spatializer; @@ -10753,6 +10829,7 @@ typedef struct ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ ma_uint64 rangeBegInPCMFrames; @@ -10763,7 +10840,8 @@ typedef struct ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */ } ma_sound_config; -MA_API ma_sound_config ma_sound_config_init(void); +MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ +MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ struct ma_sound { @@ -10795,8 +10873,8 @@ struct ma_sound_inlined typedef ma_sound_config ma_sound_group_config; typedef ma_sound ma_sound_group; -MA_API ma_sound_group_config ma_sound_group_config_init(void); - +MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ +MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ typedef struct { @@ -10807,6 +10885,7 @@ typedef struct ma_context* pContext; ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ + ma_device_notification_proc notificationCallback; #endif ma_log* pLog; /* When set to NULL, will use the context's log. */ ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ @@ -11016,6 +11095,7 @@ MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); #endif /* MA_NO_ENGINE */ +/* END SECTION: miniaudio_engine.h */ #ifdef __cplusplus } @@ -11801,12 +11881,19 @@ Standard Library Stuff #endif #endif -#ifndef MA_ZERO_MEMORY +static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) +{ #ifdef MA_WIN32 -#define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz)) + ZeroMemory(p, sz); #else -#define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) + if (sz > 0) { + memset(p, 0, sz); + } #endif +} + +#ifndef MA_ZERO_MEMORY +#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) #endif #ifndef MA_COPY_MEMORY @@ -12227,12 +12314,15 @@ MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { + size_t sz; + char* dst; + if (src == NULL) { return NULL; } - size_t sz = strlen(src)+1; - char* dst = (char*)ma_malloc(sz, pAllocationCallbacks); + sz = strlen(src)+1; + dst = (char*)ma_malloc(sz, pAllocationCallbacks); if (dst == NULL) { return NULL; } @@ -13187,7 +13277,7 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat return MA_INVALID_ARGS; } - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) { ma_result result; int length; @@ -17052,6 +17142,10 @@ DEVICE I/O #include /* For mach_absolute_time() */ #endif +#ifdef MA_ANDROID + #include +#endif + #ifdef MA_POSIX #include #include @@ -17184,13 +17278,43 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) #endif case ma_backend_aaudio: #if defined(MA_HAS_AAUDIO) - return MA_TRUE; + #if defined(MA_ANDROID) + { + char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; + if (__system_property_get("ro.build.version.sdk", sdkVersion)) { + if (atoi(sdkVersion) >= 26) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + } + #else + return MA_FALSE; + #endif #else return MA_FALSE; #endif case ma_backend_opensl: #if defined(MA_HAS_OPENSL) - return MA_TRUE; + #if defined(MA_ANDROID) + { + char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; + if (__system_property_get("ro.build.version.sdk", sdkVersion)) { + if (atoi(sdkVersion) >= 9) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + } + #else + return MA_TRUE; + #endif #else return MA_FALSE; #endif @@ -17584,17 +17708,18 @@ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); #ifdef _WIN32 -#ifdef MA_WIN32_DESKTOP - handle = (ma_handle)LoadLibraryA(filename); -#else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } -#endif + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(WINAPI_FAMILY) || (defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_DESKTOP_APP) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif #else handle = (ma_handle)dlopen(filename, RTLD_NOW); #endif @@ -17772,6 +17897,11 @@ static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* { MA_ASSERT(pDevice != NULL); + /* Don't read more data from the client if we're in the process of stopping. */ + if (ma_device_get_state(pDevice) == ma_device_state_stopping) { + return; + } + if (pDevice->noFixedSizedCallback) { /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); @@ -18059,6 +18189,9 @@ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frame totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; totalClientFramesProcessed += clientFramesProcessedThisIteration; + /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ + (void)totalClientFramesProcessed; + if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { break; /* We're done. */ } @@ -19064,11 +19197,9 @@ static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap /* Converts a Win32-style channel mask to a miniaudio channel map. */ static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) { - if (channels == 1 && dwChannelMask == 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else if (channels == 2 && dwChannelMask == 0) { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; + /* If the channel mask is set to 0, just assume a default Win32 channel map. */ + if (dwChannelMask == 0) { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); } else { if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { pChannelMap[0] = MA_CHANNEL_MONO; @@ -19116,7 +19247,7 @@ static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF) } if (pWFEX->Samples.wValidBitsPerSample == 24) { if (pWFEX->Format.wBitsPerSample == 32) { - /*return ma_format_s24_32;*/ + return ma_format_s32; } if (pWFEX->Format.wBitsPerSample == 24) { return ma_format_s24; @@ -19693,8 +19824,16 @@ static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } +#if defined(MA_WIN32_UWP) +/* mmdevapi Functions */ +typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(LPCWSTR deviceInterfacePath, const IID* riid, PROPVARIANT *activationParams, ma_IActivateAudioInterfaceCompletionHandler *completionHandler, ma_IActivateAudioInterfaceAsyncOperation **activationOperation); +#endif + +/* Avrt Functions */ +typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsW)(LPCWSTR TaskName, LPDWORD TaskIndex); +typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); + #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -#include typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; typedef struct @@ -20045,6 +20184,18 @@ static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { }; #endif /* MA_WIN32_DESKTOP */ +static LPCWSTR ma_to_usage_string__wasapi(ma_wasapi_usage usage) +{ + switch (usage) { + case ma_wasapi_usage_default: return NULL; + case ma_wasapi_usage_games: return L"Games"; + case ma_wasapi_usage_pro_audio: return L"Pro Audio"; + default: break; + } + + return NULL; +} + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) typedef ma_IMMDevice ma_WASAPIDeviceInterface; #else @@ -20369,6 +20520,10 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); } } + #else + { + (void)pMMDevice; /* Unused. */ + } #endif return MA_SUCCESS; @@ -20473,7 +20628,7 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); return ma_result_from_HRESULT(hr); } @@ -20485,7 +20640,7 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); return ma_result_from_HRESULT(hr); } @@ -20594,7 +20749,7 @@ static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pConte if (SUCCEEDED(hr)) { hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); result = ma_result_from_HRESULT(hr); goto done; } @@ -20634,7 +20789,7 @@ done: return result; } -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) +static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) { ma_result result; HRESULT hr; @@ -20648,7 +20803,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex return result; } - hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient); + hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); if (FAILED(hr)) { return ma_result_from_HRESULT(hr); } @@ -20656,7 +20811,7 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex return MA_SUCCESS; } #else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) +static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) { ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; ma_completion_handler_uwp completionHandler; @@ -20671,45 +20826,43 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m MA_ASSERT(ppAudioClient != NULL); if (pDeviceID != NULL) { - MA_COPY_MEMORY(&iid, pDeviceID->wasapi, sizeof(iid)); + iidStr = (LPOLESTR)pDeviceID->wasapi; } else { - if (deviceType == ma_device_type_playback) { - iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; - } else { + if (deviceType == ma_device_type_capture) { iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; + } else { + iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; } - } -#if defined(__cplusplus) - hr = StringFromIID(iid, &iidStr); -#else - hr = StringFromIID(&iid, &iidStr); -#endif - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory."); - return ma_result_from_HRESULT(hr); + #if defined(__cplusplus) + hr = StringFromIID(iid, &iidStr); + #else + hr = StringFromIID(&iid, &iidStr); + #endif + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); + return ma_result_from_HRESULT(hr); + } } result = ma_completion_handler_uwp_init(&completionHandler); if (result != MA_SUCCESS) { ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync()."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); return result; } -#if defined(__cplusplus) - hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); -#else - hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); -#endif + hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); if (FAILED(hr)) { ma_completion_handler_uwp_uninit(&completionHandler); ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); return ma_result_from_HRESULT(hr); } - ma_CoTaskMemFree(pContext, iidStr); + if (pDeviceID == NULL) { + ma_CoTaskMemFree(pContext, iidStr); + } /* Wait for the async operation for finish. */ ma_completion_handler_uwp_wait(&completionHandler); @@ -20719,14 +20872,14 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); if (FAILED(hr) || FAILED(activateResult)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); } /* Here is where we grab the IAudioClient interface. */ hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface."); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); return ma_result_from_HRESULT(hr); } @@ -20740,13 +20893,106 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m } #endif -static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ +typedef enum { -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); -#else - return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface); + MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, + MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK +} MA_AUDIOCLIENT_ACTIVATION_TYPE; + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ +typedef enum +{ + MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, + MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE +} MA_PROCESS_LOOPBACK_MODE; + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ +typedef struct +{ + DWORD TargetProcessId; + MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; +} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; + +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif #endif +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ +typedef struct +{ + MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; + union + { + MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; + }; +} MA_AUDIOCLIENT_ACTIVATION_PARAMS; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop +#endif + +#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" + +static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) +{ + ma_result result; + ma_bool32 usingProcessLoopback = MA_FALSE; + MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; + PROPVARIANT activationParams; + PROPVARIANT* pActivationParams = NULL; + ma_device_id virtualDeviceID; + + /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ + if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { + usingProcessLoopback = MA_TRUE; + } + + if (usingProcessLoopback) { + MA_ZERO_OBJECT(&audioclientActivationParams); + audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; + audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; + audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; + + ma_PropVariantInit(&activationParams); + activationParams.vt = VT_BLOB; + activationParams.blob.cbSize = sizeof(audioclientActivationParams); + activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; + pActivationParams = &activationParams; + + /* When requesting a specific device ID we need to use a special device ID. */ + MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ + pDeviceID = &virtualDeviceID; + } else { + pActivationParams = NULL; /* No activation parameters required. */ + } + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); +#else + result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); +#endif + + /* + If loopback mode was requested with a process ID and initialization failed, it could be because it's + trying to run on an older version of Windows where it's not supported. We need to let the caller + know about this with a log message. + */ + if (result != MA_SUCCESS) { + if (usingProcessLoopback) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); + } + } + + return result; } @@ -20839,7 +21085,7 @@ static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_dev ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL); + result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); if (result != MA_SUCCESS) { return result; } @@ -20918,6 +21164,8 @@ typedef struct ma_bool32 noAutoConvertSRC; ma_bool32 noDefaultQualitySRC; ma_bool32 noHardwareOffloading; + ma_uint32 loopbackProcessID; + ma_bool32 loopbackProcessExclude; /* Output. */ ma_IAudioClient* pAudioClient; @@ -20971,7 +21219,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; } - result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface); + result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); if (result != MA_SUCCESS) { goto done; } @@ -21352,7 +21600,7 @@ done: } if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s", errorMsg); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); } return result; @@ -21429,6 +21677,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; + data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); if (result != MA_SUCCESS) { return result; @@ -21492,9 +21742,12 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->wasapi); - pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + pDevice->wasapi.usage = pConfig->wasapi.usage; + pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; + pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; + pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; /* Exclusive mode is not allowed with loopback. */ if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { @@ -21515,6 +21768,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); if (result != MA_SUCCESS) { @@ -21579,6 +21834,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); if (result != MA_SUCCESS) { @@ -21785,6 +22042,14 @@ static ma_result ma_device_start__wasapi(ma_device* pDevice) MA_ASSERT(pDevice != NULL); + if (pDevice->pContext->wasapi.hAvrt) { + LPCWSTR pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); + if (pTaskName) { + DWORD idx = 0; + pDevice->wasapi.hAvrtHandle = ((MA_PFN_AvSetMmThreadCharacteristicsW)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsW)(pTaskName, &idx); + } + } + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { @@ -21815,6 +22080,11 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) MA_ASSERT(pDevice != NULL); + if (pDevice->wasapi.hAvrtHandle) { + ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); + pDevice->wasapi.hAvrtHandle = NULL; + } + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { @@ -21873,7 +22143,7 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) } prevFramesAvaialablePlayback = framesAvailablePlayback; - WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); + WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime * 1000); ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */ } } @@ -22011,7 +22281,14 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui loopback mode, in which case a timeout probably just means the nothing is playing through the speakers. */ - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + + /* Experiment: Use a shorter timeout for loopback mode. */ + DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; + if (pDevice->type == ma_device_type_loopback) { + timeoutInMilliseconds = 10; + } + + if (WaitForSingleObject(pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { if (pDevice->type == ma_device_type_loopback) { continue; /* Keep waiting in loopback mode. */ } else { @@ -22169,6 +22446,20 @@ static ma_result ma_context_uninit__wasapi(ma_context* pContext) ma_context_post_command__wasapi(pContext, &cmd); ma_thread_wait(&pContext->wasapi.commandThread); + if (pContext->wasapi.hAvrt) { + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + + #if defined(MA_WIN32_UWP) + { + if (pContext->wasapi.hMMDevapi) { + ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + pContext->wasapi.hMMDevapi = NULL; + } + } + #endif + /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); @@ -22274,6 +22565,41 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ ma_mutex_uninit(&pContext->wasapi.commandLock); return result; } + + #if defined(MA_WIN32_UWP) + { + /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ + pContext->wasapi.hMMDevapi = ma_dlopen(pContext, "mmdevapi.dll"); + if (pContext->wasapi.hMMDevapi) { + pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(pContext, pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); + if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ + } + } else { + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ + } + } + #endif + + /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ + pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); + if (pContext->wasapi.hAvrt) { + pContext->wasapi.AvSetMmThreadCharacteristicsW = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsW"); + pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); + + /* If either function could not be found, disable use of avrt entirely. */ + if (!pContext->wasapi.AvSetMmThreadCharacteristicsW || !pContext->wasapi.AvRevertMmThreadcharacteristics) { + pContext->wasapi.AvSetMmThreadCharacteristicsW = NULL; + pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + } } @@ -26462,7 +26788,11 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ { - ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); + ma_snd_pcm_chmap_t* pChmap = NULL; + if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { + pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); + } + if (pChmap != NULL) { ma_uint32 iChannel; @@ -28169,6 +28499,14 @@ static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_ return; } + /* + There has been a report that indicates that pInfo can be null which results + in a null pointer dereference below. We'll check for this for safety. + */ + if (pInfo == NULL) { + return; + } + pInfoOut = (ma_pa_sink_info*)pUserData; MA_ASSERT(pInfoOut != NULL); @@ -28185,6 +28523,14 @@ static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const m return; } + /* + There has been a report that indicates that pInfo can be null which results + in a null pointer dereference below. We'll check for this for safety. + */ + if (pInfo == NULL) { + return; + } + pInfoOut = (ma_pa_source_info*)pUserData; MA_ASSERT(pInfoOut != NULL); @@ -28912,6 +29258,11 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sourceInfo.sample_spec; cmap = sourceInfo.channel_map; + /* Use the requested sample rate if one was specified. */ + if (pDescriptorCapture->sampleRate != 0) { + ss.rate = pDescriptorCapture->sampleRate; + } + if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; @@ -29048,6 +29399,11 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sinkInfo.sample_spec; cmap = sinkInfo.channel_map; + /* Use the requested sample rate if one was specified. */ + if (pDescriptorPlayback->sampleRate != 0) { + ss.rate = pDescriptorPlayback->sampleRate; + } + if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; @@ -30401,7 +30757,7 @@ structure with three variables and is used to identify which property you are ge which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to -kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different. +kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property @@ -30705,6 +31061,14 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* return MA_SUCCESS; } +#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ + (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) +#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain +#else +/* kAudioObjectPropertyElementMaster is deprecated. */ +#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster +#endif + static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ { AudioObjectPropertyAddress propAddressDevices; @@ -30722,7 +31086,7 @@ static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt3 propAddressDevices.mSelector = kAudioHardwarePropertyDevices; propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDevices.mElement = kAudioObjectPropertyElementMaster; + propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); if (status != noErr) { @@ -30756,7 +31120,7 @@ static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, Aud propAddress.mSelector = kAudioDevicePropertyDeviceUID; propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(*pUID); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); @@ -30798,7 +31162,7 @@ static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID obj propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(deviceName); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); @@ -30827,7 +31191,7 @@ static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioOb /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; propAddress.mScope = scope; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { @@ -30882,7 +31246,7 @@ static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, Au */ propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { @@ -30920,7 +31284,7 @@ static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioOb propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { @@ -31010,7 +31374,7 @@ static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObje propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { @@ -31132,7 +31496,7 @@ static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pC propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(bufferSizeRange); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); @@ -31170,7 +31534,7 @@ static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); @@ -31199,7 +31563,7 @@ static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_t *pDeviceObjectID = 0; propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster; + propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; if (deviceType == ma_device_type_playback) { propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; } else { @@ -32272,7 +32636,7 @@ static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContex if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); @@ -32302,7 +32666,7 @@ static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pCont if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); @@ -32753,7 +33117,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = kAudioObjectPropertyElementMaster; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); if (status != noErr) { @@ -35974,22 +36338,22 @@ static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) { switch (usage) { - case ma_aaudio_usage_announcement: return MA_AAUDIO_USAGE_MEDIA; - case ma_aaudio_usage_emergency: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; - case ma_aaudio_usage_safety: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_USAGE_ALARM; - case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_NOTIFICATION; - case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; - case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; - case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; - case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_GAME; - case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_ASSISTANT; - case ma_aaudio_usage_notification_event: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; - case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; - case ma_aaudio_usage_voice_communication: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; - case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; + case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; + case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; + case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; + case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; + case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; + case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; + case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; + case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; + case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; + case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; + case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; + case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; + case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; + case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; + case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; + case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; default: break; } @@ -35999,10 +36363,10 @@ static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) { switch (contentType) { - case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; - case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; - case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; + case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; + case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; + case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; default: break; } @@ -36014,9 +36378,9 @@ static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_prese switch (inputPreset) { case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; - case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; + case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; default: break; } @@ -38376,7 +38740,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d } /* Send data to the client from our intermediary buffer. */ - ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]); + _ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); totalFramesProcessed += framesToProcess; } @@ -38422,7 +38786,7 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d } /* Read data from the client into our intermediary buffer. */ - ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]); + _ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */ if (outputSilence) { @@ -38566,8 +38930,17 @@ static ma_result ma_context_uninit__webaudio(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_webaudio); - /* Nothing needs to be done here. */ - (void)pContext; + (void)pContext; /* Unused. */ + + /* Remove the global miniaudio object from window if there are no more references to it. */ + EM_ASM({ + if (typeof(window.miniaudio) !== 'undefined') { + window.miniaudio.referenceCount--; + if (window.miniaudio.referenceCount === 0) { + delete window.miniaudio; + } + } + }); return MA_SUCCESS; } @@ -38582,12 +38955,14 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex /* Here is where our global JavaScript object is initialized. */ resultFromJS = EM_ASM_INT({ - if ((window.AudioContext || window.webkitAudioContext) === undefined) { + if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { return 0; /* Web Audio not supported. */ } if (typeof(window.miniaudio) === 'undefined') { - window.miniaudio = {}; + window.miniaudio = { + referenceCount: 0 + }; miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ miniaudio.track_device = function(device) { @@ -38651,6 +39026,8 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex }); } + window.miniaudio.referenceCount++; + return 1; }, 0); /* Must pass in a dummy argument for C99 compatibility. */ @@ -38761,20 +39138,21 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { /* Converting from internal device format to client format. */ ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - converterConfig.pChannelMapOut = pDevice->capture.channelMap; - converterConfig.channelMixMode = pDevice->capture.channelMixMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + converterConfig.formatIn = pDevice->capture.internalFormat; + converterConfig.channelsIn = pDevice->capture.internalChannels; + converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; + converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; + converterConfig.formatOut = pDevice->capture.format; + converterConfig.channelsOut = pDevice->capture.channels; + converterConfig.sampleRateOut = pDevice->sampleRate; + converterConfig.pChannelMapOut = pDevice->capture.channelMap; + converterConfig.channelMixMode = pDevice->capture.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; /* Make sure the old converter is uninitialized first. */ if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { @@ -38790,20 +39168,21 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { /* Converting from client format to device format. */ ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - converterConfig.pChannelMapIn = pDevice->playback.channelMap; - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + converterConfig.formatIn = pDevice->playback.format; + converterConfig.channelsIn = pDevice->playback.channels; + converterConfig.sampleRateIn = pDevice->sampleRate; + converterConfig.pChannelMapIn = pDevice->playback.channelMap; + converterConfig.formatOut = pDevice->playback.internalFormat; + converterConfig.channelsOut = pDevice->playback.internalChannels; + converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; + converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; + converterConfig.channelMixMode = pDevice->playback.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; /* Make sure the old converter is uninitialized first. */ if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { @@ -39111,6 +39490,8 @@ static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); +#else + (void)pContext; /* Unused. */ #endif ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); @@ -39502,13 +39883,17 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC #ifdef MA_HAS_AAUDIO case ma_backend_aaudio: { - pContext->callbacks.onContextInit = ma_context_init__aaudio; + if (ma_is_backend_enabled(backend)) { + pContext->callbacks.onContextInit = ma_context_init__aaudio; + } } break; #endif #ifdef MA_HAS_OPENSL case ma_backend_opensl: { - pContext->callbacks.onContextInit = ma_context_init__opensl; + if (ma_is_backend_enabled(backend)) { + pContext->callbacks.onContextInit = ma_context_init__opensl; + } } break; #endif #ifdef MA_HAS_WEBAUDIO @@ -39717,7 +40102,12 @@ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** p /* Capture devices. */ if (ppCaptureDeviceInfos != NULL) { - *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */ + *ppCaptureDeviceInfos = pContext->pDeviceInfos; + /* Capture devices come after playback devices. */ + if (pContext->playbackDeviceInfoCount > 0) { + /* Conditional, because NULL+0 is undefined behavior. */ + *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; + } } if (pCaptureDeviceCount != NULL) { *pCaptureDeviceCount = pContext->captureDeviceInfoCount; @@ -39867,13 +40257,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC pDevice->capture.channels = pConfig->capture.channels; ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; + pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; pDevice->playback.shareMode = pConfig->playback.shareMode; pDevice->playback.format = pConfig->playback.format; pDevice->playback.channels = pConfig->playback.channels; ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; - + pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; result = ma_mutex_init(&pDevice->startStopLock); if (result != MA_SUCCESS) { @@ -40135,9 +40526,9 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC /* Log device information. */ { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); + ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); @@ -40150,6 +40541,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); + { + char channelMapStr[1024]; + ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); + + ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); + } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; @@ -40166,6 +40565,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); + { + char channelMapStr[1024]; + ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); + + ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); + } } } @@ -40216,6 +40623,33 @@ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backen result = MA_NO_BACKEND; for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { + /* + This is a hack for iOS. If the context config is null, there's a good chance the + `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this + case, set the session category based on the device type. + */ + #if defined(MA_APPLE_MOBILE) + ma_context_config contextConfig; + + if (pContextConfig == NULL) { + contextConfig = ma_context_config_init(); + switch (pConfig->deviceType) { + case ma_device_type_duplex: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; + } break; + case ma_device_type_capture: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; + } break; + case ma_device_type_playback: + default: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; + } break; + } + + pContextConfig = &contextConfig; + } + #endif + result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); if (result == MA_SUCCESS) { result = ma_device_init(pContext, pConfig, pDevice); @@ -40491,6 +40925,15 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) ma_event_wait(&pDevice->stopEvent); result = MA_SUCCESS; } + + /* + This is a safety measure to ensure the internal buffer has been cleared so any leftover + does not get played the next time the device starts. Ideally this should be drained by + the backend first. + */ + pDevice->playback.intermediaryBufferLen = 0; + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = 0; } ma_mutex_unlock(&pDevice->startStopLock); @@ -49158,7 +49601,7 @@ static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_c } /* LPF */ - pHeapLayout->lpfOffset = pHeapLayout->sizeInBytes; + pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); { ma_result result; size_t lpfHeapSizeInBytes; @@ -50084,6 +50527,7 @@ MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_ result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); return result; } @@ -50388,6 +50832,23 @@ static ma_int32 ma_channel_converter_float_to_fixed(float x) return (ma_int32)(x * (1< 0); + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { + spatialChannelCount++; + } + } + + return spatialChannelCount; +} + static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) { int i; @@ -51117,6 +51578,26 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert /* We now need to fill out our weights table. This is determined by the mixing mode. */ + + /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); + + if (channelPosIn == channelPosOut) { + float weight = 1; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } + switch (pConverter->mixingMode) { case ma_channel_mix_mode_custom_weights: @@ -51140,19 +51621,10 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert case ma_channel_mix_mode_simple: { - /* In simple mode, excess channels need to be silenced or dropped. */ - ma_uint32 iChannel; - for (iChannel = 0; iChannel < ma_min(pConverter->channelsIn, pConverter->channelsOut); iChannel += 1) { - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannel][iChannel] == 0) { - pConverter->weights.f32[iChannel][iChannel] = 1; - } - } else { - if (pConverter->weights.s16[iChannel][iChannel] == 0) { - pConverter->weights.s16[iChannel][iChannel] = ma_channel_converter_float_to_fixed(1); - } - } - } + /* + In simple mode, only set weights for channels that have exactly matching types, leave the rest at + zero. The 1:1 mappings have already been covered before this switch statement. + */ } break; case ma_channel_mix_mode_rectangular: @@ -51160,12 +51632,12 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert { /* Unmapped input channels. */ for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn]; + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); if (ma_is_spatial_channel_position(channelPosIn)) { if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut]; + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); if (ma_is_spatial_channel_position(channelPosOut)) { float weight = 0; @@ -51191,12 +51663,12 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert /* Unmapped output channels. */ for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = pConverter->pChannelMapOut[iChannelOut]; + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); if (ma_is_spatial_channel_position(channelPosOut)) { if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = pConverter->pChannelMapIn[iChannelIn]; + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); if (ma_is_spatial_channel_position(channelPosIn)) { float weight = 0; @@ -51219,6 +51691,32 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert } } } + + /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ + if (pConfig->calculateLFEFromSpatialChannels) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { + ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); + ma_uint32 iChannelOutLFE; + + if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { + const float weightForLFE = 1.0f / spatialChannelCount; + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + if (ma_is_spatial_channel_position(channelPosIn)) { + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); + } + } + } + } + } + } + } } break; } } @@ -51720,6 +52218,7 @@ static ma_channel_converter_config ma_channel_converter_config_init_from_data_co channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); channelConverterConfig.ppWeights = pConfig->ppChannelWeights; + channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; return channelConverterConfig; } @@ -53643,18 +54142,128 @@ MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint3 } MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) +{ + return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); +} + +MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) { ma_uint32 iChannel; + if (pChannelIndex != NULL) { + *pChannelIndex = (ma_uint32)-1; + } + for (iChannel = 0; iChannel < channels; ++iChannel) { if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { + if (pChannelIndex != NULL) { + *pChannelIndex = iChannel; + } + return MA_TRUE; } } + /* Getting here means the channel position was not found. */ return MA_FALSE; } +MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) +{ + size_t len; + ma_uint32 iChannel; + + len = 0; + + for (iChannel = 0; iChannel < channels; iChannel += 1) { + const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); + size_t channelStrLen = strlen(pChannelStr); + + /* Append the string if necessary. */ + if (pBufferOut != NULL && bufferCap > len + channelStrLen) { + MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); + } + len += channelStrLen; + + /* Append a space if it's not the last item. */ + if (iChannel+1 < channels) { + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = ' '; + } + len += 1; + } + } + + /* Null terminate. Don't increment the length here. */ + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = '\0'; + } + + return len; +} + +MA_API const char* ma_channel_position_to_string(ma_channel channel) +{ + switch (channel) + { + case MA_CHANNEL_NONE : return "CHANNEL_NONE"; + case MA_CHANNEL_MONO : return "CHANNEL_MONO"; + case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; + case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; + case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; + case MA_CHANNEL_LFE : return "CHANNEL_LFE"; + case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; + case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; + case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER "; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; + case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; + case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; + case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; + case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; + case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; + case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; + case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; + case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; + case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; + case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; + case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; + case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; + case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; + case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; + case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; + case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; + case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; + case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; + case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; + case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; + case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; + case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; + case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; + case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; + case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; + case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; + case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; + case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; + case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; + case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; + case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; + case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; + case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; + case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; + case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; + case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; + case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; + case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; + case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; + case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; + case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; + case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; + default: break; + } + + return "UNKNOWN"; +} + /************************************************************************************************************************************************************** @@ -55067,7 +55676,8 @@ MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSourc return result; } - *pCursor = cursorInPCMFrames / (float)sampleRate; + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; return MA_SUCCESS; } @@ -55094,7 +55704,8 @@ MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSourc return result; } - *pLength = lengthInPCMFrames / (float)sampleRate; + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; return MA_SUCCESS; } @@ -57199,7 +57810,7 @@ extern "C" { #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 6 +#define DRWAV_VERSION_REVISION 7 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include typedef signed char drwav_int8; @@ -57734,7 +58345,7 @@ extern "C" { #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 38 +#define DRFLAC_VERSION_REVISION 39 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include typedef signed char drflac_int8; @@ -57863,14 +58474,12 @@ typedef enum drflac_seek_origin_start, drflac_seek_origin_current } drflac_seek_origin; -#pragma pack(2) typedef struct { drflac_uint64 firstPCMFrame; drflac_uint64 flacFrameOffset; drflac_uint16 pcmFrameCount; } drflac_seekpoint; -#pragma pack() typedef struct { drflac_uint16 minBlockSizeInPCMFrames; @@ -58057,14 +58666,12 @@ typedef struct drflac_uint32 countRemaining; const char* pRunningData; } drflac_cuesheet_track_iterator; -#pragma pack(4) typedef struct { drflac_uint64 offset; drflac_uint8 index; drflac_uint8 reserved[3]; } drflac_cuesheet_track_index; -#pragma pack() typedef struct { drflac_uint64 offset; @@ -58095,7 +58702,7 @@ extern "C" { #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 33 +#define DRMP3_VERSION_REVISION 34 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include typedef signed char drmp3_int8; @@ -58277,7 +58884,6 @@ typedef struct typedef struct { drmp3dec decoder; - drmp3dec_frame_info frameInfo; drmp3_uint32 channels; drmp3_uint32 sampleRate; drmp3_read_proc onRead; @@ -60095,6 +60701,7 @@ static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_back mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { + ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; } @@ -64078,10 +64685,15 @@ static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) { + ma_uint32 block; + + /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ + MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); + if (ma_is_little_endian()) { - return blocks[i]; + return block; } else { - return ma_swap_endian_uint32(blocks[i]); + return ma_swap_endian_uint32(block); } } @@ -68425,11 +69037,15 @@ static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* p static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) { + MA_ASSERT(pInputBus != NULL); + ma_spinlock_lock(&pInputBus->lock); } static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) { + MA_ASSERT(pInputBus != NULL); + ma_spinlock_unlock(&pInputBus->lock); } @@ -69980,8 +70596,9 @@ MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) ma_splitter_node_config config; MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.channels = channels; + config.nodeConfig = ma_node_config_init(); + config.channels = channels; + config.outputBusCount = 2; return config; } @@ -70012,9 +70629,9 @@ static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** pp static ma_node_vtable g_ma_splitter_node_vtable = { ma_splitter_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - 2, /* 2 output buses. */ + NULL, /* onGetRequiredInputFrameCount */ + 1, /* 1 input bus. */ + MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ 0 }; @@ -70023,7 +70640,8 @@ MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_split ma_result result; ma_node_config baseConfig; ma_uint32 pInputChannels[1]; - ma_uint32 pOutputChannels[2]; + ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; + ma_uint32 iOutputBus; if (pSplitterNode == NULL) { return MA_INVALID_ARGS; @@ -70035,15 +70653,21 @@ MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_split return MA_INVALID_ARGS; } + if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { + return MA_INVALID_ARGS; /* Too many output buses. */ + } + /* Splitters require the same number of channels between inputs and outputs. */ pInputChannels[0] = pConfig->channels; - pOutputChannels[0] = pConfig->channels; - pOutputChannels[1] = pConfig->channels; + for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { + pOutputChannels[iOutputBus] = pConfig->channels; + } baseConfig = pConfig->nodeConfig; baseConfig.vtable = &g_ma_splitter_node_vtable; baseConfig.pInputChannels = pInputChannels; baseConfig.pOutputChannels = pOutputChannels; + baseConfig.outputBusCount = pConfig->outputBusCount; result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); if (result != MA_SUCCESS) { @@ -70938,6 +71562,7 @@ MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) #endif /* MA_NO_NODE_GRAPH */ +/* SECTION: miniaudio_engine.c */ #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) /************************************************************************************************************************************************************** @@ -70955,6 +71580,7 @@ MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_e config.type = type; config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; + config.monoExpansionMode = pEngine->monoExpansionMode; return config; } @@ -71147,7 +71773,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut); } else { /* Channel conversion required. TODO: Add support for channel maps here. */ - ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->pEngine->monoExpansionMode); + ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); } } @@ -71496,6 +72122,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p pEngineNode->pEngine = pConfig->pEngine; pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); + pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; pEngineNode->pitch = 1; pEngineNode->oldPitch = 1; pEngineNode->oldDopplerPitch = 1; @@ -71629,10 +72256,22 @@ MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocati MA_API ma_sound_config ma_sound_config_init(void) +{ + return ma_sound_config_init_2(NULL); +} + +MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) { ma_sound_config config; MA_ZERO_OBJECT(&config); + + if (pEngine != NULL) { + config.monoExpansionMode = pEngine->monoExpansionMode; + } else { + config.monoExpansionMode = ma_mono_expansion_mode_default; + } + config.rangeEndInPCMFrames = ~((ma_uint64)0); config.loopPointEndInPCMFrames = ~((ma_uint64)0); @@ -71640,11 +72279,22 @@ MA_API ma_sound_config ma_sound_config_init(void) } MA_API ma_sound_group_config ma_sound_group_config_init(void) +{ + return ma_sound_group_config_init_2(NULL); +} + +MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) { ma_sound_group_config config; MA_ZERO_OBJECT(&config); + if (pEngine != NULL) { + config.monoExpansionMode = pEngine->monoExpansionMode; + } else { + config.monoExpansionMode = ma_mono_expansion_mode_default; + } + return config; } @@ -71746,6 +72396,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng deviceConfig.sampleRate = engineConfig.sampleRate; deviceConfig.dataCallback = ma_engine_data_callback_internal; deviceConfig.pUserData = pEngine; + deviceConfig.notificationCallback = engineConfig.notificationCallback; deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ @@ -72512,8 +73163,9 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con source that provides this information upfront. */ engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; + engineNodeConfig.channelsIn = pConfig->channelsIn; + engineNodeConfig.channelsOut = pConfig->channelsOut; + engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ if (pConfig->pDataSource != NULL) { @@ -72540,7 +73192,7 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con /* If no attachment is specified, attach the sound straight to the endpoint. */ if (pConfig->pInitialAttachment == NULL) { - /* No group. Attach straight to the endpoint by default, unless the caller has requested that do not. */ + /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); } @@ -72643,7 +73295,7 @@ done: MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) { - ma_sound_config config = ma_sound_config_init(); + ma_sound_config config = ma_sound_config_init_2(pEngine); config.pFilePath = pFilePath; config.flags = flags; config.pInitialAttachment = pGroup; @@ -72653,7 +73305,7 @@ MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePa MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) { - ma_sound_config config = ma_sound_config_init(); + ma_sound_config config = ma_sound_config_init_2(pEngine); config.pFilePathW = pFilePath; config.flags = flags; config.pInitialAttachment = pGroup; @@ -72695,10 +73347,11 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin return result; } - config = ma_sound_config_init(); + config = ma_sound_config_init_2(pEngine); config.pDataSource = pSound->pResourceManagerDataSource; config.flags = flags; config.pInitialAttachment = pGroup; + config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); if (result != MA_SUCCESS) { @@ -72714,7 +73367,7 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) { - ma_sound_config config = ma_sound_config_init(); + ma_sound_config config = ma_sound_config_init_2(pEngine); config.pDataSource = pDataSource; config.flags = flags; config.pInitialAttachment = pGroup; @@ -73458,7 +74111,7 @@ MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) { - ma_sound_group_config config = ma_sound_group_config_init(); + ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); config.flags = flags; config.pInitialAttachment = pParentGroup; return ma_sound_group_init_ex(pEngine, &config, pGroup); @@ -73760,6 +74413,7 @@ MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGr return ma_sound_get_time_in_pcm_frames(pGroup); } #endif /* MA_NO_ENGINE */ +/* END SECTION: miniaudio_engine.c */ @@ -73778,13 +74432,18 @@ code below please report the bug to the respective repository for the relevant p /* dr_wav_c begin */ #ifndef dr_wav_c #define dr_wav_c +#ifdef __MRC__ +#pragma options opt off +#endif #include #include #include #ifndef DR_WAV_NO_STDIO #include +#ifndef DR_WAV_NO_WCHAR #include #endif +#endif #ifndef DRWAV_ASSERT #include #define DRWAV_ASSERT(expression) assert(expression) @@ -76491,6 +77150,7 @@ DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, con #define DRWAV_HAS_WFOPEN #endif #endif +#ifndef DR_WAV_NO_WCHAR DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { @@ -76515,6 +77175,10 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, (void)pAllocationCallbacks; } #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -76547,12 +77211,14 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, *ppFile = fopen(pFilePathMB, pOpenModeMB); drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRWAV_ERROR; } #endif return DRWAV_SUCCESS; } +#endif DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); @@ -76593,6 +77259,7 @@ DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drw } return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); @@ -76605,6 +77272,7 @@ DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename } return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } +#endif DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; @@ -76613,6 +77281,7 @@ DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* fi } return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; @@ -76621,6 +77290,7 @@ DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_ } return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } +#endif DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav_bool32 result; @@ -76644,6 +77314,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const ch } return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; @@ -76652,6 +77323,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const } return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } +#endif DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); @@ -76667,6 +77339,7 @@ DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, } return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); @@ -76683,6 +77356,7 @@ DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } #endif +#endif DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { drwav* pWav = (drwav*)pUserData; @@ -78701,6 +79375,7 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filen } return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; @@ -78753,6 +79428,7 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif +#endif DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; @@ -78866,6 +79542,9 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) a[2] == b[2] && a[3] == b[3]; } +#ifdef __MRC__ +#pragma options opt reset +#endif #endif /* dr_wav_c end */ #endif /* DRWAV_IMPLEMENTATION */ @@ -79022,9 +79701,7 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) { #if defined(DRFLAC_SUPPORT_SSE41) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) - #if defined(DRFLAC_X64) - return DRFLAC_TRUE; - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__) + #if defined(__SSE4_1__) || defined(__AVX__) return DRFLAC_TRUE; #else #if defined(DRFLAC_NO_CPUID) @@ -79086,18 +79763,21 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); #pragma aux _watcom_bswap16 = \ "xchg al, ah" \ - parm [ax] \ - modify [ax]; + parm [ax] \ + value [ax] \ + modify nomemory; #pragma aux _watcom_bswap32 = \ - "bswap eax" \ - parm [eax] \ - modify [eax]; + "bswap eax" \ + parm [eax] \ + value [eax] \ + modify nomemory; #pragma aux _watcom_bswap64 = \ "bswap eax" \ "bswap edx" \ "xchg eax,edx" \ parm [eax edx] \ - modify [eax edx]; + value [eax edx] \ + modify nomemory; #endif #ifndef DRFLAC_ASSERT #include @@ -79189,6 +79869,9 @@ typedef drflac_int32 drflac_result; #define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 #define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 +#define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) { @@ -79962,6 +80645,10 @@ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) #if defined(__WATCOMC__) && defined(__386__) #define DRFLAC_IMPLEMENT_CLZ_WATCOM #endif +#ifdef __MRC__ +#include +#define DRFLAC_IMPLEMENT_CLZ_MRC +#endif static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) { drflac_uint32 n; @@ -79996,6 +80683,8 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) { #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) return DRFLAC_TRUE; +#elif defined(__MRC__) + return DRFLAC_TRUE; #else #ifdef DRFLAC_HAS_LZCNT_INTRINSIC return drflac__gIsLZCNTSupported; @@ -80076,6 +80765,13 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) #endif #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +#pragma aux drflac__clz_watcom_lzcnt = \ + "db 0F3h, 0Fh, 0BDh, 0C0h" \ + parm [eax] \ + value [eax] \ + modify nomemory; +#else #pragma aux drflac__clz_watcom = \ "bsr eax, eax" \ "xor eax, 31" \ @@ -80083,6 +80779,7 @@ static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); value [eax] \ modify exact [eax] nomemory; #endif +#endif static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT @@ -80093,8 +80790,12 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC return drflac__clz_msvc(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return drflac__clz_watcom_lzcnt(x); #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); +#elif defined(__MRC__) + return __cntlzw(x); #else return drflac__clz_software(x); #endif @@ -81261,7 +81962,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ int32x2_t shift64; uint32x4_t one128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = ~((~0UL) << riceParam); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s32(-shift); @@ -81401,8 +82102,11 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ int32x4_t riceParam128; int64x1_t shift64; uint32x4_t one128; + int64x2_t prediction128 = { 0 }; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = ~((~0UL) << riceParam); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s64(-shift); @@ -81458,9 +82162,6 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ coefficients128_8 = drflac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { - int64x2_t prediction128; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || @@ -82751,7 +83452,7 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -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* pSeektableSize, drflac_allocation_callbacks* pAllocationCallbacks) +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) { drflac_uint64 runningFilePos = 42; drflac_uint64 seektablePos = 0; @@ -82798,26 +83499,28 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d seektablePos = runningFilePos; seektableSize = blockSize; if (onMeta) { + drflac_uint32 seekpointCount; drflac_uint32 iSeekpoint; void* pRawData; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint); - metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; - for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { + for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = seekpointCount; + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } @@ -82882,8 +83585,10 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d void* pRawData; const char* pRunningData; const char* pRunningDataEnd; + size_t bufferSize; drflac_uint8 iTrack; drflac_uint8 iIndex; + void* pTrackData; pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; @@ -82900,29 +83605,61 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = pRunningData; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - drflac_uint8 indexCount; - drflac_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < 36) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - pRunningData += 35; - indexCount = pRunningData[0]; pRunningData += 1; - indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index); - if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - for (iIndex = 0; iIndex < indexCount; ++iIndex) { - drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData; - pRunningData += sizeof(drflac_cuesheet_track_index); - pTrack->offset = drflac__be2host_64(pTrack->offset); + metadata.data.cuesheet.pTrackData = NULL; + { + const char* pRunningDataSaved = pRunningData; + bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + drflac_uint32 indexPointSize; + if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + pRunningData += 35; + indexCount = pRunningData[0]; + pRunningData += 1; + bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); + indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + pRunningData += indexPointSize; } + pRunningData = pRunningDataSaved; + } + { + char* pRunningTrackData; + pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + if (pTrackData == NULL) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + pRunningTrackData = (char*)pTrackData; + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + indexCount = pRunningData[0]; + pRunningData += 1; + pRunningTrackData += 1; + for (iIndex = 0; iIndex < indexCount; ++iIndex) { + drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(drflac_cuesheet_track_index); + pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); + } + } + metadata.data.cuesheet.pTrackData = pTrackData; } - onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + pRawData = NULL; + onMeta(pUserDataMD, &metadata); + drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); + pTrackData = NULL; } } break; case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: @@ -82952,13 +83689,13 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; + metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; @@ -83020,9 +83757,9 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d break; } } - *pSeektablePos = seektablePos; - *pSeektableSize = seektableSize; - *pFirstFramePos = runningFilePos; + *pSeektablePos = seektablePos; + *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + *pFirstFramePos = runningFilePos; return DRFLAC_TRUE; } static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) @@ -83773,11 +84510,11 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac drflac_uint32 wholeSIMDVectorCountPerChannel; drflac_uint32 decodedSamplesAllocationSize; #ifndef DR_FLAC_NO_OGG - drflac_oggbs oggbs; + drflac_oggbs* pOggbs = NULL; #endif drflac_uint64 firstFramePos; drflac_uint64 seektablePos; - drflac_uint32 seektableSize; + drflac_uint32 seekpointCount; drflac_allocation_callbacks allocationCallbacks; drflac* pFlac; drflac__init_cpu_caps(); @@ -83807,22 +84544,24 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { allocationSize += sizeof(drflac_oggbs); - } - DRFLAC_ZERO_MEMORY(&oggbs, sizeof(oggbs)); - if (init.container == drflac_container_ogg) { - oggbs.onRead = onRead; - oggbs.onSeek = onSeek; - oggbs.pUserData = pUserData; - oggbs.currentBytePos = init.oggFirstBytePos; - oggbs.firstBytePos = init.oggFirstBytePos; - oggbs.serialNumber = init.oggSerial; - oggbs.bosPageHeader = init.oggBosHeader; - oggbs.bytesRemainingInPage = 0; + pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + if (pOggbs == NULL) { + return NULL; + } + DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + pOggbs->onRead = onRead; + pOggbs->onSeek = onSeek; + pOggbs->pUserData = pUserData; + pOggbs->currentBytePos = init.oggFirstBytePos; + pOggbs->firstBytePos = init.oggFirstBytePos; + pOggbs->serialNumber = init.oggSerial; + pOggbs->bosPageHeader = init.oggBosHeader; + pOggbs->bytesRemainingInPage = 0; } #endif - firstFramePos = 42; - seektablePos = 0; - seektableSize = 0; + firstFramePos = 42; + seektablePos = 0; + seekpointCount = 0; if (init.hasMetadataBlocks) { drflac_read_proc onReadOverride = onRead; drflac_seek_proc onSeekOverride = onSeek; @@ -83831,16 +84570,22 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac if (init.container == drflac_container_ogg) { onReadOverride = drflac__on_read_ogg; onSeekOverride = drflac__on_seek_ogg; - pUserDataOverride = (void*)&oggbs; + pUserDataOverride = (void*)pOggbs; } #endif - if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize, &allocationCallbacks)) { + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif return NULL; } - allocationSize += seektableSize; + allocationSize += seekpointCount * sizeof(drflac_seekpoint); } pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); if (pFlac == NULL) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif return NULL; } drflac__init_from_info(pFlac, &init); @@ -83848,8 +84593,10 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { - drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize); - DRFLAC_COPY_MEMORY(pInternalOggbs, &oggbs, sizeof(oggbs)); + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); + DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + pOggbs = NULL; pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; @@ -83867,21 +84614,22 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac #endif { if (seektablePos != 0) { - pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints); + pFlac->seekpointCount = seekpointCount; pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); DRFLAC_ASSERT(pFlac->bs.onRead != NULL); if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) { - drflac_uint32 iSeekpoint; - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + drflac_uint32 iSeekpoint; + for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + } else { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + break; } - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; } if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { drflac__free_from_callbacks(pFlac, &allocationCallbacks); @@ -83917,7 +84665,9 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac } #ifndef DR_FLAC_NO_STDIO #include +#ifndef DR_FLAC_NO_WCHAR #include +#endif #include static drflac_result drflac_result_from_errno(int e) { @@ -84361,6 +85111,7 @@ static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const ch #define DRFLAC_HAS_WFOPEN #endif #endif +#ifndef DR_FLAC_NO_WCHAR static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { @@ -84385,6 +85136,10 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons (void)pAllocationCallbacks; } #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -84417,12 +85172,14 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons *ppFile = fopen(pFilePathMB, pOpenModeMB); drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRFLAC_ERROR; } #endif return DRFLAC_SUCCESS; } +#endif static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); @@ -84446,6 +85203,7 @@ DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocati } return pFlac; } +#ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -84460,6 +85218,7 @@ DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_all } return pFlac; } +#endif DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -84474,6 +85233,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_ } return pFlac; } +#ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -84489,6 +85249,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, dr return pFlac; } #endif +#endif static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; @@ -87201,7 +87962,7 @@ DRMP3_API const char* drmp3_version_string(void) #if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) #define DR_MP3_ONLY_SIMD #endif -#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) +#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) #if defined(_MSC_VER) #include #endif @@ -87847,7 +88608,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n)) +#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) #define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } #define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } #define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) @@ -88363,7 +89124,7 @@ static void drmp3d_DCT_II(float *grbuf, int n) #if DRMP3_HAVE_SSE #define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) #else -#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v)) +#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) #endif for (i = 0; i < 7; i++, y += 4*18) { @@ -88379,7 +89140,7 @@ static void drmp3d_DCT_II(float *grbuf, int n) DRMP3_VSAVE2(3, t[3][7]); } else { -#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v) +#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) for (i = 0; i < 7; i++, y += 4*18) { drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); @@ -88879,7 +89640,7 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num #endif #define DRMP3_MIN_DATA_CHUNK_SIZE 16384 #ifndef DRMP3_DATA_CHUNK_SIZE -#define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4 +#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) #endif #define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) #define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) @@ -89711,6 +90472,10 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const (void)pAllocationCallbacks; } #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -89743,6 +90508,7 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const *ppFile = fopen(pFilePathMB, pOpenModeMB); drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRMP3_ERROR; } From e5d332dea23e65f66e7e7b279dc712afeb9404c9 Mon Sep 17 00:00:00 2001 From: nobytesgiven <80068279+nobytesgiven@users.noreply.github.com> Date: Mon, 24 Oct 2022 17:35:47 +0300 Subject: [PATCH 0121/1710] Fix bezier line breaking #2735 (#2767) * Fixed bezier line breaking #2735 * converted tabs to spaces * typo * Changed doubles to floats * removed heap allocations\ Co-authored-by: nobytesgiven --- src/rshapes.c | 70 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 7e4cad74b..2262ae978 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -58,6 +58,7 @@ #include // Required for: sinf(), asinf(), cosf(), acosf(), sqrtf(), fabsf() #include // Required for: FLT_EPSILON +#include // Required for: RL_FREE //---------------------------------------------------------------------------------- // Defines and Macros @@ -197,6 +198,8 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) Vector2 previous = startPos; Vector2 current = { 0 }; + Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) { // Cubic easing in-out @@ -204,12 +207,27 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)BEZIER_LINE_DIVISIONS); current.x = previous.x + (endPos.x - startPos.x)/ (float)BEZIER_LINE_DIVISIONS; - // TODO: Avoid drawing the line by pieces, it generates gaps for big thicks, - // Custom "triangle-strip" implementation should be used, check DrawTriangleStrip() for reference - DrawLineEx(previous, current, thick, color); + float dy = current.y-previous.y; + float dx = current.x-previous.x; + float size = 0.5*thick/sqrt(dx*dx+dy*dy); + + if (i==1) + { + points[0].x = previous.x+dy*size; + points[0].y = previous.y-dx*size; + points[1].x = previous.x-dy*size; + points[1].y = previous.y+dx*size; + } + + points[2*i+1].x = current.x-dy*size; + points[2*i+1].y = current.y+dx*size; + points[2*i].x = current.x+dy*size; + points[2*i].y = current.y-dx*size; previous = current; } + + DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); } // Draw line using quadratic bezier curves with a control point @@ -221,6 +239,8 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl Vector2 current = { 0 }; float t = 0.0f; + Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + for (int i = 0; i <= BEZIER_LINE_DIVISIONS; i++) { t = step*i; @@ -232,12 +252,27 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl current.y = a*startPos.y + b*controlPos.y + c*endPos.y; current.x = a*startPos.x + b*controlPos.x + c*endPos.x; - // TODO: Avoid drawing the line by pieces, it generates gaps for big thicks, - // Custom "triangle-strip" implementation should be used, check DrawTriangleStrip() for reference - DrawLineEx(previous, current, thick, color); + float dy = current.y-previous.y; + float dx = current.x-previous.x; + float size = 0.5*thick/sqrt(dx*dx+dy*dy); + + if (i==1) + { + points[0].x = previous.x+dy*size; + points[0].y = previous.y-dx*size; + points[1].x = previous.x-dy*size; + points[1].y = previous.y+dx*size; + } + + points[2*i+1].x = current.x-dy*size; + points[2*i+1].y = current.y+dx*size; + points[2*i].x = current.x+dy*size; + points[2*i].y = current.y-dx*size; previous = current; } + + DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); } // Draw line using cubic bezier curves with 2 control points @@ -249,6 +284,8 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP Vector2 current = { 0 }; float t = 0.0f; + Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + for (int i = 0; i <= BEZIER_LINE_DIVISIONS; i++) { t = step*i; @@ -260,12 +297,27 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y; current.x = a*startPos.x + b*startControlPos.x + c*endControlPos.x + d*endPos.x; - // TODO: Avoid drawing the line by pieces, it generates gaps for big thicks, - // Custom "triangle-strip" implementation should be used, check DrawTriangleStrip() for reference - DrawLineEx(previous, current, thick, color); + float dy = current.y-previous.y; + float dx = current.x-previous.x; + float size = 0.5*thick/sqrt(dx*dx+dy*dy); + + if (i==1) + { + points[0].x = previous.x+dy*size; + points[0].y = previous.y-dx*size; + points[1].x = previous.x-dy*size; + points[1].y = previous.y+dx*size; + } + + points[2*i+1].x = current.x-dy*size; + points[2*i+1].y = current.y+dx*size; + points[2*i].x = current.x+dy*size; + points[2*i].y = current.y-dx*size; previous = current; } + + DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); } // Draw lines sequence From 072e92615aad45777e667e7b29137095b815734a Mon Sep 17 00:00:00 2001 From: Ian Rash Date: Tue, 25 Oct 2022 01:51:40 -0700 Subject: [PATCH 0122/1710] Updated support for crystal (#2774) All fresh and current. --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index b2bced133..e52dee9b9 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -11,7 +11,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib-cs | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | **4.0** | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | -| raylib-cr | **4.0** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | +| raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | From dbecb950242e51f5a55da916590b0d187515189c Mon Sep 17 00:00:00 2001 From: nobytesgiven <80068279+nobytesgiven@users.noreply.github.com> Date: Tue, 25 Oct 2022 18:56:06 +0300 Subject: [PATCH 0123/1710] Added Box and Gaussian blurring (#2770) * Added Box and Gaussian blurring * Removed dependence of gaussian blur to box blur & Fixed precision errors Co-authored-by: nobytesgiven --- src/raylib.h | 1 + src/rtextures.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 4c353ac53..dc0275911 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1258,6 +1258,7 @@ RLAPI void ImageAlphaCrop(Image *image, float threshold); RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); // Clear alpha channel to desired color 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 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 diff --git a/src/rtextures.c b/src/rtextures.c index 569832ac2..5e6f344dd 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -194,6 +194,10 @@ #define PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD 50 // Threshold over 255 to set alpha as 0 #endif +#ifndef GAUSSIAN_BLUR_ITERATIONS + #define GAUSSIAN_BLUR_ITERATIONS 4 // Number of box blur iterations to approximate gaussian blur +#endif + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -1494,6 +1498,159 @@ void ImageAlphaPremultiply(Image *image) ImageFormat(image, format); } +// Apply box blur +void ImageBlurGaussian(Image *image, int blurSize) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + + ImageAlphaPremultiply(image); + + Color *pixels = LoadImageColors(*image); + Color *pixelsCopy = 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)); + + for (int i = 0; i < (image->height)*(image->width); i++) { + pixelsCopy1[i].x = pixels[i].r; + pixelsCopy1[i].y = pixels[i].g; + pixelsCopy1[i].z = pixels[i].b; + pixelsCopy1[i].w = pixels[i].a; + } + + // Repeated convolution of rectangular window signal by itself converges to a gaussian distribution + for (int j = 0; j < GAUSSIAN_BLUR_ITERATIONS; j++) { + // Horizontal motion blur + for (int row = 0; row < image->height; row++) + { + float avgR = 0.0f; + float avgG = 0.0f; + float avgB = 0.0f; + float avgAlpha = 0.0f; + int convolutionSize = blurSize+1; + + for (int i = 0; i < blurSize+1; i++) + { + avgR += pixelsCopy1[row*image->width + i].x; + avgG += pixelsCopy1[row*image->width + i].y; + avgB += pixelsCopy1[row*image->width + i].z; + avgAlpha += pixelsCopy1[row*image->width + i].w; + } + + pixelsCopy2[row*image->width].x = avgR/convolutionSize; + pixelsCopy2[row*image->width].y = avgG/convolutionSize; + pixelsCopy2[row*image->width].z = avgB/convolutionSize; + pixelsCopy2[row*image->width].w = avgAlpha/convolutionSize; + + for (int x = 1; x < image->width; x++) + { + if (x-blurSize >= 0) + { + avgR -= pixelsCopy1[row*image->width + x-blurSize].x; + avgG -= pixelsCopy1[row*image->width + x-blurSize].y; + avgB -= pixelsCopy1[row*image->width + x-blurSize].z; + avgAlpha -= pixelsCopy1[row*image->width + x-blurSize].w; + convolutionSize--; + } + + if (x+blurSize < image->width) + { + avgR += pixelsCopy1[row*image->width + x+blurSize].x; + avgG += pixelsCopy1[row*image->width + x+blurSize].y; + avgB += pixelsCopy1[row*image->width + x+blurSize].z; + avgAlpha += pixelsCopy1[row*image->width + x+blurSize].w; + convolutionSize++; + } + + pixelsCopy2[row*image->width + x].x = avgR/convolutionSize; + pixelsCopy2[row*image->width + x].y = avgG/convolutionSize; + pixelsCopy2[row*image->width + x].z = avgB/convolutionSize; + pixelsCopy2[row*image->width + x].w = avgAlpha/convolutionSize; + } + } + + // Vertical motion blur + for (int col = 0; col < image->width; col++) + { + float avgR = 0.0f; + float avgG = 0.0f; + float avgB = 0.0f; + float avgAlpha = 0.0f; + int convolutionSize = blurSize+1; + + for (int i = 0; i < blurSize+1; i++) + { + avgR += pixelsCopy2[i*image->width + col].x; + avgG += pixelsCopy2[i*image->width + col].y; + avgB += pixelsCopy2[i*image->width + col].z; + avgAlpha += pixelsCopy2[i*image->width + col].w; + } + + pixelsCopy1[col].x = (unsigned char) (avgR/convolutionSize); + pixelsCopy1[col].y = (unsigned char) (avgG/convolutionSize); + pixelsCopy1[col].z = (unsigned char) (avgB/convolutionSize); + pixelsCopy1[col].w = (unsigned char) (avgAlpha/convolutionSize); + + for (int y = 1; y < image->height; y++) + { + if (y-blurSize >= 0) + { + avgR -= pixelsCopy2[(y-blurSize)*image->width + col].x; + avgG -= pixelsCopy2[(y-blurSize)*image->width + col].y; + avgB -= pixelsCopy2[(y-blurSize)*image->width + col].z; + avgAlpha -= pixelsCopy2[(y-blurSize)*image->width + col].w; + convolutionSize--; + } + if (y+blurSize < image->height) + { + avgR += pixelsCopy2[(y+blurSize)*image->width + col].x; + avgG += pixelsCopy2[(y+blurSize)*image->width + col].y; + avgB += pixelsCopy2[(y+blurSize)*image->width + col].z; + avgAlpha += pixelsCopy2[(y+blurSize)*image->width + col].w; + convolutionSize++; + } + + pixelsCopy1[y*image->width + col].x = (unsigned char) (avgR/convolutionSize); + pixelsCopy1[y*image->width + col].y = (unsigned char) (avgG/convolutionSize); + pixelsCopy1[y*image->width + col].z = (unsigned char) (avgB/convolutionSize); + pixelsCopy1[y*image->width + col].w = (unsigned char) (avgAlpha/convolutionSize); + } + } + } + + + // Reverse premultiply + for (int i = 0; i < (image->width)*(image->height); i++) + { + if (pixelsCopy1[i].w == 0) + { + pixels[i].r = 0; + pixels[i].g = 0; + pixels[i].b = 0; + pixels[i].a = 0; + } + 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].a = (unsigned char) pixelsCopy1[i].w; + } + } + + int format = image->format; + RL_FREE(image->data); + RL_FREE(pixelsCopy1); + RL_FREE(pixelsCopy2); + + image->data = pixels; + image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + ImageFormat(image, format); +} + // Resize and image to new size // NOTE: Uses stb default scaling filters (both bicubic): // STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM From 865f823835779905d01d39fde3a4ebc1ea293f6a Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 Oct 2022 21:03:33 +0200 Subject: [PATCH 0124/1710] Review -s --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 26d8b43ea..0c3a1ea35 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -283,7 +283,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map - LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 -s --preload-file $(dir $<)resources@resources + LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 --preload-file $(dir $<)resources@resources # NOTE: Simple raylib examples are compiled to be interpreter with asyncify, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger From c4abf6835190218b86df3d433a93280a63496c1d Mon Sep 17 00:00:00 2001 From: nobytesgiven <80068279+nobytesgiven@users.noreply.github.com> Date: Wed, 26 Oct 2022 10:11:14 +0300 Subject: [PATCH 0125/1710] fixed blur issue on opaque pictures & added example (#2775) Co-authored-by: nobytesgiven --- examples/textures/textures_image_processing.c | 5 ++++- src/rtextures.c | 5 ++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/textures/textures_image_processing.c b/examples/textures/textures_image_processing.c index 7786ab214..49aacfc75 100644 --- a/examples/textures/textures_image_processing.c +++ b/examples/textures/textures_image_processing.c @@ -17,7 +17,7 @@ #include // Required for: free() -#define NUM_PROCESSES 8 +#define NUM_PROCESSES 9 typedef enum { NONE = 0, @@ -26,6 +26,7 @@ typedef enum { COLOR_INVERT, COLOR_CONTRAST, COLOR_BRIGHTNESS, + GAUSSIAN_BLUR, FLIP_VERTICAL, FLIP_HORIZONTAL } ImageProcess; @@ -37,6 +38,7 @@ static const char *processText[] = { "COLOR INVERT", "COLOR CONTRAST", "COLOR BRIGHTNESS", + "GAUSSIAN BLUR", "FLIP VERTICAL", "FLIP HORIZONTAL" }; @@ -125,6 +127,7 @@ int main(void) case COLOR_INVERT: ImageColorInvert(&imCopy); break; case COLOR_CONTRAST: ImageColorContrast(&imCopy, -40); break; case COLOR_BRIGHTNESS: ImageColorBrightness(&imCopy, -80); break; + case GAUSSIAN_BLUR: ImageBlurGaussian(&imCopy, 10); break; case FLIP_VERTICAL: ImageFlipVertical(&imCopy); break; case FLIP_HORIZONTAL: ImageFlipHorizontal(&imCopy); break; default: break; diff --git a/src/rtextures.c b/src/rtextures.c index 5e6f344dd..343daa181 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1506,7 +1506,6 @@ void ImageBlurGaussian(Image *image, int blurSize) { ImageAlphaPremultiply(image); Color *pixels = LoadImageColors(*image); - Color *pixelsCopy = LoadImageColors(*image); // Loop switches between pixelsCopy1 and pixelsCopy2 Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); @@ -1623,14 +1622,14 @@ void ImageBlurGaussian(Image *image, int blurSize) { // Reverse premultiply for (int i = 0; i < (image->width)*(image->height); i++) { - if (pixelsCopy1[i].w == 0) + if (pixelsCopy1[i].w == 0.0f) { pixels[i].r = 0; pixels[i].g = 0; pixels[i].b = 0; pixels[i].a = 0; } - else if (pixelsCopy1[i].w < 255.0f) + 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); From df4199e2c1fa6478a24ea49d56d9b60d0e39f8d3 Mon Sep 17 00:00:00 2001 From: Yunoinsky Date: Wed, 26 Oct 2022 15:12:14 +0800 Subject: [PATCH 0126/1710] Add ChezScheme support (#2776) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index e52dee9b9..21112b95d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -11,6 +11,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib-cs | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | **4.0** | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | +| chez-raylib | auto | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | From bc60812d68c51b37e1816660a6e4e82d6497732d Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:14:24 +0300 Subject: [PATCH 0127/1710] added raylib-python-ctypes bindings (#2780) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 21112b95d..7ca8e8d45 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -46,6 +46,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | | raylib-python-cffi | **4.2** | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | +| raylib-python-ctypes | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | **4.0** | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | From bcb47255b98773a1f5e810c5a1944ffa968d4d97 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:14:52 +0300 Subject: [PATCH 0128/1710] fixing typo (#2781) fixing typo --- examples/shapes/shapes_basic_shapes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/shapes/shapes_basic_shapes.c b/examples/shapes/shapes_basic_shapes.c index d06c2ee15..edc0b0c6b 100644 --- a/examples/shapes/shapes_basic_shapes.c +++ b/examples/shapes/shapes_basic_shapes.c @@ -51,7 +51,7 @@ int main(void) DrawCircleGradient(screenWidth/5, 220, 60, GREEN, SKYBLUE); DrawCircleLines(screenWidth/5, 340, 80, DARKBLUE); - // Rectangle shapes and ines + // Rectangle shapes and lines DrawRectangle(screenWidth/4*2 - 60, 100, 120, 60, RED); DrawRectangleGradientH(screenWidth/4*2 - 90, 170, 180, 130, MAROON, GOLD); DrawRectangleLines(screenWidth/4*2 - 40, 320, 80, 60, ORANGE); // NOTE: Uses QUADS internally, not lines @@ -83,4 +83,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} From 28e8b2add38098aee2b55243af4f3a62f2cd60c4 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 26 Oct 2022 18:04:20 +0200 Subject: [PATCH 0129/1710] REVIEWED: Issue with `OpenURL()` --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index d5823ad0b..b75b53b4e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3411,7 +3411,7 @@ void OpenURL(const char *url) else { #if defined(PLATFORM_DESKTOP) - char *cmd = (char *)RL_CALLOC(strlen(url) + 10, sizeof(char)); + char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char)); #if defined(_WIN32) sprintf(cmd, "explorer \"%s\"", url); #endif From 51138175078e56d22aee7703d083708df683cac5 Mon Sep 17 00:00:00 2001 From: nobytesgiven <80068279+nobytesgiven@users.noreply.github.com> Date: Wed, 26 Oct 2022 20:16:35 +0300 Subject: [PATCH 0130/1710] Improved billboards example, highlighting rotation and draw order (#2779) * Improved billboards example, highlighting rotation and draw order * changes to conform to the raylib conventions * NOW it conforms Co-authored-by: nobytesgiven --- examples/models/models_billboard.c | 39 +++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 68a24e6a7..0b5a42d57 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -12,6 +12,7 @@ ********************************************************************************************/ #include "raylib.h" +#include "raymath.h" //------------------------------------------------------------------------------------ // Program main entry point @@ -33,11 +34,28 @@ int main(void) camera.fovy = 45.0f; camera.projection = CAMERA_PERSPECTIVE; - Texture2D bill = LoadTexture("resources/billboard.png"); // Our texture billboard - Vector3 billPosition = { 0.0f, 2.0f, 0.0f }; // Position where draw billboard + Texture2D bill = LoadTexture("resources/billboard.png"); // Our billboard texture + Vector3 billPositionStatic = { 0.0f, 2.0f, 0.0f }; // Position of billboard + Vector3 billPositionRotating = { 1.0f, 2.0f, 1.0f }; + // 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 }; + + // Rotate around origin + // Here we choose to rotate around the image center + // NOTE: (-1, 1) is the range where origin.x, origin.y is inside the texture + Vector2 rotateOrigin = { 0.0f }; SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode + // 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 //-------------------------------------------------------------------------------------- @@ -47,6 +65,9 @@ int main(void) // Update //---------------------------------------------------------------------------------- UpdateCamera(&camera); + rotation += 0.4f; + distanceStatic = Vector3Distance(camera.position, billPositionStatic); + distanceRotating = Vector3Distance(camera.position, billPositionRotating); //---------------------------------------------------------------------------------- // Draw @@ -59,8 +80,18 @@ int main(void) DrawGrid(10, 1.0f); // Draw a grid - DrawBillboard(camera, bill, billPosition, 2.0f, WHITE); - + // Draw order matters! + if (distanceStatic > distanceRotating) + { + DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE); + DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, (Vector2) {1.0f, 1.0f}, rotateOrigin, rotation, WHITE); + } + else + { + DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, (Vector2) {1.0f, 1.0f}, rotateOrigin, rotation, WHITE); + DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE); + } + EndMode3D(); DrawFPS(10, 10); From d91f30958f5c9cb617c2738722c1966b8cd3ce67 Mon Sep 17 00:00:00 2001 From: Angga Permana <86430023+anggape@users.noreply.github.com> Date: Thu, 27 Oct 2022 16:38:25 +0700 Subject: [PATCH 0131/1710] Fix deprecation error on android api higher than 23 (#2778) --- cmake/LibraryConfigurations.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 1e60e8283..cc0f01e40 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -45,7 +45,6 @@ elseif (${PLATFORM} MATCHES "Android") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") set(CMAKE_POSITION_INDEPENDENT_CODE ON) list(APPEND raylib_sources ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) - add_definitions(-DANDROID -D__ANDROID_API__=21) include_directories(${ANDROID_NDK}/sources/android/native_app_glue) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs,libatomic.a -Wl,--build-id -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings -u ANativeActivity_onCreate -Wl,-undefined,dynamic_lookup") From 4c4a7038418de96c47bc34102fb61ea3551f510f Mon Sep 17 00:00:00 2001 From: IsaacTCB <116975602+IsaacTCB@users.noreply.github.com> Date: Sun, 30 Oct 2022 08:18:12 -0300 Subject: [PATCH 0132/1710] Fix Android x86 Architecture name (#2783) When building a x86 project, the folder inside lib is named i686. However Android x86 actually expects the folder to be called x86. --- examples/Makefile.Android | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile.Android b/examples/Makefile.Android index 5d76fb761..0570d8b23 100644 --- a/examples/Makefile.Android +++ b/examples/Makefile.Android @@ -48,7 +48,7 @@ ifeq ($(ANDROID_ARCH),ARM64) ANDROID_ARCH_NAME = arm64-v8a endif ifeq ($(ANDROID_ARCH),x86) - ANDROID_ARCH_NAME = i686 + ANDROID_ARCH_NAME = x86 endif ifeq ($(ANDROID_ARCH),x86_64) ANDROID_ARCH_NAME = x86_64 From 1cb81e3f4c30479d2d5e2194e7bf332ded750554 Mon Sep 17 00:00:00 2001 From: Roman Akberov Date: Wed, 2 Nov 2022 18:41:21 +0100 Subject: [PATCH 0133/1710] Fix examples/build.zig for the latest Zig version (#2786) --- examples/build.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/build.zig b/examples/build.zig index 3e6d68ade..cb211aefd 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -35,16 +35,16 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi else => @panic("Unsupported OS"), }); - exe.addIncludeDir("../src"); - exe.addIncludeDir("../src/external"); - exe.addIncludeDir("../src/external/glfw/include"); + exe.addIncludePath("../src"); + exe.addIncludePath("../src/external"); + exe.addIncludePath("../src/external/glfw/include"); switch (exe.target.toTarget().os.tag) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludeDir("external/glfw/deps/mingw"); + exe.addIncludePath("external/glfw/deps/mingw"); }, .linux => { exe.linkSystemLibrary("GL"); From dbdfad7ace061931da701070c5c7fbe772c57725 Mon Sep 17 00:00:00 2001 From: RGDTAB <117452902+RGDTAB@users.noreply.github.com> Date: Fri, 4 Nov 2022 15:39:04 -0400 Subject: [PATCH 0134/1710] Fix ExportDataAsCode() data types (#2787) --- src/raylib.h | 2 +- src/utils.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index dc0275911..11010f147 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1058,7 +1058,7 @@ RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success -RLAPI bool ExportDataAsCode(const char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success +RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, 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 diff --git a/src/utils.c b/src/utils.c index 208a483a7..b165449b4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -270,7 +270,7 @@ bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) } // Export data to code (.h), returns true on success -bool ExportDataAsCode(const char *data, unsigned int size, const char *fileName) +bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName) { bool success = false; From ca6f58eed1dfb74d93fb5ffe222cee61c20dde80 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 5 Nov 2022 00:31:13 +0100 Subject: [PATCH 0135/1710] Update rcore.c --- src/rcore.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b75b53b4e..54feb38b3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1937,18 +1937,6 @@ void SetClipboardText(const char *text) #endif } -// 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; -} - // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) @@ -1962,6 +1950,18 @@ const char *GetClipboardText(void) return NULL; } +// 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; +} + // Show mouse cursor void ShowCursor(void) { @@ -3288,6 +3288,9 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i // Decompress data from a valid DEFLATE stream data = 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 = RL_REALLOC(data, length); if (temp != NULL) data = temp; From 773c0d78d85ea3564b8bca1de97a89d9066f5b47 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Tue, 8 Nov 2022 22:47:05 +0200 Subject: [PATCH 0136/1710] removing typo (#2790) --- examples/audio/audio_music_stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/audio/audio_music_stream.c b/examples/audio/audio_music_stream.c index 44b1faf0c..f4819eb13 100644 --- a/examples/audio/audio_music_stream.c +++ b/examples/audio/audio_music_stream.c @@ -34,7 +34,7 @@ int main(void) float timePlayed = 0.0f; // Time played normalized [0.0f..1.0f] bool pause = false; // Music playing paused - SetTargetFPS(30); // Set our game to run at 60 frames-per-second + SetTargetFPS(30); // Set our game to run at 30 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -95,4 +95,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} From 311a57f9fcbb424ca7818bbdf591b564aa022b76 Mon Sep 17 00:00:00 2001 From: Martin Wickham Date: Wed, 9 Nov 2022 00:40:59 -0600 Subject: [PATCH 0137/1710] Add frameworks needed on macos (#2793) --- src/build.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/build.zig b/src/build.zig index 6a4c07e7e..e44271e8d 100644 --- a/src/build.zig +++ b/src/build.zig @@ -64,6 +64,10 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. raylib_flags ++ raylib_flags_extra_macos, ); raylib.linkFramework("Foundation"); + raylib.linkFramework("CoreServices"); + raylib.linkFramework("CoreGraphics"); + raylib.linkFramework("AppKit"); + raylib.linkFramework("IOKit"); }, else => { @panic("Unsupported OS"); From fca58c8e2f8768377fe0e535a08b99b6db40a5d6 Mon Sep 17 00:00:00 2001 From: red thing Date: Tue, 8 Nov 2022 22:42:58 -0800 Subject: [PATCH 0138/1710] dray is now 4.2.0 (#2792) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 7ca8e8d45..fac872e88 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -16,7 +16,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | -| dray | **4.0** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/xdrie/dray | +| dray | **4.2** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | | raylib-d | **4.2** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | From 84a2a8857229a696852bb97938e1ec2f7b1df892 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 10 Nov 2022 10:05:11 +0100 Subject: [PATCH 0139/1710] WARNING: REMOVED: `DrawTexturePoly()` Function moved to `examples/textures/textures_polygon.c`, so users can learn from the implementation and create custom variants as required. --- examples/textures/textures_polygon.c | 36 ++++++++++++++++++++++++++++ src/raylib.h | 1 - src/rtextures.c | 31 ------------------------ 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/examples/textures/textures_polygon.c b/examples/textures/textures_polygon.c index 357862c32..287029f73 100644 --- a/examples/textures/textures_polygon.c +++ b/examples/textures/textures_polygon.c @@ -14,10 +14,15 @@ ********************************************************************************************/ #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 //------------------------------------------------------------------------------------ @@ -102,3 +107,34 @@ int main(void) 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/src/raylib.h b/src/raylib.h index 11010f147..128711c99 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1325,7 +1325,6 @@ RLAPI void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Re RLAPI void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint); // Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters 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 -RLAPI void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint); // Draw a textured polygon // Color/pixel related functions RLAPI Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f diff --git a/src/rtextures.c b/src/rtextures.c index 343daa181..5440ed7cc 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3906,37 +3906,6 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, } } -// Draw textured polygon, defined by vertex and texturecoordinates -// 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); -} - // Get color with alpha applied, alpha goes from 0.0f to 1.0f Color Fade(Color color, float alpha) { From 7f68c65406a8321ec192adc6abc3142ac48eb071 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 10 Nov 2022 10:11:28 +0100 Subject: [PATCH 0140/1710] WARNING: REMOVED: `DrawTextureQuad()` This function can be easely replicated using `DrawtexturePro()` and actually it was doing some assumptions not transparent to the user. Even the function name was confusing. No example was available for it and actually noone requested one example. --- src/raylib.h | 5 ++--- src/rtextures.c | 13 ------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 128711c99..8274ff897 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1321,10 +1321,9 @@ RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters RLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle -RLAPI void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint); // Draw texture quad with tiling and offset parameters +RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters RLAPI void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint); // Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. -RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters -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 +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 Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f diff --git a/src/rtextures.c b/src/rtextures.c index 5440ed7cc..b367aa194 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3611,19 +3611,6 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 } } -// Draw texture quad with tiling and offset parameters -// NOTE: Tiling and offset should be provided considering normalized texture values [0..1] -// i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center -void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint) -{ - // WARNING: This solution only works if TEXTURE_WRAP_REPEAT is supported, - // NPOT textures supported is required and OpenGL ES 2.0 could not support it - Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height }; - Vector2 origin = { 0.0f, 0.0f }; - - DrawTexturePro(texture, source, quad, origin, 0.0f, tint); -} - // Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. // NOTE: For tilling a whole texture DrawTextureQuad() is better void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint) From 3888299bf5f50a828a26ce52e29db41a8764a193 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 10 Nov 2022 10:17:37 +0100 Subject: [PATCH 0141/1710] WARNING: REMOVED: `DrawTextureTiled()` This function implementation has been moved to the related example. Current implementation can be probably customized depending on user needs. --- examples/textures/textures_draw_tiled.c | 86 +++++++++++++++++++++++++ src/raylib.h | 3 +- src/rtextures.c | 85 ------------------------ 3 files changed, 87 insertions(+), 87 deletions(-) diff --git a/examples/textures/textures_draw_tiled.c b/examples/textures/textures_draw_tiled.c index 908bf238d..f68f23669 100644 --- a/examples/textures/textures_draw_tiled.c +++ b/examples/textures/textures_draw_tiled.c @@ -20,6 +20,9 @@ #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 //------------------------------------------------------------------------------------ @@ -168,3 +171,86 @@ int main(void) 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/src/raylib.h b/src/raylib.h index 8274ff897..6e549208e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1322,8 +1322,7 @@ RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters RLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters -RLAPI void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint); // Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. -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 +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 Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f diff --git a/src/rtextures.c b/src/rtextures.c index b367aa194..a23224a04 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3611,91 +3611,6 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 } } -// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. -// NOTE: For tilling a whole texture DrawTextureQuad() is better -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); - } - } - } -} - // Draws a texture (or part of it) that stretches or shrinks nicely using n-patch info void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint) { From 82e064419542d647d8eef7cf608ab92c9da33f51 Mon Sep 17 00:00:00 2001 From: Julianiolo <50519317+Julianiolo@users.noreply.github.com> Date: Thu, 10 Nov 2022 11:51:51 +0100 Subject: [PATCH 0142/1710] Fix Makefile emscripten path (#2785) --- src/Makefile | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Makefile b/src/Makefile index 291508edc..40a509bd4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -181,13 +181,15 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif ifeq ($(PLATFORM),PLATFORM_WEB) - # Emscripten required variables - EMSDK_PATH ?= C:/emsdk - EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten - CLANG_PATH = $(EMSDK_PATH)/upstream/bin - PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-1_64bit - NODE_PATH = $(EMSDK_PATH)/node/14.15.5_64bit/bin - export PATH := $(EMSDK_PATH):$(EMSCRIPTEN_PATH):$(CLANG_PATH):$(NODE_PATH):$(PYTHON_PATH):C:\raylib\MinGW\bin:$(PATH) + ifeq ($(PLATFORM_OS), WINDOWS) + # Emscripten required variables + EMSDK_PATH ?= C:/emsdk + EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten + CLANG_PATH := $(EMSDK_PATH)/upstream/bin + PYTHON_PATH := $(EMSDK_PATH)/python/3.9.2-1_64bit + NODE_PATH := $(EMSDK_PATH)/node/14.15.5_64bit/bin + export PATH := $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:/raylib/MinGW/bin;$(PATH) + endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) From 31edd13a72edd69993074941e013cbeff475f782 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 10 Nov 2022 12:03:17 +0100 Subject: [PATCH 0143/1710] Minor formating tweaks --- src/raylib.h | 6 +++--- src/rmodels.c | 31 ++++++++++++++++--------------- src/rtextures.c | 1 + 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 6e549208e..bebd3e018 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1439,11 +1439,11 @@ RLAPI void UnloadModelKeepMeshes(Model model); RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) // Model drawing functions -RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) +RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) 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 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 DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) +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 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 diff --git a/src/rmodels.c b/src/rmodels.c index 80205d499..2ca5ef09b 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2117,13 +2117,14 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) 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); + TRACELOG(LOG_WARNING, "MODEL: UpdateModelAnimation(): Mesh %i has no connection to bones", m); continue; } - bool updated = false; // set to true when anim vertex information is updated + bool updated = false; // Flag to check when anim vertex information is updated Vector3 animVertex = { 0 }; Vector3 animNormal = { 0 }; @@ -2140,13 +2141,13 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) float boneWeight = 0.0; const int vValues = mesh.vertexCount*3; - for (int vCounter = 0; vCounter < vValues; vCounter+=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) + if (mesh.animNormals != NULL) { mesh.animNormals[vCounter] = 0; mesh.animNormals[vCounter + 1] = 0; @@ -2157,16 +2158,15 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) 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; - } + + // 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; + //inScale = model.bindPose[boneId].scale; outTranslation = anim.framePoses[frame][boneId].translation; outRotation = anim.framePoses[frame][boneId].rotation; outScale = anim.framePoses[frame][boneId].scale; @@ -2178,7 +2178,7 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) animVertex = Vector3Subtract(animVertex, inTranslation); animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation))); animVertex = Vector3Add(animVertex, outTranslation); -// animVertex = Vector3Transform(animVertex, model.transform); + //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; @@ -2198,10 +2198,11 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) } // Upload new vertex data to GPU for model drawing - // 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 + // 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 } } } diff --git a/src/rtextures.c b/src/rtextures.c index a23224a04..6b8af71dd 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1297,6 +1297,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor); // Using nearest-neighbor scaling algorithm for default font + // TODO: Allow defining the preferred scaling mechanism externally // WARNING: Module required: rtext if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); From baabe22f7a4649015157e72abbd9a3be3088d44b Mon Sep 17 00:00:00 2001 From: Scott Helvick <4016577+shelvick@users.noreply.github.com> Date: Fri, 11 Nov 2022 08:23:02 -0800 Subject: [PATCH 0144/1710] Add Claylib (CL bindings + convenience layer) (#2796) --- BINDINGS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BINDINGS.md b/BINDINGS.md index fac872e88..ad01c48b1 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -11,6 +11,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib-cs | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | **4.0** | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | +| claylib/wrap | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | | chez-raylib | auto | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | @@ -70,6 +71,7 @@ These are utility wrappers for specific languages, they are not required to use | name | raylib version | language | license | repo | |:------------------:|:-------------: | :--------:|:-------:|:-------------------------------------------------------------| | raylib-cpp | **4.2** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | +| claylib | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | ### Older or Unmaintained Language Bindings These are older raylib bindings that are more than 2 versions old or have not been maintained. From 4bb71c8fa2e4965bf416f5dbf5838284cdf8a333 Mon Sep 17 00:00:00 2001 From: "Jorge A. Gomes" Date: Sat, 12 Nov 2022 14:56:54 -0300 Subject: [PATCH 0145/1710] Raylib-py updated to 4.2, plus parallel project (#2798) Now Raylib-py is a releases-only project. For now on, code maintenance will happen in a parallel project created to automate the binding generation: RaylibpyCtbg --- BINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index ad01c48b1..a73baa9b2 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -47,6 +47,8 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | | raylib-python-cffi | **4.2** | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | +| raylibpyctbg | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | +| raylib-py | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | | raylib-python-ctypes | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | @@ -103,8 +105,6 @@ These are older raylib bindings that are more than 2 versions old or have not be | raylib-ruby | 2.6 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/a0/raylib-ruby | | raylib-ruby-ffi | 2.0 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/D3nX/raylib-ruby-ffi | | raylib-mruby | 2.5-dev | [mruby](https://github.com/mruby/mruby) | https://github.com/lihaochen910/raylib-mruby | -| raylib-py-ctbg | 2.6 | [Python](https://www.python.org/) | https://github.com/overdev/raylib-py-ctbg | -| raylib-py | 2.0 | [Python](https://www.python.org/) | https://github.com/overdev/raylib-py | | raylib-java | 2.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | https://github.com/XoanaIO/raylib-java | | clj-raylib | 3.0 | [Clojure](https://clojure.org/) | https://github.com/lsevero/clj-raylib | | QuickJS-raylib | 3.0 | [QuickJS](https://bellard.org/quickjs/) | https://github.com/sntg-p/QuickJS-raylib | From fadc29d811de346218b4d03a41e3d90904043bd6 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 15 Nov 2022 12:16:28 +0100 Subject: [PATCH 0146/1710] WARNING: REMOVED: `DrawCubeTexture()`, `DrawCubeTextureRec()` Those two functions have been moved to a new example: `models_draw_cube_texture`. The reasons for this decision: - Function inflexibility: Many users with the need to draw a textured cube could need to customize the texture applied to every face, that function did not allow that kind of functionality. - rlgl functionality exposure: The implementation exposed will teach users how to implement custom textured triangles drawing. --- examples/models/models_draw_cube_texture.c | 245 +++++++++++++++++++ examples/models/models_draw_cube_texture.png | Bin 0 -> 63170 bytes src/raylib.h | 2 - src/rmodels.c | 145 ----------- 4 files changed, 245 insertions(+), 147 deletions(-) create mode 100644 examples/models/models_draw_cube_texture.c create mode 100644 examples/models/models_draw_cube_texture.png diff --git a/examples/models/models_draw_cube_texture.c b/examples/models/models_draw_cube_texture.c new file mode 100644 index 000000000..b36e968f4 --- /dev/null +++ b/examples/models/models_draw_cube_texture.c @@ -0,0 +1,245 @@ +/******************************************************************************************* +* +* 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 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, texture.height/2, texture.width/2, texture.height/2 }, + (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); +} \ No newline at end of file diff --git a/examples/models/models_draw_cube_texture.png b/examples/models/models_draw_cube_texture.png new file mode 100644 index 0000000000000000000000000000000000000000..45b3a13d1024093f09f9f64ea2b98d68b89303ac GIT binary patch literal 63170 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 z%0D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 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; - - rlSetTexture(texture.id); - - 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); -} - // Draw sphere void DrawSphere(Vector3 centerPos, float radius, Color color) { From 2a88dc9bb71cd6fb95986ade0fb50a07a3d1e4a7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 15 Nov 2022 12:22:26 +0100 Subject: [PATCH 0147/1710] Update linux_examples.yml --- .github/workflows/linux_examples.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux_examples.yml b/.github/workflows/linux_examples.yml index 3084af05c..3ed2dc796 100644 --- a/.github/workflows/linux_examples.yml +++ b/.github/workflows/linux_examples.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Environment run: | From 656f47b7cceded25af8dd7891d0fcf72fdbe742f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 15 Nov 2022 12:23:24 +0100 Subject: [PATCH 0148/1710] Update cmake.yml --- .github/workflows/cmake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index fc2885a94..0426bda50 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -36,7 +36,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory From 3c51d066f1f6e2a2035b74d4475aa2f9628eb1c3 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 15 Nov 2022 12:26:22 +0100 Subject: [PATCH 0149/1710] Avoid using `DrawCubeTexture()` --- examples/core/core_split_screen.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/examples/core/core_split_screen.c b/examples/core/core_split_screen.c index 21bd9615a..1f1f3967b 100644 --- a/examples/core/core_split_screen.c +++ b/examples/core/core_split_screen.c @@ -15,7 +15,6 @@ #include "raylib.h" -Texture2D textureGrid = { 0 }; Camera cameraPlayer1 = { 0 }; Camera cameraPlayer2 = { 0 }; @@ -32,8 +31,8 @@ void DrawScene(void) { for (float z = -count*spacing; z <= count*spacing; z += spacing) { - DrawCubeTexture(textureGrid, (Vector3) { x, 1.5f, z }, 1, 1, 1, GREEN); - DrawCubeTexture(textureGrid, (Vector3) { x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN); + DrawCube((Vector3) { x, 1.5f, z }, 1, 1, 1, LIME); + DrawCube((Vector3) { x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN); } } @@ -54,13 +53,6 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - split screen"); - // Generate a simple texture to use for trees - Image img = GenImageChecked(256, 256, 32, 32, DARKGRAY, WHITE); - textureGrid = LoadTextureFromImage(img); - UnloadImage(img); - SetTextureFilter(textureGrid, TEXTURE_FILTER_ANISOTROPIC_16X); - SetTextureWrap(textureGrid, TEXTURE_WRAP_CLAMP); - // Setup player 1 camera and screen cameraPlayer1.fovy = 45.0f; cameraPlayer1.up.y = 1.0f; @@ -151,7 +143,6 @@ int main(void) //-------------------------------------------------------------------------------------- UnloadRenderTexture(screenPlayer1); // Unload render texture UnloadRenderTexture(screenPlayer2); // Unload render texture - UnloadTexture(textureGrid); // Unload texture CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From c8fd93d35628545a22189f07c39e4cb821ddc1c2 Mon Sep 17 00:00:00 2001 From: Pere001 <68083904+Pere001@users.noreply.github.com> Date: Tue, 15 Nov 2022 12:29:19 +0100 Subject: [PATCH 0150/1710] Warning on GetRandomValue range limit (#2800) Added a comment explaining the range limitations of GetRandomValue. Added a run-time warning TRACELOG when GetRandomValue is called with an invalid range. --- src/rcore.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 54feb38b3..3fec0d42c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2816,6 +2816,13 @@ int GetRandomValue(int min, int max) max = min; min = tmp; } + + // 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. + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) + { + TRACELOG(LOG_WARNING, "Invalid GetRandomValue arguments. Range should not be higher than %i.", RAND_MAX); + } return (rand()%(abs(max - min) + 1) + min); } From 2761aa40dd7213e9ace68eb5f0aa82d8e505edcc Mon Sep 17 00:00:00 2001 From: jtainer <92753845+jtainer@users.noreply.github.com> Date: Tue, 15 Nov 2022 06:30:32 -0500 Subject: [PATCH 0151/1710] Added function rlCullFace (#2797) rlCullFace sets the face culling mode to RL_FRONT or RL_BACK which correspond to GL_FRONT and GL_BACK respectively. --- src/rlgl.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index 79c661cd9..eb6f9afef 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -498,6 +498,12 @@ typedef enum { RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer } rlFramebufferAttachTextureType; +// Face culling mode +typedef enum { + RL_FRONT = 0, + RL_BACK +} rlCullMode; + //------------------------------------------------------------------------------------ // Functions Declaration - Matrix operations //------------------------------------------------------------------------------------ @@ -578,6 +584,7 @@ RLAPI void rlEnableDepthMask(void); // Enable depth write RLAPI void rlDisableDepthMask(void); // Disable depth write RLAPI void rlEnableBackfaceCulling(void); // Enable backface culling RLAPI void rlDisableBackfaceCulling(void); // Disable backface culling +RLAPI void rlCullFace(rlCullMode mode); // Set face culling mode 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 @@ -1671,6 +1678,18 @@ void rlEnableBackfaceCulling(void) { glEnable(GL_CULL_FACE); } // Disable backface culling void rlDisableBackfaceCulling(void) { glDisable(GL_CULL_FACE); } +// Set face culling mode +void rlCullFace(rlCullMode mode) { + switch (mode) { + case RL_BACK: + glCullFace(GL_BACK); + break; + case RL_FRONT: + glCullFace(GL_FRONT); + break; + } +} + // Enable scissor test void rlEnableScissorTest(void) { glEnable(GL_SCISSOR_TEST); } From e6306e5e76f83c9cf0b2bfbb85ceffa4ed2926ee Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 15 Nov 2022 12:34:01 +0100 Subject: [PATCH 0152/1710] REVIEWED: `rlCullFace()` -> `rlSetCullFace()` Reviewed formating to follow raylib coding conventions. --- src/rlgl.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index eb6f9afef..edd74862c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -500,8 +500,8 @@ typedef enum { // Face culling mode typedef enum { - RL_FRONT = 0, - RL_BACK + RL_CULL_FACE_FRONT = 0, + RL_CULL_FACE_BACK } rlCullMode; //------------------------------------------------------------------------------------ @@ -584,7 +584,7 @@ RLAPI void rlEnableDepthMask(void); // Enable depth write RLAPI void rlDisableDepthMask(void); // Disable depth write RLAPI void rlEnableBackfaceCulling(void); // Enable backface culling RLAPI void rlDisableBackfaceCulling(void); // Disable backface culling -RLAPI void rlCullFace(rlCullMode mode); // Set face culling mode +RLAPI void rlSetCullFace(int mode); // Set face culling mode 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 @@ -1679,14 +1679,13 @@ void rlEnableBackfaceCulling(void) { glEnable(GL_CULL_FACE); } void rlDisableBackfaceCulling(void) { glDisable(GL_CULL_FACE); } // Set face culling mode -void rlCullFace(rlCullMode mode) { - switch (mode) { - case RL_BACK: - glCullFace(GL_BACK); - break; - case RL_FRONT: - glCullFace(GL_FRONT); - break; +void rlSetCullFace(int mode) +{ + switch (mode) + { + case RL_CULL_FACE_BACK: glCullFace(GL_BACK); break; + case RL_CULL_FACE_FRONT: glCullFace(GL_FRONT); break; + default: break; } } From 36bb57d1becc0bfa543e9ea5beb489a718cb237b Mon Sep 17 00:00:00 2001 From: Alex Macafee Date: Mon, 21 Nov 2022 23:14:20 +1100 Subject: [PATCH 0153/1710] Add raylib-vapi (#2804) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index a73baa9b2..6d1b00f2e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -61,6 +61,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | | raylib-umka | **4.2** | [Umka](https://github.com/vtereshkov/umka-lang) | Zlib | https://github.com/robloach/raylib-umka | | raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | +| raylib-vapi | **4.2** | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | | raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | | raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | From f6558fe6e0932863b83d2a6a49b8fc81b7354242 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 22 Nov 2022 01:02:54 +0100 Subject: [PATCH 0154/1710] Minor tweaks --- src/raylib.h | 4 ++-- src/rcore.c | 4 ++-- src/rlgl.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 1abce7173..c48380d82 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -857,8 +857,8 @@ typedef enum { BLEND_ADD_COLORS, // Blend textures adding colors (alternative) BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha - BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendMode()) - BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendModeSeparate()) + BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) } BlendMode; // Gesture diff --git a/src/rcore.c b/src/rcore.c index 3fec0d42c..fe1f20a0f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3190,9 +3190,9 @@ FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool // Unload directory filepaths void UnloadDirectoryFiles(FilePathList files) { - if (files.capacity > 0) + if (files.count > 0) { - for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]); + for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]); RL_FREE(files.paths); } diff --git a/src/rlgl.h b/src/rlgl.h index edd74862c..496f4c41f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1816,7 +1816,7 @@ void rlCheckErrors() void rlSetBlendMode(int mode) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.State.currentBlendMode != mode || ((mode == RL_BLEND_CUSTOM || mode == RL_BLEND_CUSTOM_SEPARATE) && RLGL.State.glCustomBlendModeModified)) + if ((RLGL.State.currentBlendMode != mode) || ((mode == RL_BLEND_CUSTOM || mode == RL_BLEND_CUSTOM_SEPARATE) && RLGL.State.glCustomBlendModeModified)) { rlDrawRenderBatch(RLGL.currentBatch); From c0010105c258096d190536c9043e67be18164413 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 22 Nov 2022 17:09:39 +0100 Subject: [PATCH 0155/1710] REVIEWED: UnloadDirectoryFiles() --- src/rcore.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index fe1f20a0f..06f9bbdd6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3188,14 +3188,12 @@ FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool } // Unload directory filepaths +// WARNING: files.count is not reseted to 0 after unloading void UnloadDirectoryFiles(FilePathList files) { - if (files.count > 0) - { - for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]); + for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]); - RL_FREE(files.paths); - } + RL_FREE(files.paths); } // Change working directory, returns true on success From c48de2d1afdeefa99c5ad7760ffe8873993c841c Mon Sep 17 00:00:00 2001 From: gtrxAC <59177874+gtrxAC@users.noreply.github.com> Date: Tue, 22 Nov 2022 19:16:44 +0200 Subject: [PATCH 0156/1710] Rename lighting_instanced shader (glsl100) to lighting_instancing (#2805) * JSON parser: Use array for function params (#2255) * Parser: follow C convention of type before name * Update file names in build scripts * Rename lighting_instanced shader to instancing --- .../glsl100/{lighting_instanced.vs => lighting_instancing.vs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/shaders/resources/shaders/glsl100/{lighting_instanced.vs => lighting_instancing.vs} (100%) diff --git a/examples/shaders/resources/shaders/glsl100/lighting_instanced.vs b/examples/shaders/resources/shaders/glsl100/lighting_instancing.vs similarity index 100% rename from examples/shaders/resources/shaders/glsl100/lighting_instanced.vs rename to examples/shaders/resources/shaders/glsl100/lighting_instancing.vs From 2c77b31e304688c4067503d70f539dbb9a496816 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Thu, 24 Nov 2022 01:58:31 +0200 Subject: [PATCH 0157/1710] Correct types for rlBindImageTexture (#2808) --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 496f4c41f..59c4880db 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -696,7 +696,7 @@ RLAPI void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned RLAPI unsigned int rlGetShaderBufferSize(unsigned int id); // Get SSBO buffer size // Buffer management -RLAPI void rlBindImageTexture(unsigned int id, unsigned int index, unsigned int format, int readonly); // Bind image texture +RLAPI void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool readonly); // Bind image texture // Matrix state management RLAPI Matrix rlGetMatrixModelview(void); // Get internal modelview matrix @@ -4055,7 +4055,7 @@ void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int de } // Bind image texture -void rlBindImageTexture(unsigned int id, unsigned int index, unsigned int format, int readonly) +void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool readonly) { #if defined(GRAPHICS_API_OPENGL_43) unsigned int glInternalFormat = 0, glFormat = 0, glType = 0; From 2fd6d7e8c09ac9a13d1e8a9d36741934cb6a1f09 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Thu, 24 Nov 2022 01:58:56 +0200 Subject: [PATCH 0158/1710] Use const for pointer float array (#2807) * Use const for pointer float array * missed a definition --- src/rlgl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 59c4880db..04fcf8f91 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -519,7 +519,7 @@ RLAPI void rlLoadIdentity(void); // Reset current matrix to RLAPI void rlTranslatef(float x, float y, float z); // Multiply the current matrix by a translation matrix RLAPI void rlRotatef(float angle, float x, float y, float z); // Multiply the current matrix by a rotation matrix RLAPI void rlScalef(float x, float y, float z); // Multiply the current matrix by a scaling matrix -RLAPI void rlMultMatrixf(float *matf); // Multiply the current matrix by another matrix +RLAPI void rlMultMatrixf(const float *matf); // Multiply the current matrix by another matrix RLAPI void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar); RLAPI void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar); RLAPI void rlViewport(int x, int y, int width, int height); // Set the viewport area @@ -1048,7 +1048,7 @@ void rlLoadIdentity(void) { glLoadIdentity(); } void rlTranslatef(float x, float y, float z) { glTranslatef(x, y, z); } void rlRotatef(float angle, float x, float y, float z) { glRotatef(angle, x, y, z); } void rlScalef(float x, float y, float z) { glScalef(x, y, z); } -void rlMultMatrixf(float *matf) { glMultMatrixf(matf); } +void rlMultMatrixf(const float *matf) { glMultMatrixf(matf); } #endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Choose the current matrix to be transformed @@ -1173,7 +1173,7 @@ void rlScalef(float x, float y, float z) } // Multiply the current matrix by another matrix -void rlMultMatrixf(float *matf) +void rlMultMatrixf(const float *matf) { // Matrix creation from array Matrix mat = { matf[0], matf[4], matf[8], matf[12], From fc5894e734e1f2996b5c946d9f29890240aa76be Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 28 Nov 2022 14:16:59 +0100 Subject: [PATCH 0159/1710] REVIEWED: Some compilation warnings (for strict rules) --- src/rcore.c | 12 +++++++----- src/rtext.c | 30 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 06f9bbdd6..22ce93384 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2755,8 +2755,9 @@ float GetFrameTime(void) // NOTE: On PLATFORM_DESKTOP, timer is initialized on glfwInit() double GetTime(void) { + double time = 0.0; #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return glfwGetTime(); // Elapsed time since glfwInit() + time = glfwGetTime(); // Elapsed time since glfwInit() #endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) @@ -2764,8 +2765,9 @@ double GetTime(void) clock_gettime(CLOCK_MONOTONIC, &ts); unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - return (double)(time - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() + time = (double)(time - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() #endif + return time; } // Setup window configuration flags (view FLAGS) @@ -3291,12 +3293,12 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i #if defined(SUPPORT_COMPRESSION_API) // Decompress data from a valid DEFLATE stream - data = RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1); + 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 = RL_REALLOC(data, length); + 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"); @@ -3322,7 +3324,7 @@ char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) *outputSize = 4*((dataSize + 2)/3); - char *encodedData = RL_MALLOC(*outputSize); + char *encodedData = (char *)RL_MALLOC(*outputSize); if (encodedData == NULL) return NULL; diff --git a/src/rtext.c b/src/rtext.c index 62996be6a..1eb8f4532 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -199,11 +199,11 @@ extern void LoadFontDefault(void) // Re-construct image from defaultFontData and generate OpenGL texture //---------------------------------------------------------------------- Image imFont = { - .data = calloc(128*128, 2), // 2 bytes per pixel (gray + alpha) + .data = RL_CALLOC(128*128, 2), // 2 bytes per pixel (gray + alpha) .width = 128, .height = 128, - .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA }; // Fill image.data with defaultFontData (convert from bit to pixel!) @@ -454,8 +454,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) .data = pixels, .width = image.width, .height = image.height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; // Set font with all data parsed from image @@ -620,11 +620,11 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz if (ch == 32) { Image imSpace = { - .data = calloc(chars[i].advanceX*fontSize, 2), + .data = RL_CALLOC(chars[i].advanceX*fontSize, 2), .width = chars[i].advanceX, .height = fontSize, - .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE }; chars[i].image = imSpace; @@ -896,7 +896,7 @@ bool ExportFontAsCode(Font font, const char *fileName) // Compress font image data int compDataSize = 0; - unsigned char *compData = CompressData(image.data, imageDataSize, &compDataSize); + unsigned char *compData = CompressData((const unsigned char *)image.data, imageDataSize, &compDataSize); // Save font image data (compressed) byteCount += sprintf(txtData + byteCount, "#define COMPRESSED_DATA_SIZE_FONT_%s %i\n\n", TextToUpper(fileNamePascal), compDataSize); @@ -1665,7 +1665,7 @@ int *LoadCodepoints(const char *text, int *count) int codepointCount = 0; // Allocate a big enough buffer to store as many codepoints as text bytes - int *codepoints = RL_CALLOC(textLength, sizeof(int)); + int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int)); for (int i = 0; i < textLength; codepointCount++) { @@ -1674,7 +1674,7 @@ int *LoadCodepoints(const char *text, int *count) } // Re-allocate buffer to the actual number of codepoints loaded - void *temp = RL_REALLOC(codepoints, codepointCount*sizeof(int)); + int *temp = (int *)RL_REALLOC(codepoints, codepointCount*sizeof(int)); if (temp != NULL) codepoints = temp; *count = codepointCount; @@ -1992,7 +1992,7 @@ static Font LoadBMFont(const char *fileName) if (lastSlash != NULL) { // NOTE: We need some extra space to avoid memory corruption on next allocations! - imPath = RL_CALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(imFileName) + 4, 1); + imPath = (char *)RL_CALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(imFileName) + 4, 1); memcpy(imPath, fileName, TextLength(fileName) - TextLength(lastSlash) + 1); memcpy(imPath + TextLength(fileName) - TextLength(lastSlash) + 1, imFileName, TextLength(imFileName)); } @@ -2006,11 +2006,11 @@ static Font LoadBMFont(const char *fileName) { // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel Image imFontAlpha = { - .data = calloc(imFont.width*imFont.height, 2), + .data = RL_CALLOC(imFont.width*imFont.height, 2), .width = imFont.width, .height = imFont.height, - .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA }; for (int p = 0, i = 0; p < (imFont.width*imFont.height*2); p += 2, i++) From bbf9935828553dd38633a034a3c961a08b15a0a6 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 28 Nov 2022 21:03:21 +0100 Subject: [PATCH 0160/1710] Update github workflows --- .github/workflows/android.yml | 2 +- .github/workflows/cmake.yml | 2 +- .github/workflows/linux.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/webassembly.yml | 2 +- .github/workflows/windows.yml | 2 +- .github/workflows/windows_examples.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index dcee5a46b..838d7a341 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -82,7 +82,7 @@ jobs: tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }} - name: Upload Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.RELEASE_NAME }}.tar.gz path: ./build/${{ env.RELEASE_NAME }}.tar.gz diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 0426bda50..0e13b8e8f 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -75,7 +75,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 59cbfe334..3c1ef62b8 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -94,7 +94,7 @@ jobs: tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }} - name: Upload Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.RELEASE_NAME }}.tar.gz path: ./build/${{ env.RELEASE_NAME }}.tar.gz diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 0bcdf9c0a..a9a036bd4 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -99,7 +99,7 @@ jobs: tar -czvf ${{ env.RELEASE_NAME }}.tar.gz ${{ env.RELEASE_NAME }} - name: Upload Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.RELEASE_NAME }}.tar.gz path: ./build/${{ env.RELEASE_NAME }}.tar.gz diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index b5d2ba6ca..198e88802 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -69,7 +69,7 @@ jobs: shell: cmd - name: Upload Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.RELEASE_NAME }}.zip path: ./build/${{ env.RELEASE_NAME }}.zip diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bdbe06887..55a2410d0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -116,7 +116,7 @@ jobs: shell: cmd - name: Upload Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: ${{ env.RELEASE_NAME }}.zip path: ./build/${{ env.RELEASE_NAME }}.zip diff --git a/.github/workflows/windows_examples.yml b/.github/workflows/windows_examples.yml index 136273991..19e23cf1b 100644 --- a/.github/workflows/windows_examples.yml +++ b/.github/workflows/windows_examples.yml @@ -22,7 +22,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1 From 66a2cdee4026379e169faa9e45b62b7ec3025d01 Mon Sep 17 00:00:00 2001 From: Daijiro Fukuda Date: Tue, 29 Nov 2022 06:35:44 +0900 Subject: [PATCH 0161/1710] Fix array out of range (#2814) This breaks other values of the struct. --- src/rcore.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 22ce93384..0f47420b2 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3516,7 +3516,7 @@ int GetKeyPressed(void) 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] = 0; + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount - 1] = 0; CORE.Input.Keyboard.keyPressedQueueCount--; } @@ -3538,7 +3538,7 @@ int GetCharPressed(void) 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] = 0; + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount - 1] = 0; CORE.Input.Keyboard.charPressedQueueCount--; } @@ -5372,7 +5372,7 @@ static void CharCallback(GLFWwindow *window, unsigned int key) // 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_KEY_PRESSED_QUEUE) + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) { // Add character to the queue CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; From 57bd84510fee9515788361b39ec4f3e3fb2cbaea Mon Sep 17 00:00:00 2001 From: Daijiro Fukuda Date: Tue, 29 Nov 2022 06:36:22 +0900 Subject: [PATCH 0162/1710] Fix wrong compile definition (#2815) --- cmake/CompileDefinitions.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake index 6c8d0d715..7193f5ffb 100644 --- a/cmake/CompileDefinitions.cmake +++ b/cmake/CompileDefinitions.cmake @@ -76,7 +76,7 @@ if (${CUSTOMIZE_BUILD}) target_compile_definitions("raylib" PUBLIC "MAX_KEY_PRESSED_QUEUE=16") target_compile_definitions("raylib" PUBLIC "STORAGE_DATA_FILE=\"storage.data\"") - target_compile_definitions("raylib" PUBLIC "MAX_KEY_PRESSED_QUEUE=16") + target_compile_definitions("raylib" PUBLIC "MAX_CHAR_PRESSED_QUEUE=16") target_compile_definitions("raylib" PUBLIC "MAX_DECOMPRESSION_SIZE=64") if (${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_33" OR ${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_11") From 50a716c0d91f90e73f1fe2b910b3f7426a0d1360 Mon Sep 17 00:00:00 2001 From: jtainer <92753845+jtainer@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:46:10 -0500 Subject: [PATCH 0163/1710] Updated rcore.c, renamed 'time' to 'nanoSeconds' (#2816) * Updated rcore.c, renamed 'time' to 'time_nsec' When PLATFORM_ANDROID, PLATFORM_RPI or PLATFORM_DRM were defined, there is a compilation error to redefinition of the variable 'time', so the second instance of 'time' was changed to 'time_nsec' which both fixes the name collision and more accurately describes what that variable represents. * Renamed 'time_nsec' to 'nanoSeconds' --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 0f47420b2..8f5b63b15 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2763,9 +2763,9 @@ double GetTime(void) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) struct timespec ts = { 0 }; clock_gettime(CLOCK_MONOTONIC, &ts); - unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - time = (double)(time - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() + time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() #endif return time; } From 2edf5a958484f8dc8fb6d3be14606852b696e379 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 29 Nov 2022 10:45:10 +0100 Subject: [PATCH 0164/1710] REVIEWED: Issue with shader linkage --- src/rcore.c | 11 +++++++---- src/rlgl.h | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8f5b63b15..118a5736b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2446,10 +2446,6 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) { Shader shader = { 0 }; - shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); - - // NOTE: All locations must be reseted to -1 (no location) - for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; shader.id = rlLoadShaderCode(vsCode, fsCode); @@ -2465,6 +2461,11 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) // vertex texcoord2 location = 5 // NOTE: If any location is not found, loc point becomes -1 + + shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); + + // All locations reseted to -1 (no location) + for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; // Get handles to GLSL input attibute locations shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION); @@ -2497,6 +2498,8 @@ void UnloadShader(Shader shader) if (shader.id != rlGetShaderIdDefault()) { rlUnloadShaderProgram(shader.id); + + // NOTE: If shader loading failed, it should be 0 RL_FREE(shader.locs); } } diff --git a/src/rlgl.h b/src/rlgl.h index 04fcf8f91..e4f65d69a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3643,12 +3643,14 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) // NOTE: We detach shader before deletion to make sure memory is freed if (vertexShaderId != RLGL.State.defaultVShaderId) { - glDetachShader(id, vertexShaderId); + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, vertexShaderId); glDeleteShader(vertexShaderId); } if (fragmentShaderId != RLGL.State.defaultFShaderId) { - glDetachShader(id, fragmentShaderId); + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, fragmentShaderId); glDeleteShader(fragmentShaderId); } From d241ee85166e720cc0d3bc777ea940e25e1f1d3c Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 29 Nov 2022 10:58:27 +0100 Subject: [PATCH 0165/1710] ADDED: Optional support for PNM images (.ppm, .pgm) --- CMakeOptions.txt | 1 + cmake/CompileDefinitions.cmake | 1 + src/config.h | 1 + src/rtextures.c | 11 ++++++++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index d76983391..326ed44e6 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -57,6 +57,7 @@ cmake_dependent_option(SUPPORT_IMAGE_MANIPULATION "Support multiple image editin cmake_dependent_option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_HDR "Support loading HDR as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_PNM "Support loading PNM as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_BMP "Support loading BMP as textures" ${OFF} CUSTOMIZE_BUILD OFF) diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake index 7193f5ffb..749222455 100644 --- a/cmake/CompileDefinitions.cmake +++ b/cmake/CompileDefinitions.cmake @@ -35,6 +35,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_FILEFORMAT_PNG) define_if("raylib" SUPPORT_FILEFORMAT_DDS) define_if("raylib" SUPPORT_FILEFORMAT_HDR) + define_if("raylib" SUPPORT_FILEFORMAT_PNM) define_if("raylib" SUPPORT_FILEFORMAT_KTX) define_if("raylib" SUPPORT_FILEFORMAT_ASTC) define_if("raylib" SUPPORT_FILEFORMAT_BMP) diff --git a/src/config.h b/src/config.h index a7ec2523f..82224e3a0 100644 --- a/src/config.h +++ b/src/config.h @@ -148,6 +148,7 @@ //#define SUPPORT_FILEFORMAT_PSD 1 #define SUPPORT_FILEFORMAT_DDS 1 #define SUPPORT_FILEFORMAT_HDR 1 +//#define SUPPORT_FILEFORMAT_PNM 1 //#define SUPPORT_FILEFORMAT_KTX 1 //#define SUPPORT_FILEFORMAT_ASTC 1 //#define SUPPORT_FILEFORMAT_PKM 1 diff --git a/src/rtextures.c b/src/rtextures.c index 6b8af71dd..19dd5f628 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -16,6 +16,7 @@ * #define SUPPORT_FILEFORMAT_PSD * #define SUPPORT_FILEFORMAT_PIC * #define SUPPORT_FILEFORMAT_HDR +* #define SUPPORT_FILEFORMAT_PNM * #define SUPPORT_FILEFORMAT_DDS * #define SUPPORT_FILEFORMAT_PKM * #define SUPPORT_FILEFORMAT_KTX @@ -103,6 +104,9 @@ #if !defined(SUPPORT_FILEFORMAT_HDR) #define STBI_NO_HDR #endif +#if !defined(SUPPORT_FILEFORMAT_PNM) + #define STBI_NO_PNM +#endif #if defined(SUPPORT_FILEFORMAT_DDS) #define RL_GPUTEX_SUPPORT_DDS @@ -135,7 +139,8 @@ defined(SUPPORT_FILEFORMAT_PSD) || \ defined(SUPPORT_FILEFORMAT_GIF) || \ defined(SUPPORT_FILEFORMAT_PIC) || \ - defined(SUPPORT_FILEFORMAT_HDR)) + defined(SUPPORT_FILEFORMAT_HDR) || \ + defined(SUPPORT_FILEFORMAT_PNM)) #define STBI_MALLOC RL_MALLOC #define STBI_FREE RL_FREE @@ -234,6 +239,7 @@ Image LoadImage(const char *fileName) defined(SUPPORT_FILEFORMAT_GIF) || \ defined(SUPPORT_FILEFORMAT_PIC) || \ defined(SUPPORT_FILEFORMAT_HDR) || \ + defined(SUPPORT_FILEFORMAT_PNM) || \ defined(SUPPORT_FILEFORMAT_PSD) #define STBI_REQUIRED @@ -344,6 +350,9 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #if defined(SUPPORT_FILEFORMAT_PIC) || (strcmp(fileType, ".pic") == 0) #endif +#if defined(SUPPORT_FILEFORMAT_PNM) + || ((strcmp(fileType, ".ppm") == 0) || (strcmp(fileType, ".pgm") == 0)) +#endif #if defined(SUPPORT_FILEFORMAT_PSD) || (strcmp(fileType, ".psd") == 0) #endif From 0b6d4b376ff8926c5916aace66b8778b768955b2 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 29 Nov 2022 17:28:23 +0100 Subject: [PATCH 0166/1710] REVIEWED: Image fileformat support: PIC, PNM --- src/config.h | 1 + src/rtextures.c | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/config.h b/src/config.h index 82224e3a0..d5f59a372 100644 --- a/src/config.h +++ b/src/config.h @@ -148,6 +148,7 @@ //#define SUPPORT_FILEFORMAT_PSD 1 #define SUPPORT_FILEFORMAT_DDS 1 #define SUPPORT_FILEFORMAT_HDR 1 +//#define SUPPORT_FILEFORMAT_PIC 1 //#define SUPPORT_FILEFORMAT_PNM 1 //#define SUPPORT_FILEFORMAT_KTX 1 //#define SUPPORT_FILEFORMAT_ASTC 1 diff --git a/src/rtextures.c b/src/rtextures.c index 19dd5f628..12c309d0a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -14,8 +14,8 @@ * #define SUPPORT_FILEFORMAT_GIF * #define SUPPORT_FILEFORMAT_QOI * #define SUPPORT_FILEFORMAT_PSD -* #define SUPPORT_FILEFORMAT_PIC * #define SUPPORT_FILEFORMAT_HDR +* #define SUPPORT_FILEFORMAT_PIC * #define SUPPORT_FILEFORMAT_PNM * #define SUPPORT_FILEFORMAT_DDS * #define SUPPORT_FILEFORMAT_PKM @@ -125,9 +125,6 @@ #endif // Image fileformats not supported by default -#define STBI_NO_PIC -#define STBI_NO_PNM // Image format .ppm and .pgm - #if defined(__TINYC__) #define STBI_NO_SIMD #endif @@ -138,8 +135,8 @@ defined(SUPPORT_FILEFORMAT_JPG) || \ defined(SUPPORT_FILEFORMAT_PSD) || \ defined(SUPPORT_FILEFORMAT_GIF) || \ - defined(SUPPORT_FILEFORMAT_PIC) || \ defined(SUPPORT_FILEFORMAT_HDR) || \ + defined(SUPPORT_FILEFORMAT_PIC) || \ defined(SUPPORT_FILEFORMAT_PNM)) #define STBI_MALLOC RL_MALLOC From 387c06000618ef0aa3b15c5e46d1c525ba194c50 Mon Sep 17 00:00:00 2001 From: Hristo Iliev Date: Wed, 30 Nov 2022 11:36:01 +0200 Subject: [PATCH 0167/1710] Fix an issue when compiling for web (#2820) It would try to use the glfw on the system but we're cross-compiling for web where the implementation is provided by emscripten's team --- src/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 21cd3d3f0..690f970a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,7 +38,9 @@ set(raylib_sources ) # /cmake/GlfwImport.cmake handles the details around the inclusion of glfw -include(GlfwImport) +if (NOT ${PLATFORM} MATCHES "Web") + include(GlfwImport) +endif () # Sets additional platform options and link libraries for each platform # also selects the proper graphics API and version for that platform @@ -68,6 +70,10 @@ else() endif () endif() +if (${PLATFORM} MATCHES "Web") + target_link_options(raylib PRIVATE "-sUSE_GLFW=3") +endif() + set_target_properties(raylib PROPERTIES PUBLIC_HEADER "${raylib_public_headers}" VERSION ${PROJECT_VERSION} From a4079ad565b8b44694ea8e36b30b2c26f4ade813 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 2 Dec 2022 11:59:43 +0100 Subject: [PATCH 0168/1710] Update Makefile --- examples/Makefile | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 0c3a1ea35..9afa48cb6 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -51,6 +51,13 @@ USE_EXTERNAL_GLFW ?= FALSE # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE +# PLATFORM_WEB: Default properties +BUILD_WEB_ASYNCIFY ?= FALSE +BUILD_WEB_SHELL ?= minshell.html +BUILD_WEB_HEAP_SIZE ?= 134217728 +BUILD_WEB_RESOURCES ?= TRUE +BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources + # Use cross-compiler for PLATFORM_RPI ifeq ($(PLATFORM),PLATFORM_RPI) USE_RPI_CROSS_COMPILER ?= FALSE @@ -195,9 +202,13 @@ ifeq ($(BUILD_MODE),DEBUG) endif else ifeq ($(PLATFORM),PLATFORM_WEB) - CFLAGS += -Os + ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) + CFLAGS += -O3 + else + CFLAGS += -Os + endif else - CFLAGS += -s -O1 + CFLAGS += -s -O2 endif endif @@ -283,16 +294,32 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map - LDFLAGS += -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 --preload-file $(dir $<)resources@resources + LDFLAGS += -s USE_GLFW=3 -s TOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -s FORCE_FILESYSTEM=1 + + # Build using asyncify + ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) + LDFLAGS += -s ASYNCIFY + endif + + # Add resources building if required + ifeq ($(BUILD_WEB_RESOURCES),TRUE) + LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) + endif + + # Add debug mode flags if required + ifeq ($(BUILD_MODE),DEBUG) + LDFLAGS += -s ASSERTIONS=1 --profiling + endif + # Define a custom shell .html and output extension + LDFLAGS += --shell-file $(BUILD_WEB_SHELL) + EXT = .html + # NOTE: Simple raylib examples are compiled to be interpreter with asyncify, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger # projects, code needs to be refactored to avoid a blocking while() loop, moving Update and Draw # logic to a self contained function: UpdateDrawFrame(), check core_basic_window_web.c for reference. - # Define a custom shell .html and output extension - LDFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html - EXT = .html endif ifeq ($(PLATFORM),PLATFORM_RPI) LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib From 5b5dff3f9e33ee53b18c92e94cc98c13d19c29c5 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 2 Dec 2022 11:59:54 +0100 Subject: [PATCH 0169/1710] format tweak --- src/rtextures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index 12c309d0a..82de04785 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3821,7 +3821,7 @@ Color Fade(Color color, float alpha) if (alpha < 0.0f) alpha = 0.0f; else if (alpha > 1.0f) alpha = 1.0f; - return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)}; + return (Color){ color.r, color.g, color.b, (unsigned char)(255.0f*alpha) }; } // Get hexadecimal value for a Color From 57dd345dc3ac95332d656a06ac110237699e3726 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sat, 3 Dec 2022 13:55:05 -0800 Subject: [PATCH 0170/1710] Add a textured curve example (#2821) --- examples/Makefile | 1 + .../textures/resources/roadTexture_01.png | Bin 0 -> 1006 bytes examples/textures/textures_textured_curve.c | 241 +++++++++++ examples/textures/textures_textured_curve.png | Bin 0 -> 21036 bytes .../examples/textures_textured_curve.vcxproj | 387 ++++++++++++++++++ projects/VS2022/raylib.sln | 19 + 6 files changed, 648 insertions(+) create mode 100644 examples/textures/resources/roadTexture_01.png create mode 100644 examples/textures/textures_textured_curve.c create mode 100644 examples/textures/textures_textured_curve.png create mode 100644 projects/VS2022/examples/textures_textured_curve.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 9afa48cb6..edfbab118 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -460,6 +460,7 @@ TEXTURES = \ textures/textures_sprite_anim \ textures/textures_sprite_button \ textures/textures_sprite_explosion \ + textures/textures_textured_curve \ textures/textures_bunnymark \ textures/textures_blend_modes \ textures/textures_draw_tiled \ diff --git a/examples/textures/resources/roadTexture_01.png b/examples/textures/resources/roadTexture_01.png new file mode 100644 index 0000000000000000000000000000000000000000..082f4cd2e0f45189431385d5ae9c67ffc2e193ea GIT binary patch literal 1006 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 bFq^~G`NAeyg|lH8>gVu5!vh)GwBY{>Js&=- literal 0 HcmV?d00001 diff --git a/projects/VS2022/examples/textures_textured_curve.vcxproj b/projects/VS2022/examples/textures_textured_curve.vcxproj new file mode 100644 index 000000000..9b989f72c --- /dev/null +++ b/projects/VS2022/examples/textures_textured_curve.vcxproj @@ -0,0 +1,387 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06} + Win32Proj + textures_textured_curve + 10.0 + textures_textured_curve + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index b4ab4201c..13d0df6de 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -263,6 +263,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_polygon", "example EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_stream_effects", "examples\audio_stream_effects.vcxproj", "{4A87569C-4BD3-4113-B4B9-573D65B3D3F8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_textured_curve", "examples\textures_textured_curve.vcxproj", "{769FF0C1-4424-4FA3-BC44-D7A7DA312A06}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -2207,6 +2209,22 @@ Global {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x64.Build.0 = Release|x64 {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x86.ActiveCfg = Release|Win32 {4A87569C-4BD3-4113-B4B9-573D65B3D3F8}.Release|x86.Build.0 = Release|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x64.ActiveCfg = Debug|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x64.Build.0 = Debug|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x86.ActiveCfg = Debug|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Debug|x86.Build.0 = Debug|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x64.ActiveCfg = Release|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x64.Build.0 = Release|x64 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x86.ActiveCfg = Release|Win32 + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2340,6 +2358,7 @@ Global {27B110CC-43C0-400A-89D9-245E681647D7} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {1DE84812-E143-4C4B-A61D-9267AAD55401} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {4A87569C-4BD3-4113-B4B9-573D65B3D3F8} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {769FF0C1-4424-4FA3-BC44-D7A7DA312A06} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 89698844a132321fb94402e20eb095cbd6462e78 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 4 Dec 2022 11:01:59 +0100 Subject: [PATCH 0171/1710] REVIEWED: Example: `textures_textured_curve` --- .../{roadTexture_01.png => road.png} | Bin examples/textures/textures_textured_curve.c | 288 ++++++++++-------- examples/textures/textures_textured_curve.png | Bin 21036 -> 47993 bytes 3 files changed, 153 insertions(+), 135 deletions(-) rename examples/textures/resources/{roadTexture_01.png => road.png} (100%) diff --git a/examples/textures/resources/roadTexture_01.png b/examples/textures/resources/road.png similarity index 100% rename from examples/textures/resources/roadTexture_01.png rename to examples/textures/resources/road.png diff --git a/examples/textures/textures_textured_curve.c b/examples/textures/textures_textured_curve.c index 1407f7388..276670972 100644 --- a/examples/textures/textures_textured_curve.c +++ b/examples/textures/textures_textured_curve.c @@ -2,145 +2,219 @@ * * raylib [textures] example - Draw a texture along a segmented curve * -* Example originally created with raylib 4.5 +* Example originally created with raylib 4.5-dev * * Example contributed by Jeffery Myers 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-2022 Jeffery Myers and Ramon Santamaria (@raysan5) +* Copyright (c) 2022 Jeffery Myers and Ramon Santamaria (@raysan5) * ********************************************************************************************/ #include "raylib.h" + #include "raymath.h" #include "rlgl.h" -Texture RoadTexture = { 0 }; +#include // Required for: powf() +#include // Required for: NULL -bool ShowCurve = false; +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static Texture texRoad = { 0 }; -float Width = 50; -int Segments = 24; +static bool showCurve = false; -Vector2 SP = { 0 }; -Vector2 SPTangent = { 0 }; +static float curveWidth = 50; +static int curveSegments = 24; -Vector2 EP = { 0 }; -Vector2 EPTangent = { 0 }; +static Vector2 curveStartPosition = { 0 }; +static Vector2 curveStartPositionTangent = { 0 }; -Vector2* Selected = NULL; +static Vector2 curveEndPosition = { 0 }; +static Vector2 curveEndPositionTangent = { 0 }; -void DrawCurve() +static Vector2 *curveSelectedPoint = NULL; + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +static void UpdateOptions(void); +static void UpdateCurve(void); +static void DrawCurve(void); +static void DrawTexturedCurve(void); + + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main() { - if (ShowCurve) - DrawLineBezierCubic(SP, EP, SPTangent, EPTangent, 2, BLUE); + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; - // draw the various control points and highlight where the mouse is - DrawLineV(SP, SPTangent, SKYBLUE); - DrawLineV(EP, EPTangent, PURPLE); - Vector2 mouse = GetMousePosition(); + SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT); + InitWindow(screenWidth, screenHeight, "raylib [textures] examples - textured curve"); - if (CheckCollisionPointCircle(mouse, SP, 6)) - DrawCircleV(SP, 7, YELLOW); - DrawCircleV(SP, 5, RED); + // Load the road texture + texRoad = LoadTexture("resources/road.png"); + SetTextureFilter(texRoad, TEXTURE_FILTER_BILINEAR); - if (CheckCollisionPointCircle(mouse, SPTangent, 6)) - DrawCircleV(SPTangent, 7, YELLOW); - DrawCircleV(SPTangent, 5, MAROON); + // Setup the curve + curveStartPosition = (Vector2){ 80, 100 }; + curveStartPositionTangent = (Vector2){ 100, 300 }; - if (CheckCollisionPointCircle(mouse, EP, 6)) - DrawCircleV(EP, 7, YELLOW); - DrawCircleV(EP, 5, GREEN); + curveEndPosition = (Vector2){ 700, 350 }; + curveEndPositionTangent = (Vector2){ 600, 100 }; - if (CheckCollisionPointCircle(mouse, EPTangent, 6)) - DrawCircleV(EPTangent, 7, YELLOW); - DrawCircleV(EPTangent, 5, DARKGREEN); + 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 + //---------------------------------------------------------------------------------- + UpdateCurve(); + UpdateOptions(); + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawTexturedCurve(); + DrawCurve(); + + 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(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(texRoad); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; } -void EditCurve() +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +static void DrawCurve(void) { - // if the mouse is not down, we are not editing the curve so clear the selection + if (showCurve) DrawLineBezierCubic(curveStartPosition, curveEndPosition, curveStartPositionTangent, curveEndPositionTangent, 2, BLUE); + + // Draw the various control points and highlight where the mouse is + DrawLineV(curveStartPosition, curveStartPositionTangent, SKYBLUE); + DrawLineV(curveEndPosition, curveEndPositionTangent, PURPLE); + Vector2 mouse = GetMousePosition(); + + if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) DrawCircleV(curveStartPosition, 7, YELLOW); + DrawCircleV(curveStartPosition, 5, RED); + + if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) DrawCircleV(curveStartPositionTangent, 7, YELLOW); + DrawCircleV(curveStartPositionTangent, 5, MAROON); + + if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) DrawCircleV(curveEndPosition, 7, YELLOW); + DrawCircleV(curveEndPosition, 5, GREEN); + + if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) DrawCircleV(curveEndPositionTangent, 7, YELLOW); + DrawCircleV(curveEndPositionTangent, 5, DARKGREEN); +} + +static void UpdateCurve(void) +{ + // If the mouse is not down, we are not editing the curve so clear the selection if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - Selected = NULL; + curveSelectedPoint = NULL; return; } - // if a point was selected, move it - if (Selected) + // If a point was selected, move it + if (curveSelectedPoint) { - *Selected = Vector2Add(*Selected, GetMouseDelta()); + *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta()); return; } - // the mouse is down, and nothing was selected, so see if anything was picked + // The mouse is down, and nothing was selected, so see if anything was picked Vector2 mouse = GetMousePosition(); - if (CheckCollisionPointCircle(mouse, SP, 6)) - Selected = &SP; - else if (CheckCollisionPointCircle(mouse, SPTangent, 6)) - Selected = &SPTangent; - else if (CheckCollisionPointCircle(mouse, EP, 6)) - Selected = &EP; - else if (CheckCollisionPointCircle(mouse, EPTangent, 6)) - Selected = &EPTangent; + if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) curveSelectedPoint = &curveStartPosition; + else if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) curveSelectedPoint = &curveStartPositionTangent; + else if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) curveSelectedPoint = &curveEndPosition; + else if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) curveSelectedPoint = &curveEndPositionTangent; } -void DrawTexturedCurve() +static void DrawTexturedCurve(void) { - const float step = 1.0f / Segments; + const float step = 1.0f/curveSegments; - Vector2 previous = SP; + Vector2 previous = curveStartPosition; Vector2 previousTangent = { 0 }; float previousV = 0; - // we can't compute a tangent for the first point, so we need to reuse the tangent from the first segment + // We can't compute a tangent for the first point, so we need to reuse the tangent from the first segment bool tangentSet = false; Vector2 current = { 0 }; float t = 0.0f; - for (int i = 1; i <= Segments; i++) + for (int i = 1; i <= curveSegments; i++) { - // segment the curve - t = step * i; + // Segment the curve + t = step*i; float a = powf(1 - t, 3); - float b = 3 * powf(1 - t, 2) * t; - float c = 3 * (1 - t) * powf(t, 2); + float b = 3*powf(1 - t, 2)*t; + float c = 3*(1 - t)*powf(t, 2); float d = powf(t, 3); - // compute the endpoint for this segment - current.y = a * SP.y + b * SPTangent.y + c * EPTangent.y + d * EP.y; - current.x = a * SP.x + b * SPTangent.x + c * EPTangent.x + d * EP.x; + // Compute the endpoint for this segment + current.y = a*curveStartPosition.y + b*curveStartPositionTangent.y + c*curveEndPositionTangent.y + d*curveEndPosition.y; + current.x = a*curveStartPosition.x + b*curveStartPositionTangent.x + c*curveEndPositionTangent.x + d*curveEndPosition.x; - // vector from previous to current + // Vector from previous to current Vector2 delta = { current.x - previous.x, current.y - previous.y }; - // the right hand normal to the delta vector + // The right hand normal to the delta vector 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) + // The v texture coordinate of the segment (add up the length of all the segments so far) float v = previousV + Vector2Length(delta); - // make sure the start point has a normal + // Make sure the start point has a normal if (!tangentSet) { previousTangent = normal; tangentSet = true; } - // extend out the normals from the previous and current points to get the quad for this segment - Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, Width)); - Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -Width)); + // Extend out the normals from the previous and current points to get the quad for this segment + Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, curveWidth)); + Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -curveWidth)); - Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, Width)); - Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -Width)); + Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, curveWidth)); + Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -curveWidth)); - // draw the segment as a quad - rlSetTexture(RoadTexture.id); + // Draw the segment as a quad + rlSetTexture(texRoad.id); rlBegin(RL_QUADS); rlColor4ub(255,255,255,255); @@ -160,82 +234,26 @@ void DrawTexturedCurve() rlEnd(); - // the current step is the start of the next step + // The current step is the start of the next step previous = current; previousTangent = normal; previousV = v; } } -void UpdateOptions() +static void UpdateOptions(void) { - if (IsKeyPressed(KEY_SPACE)) - ShowCurve = !ShowCurve; + if (IsKeyPressed(KEY_SPACE)) showCurve = !showCurve; - // width - if (IsKeyPressed(KEY_EQUAL)) - Width += 2; + // Update with + if (IsKeyPressed(KEY_EQUAL)) curveWidth += 2; + if (IsKeyPressed(KEY_MINUS)) curveWidth -= 2; - if (IsKeyPressed(KEY_MINUS)) - Width -= 2; + if (curveWidth < 2) curveWidth = 2; - if (Width < 2) - Width = 2; + // Update segments + if (IsKeyPressed(KEY_LEFT)) curveSegments -= 2; + if (IsKeyPressed(KEY_RIGHT)) curveSegments += 2; - // segments - - if (IsKeyPressed(KEY_LEFT_BRACKET)) - Segments -= 2; - - if (IsKeyPressed(KEY_RIGHT_BRACKET)) - Segments += 2; - - if (Segments < 2) - Segments = 2; + if (curveSegments < 2) curveSegments = 2; } - -int main () -{ - // set up the window - SetConfigFlags(FLAG_VSYNC_HINT); - InitWindow(1280, 800, "raylib [textures] examples - textured curve"); - SetTargetFPS(144); - - // load the road texture - RoadTexture = LoadTexture("resources/roadTexture_01.png"); - - // setup the curve - SP = (Vector2){ 80, 400 }; - SPTangent = (Vector2){ 600, 100 }; - - EP = (Vector2){ 1200, 400 }; - EPTangent = (Vector2){ 600, 700 }; - - // game loop - while (!WindowShouldClose()) - { - EditCurve(); - UpdateOptions(); - - BeginDrawing(); - - ClearBackground(BLACK); - - DrawTexturedCurve(); - DrawCurve(); - - DrawText("Drag points to move curve, press space to show/hide base curve", 10, 0, 20, WHITE); - DrawText(TextFormat("Width %2.0f + and - to adjust", Width), 10, 20, 20, WHITE); - DrawText(TextFormat("Segments %d [ and ] to adjust", Segments), 10, 40, 20, WHITE); - DrawFPS(10, 60); - - EndDrawing(); - } - - // cleanup - UnloadTexture(RoadTexture); - CloseWindow(); - return 0; -} - - \ No newline at end of file diff --git a/examples/textures/textures_textured_curve.png b/examples/textures/textures_textured_curve.png index 1f45e8d2544ac297ea274c953ff74a2007432b4f..6d5efacb6c563305ee76169643fe9780eb959c10 100644 GIT binary patch literal 47993 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 bFq^~G`NAeyg|lH8>gVu5!vh)GwBY{>Js&=- From 4de64f5750eacd5d7acfb7b41d5b9f251cb502fb Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 4 Dec 2022 13:22:37 +0100 Subject: [PATCH 0172/1710] Expose OpenGL blending mode factors and functions/equations --- src/rlgl.h | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index e4f65d69a..301bd976d 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -264,7 +264,7 @@ #define RL_UNSIGNED_BYTE 0x1401 // GL_UNSIGNED_BYTE #define RL_FLOAT 0x1406 // GL_FLOAT -// Buffer usage hint +// GL buffer usage hint #define RL_STREAM_DRAW 0x88E0 // GL_STREAM_DRAW #define RL_STREAM_READ 0x88E1 // GL_STREAM_READ #define RL_STREAM_COPY 0x88E2 // GL_STREAM_COPY @@ -280,6 +280,37 @@ #define RL_VERTEX_SHADER 0x8B31 // GL_VERTEX_SHADER #define RL_COMPUTE_SHADER 0x91B9 // GL_COMPUTE_SHADER +// GL blending factors +#define RL_ZERO 0 // GL_ZERO +#define RL_ONE 1 // GL_ONE +#define RL_SRC_COLOR 0x0300 // GL_SRC_COLOR +#define RL_ONE_MINUS_SRC_COLOR 0x0301 // GL_ONE_MINUS_SRC_COLOR +#define RL_SRC_ALPHA 0x0302 // GL_SRC_ALPHA +#define RL_ONE_MINUS_SRC_ALPHA 0x0303 // GL_ONE_MINUS_SRC_ALPHA +#define RL_DST_ALPHA 0x0304 // GL_DST_ALPHA +#define RL_ONE_MINUS_DST_ALPHA 0x0305 // GL_ONE_MINUS_DST_ALPHA +#define RL_DST_COLOR 0x0306 // GL_DST_COLOR +#define RL_ONE_MINUS_DST_COLOR 0x0307 // GL_ONE_MINUS_DST_COLOR +#define RL_SRC_ALPHA_SATURATE 0x0308 // GL_SRC_ALPHA_SATURATE +#define RL_CONSTANT_COLOR 0x8001 // GL_CONSTANT_COLOR +#define RL_ONE_MINUS_CONSTANT_COLOR 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR +#define RL_CONSTANT_ALPHA 0x8003 // GL_CONSTANT_ALPHA +#define RL_ONE_MINUS_CONSTANT_ALPHA 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA + +// GL blending functions/equations +#define RL_FUNC_ADD 0x8006 // GL_FUNC_ADD +#define RL_FUNC_SUBTRACT 0x800A // GL_FUNC_SUBTRACT +#define RL_FUNC_REVERSE_SUBTRACT 0x800B // GL_FUNC_REVERSE_SUBTRACT +#define RL_BLEND_EQUATION 0x8009 // GL_BLEND_EQUATION +#define RL_BLEND_EQUATION_RGB 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION) +#define RL_BLEND_EQUATION_ALPHA 0x883D // GL_BLEND_EQUATION_ALPHA +#define RL_BLEND_DST_RGB 0x80C8 // GL_BLEND_DST_RGB +#define RL_BLEND_SRC_RGB 0x80C9 // GL_BLEND_SRC_RGB +#define RL_BLEND_DST_ALPHA 0x80CA // GL_BLEND_DST_ALPHA +#define RL_BLEND_SRC_ALPHA 0x80CB // GL_BLEND_SRC_ALPHA +#define RL_BLEND_COLOR 0x8005 // GL_BLEND_COLOR + + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- From f1368c36dd38dd912782485ce827fb8b35b86365 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 5 Dec 2022 00:24:55 +0100 Subject: [PATCH 0173/1710] ADDED: `ColorBrightness()` --- src/raylib.h | 1 + src/rtextures.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index c48380d82..6642bc8d3 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1331,6 +1331,7 @@ RLAPI Vector4 ColorNormalize(Color color); // G 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] RLAPI Color ColorFromHSV(float hue, float saturation, float value); // Get a Color from HSV values, hue [0..360], saturation/value [0..1] +RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from 0.0f to 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 GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value diff --git a/src/rtextures.c b/src/rtextures.c index 82de04785..66434c116 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3944,6 +3944,39 @@ Color ColorFromHSV(float hue, float saturation, float value) return color; } +// Get color with brightness correction, brightness factor goes from 0.0f to 1.0f +Color ColorBrightness(Color color, float factor) +{ + Color result = color; + + if (factor > 1.0f) factor = 1.0f; + else if (factor < -1.0f) factor = -1.0f; + + float red = (float)color.r; + float green = (float)color.g; + float blue = (float)color.b; + + if (factor < 0.0f) + { + factor = 1.0f + factor; + red *= factor; + green *= factor; + blue *= factor; + } + else + { + red = (255 - red)*factor + red; + green = (255 - green)*factor + green; + blue = (255 - blue)*factor + blue; + } + + result.r = (unsigned char)red; + result.g = (unsigned char)green; + result.b = (unsigned char)blue; + + return result; +} + // Get color with alpha applied, alpha goes from 0.0f to 1.0f Color ColorAlpha(Color color, float alpha) { From 2c9d116a5ce835328dc3267313f1b34b2e7ad8c9 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 7 Dec 2022 12:52:42 +0100 Subject: [PATCH 0174/1710] ADDED: `ColorTint()`, `ColorContrast()` --- src/raylib.h | 4 ++- src/rtextures.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 6642bc8d3..dc71af847 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1331,7 +1331,9 @@ RLAPI Vector4 ColorNormalize(Color color); // G 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] RLAPI Color ColorFromHSV(float hue, float saturation, float value); // Get a Color from HSV values, hue [0..360], saturation/value [0..1] -RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from 0.0f to 1.0f +RLAPI Color ColorTint(Color color, Color tint); // Get color multiplied with another color +RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f +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 GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value diff --git a/src/rtextures.c b/src/rtextures.c index 66434c116..3313a06f1 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3944,7 +3944,30 @@ Color ColorFromHSV(float hue, float saturation, float value) return color; } -// Get color with brightness correction, brightness factor goes from 0.0f to 1.0f +// Get color multiplied with another color +Color ColorTint(Color color, Color tint) +{ + Color result = color; + + float cR = (float)tint.r/255; + float cG = (float)tint.g/255; + float cB = (float)tint.b/255; + float cA = (float)tint.a/255; + + unsigned char r = (unsigned char)(((float)color.r/255*cR)*255.0f); + unsigned char g = (unsigned char)(((float)color.g/255*cG)*255.0f); + unsigned char b = (unsigned char)(((float)color.b/255*cB)*255.0f); + unsigned char a = (unsigned char)(((float)color.a/255*cA)*255.0f); + + result.r = r; + result.g = g; + result.b = b; + result.a = a; + + return result; +} + +// Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Color ColorBrightness(Color color, float factor) { Color result = color; @@ -3977,6 +4000,49 @@ Color ColorBrightness(Color color, float factor) return result; } +// Get color with contrast correction +// NOTE: Contrast values between -1.0f and 1.0f +Color ColorContrast(Color color, float contrast) +{ + Color result = color; + + if (contrast < -1.0f) contrast = -1.0f; + else if (contrast > 1.0f) contrast = 1.0f; + + contrast = (1.0f + contrast); + contrast *= contrast; + + float pR = (float)color.r/255.0f; + pR -= 0.5f; + pR *= contrast; + pR += 0.5f; + pR *= 255; + if (pR < 0) pR = 0; + else if (pR > 255) pR = 255; + + float pG = (float)color.g/255.0f; + pG -= 0.5f; + pG *= contrast; + pG += 0.5f; + pG *= 255; + if (pG < 0) pG = 0; + else if (pG > 255) pG = 255; + + float pB = (float)color.b/255.0f; + pB -= 0.5f; + pB *= contrast; + pB += 0.5f; + pB *= 255; + if (pB < 0) pB = 0; + else if (pB > 255) pB = 255; + + result.r = (unsigned char)pR; + result.g = (unsigned char)pG; + result.b = (unsigned char)pB; + + return result; +} + // Get color with alpha applied, alpha goes from 0.0f to 1.0f Color ColorAlpha(Color color, float alpha) { From 935a306b29ea53cba1633c1b82e9280a51307dcc Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Dec 2022 12:44:25 +0100 Subject: [PATCH 0175/1710] Update windows.yml --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 55a2410d0..ff6fdb8f7 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -74,7 +74,7 @@ jobs: # Setup MSBuild.exe path if required - name: Setup MSBuild - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v1.1 if: matrix.compiler == 'msvc16' - name: Build Library (MinGW-w64) From 6d59a21e99d73f6467d481875c0e6e4e843e087b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Dec 2022 13:01:57 +0100 Subject: [PATCH 0176/1710] Update windows.yml --- .github/workflows/windows.yml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index ff6fdb8f7..4bd4a5801 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -77,7 +77,22 @@ jobs: uses: microsoft/setup-msbuild@v1.1 if: matrix.compiler == 'msvc16' - - name: Build Library (MinGW-w64) + - name: Build Library (MinGW-w64 32bit) + run: | + cd src + x86_64-w64-mingw32-gcc.exe --version + windres.exe --version + dir C:\msys64\mingw64\bin + make PLATFORM=PLATFORM_DESKTOP CC=x86_64-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" CFLAGS=-m32 + //windres.exe -i raylib.dll.rc -o raylib.dll.rc.data -O coff --target=${{ matrix.WINDRES_ARCH }} + //make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B + cd .. + shell: cmd + if: | + matrix.compiler == 'mingw-w64' && + matrix.bits == 32 + + - name: Build Library (MinGW-w64 64bit) run: | cd src ${{ matrix.ARCH }}-w64-mingw32-gcc.exe --version @@ -88,7 +103,9 @@ jobs: make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. shell: cmd - if: matrix.compiler == 'mingw-w64' + if: | + matrix.compiler == 'mingw-w64' && + matrix.bits == 64 - name: Build Library (MSVC16) run: | From 855a5f3701aaab854a50ef962e4b1432f6b52f34 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Dec 2022 13:06:13 +0100 Subject: [PATCH 0177/1710] Update windows.yml --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4bd4a5801..17a851868 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -83,7 +83,7 @@ jobs: x86_64-w64-mingw32-gcc.exe --version windres.exe --version dir C:\msys64\mingw64\bin - make PLATFORM=PLATFORM_DESKTOP CC=x86_64-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" CFLAGS=-m32 + make PLATFORM=PLATFORM_DESKTOP CC=x86_64-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" EXTERNAL_CONFIG_FLAGS=-m32 //windres.exe -i raylib.dll.rc -o raylib.dll.rc.data -O coff --target=${{ matrix.WINDRES_ARCH }} //make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. From c04c3668cbcdabac492532ede7b251d1a86ed8e9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Dec 2022 13:09:05 +0100 Subject: [PATCH 0178/1710] Update windows.yml --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 17a851868..cf9a9f33e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -83,7 +83,7 @@ jobs: x86_64-w64-mingw32-gcc.exe --version windres.exe --version dir C:\msys64\mingw64\bin - make PLATFORM=PLATFORM_DESKTOP CC=x86_64-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" EXTERNAL_CONFIG_FLAGS=-m32 + make PLATFORM=PLATFORM_DESKTOP CC=x86_64-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=STATIC RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" CUSTOM_CFLAGS=-m32 //windres.exe -i raylib.dll.rc -o raylib.dll.rc.data -O coff --target=${{ matrix.WINDRES_ARCH }} //make PLATFORM=PLATFORM_DESKTOP CC=${{ matrix.ARCH }}-w64-mingw32-gcc.exe RAYLIB_LIBTYPE=SHARED RAYLIB_RELEASE_PATH="../build/${{ env.RELEASE_NAME }}/lib" -B cd .. From 884d30b85acae99f74ba072bda52172dc5757ef1 Mon Sep 17 00:00:00 2001 From: simendsjo Date: Sun, 11 Dec 2022 13:37:38 +0100 Subject: [PATCH 0179/1710] Use GLVND also for old cmake versions (#2826) Use GLVND also when legacy implementations exist for old cmake versions <= 3.10. This is a breaking change for old cmake versions (prior to around 2017-10-05) which will now use GLVND rather than defaulting to libGL. This fixes the following warning when building: CMake Warning (dev) at /gnu/store/qv13zgbmyx0vjav8iiqp772kp6rxvwnd-cmake-3.24.2/share/cmake-3.24/Modules/FindOpenGL.cmake:315 (message): Policy CMP0072 is not set: FindOpenGL prefers GLVND by default when available. Run "cmake --help-policy CMP0072" for policy details. Use the cmake_policy command to set the policy and suppress this warning. FindOpenGL found both a legacy GL library: OPENGL_gl_LIBRARY: /home/simendsjo/.guix-profile/lib/libGL.so and GLVND libraries for OpenGL and GLX: OPENGL_opengl_LIBRARY: /home/simendsjo/.guix-profile/lib/libOpenGL.so OPENGL_glx_LIBRARY: /home/simendsjo/.guix-profile/lib/libGLX.so OpenGL_GL_PREFERENCE has not been set to "GLVND" or "LEGACY", so for compatibility with CMake 3.10 and below the legacy GL library will be used. Call Stack (most recent call first): cmake/LibraryConfigurations.cmake:21 (find_package) src/CMakeLists.txt:46 (include) This warning is for project developers. Use -Wno-dev to suppress it. See https://cmake.org/cmake/help/latest/policy/CMP0072.html Closes #2825 --- cmake/LibraryConfigurations.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index cc0f01e40..a16bde69d 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -1,3 +1,12 @@ +# Set OpenGL_GL_PREFERENCE to new "GLVND" even when legacy library exists and +# cmake is <= 3.10 +# +# See https://cmake.org/cmake/help/latest/policy/CMP0072.html for more +# information. +if(POLICY CMP0072) + cmake_policy(SET CMP0072 NEW) +endif() + if (${PLATFORM} MATCHES "Desktop") set(PLATFORM_CPP "PLATFORM_DESKTOP") From 445ce51e510bc3733e6ab7db618ad3d53de8943c Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 13 Dec 2022 10:46:49 +0100 Subject: [PATCH 0180/1710] Update Makefile --- examples/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index edfbab118..76a33149f 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -52,8 +52,8 @@ USE_EXTERNAL_GLFW ?= FALSE USE_WAYLAND_DISPLAY ?= FALSE # PLATFORM_WEB: Default properties -BUILD_WEB_ASYNCIFY ?= FALSE -BUILD_WEB_SHELL ?= minshell.html +BUILD_WEB_ASYNCIFY ?= TRUE +BUILD_WEB_SHELL ?= $(RAYLIB_PATH)/src/minshell.html BUILD_WEB_HEAP_SIZE ?= 134217728 BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources From 3419aef6778d5b1d9ed61e6d63d11c44f29ef8e1 Mon Sep 17 00:00:00 2001 From: Alexander Heinrich Date: Tue, 13 Dec 2022 18:59:00 +0100 Subject: [PATCH 0181/1710] Fix Vector2Angle() (#2829) With this fix the function still returns negative values, which is wrong. But we keep this behaviour to maintain backwards compatibility. --- src/raymath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raymath.h b/src/raymath.h index fbe4ceadc..34db2d885 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -309,7 +309,7 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) // Calculate angle from two vectors RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { - float result = atan2f(v2.y, v2.x) - atan2f(v1.y, v1.x); + float result = -acos(v1.x*v2.x + v1.y*v2.y); return result; } From 619331f4a7e146a122e45214f40c32bdce25c650 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 14 Dec 2022 12:57:39 +0100 Subject: [PATCH 0182/1710] REVIEWED: Issue with depth textures on WebGL #2824 --- src/rlgl.h | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 301bd976d..8afe1fdcf 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -987,7 +987,8 @@ typedef struct rlglData { bool vao; // VAO support (OpenGL ES2 could not support VAO extension) (GL_ARB_vertex_array_object) bool instancing; // Instancing supported (GL_ANGLE_instanced_arrays, GL_EXT_draw_instanced + GL_EXT_instanced_arrays) bool texNPOT; // NPOT textures full support (GL_ARB_texture_non_power_of_two, GL_OES_texture_npot) - bool texDepth; // Depth textures supported (GL_ARB_depth_texture, GL_WEBGL_depth_texture, GL_OES_depth_texture) + bool texDepth; // Depth textures supported (GL_ARB_depth_texture, GL_OES_depth_texture) + bool texDepthWebGL; // Depth textures supported WebGL specific (GL_WEBGL_depth_texture) bool texFloat32; // float textures support (32 bit per channel) (GL_OES_texture_float) bool texCompDXT; // DDS texture compression support (GL_EXT_texture_compression_s3tc, GL_WEBGL_compressed_texture_s3tc, GL_WEBKIT_WEBGL_compressed_texture_s3tc) bool texCompETC1; // ETC1 texture compression support (GL_OES_compressed_ETC1_RGB8_texture, GL_WEBGL_compressed_texture_etc1) @@ -2218,11 +2219,12 @@ void rlLoadExtensions(void *loader) if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) RLGL.ExtSupported.texFloat32 = true; // Check depth texture support - if ((strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) || - (strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0)) RLGL.ExtSupported.texDepth = true; + 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 (RLGL.ExtSupported.texDepthWebGL) RLGL.ExtSupported.texDepth = true; - if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) RLGL.ExtSupported.maxDepthBits = 24; - if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) RLGL.ExtSupported.maxDepthBits = 32; + 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) || @@ -2975,7 +2977,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, } // Load depth texture/renderbuffer (to be attached to fbo) -// WARNING: OpenGL ES 2.0 requires GL_OES_depth_texture/WEBGL_depth_texture extensions +// WARNING: OpenGL ES 2.0 requires GL_OES_depth_texture and WebGL requires WEBGL_depth_texture extensions unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) { unsigned int id = 0; @@ -2983,15 +2985,20 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // In case depth textures not supported, we force renderbuffer usage if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true; - + // NOTE: We let the implementation to choose the best bit-depth // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F unsigned int glInternalFormat = GL_DEPTH_COMPONENT; #if defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.ExtSupported.maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; - else if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; - else glInternalFormat = GL_DEPTH_COMPONENT16; + // WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT) + // while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities + if (!RLGL.ExtSupported.texDepthWebGL) + { + if (RLGL.ExtSupported.maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; + else if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; + else glInternalFormat = GL_DEPTH_COMPONENT16; + } #endif if (!useRenderBuffer && RLGL.ExtSupported.texDepth) From d1a104bba46d2918c2a521e255edee07ea2e5f3f Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sat, 17 Dec 2022 13:13:40 +0200 Subject: [PATCH 0183/1710] Fix vector2angle (#2832) * Fix vector2angle * Fix ; * use acosf * need a break * add comments --- src/raymath.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/raymath.h b/src/raymath.h index 34db2d885..d3130750c 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -307,9 +307,15 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) } // Calculate angle from two vectors +// Parameters need to be normalized RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { - float result = -acos(v1.x*v2.x + v1.y*v2.y); + float dotProduct = v1.x*v2.x + v1.y*v2.y; // Dot product + + float t = dotProduct < -1 ? -1 : dotProduct; // Clamp + if (t > 1) t = 1; + + float result = acosf(t); return result; } From 72b9f3c5def47fe6bf840466e464485878d93967 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Dec 2022 12:15:19 +0100 Subject: [PATCH 0184/1710] Minor tweaks --- src/rcore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 118a5736b..e1fcd0de8 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -151,7 +151,7 @@ #include "external/sdefl.h" // Deflate (RFC 1951) compressor #endif -#if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_C_SOURCE < 199309L +#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 @@ -2813,6 +2813,9 @@ void TakeScreenshot(const char *fileName) } // Get a random value between min and max (both included) +// 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 int GetRandomValue(int min, int max) { if (min > max) @@ -2821,9 +2824,6 @@ int GetRandomValue(int min, int max) max = min; min = tmp; } - - // 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. if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue arguments. Range should not be higher than %i.", RAND_MAX); From d7f7c94c4d44df89f6104dcda93eb330fc8534f3 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 18 Dec 2022 18:00:14 +0100 Subject: [PATCH 0185/1710] REVIEWED: `Vector2Angle()` --- src/raymath.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index d3130750c..ea3085a54 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -307,15 +307,23 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) } // Calculate angle from two vectors -// Parameters need to be normalized +// NOTE: Parameters need to be normalized +// Current implementation should be aligned with glm::angle RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { - float dotProduct = v1.x*v2.x + v1.y*v2.y; // Dot product + float result = 0.0f; + + float dot = v1.x*v2.x + v1.y*v2.y; // Dot product - float t = dotProduct < -1 ? -1 : dotProduct; // Clamp - if (t > 1) t = 1; + float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp + if (dotClamp > 1.0f) dotClamp = 1.0f; - float result = acosf(t); + result = acosf(dotClamp); + + // Alternative implementation, more costly + //float v1Length = sqrtf((v1.x*v1.x) + (v1.y*v1.y)); + //float v2Length = sqrtf((v2.x*v2.x) + (v2.y*v2.y)); + //float result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); return result; } From 03cc540d5f1df71bd7ad8118d0e11b492fa5cc18 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 18 Dec 2022 18:00:21 +0100 Subject: [PATCH 0186/1710] Minor tweak --- src/rcore.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index e1fcd0de8..12fe73512 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2824,9 +2824,10 @@ int GetRandomValue(int min, int max) max = min; min = tmp; } + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) { - TRACELOG(LOG_WARNING, "Invalid GetRandomValue arguments. Range should not be higher than %i.", RAND_MAX); + TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); } return (rand()%(abs(max - min) + 1) + min); From dbdbbea47193ed19cd188dccbe694970387f810d Mon Sep 17 00:00:00 2001 From: turborium <45082001+turborium@users.noreply.github.com> Date: Wed, 28 Dec 2022 14:04:55 +0400 Subject: [PATCH 0187/1710] Add new Delphi/Lazarus bindings (#2838) TurboRaylib - dynamic bindings of ray lib for Delphi/Lazarus --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 6d1b00f2e..39a333e51 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -43,6 +43,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | | raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.0** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | +| TurboRaylib | **4.2** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.2** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | From ba38fe5b97c2401830d612376d198f1d3c811969 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sat, 31 Dec 2022 20:25:35 +0200 Subject: [PATCH 0188/1710] core_loading_thread example join thread on completion (#2845) * core_loading_thread example join thread on completion * error checking --- examples/core/core_loading_thread.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 09ec76460..5ab8bbc3a 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -72,6 +72,10 @@ int main(void) if (atomic_load(&dataLoaded)) { 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; From 713e26332f15209a9b8f2315a909858a21fa5fd4 Mon Sep 17 00:00:00 2001 From: Wytek01 <101928745+Wytekol@users.noreply.github.com> Date: Sun, 1 Jan 2023 12:55:49 +0100 Subject: [PATCH 0189/1710] Update year to 2023 (#2846) * Update year to 2023 * Update raylib.h year to 2023 --- LICENSE | 2 +- src/raylib.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 2694b4748..91da62ed9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +Copyright (c) 2013-2023 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/src/raylib.h b/src/raylib.h index dc71af847..5a230a3ac 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -57,7 +57,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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. From b59fab7ee6035886554778e8adc540deb3f29f4a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 1 Jan 2023 16:00:56 +0100 Subject: [PATCH 0190/1710] Update year to 2023 --- examples/audio/audio_module_playing.c | 2 +- examples/audio/audio_multichannel_sound.c | 2 +- examples/audio/audio_music_stream.c | 2 +- examples/audio/audio_raw_stream.c | 2 +- examples/audio/audio_sound_loading.c | 2 +- examples/audio/audio_stream_effects.c | 2 +- examples/core/core_2d_camera.c | 2 +- examples/core/core_2d_camera_platformer.c | 2 +- examples/core/core_3d_camera_first_person.c | 2 +- examples/core/core_3d_camera_free.c | 2 +- examples/core/core_3d_camera_mode.c | 2 +- examples/core/core_3d_picking.c | 2 +- examples/core/core_basic_screen_manager.c | 2 +- examples/core/core_basic_window.c | 2 +- examples/core/core_basic_window_web.c | 2 +- examples/core/core_custom_frame_control.c | 2 +- examples/core/core_custom_logging.c | 2 +- examples/core/core_drop_files.c | 2 +- examples/core/core_input_gamepad.c | 2 +- examples/core/core_input_gestures.c | 2 +- examples/core/core_input_keys.c | 2 +- examples/core/core_input_mouse.c | 2 +- examples/core/core_input_mouse_wheel.c | 2 +- examples/core/core_input_multitouch.c | 2 +- examples/core/core_loading_thread.c | 2 +- examples/core/core_random_values.c | 2 +- examples/core/core_scissor_test.c | 2 +- examples/core/core_smooth_pixelperfect.c | 2 +- examples/core/core_split_screen.c | 2 +- examples/core/core_storage_values.c | 2 +- examples/core/core_vr_simulator.c | 2 +- examples/core/core_window_flags.c | 2 +- examples/core/core_window_letterbox.c | 2 +- examples/core/core_window_should_close.c | 2 +- examples/core/core_world_screen.c | 2 +- examples/examples_template.c | 2 +- examples/models/models_animation.c | 2 +- examples/models/models_billboard.c | 2 +- examples/models/models_box_collisions.c | 2 +- examples/models/models_cubicmap.c | 2 +- examples/models/models_first_person_maze.c | 2 +- examples/models/models_geometric_shapes.c | 2 +- examples/models/models_heightmap.c | 2 +- examples/models/models_loading.c | 2 +- examples/models/models_loading_gltf.c | 2 +- examples/models/models_loading_vox.c | 2 +- examples/models/models_mesh_generation.c | 2 +- examples/models/models_mesh_picking.c | 2 +- examples/models/models_orthographic_projection.c | 2 +- examples/models/models_rlgl_solar_system.c | 2 +- examples/models/models_skybox.c | 2 +- examples/models/models_waving_cubes.c | 2 +- examples/models/models_yaw_pitch_roll.c | 2 +- examples/others/easings_testbed.c | 2 +- examples/others/embedded_files_loading.c | 2 +- examples/others/raylib_opengl_interop.c | 2 +- examples/others/reasings.h | 2 +- examples/others/rlgl_compute_shader.c | 2 +- examples/others/rlgl_standalone.c | 2 +- examples/shaders/rlights.h | 2 +- examples/shaders/shaders_basic_lighting.c | 2 +- examples/shaders/shaders_custom_uniform.c | 2 +- examples/shaders/shaders_eratosthenes.c | 2 +- examples/shaders/shaders_fog.c | 2 +- examples/shaders/shaders_hot_reloading.c | 2 +- examples/shaders/shaders_julia_set.c | 2 +- examples/shaders/shaders_mesh_instancing.c | 2 +- examples/shaders/shaders_model_shader.c | 2 +- examples/shaders/shaders_multi_sample2d.c | 2 +- examples/shaders/shaders_palette_switch.c | 2 +- examples/shaders/shaders_postprocessing.c | 2 +- examples/shaders/shaders_raymarching.c | 2 +- examples/shaders/shaders_shapes_textures.c | 2 +- examples/shaders/shaders_simple_mask.c | 2 +- examples/shaders/shaders_spotlight.c | 2 +- examples/shaders/shaders_texture_drawing.c | 2 +- examples/shaders/shaders_texture_outline.c | 2 +- examples/shaders/shaders_texture_waves.c | 2 +- examples/shapes/shapes_basic_shapes.c | 2 +- examples/shapes/shapes_bouncing_ball.c | 2 +- examples/shapes/shapes_collision_area.c | 2 +- examples/shapes/shapes_colors_palette.c | 2 +- examples/shapes/shapes_draw_circle_sector.c | 2 +- examples/shapes/shapes_draw_rectangle_rounded.c | 2 +- examples/shapes/shapes_draw_ring.c | 2 +- examples/shapes/shapes_easings_ball_anim.c | 2 +- examples/shapes/shapes_easings_box_anim.c | 2 +- examples/shapes/shapes_easings_rectangle_array.c | 2 +- examples/shapes/shapes_following_eyes.c | 2 +- examples/shapes/shapes_lines_bezier.c | 2 +- examples/shapes/shapes_logo_raylib.c | 2 +- examples/shapes/shapes_logo_raylib_anim.c | 2 +- examples/shapes/shapes_rectangle_scaling.c | 2 +- examples/text/text_draw_3d.c | 2 +- examples/text/text_font_filters.c | 2 +- examples/text/text_font_loading.c | 2 +- examples/text/text_font_sdf.c | 2 +- examples/text/text_font_spritefont.c | 2 +- examples/text/text_format_text.c | 2 +- examples/text/text_input_box.c | 2 +- examples/text/text_raylib_fonts.c | 2 +- examples/text/text_rectangle_bounds.c | 2 +- examples/text/text_unicode.c | 2 +- examples/text/text_writing_anim.c | 2 +- examples/textures/textures_background_scrolling.c | 2 +- examples/textures/textures_blend_modes.c | 2 +- examples/textures/textures_bunnymark.c | 2 +- examples/textures/textures_draw_tiled.c | 2 +- examples/textures/textures_fog_of_war.c | 2 +- examples/textures/textures_gif_player.c | 2 +- examples/textures/textures_image_drawing.c | 2 +- examples/textures/textures_image_generation.c | 2 +- examples/textures/textures_image_loading.c | 2 +- examples/textures/textures_image_processing.c | 2 +- examples/textures/textures_image_text.c | 2 +- examples/textures/textures_logo_raylib.c | 2 +- examples/textures/textures_mouse_painting.c | 2 +- examples/textures/textures_npatch_drawing.c | 2 +- examples/textures/textures_particles_blending.c | 2 +- examples/textures/textures_polygon.c | 2 +- examples/textures/textures_raw_data.c | 2 +- examples/textures/textures_sprite_anim.c | 2 +- examples/textures/textures_sprite_button.c | 2 +- examples/textures/textures_sprite_explosion.c | 2 +- examples/textures/textures_srcrec_dstrec.c | 2 +- examples/textures/textures_to_image.c | 2 +- parser/raylib_parser.c | 4 ++-- projects/Geany/core_basic_window.c | 2 +- projects/VSCode/main.c | 2 +- src/config.h | 2 +- src/raudio.c | 4 ++-- src/raymath.h | 2 +- src/rcamera.h | 2 +- src/rcore.c | 2 +- src/rgestures.h | 2 +- src/rglfw.c | 2 +- src/rlgl.h | 2 +- src/rmodels.c | 4 ++-- src/rshapes.c | 2 +- src/rtextures.c | 4 ++-- 140 files changed, 144 insertions(+), 144 deletions(-) diff --git a/examples/audio/audio_module_playing.c b/examples/audio/audio_module_playing.c index e47db592b..b8482aa57 100644 --- a/examples/audio/audio_module_playing.c +++ b/examples/audio/audio_module_playing.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/audio/audio_multichannel_sound.c b/examples/audio/audio_multichannel_sound.c index 3e6a612c8..1d549a055 100644 --- a/examples/audio/audio_multichannel_sound.c +++ b/examples/audio/audio_multichannel_sound.c @@ -9,7 +9,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-2022 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/audio/audio_music_stream.c b/examples/audio/audio_music_stream.c index f4819eb13..4cf29973d 100644 --- a/examples/audio/audio_music_stream.c +++ b/examples/audio/audio_music_stream.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index afee42962..142450e83 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) * ********************************************************************************************/ diff --git a/examples/audio/audio_sound_loading.c b/examples/audio/audio_sound_loading.c index 6a2670439..dae6516ee 100644 --- a/examples/audio/audio_sound_loading.c +++ b/examples/audio/audio_sound_loading.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/audio/audio_stream_effects.c b/examples/audio/audio_stream_effects.c index ddaeb7fb0..219305057 100644 --- a/examples/audio/audio_stream_effects.c +++ b/examples/audio/audio_stream_effects.c @@ -7,7 +7,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 Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_2d_camera.c b/examples/core/core_2d_camera.c index edc67a0a8..24cca6985 100644 --- a/examples/core/core_2d_camera.c +++ b/examples/core/core_2d_camera.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_2d_camera_platformer.c b/examples/core/core_2d_camera_platformer.c index 5750e7a80..1ae9e0fcb 100644 --- a/examples/core/core_2d_camera_platformer.c +++ b/examples/core/core_2d_camera_platformer.c @@ -9,7 +9,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-2022 arvyy (@arvyy) +* Copyright (c) 2019-2023 arvyy (@arvyy) * ********************************************************************************************/ diff --git a/examples/core/core_3d_camera_first_person.c b/examples/core/core_3d_camera_first_person.c index 3af829e25..a7aa5f49a 100644 --- a/examples/core/core_3d_camera_first_person.c +++ b/examples/core/core_3d_camera_first_person.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index b3b0b9b0b..af7ab8d72 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_3d_camera_mode.c b/examples/core/core_3d_camera_mode.c index ffa1acd6d..cb83a4a2b 100644 --- a/examples/core/core_3d_camera_mode.c +++ b/examples/core/core_3d_camera_mode.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_3d_picking.c b/examples/core/core_3d_picking.c index 8cd3a3fe1..8c0df9663 100644 --- a/examples/core/core_3d_picking.c +++ b/examples/core/core_3d_picking.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_basic_screen_manager.c b/examples/core/core_basic_screen_manager.c index 1364982e8..a80572988 100644 --- a/examples/core/core_basic_screen_manager.c +++ b/examples/core/core_basic_screen_manager.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_basic_window.c b/examples/core/core_basic_window.c index be7a449ab..5bfd186da 100644 --- a/examples/core/core_basic_window.c +++ b/examples/core/core_basic_window.c @@ -17,7 +17,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_basic_window_web.c b/examples/core/core_basic_window_web.c index 7dc47f9c9..7d83254a1 100644 --- a/examples/core/core_basic_window_web.c +++ b/examples/core/core_basic_window_web.c @@ -11,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_custom_frame_control.c b/examples/core/core_custom_frame_control.c index 016c6c448..ed0dbc661 100644 --- a/examples/core/core_custom_frame_control.c +++ b/examples/core/core_custom_frame_control.c @@ -22,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) 2021-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_custom_logging.c b/examples/core/core_custom_logging.c index 9794d14b9..b4da2d575 100644 --- a/examples/core/core_custom_logging.c +++ b/examples/core/core_custom_logging.c @@ -9,7 +9,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-2022 Pablo Marcos Oltra (@pamarcos) and Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Pablo Marcos Oltra (@pamarcos) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_drop_files.c b/examples/core/core_drop_files.c index 35ebc7689..9ebe0f957 100644 --- a/examples/core/core_drop_files.c +++ b/examples/core/core_drop_files.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index 02ac17053..411472b34 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -13,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) 2013-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_input_gestures.c b/examples/core/core_input_gestures.c index 012204ddd..312e78ffd 100644 --- a/examples/core/core_input_gestures.c +++ b/examples/core/core_input_gestures.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_input_keys.c b/examples/core/core_input_keys.c index 6020d577f..ccff24005 100644 --- a/examples/core/core_input_keys.c +++ b/examples/core/core_input_keys.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_input_mouse.c b/examples/core/core_input_mouse.c index 6e164f50b..874e16c30 100644 --- a/examples/core/core_input_mouse.c +++ b/examples/core/core_input_mouse.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_input_mouse_wheel.c b/examples/core/core_input_mouse_wheel.c index 1f384cd0c..826bad3a1 100644 --- a/examples/core/core_input_mouse_wheel.c +++ b/examples/core/core_input_mouse_wheel.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_input_multitouch.c b/examples/core/core_input_multitouch.c index 1eb6905b5..980af3801 100644 --- a/examples/core/core_input_multitouch.c +++ b/examples/core/core_input_multitouch.c @@ -9,7 +9,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-2022 Berni (@Berni8k) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Berni (@Berni8k) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 5ab8bbc3a..d5c00fc56 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -9,7 +9,7 @@ * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * -* Copyright (c) 2014-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_random_values.c b/examples/core/core_random_values.c index 9150b9f45..c2225bcaf 100644 --- a/examples/core/core_random_values.c +++ b/examples/core/core_random_values.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_scissor_test.c b/examples/core/core_scissor_test.c index f296a3acb..c2b853c05 100644 --- a/examples/core/core_scissor_test.c +++ b/examples/core/core_scissor_test.c @@ -9,7 +9,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-2022 Chris Dill (@MysteriousSpace) +* Copyright (c) 2019-2023 Chris Dill (@MysteriousSpace) * ********************************************************************************************/ diff --git a/examples/core/core_smooth_pixelperfect.c b/examples/core/core_smooth_pixelperfect.c index 36fb09c58..776706c00 100644 --- a/examples/core/core_smooth_pixelperfect.c +++ b/examples/core/core_smooth_pixelperfect.c @@ -10,7 +10,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-2022 Giancamillo Alessandroni (@NotManyIdeasDev) and Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2023 Giancamillo Alessandroni (@NotManyIdeasDev) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_split_screen.c b/examples/core/core_split_screen.c index 1f1f3967b..50cfcf7ac 100644 --- a/examples/core/core_split_screen.c +++ b/examples/core/core_split_screen.c @@ -9,7 +9,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-2022 Jeffery Myers (@JeffM2501) +* Copyright (c) 2021-2023 Jeffery Myers (@JeffM2501) * ********************************************************************************************/ diff --git a/examples/core/core_storage_values.c b/examples/core/core_storage_values.c index c6f01217b..127399123 100644 --- a/examples/core/core_storage_values.c +++ b/examples/core/core_storage_values.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index f5dc4d55d..5914897b0 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_window_flags.c b/examples/core/core_window_flags.c index 27a6e7b65..b1cbb1b64 100644 --- a/examples/core/core_window_flags.c +++ b/examples/core/core_window_flags.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2020-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_window_letterbox.c b/examples/core/core_window_letterbox.c index 774e8d99f..751fb14ab 100644 --- a/examples/core/core_window_letterbox.c +++ b/examples/core/core_window_letterbox.c @@ -9,7 +9,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-2022 Anata (@anatagawa) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Anata (@anatagawa) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_window_should_close.c b/examples/core/core_window_should_close.c index fa6b9b97b..f0392b013 100644 --- a/examples/core/core_window_should_close.c +++ b/examples/core/core_window_should_close.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/core/core_world_screen.c b/examples/core/core_world_screen.c index 80d9cf918..d86adf877 100644 --- a/examples/core/core_world_screen.c +++ b/examples/core/core_world_screen.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/examples_template.c b/examples/examples_template.c index 1ea615d4a..3d8332e67 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -48,7 +48,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 (@) +* Copyright (c) 2023 (@) * ********************************************************************************************/ diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c index 161b21995..b9216b21f 100644 --- a/examples/models/models_animation.c +++ b/examples/models/models_animation.c @@ -9,7 +9,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-2022 Culacant (@culacant) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Culacant (@culacant) and Ramon Santamaria (@raysan5) * ******************************************************************************************** * diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 0b5a42d57..ce313949b 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_box_collisions.c b/examples/models/models_box_collisions.c index db8eb1f2d..9cff5e058 100644 --- a/examples/models/models_box_collisions.c +++ b/examples/models/models_box_collisions.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index e9da0482f..4953a99fc 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_first_person_maze.c b/examples/models/models_first_person_maze.c index b2211c57e..b5e35b585 100644 --- a/examples/models/models_first_person_maze.c +++ b/examples/models/models_first_person_maze.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_geometric_shapes.c b/examples/models/models_geometric_shapes.c index 90261ad21..33c406347 100644 --- a/examples/models/models_geometric_shapes.c +++ b/examples/models/models_geometric_shapes.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index 189d07d75..a5d17b02e 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c index d40661412..7bdf1772f 100644 --- a/examples/models/models_loading.c +++ b/examples/models/models_loading.c @@ -20,7 +20,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index 01a084069..68228a3f0 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2020-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c index 80226d0a4..49477cd9c 100644 --- a/examples/models/models_loading_vox.c +++ b/examples/models/models_loading_vox.c @@ -9,7 +9,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-2022 Johann Nadalutti (@procfxgen) and Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2023 Johann Nadalutti (@procfxgen) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index 35586c811..b4ff018cc 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 64aba2624..52f9974d3 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -9,7 +9,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-2022 Joel Davis (@joeld42) and Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Joel Davis (@joeld42) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_orthographic_projection.c b/examples/models/models_orthographic_projection.c index cfdeb859b..098e4b20a 100644 --- a/examples/models/models_orthographic_projection.c +++ b/examples/models/models_orthographic_projection.c @@ -9,7 +9,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-2022 Max Danielsson (@autious) and Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Max Danielsson (@autious) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c index a49b37d06..903752f76 100644 --- a/examples/models/models_rlgl_solar_system.c +++ b/examples/models/models_rlgl_solar_system.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index b6ce72b2a..1b9923334 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_waving_cubes.c b/examples/models/models_waving_cubes.c index f0737de3a..31ed4ff41 100644 --- a/examples/models/models_waving_cubes.c +++ b/examples/models/models_waving_cubes.c @@ -9,7 +9,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-2022 Codecat (@codecat) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Codecat (@codecat) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index d3db9d063..611ee3146 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -9,7 +9,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-2022 Berni (@Berni8k) and Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Berni (@Berni8k) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/others/easings_testbed.c b/examples/others/easings_testbed.c index 38f47dd09..227efd231 100644 --- a/examples/others/easings_testbed.c +++ b/examples/others/easings_testbed.c @@ -9,7 +9,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-2022 Juan Miguel López (@flashback-fx ) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Juan Miguel López (@flashback-fx ) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/others/embedded_files_loading.c b/examples/others/embedded_files_loading.c index 3e3bc7d44..64023539c 100644 --- a/examples/others/embedded_files_loading.c +++ b/examples/others/embedded_files_loading.c @@ -9,7 +9,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-2022 Kristian Holmgren (@defutura) and Ramon Santamaria (@raysan5) +* Copyright (c) 2020-2023 Kristian Holmgren (@defutura) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/others/raylib_opengl_interop.c b/examples/others/raylib_opengl_interop.c index 2acb62193..bda301a7e 100644 --- a/examples/others/raylib_opengl_interop.c +++ b/examples/others/raylib_opengl_interop.c @@ -9,7 +9,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-2022 Stephan Soller (@arkanis) and Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2023 Stephan Soller (@arkanis) and Ramon Santamaria (@raysan5) * ******************************************************************************************** * diff --git a/examples/others/reasings.h b/examples/others/reasings.h index 8b14ba6aa..657ea242e 100644 --- a/examples/others/reasings.h +++ b/examples/others/reasings.h @@ -60,7 +60,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. * --------------------------------------------------------------------------------- * -* Copyright (c) 2015-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 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/examples/others/rlgl_compute_shader.c b/examples/others/rlgl_compute_shader.c index b74ed932f..e19a6c2f5 100644 --- a/examples/others/rlgl_compute_shader.c +++ b/examples/others/rlgl_compute_shader.c @@ -12,7 +12,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-2022 Teddy Astie (@tsnake41) +* Copyright (c) 2021-2023 Teddy Astie (@tsnake41) * ********************************************************************************************/ diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index 308699992..43cc3288e 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -29,7 +29,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 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/examples/shaders/rlights.h b/examples/shaders/rlights.h index cf0f7629a..d17bf7c2e 100644 --- a/examples/shaders/rlights.h +++ b/examples/shaders/rlights.h @@ -11,7 +11,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2017-2022 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Victor Fisac (@victorfisac) 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. diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index c557ecf73..1b29b8522 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -14,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) 2019-2022 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index 401328e0f..75f6112e5 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -14,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) 2015-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_eratosthenes.c b/examples/shaders/shaders_eratosthenes.c index a1f4101ac..a481f3008 100644 --- a/examples/shaders/shaders_eratosthenes.c +++ b/examples/shaders/shaders_eratosthenes.c @@ -21,7 +21,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-2022 ProfJski and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 ProfJski and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index de5c23df5..ac84a0180 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -14,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) 2019-2022 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_hot_reloading.c b/examples/shaders/shaders_hot_reloading.c index e9aae3496..e58c45f3c 100644 --- a/examples/shaders/shaders_hot_reloading.c +++ b/examples/shaders/shaders_hot_reloading.c @@ -10,7 +10,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2020-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_julia_set.c b/examples/shaders/shaders_julia_set.c index 962c723b1..ee1988058 100644 --- a/examples/shaders/shaders_julia_set.c +++ b/examples/shaders/shaders_julia_set.c @@ -14,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) 2019-2022 eggmund (@eggmund) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 eggmund (@eggmund) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_mesh_instancing.c b/examples/shaders/shaders_mesh_instancing.c index ce0855d94..f4e6b40b0 100644 --- a/examples/shaders/shaders_mesh_instancing.c +++ b/examples/shaders/shaders_mesh_instancing.c @@ -9,7 +9,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-2022 @seanpringle, Max (@moliad) and Ramon Santamaria (@raysan5) +* Copyright (c) 2020-2023 @seanpringle, Max (@moliad) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index 29c1948c9..de3cb5f8d 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -14,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) 2014-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_multi_sample2d.c b/examples/shaders/shaders_multi_sample2d.c index ae5470fff..d6f8803b7 100644 --- a/examples/shaders/shaders_multi_sample2d.c +++ b/examples/shaders/shaders_multi_sample2d.c @@ -14,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) 2020-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2020-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_palette_switch.c b/examples/shaders/shaders_palette_switch.c index b47bbf16f..bb1eda4e0 100644 --- a/examples/shaders/shaders_palette_switch.c +++ b/examples/shaders/shaders_palette_switch.c @@ -16,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-2022 Marco Lizza (@MarcoLizza) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Marco Lizza (@MarcoLizza) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index 758250f19..d0555f219 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -14,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) 2015-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index 8be45fb9f..9b8bc30cd 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -10,7 +10,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_shapes_textures.c b/examples/shaders/shaders_shapes_textures.c index ca6677fbd..d3ec8daf0 100644 --- a/examples/shaders/shaders_shapes_textures.c +++ b/examples/shaders/shaders_shapes_textures.c @@ -14,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) 2015-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_simple_mask.c b/examples/shaders/shaders_simple_mask.c index d10878d77..ce3fc8bb9 100644 --- a/examples/shaders/shaders_simple_mask.c +++ b/examples/shaders/shaders_simple_mask.c @@ -9,7 +9,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-2022 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ******************************************************************************************** * diff --git a/examples/shaders/shaders_spotlight.c b/examples/shaders/shaders_spotlight.c index 6ddcd42f6..a21e3c82a 100644 --- a/examples/shaders/shaders_spotlight.c +++ b/examples/shaders/shaders_spotlight.c @@ -9,7 +9,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-2022 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ******************************************************************************************** * diff --git a/examples/shaders/shaders_texture_drawing.c b/examples/shaders/shaders_texture_drawing.c index 6b82915b5..006168d57 100644 --- a/examples/shaders/shaders_texture_drawing.c +++ b/examples/shaders/shaders_texture_drawing.c @@ -11,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-2022 Michał Ciesielski and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Michał Ciesielski and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_texture_outline.c b/examples/shaders/shaders_texture_outline.c index c81d3ff16..a28ab80e4 100644 --- a/examples/shaders/shaders_texture_outline.c +++ b/examples/shaders/shaders_texture_outline.c @@ -12,7 +12,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-2022 Samuel SKiff (@GoldenThumbs) and Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2023 Samuel SKiff (@GoldenThumbs) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shaders/shaders_texture_waves.c b/examples/shaders/shaders_texture_waves.c index cc250f1b9..a087ec4d0 100644 --- a/examples/shaders/shaders_texture_waves.c +++ b/examples/shaders/shaders_texture_waves.c @@ -16,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-2022 Anata (@anatagawa) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Anata (@anatagawa) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_basic_shapes.c b/examples/shapes/shapes_basic_shapes.c index edc0b0c6b..40056b4e7 100644 --- a/examples/shapes/shapes_basic_shapes.c +++ b/examples/shapes/shapes_basic_shapes.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_bouncing_ball.c b/examples/shapes/shapes_bouncing_ball.c index 19f58da65..5e43b80da 100644 --- a/examples/shapes/shapes_bouncing_ball.c +++ b/examples/shapes/shapes_bouncing_ball.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_collision_area.c b/examples/shapes/shapes_collision_area.c index e83f83834..34d04811d 100644 --- a/examples/shapes/shapes_collision_area.c +++ b/examples/shapes/shapes_collision_area.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_colors_palette.c b/examples/shapes/shapes_colors_palette.c index 49aad8a37..d55782550 100644 --- a/examples/shapes/shapes_colors_palette.c +++ b/examples/shapes/shapes_colors_palette.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_draw_circle_sector.c b/examples/shapes/shapes_draw_circle_sector.c index 1fcd8aa48..c95f43643 100644 --- a/examples/shapes/shapes_draw_circle_sector.c +++ b/examples/shapes/shapes_draw_circle_sector.c @@ -9,7 +9,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-2022 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_draw_rectangle_rounded.c b/examples/shapes/shapes_draw_rectangle_rounded.c index 9c527e5ef..8150e0946 100644 --- a/examples/shapes/shapes_draw_rectangle_rounded.c +++ b/examples/shapes/shapes_draw_rectangle_rounded.c @@ -9,7 +9,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-2022 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_draw_ring.c b/examples/shapes/shapes_draw_ring.c index 6225d473c..b001b9216 100644 --- a/examples/shapes/shapes_draw_ring.c +++ b/examples/shapes/shapes_draw_ring.c @@ -9,7 +9,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-2022 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_easings_ball_anim.c b/examples/shapes/shapes_easings_ball_anim.c index e6b595c32..866ec0971 100644 --- a/examples/shapes/shapes_easings_ball_anim.c +++ b/examples/shapes/shapes_easings_ball_anim.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_easings_box_anim.c b/examples/shapes/shapes_easings_box_anim.c index 05cba1959..a854bf6c3 100644 --- a/examples/shapes/shapes_easings_box_anim.c +++ b/examples/shapes/shapes_easings_box_anim.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_easings_rectangle_array.c b/examples/shapes/shapes_easings_rectangle_array.c index a2ab6397d..03916f5b4 100644 --- a/examples/shapes/shapes_easings_rectangle_array.c +++ b/examples/shapes/shapes_easings_rectangle_array.c @@ -10,7 +10,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_following_eyes.c b/examples/shapes/shapes_following_eyes.c index 43a50a79d..2137d797a 100644 --- a/examples/shapes/shapes_following_eyes.c +++ b/examples/shapes/shapes_following_eyes.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_lines_bezier.c b/examples/shapes/shapes_lines_bezier.c index 76c232792..195281be3 100644 --- a/examples/shapes/shapes_lines_bezier.c +++ b/examples/shapes/shapes_lines_bezier.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_logo_raylib.c b/examples/shapes/shapes_logo_raylib.c index 48005c7ae..4f625c2c8 100644 --- a/examples/shapes/shapes_logo_raylib.c +++ b/examples/shapes/shapes_logo_raylib.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_logo_raylib_anim.c b/examples/shapes/shapes_logo_raylib_anim.c index 60e839f51..5d6dd3538 100644 --- a/examples/shapes/shapes_logo_raylib_anim.c +++ b/examples/shapes/shapes_logo_raylib_anim.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/shapes/shapes_rectangle_scaling.c b/examples/shapes/shapes_rectangle_scaling.c index 0faeee2f3..18104b120 100644 --- a/examples/shapes/shapes_rectangle_scaling.c +++ b/examples/shapes/shapes_rectangle_scaling.c @@ -9,7 +9,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-2022 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index f03ad7849..0711d54cd 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -22,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) 2021-2022 Vlad Adrian (@demizdor) +* Copyright (c) 2021-2023 Vlad Adrian (@demizdor) * ********************************************************************************************/ diff --git a/examples/text/text_font_filters.c b/examples/text/text_font_filters.c index 7d9082b5d..e6756713c 100644 --- a/examples/text/text_font_filters.c +++ b/examples/text/text_font_filters.c @@ -11,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_font_loading.c b/examples/text/text_font_loading.c index 6ebdbbcdd..2ded64f09 100644 --- a/examples/text/text_font_loading.c +++ b/examples/text/text_font_loading.c @@ -16,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) 2016-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index 9e7465019..6a223ae5c 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_font_spritefont.c b/examples/text/text_font_spritefont.c index 109ab486b..b5df2d0dc 100644 --- a/examples/text/text_font_spritefont.c +++ b/examples/text/text_font_spritefont.c @@ -17,7 +17,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_format_text.c b/examples/text/text_format_text.c index dbdbb0fdb..6b1b0456f 100644 --- a/examples/text/text_format_text.c +++ b/examples/text/text_format_text.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_input_box.c b/examples/text/text_input_box.c index 4d2eee442..f3923c1e9 100644 --- a/examples/text/text_input_box.c +++ b/examples/text/text_input_box.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_raylib_fonts.c b/examples/text/text_raylib_fonts.c index f08569a2e..8ef67c71d 100644 --- a/examples/text/text_raylib_fonts.c +++ b/examples/text/text_raylib_fonts.c @@ -10,7 +10,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_rectangle_bounds.c b/examples/text/text_rectangle_bounds.c index 9ae2ba938..886c348d5 100644 --- a/examples/text/text_rectangle_bounds.c +++ b/examples/text/text_rectangle_bounds.c @@ -9,7 +9,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-2022 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index 4af542655..eb2a7843c 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -9,7 +9,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-2022 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/text/text_writing_anim.c b/examples/text/text_writing_anim.c index 6beb3e552..4ee67700e 100644 --- a/examples/text/text_writing_anim.c +++ b/examples/text/text_writing_anim.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_background_scrolling.c b/examples/textures/textures_background_scrolling.c index 70b67c3de..55fa2115e 100644 --- a/examples/textures/textures_background_scrolling.c +++ b/examples/textures/textures_background_scrolling.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_blend_modes.c b/examples/textures/textures_blend_modes.c index b8131354a..246a98e47 100644 --- a/examples/textures/textures_blend_modes.c +++ b/examples/textures/textures_blend_modes.c @@ -11,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) 2020-2022 Karlo Licudine (@accidentalrebel) +* Copyright (c) 2020-2023 Karlo Licudine (@accidentalrebel) * ********************************************************************************************/ diff --git a/examples/textures/textures_bunnymark.c b/examples/textures/textures_bunnymark.c index 2cca8b48f..ede3036fa 100644 --- a/examples/textures/textures_bunnymark.c +++ b/examples/textures/textures_bunnymark.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_draw_tiled.c b/examples/textures/textures_draw_tiled.c index f68f23669..34fe82ce4 100644 --- a/examples/textures/textures_draw_tiled.c +++ b/examples/textures/textures_draw_tiled.c @@ -9,7 +9,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-2022 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Copyright (c) 2020-2023 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_fog_of_war.c b/examples/textures/textures_fog_of_war.c index d89386c86..7c3d66642 100644 --- a/examples/textures/textures_fog_of_war.c +++ b/examples/textures/textures_fog_of_war.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_gif_player.c b/examples/textures/textures_gif_player.c index 099a71268..108522835 100644 --- a/examples/textures/textures_gif_player.c +++ b/examples/textures/textures_gif_player.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_image_drawing.c b/examples/textures/textures_image_drawing.c index 190fb8594..967cb52a4 100644 --- a/examples/textures/textures_image_drawing.c +++ b/examples/textures/textures_image_drawing.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_image_generation.c b/examples/textures/textures_image_generation.c index ed67be036..1ab08ae84 100644 --- a/examples/textures/textures_image_generation.c +++ b/examples/textures/textures_image_generation.c @@ -7,7 +7,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) 2O17-2022 Wilhem Barbier (@nounoursheureux) and Ramon Santamaria (@raysan5) +* Copyright (c) 2O17-2023 Wilhem Barbier (@nounoursheureux) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_image_loading.c b/examples/textures/textures_image_loading.c index 1e5cb6c5a..b1bfe09f7 100644 --- a/examples/textures/textures_image_loading.c +++ b/examples/textures/textures_image_loading.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_image_processing.c b/examples/textures/textures_image_processing.c index 49aacfc75..6e6c85482 100644 --- a/examples/textures/textures_image_processing.c +++ b/examples/textures/textures_image_processing.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_image_text.c b/examples/textures/textures_image_text.c index f4c919d53..50db688de 100644 --- a/examples/textures/textures_image_text.c +++ b/examples/textures/textures_image_text.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_logo_raylib.c b/examples/textures/textures_logo_raylib.c index 5afd14753..8bd865811 100644 --- a/examples/textures/textures_logo_raylib.c +++ b/examples/textures/textures_logo_raylib.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_mouse_painting.c b/examples/textures/textures_mouse_painting.c index eb6dc480a..e985cee0c 100644 --- a/examples/textures/textures_mouse_painting.c +++ b/examples/textures/textures_mouse_painting.c @@ -9,7 +9,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-2022 Chris Dill (@MysteriousSpace) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Chris Dill (@MysteriousSpace) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_npatch_drawing.c b/examples/textures/textures_npatch_drawing.c index 515ad9b52..f85aaa129 100644 --- a/examples/textures/textures_npatch_drawing.c +++ b/examples/textures/textures_npatch_drawing.c @@ -11,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-2022 Jorge A. Gomes (@overdev) and Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Jorge A. Gomes (@overdev) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_particles_blending.c b/examples/textures/textures_particles_blending.c index 7f1952f05..ec144ff4b 100644 --- a/examples/textures/textures_particles_blending.c +++ b/examples/textures/textures_particles_blending.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_polygon.c b/examples/textures/textures_polygon.c index 287029f73..18fc95249 100644 --- a/examples/textures/textures_polygon.c +++ b/examples/textures/textures_polygon.c @@ -9,7 +9,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-2022 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2023 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_raw_data.c b/examples/textures/textures_raw_data.c index cd0c9bf0e..22d7cc06f 100644 --- a/examples/textures/textures_raw_data.c +++ b/examples/textures/textures_raw_data.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_sprite_anim.c b/examples/textures/textures_sprite_anim.c index aa9f18aeb..99efe2eab 100644 --- a/examples/textures/textures_sprite_anim.c +++ b/examples/textures/textures_sprite_anim.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_sprite_button.c b/examples/textures/textures_sprite_button.c index 5e2a4011c..bd9891826 100644 --- a/examples/textures/textures_sprite_button.c +++ b/examples/textures/textures_sprite_button.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_sprite_explosion.c b/examples/textures/textures_sprite_explosion.c index 32c69849c..a6cbd3e5b 100644 --- a/examples/textures/textures_sprite_explosion.c +++ b/examples/textures/textures_sprite_explosion.c @@ -7,7 +7,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-2022 Anata and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Anata and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_srcrec_dstrec.c b/examples/textures/textures_srcrec_dstrec.c index 1839fc780..caf8f64b3 100644 --- a/examples/textures/textures_srcrec_dstrec.c +++ b/examples/textures/textures_srcrec_dstrec.c @@ -7,7 +7,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_to_image.c b/examples/textures/textures_to_image.c index 9acdf2334..fd3c848e8 100644 --- a/examples/textures/textures_to_image.c +++ b/examples/textures/textures_to_image.c @@ -9,7 +9,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 5ca186d71..97e1e79a9 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -54,7 +54,7 @@ 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-2022 Ramon Santamaria (@raysan5) + Copyright (c) 2021-2023 Ramon Santamaria (@raysan5) **********************************************************************************************/ @@ -1073,7 +1073,7 @@ static void ShowCommandLineInfo(void) printf("// //\n"); printf("// more info and bugs-report: github.com/raysan5/raylib/parser //\n"); printf("// //\n"); - printf("// Copyright (c) 2021-2022 Ramon Santamaria (@raysan5) //\n"); + printf("// Copyright (c) 2021-2023 Ramon Santamaria (@raysan5) //\n"); printf("// //\n"); printf("//////////////////////////////////////////////////////////////////////////////////\n\n"); diff --git a/projects/Geany/core_basic_window.c b/projects/Geany/core_basic_window.c index dc1bba141..c4dd584a5 100644 --- a/projects/Geany/core_basic_window.c +++ b/projects/Geany/core_basic_window.c @@ -5,7 +5,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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/projects/VSCode/main.c b/projects/VSCode/main.c index 2d7b121f9..0794672d0 100644 --- a/projects/VSCode/main.c +++ b/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-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/src/config.h b/src/config.h index d5f59a372..17334b8ac 100644 --- a/src/config.h +++ b/src/config.h @@ -6,7 +6,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2018-2022 Ahmad Fatoum & Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2023 Ahmad Fatoum & 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/src/raudio.c b/src/raudio.c index d1b47f64a..40b89106a 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -50,7 +50,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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. @@ -974,7 +974,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName) 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-2022 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n"); diff --git a/src/raymath.h b/src/raymath.h index ea3085a54..435ea3c6e 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -25,7 +25,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2015-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 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/src/rcamera.h b/src/rcamera.h index 37ea13a67..92c3c49a9 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -22,7 +22,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2015-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 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/src/rcore.c b/src/rcore.c index 12fe73512..b88d6e9f2 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -89,7 +89,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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/src/rgestures.h b/src/rgestures.h index 1703dbd0d..b63ac9678 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -24,7 +24,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 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/src/rglfw.c b/src/rglfw.c index 61922604c..940257ab1 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -7,7 +7,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2017-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2023 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/src/rlgl.h b/src/rlgl.h index 8afe1fdcf..072925551 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -85,7 +85,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 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/src/rmodels.c b/src/rmodels.c index 290d6e295..9bb54961a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -22,7 +22,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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. @@ -1795,7 +1795,7 @@ bool ExportMesh(Mesh mesh, const char *fileName) 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-2022 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2023 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); diff --git a/src/rshapes.c b/src/rshapes.c index 2262ae978..371abe2c7 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -26,7 +26,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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/src/rtextures.c b/src/rtextures.c index 3313a06f1..5d1718489 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -43,7 +43,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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. @@ -609,7 +609,7 @@ bool ExportImageAsCode(Image image, const char *fileName) 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-2022 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); From e2a8066fca67fe39ab2b5113d748bb04cd310f6c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 1 Jan 2023 16:07:51 +0100 Subject: [PATCH 0191/1710] Update year to 2023 --- examples/core/core_2d_camera_mouse_zoom.c | 2 +- examples/models/models_draw_cube_texture.c | 2 +- examples/models/models_loading_m3d.c | 2 +- examples/shapes/reasings.h | 2 +- examples/shapes/shapes_top_down_lights.c | 2 +- examples/text/text_codepoints_loading.c | 2 +- examples/textures/textures_textured_curve.c | 2 +- src/raylib.dll.rc | 10 +++++----- src/raylib.rc | 10 +++++----- src/rtext.c | 4 ++-- src/utils.c | 4 ++-- src/utils.h | 2 +- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/core/core_2d_camera_mouse_zoom.c b/examples/core/core_2d_camera_mouse_zoom.c index 74ac76499..2e94d37a5 100644 --- a/examples/core/core_2d_camera_mouse_zoom.c +++ b/examples/core/core_2d_camera_mouse_zoom.c @@ -7,7 +7,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 Jeffery Myers (@JeffM2501) +* Copyright (c) 2022-2023 Jeffery Myers (@JeffM2501) * ********************************************************************************************/ diff --git a/examples/models/models_draw_cube_texture.c b/examples/models/models_draw_cube_texture.c index b36e968f4..e43c3c844 100644 --- a/examples/models/models_draw_cube_texture.c +++ b/examples/models/models_draw_cube_texture.c @@ -7,7 +7,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 Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index 819684073..52dc95226 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -13,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) 2022 bzt (@bztsrc) +* Copyright (c) 2022-2023 bzt (@bztsrc) * ********************************************************************************************/ diff --git a/examples/shapes/reasings.h b/examples/shapes/reasings.h index 8b14ba6aa..657ea242e 100644 --- a/examples/shapes/reasings.h +++ b/examples/shapes/reasings.h @@ -60,7 +60,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. * --------------------------------------------------------------------------------- * -* Copyright (c) 2015-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2023 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/examples/shapes/shapes_top_down_lights.c b/examples/shapes/shapes_top_down_lights.c index d75d46d74..b09137cba 100644 --- a/examples/shapes/shapes_top_down_lights.c +++ b/examples/shapes/shapes_top_down_lights.c @@ -9,7 +9,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 Jeffery Myers (@JeffM2501) +* Copyright (c) 2022-2023 Jeffery Myers (@JeffM2501) * ********************************************************************************************/ diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index c68544030..1f3db0cf3 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -7,7 +7,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 Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/examples/textures/textures_textured_curve.c b/examples/textures/textures_textured_curve.c index 276670972..201b01fec 100644 --- a/examples/textures/textures_textured_curve.c +++ b/examples/textures/textures_textured_curve.c @@ -9,7 +9,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 Jeffery Myers and Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2023 Jeffery Myers and Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/src/raylib.dll.rc b/src/raylib.dll.rc index 4380d9784..c2a42dca2 100644 --- a/src/raylib.dll.rc +++ b/src/raylib.dll.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 4,2,0,0 -PRODUCTVERSION 4,2,0,0 +FILEVERSION 4,5,0,0 +PRODUCTVERSION 4,5,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib dynamic library (www.raylib.com)" - VALUE "FileVersion", "4.2.0" + VALUE "FileVersion", "4.5.0" VALUE "InternalName", "raylib.dll" - VALUE "LegalCopyright", "(c) 2022 Ramon Santamaria (@raysan5)" + VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "raylib.dll" VALUE "ProductName", "raylib" - VALUE "ProductVersion", "4.2.0" + VALUE "ProductVersion", "4.5.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.rc b/src/raylib.rc index 984017055..6a9546537 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 4,2,0,0 -PRODUCTVERSION 4,2,0,0 +FILEVERSION 4,5,0,0 +PRODUCTVERSION 4,5,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib application (www.raylib.com)" - VALUE "FileVersion", "4.2.0" + VALUE "FileVersion", "4.5.0" VALUE "InternalName", "raylib app" - VALUE "LegalCopyright", "(c) 2022 Ramon Santamaria (@raysan5)" + VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" //VALUE "OriginalFilename", "raylib_app.exe" VALUE "ProductName", "raylib app" - VALUE "ProductVersion", "4.2.0" + VALUE "ProductVersion", "4.5.0" END END BLOCK "VarFileInfo" diff --git a/src/rtext.c b/src/rtext.c index 1eb8f4532..f98386702 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -30,7 +30,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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. @@ -867,7 +867,7 @@ bool ExportFontAsCode(Font font, const char *fileName) 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-2022 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "// ---------------------------------------------------------------------------------- //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); diff --git a/src/utils.c b/src/utils.c index b165449b4..030e59734 100644 --- a/src/utils.c +++ b/src/utils.c @@ -11,7 +11,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 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. @@ -290,7 +290,7 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * 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 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2023 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); diff --git a/src/utils.h b/src/utils.h index a2b3e03c5..ff8246a7b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,7 +5,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 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. From 30b75702df9d5d8f1ce2b9c7911b05e992b777f5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 1 Jan 2023 18:09:22 +0100 Subject: [PATCH 0192/1710] Update year to 2023 --- parser/LICENSE | 2 +- parser/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parser/LICENSE b/parser/LICENSE index fac92d37b..125d7dbae 100644 --- a/parser/LICENSE +++ b/parser/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Ramon Santamaria (@raysan5) +Copyright (c) 2021-2023 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/parser/README.md b/parser/README.md index 4d00fde06..0c55f20ea 100644 --- a/parser/README.md +++ b/parser/README.md @@ -19,7 +19,7 @@ Check `raylib_parser.c` for details about those structs. // // // more info and bugs-report: github.com/raysan5/raylib/parser // // // -// Copyright (c) 2021-2022 Ramon Santamaria (@raysan5) // +// Copyright (c) 2021-2023 Ramon Santamaria (@raysan5) // // // ////////////////////////////////////////////////////////////////////////////////// From 3cfb9a6e83af9a94a2d1d57dd6bbfba738c09b2a Mon Sep 17 00:00:00 2001 From: BugraAlptekinSari <94199723+BugraAlptekinSari@users.noreply.github.com> Date: Sun, 1 Jan 2023 20:17:28 +0300 Subject: [PATCH 0193/1710] [example] Writing into the depth buffer (#2836) * Add a depth buffer example. * Fixed a typo --- examples/Makefile | 3 +- .../resources/shaders/glsl100/write_depth.fs | 15 ++ .../resources/shaders/glsl330/write_depth.fs | 13 ++ examples/shaders/shaders_write_depth.c | 149 ++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 examples/shaders/resources/shaders/glsl100/write_depth.fs create mode 100644 examples/shaders/resources/shaders/glsl330/write_depth.fs create mode 100644 examples/shaders/shaders_write_depth.c diff --git a/examples/Makefile b/examples/Makefile index 76a33149f..7beff2934 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -520,7 +520,8 @@ SHADERS = \ shaders/shaders_spotlight \ shaders/shaders_hot_reloading \ shaders/shaders_mesh_instancing \ - shaders/shaders_multi_sample2d + shaders/shaders_multi_sample2d \ + shaders/shaders_write_depth AUDIO = \ audio/audio_module_playing \ diff --git a/examples/shaders/resources/shaders/glsl100/write_depth.fs b/examples/shaders/resources/shaders/glsl100/write_depth.fs new file mode 100644 index 000000000..5167efb8e --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/write_depth.fs @@ -0,0 +1,15 @@ +#version 100 +#extension GL_EXT_frag_depth : enable +precision mediump float; // Precision required for OpenGL ES2 (WebGL) + +varying vec2 fragTexCoord; +varying vec4 fragColor; + +uniform sampler2D texture0; +uniform vec4 colDiffuse; +void main() +{ + vec4 texelColor = texture2D(texture0, fragTexCoord); + gl_FragColor = texelColor*colDiffuse*fragColor; + gl_FragDepthEXT = 1.0 - gl_FragCoord.z; +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/write_depth.fs b/examples/shaders/resources/shaders/glsl330/write_depth.fs new file mode 100644 index 000000000..36c70fe41 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/write_depth.fs @@ -0,0 +1,13 @@ +#version 330 + +in vec2 fragTexCoord; +in vec4 fragColor; + +uniform sampler2D texture0; +uniform vec4 colDiffuse; +void main() +{ + vec4 texelColor = texture2D(texture0, fragTexCoord); + gl_FragColor = texelColor*colDiffuse*fragColor; + gl_FragDepth = 1.0 - gl_FragCoord.z; +} \ No newline at end of file diff --git a/examples/shaders/shaders_write_depth.c b/examples/shaders/shaders_write_depth.c new file mode 100644 index 000000000..28c605c70 --- /dev/null +++ b/examples/shaders/shaders_write_depth.c @@ -0,0 +1,149 @@ +/******************************************************************************************* +* +* raylib [core] example - Basic window +* +* 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 Buğra Alptekin Sarı (@BugraAlptekinSari) +* +********************************************************************************************/ + +#include "raylib.h" +#include "rlgl.h" + +#if defined(PLATFORM_DESKTOP) +#define GLSL_VERSION 330 +#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#define GLSL_VERSION 100 +#endif +//------------------------------------------------------------------------------------ +// Customized render texture function to create a writable render buffer +RenderTexture2D LoadRenderTextureMOD(int width, int height) +{ + RenderTexture2D target = { 0 }; + + target.id = rlLoadFramebuffer(width, height); // 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 rendertexture + 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 renderbuffer/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 UnloadRenderTextureMOD(RenderTexture2D target) +{ + if (target.id > 0) + { + // Color texture attached to FBO is deleted + rlUnloadTexture(target.texture.id); + rlUnloadTexture(target.depth.id); + + // NOTE: Depth texture/renderbuffer is automatically + // queried and deleted before deleting framebuffer + rlUnloadFramebuffer(target.id); + } +} +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); + + // The shader inverst 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 buffer + RenderTexture2D target = LoadRenderTextureMOD(screenWidth, screenHeight); + + Camera camera = { // Define the camera to look into our 3d world + .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 mode type + }; + SetCameraMode(camera, CAMERA_ORBITAL); + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); + // Draw + //---------------------------------------------------------------------------------- + // Draw FBO + 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 Screen + BeginDrawing(); + + ClearBackground(RAYWHITE); + DrawTextureRec(target.texture, (Rectangle) { 0, 0, screenWidth, -screenHeight }, (Vector2) { 0, 0 }, WHITE); + DrawFPS(0, 0); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTextureMOD(target); + UnloadShader(shader); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} From 5ba41e4f7fc2af29c59705e2179e7333ff68b92f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 1 Jan 2023 18:31:03 +0100 Subject: [PATCH 0194/1710] REVIEWED: `shaders_write_depth` example --- .../resources/shaders/glsl100/write_depth.fs | 4 +- .../resources/shaders/glsl330/write_depth.fs | 6 +- examples/shaders/shaders_write_depth.c | 181 ++++++++++-------- 3 files changed, 107 insertions(+), 84 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/write_depth.fs b/examples/shaders/resources/shaders/glsl100/write_depth.fs index 5167efb8e..341c6115f 100644 --- a/examples/shaders/resources/shaders/glsl100/write_depth.fs +++ b/examples/shaders/resources/shaders/glsl100/write_depth.fs @@ -7,9 +7,11 @@ varying vec4 fragColor; uniform sampler2D texture0; uniform vec4 colDiffuse; + void main() { vec4 texelColor = texture2D(texture0, fragTexCoord); + gl_FragColor = texelColor*colDiffuse*fragColor; - gl_FragDepthEXT = 1.0 - gl_FragCoord.z; + gl_FragDepthEXT = 1.0 - gl_FragCoord.z; } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/write_depth.fs b/examples/shaders/resources/shaders/glsl330/write_depth.fs index 36c70fe41..88a4113ff 100644 --- a/examples/shaders/resources/shaders/glsl330/write_depth.fs +++ b/examples/shaders/resources/shaders/glsl330/write_depth.fs @@ -5,9 +5,11 @@ in vec4 fragColor; uniform sampler2D texture0; uniform vec4 colDiffuse; + void main() { vec4 texelColor = texture2D(texture0, fragTexCoord); + gl_FragColor = texelColor*colDiffuse*fragColor; - gl_FragDepth = 1.0 - gl_FragCoord.z; -} \ No newline at end of file + gl_FragDepth = 1.0 - gl_FragCoord.z; +} diff --git a/examples/shaders/shaders_write_depth.c b/examples/shaders/shaders_write_depth.c index 28c605c70..317f6ac75 100644 --- a/examples/shaders/shaders_write_depth.c +++ b/examples/shaders/shaders_write_depth.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [core] example - Basic window +* raylib [shaders] example - Depth buffer writing * * Example originally created with raylib 4.2, last time updated with raylib 4.2 * @@ -9,7 +9,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 Buğra Alptekin Sarı (@BugraAlptekinSari) +* Copyright (c) 2022-2023 Buğra Alptekin Sarı (@BugraAlptekinSari) * ********************************************************************************************/ @@ -21,9 +21,100 @@ #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif + //------------------------------------------------------------------------------------ -// Customized render texture function to create a writable render buffer -RenderTexture2D LoadRenderTextureMOD(int width, int height) +// 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 mode type + }; + + SetCameraMode(camera, CAMERA_ORBITAL); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); + //---------------------------------------------------------------------------------- + + // 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, screenWidth, -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 }; @@ -40,14 +131,14 @@ RenderTexture2D LoadRenderTextureMOD(int width, int height) target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; target.texture.mipmaps = 1; - // Create depth rendertexture + // 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 renderbuffer/texture to FBO + // 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); @@ -62,7 +153,7 @@ RenderTexture2D LoadRenderTextureMOD(int width, int height) } // Unload render texture from GPU memory (VRAM) -void UnloadRenderTextureMOD(RenderTexture2D target) +void UnloadRenderTextureDepthTex(RenderTexture2D target) { if (target.id > 0) { @@ -70,80 +161,8 @@ void UnloadRenderTextureMOD(RenderTexture2D target) rlUnloadTexture(target.texture.id); rlUnloadTexture(target.depth.id); - // NOTE: Depth texture/renderbuffer is automatically + // NOTE: Depth texture is automatically // queried and deleted before deleting framebuffer rlUnloadFramebuffer(target.id); } -} -//------------------------------------------------------------------------------------ -// Program main entry point -//------------------------------------------------------------------------------------ -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); - - // The shader inverst 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 buffer - RenderTexture2D target = LoadRenderTextureMOD(screenWidth, screenHeight); - - Camera camera = { // Define the camera to look into our 3d world - .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 mode type - }; - SetCameraMode(camera, CAMERA_ORBITAL); - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(&camera); - // Draw - //---------------------------------------------------------------------------------- - // Draw FBO - 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 Screen - BeginDrawing(); - - ClearBackground(RAYWHITE); - DrawTextureRec(target.texture, (Rectangle) { 0, 0, screenWidth, -screenHeight }, (Vector2) { 0, 0 }, WHITE); - DrawFPS(0, 0); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadRenderTextureMOD(target); - UnloadShader(shader); - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} +} \ No newline at end of file From 0ccc1d3686f7c9eeafd88c7385d908b23812231e Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 1 Jan 2023 19:07:58 +0100 Subject: [PATCH 0195/1710] Update year to 2023 --- examples/Makefile | 2 +- examples/Makefile.Android | 2 +- examples/Makefile.Web | 2 +- projects/4coder/Makefile | 2 +- src/Makefile | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 7beff2934..bc2c8d711 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2023 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/examples/Makefile.Android b/examples/Makefile.Android index 0570d8b23..267c4aa57 100644 --- a/examples/Makefile.Android +++ b/examples/Makefile.Android @@ -2,7 +2,7 @@ # # raylib makefile for Android project (APK building) # -# Copyright (c) 2017-2022 Ramon Santamaria (@raysan5) +# Copyright (c) 2017-2023 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/examples/Makefile.Web b/examples/Makefile.Web index d7e640862..a55a76b8c 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2023 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/projects/4coder/Makefile b/projects/4coder/Makefile index 4178920bb..0d254e340 100644 --- a/projects/4coder/Makefile +++ b/projects/4coder/Makefile @@ -2,7 +2,7 @@ # # raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 # -# Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2023 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/src/Makefile b/src/Makefile index 40a509bd4..fdc52a839 100644 --- a/src/Makefile +++ b/src/Makefile @@ -15,7 +15,7 @@ # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # -# Copyright (c) 2013-2022 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2023 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. From 1dbcce8b56933aa9983b81f33a2f6db64e93d5af Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Mon, 2 Jan 2023 17:48:53 +0200 Subject: [PATCH 0196/1710] Use explicit atomics (#2849) * Use explicit atomics * missed one * use relaced ordering --- examples/core/core_loading_thread.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index d5c00fc56..5a988bb27 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -23,10 +23,10 @@ // 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 = ATOMIC_VAR_INIT(false); // Data Loaded completion indicator +static atomic_bool dataLoaded = false; // Data Loaded completion indicator static void *LoadDataThread(void *arg); // Loading data thread function declaration -static int dataProgress = 0; // Data progress accumulator +static atomic_int dataProgress = 0; // Data progress accumulator //------------------------------------------------------------------------------------ // Program main entry point @@ -69,7 +69,7 @@ int main(void) case STATE_LOADING: { framesCounter++; - if (atomic_load(&dataLoaded)) + if (atomic_load_explicit(&dataLoaded, memory_order_relaxed)) { framesCounter = 0; int error = pthread_join(threadId, NULL); @@ -84,8 +84,8 @@ int main(void) if (IsKeyPressed(KEY_ENTER)) { // Reset everything to launch again - atomic_store(&dataLoaded, false); - dataProgress = 0; + atomic_store_explicit(&dataLoaded, false, memory_order_relaxed); + atomic_store_explicit(&dataProgress, 0, memory_order_relaxed); state = STATE_WAITING; } } break; @@ -104,7 +104,7 @@ int main(void) case STATE_WAITING: DrawText("PRESS ENTER to START LOADING DATA", 150, 170, 20, DARKGRAY); break; case STATE_LOADING: { - DrawRectangle(150, 200, dataProgress, 60, SKYBLUE); + DrawRectangle(150, 200, atomic_load_explicit(&dataProgress, memory_order_relaxed), 60, SKYBLUE); if ((framesCounter/15)%2) DrawText("LOADING DATA...", 240, 210, 40, DARKBLUE); } break; @@ -145,11 +145,11 @@ static void *LoadDataThread(void *arg) // We accumulate time over a global variable to be used in // main thread as a progress bar - dataProgress = timeCounter/10; + atomic_store_explicit(&dataProgress, timeCounter/10, memory_order_relaxed); } // When data has finished loading, we set global variable - atomic_store(&dataLoaded, true); + atomic_store_explicit(&dataLoaded, true, memory_order_relaxed); return NULL; } From 62f63f9e485fdffa1e981ae4ae58f5eb8ccfff8e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Jan 2023 17:06:52 +0100 Subject: [PATCH 0197/1710] REVIEWED: Avoid possible gamepad index as `-1` #2839 WARNING: It could require further review of `GamepadThread()` function where `js_event gamepadEvent.number` detecting current pressed button could generate a missmatch with index 0 (reserved for button unknow). Or maybe `0` could just be `GAMEPAD_BUTTON_NONE`? In that case, consistency with other inputs should be carefully considered... --- src/raylib.h | 4 ++-- src/rcore.c | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 5a230a3ac..1c3f77669 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -716,7 +716,7 @@ typedef enum { // Material map index typedef enum { - MATERIAL_MAP_ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE) + MATERIAL_MAP_ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE) MATERIAL_MAP_METALNESS, // Metalness material (same as: MATERIAL_MAP_SPECULAR) MATERIAL_MAP_NORMAL, // Normal material MATERIAL_MAP_ROUGHNESS, // Roughness material @@ -862,7 +862,7 @@ typedef enum { } BlendMode; // Gesture -// NOTE: It could be used as flags to enable only some gestures +// NOTE: Provided as bit-wise flags to enable only desired gestures typedef enum { GESTURE_NONE = 0, // No gesture GESTURE_TAP = 1, // Tap gesture diff --git a/src/rcore.c b/src/rcore.c index b88d6e9f2..0e9aa76ec 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -765,7 +765,7 @@ void InitWindow(int width, int height, const char *title) 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 = -1; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN #if defined(SUPPORT_EVENTS_WAITING) CORE.Window.eventWaiting = true; #endif @@ -4871,7 +4871,7 @@ void PollInputEvents(void) #if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) // Reset last gamepad button/axis registered state - CORE.Input.Gamepad.lastButtonPressed = -1; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Input.Gamepad.axisCount = 0; #endif @@ -5714,14 +5714,15 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) CORE.Input.Gamepad.ready[0] = true; GamepadButton button = AndroidTranslateGamepadButton(keycode); - if (button == GAMEPAD_BUTTON_UNKNOWN) - return 1; + + if (button == GAMEPAD_BUTTON_UNKNOWN) return 1; if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) { CORE.Input.Gamepad.currentButtonState[0][button] = 1; } else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up + return 1; // Handled gamepad button } @@ -6685,7 +6686,7 @@ static void *GamepadThread(void *arg) CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value; if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number; - else CORE.Input.Gamepad.lastButtonPressed = -1; + else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN } } else if (gamepadEvent.type == JS_EVENT_AXIS) From f2e3d6eca724ee88794b20934aa9bf9d818280a9 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 2 Jan 2023 14:23:48 -0500 Subject: [PATCH 0198/1710] [models] Add GLTF animation support (#2844) * add GLTF animation support * use correct index when allocating animVertices and animNormals * early exit LoadModelAnimationsGLTF if the gtlf file fails to parse * update models/models_loading_gltf.c to play gltf animation Updated the .blend file to use weights rather than bone parents so it fits into the framework. Exported with weights to the .glb file. * fix order of operations for bone scale in UpdateModelAnimation * minor doc cleanup and improvements * fix formatting * fix float formatting * fix brace alignment and replace asserts with log messages --- examples/models/models_loading_gltf.c | 23 +- .../models/resources/models/gltf/robot.blend | Bin 1899312 -> 2107372 bytes .../models/resources/models/gltf/robot.glb | Bin 1477024 -> 1602604 bytes src/rmodels.c | 320 +++++++++++++++++- 4 files changed, 324 insertions(+), 19 deletions(-) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index 68228a3f0..879715906 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -35,6 +35,10 @@ int main(void) // Loaf gltf model Model model = LoadModel("resources/models/gltf/robot.glb"); + unsigned int animsCount = 0; + ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); + + unsigned int animIndex = 0; Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position @@ -43,11 +47,26 @@ int main(void) SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + unsigned int currentFrame = 0; + // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- + ModelAnimation anim = modelAnimations[animIndex]; + if (IsKeyPressed(KEY_UP)) + { + animIndex = (animIndex + 1) % animsCount; + } + + if (IsKeyPressed(KEY_DOWN)) + { + animIndex = (animIndex + animsCount - 1) % animsCount; + } + + currentFrame = (currentFrame + 1) % anim.frameCount; + UpdateModelAnimation(model, anim, currentFrame); UpdateCamera(&camera); //---------------------------------------------------------------------------------- @@ -64,6 +83,8 @@ int main(void) EndMode3D(); + DrawText("Use the up/down arrow keys to switch animation.", 10, 10, 20, WHITE); + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -71,7 +92,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- UnloadModel(model); // Unload model and meshes/material - + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/models/resources/models/gltf/robot.blend b/examples/models/resources/models/gltf/robot.blend index d3bdac23721927c385d17f0c3dc32bd899dc6169..efe43c5e591d2b6edf1a52539aa598ad5a1e0be7 100644 GIT binary patch literal 2107372 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

E2@h_q`VbeY@rQdzc zw^GS^C12acz9jp`uZ0L=V)kJTize|31Kl#0FdHS%7e$7U@{Erz2?SIegkMJ#P zT(4t@B9-U~zbnly$P0NOFS#e=eKpaWX#QU$zC(_B-fc$ntkh`UZs%yq3$dRj9}Lj~ zd0AOTbJ3&Se}?aE^G9CD19^$QEBZpcS(MJp;QsEuZu2S$+bcEPTwy)7k8{l=!^?R_ zU;fa<86uy&6#CvD%7nk%mA_8>{Rr|x9>`1fd!jF*H__5`UdnzUO9nKm7qUzn;N_cU zT?am}17V2f$x9tTiNEOTbY92WByqK^0b$9ml?i%PS{adOSs`;*8 zchx#quc~@o_r9*(M`o2c2NVM@4+$^8;KltWWX_fgFNNMqVo5*DO;6{AJdl^+?>K!S zUr}v3FY+F2=^s*+!N_+*EB>G=c==beejy(W@mvsjY4#^xB%Yg-&I@@^UjAPE3-zWs zotLs7+?Da+xgdMhyTS)~!OLc|p9UZIm^ec;PdlmwDDjtDJlom_i_^ah3lHQa+j;0i z@XzEcYD?#(cfYw0aJzZ8v3ICza5q~m9!%7xJLIyvq3(@)iB%rydN?V>nOac4Ha0jdV!v{YHglN5D%Yyx=?D&=)@6O?_$d zTFu?)Km=SJeC?9=tBI8RS0!i%1wMr6cg=I$=Qi|{~R zsBh#e`oxI5xW{+Pcz@bG*IeO?(`G8Z9bsnv`RE)uPnUC_Bzk8$FXVx|I2O&{6BE9o zzZsDi`tF7ZbzRQ12>*i6|Ka*WslVL6rSn1_$V>KBju+}p^grpmm}^ppx;xE7nPpze zG}omLnN?lc^*B%1(b>WaE}Vmx@B;dD7A_%-7B_t|O<%|ZdC9%n@j||$aSx>0(c#<8 z!!P!s?-Dxqifr9x9>y&HvsBswc){PZ0Uzs3oS_ylkr_TH63_ipy1tMH<)z!LU&vQ9 zA)OboBm1;!IV$6xQ*9m^8t66O0xAE*1YTwfFSnRDctPm@X8NE=Jo-riUW5nol07Kv zI_k}t>Ac|6)#d?@vL(OD)EkY)fS2d-)vjH#o?iTa}KZgGMN9A14$!&e*qiOzyJdl^@4mlr*-bCl5 z^KuY>e50=%3v%;VUtv90+pTwx$aPGdcfd<{{+YtUC4>d9O9>Ck%lGR40{oG$Xu(HL z(lhL*#p!kq9xDA?@EGP6=d7zXmsyWfgudJ?ydW)2U#@2162fS~4EuLaS(NL#NO&MG z!$;+OBz#4S(|H*%F9lSxqT5tqJ&qf^v=-rIDhrnoMt$l2g*=d#=q}+!_==XL^Wyda z<^N?p{=KZKw#xn}A9(pr5&tr~1U^_KdU-l8$}vO&wn)GUlPyn!pU^@+iyaaFoaW9q}cvwRT(|y z-tlewcWhaO{)Ie{m)zevUdUJUw}p7IPru-c&1|xy&_4fCcCCEVu%mlrAAofR{O(Wk z**3P?!^2%}J;u<>veDdAze>HY#`5GPtQS3Ah?nQ*d(8=6%wW^bN8ubs!UeDIihhun z;r+rd?dhw9c+u|?kJRl+dHP-AlX6N+058L`-u;A$gBOJU?^+)eiHCouUhPPDATQbM z{eN!PFTz*!jY7Pbtx=!(*lda4AK>uJ{-}altnJoye_muCur^=3XsnRmB@g6f_^Wb0 z6279pE5u8$`DUTH7f|NoPHQTvm7gB;<*h|{!9KgxUsQXg{X60;ulgc9keBG&ZoNys zqVE>srLsG*r{X^Bbw$q8n>fi5!szC&pLD+i{+v9Jm+TXc7wS!P*EbXA>DKF8*R^l% z%%84>o>Kp?rSMYm-?fCkEKA|#Jnwzj%_Z=`B2jlbFXVx|40kxcOTMDl72yRBla*mh zc#f=$;vBw);N`LuUi|&^7A;*olDj>f7xF+}vR`)og?vSCE5b`zf1Mss%9~6m4uPVTc z@IYRouZzBj-b6ns#0%cA=_=)w#>B|hnOf$1WF@=_nW&}!`qEcKUp8v#;*sce>Aa8! z<>evaMfi&TsSq#zowefqv&5;`H~!c>r^mC74Po6hEbABhSVLNVu{7uld08a9AguJ4`(OcHL_f$&^cklw)T8Ku ziscf}7 z`tRa^m-nRj7k|CmRR%A)Y&tLGfxHZNx%btmH_^fe6W3q1Z`ic1&3^%@oR27#G2W4i z7F+L@@%VYBkbfZ$91pt{Cm#n}ELDTjW0Mo{>lm@(Ql&ga`7H z-6QWeQg1#uGB4%+LE+K9_iD=VT!q$kyddW`47&C{*LMo&Y3pA3;qr&^aMYK zzTe&FvP$~D;Qb@vfxP4%ko7M0roMpRy-spvBJRxRd-XK6}YPY&);)?oDb)Be5#ut=Y9Og$@$rhmwm3h;wjqs z6&l}Y)2qLklpp8A`Q@g$`ElOtkMcWwJ0AA!yxrXGAMA91MM@5-pA1W`SVlq0kMrUD zdZxSiao%h)b$)i1EKz#Mc`+=#X6Yq4KhB5q%g%7~V`2%#x1U4{2$aygwp=jAz!`}RMnA#`a{T+M0@00ae*x=z_ z+2MC~bm5~;U43DRk?|qPhP^*y;!)EN?0Vro=`UCJNMcIF8%o8*d?AjJX z%RM&3Uia;M#kkCeys<{TGweK%|M$!7`xfBixN)-C*DSUE;5M$0U+1t_HIr$&tAsC0&Bx$)`=-;bsjp#~L&Czkta2$3266U z%tCvjsTBUu0#^JukGObP;`GLpUnKIP{Ib8}SsrRN>^_#CG4K?N|G^~k`H|Na z*swe;6$|7W`DL13V`MQdb0*f$6DFA!Uv~zMk>!VWdyIho8Jnfx=W0*PAnT`A%0YqC z98!Li7v(qnMkhbY)lKsoZ7$5Vq~npvc(Z%}(*E7KC%hjOZib=lpp6oxjOuPV{g5l{Xw3$T71;V<}K#HzVqW^ z2b358o(!;`&puq}{3ouz(SJ@dEq-GBTP7Vh?G^H$lpoq)zpg#^KUyI6<4il91T7PFqXzcKMWAk98S5v;-RV{;gHwGlS^{WBSN^X}c| zFEp~$sSjIgc5WRsUmP<3AMP6H?cdqfJ-C0U|Ip4YJp=uBm?vO&Ztv>bzcVw?-EBTS z-`zL3vvcdgy}bu}2Yb5r?A+A9tAD7R$74?Y1jbq%V_=Md=aYeX3l=O`IPca&`<~B1 zJdb^M-%+)yzt3D2dmhIy*7XaF0kbr?3RAL+1n<9NChbM$t}J%Ix{xzzSU)^u!r@wX z-J@UD57~N$vo$W9ykMHWF0-QAg-^_MVf8E*nkMwuV%DeluKL@}arW8f8J)rPX-qLBq?Al|tpX^Y|bqCYl6Z7}k@7(U#|I~N=??hlw;_9$jhH-T29Mj9Z42*ku;{AM?b|UdL!cU2`3SVd_4rb<(AV4P1%Hui$JGiT}{< zD@+Rb3xB8$8DhS!C08Q4?H87h+#>h=42EI-a`Eu^275i|!~_=>tKW0?hoAK2Qmj1l zAA0a-dY=#1p(roPZ}<+mjv@NNde3%MfV-nVhB6M-*0~0gx8saA*xsM z-F?ar_3@)#Q;(_FF4uT|@lWQ`=cDd?a!B<0{7+{x54KRRZ!!7>9W$rJ4E1)e!T2E! zKVs%1?aBD)nm9gs^sH}BHJ|y;w#d8pXPlooW5NW}qAsEf=r!9dF1SU(RVi@b6vPXy=HyLZzIktvfjATWMIBIS$D8p7*Zx-`fnPJ z9^9Dt4$`R}AFn-9M{N5WCyhVjXtF2pyuc)p*Y}>0KgXdY`A^o$m^%f@AKJmXl1?%alDj_rxS7CIvj84%j=FI>#$i4)}?2g`)hUMF11JDbt(Bf%kT#N!XH}Tx)gE#qDhWdey58e^D7O9#c!NA zC*6O9?Nm^+=>LmMt?-B1tnRlIO%(Zk(7n$Z zNf_2I7f0tgxeZHLtp14)R<^D={)xQ&vSxy`>-PnIsZjqZR23@vyrgF@IqLSy5=Oae zD$?^}gTEzno?pZ)x6BIoqSc#@KlLHI--EwC-;`kg(}drgDTd>G85hbO%jWS^Qx5t3 z@jP~cNq{~QW8LE8i>$ZF=cB&OzwcqYKE~fh_sb(clqLLCbg0MjhkW4NpSb$O(fvsr z=7%^eAL85}7j7?gd^`3g#^<+}TK>MJiSi?l&u|}(=d8W|@fiVj zJKB7{dj=(sCkEvw1^CPW<64#9--`l#R)Aj|;IjjKPJqu1@Oc3~Kfo^u@Jj=HL4Yp| zFf`Vm&&vXQaeyZW_>utQeCxM&S%5DO@D%~RGQd{_xGKO?0(^CVrv`XhW;|?L^<{dH zKO?|116&>8SplA%z=jHgjXj^A*Z)-ei1w9iAF1^Z@wo=W!{;&X^ZMA`bIRwB5Ar7j7|+`J^(O`R%m7CLeo=tW3h;{q ze0G4(3GlfAJ}(sJKDoc0DC?(ud&&Ln zM03S2*?w|=JJCFyk<3r-ZzpDw``d};Zc(y)a(_E9lic4Tg!`qu74A*nT5CpFldke8%?IWBco*KDhG(^>KpvT3<5%+1+2GFTcMj zC*h?2P6@6LJ=^{$v{z?;#=pvb$6fEr`pw5!zxf#JHy>mD=3}hie2n#*k71ua#`?|2 zSikw$-BHW)f%Ti8kM*06v3~P0)^9#`&jaM!!}`t7$NJ63Sikug>o*@`{pRDU08a@p z)^C1!*r$(SpFW0t`WWjsA7lOIW31nNjP;w3v3|?r)6G7|#tZ+D|4j+5kC(e13;o~e z#xIz#`aI$1_WG3iZ^7!n1*^Xnto~XtS~K>cSp2nO@z;vQUn|D?*sm}CTJyzUD;9sP zSp2nO@z;vQUn>@WtyuiEV)56C#a}BHf2~;jwPNwtip5_m7Jsc+{Iz27*NVkoD;9sP zSp2nO@z;vQUn>@WtyuiEV)56C#a}BHf30{*AV2ZfnlJuZvG{An;;$8pzg8^%TCww<^9i|9xqzf6x3ZX%B+c9t5jB2v&PgjMj`kD;9fDEcT#S>_M^E zgJQ7<#bOVN#U2!kJt!7?P%QSKSnNTu*n?uR2gPC!ip3rji#;e7dr&O)pjhldvDkxR zu?NLs4~oSe6pKA57JE=E_MlkoL9y6_VzCFsVh@VN9u$i`C>DEAEcT#S>_M^EgJQ7< z#bOVN#U2!kJt!7?5Ul!tR^a~ytG)|XeHX0yu2}S4vFN*E(Ran7?}|m=6^p(r7JXMN z`mR{?U9sr9V$pZSqVI}D-xZ6#D;9lMEc&ik^j)#&yJFFI#iH+uMc);RzAF}eS1kIj zSoB@7=(}Rkcg3RbibdZQi@qxseOD~{u2}S4vFN*E(Ran7?}|m=6^p(r7JXMN`mR{? zU9sr9VAc1l1OML^`2R%#ZV2#G?);$bJsIE?LHQXD>-tLiSNjsI_9a;DOR?COVzDp9 zVqc2Iz7&gnDHi)uEcT^X>`SrOmtwIm#bRHI#l94aeJK|EQY`kRSnNx&*q36lFU4YC zip9PZi+w2;`%*0SrC97svDlYlu`k79Uy8-P6pMW+7W+~x_N7?tOR?COVzDp9Vqc2I zz7&gnDHi)uEcT^X>`SrOmtwIm!Qu6t(``R=x{X);iL7VUp9ofeB3S*2V(}-6#h)k^ zf1+6YiDL05ip8HO7Js5x{E1@mCyK?NC>DRBSp11%@h6JKpC}f8qFDTiV(}-6#h)k^ zf1+6YiDL05ip8HO7Js5x{E1@mCyK?NC>DRBSp11%@h6JKpC}f8qFDTiV(}-6#h)k^ zf1+6YiDL05ip8HO7Js5x{E1@mCyK?N2oC*;>bsn8Ro?}N)zhcpU#iIX;MgJ9x{wo&!S1kIkSoB}9=)Ypof5oEzibekw zi~cJX{Z}mduUPb7vFN{I(SO0J@3P)heHX0yEm-wivFNvA(Qn0~--<=Q6^ni=7X4N% z`mI>>Te0Z3V$pBKqTh-|zZHvqD;E7$Ec&fj^jop$w_?$6#iHMeMZXn`ek&IJRxJ9h zSoB-5=(l3gZ^feDibcN_i+(E>{Z=gctyuJ1vFNvA(Qn0~--<=Q6^ni=7X4N%`mI>> zTe0Z3V$pBGp}%(PuW^|ZH@wIGz4$3+g9Q!VaU-$Q=V8T8pI7W;zSHO9ik&`BzKQux zpL2?xK0l+_>2uG`EbsLB3B^vIvz;zq?Nj!5YM+9``Ki51zS^r|u~)@nuZqQ96^p$p z7JF4J_NrLyRk7HsVzF1nVy}wDUKNYIDi(WHEcU8c>{YSYt75TN#bU3D#abqdoZ^5eHibcN_i+(E>{Z=gctyuJ1vFNvA(Qn0~ z--<=Q6^ni=7X4N%`mI>>Te0Z3V$pBKqTh-|zZHvqD;E7$Ec&fj^jop$w_?$6#iHMe zMZXn`ek&IJRxJ9hSoB-5=(l3gZ^feDibcN_i+(E>{Z=gctyuJ1vFNvA(Qn0~--<=Q z6^nig4*j+2yIlWMeHX0yE?D(lvFN*E(Ran7?}|m=6^p(r7JXMN`mR{?U9sr9V$pZS zqVI}D-xZ6#D;9lMEc&ik^j)#&yJFFI#iH+uMc);RzAF}eS1kIjSoB@7=(}Rkcg3Rb zibdZQi@qxseOD~{u2}S4vFN*E(Ran7?}|m=6^p(r7JXMN`mR{?U9sr9V$pZO1@&F7 zU#q?gR(%(&`mR{?U9sr9V$pZSqVI}D-xZ6#D;9lMEc&ik^j)#&yJFFI#iH+uMc);R zzAF}eS1kIjSoB@7=(}Rkcg3RbibdZQi@qxseOD~{u2}S4vFN*E(Ran7?}|m=6^p(r z7JXMN`mR{?U9sr9V$pZSqVI}D-xZ6#D;9lMEc&ik^j)#&yWoQQF86;_-vz6_3s!ws zEc&ik^j)#&yJFFI#iH+uMc);RzAF}eS1kIjSoB@7=(}Rkcg3RbibdZQi@qxseOD~{ zu2}S4vFN*E(Ran7?}|m=6^p(r7JXMN`mR{?U9sr9V$pZSqVI}D-xZ6#D;9lMEc&ik z^j)#&yJFFI#iH+uMc);RzAF}eS1kIjSoB?RL4B9!T~yx%tG)|XeOD~{u2}S4vFN*E z(Ran7?}|m=6^p(r7JXMN`mR{?U9sr9V$pZSqVI}D-xZ6#D;9lMEc&ik^j)#&yJFFI z#iH+uMc);RzAF}eS1kIjSoB@7=(}Rkcg3RbibdZQi@qxseOD~{u2}S4vFN*E(Ran7 z?}|m=6^p(r7JXMN`mR{?U2v%H{N5bDFURlA4a@iD_#L|A^4;S*C446M9y0FpgAV12B@*94a=!NKqYZBjt!>>^2 zax44aeQyqR@x3|Y=gs#5oo|P(75a`f{|<6qewjF>hDn0_a6Dk*h2`hoioP$i%lb7dzq8Wimwnel>)(do?ZWK6 z?(h6DOdmx)^~9H*{EmOch54LkCkFXJAvtb-2RZk@-S`Xjx@}GS_Qh@Z2=Y)r{$%Z; z{=x18<`3W$Dj%lX4W$@mDVsH2xT$wv z@7^O-*B$N}n%{o7uX~`Xwg2Gl-2>)tVO*h+ukWuvJ~JMT4( zrSUupmA6AfKlXen{+0fA_*=hx2;ViQA9nM|`_~ZD-|}0mF5ms1mgeV6-1^%$m_=Ty zzfGxtB&-#2jO=goU+n^iIHDJ&`&+a*nqSXX<$U?I=g!|kev}vG$MrAIn{H0#e3^Ol z)A{)U4_o|BlX#zGOwJcb#NIn%zFY4k|9<6tW^IeZ{0f{e6CI=g)HZK-n78)NVM|Va z=cLOoT6e#_ugx%}$q|#H*Z#n+caFctg(DU=nZtd__3DammG-^l++QXCZUDy{`akv` zZU5uM-v=>&HegfpmJOS5#vJJ0Q^*Ce`bl9&*M9R?1G>!L7az)G4xW@Kn8tXnO z$q-+}JEsE2m%DqmUFWc4`X%Zy^_uzAcjD(OEVhm>c-XEpe3 zApX{m*PWEs|I*ifQpz1Op&a==m86*PoMYb zUH`btoL{v5F_WdoDpU4<;-emG zm%g^v);9mW&CKWcCoWh3{D-f8ZT$~deCy#?ZRs)dIo5dJHy>?6y7?Wm|C{g+KGOQJ zzrXL#o0#^i_(5CYySHm>;HS- zPZ~@c4R3zWQxE_Ag6kW8_qv-JO&g6h|L5F~UHrM%H!l47u7^z<5BL3OQNvrVnenNQ zp1rftv_a0?U|zugKNWSIG}ltoe0fzy(Xqy#O<0P<$`ozvzw?t#Z0n8DjymYM(bGqa zo<7>RYF9J;3CsNY*Je-r$t8_{{il;3`Ou+PHvHhgJ5I7Wl!e_knKf7>*VQJ8-9~bK zjqQb@S%Sz9`sx3wn!o(`x`s16AA$8<`j2%Prk5%=H9t1g#9twbqPN&a&TfHhzWn&( z^@g3+pxyD~$4|WY{0y$(=f`t~y>gbxm^szI>z$X_d5v3WdZ^%>7xZ`9!@d0j-Gf8( zy6-G)3w7GhH=Mw(uEE~joeO_^3CLuOEv?fd&yTgAz%OJ3BM&yXuAYapw_}Y-yUn*Y z+So0oM`!a-Hkr1(ak;U${+sN$Tt3om9r#Qy9dX4dQt-$1Z1&=MdtZX<+2LKku>0&} z#i2HbyOO+8IR;Pe%hXgS*K9=R@rOp^7b5s%7*D9$qzPINPce}pYP3& zhwY!P81g$b%j^qfANXOT%l`U|`$e7yDQ82O{Y9^?NnGDU;vSck^8fik+E1I2@UC~h z)9zVO5AnkFgX&{&jDax*#uyl5V2pt=2F4f|V_=MdF$TsM7-L|JfiVWg7#L$1{pZ}%A`*!;5;y8Ii_>|`dqNI!{>stQ_|Y|S*xor@5N;1 zv?VcHPY*Fq{OtD#9=*oYHQ(D}nEGDK17}=n*cj=1TWF8`rG0M;wef5|x^^w0hT-pa z$7{5FhUJH$%dPzX6TkYEeV?fMzg<|YrQDbPrK0a`4S(Rx4?+(pKgx^pi>8WRh+N$` zvPOO%`)qzp!2j;yd=6q?5XiShiGN7tNfgIBO~)D|lZN-2%>NT6Wrj098i!-#&rhIT zyr&QS4}Yi)>6r#?Ys`=C5cxGXebU;)Q|`NI%&#=e{^I|!_a$&v6xaGa3qz}@#3dqX z9HWW2A-E#S?7&q~kwq~f8HSmQj1J5=Gbk8!Mzg&8vZyhdAXyy6XHeskh)R4qfRMcV zToUyq5fe@HpXCvg81qabE|LE^Rp;xjuIaw__MKsw;r8!$Z}q7<=XBL~YVWFQv7gv6 zo1XNK^RwTm4Z1wSez2EkKPk_!Yqof7>+q;``CRt+7iB+tUktL2yEIs@ZR_}NEB&7R z#9pu;o(H=+&dBwSa~+j8lt0kA{JloNY^L7`5=8m>Ujd8#_yUb^_Iq_}*O^Y$!BcjB z@$fU~w4$1`*pmw1NGgA&q>;;>y0(gIIccIZ*^f0qg`v%HmuwsRCH|$$-{$qha;g_s zH3!dsv67dSWcxiwTxeJCq`b`Ob-#LK==qgUNX@jmgxyK~XhKGQ$n7YX)7eH3M?4Rv zdzH@B)3<57(SJg^z4CJQ?rJ{@%1e5(ropG&b;nu#i^0*~U}Py@sQw0@Q^uEj*|sS! zTKo3U+q3_iXSnoJ+KBubwB*mnFb#k9=Oo`Bmm{-b+|@T?v<# z;m$sEa8(&;{9O_MqR(gj8HHtPue3Dp!{zvSt)Js|3^DtOyGq@gyRVQks$212hn_?zX2NI9LD{Pty{ag!uuN|a_VpV4?QQUzi4Qn_!;(V+cEYV zvpKhZ2xF7W#r5ZON^aIDM_&pN?{pDhZj_Sa;fX>}QOibEpSqUL&-haqZiI`Jynm27@2^8L$`Zml&q) z2fOOD%&EQLr^gumjElT>>IGtWNfL~D4E+k6L%qog7g8KOufgYP>Uj-rr(U9#g@42n zF|J}^)*DsNX=y#LA(~jkajzR%_Jh4(KXTB9?PdwFvOA{`ZfZcljr1M=Hz{zI}lqNn;L{Qd~0_iltqSQE zk?w^bo~GvEHdX2ONO&&Ng?1WxP){UI3DMVm)lS>bV*Ku>pM6EXKa%;J)J^|T_`tR0 zqLKR zgHH$Vj*(14T_<&cJ{6fKz#jJg=-8qOMf}Uy&2m^Tmw~YFeXum%A8Aecp_tEcou|H` z-XDn@h*BPeJ6hW~hOMN~+`?+g__Jh4V`$>6*U3F4sk-c7i(oYu8=V<%=oIEj~ z6K)@WJ|`kv&*zY_&H0=OG@rxvi|Qw1J}1h4EX^mz>iHa{Nkt#?Iqp;Py(GBrysF=i zpV#(1wrgtRa9!Vt>!&^ZZq>osZs|kBojB%6){_?3mP%ZlpZ(@+^zFwJ0ItLv=g%kJ z)#}+n0cZhs-d=W%Nx%{4Y8J$1>47n}zgR27N zkIkOz8!6~+ZP02f#63cOtRzGz$;m(a&r7S*@4x3+cU2vV&QDW6Uv^f8j= zWZci$FfN}KTsZBFi@qoaOZyVi%z&wn>)slU3vd7~5CmTC?_Z3@rJS3DNUgxOq(&_f{`WK#G|MK#sHK=cICxKP;8$mzRr{AS}zSxg1&exBf1|=F8lTVf~%W z9|^Xj>HG7mZzV~p|IPES=>KB=72#1XY!Vf|v-t0r`B$%M^I+g|!s8r_Yg(z&L%w1r zjDI$NT#s+?J<2&U-dQ95P*_I?8Fo?M;$tPcpU0W>jG`x%PM!nk^Igg(<-3&PChBy5 zHg#smxP%@urdp407S?Dt>bsQCgL3aSgql4?Xym91g%*-t7w$^QL&FG_a=Ufp?&FfI^0v{hx3{_ec`*5HveFwQE$UuupjaR z?7Fk23-;qNPS`j{+s{8gn(fEGZS`Hs?Tr)c2YbPOhzINndPljrl)o_hp)TWi^F3GO_a2_in4WW!AyO?>N7o zF#hq=PEJ;id;7QE93lHVPB}%hyOQNu<^kd(&+b{I>o+8SD#^GsZac?;`j>=uB13r-Zkym$4|QY z;mR+(^V?q1(MvkjwdR3Ct0@lEbt_J)Y+qhk`OPy=dHk=(uX*^E3Fq{lvFBM8Pdz@e z^5U1r{@|fUS8f<}@ABG1dp^Bkm6Le#`sB*mYd*bv!zJTBUAwwCLH$WRZvwq>@fddU z?|V-hb70kB*X*zGh?}0#FaEms^)I$p^&Yo>HR+%(8qU+i2wcHFq}W^ljm ze^$%0?fG3x-}t?HUzs}WB-LLWbF_9RbiAS8lhx?=oQWyg&GZx+^2ffoy(%Xu#G^@Y zUGoJAO}9k%@H~1)7<=DM&K5Ca^Qp97?FcM$aM`5%7G7Z zTmG!m2Yrw`scezDMLfX=xwObZALP1*2j##AxrHJJeULk;EErGlL2lJ5J>Lg?kfYg7 zmHiP<@IfwJd5FqS&gWQC2o&TT@a(k2n<-i9y5qy(_#r>xWGxT9$Y)XU2S4P;oFdPc zcp(0EzC-%K5BV(D&Uf!(>w#Rrba6owi zuF!X~%*!*~LO))Abav4qYT=61T(oPgKXP}w=zf0BllZEhKT3b?WL2*x7HAiGb9FgA ztElCG9?T26H|3`<*XM3uK0Uj;eV@5~o(B8DUa()PUE2?K-9E9Q{Xaa)a#E1Qsd5|3-*J(U_ZnUcEvjH*|R_Nqz=>j z$$T0T@!Smi$(;O0q4DA{n7P@0YCoB@s~@4na7}~uH7ad`Wj#Yw{0GW^q$mp1&eO|6 z_mhnyUf_D0jN1?o^lL!FwWrkCUTR&0^taIeN(+sC8TwrqM_?R-ei-zl|HZ5S_`9^U z2#ES$%S$*tVecZ{Gv(YMant9&tLAIcbF>S+xw@!7pa=Cw;>CRf>(8{lHMEw2FML;9 zt&8w!6&C$>mKaH$AJqL_(#7i{#5LZ7@jk6mhxgf?T{K$#!m;2d0lz=fdyTN~`mzQt zQT%9q&r(C@g8g7G*bn&ub{*U)-c6k!G`Q0c9(&ji_JaMu4ZAA3`&+fnx_KKB_QBR~ z)qc=bujq-C!OxcRbv2a*zCdH2P4VGW?od-&OIs%N}fN9_JUyLR}_Mzj66azVVYo;Q7a=J?mlo;B;db7%7E@5a`} zoyny!7v$FFBV>X1GOg$3JaL5OKM*8v0S>^$U8!*aUiPzQOg^`6@(hNhJ-N6k*=~nt zqi5tL71Vl$;F1b@dz2I-kB)N#Tz~^`N!+P%0baIFAD5=)Majh-v|k|B()PN%q=K=y z=mVW!o(_6@RDVX?zy&w}m-Jm47vSYROm2K!IyxH`wb{X0>2s2u4NVQ54Otm8FjNaJ zK^E%B$>mjm@QC387vKO~+*KME;AN}yacN!B*|sER?czA+h75ca=Xip|PI+wL1ZI+Dml)R*ZDz6Y+OyB*C(K0o`d0)T< zH~^Q_JyMP&-`EeRUDnu=?9jC**S;N8m|FPNbdJ}|nEYXC>qBr++r@=BpZCdl4sZbu zz$JaJlq1PE;D>ADa&fYCQL?jr8N#!6@Ab7ki8vrX$J^xBv&>;@+om0baJe zjY~`O#S3{$Qs=_lnx$#XUp_r&e*5`MAD;yme=ayoI?=alh0rl5cD+HZF@= zd23(&{FWuTb!E;`{N**d^4rg6`hXBz%=%@wFrgg%fD3Q{E~y8kevy1*tFv)g(A=UI zDA3sLJ2 zM$lm7MCnM~MUmlkP`HVUMm(_TAv@Q?=6NwPI7+a z^%}63XTP~RKftboJDL7IyuqE0zOnc0C-#E@*aQks&c)f;1%75OFxI_7ayAFShiIj|Rxb^Ps$y{c?o2k#l& z2Yc3w)6|~Q2S4HfmQM;x{Mi65M^ z@1)=D|Nkhyb013m?Tjba_I#-3?wc-Of7zDX*1dA&X*CDGv-guz|Ffg|1-D1Vf#bn( z<*UDV^fx^^7DNC4pRfMQ!yKv2n;7_-MzP*_2*u%0)irPx!kIKuriJruzj%97s0g??1Od9eX!3y^UNg2$!{Mle*vm! zJRwQr?_K>W?1L@rgDvcX4X*{bUgh~I%tv8<3j0GbZ-xCV*pG#Ek(j44we$FZ=NNcB z-$+{Ec~aS@i@8n@>yxI=l=E=KO)t>n*z}_Q%}+rO`sb;IVftp%oDyGH5BcLDtpg+f zF%PHak>;^1%}>D%ou4|XY&4;fX?}_y2K?5CG=y4O&rj+5$mk8q0F19-FWAp*k$fTf zV@D@0EH9gJdQ$Sg>BskaCF{_i-+;YfKa^+KRpW9}t>e^g@G)ZgLmQc1Nk5+7;Ol(p z!#t!(Z+-)GZFnDt`^8oK-3Wg@>X+Iq2OT9aV#sX8QA2Vt97x%5w@48D^tdup@6}JvMAw#}q#xBwum9JR? z^YCTfYoEdY&-R-~`5OI=M<}&KX1%EUvfVDWYuty~=Zyy6Y5VgrQF?KTNo)JLtxd^U zv^p+|Gu+?C@Dz)Uap(Re`jhBif(`vn?2|@66YoLsUKIT7?yj=Eo_{W*z9yd~QA9gO z!`ftb`fRn|IXzX*J-JNxBN9u)`h(_E!tYTJBt#GDfmC(K`_HQ5sUF}<`j_i!g0v2d z{6{)f|5C|P{}OI!|8fs{_sPHL_KhDF&}%(iOY8onzPA_#ISKp0Ua%jXXRr2Q!LEZl znM42mC+r3Lr8~9#U{__o?jKNp^63q}EzkC7UIUNsE!<*PVj}RAxDny1f5~OZ?_a*` ze7)#t`bC#N=I3A3`xiYv82azeU@zDY^)c+Kpx+7YCs`tk{f7Sd4)%imfD7!Z>24}- ze0A3@`Pj*8AHV6Bl^w}HKXKuYukZ8DLh}Xx_n>mZm;Xjm)4BYI^#vf8sr>C>y#F-s zAv@SzZvWbjqV;*wFM2@w!RQ}FrH{H<>TJzT8i8GSMCg$~KHB&2Y zy;Z#fZIXL-3s(Vw0s;jD3J4SsC?HTkpnyODfdT>r1PTZg5GWu}K%js?0f7Pn1q67m zu`pj)$Oi=k3J4SsC?HTkpnyODfdT>r1PTZg5GWu}K%js?0f7Pn1q2ERu6Iqh3reIx~xu1%RUn4 z4U5*HQU9%Sz_8j!^6FnmmiCdr?X!=>q3^b|4Iiz3(d*dxS*-UJO3*)%C?f)_fstS= z^t}-{1a7^3Bw{bmez$7-rNyo~scp4%>e)s=*+f?zV$gm_*xg^nv(ww57vKun=lZ6z zq?mu1?Z?ihu{Sx1leI$W$_?(otM2$|kO=pIq zOBPR$XSP>=O1kQ5JU6ubb(fvs@GshaZ&DbSzkA6*VfNF~d?~WQiH%zp z+Q%@qBCsDpc@F3w?`-g`$nhRS#>R8+k$9iimRwwZ=Awp+WA2~LHLA4}=g#g(ws*|E zuz4}x%&kjyT-w>%HuuS%xiu~7qd9Zy8ZK{cnzgXCeev8})fV^)2ow<5B@mcJ-zZzs zo*dgoABAZdJD=-}T_W9uL>3U(b_h_{;851iy%DpY&`tDij!WlK6sa-PIQf{(%ya2b zB4T#W*5{rzTKygp%XrP4QI}lMu%x9kBUgTbKd&v#PmrWioo}e`&5rt-xL?<9dWCjV z2|0gbnszfiNQO(9So(x6g9yu?{@f)XN$`oA>au-PCG1cl5R7!|z{XhdR#YG3iIy zf7N(#zx|Rr|7?}GeI>T750^gz%t5?y?x_2yPDj+!cb4z4bG|tuDw6&12QI(?xFptS zT!5GT2>(0DJN7&5j7!RnoY1Wi{<(Mq7vKO~Qa{wV054mYkIRnz4m;z*XV8URu@BER zlmHjt09?|KNqr#s#t>1rJ}wQ5JDVF?nj2z#tdlfUlhkq5vBd(>m_grRzmt3>8ouWF zy_XsOac_YOZ~!ju<5G?!-`IM6T;hHSSZh+>)lN2*_vgF769pGDZszxZ+s%NO_rL`> z0GGrQ8W-SYtMzf|SlG~%`*&TTXJ%efkk;-;LvS&_H=f&l6X?;7s)raQXiMrC7o?cVrSX&@{$V1;s(kujLV9kw?|#!-NnBFF2Dh}q&7(X zBKgMF?Bf#m%h6iXKzu)XWR`NY)Q)rv8n^%l;F8`b^^4>iTcwZ70y@LIqjOnHyoDH3 z@{$V1;tI(xrmxvfFEayT-UAol09@Q3OZ_7G##Vb?%~_LcXYrRA8e124bhbA%FYb)f zE}l34l4N6Nd2L?85}7shVvNVsN7#`QdU1q*F5bWeH~^Q#e@Xo!`Nr18a*WEN)~4nK z&9tz_X2?RHlc$`iHXAOBOy-#Pzy&w}msDEn7s)ra+CYBkq$LCOtttoDeA|-FhReKu zJRGe7--`J1vaiKD_xPv|^7tcDg@{$V1;z%h+QT0fFIsw21H~<&-X{lc% z-vndiI6f{-$(GKB`nc%Su~d2g(0PFRFI>OO3q;QEzswK#$0L9XZ~!ieXQY0Sd}C|% zaq$qTZ)t73G}n5@L!^9K45?r&-WFVfzCJFE!GKs4Z~+d$CH1V-FOqL;r9Lk0$(DxB zW@=Y#mW=d_yrhCEuNGXQ@5w*nmgKB8lvhxTMl$xUQ&?O?#h zmCmf4e6ISSUq@qdaUK=_%>2Xx(O69VU7mlrAmHm0v;EuHcfbWW0GIR@DMylTY{ecf zOBOfBo|GbGvq>zUJYvS<$J90&&g3O&%_A0S+E6hn}kXb->G3?cvg}q^UWN z7s1u}i3OrDB?~SW1-Ro89xj4|hs#?Z=>D$c8(XV~OWVR_^wsv*RmIeH+62}ax0f7@ z1;0Py`o-+m?Psuj!nlun>)|3e0GGtuf{Wl~>-BJHZ@##>seXQI)3W+2TH9NiY{m$B z28)%~kXZSJF~kDeenRcEjf=_fNt1|q4_ttQhfDQAkMTO4l;CBn^>FEEUPR-71#vAu zGe5CFG+ojlf4?&^4x#Pjo*Gg)2|XPnZ^MvVM00j0T-~t?gOZrNwUnJky5AboxYdAe1^=^;6Cke*QuXoQ22E?L( z3vd7~iK{g(z{^&u%aQ7OaIR@;4(c#CRZ=u0vv!#>KdtE zB;VNjEL=L4v@d9Aj4|IZGp>Lg0isV7TqcEtvDf(#{<(Mq7vKO~(p^%HB;VM&EL<8E z&2OebV|{C5W6P2bdTV0y+jO-SC_gV(0b2plcz<++5oWg2GYtQ@x4;EB02lXKDMylT zY`qpP?G4N7FK_N>p5NS(+o)$o!?N-V;t1FgAo@2nKFZWi&kT{t)eE=)2jG&pPRfzw z8(W`+OV0fk=;>F-MU?zk7F=fGVR^W~1vmhg)Mqs=z{^&xagnnRjP}~*+m`grQokf3 z9&%2Ee=gp@1vmhg^yj30k$hw8(zqCa(9~3CvYl3b+nn2uK2vb{TEru!MEK|84P1Z& zaB;sN^^4>iTh}?&vnJ0tvwE6BCXX)6K#Uv5XKAOW+L4Yy0~g=`Tv92iUnJkyDs_H2 zNAgJgF3dpmUk=QIOH}!?(-KAl7vKO~(*G{?i{u+ywl6O6x-bLLf7vMYZdCu}Ogqvs zXy5`IfQ$PjDMylTY?UFn#2Ok8M0@nTEbY-WJJK;|-~t?gOXAB?jwIjMD*NJcd27p( zMLG9)FNno$KV8$`&VtKX;bJ-Y0vF%_TvA_=awPf2R@N7nCbe>-F~^rBdYwnE-1gHo z-5|J_{ausuY?Fw24_tr)a7kaUaRFYoT9z|g>6Uyp3D`$_MHo86rI0Mm6XPalJu_GOW1}?w>xTJ=?qHqCTwn|-&rco}5-$t{p zfoP|*%`?>6k&ZzF7vKO~5=UxWfS0W@h)ax&B8nJMTW4JQQ0c!!%_r2wRcP=77vKO~ z+)EXfeUZ|E{O(>3-Gda zv7A}&Ha09uwns(K79JovTfJ-Ula2dG-~t?gOS)0x0=#U!z=e(v%>Giu7AAUHo^qzD zk4iZTIo3e2oo+J6yaz780k|X(Qm;F8F*lKh;t!oKxmzrARtvatuwskHnZ(UG6r8$}N zq8Td^(*>97Eq{F2Dh}xUCu&;AN}!@=NPt^%{0jvZFuCt8Mi;(b?MR@i}P; zX96z30l1_t)3^XHTN=15rj2S1E%BS`bMurl6>8O6n2_|#Qd*A{bfJ?ev>KDm3 zwp!rAnT1Y6uJ34z^}5lKHwoo4W6GKFI9G5n{mgSKP7DUbqJRr<04}M^r5s7Vv6TXs z%V`yRa%p|+O-L6gx$=H(Lwa4>qo{eglk7;xpn(f;04|9ur5s7Vu~h;WI_0D-Sx<|b zV-_Vk=cFxJPOF<^%9-)_LKa+3Hko7I0~g=`TvDHr{37|rRtsE~TwX6l$>zvR&%S^b zHOJX#5u)xC!6j6?slR4%+Z~+d$#l1%Ii{u+yEpTa6{kr%kmP}2yE=qQ`FDtLh|F9EYznkg3eH31H zTEb}H0vv!#qD$%*$v3ua5Es4ss6Iv~W+3{zyiPR}E~nX%jzI$#-~e1w-5M9*WvkTZ zc}%OmXz~p1?$XB3cxN5|>Vq#HF!il%x$dM^xY$Z#(zi>!`|}97<0AZX@dhry0l2uImHI{Ujjbyjm$rtE zj@%|Rr{~ORKiQ}MU2ySz_|9K_)liz+m z)0c(dV$L%hA10KeA8-KMPBneLJO0c*no$?wVt|6II* z3vd7~sV_=7l6+(9vT?as`Xe@96pj8%RCq4_FTSVuO?*GV?AI@!3HZk&fD3Q{F6l2x zIg)&1YqfD{X})-2XMOIQ%cn7adB3)mJNF4L8v>E@ak(nsACCYozyY|pU(vV#FI%gP z%Z}JLF-&loNqhNlnf30~@XW&nF2Dh}B(9foB>BcxZR4__xh2*?N>gI;+s|jZOzM~5 zyp*3`qTVZo>9YNR3vd7~sT-smNxre=Y+T}=9y$=`hR%@u@(Vk1LU%>@=i&`qfCF%$ z?>nmZ)4xTJ59`bF}MtqZv5dCd5zsCi6VX-xVbG!9@~?v0Q;JiLf?&6?Gf;@bpe+JRKM6^xzJPc zlrwdoD!3d&*o0kU!aQ^I11`V;xTIG|Ig)&1s{<}=4ebpVw>PxKeF-}~PdPIlpQL^r z=a-G9r*VIjaUb^;(klx0=#U!z(qAi^IMnZHXG$@;rfM+t%xn>YjB*OQU8T; zi8^NGXp=eSJ#YaIz$Ni*sb3`D*lK}`ejgz7-Z7e`ftdGxSn^9SZhpOcOfVo81zdmw za7lef>KDm3wo>52Bj|?3&9TNttcyp|<<%q@_dq}+B3DcOGSl!g=cPVw_{Y5kF2Dh} zq;He@Me>cU7q~Pub~azmukC44Do($yTFKFC`BdcI1RUAi_TvZ{PwP zfJlD<>w7s)raE)y3z z5q`mvjySao?MKN&#*V{jf=kr;!UJuA7&LGJ4#35|OX?TNH?~R>myWh%xGZXJZ*PrNiX7*hJY?)R{8n%={mu6QhnN8|?|}<&04|B| zNjZ{yW2-fB>F9{_W?E~Sxn#acFg3Cw1n1>5ofCF$z-6!Qp z@{O%JfD0F-CYvKOJu?p(TjN6^xR~o14z&eh(7**a0GIUr8W-SYt2A-RebtMv<-D9% z%J?$&-P6+Fea;RLx;(-^7jNJK9Dqye-I_J(97xGGwk{JFn&WM1jd8XV>*Dq(u8bXr zhXj|PzdxQnEEo`r0xrM-xVX2>Qn&ywTPe$#?*kfQez(Qemk-VF12UZ(x<@`rMFzM4 z2M?EVvvq!vd}BWXxO7~aY)N)zKTpb*pP8qesb@FAsy-Vw>PxM>bFiOIa;O~Th5Hf zTQUwX{mgcH?_fYI3b+6V;F35+>KDm3wo>47@j`lSZwKZ;Pt8-#RGkuB%yEDjKGGy& z-UAol;Nfz*#szrUYJp4hr8G}Rn*{2cI~zJ;e2D4H#Uw;o=4_h&8>CxkmrTbBIt zAv@ABXb%^`0l1{jka8sX##RYjWSvy*3&=!~oHN_6g=G9a8*ZnAm*0QcCm0Zm0xrM- zxTNRJQSEd>@UoQxm%w|H7@e4bcu&ILv&n?ZzILQz(7**ac(}ZwaRFYoO5noB!7o@w z=fTHpmiRpQ1-HT+BHKJO0^d4;R4!xFjz7*=imK054mQg^SuZQNNfD z7szpFEx73I6X)hGXU2oSXOjt+{Y~bW_rL`>0GD)!;39b0YAsymFX^OCZ;qI-F65WE za%MdKQ^wO#=M8+=WR7_cTz~^`asOT80=#UsHRtKi{9wHl7bKf!ThrObM}4Z~7!d+4 zz`?`i5y>x-Z)}wwF1&s$=G+VGs-Yb^x_XI`$*f4!N`E)%y_(-LS%Nu`9xj3da7m4M zQPnTN%T@(kysxX-3|VOJ`)XPEG6jEL>X*ABn1)68=i&`qfCF$zA0@a5UbZgavLnZh zCkif6>r~6^NXMXo3vd7~?lBq{;AN}ya7o7gWUf+Gm#3T=kCKq~$ej0fP3D;Qzy&w} zm(+0@7vN>91uka4&gRKP4@AH2Pg&-7cgqxkEMdR}H~^RQSSd%6Z)`QdMZG)9Bg?4w zM|sGZ@u-q{sd=WSv5&7f;2)0wF2Dh}xUVK3R`a`(Z)~l7aEbLbVaM@s>Bskk`CPwD z{kjr6(lKb@0vv!#;x&y6@Um3`mu5XKuD>+qxOgDO(+7prFXsHqa68g5Xy5`IfD2Rr zFVJv-3&uxr`>oR7owckj|L))eQokI5NLlXg79EFVT+$@4c}!ZZ*S&-aCHE84M`_yD zW$q`RJ*}SJFE=II)##`rujNVo>66-V3!{-fqzCC+b+k?&(hXTFeZ0zzz5$S5+F0lI zN*~gL^reo`=|j4aKJWb&8IR_@I$B!uPaIWaTl-UoBTkuI*bnJJ`W7Cm(}#3J$Lurf zDP=8mUI8C6L+`L-e?b$P9Ea1#6kI=K;O>wk-jCD zFQQ|6>My5*Z93{T0hXLyqz`V+f-xA8KBNcf+brorx}h>OeJYjU$lXP{;N~uwJ15eI z^dNodaoT=JH#G9P>DI-nexXtb-~3!o*Y;{3kRGIO)p(sgq#G$P>d)A7!O#JBTiOlJ zfc=mjqz~apH)tb$(xvO9u33I_OPs#`yhBEuGP#_-ImEE)+b{L&y{@___6>UEkbbsd1pkv#*r8M#^?b9Etr$dgT)|GR7 z7>`v{NK>2MRrp^fcqD$Q=|G&TF00e@bJf~?!=eM+eTY^W;HdSHT><4>!_Ipj+ z4|dgwZL4*h-VI22k;C*M8=3wM{oa&-j)+e0Uo(f3{hX@n$QDJ;aEIQ3mX?vm&SL&$ zwjb+VNCx^yQ2Bk|TcGWqv zul|&D)zvs~L(5-x*$EE+qV30dhRfeQWS}tnX=$#PN<~xVI1aW@*bnxC{g94?*j3Z7 zPKZ|P(=HkZ%%q15 zD|KiD?>L=B57ATI(7tEtywV@4_t@g5e<$PBKZMLX;2{}Y^uwVC{qR(%zJJteRhY+k z%%U?qh=MwFm(S7rtWPCbn#XY9+VdDAXda`pXter8_rrnTp9wd{&%Rg#<}viW%`v!X z*bnxC{g5iyRnt9$`70X`=5&8w)7>|2;B^N$qkOqA)Qi0^j+!qt!aY6eM%!-%6(k-< zInD^0$LK7&)oG+(cKdPu{}cV}i#6af@8LJtUAA9>a7DSk-5N(lr7r}kDAt|AKmFDE z`?=n7Xm7<#I_01(pG_+vDVgW+?i+7ZNd#2_`%j$#R>pTE5qSO)f^RKh&cEj*Ud%U+ z7L;Hh*9J@#O9F_Ftd*d`1)(mmZ(ezKnNj_XTRb9G5H( z_i1e%pXSnGhL>2hez)TD53|C{EPA*_yB58hMelCWdsy@ci{8_s_p<1{EqbIyf5@Wu zvFLp*dOwTi8p6cu!xnvjMUS%R11);AMIU6*2V3+Z7X6U`&F?M4${+F%byoxYY&|nQ zWvaIH(^p2l=U{j+Ka^439SjfVhcbF65)2RKhcfD=2g8H;q09;9hcYLaAIh9yekgN- z`Js&3m!SS&ekgN-`Js$vnuGko{7~iu^Fx^v%nxNwFh7(z!TeC>1oJ}~^`?W*3+9J1 zCzv0~oM3(^bAtJy%n9ZPGB@eB>mz=mf2r{0r^ITnd}-0|o|-|P+Ec5&>m$Fa(vYY8 z7iceSc{@BLJ?_PZzL4-X-F|UA!pe)g&Q zF^m4VMIUL=V=Ve8i$2<-kFn@uE&4c%9&6FZTl6@K9&gbn1n6NFe#4v~e#4v~e#4v~ ze#4v~e#7Y0Y0eFPdltlRm=nZrm=nZrm=nZrm=nZrm=na0_uQNLi)*JK&F!zBKlf7g ztx0ozYtmfbnlx%~&dK!6T235H&*%Eq z4CngRq`AH|X|8Wgn(JGW=K9v8(L>iy+)LHBW;oZkCe8J&NppQ`(p=w~G}pH#&GoHG zbA4;lT;G~Bmv@uq{A$vNSv2QYGo16QNppTRY0j@E&H2@*X(}o}yUykkGkh_Vd^(CO>GmZDHp6czUq?VL4o^&mEN|JY|MQ*Voy`D}2_B z8Gjz9n>0^R1!<;yd`eaS$)vgeWYWR%T&ns{W_YkXms;hyl;#zK`hw-T)Crd7Qq_Ml z^#{vysp>zO;lc7;YL(|wCs>|KRsYG950>Xr8gU2X8!XSIs{drlbN|VtgXOtY^`Fe} zV0kWeg5|kX^`A`sV0kW8{UGW>s@flu zpW7dk=Jv;=gXOhUwLgA1mm!lMv^&o1uSJ)aLjDi7XNCMfxbnYUpL_X#Y>|2o^Ktet zE}lo^_d|Feg!e;;?NJ{wn!G^2gv#Cih9M9o$KXh&}8Fd%=F`ceMRrS1o>Mty5LH5vpkHTU)m7WJO<3 zp0`QBFF3Q)^&Ra{4@_U65iZ_vLe?qY=Db+Uzs%*2^&U@oFzo%Tme%W(Z*v~V`dTaX z|MY%k_pjo9NAG`d#YJRy7MJbE-7Ul$`-{`R)$xyt=LKg@zVO64K3TJ~mA8V$-LLQM zM~`|y7Jq)P^;udF@g;JT5xi1eM}%hf1}?w>xVXR9xBxF(i;YX`{7aIJG2SIkZfcIf zZ9mX=Yio?75)(1`?dLNt zmkTbzuu_kdB{Uu#=LWa{2jG(WU#SNq-`F~BT)c9W<452Jq8xQnIl7V}7M1a`R(f`# z3>vrq2jG(4tZ@NehKRbgaiIgu7SdYds3_Xv&N;+iK8vfSK0!HosQg0bIxsGOp!zZF z@^emDXl6g)0vv#g`?}OGl5cD+VYpn8evy1*E3LSiwT+k z!8lSU@$gz_UC%~8xrft|w?;*;B+u0n@FnS>+#xi^i=Ywi>rr=lyhx3ezpr(*^W~D2 z^ot%Z@>~u7E+iFU<3&9et#@p*HtU&#o`n5iFW3(W!?+*xj&l7Sb-@_$9787iwG8ve z_~w03%g#f&FUz{s9HYec>!Dl;xgxsfa{hmR`BR@`X`jFSxC&cs>pms_DSqAL%CEcf zjJNi$eCemBO+#AujrRyZOi} zmR~jgi<4g1^ZnDOe)MM(nSbttwNL!$$VX0p{oGp>{e_V=4;?Z0xQaimf1Rhloky?x z*5mvB`)_-Hy{=;d%l~5a9aZxVe{J0b?_d20hkxnaRK?-%Pp&_51!oc`j`-}LBM4E_IkN4BZ_VWu>b-&22h==c*? z-Zkm_%XX_W?Pbd6e&1br(;XkHJnUNsul>{kJryUPe|N3{Q7e$?;xCAsi!y%YrS77hXgM!w^h_u;r&UOlw0?l%;%eM+13~5PxC6Te)gGZ z6~#si#qD7D1zEV^TycHIFw=4&yR7Z4!REzU;0HmRnz2fA8ke6Lpz?wTg5Z@zQb8&#zMP(&6l{ zWVoMV`18F-GfU)!{;a!Izpp}nHc|SXx1P%r?sHDkn^HGnzoEw&dc*rD`BLwXd+s4w zdVdVp=s)0`kKLVaI^~FeUOS=W?LXEVwZBkH>rsimx1|R+4g0}fuwQz(wjb=OQ_1Hd z6>s!Y%=BT3{tbEZnJIjIHloe=s}7C|=RDWvoTSguIZ2-*eNp2PR>^w#oTRY*g<2Zt zB(ar;)&={)Ua((kH*G)IRi%2}m!-VnoTL{lIx{vm$P2;o%+|xY=$s_Dq2=#JIwOgH z(e~p!!{zTWvP_u$v^1}YEEP@s@S$_Tey|tphZMrDn!boIZ{CP7$6@(UA0J)LpBsqLe(%7+c9X=(@0nCQd{ejz2ow<5RS=lbI={73 zosvWcB{ezfZdXaWN@wzl@+ovMRE{scb}48IiOm-Q>KYu%vZ|9{^b@+tFY25meh-=0 zLyt4iZ%zDn*t+r4>R9)HtQ+rHOS*X6FK+0#qp)sV;~o8vaR=-L`=$4iaiiEZTEk!n z8T#wSVK3M(v5(ja}KoB1P-;Ireb0|-hE4azHjVlUXw-A~&OcHPmbjj|u&m3bWp z&%14_*>1#O$GET`>;?NFHYkUh#yhwx>+YX5aqWdizfyTz%Qd~9fA%(>^UQkfA@gqj z%8INa9z!PkWm?CvN16XL^WIZD$ZyzwZAa0(jqIa)fac81oQnAz^lxBiF@@t^Gg0ls zGht{liNO8H07Wz9)$Fn2)?3y35SHWnXn9M#Ko$@vAW%S{fItC(0s;jD3J4SsC?HTk zpnyODfdT>r1PTZg5GWupq!Hk`%EEl%kT!84aRmel2ow+~AW%S{fItC(0s;jD3J4Ss zC?HTkpnyODfdT>r1O^m=IP-}IPChKKcGX)uIkJl4q|dluKl{wiQi(A(1@k&gK2QqZ zPr&ySgw6ZTLaI}LHv!*Oz;_f<^8Ib_v&-vR`8u6`BS!GK|7WPRoxB#--TL;g)OTtX z2c6r!`$g(I3hqt%9I(X2-Rj(m)CDK2@4*UN&JNZELl4#kr;=K4&Wd(wM*E8~J9T7Z zMGp3Zy*&GUO6LdIHI~wTF#Bg}M;xk=mu@;CC9v8#FC{yrkY_)!muEjI&#>!G8Tp(e z#ph!V&C9c&*bDYUYExoY#~HbvPB5*ZgGk8b%ciw&-2Yd%cAdF(YggCqFCKmdwMsQ- zX$8e2=^(VBig{d&GV!zhrc=X@cJ(8~%>4kguTiqpOcyRf06Rq(Gw+&SR#M!hDflza z|IRQWHZ`*}&WqI&J-rd(KZ^%dDLNDaO5BcRH54|V{ww)jke#mbT zdFVyHh>{0CiOV@d`je@7x~OCPe?!bA-_iCp%?PN z(Z(PAkWcKd(+|Cn2adKp_#wYaz@t zBQDQtS6xR3H2U8)nN{?eqFVZ8FMq59&lP>jVGW?od-&O|`*-<{*{}rLyzyP2#6NU8qT*RId)BP;&Yj60!fb3^+?ib38O0gyPhSCwFJ!5Dna;c7JaL5O zKM*8v0S>?={kD8}OzdnwYsTbr>n6`&SlW||^Em5mMqX0ESezlaq=Mca?= z@vg=Nc-ar|ap~x6Skz_*XQj_cb~ZFMbT(vV%)n4BxCB}JIh_8qUBCr60GHHzQjR3w z1fT6VJ}#|GI@^}StX&-Eyu74>vABW0JI48CMbO)$e1tr&oNs^&Z~!jp_oW<3zOfbh zxGbQL8Fq9oi+R%Tl)R*ZvA9BTF@5(lX4(BSl1+}>?tu$%050wa8W-SYYpY$>*plqf zwI|oU9aNZFn(3s`9Iu%%`NP!Khu}gZB9Aqn`A7%@7vKO~5?iHyk$eMwxHc{qCtDXK zJ9Ga4@zj|7c&_PA&4PFEjBKTT05z!uAkqsq}^r>rO#3PF@U*xxWh>fQwtAaRFX-ylh++G`B3Wc{0&cV)BRS`qBHkJyIWb3t_O= z?1ZpHdLh362jG$@)wlpJTY-&BTe6)#yR@ibabs>L&`*!aZ$F>u`vjL@m|ySi5e$e$ z0TY8@>dDV{Iob%Ok^k>eG% zPS72p*9oR%onZR!a!%`n{;d;)9?W;S2Wh=#JO}Un&|fDAd%=E*TAd$Y*X?CHzmM0S z*#|qeLw}th>;?O!r98u~HX?(I9{TG9VK3MZ_JLjfb%Ddx`+>eRB;vV+b%OEmvbniE z=XHW;SFuhI?W@fXx<7qQ=okua+;xJ$)m|qE{B2C^xfJxj(BDG;D|MOff8o79);nO` z0oHBcy+58)L6dv@zW?i#2mVPD!@JR7{Dk6$V)Vbj2iN`i3bh{1bNANo`P0Syd*APh z9@HQ17X2MlJXg8vit02i@B6z)1nB^CUt+v8$?ChM>-HpBdfyK>^nL#c#iI$0Oy4av z@q2(lvo0-NM5~q=SJpJNF3yjH{^6Zg-xe;C8>obR|_x-z59pu07?{Pj=d;|TW-}iI*O#k|y(1ZFjQ4ykVU8mHO=zm@lq>-oy_Z@26_)`7Ps&0~{ z{wLhf{^z?TqX~^n{ZA9Wu>Pl(*8NX?uj4RO{?Y!yUY`B7=zIaY4({|0{qN@`#9pwU zd(fS)@_I|yRojpIn^QLWw%ogRYnP_c|K#tgA0y=q{Y|VlW!en)^{6}D|D;>#e>!E< zqdm6d24^(s3tRinQe6J{H-_469zWYxk@Hgu-37kCCie2|C-pJxs&P4~)^QdY_B)*H zd4!@@62oc%??%FsuRFBkz=tE!TJ^Xey*G=p5PjZbHw|_lU??A4I?x_ekquxGtlC5Jy z@i+STweC~$pTgHouKc>GEb-62;`qhk?faZO;%_MFu6(}ziE6_`-9GlOIeq-LZhdEi z&8O3DW3Xe>(>IPo)92sLudlLGmaAWQp0%}QqHUYgZt0VM>gAbxZmh7%PY;Dr<6&Iy zz2`V{D^9JP@Yt)pe|h@m-jYAR-dkjZ96^UXTQzqpW;Zd%y%g&GG)z3aNt)iIp z;No^L{DLgpaIUz%e%x5H$SHMP_eP~;L=D^K*=L?1{O}D{PkTv8$*==PsuQaG_!P^G zE2U}Didzq?C}DiY(-XYf+>_tsZfHRstrQ=dT$N5Xo6=^l%rQ@QXU!~%u!`WZSR2Ve- z$xnwfPt=9^Da=PIDVtv3bY<@~h>-i~t&k{rDg8g7G*bga$T{ZpnTBp0#A0Kxwr~S1bQZzSxymyH& zynm?^8sp<}R=DQIKbM{E$`#b-pw(oQi$|1}k;bXTU>_2q|p2PU^8X7$R2*Gz5`SI^L zi5K(TNZnb`hZ7%%>#N9dS5H^xgzifoJZA%XKa1X9g;Kv;_*q`@_w+uB{oQ)bhePyb z()*K_sh>H_@Nk-a{mafjnP`5`XNL2h5R+!D+36rhkjh1#pWV9?*+{N>9JOnLJ4@m*g$gFgK!FWV=++0s)8HxUS0H_ZMfcP`<+ z#{Sy7zw#V&KQ^=;)S~V7=i|YoIylENH9OyPEM>>(toDY+OLN~}XGJ5Fe6b(rSdJQ{ z&ogrmm2=t3<$SXfwVS?5*56*M-Ovb#XFmJJx&Kp6@`wk{7u+QA64iYi$bk=X3lG%s zhCT>($&?}Fzz4bIA_sktbDTq{W9NA1Sb`67n-0>?gFeW04G+qJ4|49oS`PXk=Qz{V zE#e72$W0JA=!0C>@Sq&{Ah%HDpbv8NCR%mr@jUQBE-iA<2f41{K{@b2Zi~o4ALN=! zf^y)4+?Yco--*007sL~MkV}ai^g)i^fU2H1(g!}sE&Ygo9`r%3Yj{u&e30|Xi#!){ zb!9<2fDdxugFeu8W&ZQ{+{=`dL!6tpMriOOf7|($)5-GShx}%dhh9%!@-Os49{i9W zb(qEpdhuM>vJ>RN5BY@1Lof1glsxz$zf|O*7v;^)mv}z-A-_uGp%>-O&YzG6Kjhty z>hwb|o*N|(e#lP{dFX|FIs!lNLw=#iLoc2iB@cecFBf^}1%7sULHfZD`7wv<^g}Q3 zi;@RFzbSS=5|z&}bJ{E$x^r{$p+_@h3t z%MbV=ze?nx*A=@*$%7yAn?xRZ5$`B@@I!w2SRH@p1%7?%A*nyW5BUk>wLJ79{!#Ma zhy3Ofv^?}8{-_^q{J;H&zMZ@-ir}otQz!P87>m|}7_56CG^}}j?DC>)Jae3fNbox>z6e79%J97y;A-y!2mZbieP^%aVtc7`c%;9D{+HXO`(K!kLjTq7 zN1|V6KBtx@rp_mi2WTc>KYDRiS5_an4?I<_+f~o)*jJqcpYGDGsW+E<)c+~bgZd+} zBuwA0X>9^u*oTv@3(%T~{O5G?rPet(Z;~vnbMRdKo}9z^JvmKk(LTnyveD|7`8_%C zyM%?Ui#2fWAZSIC#W2W8*bnxC{ZdzI`@ya{ndM}^H=p#AOmz1jHZYBS9DF~I!J_Ln z?RfhDKtB^GY{I113RPW41;*b8(BnK?#J|k;W4*tnALoDj($c(-!*P0?$sruJ#NeV~ zKiCWQOI)Sx2fON2vHji++OJBlQ!(wgDU1E;!cnAkDs=9(EEC~sJ2#5C`tTEm5* z&!y{69{=cdM#X9$)elR0obM8Ul>K^$pMO#I(<4{TFyV!s0tWSk@&|jterQi&SDnZ! zsr;3%r(bnA9S4eTVy53!HzoLci;!%_jpg>we%y;0+V+FJU_ZbIyK1_d?8g_m-Of6v zyQ<3R{^(k#>UpmFvR#k(G@i++M z9*l$1G7iFgbXwjc;5`c7%Ph3sD<$cHJP&;)xd+oj_&kDxN`I#ELRj7BK3J>IBM>+J z1!-@;t=+iEMmqpKs7F#~gv>*CUsk8}%Q$G>XM=Q>hmY1E%6SAV%jXe@+h@HukAn^> z9j$)R^@xez{1D@@1}<6LlI!~{ksM^fupjIN`=!64^8@TU$dYA6cj%u-0DHlH?g}Z_ zVdc<5XM2!gbrC<8%OC$5rT((xQsg{FUc10~1Y$4PFD3Oc>}un(z3C|XaofYcX!~)y zKJ?EsgS}us*azhh^p0|)?1%O*%6t)qTAWXX#wAJ=zbd;hZg{bK8(>q#FyJ3DXKb4b2g%jOsV z$L~M1nswa1*Cl<@fBJ(T<9&{S>!1(b+3?^8Q=f$A{H}LuAK5y!26VyQz6cyv_T4^q z%hfMD&&7#<-+Zg{qt-EdkKawSwefhxy!bqVTrMuRRW$y`cqaaN1bv@E`-}tU5%lHQ zwmv~9`kqH%&XpK3DV#@OfRW2>;XH!Ec?5;?2*OJW*Q;usq|8TQek#@dmi}(K_+y=| zz1|q}R;G6DTVj5S-_tjhWSF1&ydJl@;->Bt`T^~ldULt=<~KwS`sazeLiBY%RjYO4 z{DF&N%};SU`BLxc)!Y`%Pr)_%bpCt#CORvVf6?<(tPl8w%};4*JwK)ISz_p1upjIN z`=#&J_Cx+qDdzLOompOA)N$y&q0r`g-jgEW!TF03;oPsPGS2%}?+H()6#yqwK-BmO zkBS;!>Gy>Ef+X1bLvFCUupjIN`=##H_JdtjI+gv7+u)~}?Wbwee&6#M0(Y=qM0lwE z?j!r%M_T%4Kc1K#+V+FJV86tD+J3OBHiU04O_!7X#u)b0zWBdQZtVB;KLkU-)ng0e zJw2TV(nGU@Jv0W-Y(IWZRQY2K>dim>>wCHxLKn&(>;?O|(w@Su4!@u8{$$;~_k2$3 zPff#q4;4GkM*8tO3fSidMDqpvF>QvcKzUEE$1aJQvy`V(JklvHEhUYZU5YAyJ;cwp zi?W~AKogL3`PvjYB>rGO*bDYU;$T@uws8>fdrIml{S^JLrS&*S-{~q>( zyhFWAq0Lfa2^)v08And_AYi~aOCXeiqc z_JaM=z1n`Tt7pHRY`ro{Pv-K+73r-#BEpBt4j7;g{>(M9|u*HH9P#vY`;toHpf9)Qzq`g7smGM zp{fP_+QB~#%3M$CaZu5a83$q9gK-dYAjU`7pNVrfymLIP^~#f}Y;iqyJ}G#Bnj4w~ za!xOb-nZ#~^)>z8N!;|0bbFinFYO{8xw@!Fpa=Cx>V`0V>!lvSzRhn3=`0VOC|N|0 zgKi;N-nR+YUn}9i4?3OBrsiLCJp%l`PS`Pi_NAqDZKCgOj=@dCez2EkKgkcU>)=l1 zZff7=;7&t$>^=L5y*d=wop<~FZWOi1?G$iq-uUpTy|Qv9;f$tMnxXy zhq@Q$dw%H5#`fm6PChyG;<4k#jgNXz9{!v9aeksDk?(B# zuw7Ul{E%M*IdPHS``Ah3!4LUSzt`m(dXayl|n zk_SKJ*Zi-3KJ+5q$cL~Co)3P=Zx(sz#dD+N!4LVwW=X&3LHzCfhv$PI@=HY?dVyb* zJoq8MN#vmy`OnVBcs}?c@BUH8A9|7hqU6C3`3WKqy@+>|Joq8MP~@Q(`48pOwjcN* zpB8!O1%6TT;D`Jck%wN`JxU(@koVe0q(kag*B`3T``KebL%+F^{# zQ=bjC$B?t_0J}mT#+6JTKsBl!myaspMj&iX(*2r@%f(GRr^nCk3)+R=TwP8N&(A^+ z#s%&bVfqGeT;6>fjWK9k4!6%b%@MSZIQMb+2h<9b6uA07!%8WlQU)*_D?YWI-sd2hFF8>#m1->v&=l3IgVO|X3+SmKn<8qoK z^v}aOz378t{)N{?ENRwXVS2T+eujDPJ#~=ZaQ&&qf>^>k7y6kJ|;e=+eeIF z?e-o01-rfGJjLgAT}y4$uc&_KeQQ%oJ{fu5p!yAFbD^nxfi6&|AASjrCW&KHwNB zXIO8A_K|5bT>iVV@qyuMw7m$^a!|9x}>ka!6?Tl9RN@7?|Keo>@!ji8$wBxOxW7-UtU^pT6TkTv~ywX`n z`l9T|Z4dvxDeVuG!PRg4hW7q|wd5x5SNQikDjnIL z>n_amoDGr7bk4?>uRhIXBydeH3ZAp^nw+yy_t)nuPW=9}y=%U6zLJ@8MRVm1XHBU% z{MaA%vVuEiUG(@}hb1eIp7guG*%iO|pVsv>$6Yy@`<#cpGN$Z@Fn z>gq+0&pzpq%4;is*Gq9wabmsmKG#}Jaj35N>)MJ3I%ijodv*#vzvJOIXI|TT$3^c< z`ujd3E1mAt$uBNEaO3Ac_iwcy+wHp>QtwZFvgDKl>%Md0zioK-`MYYr@xjy@)>XK+ zKLW#ldT^Ef)Li{I-`KM8=>nkUDx7&vji`U7wnx*QzSDrTHniq4QI@e-DF`tk-J>6Ti!R4@2w)`vC>m zRnZD-zMG-#IPb?~1HK-rJ(2QYo#ex0u%Xo~^nXIeaBl0nE4lfkea0o^s_&UBC_1rh z0qG0N%TUbupXVSs|JzrclGeWFz0EgRX)b@f9%Qqu&w7-A3xUD&+jM*R%ex`b0B=}rJX+ih%4NRii+|+OuLXr+p^Aut&5`*e855UZkc{; z5B2U}+6u4jkhOSdQ}E6o?=+&;g5cfRfz)^9xk2v3o?f&;<(07VminZ$9pa|Pzq^L> zQfi`hqf-zb!s$_avqTT-o^XAC*L4o|B6s&=O}>)lcN?sO+|afo_jmt{-wE`yFTDHb zI!)i(9D|#N{a`QH52-@_(Dc^{>%TvVFpd3D8<;+t>KJ}k%=N}9VZgg$%#DLaxUh+% z+m019oYdcP8KKs6-%VQYCXTSY%!0`OCz9>L=Ek+OeuR0ieFpzO+b_ZOyOihKbFAM( zvieR9Mq0kTJJ6Aw=j=@?X+yleh@h5q|EV+J!T9p`_VD~81Yefo-*XZ#=6lni4<|kj z*H@9_uAZ*i?0w0D=lenPSsNz3zoKaEwD7aM3h(KC6m7nKU%(;y+Me|O^hh(lA4ep_ zWgJxdChZy8H?U!hgfWr5MiB4x!O!k{geBhH@3x!cykDH*-TkH&>li0j+{78WT}xN? zuYH3a)Q|3`-&Jes&3HOhMbekXyZTCt4siD&IGuc{G0eIOlBISHZfN`Ve(`8RBh$Nk z)(8B;-rZ|y{qA1hv&7K3I6rd#1oramSE2I*>^itpyvLB8vh?gH_JaLTo?%yIKR$!7 zYUD=Wmi#VX(>IW3j|5anIrI7S9p2?fg!8<=d4{Aq!>+pc28ZAHMfFecjHq{qy6)Zu z-u+7b3;O{VSL~|jSD(=J<+=~oZTb2Eb?#A3b$7jZTJ3Q+KS%w}>=)|KF#Cn<{inH! z_lv8G`Ip&#tT)PjT3W}=yw4un!NI}hZ#ktO?dkXFo~qNV7d6uvt`XeJqRW1>l|iHs zR6rmH1iU?+pUQVn=j;p0CpR^#O`SPBFAmE~{n*nv>Q8!a&!)eL`*-bb`GKF9%Y=!4vIB|$mxK@NP-2l_b~crz|M=etSlh&@EE z*c1GSudN62;D`JKk%wO3i}*n=v68Ww-^RF$@x)@bH!es{iew{SL`~tlR5O) zsJmh>&wewt{etCiE{%uUp7d==^t|qkOg~M(mnERMRNwqcXw1WdMz{pSQRCd}0(-b# zc1lY57hV4TPU8YDe_Ten{<1GE&Es5-A8Q&~mskG8Ua((U>SNedrCQBXXnScC&Zc>a zzV<7MK@9)L_)69Fw4tAHzU)xbMPa*xG z2MCK?<|#6bg_r z1PTZg5GWu}K%js?0f7Pn1q2ER>?#QGTxDUta96Q*A)N&T3J4SsC?HTkpnyODfdT>r z1PTZg5GWu}K%js?0f7Pn1qAw!K)m_H9$q)>UEbQskyYf^UCA{6_ykS!DiuPuu6XAg zVIK_k#Xtu8Ww8GR`(d!Z2K#Nm|2!$io-kfl&O1gQ$h0r!art(GxTy|v#`?5M6Z#n;A-K9YqiHdOV7ZCNn7?$OIG2({qi+O11e@A?t^ zjfiVhz5u;-t)^F!2fh(8g6ziE9op&hjR=HGFdUUng5UU9?d(~)(s?gKKH)Zwe^L84 zc9nBNU@zDYprX8k-cfGU{+r7vuVk{{-X(t7Ht%;-I)% zU+&{~@SeeMM4a{Fv|bj<_Ztz;f%WUS-->TU^vE|NPRjC)h{x|cr7Bf_``WMl?~f^t z-IWjA^5M$o-g&im+-m)ehn^Q5mQhy^N{o8Lo{*!NAS^33T!EZz~&7V<4 zaj3fHf}1KHc;cg#)4L9!Z$y-@-mv%Mz1N+6a>b^r`I{H(9_l!!Y5mCi{=4eoo42fc z`t94+{qd{|tN%H+cio&zUavaN?Wtl63fJ~WVBPFD)Lt(0$+`M*zA@$E-}n7S1m`8_ zxwZa}6LAmMK1nC4o&rByZa398B4oU@D|{p3*=L@alVHdQTRqg;TsX* zr3dFwr9PeSIaC)kHzixQ$x&3ltd)UHc+@lcj`^oPe`&%N?T&dzyXp6|o0#~XK9{CK zyP1?_+It-($L&=)1tK0eZ)uIhE1MknAh%iMpbzqxbHww&2f0yi>-0h&~pBV2f3vp2YtSrOCxH1u6#MU;Dg*Mk%K*+M?WIZtyW--3Ts1Wt?( z%Yz^CDUpX>zBMahF7@)JZJ zdSQRW!;U}rA-_=Mp?8#|A2{0b;D`Kjk%wO37ll9gA)gj`=tcfRKCqt;e#j#ndhz@) zd38>jv>3+lr8f{To=XX;)=5e>a{VSZ4&b;p47u~PE zZ*gZca zp8YP<`2qQ3+s8yd!Sx=bnn#c7-wl01Bd61E`uCI_A??{u>;?OwJj1R#B5nisOnpw; z!0Fq0G{Jtb7wm@^x?)$y8M&U`#MF@cqzMk5FQ`?M+Gh8kII`rVAG#fRn=*Gw+&SR#M!hDflza!k82IYbpPqULgi9*2DcT^taIeN=(xIFO1jG??V3#{W-fo%*wg{wTRr|6epJ& zu=GLw_CrrcW>(FdK}S%`XikVTaR-^9mkhW(yi9+n-;BoqyEv) z)azzgme%7qaP{}9SeCz6RqBkO?+JAOfA+2fK8o7=Z=t{fA}A<_AO#VMD7Sz>cgm&9 zeJdaeg%-9_+S-=Oi$w)je}K0lpn@0GzXys6NOz}*!lHQK5Ai@0QL*|%#T!N8pO@s# zWU{mEo@IfVex}JwUS3{a@{;dnGqW|C&^!2f0N_XaEdoNiKmznMF)f!QF`VF}HvjYf z!v3>YKSIwjczC~_*v&iz&g1a@!v6C=$o$XL4^OzxJO$o!%KHoZ&$E=z%e-GJzsq38 zi`506e@@N|=TGb}>_6vStpCstS?izaXR|%CMYjZbPqe#~(&Brf-5glpd!g|>9MYJt z%b_v&jywHkV(27W4fx$h@_R_1k>6>9JNWwd1ljb5_3t)P81RF9vC{aSXpnSrUD$uv zU)X<$hp=DC`@P>FH^Ce=NLYh}G995^+`TTfk^9*<+jO^$*m4<@68KGw8J5EuA1Cb4 zcV_*^c8*8~lTmRpUf6%wU)X=xR_s?sUq}3pUVV(TozmcE#BXfiUl4*_;lgyFG2doO zA!P{Wyx|@vVd=*{zxw-LLrcIvc)Rx#)zM=r>c3$Ar%Mo}X_cJt5$L~h#NPOMq?n9j zLRNU``*anu9#;lzOOG=6O;dL|($Ojfzf%fVYN_^HsVt#_|6p&r|Ia^f;^$90|9%)h zm*VG6{2l_o2a&(efa*q*HK!)&H#kpSg@j|OHmCyaG#m5w2fk$ANAu8sFUL6#Mfdp= z<)Ht2?l+Tn6PdT+!q1;$C7R_1`w#7ei#-oAhLnZpL3l{dn6Yza`sdFv>T}2KwJEK>Nx43Atf@ z{);==HWG&m@zckQ)bcsW1Gax~2U#ki|41UiPoLQSPJ+Aq{sX!2`@&|=6R^?@|FXET z))Sq={=@#l{80k31DqO73)) z4|oc`2LLRj<0SAu@Wbo)e1`Z5X!Os#GjHh$H^omQ`d!B4*=#y~vXRx~`YPaia0Tl> z@cHukPnRG{qasg=f8c+INlS2DeVo{so2=aS@vbSC=Xhq?xAvgF&knP(t$JXKg6R+T z$On7y@=Zw`N{heW9*=g(8iF2xvk+Gy&ceEJzaQ}w;;TkM&OMDKa}<0|N1O%w{Oyv< z!C8;9eSSL+{mc1!`r?rdQJu5ka&VsTZ#LQIFTL1^t0pfr(rAPr;RVF=;i5Q;mL)g~ zLsOj9F1ZP@MtL|(w4WKyVx<|*VsWO!$?_l9Y3wiTKhGC@pP2XSiQV3lf1e-wOY6Ut z+^^02@PzBklYgJz!}|;S&-n=JKkQf9W$=4+wqslL?jyfP$7yk&_+NR^;RW8}VUT>f zPh5+vC*nTw(WJL+M~S>(I1APuxKDMQjQxfE=jZDw_AB{5n)KTt(qFfc@V0(=KYjcL zx%>M|hrU5>z7Fa=YZk-x4?o9Jp5Xh$WVLLU{C5)E!TS%_zhM2xN|O#I7mL&AN%K!z z|9F34|6v2MUl|>2pV-z1^6~S`U}A6hJhPpHQA`IK(jlLHHY#tQm|#TPTUCZo-r)TQ zlFGaE6QW#DdCVtZ9`qmV6C<8IS@((O@_pi9pKrqLx=8nlN0S*Wg#c>&+%$zK6wb&R z-EZ{NtlTVHQO5KvTSj(nxOxZWciU{6$@5bF2~%H*h|cvgFymv@Tt`I3Qg9P-7Y>x=P33QLJH{hy_~5F}>Q`xv=ug`h0-_t_F(CZXYGqk+oR z-suJDZn6~jAS*jPct*On5nXjAKmprGa)Zo9K+p^I!VuFjgmk=1X+10_3C=%i!;$XC zk&J#lJB%&NDkvoXXJr&*=XL0xRXC$4FTcYWS3%yK?A+-cuE@xl(E){9p=ANf0w<9L znv<`Tt@Gh0QHNShiO2%RcPYqgtJ_hPO08~V$AoZwW$)&M^|s&menSR~woMu}U|9bF zqs&z}gW@;yJhM~_znNLZX6ssg?y!1q)QMk!J?Ak1}K*o9tvKRbf-KN&8)S zZ2!PsZ$5nv9a&%x53gI7pgf%W14H&)ue%>BL)QS6$7xu6?QnYD4P~C%p}t1ypGVQA zM$5owZs3{r)c+CNx)ptg!<^q(no)EN2pQqhLeYSzC-Vo|uby2=_lf&80gUu>H|*;M z9g4KbmfxFh#?dCqAZaN;ldt<}2yuFm@y=tgOAz)IEshvnl#!d6QNXq^!mb?)1omaL z`#{l_N6j#_7gVM1rNGcr$Y{)$!=CTdSMX-jwJ6cbSvP11fMWctg)I-9A4m>-f359j!d^JuxTcuRqbM=baEd*NJ;O$3-4r4OeH-dr z{?J2YFMdZF_;)(F;agNlBYE)7QfQll(BvJLDScQue_xme(#Z|&CINj89r$<~NVtk* z`};B)&R3>uZH6>n4niXLeZouE2&$XVep1HYm(o=TgUL!UEH5P|d%CMAWopJ$S5``P zZc33WyD+7Yet82zE4Bf5S`#EGq{g5B^E9T{q4{SOmV@B4b zqIvmIeW8N_Lkv+r)Ccu*Ud7sldV_w288h=^WSxbj`k~%bzwF$&>1U=Jq@jMO z59(LU^+UZuzo{9s3X8I*7EUV2nwFC_wJ3Xb{AWVc5A{L)oKu+nP;Xhkf{e=55A{L) z%D8^0H|Up{m7AAMzUT$J$NDmX8ngVx|ExqhfO=*QNzNmFykIy@;mH#56{ zI7#ePGDt-IP#@IKmBs9bdPDzaWBKEe)efhKh)c(ADLltW4p{%q#x>o z`W18iP;V#?{*jTHkq;A}w#=C|I)-UzKhy{Hb8=jZdZT`bu_{YX)DQJR{SbGf-m-p` zHP4`as1NFgU4(jre$;;fx#VXQ5eOK&Gjv2Y$Tz!CKm0s|`r+p){M@8}KAJRjR>ABz zAQ&_4bbNsNpnk}WdV_w2GqQ8Dr^RM&_Vdq`skv;P!20-h9+Dba>GzR)Cct|)n&`k~*W z-cX+T#iWdanHfd13S#@&qayuKAJh-qf_g*w!dX)$heO1*{?~a&&}HTdsXHAOZybJp*`fgMmMxOn4KDtg#-4B;i_UA z0slbVO1o;&ke-Ff5}{I>ccNkxsV1!zBb;y@k?x-5prVIISAGk`DyzrBfr zeQ(|*iH<8AcR2scRNUVm9W`myL$OafkK`b=={&(Z(I0$LzwmyNgtXcw;aboG`!GU#mjbb7xnTjw|_l zIR7MRzRS#X<2=RYC$VidTLUuaVBEX)hjO%ejq#`V(OaCJPCQ1S`R7`~i1o&6e0u6U z{&4x{m2@pn{+QT@Vmz?rI6l#Sp^VQ}L=28E7@y)8!|`X|&&Hv>UJ}EX_RZ^|f9$`N zvamI$zvuK2HlNzNvGv-%mY1LM6XQSBn8`WF(J+gwLHMonvITBIB>rM?W!q9-{20 z>_2gC5#xadf&LjolF@!J&oEol&$7Lt1o<4~xHhd8{roiR-er!1BR9}E$>KQAW+TrD zE0~^E>k0+=p)bj22P#J!3;Z*2q+_8fC{@l_7+1k>LDJ%<%s;XJ#dx3rpnqDCWV9cQ z1!ik~ERZCv&*i>#l1%*kcL#~Xbc<6^*<;KoIR6ej`m_xC!4(*SZe-WOhH`Ev zaV8silaiADdQqwN^)}zpI!z&@V-)oPsZ4HC66A3nAC!R{d}NUC`fVH|Fah^bh@P$?X>khU!R)?pOHA&cN}jo*0TB6^8RbE_ky8( zl!x+*xqPg5w{5pke)8{z{oRV>qdb&p1`8{lGd||x-@>_;yf0T#v zJzPH48z8@bc=ktmDF0(FAL|W}-#$G3Q69=qO=9-JdIRKl4o`oShw|rg`B-m&d`Ecl zQ69?obNN_rfcyjDjZc(^@*DAa7V8a=zb`!bC=cc1{Eqbo$nO!JeNZ0CN598<1LU_3 zUA~71!}S9&t=akqxQD(`OIQd~2KG0#W*w0M}T<2GkxEb$|?A)x;S&^eT`215_kex~5 zU<>*394Mo>2lPrOnTT^fCvh{qf<5nO=lB9cT>nrmrU#Xq%zg)4*AKo$3cN(QfD=)l z@r2b(-#`g0<+4%Kds6!4<GQiXj87 zWJnB)fPRC|W?1hy5;wE2hxc1w^4XzBO@b@=_k5QH;SZ)n-%))sGi|Q+9Xoak#!^wb z2(1mgpON)yw`0jsuIBA_ADXt1?C4U zSAIIm!w}?(airxmcqfk8Xkw+K`44srlq|M45$uNg1lq-DZ?pVptYek6EMQsSRI)%4 zdvl+19%*!PIm;lN_!~^_Y15{`_d(^-38)|i6k4^ zgzYkd9b9JvL)PW`_p+ydbd%c6`+6FL?Mdk8=U0ujpQ@BT=Ed=EW(~l{@LdGj5B9iq z_tN^o(R|?yJ{Ho+dDX|sIDrfEZ8i-z^Ya(s-FHM=$?-tDL5g)*7O*T}Ss*kEP=v*Cv%a#+Wy{0Ogx7!r z<+6cv`MBBqOr9nM>f$%dhwz@M3URYRzvG3Q8$7?w0XN`ejKe%14Q@u9VTPM^>`d)M zOcfZ)xOv~P`D*(~$6kZ5Ami9;_WLIp&Pi##a7sqNer9)|F%UPS{et1tGV>4a3 z^YvN5aWg;H9|>-rRbV6g&0PbP3@t?;MiT$$alp-<*DT!3%q;q<97h^BjpNbUZ${e~ z?P9c3pxv0wt^bw|L*NRvcuq3%cE8D_Xy-zda&AuyD2v%KHzv2e5C4`4sD zTVUZ)#LZ~GV7NKt{bv4tjL2|v%IKnk?EFGoM2DR9VI(zkUp#R0Rtq;XGmE||$B_n3 z<9M`iGup;z7o(j5?Z#|w{kJS&S-`SDC>Ai_=AZ>4#mxst_As3FWfhcvPUFm=TF{Jy z$IYwe3^U+#Jl`A_+{|%?8E$6JHG$2RH-8xi-0bnkA2;JO0D7;9{hA2Rw}ED&&&qM6kx$p(!p&$K-6lrN z94ucS4OYyufMo&80_9tPzF#4#a~kkY$@1%vbIZrgu&(KtPcCbd>k4JR*&yV2?Kk86 z9n*6f9A}u}X1&qkS^=p#E#qeSoQC}Y>wL4(j$k|a);TbSxEbvi3^#{-PJ_RnIRe~V zkd;vw4R8*MoA<{7H#^^oKW@f8(nGO7E!?czMUPqU)6i{U#ViY07C6-{AnZ3+XDG_d zBIWNloBtM`nH-i@K5jODQ<^3Xa5KMQy$k7lGw^BG@xsmV*l$LhA>(HF?4kE2wG&AR z3}xK>b;b+6&bQ<%pZ+>ut+oE)5~qcmwQ&=Om%*NBQ-O^{5jUg#g5hSu%EuRcxdXI9d=Myh+q>HU-aV;M^V4{br6cWZY~7 zJGjmUhB9uJ_nSQyZWek!*jg;G@F?PDv|ljX9P)lM|GmrzaC1S%9C)8b?BCz~bv$tM z?)c+o_)KZM#D2B*n~k<%l(pY%W)&;VvVdiQQ_}+a`!sCH;xE-2tx>^#vtd8iSkjCt zf4@0!T{Fm#>pEf1X~bi{*~4*$8E)3kx&-27W>L2y<7Rj+@Iw9`x`yOEbbgMT?OUsU zZT*h7h_A%BVSG{oaWmR47;X;vxj_CKli}m$OS5xw0`&_c#rP3SDELhraI^hWhMVhY zZz2~ej6w8d$A6aH#JNuNQ85k!Nu;q%39n5>G@TM!1kWnSEG4i7iWQj9d__Gw*6#)CmciJ`{&@r2X@!;rEDKl`sMrE@SxhM0Onz3lybEwMzv(&`%6_u}YgYnp z4th=luroWS0cXP5dd=y`elL*Y3^UxU10IG4Sbbnxoi@YG;_sd~E!-@OMb=-~Z+a-O z@F?PDv|rS?`TNrKu-_lSesfMnQBhWIhkkjvMFkm!MPY+yeHcm2{5B4_+4+Tqo0*w) z9~J#IaLgIUr1d@x*=|PrnA;M|NCVs=Eelu{uqViMU0K z>l_$E+>G{%8aKC&6gLman3q)$K3c|x*n;Ec@8f`*?PV5jW~PVn1s9GpF^+k1nuVJ! z+zeyFx-1J=7KpC}5)U_5FTcSjC~hWu?Br~-e!f|6WK{dj9B0G}H#;rdtap@#VGzq; zD6sG-;%2m8)VR6GAK`xUknHI$a=zJiaiEf+r3Bq?{$Ct$v;R8_H?wX*Y%Gp3jAI^f zifI;Zws3Q3PHk~n7O*T3xdoD~_cupwJfS4MO9Mi3k$q(S!uM(58pm+5o*xZvMx0@W zoAnxSoed1-=K=%X-)!M#-HZ}O!TuB&v?$_c54T@1+#K@zn>~@@=AjvdGc>g#i3G*X z2jhU7?LS$#**LC@xLD5K-;%0Hb8RKC4XmB&)3^UxUw^3Y6KsB9~ zadW`?n=RZd^d;EM>=syf6mhe^0^A((`xwZ?={9Tws?jT@1Li zyxLe^`OgLFc+XUlTvsUXp)&}o1l(L{`^|_m%y6^bXj~TpLm4;A`_1-;tnz2CetV%$6{rzkr=XP!~NFsPuo`NufmX6J7fZf1QW`l=j98aR#P(ZbDW z8>3x}b_%o`v$^%(vVdg)%K{Z!psICFqhce4z#;87R~l|coDnbF?6h#R(EGvGVu6K6 z5jUg#qQ=crev0sX^RQW&)3f68T;NY}z|HnQEZoe7kc|HzRUChOpc&Gv^UcBclV#h9 zG};<%YK4{sEDKl`D9-|PJ1L=l_oQ}t>T17vV&mVL+d|oIHtbO!r~PJlcLwMj+xO6M zoMDEW^^HPY7Xm|hzZuSHh`)Q{v2e368fpLWaVN$F79K_1jP?tLn?pXQQ7>sjWWRed zB0G1gtHaQY>ABg{=0$>^^gaf0CHbfPo6`FUo*(e#eyd^6&Vc;RNhg`0)Z2;S4Uyv~ebqg~Ly5Iau9B{M$sD+zZ{~$KTu?DFa z$2>XB!p#yKs+rF3*0Qucw7JJWg-ZohfeoME7!cE$638i+H@ zaI?N|kNp!Enw`_QaMH2Y;Jik|jAO4kE!?d2qY<}&E%6pucocCn+AnI{{6@sMd1h8d ze17-j*Ery2XO(~GzWS;Cd>afR(O2a-(#VJ7(8A4ucGGQR^jA5ZJFXR27O*T}S)f7- z)B_HFzBxE^dGFJBXU7o3b8x|>U{1xjIp`)AJEMVuAPfaJi~P3ZbiP@`S?pYfu2MAT zG!SQ);by%CTxSDA88^%C)9_fhSvRAGlPg|x5jUg#g5lv>T?0VW8bu8SB4g0m}lG1wyfaxZfO{AKs@tnB0Kz-r2Du&A2Am4Ew9W zrC?6^xH;%T&5A=^{D!$OB;0J!@A%;6bfN>hV~Lv)XPDt;-Hy~w#8iQyjGN`>0{s?l zW}}Y&2m6`b0t=5KZbthB!_6T-7kDsY+?<;+BP+$3mp3Cq?5qzXshPjW12>;(;bvxL z(O2a-(!gmPj}~r5+ZgR)v{Rtnn9Z&KmIW*eSQZGy0t`0;CNA$PZ@*c`W93N@^UBA~ zbwt*{IK|2&nPZp}1vhKwnPdAt4URM7g`4dwtnl_$E+>G{%8aMwDF>cPA z20ydiu@kBmZWt6d{}Bh=>^Up`xH<6s@M6DO`^{zpB&kYOli!L;G`pcb&;pE`g=YC? zt+vuE3s@GgEMR7V#5|`FxVCA!18x?tQ~I0+;tVs~tQ!f|4!8nCdB0gcr{T14Gc%oL zzhLXIz(%5oo6&y3aC6A#G$toU`aX?WQ;G^QrWP6X3xf)ZoBxahZgw_|KW+|eyV$1| zZVt2$`F&LjH=CKoO0z6rS>P13Ko#qEPh!&R$L)7dIL?R{ZuVHXSr{K8z7pdC3y&gh zM*BsLo3Du!H;>7h8^y~sNOOYX=EHHo&GyFe$IXGC$&Gz!oo@~t&lYYDw275sS-`Tu zscHekezQH870NrOVg1gw%@!_hhWF9I4ST@RJQs*K!wfe^`aX^J{Cye)u1BF0k+@;%2m8Fx(vS`Q}57BiwHu3d-`Shnp*-A7O2}s?%!O#F<)3dpP2)3-KwbQX z_3qR@-;6lJ3^(h5huVplDlnAKX~@q7+E-fVoApr$_A|S64vZmgM*BsLnqllZ) zev#tl%SPF3PUz?4@z2BWY>y}MgF0!T8n(+u^~=l2$(mY}otK*u@uPtzf=N>I{^fnr zHZOyz#TnnK&>sJE7GipM;jv@k@dt84KQ$uBsLvH74)!h1GU{b-nzRx0)%bZn^7}cB za_q^!8~8CF`OA=>hxSyXe9TAw)M?ya$UkW3dQl(DM}8OQM>+PxzZvwweB}3Vew1Uc zWz-+@k-wPpyLf1CY1AL{k>AhxJjmbQs6XZ-e;Ma1M*j9j`IwLVc0P_!j=i%{KIS8T zD(6Qzc88H4^O4`h`B9GjfMidekNm})MmhFnH4#%-*xfHvmm zXBDufL{t^}dqjnWLUvC#pWI&1wL0g4_}}CPdb>7sRtU|GL{Ntz}>Vx`W&8WAo-=x{uh1pZGbFz!(AyXVf z&<{>};XDgfne{L50m{aCSmcAe_elcI^`nUZGZ}D*49^}_BOGK|%qW^wkOi`$xfC#} zaADoCT02p+hG{rPjttX`qL`-BB`7sE;Tr_gxO)Cg*WB1h)TN+3wG_lOV;evgmFMO%ASAaT4YI^E5~mjlwema(16m#l z9-5~W|GYSzFu28=r;#M>awFeu7e1Ko`S`lD7pJw`eC*hK3)5&OI(Z_vCgXf5ugTcb zSYsM{FE&3H^API^hG~!=^^?o#m;ohKPKfqHeNaEF9rf1rJ3g2O=ULDzU=1T_h-q*h z7Wp(x!{_?S#xyf38`JQ8q3|)yrR3*D$VLs>r|A^qeVR83rg8SJBuv9v0Yf07D`uFc zSgBM@WB+p--M_^!qQ;X#E{I(#55A87YViUJP89@9tj=>OVjFd6Gnb( zX;2o@$i>o#{wnDM2k8SJqzQAOwNJwgb;AG0H5unic}>O!#v0R<@%h=4W}K&kVH)H| z{p4~wWM;6BOav1l0pkHMO@wa#5~mjlwema(11b{<9tKO(>UR@Heh2W9 zbjC;Y2V7}k8e*)I^};n7=Sz7_#&*XV)A;%P>{5*LbTCYV{HUK?PR9(Wapi<)Khy{H z!`e}AUBBalX>gtey#m%Sk_PQaCLzwlA|GNJoa-wa)6A@FOvBGlg^y{R8M&EkpQdAH zuwd-2Z3NTUORs%dK3@`x8qCH+G;(8xY4Tl_ifQ~e?WOj`Fj5=Xv-x*V0;T~Q*BiZQ z2*3}drQxK7!z=fQR==C)KuGQYnV9A#`d>o&Aj0!B?TC1|uwD#V zs`WoHEP!h=&X@926rmQG~*;pV?19gO2@TP zUWAy1Sm#^pSeBlGBd#x|gSSPsMB<&lGhF)Q!pav>ylK>e5&2rx}nMy4$@s{ngHcHwq{Y5bC}#7cU=cz{+! zam5VNth%^T_h~$ChG{U2)C7KxXL-`eF#~E`IU(8)^+ElxcGO$fuktYsJa3QlEa)ZpF{-JBh;48_7Wom|;GAC` zw!t|(md`)+u54_>pYaYK+YHH?&Y$V-6yyDxw+OZ=mV8F*yeK09;{u+4Voixia?G&J zvdVp?+w)4lW$+yzh7mQM6e{D`X0Ehf^Y8yPSKb!Yb zhq%Zi#^pRLC-dlh#x?*S<#qFt5R%)F23cYniPMXOT6vy?0WFUN4}+x{>T(liavRd1 zyp~4vS4kg~kUmg>PZ;|awgF3>1Q)K&IA6+ZGd3~S*v7e)VVW`?Vm-mWgF=4PPcElp z2b5GfA=(f1LH)3H)LYlD^05tlrkl^RpqJpssHS$V2IpasPs22P&aZ4tlUv!ChQFsL zGEBpt>F%MKHYSm`38wLEGky+;iC`imU_2nEiSWE|%rK38dZq5y_>ZyYpD>KnzV>U_ zxtcNw)67phPu+2XV;W-{06Q4)gPYil+=etTqnJkG^dg~Fo+n{I%Okqib2YUSO8 z7H&iRkWMa^Wgtey~@KhI1h__h-q-HuWU?nZDnH`{(j^rFwH1icN_t+3Ev@@ zCbjOzWz!P2z) z-GqP9#iztul1{@8Q+W>7FcO_d_oRi95Uw}9C%dLQYpnjC!k;I{XKd%pExf-#0 zF>J^)uI$5p)@*c9MxpB}x15zIBVzf8;eW);hPY&&k z@@t{@S70dthcx)W6vRmS)$gzIqs)z0tmCZx6RO_Cp!BH-=#EWO4`CyDQTu z)c zxgp#8^l^9kA)?fo&gH}-?)TyB3Jg1rG-2iQBz@0-xxC_hwtd)9=5PsX@r z&74v=HlHXQ)zyg4-w;MOzR|vDZ^Zf753c6S-WYlz;v^UBP)!xpq z=acY18?d&3UGa*|d{Rn)oqfbF^jy~2M066l?PC~?H3bX%=H*6@w*crsrR4JL|A6Yj zs95~Rafo3kGLcubHI$QSv$RlnjODJ$-U-8;Tv)Ho1a)X~NuFC}{5IQP*Noh>_O;6# zGd?ceWUe1_R#vF#DO4DGn%EwUmpr)8v8V39QsGV~ae51tgc(I!R}>n>y2gJH@C*oW zq{HHxKo`to)UUZ8YkobB`8E1`FqQc)Ck4w6EnW2Mqs*@>sy8&jz$N!vC?&-*B0r?d zmz)Olp;Vn~9Er<3kc(+<^2yjub|ccs4iKcd)q0NAFYg+8RafBp`tFCj-oAIfde@F* zFVIgGPw)B|i7(#E-c7QjeM{xladjN$4}Pn+E)zJ9*M;p8>kmCmY|p!-ZB0K?d`*j| z_ZEB_{QLO%^}>awe%&Wg{hIr+=GPOsUsteh_sz?U{-=8dzqZ%>FZ+!yjvvI)5m}u2 z3iu1ZgG-rQw;J^womU=@G7YliG?_NzF{a&g&-S&fZYT?PdO7Pgi9NOU zT=31h4z21vnzrDL_Y=mi{g<2L@P3Kv*W7|xkLK4d?$;Hpmt*n@3Zve175v)y1@~*j3tSIj zK!h2E@gv@$6c5I4J{FAbTC&gXtz!FuV8 z@}1$d-vz(6ujhVULA{}mSM*N|Ln-N|@8y>LZG~Uw0Td!mw_3P5!t<#>ndcJaX_iO0-S~xY``|7XPrZpPC{PdGln`3s@?mqW{ z&D5`F&YiEM-`2$YO!u+0{HwnV^R|2L{Iq4W8UJ~^T6+I)#e8-BqGmK-9Q(2^Oux$h zZ?p%iJ@hoOJ@=H}@2&YlR$BYJZ%Mb=x^5Bfek9KCNZW_wp`y?zzxYjazrHL{{hIr+ z=GR5suerY)Tc=Z_U3s%|$S)d@SN}$S34ZP0V_dK8T#v|w%l#ILB(aRnf8~ecl($>n zeep-9x9s}OS$37>0Iz->ch-Qboymi|&nqTkeSucaXrT2P*e4k~rZM|)pvajhY zU49~bK&RM$Qupn<=jAas{6yrql^s`StBW76-7RJA2R_JeGWP>VS*OpszC+T1o=~Ig z74oinb!n|NuK+!?{aMPZf8XA_?`ciaPX@mh+mlxPTxC0HTl4eJN|^Q9Io%wGI}_Ef zx<>J(v46_jedWX4hDFXC!(0koa}jXYe(7f?s=1wS2usyfE>PD9@jXK590;G%bV(E5m=o zMN2A@pJ$?9vZs*MNs3| zvHI&KdsdTt_POHUW5-Iy)xmenh5ey>HuMd2{zUM-`!1ahgDlZbbUkCb$*}Py( zYfSulxa8Msh+kjC{rU#t*A0nZ?-Km_8{*g7xL^PNbb-2i(h|p{;+AwCXxnU+uWpBK z>NmApP`~alcdmEUr1RCh9c*3@$9|x`ky|bDl16(lIiaVC?HOF}QDy6(yVbkWx+aWY zdnTCU@WF}d*W7Az9+8p-@b61a$jb0$rtUv>HXwn^Uv2yGkdDf7yjk@aNi$vy?uVu zMaueHo2ogJ-lz2b=icrVXCX8ZE4k9;lb_fY@) zAI86O{yOj4diB*SFCRhua?OI9(mVElKyCR*l~VA_y8EtIj(#$^o1Puz2h`kJ#(!uWM+ko9_KqWU%W zW6iIJaKEl#y`)cGMztAE2EX9f{%<85&h-EviR=pBubF>fIlb1woePz=pDlMxSaOy=&Sy3%{kQXKg;Z+toZE~-^y-Hs2|o^)J&~EtdTl$N;0KK4Qi17 z;}hqn*Zu5yLX&-SU$&#eb7Rz&eaZUf1?`#fd-$i45cLU8pmVqOMR5ZS3lxw_~kW@ zv6t4MekHGwu7Ncjk2!j(Ytt{J`PJ|G$G3RMdFtM24T;~`Y(>>4s-Io4z~R5`D~hWR zbs3P}=*^$g*8a3G9b}c9T~)cqf2DH!ls)v^i8E`L@6NUvjz?-tq4J*jPr6#`!XF$h zkNrgbTpatdE}^tE_B=+LG5MjViS2pz`rDMkwMD8wc~Qdnb?PE>zaE~be$D+@^XoC( zuPazDVc!fsU5ERgDZ_XY{M!Cs?$@|)#`Q4phee1!YBs*igo$Oue6#Y#G}sRxMfT0k z;QQg{lKt>vvTt?)-#2rUeX~ZwzF8UB4=*G8W?T8b*`pg3)wWA@eD}ZF)Q^81evfZo zpPA~r_syg8z~6Nms~vZJRI;}b!{OpS)5+l1VteM!eMIT|;A(a2(_<6Hubl_X&!vn= zRKMo_qxtnX?$_Mk#nCOs2j*sG%_V-Fn;C_7i+;U>`!(VPu7_A>cyZB3jXoG$j#x(L zDj0sblKg6|66dni-n)-5zmt8b`5r~>ebZ0AH3xp8et6)iHcGXZQq)7UKB9E1OSXAe z4{EM9`jqXPwU|C9z3q>?lzsR9NpQVe8PzM@yP?rQ<-$MMzFD76^VNDcb?vog)?;4K z#etFJaHz;YnTYLBX+k%$w)#Wa!Q_q<%d&Wu9-w#p`?@+&i7(@;a}dr!*SeSAF8_zv#Z5 zh+EC|3)f!J@*C~Jq=lX)wrBKHtCSz^xmj&Fs!qcAwd-c{^?G!o`Zf0-&95`LUsrJ6 zYZQ6SCLr+KOnNdUGUpcj+OwJCaKsB-kH|+Gj4AQ1SW>X>xgP#@tTJbG?UM8>+V<|c z{50xkvM)XKLArYGGe7u>AO4Zz?ov-{<%irB>g(%2ru5buw|V#XY_6{QhT-s)duONb z-n>gGo%0*T;mV3s@5Sc~RQ{@SE?tk8^qH?_zIUkC14C9&9DennTfDz!o~v%Tmf>)5 z?8~}T+|qb(gzm$EwYwFuJ)N%iD5JA(R_i@kGhzJNd8@f!k4aR&=KiDkwTt_81?wgF zHNbEk8@(923D(M4vVrb7tktpXa)d;PA}^htDbphu{6}6!rdh zK6JdXdk9@`iw~Ch^5=F{XAWjK+;iJn?|rYIsowD*!{JwMxGw$Z?~kf;@2Fl1e%WTt z4az^i^zfdUe=aS5_p&M8jf;jU!&fjIetxxcd>8FHTix7&;c#*6pA3F2wr9`c+mu<4 zXR9p=nk9^1r=Dew!^bA7Uvod!{5psGbp`9C_B_|97(MqitZb@7i-7Ifds|TAf); zPuZ?`KT|z)=Eg3q2_NXc>%8xk)R!K7`R;><2)|pI|5KLw^7>~lzWS^$Y5v5szVzjH z-qv*iNeBMjwO*vpbJ?;c($jrcRzFXDnLP8f?j(7hOKeZOMeS8TX$TifI$wEt zLc@|Z|9QCg&imQ(&$3@%K>W4Cx2w{=`e7>gwVT{s$5ffG)T;I}#nH=&Uq3smMcQNQ zzove>llXP|6X$r7#{5kE`m7pPIL6#@t#VuUTYONn_k%Bdp5gU<&db>IT+P0mqz-z2 znPbnZTWEQ4?8~}9`O3eHb0Mog^fa+O83%IJ+m0;qE;w=^Vb*Ks+2-r@6^ZKC+e{*g7s2+o-0Ov?ODhD8u3CA(It4z*M2fw!6(XvS0(0FCZOPe2}{JE2dI{x0`RMUntT;2bbd(>G! zv`w!+?gi@CZBpl`zwJK5@mTI8%I_R~t8Z@d!zb07Cg!Ps3>cg+er<1H?$_fI)vvi9YkobB`*j8DC3~JL z8mA_`D}26op3ZSN;svfp;1~!sC49eT{(HzN||qEsZ^o(Pm73 z=xJhm7XLCwow)lBb;_WFiSv6OLHv3`qWU%WAI+~PbHA!!y~O9aV&>OxavWYky`hgs z(MQe3mzgB<{qP5V$ydJGd|uiBU(b@IPyZjq$+9oC*)vydbMZO8h2z=tTtB~8x^V{lBzub5Fy`^tlmF>vBx<1KwE6rAqQs!=&;2XHRI_1A| zO)aItRrQq`yP8t|FXnc1468RtZM>J^cya8@x|p>r{%K}54n0k5&yMw3s;l(^b&(#BOZGh1sKADXlOn9wewF(*;&8472^ zJamD&?8Ciu9%=mbK;_DJ_c*3p^(9@uTaUU&z5BTZs`|fKgx{!((GHbGnduOEn%Ex4 zrrXr26PKt1?zlN&{M!DzxnECARKMo_qxrRq`!)CXV55RR&lS~jZ4><3{weqC3hE7} zPhdKXDe(^E2uyJ+@a`V@JxCX@hIZmcfceXeiR*lVa?$-bo4IqX~hc>`tmur|~$ zZ@A-C?^A0oRWJRdIi+9nU#%Q$ex2jB>U*f4zJA(+rJt?z`Zm9UaVuhb zKAU~5+U4)t)EPhDlrVnns`I7kdVN)*`Zf1s&98H~UsteRvgf&?b8FGBJ-em#n(Glb z20~2{ecEiynaL5$i1}vajcK@VHihq-!Fuf%_RR{(zL~gQUrN^NIb`4LEWTd*7R+~a zU)Mk>m~bie>nU&Tbo}_vK=qZX|D%5W&BK$`X*-Ae8r=OkrNw=wlfkdW_9P!#s{Scl7hSJC8P$EaJz`g@?i)+}`j(Z~E3Im@ zO1orW4_ZEZMY5wypKAzc;p5uwL@#xuSDy0f&2D<9=O1y`hg((MQe3mzgB7jGq7Hm|W$T z{UbMh?rxKI-)kF9{d!H)x$5mX7x-@e?Gl>**ryY`^S`LCIv25hvyr`KJ4W3*9z z?VCN4abIb@_6;46&!|iM*sbKhGE#Xf{Sx1SG3?xF-$u2RM!tHAvxMRJsS7W2d|zdl zI&Dc^G7s2n;@CeK{90_!f-@(o%XU7f&i?NFgz;GsFvAk6`szoDO43yelpdjNkSCNM+TrPfC(I9`KHNhOL*fFa3JvD79J6Esnd^ zSE1`^^M*%#y|3!0UiZU)DUSZQ>D`VV|81!D_#vIQUlhpg{z<=vj! z)4C1sqW1WUd=E~pFF%~699Xu%ap8bNbf0Ye+J%k*yDw1xv$QiUFOGd#mrz<7d!EpJ zIIwQ=-bTTf2lQN^p1WwKTJwwb3FFtP)y#4DltlGw?mwDeui}1P!FtJ_=Zc+QJGXEg zUO~NqVaB#o?W5-K3^ftKn6*{z}(V z`?N>BPkhusJvy45GpU~aMp|jp5$dMe^{HQeecs~IQRlT(7TpK=Zsmp+qm;itUgo=b z)L(QSxbN9Bls^aCl!M>2ru?n08I(5p$KmQ3I~q`09Q!AOUyJRzacG|U>zaw`?#*8% zj9+{HF<-BzCaPa^Ki2&E3GUYwte5n;k0Dvp$)lQ)T`j8^&XL-E+^@&|!k##&#xi2fMn7jc#ur--zn9$F{=2l{ zJ=pKwrq8}T?f)9yEVI^TZ9x73fOk$4i&O_MglxtiaSI@yTl*)Gk?D~i~61$iU#arrw*E4(*XIY;RTD8FE%s^uWPU6(@zw+GVrR zc2y<)!sj_{{w?P7FQ1pOc5FX}^!c1<=HDvS>HM3-jp;1m^RHAd)*pU|eTrcy5|CH6 znM{K$W7?Hu{_IZXPY0iWzj(5P&prEkW%(n^Db8|VII!0>pRVve+Gjo)Yc|`V)(z4o zw%_Gl)pik`e_{TOa1T8MGl~0j(VHS zQCE^VO4bGPEB~VQAivY@UPJ13lN;jDR&)qRBNt6`lgLUEhgU3+-7vQzw&&gUEtQwX z)p4W`eyjJMx&upv8=kMFHz^n41@(t=8^zm?nB%If1e||M!LPX=YkvJK_v;F9jfSft z{m3NYD$jcouHt$ats4#{_gyGu#WEs4q|28v?VF_8%4Lg}_#W-up7<&G-O@k)FpW>=lUudOHK8bLZ*q-cnI;*+k-}ZK%lQ_7_737@3v;_2P zz*YWGa8;zfOXMR?6L3{T(n7^m_Lk-2Ds1lwhpYHJr_H}>`TWc0WtdCg!uDgR;VQ5? zV+rrSgsc4j@O@~h-tdHq+~QnnoGV2xV_YmF=9rZ?rosGsDVaaF^7*&k;w6qn{bnhT zUCwaTuI%(i6Wdesv-8yT35KiuN6m57^aPxLDX!vvtoilJ+^;LZHNt!v>5i3QzsmVP z?$;yOeiiBOC@(tB1Z-?BF5h+!{rvH??MnK-AU5udKFiYN@{#a>3|0>6J@n^M{z^8c zYmzP91#L)_`B&iBGarkA zWlK{@*=JaKdaCrklM5i-m`3*j2;8Cds=2LRdpYy2J=C|qzx&~?_wJpq-nS!}esA3H z_s=e#LgGvJvVD{qWKAm}Yufo_uOLugqh^&ug<>Pn#GFrTPc_oERv#(8hQ-qpW*@|J zy7@kIcB1+<_ha;H90PXl??}s`uwF;*-J)ObIzE2=&(QDazQT=vKmRiKFO&7pd@LGm z5(*ELV0=(-x_vU>P@V?q!Pje+r?1zOM=w!NruABE55^N|y)Le9?$_5OpkI5)h;lam z`+2|@4N07Szr@!|JFeSXlI|1nM+)JK)dlsDZ!hQt@xRGki{Hch+H7m9k{o~>m z%E2T5*LzFrrsI( z#55A87YViUJP89@9tj=>OEc8vCd}kEq(ONtjp(nEJ}4o5Pzrp)*iTNP-)`Hg?j{A2 z2+C!7)kyb&Jh6VHF(d`7D`6}E-;cvOgYzXUgF-)Rdg3~uB}5tV^ZD8Ju5q3Q{}(UR z5BX6)xg6*>b>7sRtU|GL{Ntz}>Vx`W&8WAo-=x{uh1pZGbFz!(AyXVf(61hmkMk^4 zW!AsImrU|-9v1l^?|qVhbNy%{z)Xgn@Q|-$=~azz^eLE`Q8cR{izh`K6iC<%>yFjh ziHd2MW>D0aW|Ylj#}4a>Bz&J>8vA=jUx`GHSsApamWP;Tl$H`!Bv$=mA5poO##O~n zu`GsRHCkXU=9nf`!ZiC1wS@KbgvK<+HfU?Qn>Qaqa?|x6;&{1*G!my53AOS(2?JUl z2_6PZGt}iK%;XN>#WXk3A8@6GX^5dt)(h8UoG;}y8QUFeOjFF~XXpFIc{&)TL4MRv zE~jG#)VOj&v>)n&`eE&;x31su!8AC}f?fe@7)e7+gY&S+hnNQE`pU*Mg_VtI+Ou=X z;qTLo$tx(#1JlL)+DR}?ndB?6sw7}MRnu^`!ucvd+9zHhLPIW&oNC8 z3DfL4+-cK0u2sF8URO8WC>z#byu}<$3s|ECCOJ>0fmKWs<8mH~m3eeNV;g{v^169R z2+3_ogDf$P#OXystvpY{fR;yshr!Yeb-4*MxdV7H%}w<0Mf#wG^g$``31i>dry<5W z87^FtalVw-WNc%sF^y*@!!oHKgobI5AN7;V>6igEuAC6vw!G4bHQm zm*B^!riN*79v1mDOvC5=%EmNlm4s=A4zStaeJggpmbM~>GP9LtG$}H4K)+d2vd9l# zM*IWxl0bnxLNHCNuQI;&+TC+0cCe?|=`&{bl=n8kcQF3;1$2%7nHb@FvH^Dn`M}`; z`@H+U_Rh#- zyNfot>*sE|&-<>o>!iNQglF3;k2N2nz@Gj+wcz(P<(&*HX@zKkcXsTc&p{%6GKGLI z#*qf$cxy_A1NfzLDI0g#*1mbUQGe$HvamjskM$Mv`cRK(pRq>0u|AZK^||h3?L$5K zM!AAkA$K+d0Y#++U`cm&=?L$5KOv%ifnU#xbMjT>&C?D(d@cK}X zSnV)jeJCI6E93Q{9>cPx&WN~CLP4w#p>sLp|^}#-gne?TPZSKIeUWeDZqnw~t~~AIiu2ig|sg2m4KyXloP3 zCzp@)+3)A$lc)3Fiio;Gv?t2P`dqv|Y$tn%ebm)y{)h6hK0mJy^I%`GC?D(d@cK{>_Ab(>tJCa>^0B@$ULWeg-jNu)`cOXB=X{X+AFr3a zyDfJ0p?s{bnAeATXzN3S=R4?REzD;qAM3L(VfM+tm;GgSYH{h58D}& z-|Q0XiSn^N`wH%Vyk70Mgm7R-7@|Eq89f$>n2xeomtv_zvC3TBVo5 z_Mv>NFZD5QPhKy++fy$YSq$q#`Bq9-XcR|FcK9rC3 zIUnc#&g<3M7v+$__Mv>N&(8PZP!FwrF{%&cV|^}OAL=1I(-)ORv3)2X>+|#aP!IlW zaAbp0D24T*e5?;yP!FMfQCSq~gZ)qZeg*1*{VdNKAA2|EQ!E6&eIy;fPgz3Zm9Z0w z-}jK}=mm9(`?WkdW&!Reqd;n(=A_;Hd#BdKIPe`)a(-16c$!@OQ5x7QIsX<1NmF#X zD%mSGciWk=}TzPUuz#J?<4wZ+ z{rS!dK8L%Ca$C{_TK@H2(;cLYL*Q3Leo`OU??tQ+ctJmj-zUg-5c#Sg-v>G&|65ES zji2l9K>ceZA8?TNXf(+OT5I2u4NH7WHq=pVRcllINu27Z(@PwXrqgw(y^tpD2mQqM zgMM23xqUSKHxv787W60egM6WWs*hG5*hU;*oYuw<&|?1s4Hu4Qh>PQ$)7tpSe`p}> z-^0hMD=^;kAKIQq(i}QHkoJ#GZ}&nOr1Ku4w7^fxYx%D?xyv!M<{WK&a#|aIpbv~u z9IqtbC-DQV{w4el!ms6XS}V`_HG1yqWxlzq8>xGKY)$P?;>xyv zI=-aS%N&rV(~VS;-&v<=c_E+jYw`vzsit=Q6IwB$eVFfL{2IMwa^JOL{eN~C1)4plF+NwPqmr(toeIor={RZkepZx2?e3GZMao_q9(tp=)ppblrL<{^+JD?2a zbNLz#brE@;F`vkT`ZRf5KY^dh)ATLsQeW*l?jJ|TEM|W~D<-rL^T#_bU;9qaR7IdW z5?V$2ou0)$Bb};TRlna$LcT*R54+b^W~doF3F?0I>;Mq~c-O*$L$kw*CiUv)LeM|#Ng zMoPQAD;&u0I<2MRu&vQ(%*XmMAL(ir_#DqY*hE478xP&?*xI|5Mz4SG700i(W(xA} z_@&fw`cthn8uPJ!%tspKqrNB~>9MzNbxdpDNJ0MWH9H;i|Jy>NF(2#4e599ryv{MV zT8f&%?Z5hn&vD-~Z8iF{&LxgcE1Rk3bNe3}yUj7IPaBQKe5@bykw*Emx&2W-(&;z- zpF_=UsUrXA)vr1}t$(3LV?Nf8`ABd3qJdJa)dnBxzyH>Al_r;M(dfDZ8!E4Ef5C_R zZGTKr-X`k>(wL9+L;enuk2K0heNjHr3ud3GjJ~N<<6n7BQ)T+28`u|CX4x@6pU z9mdaTrP+J!gzq}cT;4&Q%=@Ew*9+TrP~n!Ufgp{d$` z$4-sLe5?=ikp}r+68S}Z{DAyFhsp{{ATS)(x@^~HRom+V{X z+g9sb&Hk&amiYF+&_<&ji;8`9ic&OtkA8ZcFSYRn8jbm=FXkhS^3mQXAL+e4H~QSG z&(ZAt^Hp#7mS1|lMq@tei}^^8{JrG0&MR6fX#Y-xzSMV-g8pzU<*5?LNB(~a4f#l8 zKI)74NMFI_qrJOx`ACntd1-HukNjf@4f2u3e5?=ikxu(_lkdCP=VA^L5zQOw+eYsn0iLVH=Ic ze4H;aAL$Lt&Q?bsdcuMB{~#?zonC*fM%$`1R+ArC>A?0jUei)-vT=<@Lw+@qkNRRh z(kLJ8jq;H$=+HoYd=SB-eEnOztC{-B%ncfi`B*>ZBaP?tR~&enex57-q^6_J)d#)* z+_;z0OVavzSA5vn_cy0+xv-TF&hwvjlb-L|RLg<+P<~C@m1$VM_=fRmP(J;JMhbkM z*gr7Y2l5qx27aW0ALJqbz>>BOsILp>M|}@n)|twCecWdy$y@e&_wHU#^=&lniL?i1 zuke1s>H9N>ryZ)>&Ud8kZkj*(t`<~Y^$Qpc{F84zI}Q2WFZ;Yu-%q^!o(V_17ZLf% zoFD3YWa&L=SRdqXBQ)kiecs=zIIzBJhjn$lO!Rs5;i^<$pqG>SkcReL{pv6u(r+By zm)5>sJB@zU*WM?V@Ak!qUNIl&Bcwho5B#EhkSFFteeG7Z*7&=mRdqo7dbMs!^}Fu; zOT1t&h0{xIO=*Ak6!M{cm=EQl{aF5n8OdqTAMXusN9CQqYkL~lb1SFs=<$#j`sZDN z2Kyro<-xvKe#(b;I8tvK@2EC)Fzvt9+&+fDG*;PpZIKM8#rr>FAr zd9S=g^@DsbDPPEG(EpzIp4(Vw+`>W2ECBD7dPsviCX?a+q213`Qg{EBraXHk&3}H#bB-k+4DIE5 zp*N-1Z9ZMu_1beA$Nrh6mA~|?pS)Op2r2(yr$4-Xc=>^({+Fg+;KTaer2bESzQ~95 zU!GD$f%3Iqv{894_3BB>|8_4hg}jnZR|)K(rK^$$oHPwD1_AL>W`uLwWXe+lPbL(c6&{a>Ur`&_%KE3Lmn ztroQWlO!MN$9&>HKFR-8$^U(l|ErS!`y~HYCI9zH{;x{@@00vrmHeNUNB<@MrAq$q zll)(m{NE?}zbg5^D*3-E`XAIM`M)aqALNVv2ejyaWIj3`iXxU>W6&E z{|)s+zF0rdV*MabtRHBx{<2^9`$YdAaerG?^#6rF`Fx`Pr`+3675#ry@>-wh|BIT{ zQ$_!W@|Owz-<*^e{r?A2zv%y?N&TY#r`}Lih4SdX3m@!7%cK8q{<)Sa`v19g`l+J- zLwV8vZz1JH|G%@_IjZRYBT4Y5nNm{ri%ladi$Nzh0YJ})UBnK_BhxZ>5pD$sXlPKKW#A2U)!vg>U-cTEr0z3 zoz(GHKBtwR_*YGJ;K=78{%+G- zs`obBo{=yf1##k>>yFYN_TO z-R@n->Go6GslP87q4Dos)lyw}WLnxdF7N%`4b;0IdMFLcZ~3RAntIb*t-hvbc2eIT zvrNmMwd)e~@qN|x`mS%R&fK!kJI$6(>;GhEJGI5)FEzT|tJTy^m(|;Z`Sz6B>VmP0 zwfrvI8>=mkewc>&zTvgi!@F+Q=xTpnqAu9x*64aG+Nrr0>-@vEU84RuWvM3b%gKt` zaBhZH-_OYn)c5v3?rqX`IMr`i-`eWX+rQ|IbiL)!E#R{n`4E!8(q^SpNb@8?l@M~<{m2YkH!wcck>q;ztlGZb-r_TFDZ@d@Me z(wkEhaeOvE)I%u|#%F4F2Sps8JKnCLTrG^xH%6VIh~x9X;~f-ne7^8W4`s42KA*fK zMG?p6cYmFs)ECC*)~OAZ#lrYpd(5u9CXCOVUMY$=J|DTbts;)kp@k`mI6l+so~elA zv-5lHl~%&|{Lk0z6>)ri{Zx1531NKZ3~Q)}Gc4%2s5yxlI_U?)}KIhMDtBB*X8{KY#^%w&NVH#~KbGh+ zi9RgR_MTGvB|252dq}iXqQ^ADFC|M}C|OKh)q zm3_xB_r3AncxPm3&bj6;Yp?H<&ha@CDLY+py6SYz>AKTRr&~_9o$ffD$-DDAopn0r zbiwJO(6+7Z zr&~^UoX$Kvb-e>l=bSD$U39wSblK^O(^aQyPS>4oI^A}<<89*4yr!$XFeg2%&1*eNnSDdanU3a?abj#_s(;cTX zPfT5J*6EznMW;(nmz}OSU3I$VblvHu(=DglPIsIhc+#EE>73I=rz=iZovu6Gayr+X zdjEpcWv44nH=S-d-Elhe)YSV8IGuGm=XBZWiqlo6YfiVE?l_%!+STWD*6Ezn1*c0+ zmz}OSU3I$Ybj#_s)7fXHuD|4T#p#;Ub*Gz7x14S}-Elhe?9}xPIGuC4=yc8Ly32SJKc1;<#gNWj?CD`Jn(F8E z0jIN0=bSD$U39wSblK^O(^aQyPS>4oI^A-*?R3ZK%slS=PG_CYIbCqN=yb{HveOl( zt4`OPt~=dyy5)4+>5kKxdENP)&N`iQy5e+w{;Ah1UUA@5zuzu7U3R+ebjRs|E2myB z=XAm8lGA0UD^6FPt~p(Iy6JSw>9*4yr?Xd0eV&5TWv8o7XI`87?+00@b50kWE;?Ou zy6kkt>8jH;r|V8Poo+eZcDmzq=Jl!1JK%KI>73IAr;ARPoGv?Eak}bs&FQ++O{ZH< zx1H`doq5BZ-|4K=Ij0Lw7o9FSU3R+Sbk*sa({-ntPPd$HJKb?Q({kr`I_q@K>4MWm zr%O(kovt`tb-Lzs-RY*&EvMT~cbv|=>CW$T*6Ezn1*eNnmz*v;U2(eVbj|6y(@m#a zPPd)zIGuURo!{xK(>bRLP8XdnIbC+T;&j#Nn$vZsn@+c!Zadv^I^+IcJm7TJ>73IA zr;ARPoGv?Eak}bs&FQ++O{ZH<^LKLoJEonGUq!!2=6A_M|DRzK^5z%9-<|vU#e`o< z_~nFON%+--UrYG)gx^f~t%ToB_??8G34izQ*Z+ZppH29=gkMPb#e`o<_~nFON%+-- zUrYG)gx^f~t%ToB_??8G34izRd;JrBHsR+Iej(u(6MiY-mlJ*^;a3xWE#cP_ely{> z5`H`3cM^Ukyzim!^-uWOgr7_Jg@j*B_@#tjPWY9CUrqS6gkMki&4k}d`0a$>N%)!Y zzK_1wKjCK+elFn`5`Hn^mlA$C;a3uVHR0D1em&tg6Mie3jVXem3Ff5`H1!7ZZLd z;g=JBCE-^Sel6kG6Mi${w-SCk;dc^#CcN*d@AXgk*@T}<_=SXDO!%dQUrzXygkMeg zwS-?!_|1ghO8D)B-%0qH@V>9U*FWKB6Min?7ZQFk;g=GAIpJ3lel_9O5`I15Hxqs< z;kOfhC*fzp`{?>z|7^m~CHz9dFDCp_!mlL!TEed<{8qy6B>YTxA78)z4kY|s!Y?HJ zV!|&a{BpvtB>ZZ^uO<9?!fz)0cEax@{7iV?V&ChR@Usa&m+%V-znJh#3BR21D+#}v z@M{Ubp77fVKO5es+3)%k5`H=1R}y|T;nxy=J>fSKekfSKek5`H`3cM|?U zcwcni>zD9z3BQ=|?S0(+_OB-9*Asp#;oJMb`|WS<3-9OK`^5YC_P+6czP*pUpKtFg z@8{e5%=`KFzVm+mKzJW`Ki}S$-p|h^=9d$`y^p=$`qjjId!Kv1`K`qKPQuTG_nG%w ze<0y!6Min?7ZQFc;g=JBCE-^Sely{>5`H`3XT$sI`}Mz+@GA+wmhkO!0sZ!GCg$7c z2Kvo!C+6Gd3i{2r&l&Xd?Q;kHeES?iKi@u=(9gHeDfIL0a|``^`y4|*-#*vS&(DO< z1@!X=5`He>7ZQFk;g=GAIpJ3lel_9O5`I15Hxqs<;dc^#CVbAJ@AXUg*@T}<_=SXD zO8Di3UrG4YgkMYe^@QI{_^pKBPWYXKp9!Dy=zDz=em3Ff5`H1!7ZZLd;g=JBCE-^S zel6iQ6Mie zN%)!YIi0@OKjCK+elFn`5`Hn^mlA$C;a3uVHR0D1em&tg6MieB19{A$9lCH#8AZzlX!!fz-1PQtg(#r3;B_BpwJzI|@4pKqU|>*r^} z=g#{1*@T}<_=SXDO!%dQUrzXygkMegwS-?!_|1ghO8D)B-%0qH@VUBv*LNV{XA^!d z;TIBqG2xdIemUV+5`Hz|*Ajj`;Wrb0E8({jekb8)!siP6UjKxjP58NlUr6}HgkMVd z<%C~J_|=48OZfGK-%R+egx^m1orIqWpR4S9{S$sR;pY;5A>kJjektLX6MiM(R}+3M z;nx#>GvT)qemmiJ5`HFpuC(vF6;b#+mF5wpvelg*f5`H=1R}y|T;nxy=J>fSKekk@c^S8#avppY?A) zA1?8~^pzQrxtIOyi_f8VR@DFRdB`uLB0d}XkovIicL$B^KF%NU!H6#tIJJ+8_<|8% zBH~L2&i}u=<>8A&`Jo+0cK?6pmkN1GS~cSOziQXl$nNLqtxm=)8S*mxKQ9lp`DBTq9G5rI{9x0=(6Z* z%i@d-xD99VEj&cwCh5C0n*CDhX+2Z(DFNruMp*JU&aiC{A@z2a~JXD zqrCNjG1lAJ#qAz|uN=n1SBv6g2>)Ri54UyJ6SrQ0`7mY^p*>%~F(D6HJ>dEwznk#TE+6dPFRV)+exygc zUZCYS7q{yUZmeMZL3-r=;3{zcZq(%?hixFVdmgkr^Q@oXh6>i<9xO$i`Q*3IquqO; z<$o-0eFMiR7*9V&K*e|0BYlB$?!mypLg3W1MBw12fm7GWz`@M|-%@C=381adJbUhg zHhwE{dya#)KJ%7QunTDQ&?kHDgIo`*-|kzG>p`CL+VcpcU*xIZo;x7@B2QiR+yOZc zdG@#G2uR)JkJ95VT|U@--5RQAaM+&89TxH{35WI_?AA5(+!^e?Hf$TlFmIK>K^sq9 zL(l8ME)LZ*w9cq79vrG?aM;%3_B;Z&>5dsWjD76&2iz{?L3^HpKMDEmh4%acS(o#& zp1lr$L-mjB&Rc^a8Ur#P&ieM60CGQ(r~mev0LJ?cJ;w*T&-J0}GT6O8?KJ_kbBz(V z=RRn4?l1m51$XrbJ&uKH-*I}}qss?}{Y*GZ;5;|?>GF}oju7q`@?7_yhCKJ?u7Pu( z>=rn=L%MvhdmU_jt`+wIHE@p%@sZtmL-)+cVLK~7G=5}vA2fCDq(|F#cX2d%>arS` zH&o}yVMXz{9yH^3(BtkmM48=h_XeGuVA@4b2_wo@Kl|&PC0K2TsjgOY+oqWZ=|R4xAc} z3Y>Es9XPqe0;dKW!#wI>O=`AvSQ|~AHEb{TAG$Y&?merA8d;a|_*OILuo~Fk&V@g8 zO$WO@UO<|;>_2qR40e0DQa3-?z2-yf40hMpA>_I4GY3w+L-*LwYnSzwzGcD~dNpg{ zoN1@Pe=a<>%SR47PIye<mIu!yt^8Sf z#4~Pr@a&L3N60wFz%37+OIrE!^oVEN@*uU6r(VXv&)4IHfnOxNAaL@=$=ew4;xMKv z#A6+}<-toxD}SjT@r+v@ye#A|7c!19aLa>NkXHUmJ>nU+JV>qNsh4r^tMqtv;MWKz z2TtBNc^d=%Lc!c?^@wNO^58E+{yHJ!7z4LFcs*(5YkI^pZh7#AkiSvLIL5#&4^k`n z$$G?t|4NTH2Y!q2rohP?CvRiGUxzWb3h`J6Zh7!F(#qeiM?B+}2k!{^JB5s64BYbI zU8I%2TaS3gEe}#FdFo{x{2o2t8~A;~df?=ZleaP8{b9@lLOj-iTONFnwDME*h-cjL zU?b!o5;Bf459!hJ;KK^?kLd9sXyBFysg*qSG7kQz9v=_<3E^XblQ&M@#(+%c7!K1Evjr}ce$ z)dxaGk&LcS$r9An^?2j3*E{9Ag&Gj4g1TFFx{ zuR+ zza_2w@AQag-0~o`lBZtA!9UUCr-6SaYzI!>IC&cbejdhrA;e=HxaGm$lUDu@dc-qs zdGO1S|4PU>#=tEP{*kouf6^nKam$0$N}hTd2meNoe-4~q82%;j>BRpU`1DbJ2JueF z&nRwlXA=Kg$j=<*XA%E<$Y;cD?)SuPuUW2wX#4`=R@;EM&0SFZ-(k&##BH60#ciEM z#BE>BM6c<2w`cO;^nhN&sh3{c717Uc=doUc)&Py@pc{y@qo>dJU%@dJSh^dJShUdJSiNdJShydJShCdJU(q z^cv1O^xE?D+Bm&7POssNjk&E!F6K6uywyN1=C&7ktC?KPtxocGMshK?`pH{Q$QkFF zSZ#3YCpqKv)oO#=wIFAlYhty*?K+Y(&h@m~;C2s?GtRwWwZZNFA!nSr>{`R^ej{g` zy6irI+xiR z&Nyhb!R>iX&NzG8^S8UdH1}sW7dT_B{_ebx@8$wuRJeHHBZP}ZdE?}53^*9ZEFr{W zU%2JLks-gN5RWl%%Y#dW{L(@^#=tEPE)()uAs%DkmIs#&`5y@J7z4LFxLn9DFT`UE z-0~p3AkVq+;427M416Uan*7Q_G<+2y8osI!4Id>$!$%9zaMnY^In&U1{hm;KsQ$t3 z{zGH*Z+zv4uEk(?|Dm1_cKbS1gMKHeF+)8Z?Cvvk#=&8$DZjdcy?!V}!*fD3d<`KQ zzNQcjUrUIFuPsEwSq}}TAGRjzSx-2tjc2T#m$TaXtZn^af9oOVwVKxv&nwt#jBwq+ z$r~qcW5D&onDvEt%!OMX+#uvP6yh-kZh3H{kl$E{#~8Tf!A(NGAjD$~-16Y2A-|ar zk1=q|gFg!S&4qZ3fm{G5A|@cyU)-W2Z#Mc`Rx_# zwSy21FACA{9ffH4PC_(%XCWHCix3TGJv5wt*qW?oJ>jf2p0Rdb&T8wkw)Kbot%vl- zYW}JCZVL9=UASxD&sDR_Qni$#}8eL!S4P;J=foQG-jxV!S4P;J=E{0 zl^;6e;IPBRk5I7JkwP@QEJVYP5~ATp3(@eO3(@dnglIVHq2cty)?_{F2{qVw#@cx~ ztF6!4)*tq_9?~DH`B?Ge73?)ZcwFG*jgz-A;0a;Oi9$T)!YvP;6!Iqv@fZWQJa|gT zpDM&-4BYbI#E`ED@fZWQJa}5jpDx5>4BYbI86iJOh{qVXcE=9YKiJ)WXv|=D{Lr-+?Cw9*^TBRk zhiVw??myJS!R|goXB-@Mq1L}B>~*ma&AwG38h(io4Zl=~hF>N`!!H-2;jD*-(+^vd z^{gkH)y6Z{&dXVCeb%=Au)pmf3xtaz<({A z9Qdt58*`hut$Dlnbz#gMLReHBb@c=Bb+tqBb;^UBb>Pp2M*FN@^BkNF6Q)?G30Gcaxv$6FowL{xX$!ddJZ^;>_$5tEMt_wNiTpz0qZr7BYajvb^ z2DkfxoN?;1YX`Uchn#Whvg;1F`;DA&>au$VZucrVl94?p3%w7swgs z^~7p}+jD`OanNdm+w+K=anNdm+w+l}arR^n?hQDvcW`pX8Ef^!Eltpd3LjJcx zTl06~w$3NwHn%Nq<3AO*+CCGvxnGFe+~14a+&_rhT+Z>5f*x{KdIzU2dI#rBuLsU} zdPkl!wE|}heY8A%gi{xNgtI1ngi|AZgtISwgi|klgtHfYgi||xgtHfYgmVu12&W$U z2xnjV2xl+)2xooz2xm?D2xlGo2xso+frIpmJlw{Ri#h#e40&6VT+F#1j3I9|kc&Cj zk1^z}W^ysN+Q{1($r-1|RvX;q5>r*T-su+chO;oNH^f z!R>w^XPmn1+QIGqA!nSr?7GA4ej{g`y6m2T+r3K8ICa_m2Df{joN?;1dlhca1#-rD zJ+a#0_FN!m9JJct_Bqq9rDYn#>y;1Q)dt?qc}pbiDDbY_Z9IMdRxU9 z#deDM6S}|U+mEvs0D8-Bld=Pz> zVsph#iUk!jDeyt`&Weo{2Pl?M%&fo%(FZCvRUEEhKlY`z*%V7D#wqX_$M}o_A4Ib* z>+h~uMDaZZK8W5!v7zE1#Yn}h3VaZKuwpaCVG8zRUwSZ?Vo3$}$(D*U6{{*{SKx!_ zNs1pUex_JlF^2*lMDL>*qu@Gmz2;QlgJ`bnCW=E9?8m^qZU0flbOVOJ|S-|>p& z6vGwxAo@7PhKef{YbiJrK8U_b@e{?RiZvA6llUO|GR5|a$qM#kU+xWR|AFFI1@%o- ztfUxF?4Wp1v8UoT1=ok`w4{Q24j)7xs-VC7Dt1>qpkRHT>mw8;MP6}?g8kTc7RBO$ z?n!PD=J1Pc2wM};NHGfv6X`7>C%cp1wM#ATtR)*$sTnD>o1|;Je-U3 zl@;vAzC3r9QE-j7R^$~W#RvsG-AQqeVs{07rN2ign8TUzLG+P|g5o9xd)=*IeQH}k z!L?dX!5P?(IkPIbw+0lP1)p(@r^cNXcPUtpby@#J#R`gL75E_fBn4~Jr#%#RDp;Ru zO+V=?{pFgmAN$Ux;9lbzja6)**h|6nT3)e>;tmDZlk3X$ouI&DPka!4f`a>#`;`0j zb_MIRH+xJ}Y@yg&!G7dsSBz4usklP1v4S%#s90Hn52ESy=8B4f+$suu5Ph0rTg7>b z)fB5L@ImzXitQAaE7*^H=TQ7m!S&cmLI1f9^k=jJA4JnP`ggj5Jy{bUM4zGHe8(#2 z`RWRM5Pgh-`)g|j`?2qwinSEWD<&xD@4U_f z3eL*N3a$^=iT&7jE(JBMpy0mZ{@PzLs90Np52E)|tgkp)!9BN*0v|;GT(P<0 zECsz?SAh?r&sNamlN9X7zH=)!P;mXYuFPj0)*GY12hr@y{?{njlWT-Mp0GJ zGsfbB=t~saDo#_dAN$Uu*hE1uu2j(9Jrs)sj!zD~yMp`k3I#pgNP!QcFIQ}*xIn=* zXDmL5zEJTK#Z?OSW8ZldKT=RX=b~1|%&*{D;Dcz^9;3KcK~FbT;DhL2Dz;XfqZqB= zUcv{_=PI^Q&?oj|-}w|6 z8H*31FIG?=b+RA(qRHbkkNKA=xTh~w&bqR(wx!oq~J+am4|Oj}*Hq?pG8Qzfl~ncwcdd;@65r z6^kimQQV-QM-MAXiaiy(Deh40qBvP`l;RD=p$g8*y7wsO;HpTJ^_F+BN zZ7L2@uqJW40`FACVT#uk?8APXYe~fj#r2A@ipLb3lk?J-dlfq>ex*1<@veeC(J#)m zh+-kdjSB9KhZLNX^D?ilpilIJzPzJg9oD1HR~6JlUDWryVsFJ$3VhDPxt>*Ue)_|B z<{hYbMZxuIDX8l?1+PUd8|7@!T!u+K0f0Z|1ZTUihl+~lV?5F<$Rp; z--?Nf>8Bg|UOt`hbm$+I5oaz4&ExA1)7 zaNz~Q`Ggk zjc^&^)xsqMqRI0+o@<3a5dK1#4TvVcyzn~V3c_CsmkWp{zml*fTv>R%a77{OvhQT! zYQh_Zs|p#%c;>M#>$5-en2*mm#*Y%-AY3INnmp@G7IHq$Ia>HDVNQ6H@Q1=%glh;g$i>7nmp^VF6ZN%`w3qV9wdBGc%blQ;laX}ga-sflRs4Ws&Jg}72zQP z(d5SqUl$%Od`);*Ks5Oyg)L!O_=fNZA?va)`yVTOOL(-9ag1jk>#{!kGmrWBjAQ)I zg>MRv3Wz4pdaTR&IOj3Kw}lgg?+A||w)%f9S?w(xhtNkYako_VbMfsp-~$9#OoG5$>9Z-r+B zM3ZMd*5!Ph^DN;f!t;b};km-kgy#!C6`m6iP5wgR7s88#p9?Pth$deZ{y}(&@b|)t z1ER@aCj3fxx$sNjr9#$aU-rLR_$T3&LdG$kd92I&?9V*r<1>!&R|)x<=!$@7@~p?Y zoR4!(7XDfIuyB}SVEUn-Nv;w9i&DP``R^+~z4$K`jK}{*IIr?EieDe{AbKw4XBOTN z@*sM4{ zcuB#&?8AP4RLrY*MZv!8!+w8I%&K@s!Ox0!3+GdOt{70#!a^=j6P< zQ{XcW{knoa(HHXOe*3Q!`uV8>pK<8d6kLbG+ ze^b!EdldM$3CaIm!Skh~z-Ju#Sp|K5O-LS}=h=TL=-=IN`M(yD|A&I-<3APnj6*-I zppUN#$>Y#(2qM5^oKr@=XHeF zlj(%`j6*jSydJzHB#%$urWeu=`a*x`BY9q@c)gl2_>4n8q2TrA1tIhB>Dx>~`axgl z4}B!h>maX(vjm@U=tmU1o;@dI9zK1`2)XZWR?t8CNS@bOUTqy z&^siwYMJE+$-9I6}Cn zaB<;60ny|a7cLZBg_vvrxjlJqFy3c}5Vs|hy{{!sWMA^Wi}`>!n=EnGvmqHrA{ zedoF`4o!YlA?vLxq_6ay{^y14H(I!|aEy?1(>MA@Kj_O3g`@Luyw!yC zk$zGaePbQQGcPBkPU>Y}?hERq9_r$})ImL*cYWdN!cB#13k$-vgqsN06mBeBL%5MJ zC)`j-f9MnYupj$wE@U3_S!W9&=iz!&?~jG7!+NZ{rI7Q`59*=ctjqfBvz3r~=_h^N zT1cJLOWj;A@>~z6 zY^{K!+7Q$BBW00WnZp4by5#?abD`69?pBTaC_ki!d-+DggXn57w#lH zPPn7+SYc6kjFA4&C-z}K_B~n1Jm#~`DMHS}^`zcYg{;GRtUFQ2dFTiA&~MgdefFsc zsh57z*VBa5Nxjs~^&-#ppbqw9U(Ufe##0ylqz?9DU(Q9H)Jt9TlRDUseK{v}QtwZN zrwexzo+;d2c!qFS;iQm1OSp&hIl?`KXA2p}c=G28_Y$5j+*^2_ka3JBe}QnU@FL;Q zgck}K$9VD=3-=LTA}k53LdG$k{H4Nug_jHW6J91{oQ?mL^f>A3gog@m6f&Q6n0JM6 zf8kZa1B6!!S%>w=PZrV#&da&47BY_U<&aT{R)=upaBuNA_Vq_T@a( zNxjs~eZqM;H|M`oSQg$TJW6=C@Mz&Z!k-K4!efN@3Xc%pE@a;aLmr=T4+yD)dN@x* z$U3aYx>JOlhx>u^JS1da_NNZ62ldh~>g9S+C;PGQ!$SIO{YI0gF6yHm>f(A)2lY@F z=b#Sip)UGO9n?czoSQnRhq~xDuiH-uPY^bRCkmewP7ppG@=pm*l72>bvhZmk;}}o= zS>Y+d=Y^*VpA#~U@#J3+P87Z*tO#EeGLG@&UlyJwd{ub5@D(BBZ2U*k=SjaOJXiP| zA@f;>d9Ml25WXRtBz#@SI;=;&CFJ|mW5VNvZweX5__Kw77G5m;TzH}IPr@^WZwnd! zTj5#44}}*9KNd2db?DRYgp7Yn$T-F`?@=LrXbaB~z9VG6PlWW3K2qnW!t;gi3ppp} z<^1%4b8=qJ{jQL8SdVq-Gj&i8b$uXY9oA!A`oKQy$G)70I;oesxz3!Ib94SLgjM11 zg_j8bAiPxgrSLN0SHjDMe-vIM{7lHce+_wj#{ES|9n`~l{w`!4)??kj2{{kflk;?h z?92Yt!S$eC`bE8559(w;_Wds*eYSq1$x|2gQ4e)-y{LnFsEcz@2lY@F{iY7;p)Sr% z9n?cz^!s|@0W%E!Tk;>$HR*k%uMqw-=)I+Hl>V3WEyDf6`2B@9N$)EBm2lsX$7kHm zLe^m(^NZ5A38xocDg0Xb8-zcRzC$>p@G9Xq%HJ&9M*1${%)-gSVZvVvw-nwZ%m}X* zPA9xoxVi9N;jBXTWgqt2RCuRwgphsNhy6Ad-Yr~0$iD2uej5nu!X<_5%RcNkMtGla zX(9Wv5Bsenyj{4M@EYOl!uy453Lg^ABm9MM4&ejB9||84&MUlDIH&MI;b`Hb!uf>1 z6wW1_B3woIm~go8I^o>HhHypU!@}hQqRFo$Wc&(2FDGO@=94#{byOp6P^ohQZH=lmdx3z?vYmK0n5pr%j zCqDgNTgZ9o2mTBp&-LKCtQ&mBp_dZU2l~N0e6D9+NFV4o{%j%7_2#;-7ktK{M+)f= zePSLy_sjZ1`os0a|6a&*KXG4e7<|T|2Zi*F{xJ`q`*9;7edB(?pC#nEU%78L2|nY{ ziwo%|ePtd#_j^G|Ke-?AX9{_q4?Hh63qIq}iwfx@{bn9MefyD+KG7HYLm$a+F64Q) zMerGiUPQ?AhUW?M@afx+h4hKO&>#9peoG#c&%IP}88tAsocnTJo`wiePS`a*x` zBl&HFyl!k8e8!;{6kaLhdCok1`nH{rKG7HYLm$cWdc^C}_Q7WydVb**LS9Fhhfm*j z5OP0q|It7CNS@a_UiWqkKI6~}2zmYB^@4f$yngN^q<_-||KEBYB+u(8ucx~NpK<7g zg!GwyGY_BqPlfbvSnwG~epex{*SiIuap(adeWu^c!zaJHkpA&{jL$godkFcyv1jlZ zhyK2hKGSdJ;d9=-gq(x(aBj{|es3Y)Z^j0najd_;@NwY*Le{nYekRRz;QI#qupj@nOQwRHABz#ABmyrFb<5}Tp!q0@a3x6v-S@@jrbm14mUkiUH zJVp4t@C@Pag*OR55uPf1K{!eH2jLCEws4~GMd7u=&xO|sKNVJj{-u!d*N43MX!5Mb zy1xjzCS>0mg{*J;pvhAg^<5M6uY}ZpvyeKiUNm{m$2qSK`W7L5xK+q`?c8Yc^of2= z4*E7B=ek2k->iRV@|>IVUnQjPKM$Jo(}z1l9z@?Q9z_m$mWX!7)nzFi{ZzC1o??hpF* zV90~$hLHQ#?q4){`ba-77INPo8#MPH{d_3oLG&X+o)`A~K$EAx^!Y*|{i9Fxi$2k> zM?)S&KQ82X_gFwQd7d{se=ZQxKl((!=o9^VBIH4IQ^@oB$$)6`JTIRTo-d?-^of4a zC;IhN$b;x-guHG%9S}{P=Y3Onu8{uGC;CO7=-0C$52BwJ^15WNPiXSIt~@C`OGy9d z6aC_T{S@^?+#d^p`%L6Y?PXNFn`fg*=FUOUU<+Hv^){(_i|0rjT=U zKF-PcIOp3T52D`{^8M!>A$8e$A4`8J{gIG;SdVqb3%M@ug*=G=Bg9q};{ z-!S5Zh;JV8ts=f%#ETK%CE~kBeD8>tB7Q)`4~h8rh?gUNOvEQd{N#vNB0eeN=S2L1 zh*u+idBi72{Mv}uB7RfEZ;kk!5wA!5frvj8@y85Mhn=#^?*vI-O;!h;JA1V#Ife`0f$kJL08?9}w|FB0fIi<%k~> z@d*(>IpURwPm1_C5x*ef)remn@yQXtHsZC2-xTp%BYtPZ>k)q-;txgqv4}S#{%piw zjQFb&Z$~qWnP-9~bc>B7StlkBj(;;9`VWIw&rzF{>F&k67kz3 zes{$0i})0ATl29f-;DUP5q~k_uSUET@pr^+P5$O$_ZxpNG0xv0jPv&l1+D#?+}g88Rh4R`0$7?5b=c~zG%cpM0{k#myYVf593EioPV=fenP}giukD!KP}>uBF?`}^uO+2{JY6G|E4ou zjre5|=ij$B=IV%F8}aKS&hJ(9zwTZ)NBq`^-x2YUmNjS#BYlDt>V_VdX#@4;txgqv4}S#{%piwjQFb&Z$#2=4%Gvd!g{P~E#6!BLh{zk;#iuk(`{~+QYMf`UW|1{!XMEuK$|0&{s zjd&;G|BU#*BR<_M-#+(ejQA`OpEcrhM11au&lmCeBfen77m4`d5nm$WOGP{z@#P}E zV#HU8_~?lLFyd=Qe4U7oiTDN)-#Fr%Mtt*#ZyE7zBL0(z7bCuN#CMJO9ueO=;`>B= zzla|g@k1hhSj3Nr_)!r*CgR6O{KSZ#67elI8a8Zq>CEKSrtsf(T~fdQ(*OR)Ut4Un z!BGe7d(hrv$Ni6|{Er8Iy@7rZ{Eok}`+w5loSAbLx_1cc-T$U-=A4;^^20`T{|5Ap zn(%9V!{l#gycSemx$I{9Z{L2SFM0(2KlPd`<0pR$&0z(|92dYU8i-Q|LOh6Ufesx+z9uTxpGAJc`>c^{7>hz z{r`0Aw2WgPyWaeJ#(!nLdh{NyclMV{ch6tBVxytg2S4yX=+*vv?~d0qH{?GkU!QON zaoar4v-4)1Id9*Q|NANbf0hnB_w2LlSSPr4|6lszy#+-b$nH1(M#LE_;r_F;u7B`B zV~tM>+J3ga?bie%k!afcl| z&AWZuPg~#i%hra@XLbB|>|XnS2itYu^V5Akg!OH|cF zIkznxeE0$S>2_M56L$SyZL$E`Q+!0&25Ti^D}+!*f9u)U>nu%FejUFq-x_nhX_ecMl4-}Y;V{j84ee$#%u-u1KfZNI`zL;bWm zxL>CI>V11YTi^Dph5f9KP4!RN@^{!zTi^D}+#H^NVc+h4-(f#(ecP`Y_Om+Z=cw=S ze73&rmpLc=+#L4ZX5WMM`7W=Yt#A94!hTi<`+W!P?A@QXzU|kv^9AgEf7oW)zAM@D z-PX7LvgZ!<)9UDbfB25}v-NGicG%DA=zV|qj`p+lZNI{KL+7(PhQ2>c<7UzK2V39v ztA+in4*UKvtvlNFv-NGi%=ts-vpVei!*{ixt#A7kE*#p=>KOX|Fs-{qub*Lk+b?s; z(0*1&`2H}>8(KeYecP`T_Oo*ieSetd?Y@1!hxKj0rkyX~(D#S$Za-V!_RC&6)K5ES z`2H}>8(KeYecP`R_Om*MzCTR!cD;Vu`nF#?>}PcheSi4w_OtbEzrtlh{j@rUzCV0- z``P-oUoGrsb#z~^Hy^vtw0_IA>u2lRewoXM&S!OWU$4Kz{cL^PuN3yPI(p}u#+KUo zY<=6W8TPX}dguF&_OtbEzijxupVhI|!3T`pZjW*MjGe~cirD#VecR7|&uDdYfB!O# zXX7Uk+t1dw{p_a{t0Ow!H0{cM16thvzQR7;-)CJvrTcr;%ymOpxpoK{zm*2q-&Oek z0^oO}|Jz^ocOToQ-|xZApH{TrE899th>S4tzkr$FE%AT(|7iSb@o)d$wX}N9AFs~5 z;FtOQzZY8PfbCXjNcQwq3(U~fbN}^}{1ZQ4yN?^T*w`KqK}| zo3Hg6k50XQSDUZ;8plmtUsszym*$`M_NIBO&$RjMSNPYtJ>RtX<5hp`n>K$woquAq zzG?Fpopb9^mEl+S);Deb1v{*K=e2uZ-0P2N^IsT$Z2qG~mTx?G&~)8?&+yC3^J8>g z(+{37SAM@=Pw4sYe6jSMEB@`Qp4R-v`_cSIPnfF_kJoyQW1{t*(fC`A$>(i-#&5OT z={-H&?i-C7eqFVvUsZkAMB^VR<;#+odMM-^89Ceae_o zAAEB`Prsx3&WpxBpnBr*YmWczlo?0umS1}2OrH98oV;0M^sEc_^uvcA-59s>!t0oy z)A)Zp`=f^GT{M2FYiH@{uYa@0sC6dq+0$!{|LwZ*_$LoPI=?}*-ky^;%V(qcyX-o4 z^bE5c(c6C?`9Fy2*+lEb_5Lh(&!{(sEz}ru>uTNlmp$s!Df>UTX#VMCMs{`Xs8843 zcJjCSkmD8@eZo2mufyMKk`wzu}tK@{rT*TgZ@6THy$0Y|AfZx@Orkl{=BcPH>&vcg}w3bs-Aeh#^-mA z#%uq^k_&Az8Jh8;dfxBfqzKVN?7qfTxdvdJ#p`+LeZC+3&mXqBG6V*ac1 zYiPdtS3b1doz-Q|@A(?v$VTJ;eEximcs zF>hSn(+{bh&!h2wRlV`}Z&p7kzta~RYfR*huep(eOrm-t+$Tqfxhhctvk~ z_4mu)`Rpne^!o9@%GY$C-?rZVnqOJ)?B4i)Y5fZ)t=b!Zp!(_j{3VgE^%_U%{I=fs z8_(~){=NCfZ5!M_H~jvf{HWUo8>auh$t?Lr<{sD6xp^18bGu_t?dgARGD{=&HNUZP zG+*mCj@Ev*-u3donR~UoY3Bd=o)dfe`q68RT62YSdir15KlX1`eSeMSFMYsO`Dg#N z&^ohzG)K37{^t95r1582;GA{#xNY8^KSQL=pZmv)Oy!%l^-GHk_I%bijlcf^x2$F3 z*?)AT@%NIB_s2K=<)^3Q*J>}B|6Zv7uWO%`|NP_?dwQkUPRc*D=<@m9L;a((lkx}O zw^~n|KUb*VeABl6KB0bF-}J5jo|v!SzG&XY-?jBQ`O=ol^tAc5zxk#QtPSMPnfS~+YFwOxAR^)*TVVj$DiNR=3D>FH*M=%zioZfe|&fG{EL4- zHE-juK6=^wtDBwO)8^a$=9}I=w{ZUJc}{3p{WHI|WPbXwr}gw(2QHC6@*l@GZ2al7 zE}tK>)x@4Q-}X1(w5@OT+WMwnnSOYF%F-t_Z2U=AEt-Gis55%neB0lA(^rl@Ie%Jb z&csdTM*5+Hi%-=40{rml=r!>ly zd3yc+^V(-MmRV}yo?hX#lN$egaG=-kRkM>CzuIYuo;Kh1H{UetAF1`NUR&SvE&ra_ z`0F;a_WFIt*5@>?{c^sZHs9(u-}FAUfyR#a9@ndPfsK}E-1_T@J^k^xc^U^DdP1+> z3+7t5G48R`dfI%e*L>5qzSV2%oBsUW#T##&eRRWKKQB9a*~V9wp3OE`f99J$VD|;O>)Z47p9d_~UElOr zJ)ch%{Oi9Tfc36({E0%pQm1*l(+GxUA1WAx@9lyY4dG=^G#14eRAW+#U*<6 z{$k>!#=rlvTu<+@>8XvUmsq%0@7T-FYW%#hWKZKiBj5Hn-?Xi7_1gNT-(UR1#>;0e z+nfKJai=#<{K2T6Hs9(s-}HzjF3Z2DpGUU({mAam%aZN3?4{GV=q);hcA=IQx6 zM%w%jE`Ou@^EdPHP22ikt$KUUe@g3{#$SDbXV$Xu%>QQoZ#Djk^5gySO<%g@iTS1P zACb5EH{JL2{59YIK~Jx^>4f|Yjb-vS{{1~G`QsN{rKioe{mnOR>kk_>RKKlndZz79 z&Tn?lf_WQ%*TIwWPs}s2r_Hzh%{Tq;NyGEQjyt7S@6n$un!n}ZGkbc8)8@$Uck;wu zy;puTe}17W&+cjSZGZDk+xk|ot#A76a~95@f8_m+bi?}p;MfuQ?aw`>rx%-RvHX$0IigVv_s^yKE|dS_^pkqpe5==d)3(0dFSfqv zv9rvbU+U}Q8aDpJJr>MAa`D8THsAI)-}D^UO~^0&?yS9f?{8G{7yod0PnT~xF`s{P zhF-nPJaKyd{!w%EwE4Eb`KE1stJl^yyd-YC`K5?!~dit|Vr&|Z##(yr2Z`yp@&wSGpX1wxN=3BkPq?vE}(@Up& za7Oty9z8*RtnqC>^G)md-T0Y){;~T<&;RbvKc?}2v+%diclDSI#xUu`$ zhxO{+`tv0lyMA~=Pn&P`ns4{d=9df~U6^u7-tLzz4_|onV*|hF=_hnQZYJO6KPLTz zeADJzz2=*qd#Cxj>s!6^tiDipebaMXHr+bx-y1K@{-({h{pJq7=}l%^pfPEgiM@Is z9Xq12=6YxM^pbNe))+B(O0V9@`!3UXam_P(+I-v3eABkRz24jUruUy^?#AC%IIUOj zCwnZ|xNehkd)j=f*L>4+T{ofe{@RQ6>V2?LY3y|IGCh6tO(!;{Ut@t@y~{pvdgHkB zNAxuQ4DzjB^G)0O)^A(i^hWt(8xM?GsyBb@drxir@Uj(q+I*|meA5p-Duv1Yt!#$`R9q4`$tR@>du{dsKqWtPg9 zK0mW@>p!09)_dWad*q+bp4_-8=nt;iD!=;O`uFPDr*-`Yc09Ga-c~^~{>G&@?Ty!G zv%CJJF#q1y-dpz|t-oCu&wk^tUahwu{=U-Yv)@~@&EMN^_n)7Ue_8d6x#y4v=`Z>k z?Pr?v=-&%Hee!FE<~LTo<{!V{4eQ49|FQF**NuI2UHir3_9zOZO?)C0G@~zML%x6C5xA`aTvT^r%yz}hY-SwV6cK>ca z9|-yng;~4T$9y#XH_d$dYkH1d*UuN1KdsmAedk|2zx})yG+tfk_U`q(cblE^yFPVR zW7eH-?P{H`;qK>#yPq5GexBm)=Z3qV8}5GY&bRwh_wy8YKTmP@b9X%(ulu=MUvxir zb#y;B-2L2e_jAMD&qMRSy`LNIe(w6w{XE6p&kc7!PjUBi!`;t~_DE0b>Exen#KVm zpX)xK=32eFZmoJvW3jzv&+~fn!CYIfJ8rs(jmf9J)b+=nzumfPUo^3C_XTry{f!cz@n+(S094_?+9SI`lV+?A!}9W%$r;AP(oX$C& zb$Y<*jMJU{-1(huIo))+?sU!Rs?!yx%TAY^E;?OsI_GrO=>exRPIvZo=Xbj0bkpg& z(>157PFI{RJ6&?R=ybv9oYPsS2b|70-6^^AJKb`+>2%%cn$uOMD^8c4E;(Ivy5Mxq z>8#TOPG_9%?BmYwbj#_c({-n7PFJ0-I9+zS9W%$r;AP(oX$C&b$Y<*jMJU5?)*--oNhW@ce>_u)#-}UWv8oG4Zah9 zzZ=+hsi}0<>73IAr;ARPoGv?Eak}bs&FQ++O{ZHAKTRr&~_9o$ffDInbTo>8#T^rwdLO zoh~_DcDmwp)#;kkb*Gz7x14S}-Elf|kUPKAS*LSO7o09SU2?kYbj9ha(>15-PB)!y zIo)=;<8GBiHP4)e*<86+6`r&~^UoE~^~>Uwfc z7oDy--Ez9^bjRs|=ce8->vYlSiqmbU2cDmLy`0kpr;ARPoGv?Eb-L+v%jtm^rmiRJ zbk6C5(2%BK?2F4!_4<`_y5Mx#>8jH;r|V9)oNhbaaXRzT)b(YZ zE;wCuy5e-z>6+6`r&~_9o$ffDd3ox32b?ZDU2(eRblvHu(=DglPIsKnyfSrtS*MFm zmz*v;U2(eRblvHe(`~0aPG?@7y50e&i%yrFt~gzFy5@A<>88^yr#nt(UUT(2opZY2 zbkXUO(`BbCPFJ0-IbC2%BKj?rOYFZadv^I`f9B z-|4K=Ij0Lw7o9FUU2(eVbj|6y(@m#aPPd)zIGt%tecr6oIj0Lw7o9FSU3R+Sbk*sa z({-ntPPd%yIGuTO>hldaopn0rbiwJO(vYcPg40E(OHP-at~y6X)Nr#nt(-gf78I_q@K>4MWmr%O(kovt`tb-Lzs z-RY*&EvMT~cbv|=Ebk^yd z(*>uCPM4f6J6&U7QNy3AKTR zr&~_9o$ffDdEcGa>8#T^rwdLOoh~_DcAEDA@ZZ0+zx%JEKjibbeElcD^)(@HzP%5i zpKtFA=;zz}1p4{*zJY$ey^o-uZ|^JU=iB=X`uX<0gMNM|{9V4E-%j|ggx^f~^@Lwb z_|=48N%-Z2UrPAJgkMPbxrCoh_yY+)lkhv?eF=T9f5LAi{AR+hC;VE%uO|FT!Y?QM zQo=7L{6fOdCH!o{A4vF_gx?A8%jkRk6MieE&4{CdK#CH!i_uO$3(!Y?KKV!|&Z{9MA%Cj5bfpGo+g@IJ7<*FWL66284}t>60f z#C&^STfh0$#C&_-Tfh0`#C&^ST)+7P;eBNN{A|L{CHz9dFDCp_!Y?QMO2V%u{93}V zC;Vo@ZzcS8!tW&fOn9GMzw0}Y@Usa&m+%V-znJh#3BR21D+#}v@M{Ubp75Iqzm@RY z3BQx@GvR%LeXoDQ&nEm_!Y?HJV!|&a{BpvtB>ZZ^uO<9?!fz)0R>E&5{7%Bpg!f7I zz5WS5oA7f9zmV{Y3BQ!^%L%`d@T&>GmhkHdznSn`3BR52I|)A%-Y44k`X~Hs!p|lA zLc%X5{8GX%C;UpnuO|Fj!mlU%X2NeJ{C2|cB>YTxpKRajpYXE@KbPGssUrDTQ@2l@Ozm}NaO!)Rb{C?}(`||tw_CEc7 zzP)e1pKtHu@8{e5`uq9zKL38cz3;!DZ=VC`=iBE3`uX-bfquSyZlIr^3Gd_Y=VueX zea@iY{8D0mCE?c+ely|Q=N9_aXP;x}=MRL>9rW{a3BQ=|?Q;`U z+vh0y&9~20^z-d=7X5tt+(kd%K8MlI9|)g|=;!AWzI|?^-~3`?zJ0Ev-~4i7el_9S z=Ro?c-%89M2%p2~H$R*3a|yqY@Jk85obW3Nznbvt3BQ@}TM6Gj*V3=Qx$rrYe!hL~ zrJr9;%&#VV`N%)!YIiY_2 z%O?C{!Y?KKa>B19{93}VC;V2zZzudt!ne;|_3Q6I_}o)JznJjL3Ew`q)o=Z3V!nN@ ztKa;3V!nOutKa-qVtyy#XTs;I`mH~Z@N)^jknoEMzm)LH3BQu?s|mlB@aqY`nebZ) zzmxDY;d5nuuV2E?CHz9dFD3kP!mlL!YQnE4{AR*$C;U#r&xFsp_3eMc&nEm_!Y?HJ zV!|&c{7S;FCj45$uP6Lw!fz$~cEax@{7m?qU%%^{P58NlUr6}HgkMVd<%C~J_|=48 zOZfGK-%R+egx^W{neaKqe%Egx;b#+mF5wpvelg*f5`H=1R}y|T;nxy=J>fSKekyXF(M*@ z7-W*z2SMy(4-y8E1i{1_``Fbw_MNfsM3_`jhN9Z4hN7rttW`xrEx*tEdG622Jx_b` z{9m0DuUG%yjMsHOpKH0V>$WNzct4{E63lK%p|KP$)ImgAqD<1d8gbW0xp9Dj9= zzc$BTm*cO`@i*l78*}_kIsWDx|MVPxYmR?bj=wF(KRd@?2+tvxJia;psvLiHj=wg? zUzg*r&+#|p_#1QlO*#JN9RKtje`}6^R*t_d$3HvAUkJ}-mpuMC{;C{*b&kI-$6uf0 zZ^-dC=J=a({LMN3={f$^9RI8we_M`!c8=fA*_S-N;ko!yem{p_%J1j$OZokreks47 z+b`w!bNr?Jey;yN`M=l0{Dlwn|6AAJ>F3LTt3TD|zs{e){0YpT!2Ai!pTPVH{9l*= zf755OHrhD+rRjW6aIQGt6D&B7H($75dhvS@O3}wUgY>;gu&=5n?g>qFPI~E=^pt+T z(l4=?6XqCjp%A~YFip{~6WsPb;$1?_(n5TlI`vEb&XMgPe+vowqIymL22#px2fGrY zn8ox;oVo2_w_smfNE|V6+rjRbW$&R^eCD?ImZfK~FC`?7m?iXTJJ<`e>`UqupSkTR zrgr8>95K|PhI3K_4%*KAm>qWJ!R+9m?etY4!A{P@0`DtaCh%p2=)^3iS9JLDdR-y# z6@}>7SJEpwyh5+&@JhW_K*d*1UQIve)i>f*7_Xa z7`h+wd(Iqx_q>3(_sd@6^U(Wc_U^O4_$m^58=zNo_^NtEhga!!wZI1o(Xp?tS9G{8 zp+twTA&w3otk*RIrw%%HY{bFIO+EP9dR;g0&4r%B9^z?yWv5zk?~!#oOPKandaZye z&w+Z~F7VMp&xf`92Rp}sIr2)Z&y6wSp7Vy{>r04PPp=yU4&uX(e@NiqT7iQ@0|(a$ z9AuxYWv{~m2de`IsYlFky?Q1!;;uD9+&zpGCysdXjDm_&XQRMD_dxzp!47&Y=y>cK zOUQ$5lfX9>QV%=m82q-gU&rJ3Uf}f2TINPBII-*vj?EnOJej%If?mscy_tlyoMSr$ zzOrzOz;_h(4}2xzmVtAQ?-2M3!mR_}SGY~WE0gOu<^7U#Y+Q)hOXzc(^Lo!<=lpSg z&N27EIp#h&w_S_#lQs0uIqX`Tt3HRZk&77Cu?9{Z*1*Y4UN|*b58qy|-XnAHe6XPt z!!_iXUBq|AEPI_^(c!!46&=32UeV!Po9J+6z+B+e@l3E0hn?K%HQTS#-r|ngN8CBb zin|Anh1arfKZ*12A2{`WT(z6n2hJPp^pc)GmC60v$8-(Z*C#|V{qg`-zD(Q zLhm>21xLT6$6g_Zo_hvHwODnLgL5+w;iNc z?DUHdezRV04ZK-+OW@eeu{#F5EyUa|#77>u?cg1lWxrFe_{?nw?+W(2g~Sm9w;j9( zv+Vck6`#57AiZLzU*h2R>GgrY9~9mnICgXFjsYJEF%Jvzkq2%&_y}g%r|T7;x$WSi z!9GJs95Ha)!N)Mm{tvQ+rd9$mi-OA;xo4$d^6bJ5)wxY+;;FUm}P%kulUSu z2k8|%{k{zqe@Cx>4g5XfyMbdj$L<*L{Sfnk5FdHqwu2vHmc31{_{?nwKMMAbg~Sm9 zw;lWhv+SSh6`#57AiZLzU*h1O>Gf}ce%Rj3Uie+$*v+v!2K*t!{3ygn z9=Pq`Pnc!r2cG!MZ3h>S4|``Y;g+d+CI5B=hUFDzUn@Pe>QWH-m| z7;w=L(^ZI%JaF5=#e%(?5Fasc+rhu?#9>LyIh>sY!?I3%=PQUozO9+fA#udOZ3o!{cKXE!Ust$Z;MKxmk=-1-W5D%8%mzYy z+#=X(h4_ep+YW9S>{|)(5d*g!+&b7t3-J*Hw;f~;*y$G^d>i3*fsYYx8`;gVI|kf7 z#Oxr%M;^HC;Eut*lMo*cE^Bwg_ymC_{amd9o#3_#|rTg1GgRAH`wtvQ+rguQy+MeN7`W}=)L=hGh>sY! z?clM&ew+{=F>u>K_JEy!@xhN5o*4Kv;R%u59J^z{lS0hNLVVJ9b^yK=@%dTdf_F3-ypm+@Ee7f1%6Xx zzghf>V82D^wYQ2l1$%R3zfJt|V831HwRecS)}7+lhM2pAu6wummBD_G(E0Bbzbe@8 z6FUF>;#UXz147q&Q2d%;e@N(@4~sj`BjR2=UEJ}Hio3TN;$Hih_;nIy__)w{TEv}a zrnvL4CuU7NGh^0pX27iB^vkRr!>r-#n_0uj!>r-#n^`-CS=-L6;q=a|;pAu5aQ4Bh z;q<_);p~H1!>Prr;p~Z7!|8`v!`UCRhSLwThEtbW!>Prr;pAu5aB?zhIC+>goVhY< zIC+@0?abPoS(`I!II%HzPHZvvTI}utTg+VxyL-kKbN7ked&CxV_mAB(!Dh}eac^+X z6Pr16b#HJV3vA{b6ZZ!9al~fM@pNx+p99#;ITzd;+~*HAbNceJhWmWOW=>x|PvAa3 zv6<7C&oQ{qZEWWBjj%Refivm`#QpA&h_Np;J$XSnS<^P?&}VlIq2TtzOJ#E zQ`6Tk+}Arc^C!gJ|1Tr^uOd!9e4N{t6nSN0OV?H=HOWN`HrB&gm-5O4FHP)~$#tE^ zS9Su^{FR+v5`Qv@>DTGE!lwd%S@?9|zl-d@7k@U`{~+|*SHyoE?0<~xuZlkt?0*t^ z?JRNEdQJR=5c9gwb^k2h8tiWfo&Qbo=Yst$q4WPm{P|#iTj*Nvi2o+o-xWINU&Wp0 zJ#nvnU)=E@h`YBB#l5yo{6z^f|48UOAB#KBC*sb-o|rZ9%#2yXnE|tg(=W4j46}x_ zZ)OcA53`1|Z)WWnW^FsOhSNK#J#~iPi*GQ)xE)eEU=k#OxzpX#}S)3$J4#R zeGXtV=Ui}aaGyWe%<0R=8t(HAn>l^?Jc0ZC#AZ%kKF8obx3QVim(NeQuNQ3Q^yPCK z?&}DfIoFeWgZtXSW)8YHxUV~G=Ae6n`?|(vPEB9Ga9{7(%s&%%|DQ(o&m&Gge4N`) z7I|f2OV_6NaB>lYjrDNWrMxo1rxJT*a$TqKm7Tyee`Tk?i~lW&>6hRafrDQL4iZQH zuOp8An_vg=5%Z6TWB)4HL43qe(;U0&ek=aZ5ChH*9Q-bDkT~-E5OM5320Mt4n14ka z`}e^P;vbDlj=WbUPE+Sb} zLJS;4hc6a*H(^22H6S`M-6h2Ikl{1ePEh)iPA;Gqi zgdBY(tfhD2iDCUZ5@H5PDkS|S#1YRrju|pCuC{ zvm}Q|-jL8Iz0t?gfmaCWX+;U^nIUs$X2dXW@{yOd*jP`$6_Pb1_|}#%CwimKDw4w_ ze~?U;ye=6nIaji^Up-G2Ku?-^`Wy4wrC@t0e4${a{DPLpPEQbHZ_k>euC zj*{af^nI3OE6K@{agw_w>|>gQzBiXphkWF{Lc%`S5A!8czTlyLyq3@?8oP%Q}#6KxP$Bv)1g=3G7R=%#A+jhrZ~W{M2E8FGx5)wv^BZ{m>WtrtZd) zD#>pooNvsIKIn(@Vl&Ag$+Hr!)!igqYrm4vSFMCNbnLrG=#zfQM_%$XbNZkk`r_D- zkG$mHRKh&iH~ZgR!ukUxteqfXR+A<4N1vl4^%C|yR>JW(Ov0WHk#M{Zl8lm|W1lEt zX5%E}Jy=2QE1zc-9>*;aq5t(CeWR;)o{>9s7}z8p#w1ebO&+=-7{vus@D7 zdFhwFIbS%gM@y(fJ>tnn-XkQ;m-#cd{UqceA93i|nH%fLLq5(Ij>rBI^3XqibnF~2 z@{)fg3CCbX$KQ;oNQv{6ryjIVYEwY%dutIa5L(^iwI}T&s|fn{)Uq$u^RcB-G`aSVn@4 zbMtHovGhaV%!oY16LXG)eXt+ut|p;R`k}{jCGTvCGRv7(5BcgPcS-1jdepsCQYUGajFsFeVV~@WeO)b~ z&+8=Y`wj_xP>;G7NtgrkU~acd*a!X6_Z1T6$UK?LZ4&yR9(6C1Fem0!CD~XqQ1X}r zKmF0?Ly|2dr%O0rhDxYIJ?cI#;XL4c;JlbFp&rMBd7LaE?g)S=$il12%6h-V%19xHiN zQY+znWS{JZeQ`eSFJZ>yrS=+<(Grg9Qk`*PrC2vWn!#dWpmx5$*33-TT9rdXD z7fBZhKH|`^(--}{E%8@pk#{8QoBcC~cO`u!i%7`xwuE);kA41G!n%cmydh*>FGCI*}{baqGKl?dD-7dl0~GSD_l%?p0KO%0%14d`NBm5qGRtayinLf z_zU6U0nxE9A-q`FOL&p6XFzo9y@i(wml9qgTvAA0>QaAMVUuuaA#udBj=bcjKI>SI zpE%~k66<-!$&R|uCEUL{;nc%^W;fausOgx3fwg;xt#3W$!q zpYS?if8n*ll>?$_dgO3)dFjCR{5ZI`(yicM69I z?+~sN5FLB9@NVJy!n=g)3CT-c>W>iKE8I{>9Pz9pFZrp@I@aSSj`-oidxRSVM8{4( z^0GhnStGnpxRLOF;V9vQ!i|Lw2uB7)$G)lXVc}-NhlHC1M902`aJsNo_=s@xfaut_ z63!5AEqqkCrI5VTrT!S<WnBQN`7pW6vr zggXjn3U?4bA>2v$OX2nb(XsC$d{Vfp@K?f}1EOQ!P588Mci~gQx`61|_Y^)O+)Mat z;U2;#gw&<}e!^DaK0@M%XB~OTPkq+09zSu!j}<;E+&dsTcJh&z{jtw|h0h7c3!fK` z6TTokK=>Qs{sGak*9%_~9wdBGcwj(u?1uXqX*Mw68qGLZ^_=fNV;h%-a1w_X_P574ZB;lLF6NTiZF7;0rzAZdO zNF4F3BQN=>&pOuQC+=3*l8lzW2F2AUgJIgkK7; z75-g#bwG6N*9*TA-XP?ApX&mmW4}rGjqqmS*TNfx+usue6x`6jcy5uj-7nuWq<7RHsQCzyM*5f?-YJ7yj#fkPIm-E$9}KyN8x?KAB6V= zM92Psu#@CL3Eval9}pe;!;;RDMSIpE%-Y zNEVb#4~UMPeB@<+?DH|t;K^;ySy{KOIef~1G!`GDxy z$wyxH$39<_EFt-wq?hDnNpH#TB}+EF*bMLSE`p|82?gk~by95zjjElArplV?BQ2h<{76oaBvw=-A0eUiQa6 z{~}pI@>j`B?Ba% zNmh}3A|WqzssE*sXJUIO4yMtSb3DAUbyPk(d3k&%aAnlYA{1 zDEUet+ut}jVj3x^B43pW&Y3y6-rr*MRD31N+}hmgF~rG6jb zDB+Sq;)rJ*dC5o3nM7VT7 zbnMFuHy5rT+)TJ!Ky>UY32TKF!Yza=21LicvT!S5KjD_bN+EfvOZ_V0XyGbC;)rJ* zdC5YOgsTg;6%G`R5e^b=CtNKcI`+ZB9fWHN zw->Gv5FPtk!kvUeg*ys|1VqQaj&K*@y271>YYWLsUFvTjtP`#$B#wC2k(d0`XC3SD z6Gwcta9825fauuCM_%^FKGzrSCae+eE*vi0Q#eAnhj7Dy=-5XI_ZDs>+)FqzAUgI< zgky!93ilCi91tD*=ED7iTL||RZYCrzb*VpEI8L~wkT~L5M_%$%pLMLqPaN@E3HKM) z21LhBKJv0Z_PMoiyl^|=0m5yC^};d21BKfJM902^@L=JN!h?j{2Smrdvv7iN7vUko zodTj`uMO?a%3 zyws)s$-+kAi9+ItXB~OTPkq+09zSu!PZORYJRu-DcJh&z{jtxJgl7s*6P_hJRd|l@ zbm7^;Qv#x6Zxo&o3nQaY#9}4dmJ}sm!b*T5A@Im1-Lh4e7dhZDD6wVZ0DZEYi zknkFo0*A$iD$pMA35*Mgro^yh@kiMg=O{@0~5#}|a;As>F`$UNT&e&Wzu zg&YUw&N}w`b+Q=hyJXPd3(P2InHkjnIFdi|8>F6dBFMbZtxR_ z{%av~X5OsB&w2D$A@k!n<8Kaj&NI%p_k*7}^rwW(nR&AgKj-NOLgvSLg#VUc=RD?o zZVP_m(0?Ul&di&2__+=~5;8x|Q~Wmt`^Q4AmrsJ9IP_l%nKScd9e%E}Ple3yw&1@Z z*gq3;J$@ei#G$tcnIrRN9e(EaHz9LkF3g8HV*f(O_5SzZCl38_;pIZEPuAgQZeI$S z6LVoc%n>{H5$-2n1wV1q@Y&)mKiGAHK3e3&D4?o-^a{u%tlp+73TOvwF( zb@-XvY$0=EF3gWPV&^`{{qVcsCl37~;iW?EU#!E=-2NrxeB}INe#{X&_gU_@KLkH< z=no685OSYl9e(c9KMI-OHNk&*u>U0Fe%?v_=K>xkUG@EzPR*X3YQ9g;>dHU{2Z?(rT5Km8v#SvMW%8rbA9<*Q z9siTU^M#)Z&k_DsxUTRi;WEPC3C|4nrG>;VC;Wr(bm2?FKEb|%@Q>0@34Y>;uNMAZ zI4$^zBY%Z(mhgBXb*T5WaAo1^!efLl3;POxE$lCRLwL0C72)#2XM_WUZwaReUlpz> zd{$T`d|P;!@K3^(gw)+g_-ElnA@%8lx|;~!6dod^K7CMkGvQx^^+M{?2X(g)z9Sqj zq&|I6cckz&;eJBu(?_dtP2q>aeT44{2MV7Pt|k0PxTo;1!qtV(3x^6n7Vai|PdG^U z8{yi*PlUS&-xsbSd_lOauuZt5@B`uCpbrtgD1Cb&>vjls5Pc^h@m`0HojSh}jt)BO z$umYsUpogoh`y_ke%u#2cJ}eSaO_#AgXp^p*{}DFj-5F@C)_INtS8Sl zLgu<-4aYM(>yqSBgkmFq$bmq=H z_YHOseSab6gU<_e?97)rZ!Y9~S}o`tSLQh`*g^CIgq&|a@6fR`U*^22kn?rbpmW|Z z&jW)UL_bK#`Rwx=9Xs=7&KnE49##oD=OyzzIM_k-2|})yLjs~>XTHpNl#uJKU(g2% zndhOw4x&#Iay?EAh>o54GUpLO=Et0v7jt4>lY<>ZKU~Q5epo|C$U2y2AQk2x_f z=ES^?2zC(tNFnzV-&fGFb3gdCaJZ29F(>B5oS4^9!49G~2)SSRzJ-pR`_0qB4TQ{( zIWaHh#Jr{kJBWU)ko%$Ui|E+7pFJfUCS-ohiFt88a$X)6>>&CHLhiS|@1kSpzV)nd zJt6n&WrMzwka?aM>>&C{Lhkp|0-|GQzRY>UU-OFYNQIlm*9)oRJdVeXpZesbF7@e;_2eNR z_5LZmQTVO!7U6fon}xH5HwF8x!XKpHCj3#@EF_M2?6(X5CA?Gkz3>hpal~W4OGq8^ zk@s#Pal})X`uK?>o_yq`E_ujDJnL9bf7GWBo3Zi={0Iem3bK7Z;N9xIFai4i{~;%7uW`D~73pj5yy{aO|kd_h9CH-|J(__Z;TjbIo6k`0L_bl!x>A?a2OK#M>hNX~e&X_*W61 zE$*DWFS>4Lz2TX6iFntDcaM0li1!h9&gCO}Ma26@yei^@B0ePI>xet&29bS4#5a!k z<`Lg2;@d`i2XW`Di|l(wd~C$WMZ7-Z6CyrY+&Pbm?8ij>govLU@zW!IR>aQ}cg~9= zdsD=(iuiRA=jWq7j{NM_{7!M_yg#z@b2-QGGeh&45q~n`&qVxrap!zFvcD4XSrLCD z;%`U%y@sO_&I2O*!4aPr zasJ)z-j0g+F%dsO+&TGoqvQFvocTEs=ifTE^Y0^b{taTzzb~AVe_PnjzYom$p5C1A z}=3`Tqxp&h*VC9*d}{J4lui}yw_{I!U`5%Ir7{N0GZAMv(`e-iP}BmVb@e--h6M*O>o{}Ay`3(q+&3r4(4#21Zt zw}|(Mc&~^r74g0iUoPS+M!YiO{Ug3=#0N%vjffA4_}UR47V-5XK0M+hBffFOH;Z^} z#J7(4wh`Yx;yXor*NE>P@x3BGHsbq7{D6ob6!8fWpA_-KBYtGW8zO#e#7~I$NfAFa z;%7wstcaf*@e3k;QN%Be_~jA5D&p5h{Dz3%9P#Fe-x2Yc1t@wX!WPQ>4f_=geyIO3m0{ELYHBjVpg{M(3s zAMu|e-dQ(r-=7zbcp>78MZ9~&mxy@ph%X)SWh1^q#494+FX96tzFNcwMSRVO4~_V` z5wDK;h7lhT@r@$BX~egP_*N0$CgNivzGK99iTG|2-!tO-M0~%9kB@kL#1D!1#E2gj z@hK5MI^xGf{P>7Zi})!KKRx1SM*N(JpC9oHBYsK5n<9Q?#IF(8+fefTzzq?pNaVM5$ET)v<-c)mr4an=Bu!Jy6EB;3CVsHHXE2+-&NJmCijEd9^Dgt(QM#cbnhn{a!B2*nwJ;oJmRpI zzvUxuoi6O!Wn_BAWh3Y7@dRFc;f0!5|Nj%`-?e;J$LAZrgXX%dJ^S6{{PSKno-}d1 zc}LLooZt1DLp}Gg`J_ox2J=Cq4t}|w^SfT-8R`Dq$HtS!P3>T*WV!1(zv~swN$a_f z+Jhz^QOF^^ab+}sk`9jW5=XbrT zbJKack8Q{Ae@Ge1C7)+Zu7{A{^%{fjK5EDBJFcAdoZt1@LOt(u$NEV}PT;%S4qg*J ze$MZDwdbYtbRRoUo;?2WjSfGwT=ksa^$O>w_1wqkNk^Bpp7Xn2bKBy{`>6XEv+smM zhjw&xIsrUS=XbrrM`=CxvCTo_#!VQ%R);IPp7Xn2eW>R?(s~`=q3b!n>$Q4+0e7lD z^3eS{yt_H`bbi;X`Z%4Z`$+0_e0#;?=lrhM80xtX&X$Qe@ z?t^+|7-#YPbbi;X`Yc=zp>Fa0VTX>rEBShNe%EUZ_1s7C{h>_toZt1@LOu6Ue19lY zJ?D45+RxK@x{vhzp@YSu_Xp>9z51`ydhWyT4;`)OeW2`CYFy)N>!{ z`$PHaIlt>wEtbyHeT4Ui4j1%1o!|8uLp}GAzCU!hTydVx?|N;ap8H7OAIewH`CYHJ zTRKnok-k5aub%U}UUR7DK9c+O=-p!5?j!m6 zO9%JHcOtIm{I2KkQrt(hzYbNUUQZ?W?^me9&$AkDotgfA<(3qQdmDTBeagAw{QhBH zfBE-4u0u`womXG}d9w4fZ$4P#J_cwwqk?mz*G9K&{^*-6%^af-S@gk)KDC{G#LH8Ye8i{zZxdbdE$EYi zKhl%+#Or6+-Bp9X*AcZ@y~guN{jG_v z_?8f#)syw~zt(XdwZ!??O!PVVgI>&^>T~kPe(J^RPrPbo&7~{$Xjymcib*}|!^W;y z%%|gDlR5s<+a3R!%<(5aSs#vfP3HKcC-vB$j(<&jd~`ge&pAHm@$pIhbB+&se0+jl z%pZQYk?5RXnzw$}F(*HIoImx?$&ViA4|==&@$tc*IsWK1(eal)=lF#9#2?Ow;`(s> zYvTMme$v|=fAnJh)ITSGh%e@k*N5X>lR5r1KK{k~LiqjDP;FwAnrhsx;thT;?L9SQ z^y;nK`|C4$-;7?B(Q7k$Rc-rx)fv4mqtDLhg)Q5!ug&Q78NDH+H)iyvjNY8lXJz!Z zj6OS~_uZ;}f7KbiF{3wS^yZ8{E2Fn%^w}A`E;H}xV^?W+d~5e_uU8$=UaziiuQxuw zdb{-vziF>my*jA9f5w~1=N^1M&H5_+9EA10-?Z0fXY_`Dw)fZ1Zm-YE==I;W_cvtp zwv68JUHkafAKL4MAKUA-%Y4~>KN-F6a_#-q8ND{6*Jt#Gj9$M|`+SWVy*Z;#&*;^C zmQFrb;(51a^u9~C_j6z1_td@*tfhslO@DViZKVCaFO>58K2gf=`$j3h?<1xBzOR(> z`#w|3@B2D-d9Tb{XSF5@AsWje!mZu^80QK3B@`_q|enzYmu3`+c#L-|v&9{IkOQW+}hlM@#wrzFNxf_t{c@ zzwegv`+c~S-|x$%{C=M<<@ft`DZk&xOZol2Udr$H`BHwr@Bb%1e}l)LzvSm6y#Mq0 zv3dFB&%?R~-)BtM^JuoOswVhcEMN4aze1N_`ZJt<$HwPfrCp24ziwjYNqj($=Y;Zl}XJse`OMz_FtLgNc*o$_L%0cO#VO6 z%Y>ZZvVnuk1rGAJv#1NM5IER3aQgRJ@_3K*?0D9CU-a($$6kka~Esmi%cAuXW z;*}C&u$hA^V-9Q0!G6Kcd14Os4|dKkb8wYl=R7nAD}sH1aMi%Us=&e30tW{M4z3`SGdURcKcwa-s*OrhITqkgF-N34zixL)J)?mlN{;3DwCRN{>mga?Y}a~ zk@jDi>^03_nf!n6*AF?t4FU&wCV*Pt@W8?J!>auftPXbi_geCJkM!(#)_Py`?)>EN zzAD7iuYSq@H+e*&^h^Gq&-yU2zWOESzB$L)oLJ5eVz{4A7HbTj^FCL60ZDo-ij?>& z{@uVeb{T*8l<^HE!b+M8;kgP2(8shzbpH$X)zrina&lcETu0p`i%Gf$Sv=y{xeqKM z=_BbW;XdLo_S9XX63M-^i?D~Ja}cg;)+{P11R)o75x$6L9kmepVI4w`?uQ%*`KXIf zhkWFw2ZVkQ?tSFu+FeRgA;HGA?Ow>oF(D2g>$v~X4?;ffkMzL3klwg&x>sV*v2#zQ z7v|+&nJM$4XL_Ix_1r6cP>;GxN~l9UIQLk38X{rM>JoCTC7~Yu5I;adJojR3t4ipD zeuzWI&OHiSm4rU%hrU*maBM3j*q9f2$w^<#i$3TF8}(Sn8tRabyj%;|$j1!0N6{~R z)Bhj|dtF1q9tTU9-I@~Cv7S6bCD`#3x3+}(6%zVeM?xO*k(c?fKlV?5%!j(vr;l|d z(wqb_|?hkDFpBMGzESi-r>xrvODkemAeaib*IIe!uE3+$~% zLL55w&OwF?DzyRbcozwA#8*hRk#KHoD&d?PEvb~SFZ$U| z!nv}!gmYwD3G-ks)S({vwv>>Ed=-*SB*YQVx-BI1L%;ORo~c7U>TV{X4)v(Jp@eg3 ze+kEOZwcqXeiF`s9VEo>CLs&8l`Lp|zp zzHrVRD&bg9kZ^7uBH`E^EMY(Fi~ZG0$a9#4IO171Swh~i66&*m*0G*EhfC<6Sw*sm zq(U-9!q>t$<8BI5lc&iU@ToSWvHU*;TpbB>!i$H;tC#5aw2 zZNx{5cagBK9rWt8yF{F8(ss@(bFLM0&U16lMRU$ibIv7m&KL7zB7Q=|PmcKM5kD*9 z=SBR&h+i7$49{G111aOzkMrP}>v}(PebTncvmk}X!!y@~@XU2{ z`n#`D+6U(y*ZCKc!oABr>c9QXvz6S3!2k6xZ6o>qr?apECG*wz+Fz32ZN~c~B-Z~I zIeXw3BHW|1Uyc7rkN3~9H){VJb*MqDx$V2~);h^~{&V*8bJU9W&HHoqtFV9i%ys?o zEy-sDtCmfly^SfTN>xDkb{mixVyIx_LaDIlm#b@gHhfoK;_%~#o z);quJ)rWfSqxei+nd&*e>ote#&V3Z0sVh@G=XbrfigcdtBYmc>gT*vQ&(rx`ulD`) z88`RgXX-jy(e<3)_1fMG&+vtP`kA_N)pLH=YYg?=N4cN5c7E5Z^8Nyr{h8~K*Y#Te zn$FXGq|elKbeeO{7w31q`cTjNEcY|l&hL7KP|tmo`YZ@Chq~o{=GysPukG&iZ&tXE;`;+X9P7ZB?=Q~pdbM|^_1s7C z{h>_toZt1@?g)RABkZ&I{!pfR&hL7yw}*L#eWvdZ9W18f;CVW~>(z&P?!)g79j)kk z&hL7KP|tn%{h?g-oZt1DZ%gOtKFa>gb;$2}wLy0u<$mVc`CYHAIo+T4nZ7@CbeeO{ z7w31q#!%0Fl>3=$=Xbrfo5S@N>X!SNYv*^p!ZGR3JKRV5{!qSoI=|~RdVc}S{mixV zyI%9ubbs!n+|OJ)zv~s2Pru)FAH`>qJNTCC`-}6tUh{HkJ@-+3Cb>-YoZt0oLp}FV z++PPz8Sl^eU9T*N{gI6v{y+vSg*iS;woi9h>HHT|$YdgeCC z?`LKeZ@rj5^UQU-XQrcPsB64mJyXjw+KEoSR8ODDjr1hHp1EzwJae7se&&{bGtXRe zyyIuC6FqbM<7cip-tjZn){FbgJaf(Qj-RzuobVpSkAvqZjjMp1J1u#LrwO zdYnJ?&&iLTIsWMF@@I~J@tJ9k59@t=!||6s=lH~aj!)2w`E~r`XRbN^@iW)fYog;% ze4>ZrO?*~Q@`vLeKXc9T|MzFE!|$K`%ysRu?VrJ%{>riKp4n`8xxHSWiEn(Xy}vON z-;~j-e%C&}HM4$JMxUM03x8?9zHdga&FFO*y*{HiWb~$tK0TwiW%St@y)bHlcF*Wm zW%TNdUYF79GkQZtZ_Mbe8GTkpZ_DVjGkRg8_WkzF=+zm$Hlx>N^!kk6kkOkmdUHl^ z&FF0zeRf7KY@F#oqgQ40>Wp5S(d#mLeMWD{=#3e@DWf-M^ywMBHKWhU=xrH&c1AC3 zlG%Sougd7v8J+JJ_&v4n1AZo}4>!0@bDp{OeW8@!_lZ(|-#1G6eIF_55BHH$e&1(G z`F-Ch<@bH4l;8KIQhwj3O8I@?D&_AR?jxoAzOR+?*X6ADeXrE|hMe_HIsWN6e&07s z)t{ZSz7XzPrPf#F_^WgLbvgd}9DhTOzcI((n&Y39<8RCH&(84|!hN;WeEa73t8@Id zIsUpFe|?U>A;;g8<8RLKx90fUa{RM%{Dp8|FFF4le^rjZI>%p|KP$)ImgAqDSg1w@dl`K3~f3_x=CmpR9L!fBw>+eP1wn z|DSg+{yeNJ@O?<~%ysyj?EGi0)907dpB>GA=6c!WMCq42W42u2AkQ)IL=w0{;2_UB zVCVSq>$1Gd*+c>A%%!K%PPo_oR$4h$UR zc`585>*3_Iof!APUYwuU3JLM_?EKX79tVl9At44F95~2xPT0XAfrC7!gdJo(oL+1v z#{E;%dC2Mg(xdB=!}}U4&T~iP1bKc14)UB492^!n$n#0qLDs{mna0n5=9=fOn8^se za(=+MpHCL^=e+&Cl3t5R(0_a>K2!DIK64$Ot8jAqn6`-e2=@4y>%|q+HOOMZZW695 zt~u=7FM3M2H!LCHen1>PzIsTy1W}3P`P>DBd~uz#rVwONA-TBD@ll6*33UQz);s;2`hmF3fB=p9;2^;aO!$x1sgWf779OKm_^ofl;5DkxS;u_HLq77-CwZGfKib?hou?xTJFsVu)v5jif>{MdGgoL^hC6d;Ngu*sCS%=|~AVrb;+Q z>q*E@9qKhmsC~2qJAUGZNmx%F@*(uOk%awhCE?iAN|@!=0nxGVB4M^WNjN6EN-8Ao zbF?_;+NKiDp=~7e!#>&Xwi3>r%_W>O+exUqiKIe8JnOcUP>+6C$6n~u{cIsl9`aR6 zHk43@eB|9sLLcne%KHBc9(FDjFE6I z?jfO1`Xvq>`_2;P#JtE$zs!?)F}EEh)S(`E>6bpJN8Rlu)S({d;$9N^ohad4I7q_G zCrPMBKg5rd5WlB{bAG&pKIn%ybnN>|I42H}&4SRIrBCWmkMj)9c{f$U zb$FD7bF@LSiezKS!a)Sd^<5#{MB?uc{QTRwvMnH4Ml!dr;`ax03!00s5T3tu2z?~a zTnGF7XRbRZ*Xu&bfI27Fo;l~6IoE_a=es%QpgHHEIp>Ty=ZX0-5kDc~CrAABh@Tbl z^CEs>#4nBb6%oHC;x|P6mWbaT@w+2_f5ab-_>73pjQEogevM|NjfOUi4t{ z|GBPDpSdh_TRYLYM_w%9eCB#D{5}2b9^2#DO725H$FrjIx(55{9Hasz^HumX-FMsv zA+i3y$k_wO5b1`J`6|5m@B5xt`=s=|%AL(it-0;H@uQz_-}Lr#?LFRi0VVTQ7v7u1>3{FuQhU!@e&*WwU9b7J zbbs!n?9W_>{H|BHJ+0?HiqF)I_7(ZxA^(P~(^BVmz4}nk`z$_FSEhQ-?|M~tgn5R# z#b@fuRL}Wcukq%zp8H6jsq0`d&C&C8e%EWgDXr%|{7hX(E4rTZyIy^$=RW*QUAgKx zzw0&M7|zd7x7^QMJHP8S-VlyoTyJPc|F4bF@eBD~uPW&7BYmc>qtl#I&-q=i{`z!( z-e=jLxeobVuMl+iQTAu9GxeIo`=9rjK2uk|c{;!M*BIVsz0Yz#bM5@D*LqEQ{M<*m zpSgB^*Q*cp+(+^KVTb?jeZi}Ie{p`-D};LPqxk+%rh3lrdadV#>pj#hzCVUp2# ze&*WwU9a`@bbs!n+|OJ)zw6b9dhR2Af9U8m=e$2Szv~r3J@--WXRe*!_1aEN=jlGm z{>*jA?|RLrrS;rL`u z&wUi1N$%iVuJ12ZA;0UjewfbFeH5QbE>k_{cfG2%P%rdR++PPznd1IJe%EWgE6g+O zv$(%9)pLH=D_oQQ+|T=ro=NV|M7=-fcRl}{(S4NrnQP~FJ%7L9KBE0~Xrk<|r;_{k zE7alV%q5?>4!=*~ce?!kVP1dv_dTvdP5PZzU;cTr^E038^uu5G!86zVj;a6Z14k}9 zy+_N{-+e!vd0Btsn)I3KslUBpB+u-=HQgZnzr8ehM)c~b5030{>}xG=FL>hy^z*|CH;&xljb&!`=$z^|@86WfFWP60 z|C48Ks%^1K@pnyrB&qkeuhRAI z_r#j5M$q3TeQzH5(9XYaS?H(Tlm1s3eb2}xw!XUM>mO5nqhszIS=qby%*G{`eVq7y zcR!ruyRKKN6Mxnv?{Db%PS0PH_^%1;`^>&_p82i!fFJ8Cd_d`4r z&2>Bd`}Y6e-KwwK>wVYyyxlXtt=agDzc!=SW%T-t-jLB7GkQ}-Z_en`GkR-ApOw+u zGWzU{UKrYbzkM@$RYtGQ=(QQWE~D3H^oESyn9-XudUHmfp3z$~`mBuJmeFTt^upSi z{b%&5j9#75YcqOXMz7E44H>;Lqc>&r=8Qf)qqk=CSsA@8qtDLhg>^Ff&*)Vdy*i`U zX7sv@UZ2q$GJ0c1=ep(hyuMETOw*#Wb&8%@_H|pz-#47+rTo6GOZlsE*4O6v>vH_{ zIsS$me`AinDaYTO}08*==O zIsT>`e{+t1dXB#}$3H8_--|1hYJGjqdcRMW zTHiRA^?b(Lpa1h`ulfAay!WpeQObLUAvYn%Q*krxSe@|hEBPONkG7EeD{mOdYvexK1Vc{V2f z3|aa-sqK9gzqo|De4gEFmlal6Wr(F%^1s-AeK9 z66`DM)%lr956rUn*Q@jMx%~8JBk8lEuC=P{OGt>X(yMc>Chj}~#l3cQamNo5cW-Nm zd+lKHB_-rsQ?Jf5MBI7S5_cZ<#H@*DHcJK04Eh94ze@*B46`PleW%Y3r2oHcW{sVF zGi%2%YulMMoZgu=oczoh&OR!j;`G3*;p~H1!|6Fa1Jh@;nKc&rVb*Z=Hz08OVb<8G z%dFwlV%Bi-Gix|=V%Bi-Fl#t-W!7-=Fl*bHwK=mkXV!3HWA2>TV(zur-2=9myB2o$ zj4kHw6TA0_E#~eYyJv#UoMYnN;GQQobLQ&a;64`E%sD3R4esNJ&79-u-ryAy=7y~T zD$cp!-rzoeu$j}Bk2T!q8#Z(L@_7RH`H9V(zI=|seQskjuMnpXA}jRj>jj%Refivm z`#QpA&h_Np;J$XSnS<^P?&}VlIq2TtzOJ#EQ`6Tk+}Arcb7I{;+;(i{L&evYuy^=6 zfrIM?j^BLYh+|(M!~m=(4{H|+e3)>(h*t*=t{*slbJjWro9j|5&f{9V4^EcPpU*y5 zoSxG8iIn&%J`45VJ}Vubhj4oO7`3S0mx7&VrFlMrIV>jWB3VSzS+bynFMMz*ruee+k^ZbL|}{86cqsb*Td%C#jThUQw4i6_R}={Un@!)TJi%_Li(7 z*-%1V>QHYF$*PhX33bU$y*df^2hL&kR3WL7>?~PBLM~=N535ObkZ_+TS5FBw2THb+ z@E*ZhW=wA!v(XaXUs%g*=w*;(O9?aHG+?cexGf|c3wpy(9PwL8@Dqo=v4lG0#cu!B z;`Fh(ggoTKPapKNjRZe&=o?Ac2Yq9=e_L_(xtWAKmoR7m!d;2$fg zkPMSB=aCZZ_*uKZg!xrS@Gm35K2E|lGhTw9IP`TS%#nFx$IsjjkT4JC!hDz`_5&sC zv0j3oIP|q8-6S;Um=+!VIItd`7p-{$z(|%$zc-w#G!M}EGpstg&jY0 zJ6ytSIRBU*bHsjx1ltq|e&WzM*B6&?pTds6ujEJxvsp@lpBZ64O2S@`mf$B2eJu%d zX5QHGV{ec!zupr3#9^N*VXwzX@Dqo=ri3{&Z|wN7A1h&g>?cat>of^|;?P%@6eNQs*r!Rzf0Bgbz_FqZ^{}5TVcjVb z{KTPCi{r&{!;XIeNf*h&l19n8k~1XC@XVl}E?h@)fuve;o@9N=FC+^E5p+6B!pu&S z5P!DBU(_KVc~6xPM?7^A@(|BD>d^=NtS32NLOtqI|6Iv1$vG18kdM3+C$r-_Gtiv##hi2AoO8~+xA=k*)^h%tbFP|m9hp}|ykEozM0~Y~bDnuE*NQo_ zHs`pQGjsC|BR(SH8%2E6h;I?`ts=fn#K%N@$B6F|@!cZ6XTBYsH4 zCr13Rh);?5(Gfo;;@mS_Yg)uli8$AbW4Ol5xem-Z=gm3q%`b^~Q^c=~_%#u~KH@h; z{MLxy9`U;(es9Ddi1@=1e>CEcNBox&e=_2~j(BUte-rVSBL2IGzY_63Mf~-MzZvnj zBmUQje-QDHBK~Q_{}%BtBmQ;7XGi>B5&tpb3+TG{v0F&o*K#4^-6GyI;=Lo@H{#1j zydvWLBVPP_1Wjf5djxA~L*@M1G@U{@Q#OXbN6@&%amn+?g@?l5BRDktJ%T>z-y`t* z3-^%jnpfi|!;=4JJiYMTu}nYPGvw5pS6}R>bC3#@%va&_bl-7(LSp@Yk+TPmA;SA; z_N(xZf8Y1K+9$Q=RqkwFYRzrmjbHqH`=+;_Ywz*C`5r3!Rk(ZShxmI0Ro%NM*LnT3 zH3_bg4V;Va{lp;`FL+tYpKkx4X60Z1xn{Wy?ij&6e*WJhh$mE<|6llf1U}#VdGW%H z>3Qd=l>6@yIKS)F-HMzOIw`H^KK#6CM=QFX^SfSssOLWXylJ`WIlt=_LOu6U?!QOi{I1tL zF`cLT2+x~#bbH0)=lrf$8|t}_^m)?`mn+tDe%EU|G@PHIZn^&+f%CgwW2omo%Ki5U zoZt1TLOu79K5tsSc{;!AwN6Oq=|0N+_XwQd_3A@C_fhV@N8tRfR|xgoNAdk(hyU(< z!K-|KaemipJ|tX!p>Fa0p-lCh-}P!kJ@-+3e<)Ku=XbrfgTwU~>Zb1x9V`~TKRCbZ zHHLcb!|x9rt?1+D{H|9O>bVcUKa{JU^SfT-f#LcKb<6(u2tt0>Yds*X=RV5*_Xt9M z*DD0w`%K>-Iy%ic-%mKd>ot#0_vd|<`|lArzv~soruE!Mx&I!4^SfU2Z`1Dw-ADTV zP`-IOzw6b8dhVm#e~-ZVU9a`Uu)k2Z+<%Y2`CYF*)N>!j=k+`Imh1bA^SfRl)N>!j z=k?1}&-q=i`Gs_z?xVQB4xTdJpYyw3Rcl($eH8asrh3lrdadE-e(ocBUcW;V_5PgS z_55>2_fhV@N8tRf=kGV%N3_2VO_crdJ81uYg*yD4xzyhy@b6Rjy*s~unAczaeUIx< zlYZybmw%q@{LE*Fe)#J?_pElddQZ3fP@n2&9Ke+t)w!%)@IQ4Egr? zWL(PyW3R38`lpsWux5!pE-KcmJE*?q<>mD3c*uX$7BgzLy7`-8y=!OpY&rD%*ISyF zZc6G8UZop9YcJ~h*?Y?)V^`k5{@Hsx)^hTBrx*Q-Z{cV2_HXdkqb;+qY%J=EuQ})kSA31)TfCm1^{d_t>*~Kn{kQCO&Jjs}>c2((54Wy)Z7_Acr)$Oqzvfw^ zc^3VePmShNT(9}GXg-g(_1ZScr+GfAdA9sB=$dzn=3QgIj#rJ2S5epTtkLl-t{>8U zaLuvL9QSyq!#+u#&p&Xf*M`?`*1g8M>kXcI+6c~1*Bcge*R$XCth=7|Q>$Nnyvvb2 ziuu=nXK>4eWsWS?-|G8wAHDX+;VrKJ#t-Wc{c*3mAL#V2my`YaI`Z|!-v{72YPjgo zcK?s`x{O|*(VPCYOuP8zte%a}=&c#OEu+uQ=!J{h_t!V0S7r3-j9#13>oR(MMsLXI zjTyZuqc>;t=^1@iMsLgLvom_(lFWWHdR0cR&giuny)L8IXY_`Q-k8ywGWzt4-kQ;8 zW%Ra;Ubr-~zl>g$(W^6hZAP!l=#3e@DWf-M^wx~tmeC8BweOefh~ILvcP0JdIx6LF%JKU;Ew$d)Z7IL6<5K=YIPXjO`{wwoa{Sdf z{@NUWU5>v#$KR0SZ_M#G<@lR({L^#%vvT}xIsVx>{zAC!OCH}Ge^rjZI>%p|q)&uOP$|NacS^Yi)bg_EFu$uk1Z$>-FyEq$f`|7GW2PL@T2oo5_ei_d$fpL#PA?!-~T6w0T_fn$TUdvSSWw z!2F)?t^3qmYG>$uoyPb7`~S8-^K_ka&$;J0_uN}uJ>6B+Y^W)`s))=zZHf@s0OY0L^`v`54$xCY+S^Excl6fssZphkqXp_uVxgl%6qfIhf<%X>N zmo~|8!ogf1N3WI9{SzePtNfAGPMhRjCbQOw)6Jf@_Hs|#cJ=aBw#5%0e0zAgyKS{! z;G=f7*zjjdT*X&Rw)CNW8++DRs0q5-RTJ_L+m%1ZnyF^fIkfXkUda%xSrZz0#Mc!4 z-fPDFIqM4V@~{_~lyB#nan51q(b7P^M;lrjc#Y!07JZU|HH8mdcs*l|CKy;2UdTjWZYU!UNwJ1N#T_1}{AD zjWV!rF~9J_1K(~2_Dj|byl}#|i-G-vHN%*Y*~iNayjIbd8oS-{;PsW5)PS6MP3v#q^>2Fv@rD@S>ti5C#>zTpEcoG1zfuG7`gy&# zZQ&z6?bz|hj=qC|u^n&dWhgPwPG00U&_Ld41NwG`RSk@T_JMx8qixBPe8{W0f&Hql z0lz_pBEw($J087|X5(?D(Nm z6Xt>WpdC9k9ARj0U_D@GP1DYLWDFw>*zrT}ZlM0m1MS$U`R;}y1M3Yt`x5P=48$I7 zC^F!OzLkNxQ~x3ZcE+-YfjY9Dv9E2Qo&AZnJq_6LL+@gs-qf9T?BuYQf%;Js?7TM8 zKGwh(%MIA^LuZbtBlV^oJ9XRJKt1rGKGc!+eGKaw_BCL~51lz?Zdog|W2bKW8K?(! zp+3}+_HhQ<_BUY1552RYxq;US+Obo&0}RxIx=?hFHUJlxR6 zFx#+=;dBH2;io@-_`?V9Oar{cXWZ0-x}0I4|Ct8rNIkKm(|(!(KjsZycxa~|{H!nf z;YU09=>3qcrJ84L)RFyXd5`%0(DIy?V@vsK`Q$<#sg|||eLL4!@8ylumDyn{d6huc z`p2hL&`u5HzfK^tFRGo_6Un^xNZu@v+Xr%|K;ANtnJ@KaE+w;WY*_Ya?v5jB-`-jz z_cd8Q>7aebKpqnKlm+d(1oCcy&*-3i&p<8@e0cp;4hIDCL4iCmkShZDut1&?$VUY- z`>o=$?@4C=k<30Id1fHb4rJE1e9jK!xq*CMAYT~BmjrTUAYU2C*97wQfqYXS-x|ob z1@fJNe0Lz<8_0hNjI*`8$TLE8rUFD*Yz=i%Z>aE~nl(GWt#SM?Q>x zqrLxIt_~wCL)O$(!_iid)E$HWS6&01`BOCR@LF0XFVz1h%8Y?^4mh?YADNr}ecUT* zoN%wG*vYtXEpObZTmQImliMFP_Hf)Ol*A)7E4l|l?Yq%$PF1yk(!CyRk?>ods_{6V zsZT9<`;GE+@ok%wKHU0(^t^57q;H!1$bYYaUS0tcw*L*+Kx@6}9Mw$6Xx*t4g}w$_ z@ngR0qjQXvN1?BQ&iMG^OMP78u8CA0g}w$_@x@o?`ISeZuYp#4@m1*>Z$HjLUjwc9 z;;Zo2%qx#VUjwc9;;Z$1%A?TNKr6oZif{7g+&nGOARkLVh=!tp7-_=-KB#_6wdYciqMr{asR_S>kQ$|JhQ zt;uxp{-^ljtNJG5QyzuB23qmOSNCqjr#uRM4YcBmFXQ=?M|^zXgTW>|G=GXOzS?)9 z@hOk^_)s7|#TQ?N=Tjc>@u5I`iZ8yD=Tjch@u7)iHV>+&;)}29uJ_#kb;?7>ho&-$ zPw~Z9=J}L|jt_<6Q+)Ad=11dG9tFP!+Q%1PvDcMHp|62feDT%Z8I4cljE)aYt!Djt zQGD@Lcs}J39UltEr}*Mac|PUgj}J{I)cREXm{0eJ^1RXUp~-ah`=5_5zN$N-`BNT+ zz6M(H#aHa_5tZ_J6#5!y#TQ?uDEj|hdBoQUH}R0G`BQxH6?;DA5nm%*AU?$xU+w16 z_>@OHz9udijZg8#SK;}TM?Ag)@hQIeQl3wF1lI^RsiGR6;)_q8Gb)clUjwc9;?r9c zlBlzrEtFF&({Xo9Y=Wb&BFSQLKKRQc9(cGWlm5v2 zAAZ-w(%EJ8{l^|rUAKIP(VbH~;@RNr=atnYQjqS8KFOo{b3mKa#ry8swfkQC{Nu4ccXYq>z1?TU`UmHITz%NY z%}V9}&JQ1ycG-2qSg)*{nVryQPI~D6)$W?xFP=W4??09-iSC#=ABr7^t|Uw$F8?u>8jmZx@%-RjP8~GsOOaGU5mz6v;LkO(mQ+B zh)J>Db)^f^>l`&VE&Jr!v-t`HrQNN$xE=wQq%6u2U&&KbR z{&3B=r62tD45wdP-KM%%(Iv6oqIa__{x1afuP(bWEr0qCn0`*I&%UHjDgAeOd=(dO z=v~iN6F=1PmySR2r2ja1wKROk`2Naw&xc35@jue>szsd!|5Exw_C^=~!86V(J>i#) z($e3a*Q2!hl#a$n*Zej7|58t-u zogMpQ?{B*7@XeihU0QtAFSSWev3$<%KHTx6pJRN|8Bh0fMwLqM_4os&`&hoxAGeSX`%+m=_y`Ul^1$#&RyW~@K|T1DR>l^>)x`u0JW&pKP4 zoW6Q+Cf0Y~YNPbmn8JEgkGM|D-!zjgq}(0l?lBnp)W}2wF!N3La$5cOA~r(LSp<0y*QzlB=oX` zUY^h^68iLno=NDH3B4+zFG%RM34L)wuS@7l6ME|4#P}0>aY8Ri=w%7LJfT-4^yvvb zlh7*@dR0PSkkD%r`r?FMm(Z6c^wh+}_!D|@LN7_^WeL4Jp;sjJ!`U|5Q~q_UvGKRdV=vBQFUeys%VRIkW3R|#pPt8_$z!j~ zW3S3%Uy#RMo5#L5kG(FBeQ6$h%D-+mI{$g>#d+){dF*9*?B#jv6?yE_^VlwQjRcD?Uu%&zxAjoSVD zlE&wQ#XcD=7^%&zxYjoJ0St1-LYhc#x``?7}YoV%^_m-(EL|23m? z;N6?F74oF>$rYUUfp^#&MYVGe4$dp*+M4Kd&*sm2K6xo5`E{_U=J@50klu ziP;1&DLIG&*Iv_ zwM6ZGZBvA3KK<-jar&FAzNOA;Pv(=hXXQ4)Peeq>Z;t3H5ar=W=@nFvgVOC$;_v6L)IFgO)_gic_M55&?cF@G}p*l zZ?s7!FRc?~txwt{lb6;Qveq_jlF3W!6IuHUZIa1LYa3bn5p9y$pOhQ2_8r}>LSG+X;F_B_PP zyV`a`FYjbq`s1^KJ<}h#tv%Bpd3}4PKk_hprav;i>Wf|eY!wH){MjlF?_s8zu4{wl znY@x=Bg66@@wLpq_uel4T0C)($N6hm9=@G>yKrq@OLJJ+(A+z&X4};bs~WKJKyKl& zifvmN)Q5g417iZntqsKCL7QZJ>9?jqeTYj;xBy(1rfb87PCmq7j_JFWfgG4S+TkM} z^M|~S0YChSO+4b@PrtPdw6PB8hc9`tw%}UVKpf)HP9NgnPro(>{P4#QfAq}_nHiVW{DD1MT-4;2|Dy zw=&>|KXLCf5C?zybuqvLANev~#Nr{bmDW?`^;zJAOA9sM+=g+S#ICYhWxp7-(mUewBea8eVF! z2G$Py&Oifx_)j%p#}B>201t6#m;ES{$>VSXafpYVJjmx519tq-4>5EwkT>nJA8Rt> zJj_5G;$df;jCYy=JAUYs4AhCb&@TJ&CR4{l4a6ZHcIrqyPcUG|4}GGc$Uxm`m;FSO z2O6jgafpYVd1Ag!HVic2hkmeudaJ(Jndegs)Q@?Rv*#_#zI>J8KK>O(i*7F$#?D(PY zZ)k6z-n3(9KRDAs{aB~icQnv`mVx~xW5A9d`hJEY!$bq^*xAp{Hc-Ev3`GWNO#2)I z`{OwV?D(PYW1x-)8)(N)-R2so2X*OaILJWzJj2$8a}C(>L*Lu5nPGx~cI?#cJOlNh zF4Skdf%fwaJq#BZu;YhbZrIdtpn-Pm)a^n8^`I{84b+kLiwxZi7aOqShrX9#6T<-p z+Obo&OAOS5x)d3xBkh+OiVc?;u;Yh5+OV-Hh`YLt&n^tz^J|d5`%2h2=Rd z$CmQ<@Q@363|!gX+xT|weKge@Te@xY_&q!Cl2X>D_Ev$sb|5oH@}WMGnRCfSf!ryO znLGJ(3*?@G%=(c}Ng($RZ?;glw0=Ybp_Y36lfqbyZRziC;cgMmO zUOwEml8+4JqXYT4K%O4RCk671K%N!Irw8&`fjlRWSy#&8{6M}akS`77%LDnUK<2ea zad`cZ%xi$;szAOykXiro`Eww%KdPO5NAg2~Tph@{Kz=lkp9tiq0{NLhem;=@7Ravz z^5Q`Ldmz6R$nOU72Z8+0K>j$8KMUk91NmQp{9Pdb5Xe6V@^69M-1bq`>OZ-E5%+eL zf15yV8^{|3@qdCJZ;72^9IS?0(t8|=6px_^bX{bK<*dF=|J8wkcR~F zut459kVgda?t#2VAnz5(dz-9%d4H4j`fyMnPYUG8fjlLU<9kN+EWkaZI1m=_(R>Bm zGpbJajJo(2cdd77(M8ccqt1!$8I$@rx`!0+X)ZR5GjJcR)cw`&T0@4%y9w!vN zIG}A^I|q&@7p}g_fM~Dt4*%UWSb_fy_l(kd(=~m?&qnJ{1u67BqZD6!wO5UF>s@&i z`kqmWFTM)De<_bb-!n?_#aDHGG(P1~=zB&fzW8cyi1?I8q3;=`_~NUmj`);E!S5O6 zwGU^N8;WQXoFX7hlC{5ufsi?+H>MKE)Sb?bp$9RCz@A1Zg6f)iJK8k1xIouPYDT z6Qrq(;!}L_r97X;se6JHicj&ySJ%Vu51zNs_l#0}@s({I@hOi&-!n?_#g}>~S})2Y zx+h3et69HZ6kmK5o=y$SL^wdN1^W-rTF42 z{-@vnJa3`z8KwB*tMh!yBRW15uAYi7zOuS#e9EKH_l#0}@l|;~no>6{0;?uhnxMq~LyX7dpPHeXJTN7$RyL8#{|7OP3>99Y#?D&Uv>9UWRoT+KnZ00@o`E(6@&n&8` zS^J6WvdtdLxP0!uU{cKoZJO7lyk6WU<^1n{bQ${q*?s@C`ag5d)vkZb$1-j_3%2f{ z_@R!!)4l%y`L})M8Sy!LknaHUo&L-5H4FY(nQgaDbC>^PpI6nq^IQLHTd!|7`>L8$ z#rN6Nlh>DTr9 zp_KVR$3L{A%O3By{`(=$31GiXwf)+CZcYpysZA1kyJhuu34QU#%i5PF^xA~pE)l;t zp_e4|vV>lq&?^%9^n{*C=#>e5K|)`g(CZTV(uAJcblLH@OX$T3y(FQRCG_%yUXjqJ zC-h80uT1Dw34K9AuTAKS6M9`jUz*TUn!jM&dcNxz4vbKK0k=^>gd`e*MF|i(fLQ`$VT^BiO!vE@BAY< zy3dMH*(17#%Bo%_o@9I_Db6#0%-_ErZ>bsH2 zYxs7qDN*|-Cb#nKTqC2to0+Vbo146j_bIZic-x!2rf=_HTg9g;Yx(w0wpIMjCP$x* zbadCfh-*u0Z{z)oZL64FOjew(CadpOCdgiE44-@4z*T0wU$h+B~xo;d_!3=X$xibrCmAD7RutHT{+Vh%F2^=jghuc zR{pfBCbUUrPLvz6>Peeq>Z;t3H5ar=W=@nFvgVOC$;_v6LoPBg<{DY+jW)^TrFDX=^+}s#^3obZ*4m~`a*@g8foG9DYk#3lGI?ojBWpjRO)~qF zazoa>Lz`r_$_-ij9c_}?DmP^9*R)B7Q~NKn_IKJOcF|;mM_${#ju7!ZRAWFwO!qSkA-t%p$~reNH)H8S>wmngKh0#9!aQ zzI2QM9{9)^-y#F^iqDz`#>Sj>FdX4!Uc2FD-|1wS>}7HyS7L8rIK;p{$Qa;dEwwis zY`_N&cv)*54F?*E4CDcCKSO83{s#ET1Kv`@mWF)|@RJ9;tXp0e$_?1aEKu+l7K+T64 z=vQW-oh|y#2K?0zopyNI7zTNr{>0hIKwi5T@MDWU!azRC3!Qexv9@7HuhXA6Lk*03 zHv@ib(RVj6UX2@_cIvd2VW0t>{=^wLGy-AJN2gSX@iaf=+vEhj`i(q(f2mMp>=^yJN2c`{SAzfSm?|v_1wom zJ6rVq3~*@Oq0>%%sdHZgV`R;vvu>#8I0Nl$(GM_S)4E2deV~Clml()_eHNW{Nj=9K zXlILlkfF$Mpx>g?KEObo`xxlUYXW+afqG6b(9RZpqJe$oV82DDo%&MO?F`h9I#D0$ zL|rBsXlILFVPNcs_$@l^rG{39Z4K0qI#C~dsmq}T+S#HXX4u3q*>BNl=k;I>!!`!$ zN1dn-b)qhZ8)#>XKE<$+;RwG)r=8cE)eSujMF#3beW(+4Inq$%x9CS1);CP`TXfoa zJzLGdTB3f`iF$2dphiaff^lWpq(xH@dm~` z&2Q0Zr@qv=mjOFl^tJ}-Io&`zTl5nRjQIq=MW>zmQs=D=*x8~pHtKnjfp)g&rx@Tk z*>BNlr@qvgwavI0ALC+tjAw>{cDCp<4UG9zzeT6LhaqLy$^Z}Xh|8QYFS87^vqhh6 zAkJxii%vW9#C(y%%7#@8D;drqd4B9OZTGV4fv**7FpbIHt)WNI(DUm&Lg zdB;E=63D{>dFMbL5y-m-@*aV_S0L{l$omEI0fEf%LwQaN{F824+B+>b&2?E-m|KrRa8 z_#OjQ1Kc&X{J&^_|M6qQ(7`7he(02gCm!9SXD?o03;1ZU0`4)8I^v)1+>o-r6}fv0 zWNKe__YNpK?afHPKl)w!b!{B&4ZM~PGNg8H=B~9j(OnNtjaJlk*MDSO#?#!R2qp2z ztW)xD*PP?>N9MR9xEKR-2sm~oADLSQ{M)!!L>|P1dqu@g#)WIS;~w|#^PP-QNd1o* zdpK_1HzgmbOQP3OR@x@oXrXQc@ zE%ZGG6kmK9&!;>JeUAaf7hj#{Qy%d(q`PX5{C!*gw=cJ@iZ8xYZdG@jQ6BL%qy^$r zeDP%-j`);Ed<|)V_!M7!6;DKb$|Jglw25R^N7YmD#aHY3l!vY%Z7QSq6kmMBPe$WY z9=e9KP<)CnzS^fEKIKv9dkiRk%=fp5Pk9vj9s{*LzWC~1^5gU4jIJSVYBlTEi{gu~ z>}AjA^N6k?EgYZXi?8C9h);R=Ye<_+sP(D%;wyeN;!_^cHKa|Zi}ydp7hlzD5ufrX z^gRX?Uwo;>5ufrX^gRX?Uwmb6M10C4K0b{8ea8j$()=mD_-g+i@hOk^_)s7|#TQ@k zKO#Qm5g#84#HaY;tMh!yBRW1bkt{eqD8Bfr-t_Cm^XmA}R7TC8;)^fycEqPVbbKfj zpW=%z^=`zcJPLh}0mTpM@+kB@1{7a>WnV;m$|E{HG_{)b#|On1UzO)m9?|ik zaD0j{zSPX@l|*}_zJ|Q_~J|X&;67~a1DNwDys1*zW6FUpYka5Jq8qCe0sm3Jc98xsiKUJ?@ILj z3Oszy+~_?9{P!t**ZzO=87pyr&qLq$s4tx4yP_WYJX!InPfsg|^57l=e8;qY`;F68 z^Uqz>>GiF$)a&YJPD#IT?OEwX6V7(}$#)&ocl6`$rWc<4C#U!N`IPLrUuM#`|2?wz zyJxfPV|UD2H2jEe&W=9lxbL&gUpX_@*SPT7tnBpvZgSls^@s1xX}>HIANh1S=(?=( z!T+x>?V4Tk+bb>}{=<$N8SD5TFm!45i#hdtHG8d8BY*7AW$X2Z;xS(NYrN7mUg@f% z>dQ6X)G>3x5$^j-YRIEJp;spKR6<{R{<8gxf7@`Gc*P0-b_sh`!oDD(*CzD3gr2%^ zS^jnjy*QzlCG_%yUXjo%6FPO|dytIJL)REZ^{c-IS9NU6uDUj6PvwcPx;NHe^U$b0 zPkhZ&WBsf0^w&H#*1tAS|GGSO&3j|F-UB}C8RV(TzAI2RWTceyM<4;`T zkf&tk$gLIs-LuvT=cq035ubneJFlO}_;WqPK^|+{K-A9l6NAi1t*HxjW{lJinR+rd z$&86HQa5D$@Mq4bF>&yxA2w=F4We~of<5p7_|q?%@2JkK75czOKJujsJD z+Nzy7mrNhYtOLo^P%`sF9f(6sC3gtq&L+#hOCWa-ruPU2Souc8h)w zgyC`S32Tt;|EmZ4OCh)!59t2A1ni&5N9NE!?)P8d zZfY(5)Ab0KUTgLGMVlU}BcgLJo0`vm+8^Hk)VC>q@MykuoTp8X)FxBiIjMD54|V4V z6rcW{J6AQ|aYp^wlArYT-d;a<@TON*_n-4c>F~Rc8L;<#Z>FE>^N;_Ylcoaw2ix$R zH0JLAPY>QtxN}tedediyW!FUOPh&0gIcbV7zADeBbzkUn(iC5O71u`NQyv9BC(Xwf zU#-`bN1@M2Q+)Ad>>i7p%c^k}`kXYy7hj#{QyzsrCr$CiS9X0gKIKv9bJ7%Fd{v%L zc@+AbG{qNR*-d_Zdfxb)gI%>p{=TjDcf}W9mFH6)@i_+t;!}L_)!pL9=Xv9E4hqDl z_~NU&HR4kq(K!cAB(o_{Jr!SkWe-Mt%0uTIG?h_&iZ8w@&!;?e&OxF06kmL)hy46` z-a?;~rugEk@O;Xn(C4HnzW8cApYn*#IcRD%>(`6oi?4WLR8Qqm=yTE(Uwj$Qr#uRM zPMYG2ug>!+kLa9(!qrpp#aFf{s;BZO^f_sYFTN_zr#uRMPMYG2FIDaLKhGNJeNLLTA&!;>J zeNLL-!aW_{^@MbJBJSkVuC-y!#4Q2qUDU-LDf(_cMkztT|8{CaNb z-iOVN?GKv&r$1iSU;Zzfe_f#eH0Jzj`FF`aQ1kWT+UgdcY?H5CZT?-ne$0Mf)a>?t*R1@>cgvF7AN+OgJzf0cZ~D0A z?n$Tj@BaLxcsv*Oz5T(Rce%jD!+wL=_dI6hEcu`KUi;kLtv4=x?y?C^-=%e@+~zy% zl>YV75l*jrs#9*~qs~o#^64Cx-=&K>=5~AYfb@R*-0R{Gd2aLEqv@P4@yAO2JJwPSl!Q}5O<_09F`a7lLC z#YN8loX`5^ZhX5kd-@*NxcFB*J~%gi@e|dZEBiYCjt33O-S*HE)w|!mqw}A0%Anj? zYrI-Le&qGee_r2yxsIpZls%zjnA1PFY;dl2RCV={4-Rztu-As=UjEzu+0WO#(&?Mr zI4HNY?XB5&Dld2XUMCOAUD4yu*;9w?@AOZfEz6xZV()C*y+%8|*AXLg8_$0rd&-;Z1Z+Py|=Re5y&aCJ3TYfFe{Z#s4_UjL$`LAg)IyddLd$aGiZsFqH z_rM;xuebOpyWf5h-+;At%e`LOyyjnIm)v8!e4nj*bC%Pmf3kD#>rOvqhm33C z`VT*2bnfMYU(0su9ObjlkNtD+u6kYenKh@ldY=4`{<+h7T%Jum6V>y*iw5U@{qe`@ zPe0kv*=KhjoGX6(+v?*h*Ge;<8EpsUT5sGs+wFm9z1{ffpxnCKwangg)F@|fwy=Nh z-pen`j$ePQ)6384pWEr)bFX*BrWL|dm8yh(N zFJt@XR_}auw%y;Sxbf`y{-E41i+--Y;ECg%zI1L;uG=l2SD&%Y*)HDYBirW=%1mEa zaZmJmallSpa@(BLBYW{abDX`$b6s=2*LuAA=57D%^p{6<%(eWus`{zxK6LsowOw)> zZPz^e*2p@i|FD1i+&4dU$ku-Kpwr8*EY3Y~$(Za8KP_;2^WhzGCmlVaddnWwPQPSS z*W8SE7gk?={?kssH`_TkVWpwjJ6g3G!1%^IX|G?8^vj-c_$p4{c=OJ=Rjba(o_oPB z&VS6jF1bCnJUu({_D`Mu;;CQMy!q8h=`L?R;`9lNzpZ&@yK((Tb$i0;=@)*g+2V^G zva_c}^?&x7UuwRpJuUn5=bcO8d*$dKYhGKpPWG`S!?Wn?&iSrp?wF7RG{ zyk_9-SEpa@be8k)^~4u7&whSXy6nj*&YpVc-I@>1o|nG%+Y6ljPWjt4%|81}`m#IE zbpFN1zgx5VdKacYJO8aT@y~4YNzHoef7|cUY41Dz&QYJ#^f+|U4t>YiZ`ax5$+2J6 z?EYSl?9O*gclryvd{=YtjBB$`k8b1Q-8tZ2HPgmS%6_qFbi6s^9rruUk2 z(iiN0p0jWJ(swnLU5ED1e)6{K|J@6})pTF?^6UZct>^gXU-x~@!5{po--ah&?fm2S zE&ls>&N=6OOZmDREOU-{#oEj2(-V3op;spKs)W8Eq1Pt##R~siT+Gr=Pg2UO8h~J#*@^dR;R9Q#lu(^EP=@%#898 ze_f*g^qI@XTb$4Q)fhc>R+7DmnQNpOV}&CuJu)!$gehGFFR}5@l|+T{F#JamC$PwdTO1- z>rq0lOZXQj?5R0XJvncbM@2%fOz6euEbCvKS#~_--md*Slh7*?{+WbcmC)-F{oBo5 zcK&M<{>5i6n@{bN%f23Dl6J4F{&fjGHD}rNQI^mvysr7LO6VntdY2{i)H#Xx3B4kr z*CzD3gkGMAUy;zKC-h80uT1Dw34K9AFP@voKcUwq{1+$mx`bYp?4Qu<68h3a|5W1r zM7xAuoH!m9C+sB&o%b!ASE%F4|p$(71q?;k_|g&Q5{{5yWQ zP3b1qZ>Q8%{pGLsm!bct*;^05Z^ZQnr8l$f>g|tr@yT27Kf}DAy#ClU{_79v?dn60 zdS4pG{e1N?F76#0K2=KIcON_@j;r^rVcfP`?dj?<|J0VQ9`18*|Nb)c`%~{9-MH@g zdheKD?{h=`*LS-#P2HaSZ8OLJ;-}=r?~+zGm%=mbh5iGmU*GL+cm1C`?p!xs`ssaj*zdkU_qu++?6=U3 zXZ2NoE~WpEe=d&m)cfzS-^}*+?Lhq>ex=8NO>O)A&bPRJQ;#Zkbt|$qy-yGOE&O}~ zH@+7#A3DC1M&9D`tUYj(xQ=>1Pd}}{ej|@}{jv`|-rvpR(_1KG(xK%p&%b@x zDjuKyj|j(i<$Xu=r{6)Nj&}ZKhwSX;ul;VX)X#(e|3dxnU-{7HF3%Zr?{IazZS;J0Izw2Y4_h;wsJgwzWWn59%`lk&qAJb<;BiF_s(k0 z|FaJF#Chrex6uEBty{W!UEZy`%WL$Q)8f4J|6Q2Z9V6zsy!x#&(e?X$y%w(DYH$53 zuCx9R4Ex>PeunG!`hQXRQLjf2A6?3N8}a(Y0Y$d`Wyhk} zU;j^r{;3;KKr|1T47cK*-)b-44td*HLNf6D(q8TxnW zXUx6a`L*gkIrh{4y`kTGT~2a-ADuJB`SCt+LE<<}UK57ZAIEDOun$`Mh(9`c`IwczqBUuAF=gd=fC;0ZQ^yV|J%d7cG-V|i=Q4d%K1Nb zWOUqm=j?1>UcZ>fihzgtVI2LgT2#t7Cf+=;etsJ8d;YU0o!=VG4vzh58}Pfn)!WYR z#9PP5esvA_eR|Bd&hLq9#>IXq|Np(#1^KOi>lyvYZ^v2FV!z@B{BloR-4DMjJ5G0g zte>Tc`Ncl^=g98Xbpvs(SoDk;U{sVvJpZu>rAX7^IJ%$|-*EiFEUuC~9o!<`? z`^SD&4fwq}V3joS+V_7a*=C1#QOPjm?<2N}r&Fk%z zFy7qL4s!g@~Y z=lGpmT^?O>bX=D*|2b#qclSqg`oXvDl;dN+ii98I?KbRa_c_46)f>h2t8Kt<^#v8q z@6kE+$Dg_e{LX*n5a&1Gllxpe##x>iC%gwASpWK6(SZHQy`tA|`cH4bzWX-yb<8wi z-~76Idu0Rm1J|oR4plW^e`!?x`_2Uo*uO2QpC^6JtNJs(GkYB8>hS848{K+fKFj>) zv!UN^S0Cm4)~ntdR$Cq8^1b=zma(5ccU~smtA{zisuwSf$F;P9yv7|>KTrC6nmG37KCif=Tm3xg z^XkyP=ZyMs>GSI_kNY<{+~sk{>leg%=yUBb&SzKGuQz?p9oirGILfowUdY1p4$1Ss zkN;NSKdAu6@2Tdf`-|wlKD;Mekq_PPMI7Yub1Tck-v@?!mT}(~9_zSg{*#FILH?^C zzvw&54sO5XSWY|drK8+CxZ}HgutwjRc5v~ye-i#|c|Aww-bmbE3YmKXaZe>=?2>hV zDcWgMeE7pS;vyHtgew|SG?wU`hYs#OOs&mEOt##c37LC0aX%+y?m@-tBrwZXTSN*X^_l$~a7|I$KctXYw3L#G`d?pcM*`y0vpuZ>PS?<>$HM{zndn`teKllEimS?#BrJdI{+ zKhB=j7db<-wI6TK;yTr2&Bd7}pJ2d;|F4P=O>Xa=Be}>n=${abqe~{1WbS{3Oh5UP4}EB-O?fh2^<^v}SCQ%XZ)4Bhyu7V#xArnTJ-poA zwmrR!UAYkt8*zxCagl@k;a42C@ZnE<_>_ZWV#tR!ah02_JhwBsw}HNF`*@l5?Y&H! zhW|Kw7T5kJi|YWBm1l{`I~ee1+t~U-dIk`i#fwmp&W!fa~ zW3pnh)%@;j@<0Q;L+p8|m)Y**W!i^%nKsD-O~yuF+QdaZii5v$D>Hex0e`kTdztoK zyiA*9VoIi6`OsJK;io?_$J?{UwX4Y^479V|&C9fp^fGOcT;zT?UbQ{Q+Nm{tcem$J zUS>Pm%e3#|W!fYY9~*sX6Bjus4*tq#jLCZ%@MpW1muVmCW!fYYQ!?$!hrWsrKmCb0 z!Jf$pnRzJpGIPY*qMdnTeIRdZ&#Vb#?2_pxe|!}OyZUx8`49trJKOUlFHf}XMqb|3 zwg-E;mu=a%@n@eK=VkUY_HEkP2idog*~%Zg+UX~M?21F}#~bKNy$|#CaW)Ybo?0yV?$=F8arFHFOyVmJxzPuIe3d7;X zv;qI)?fGmkvmTE1@(kOq=jBaoJI%{$+xA2+x3leZFK=Mm6TD2DWO(EQm*N~}vbc^h zS^3ZpKXSttKV*FILx%q(FQ07NQ@l(Yx_lS|@~QSLd8Wzew9m5VnTTFioYSmDab}y0 zA3mqsv-+N4vT``nWYvTiXBqIHY0nu%li^pL=}RBl;X+5o4hJ&*;6^4Me#rDw9DEfY zU-42W$+WA^@Mta=Q!4{8iLLsR|GG3=`Ry2JOkT+ly{?$h z$Robz>hHWqaLV6%Q{nmFZ1*;nhi~T|!Q6|pr8$u6>V`E8DFb6Mllxz5W!tW0z_ywP z>$-)3zIv=#33GS^zUS7YuMaCEI0xD}r4qKH>~AuqH|k%;{hQaTo`2h8S3*{S2(3p$7729K_kl(B80vfwei%K>mz_ ze0MakX3_?D;DeWOzylw={SENI2QT9w-(iL#Ltg_t@WIPC$b)>ytHe-bz@L7_26%V& z@^IU-)^;(Fhw?$Ez09zsVOInBP!HP28OUX(ftu`Rzz=`MxUYdVQEp&P4mQ9$z(765 z7|4Y@(5VyS7;T`2BMsQ`WBk+se)524PXptl{@BrJA7!8hdl-njz;KA+Im1N5Lk8wy zpn-fIG)yu)YrqeG`jIE|w9r8A&l~W=pME&Ey6&Yy9 zzMFv>RvWfAR2a51Og2!H=YCP4zSlM^TlYH?XYhWEP7W&bj`99DMm=pZ)r(e!Mtxqzf4JQ}| z7)~@Wj-w3pV;qcUieU%Cu?FH055D6J#KE6_)PX$77eD;zcccM7{OL!&rG{w+*6efx z{NzDC#~bK(vH?GI+R2wV#DkA~=|_L!oMIqf%>z2^@DPW1#AQ7D8169)Hq15*G@NDF z*YJRW_IZY3hB<~IhPj4;2K>tncN+#7PB-9BJmUVvu(#pQh8+#38SulOe)k*Tfp4H; zmH|Kf>35%DC&ML%oedWmb}?LRAfIy#0}bRuUKzts!?_0H5D&id48*~oevF4a$rnHT z>36mPKm6%;nqj!%d_$SxLIeEdK|U84=y$0BKXls3mpH_Ok9_Gzf8tzbAYauJopyML zLp(SRTR2O1tX>}I&ifIsoZ8~$oI z$nb=Lef$aoe)!YxDFZz45$|#Xe)!X`)-c*|hhZWPMW_8y13d7-%NinwCk?D4)(d^EF)(h% z#}=J-)*F4UHgq@4^jmb=S+C;_m4==MYQh$Myy0mBJn+GLreV6_Gs6jnPYoLwiVf`y zpBSjyD+b2+vVr_||ZuVI{NUM;`2B&1`$JZN4|)hfez{wmr?ZzZh7vKNwCm z{AM7JRm~1hOWV%0?N5eThF=YgBW3O6*}}G`*!DXEJj5gJYSzv;o7)Htv7? zm2KP7(8AE%%d{cW#^)^W*jB%JG0$Xd8yc4Ph(GUHp3`z{`pp%&bTy%slohZ|joa{{ zpIH?C!MLwl#pb~O_6xu1!`OLHFCJ_qr%a|^Jk~I%eJzvKzE04-ejslY$eRUnhd|yU zkg0{@_XuRxk+^yXa!DZf3;YKJ?Sp(f>tm-t-Z_wW3*^y(yjLLa6Uh5}**5O@qnsxN z?UMs}N+7eI<5_a}AfFk?a{~F?K)x`LFAd}?0{NOizQJVG_U1sY3ViMe z+V2Y5?+xS!0(oH|*97vTf&8SGnU|-7_U8lnr9gfykpJ#w`o3*Y{_h1oO9G#d1Nrkn z{yLDq3*;XI`PV?^b&+|2m)Ahayv|DIHBB;~gGpXLkT(kC%>ubYAa4=KT?4sCAakD> z#qSfyeFHfi$b$lTr$F90kar8@(Sf{IAny~%`v>wtfjlXYCkOJBKt4KtfUbZqf zn-R#T1@f7JJSULP4de?0`O-kXB9N~MTJtnjEY}4$1-;VrX(7q^W z&js>hfm|EN&jj)df&6kHFAn5?1oAt9{6Qes1@fnX{AD126Ua*g`R7390IBuXGLTb& z+$xaytwZ^@4dix#yh$Jz1#%~oSFu|1;Fz_Y>EW@=(sFhG@AkiS_`g{L@o)cC6yUf2 zws*_3U{5O#VQ(qyqr%_QqwK5B?zg_m{`#`}jj!U_Z#q5I`B$f}YqaeRyuTh~NUawA z2A+xTH@>KGQ+|u=|0{hNkG+&Q6rm&@nLbfH!hCTF_5X=7W6*xm(El5$^uLdLMUC^1 ztY^M)XZrnd@P%fBVnJ7hjdvl}EvU`!B(l zx-}Y~#u@*%)2`Yhf8SR7yW(qn6`oJyjDOpyKzxcXzFNf7kJtvvMGPEBPLpW=(J&hLN9L%;1*C_cp(U)cpwJ(WkHfBR4I z#aHF|lt-a|`%m%3m%7mJf1Wq`ZKtMIvwpoOzW6FUpYka5Z~rO2_=+#`^XGXB{o8+v zFTRZDQy$T8I~A^;iZ8x8&!;>J{o8+vFTS#iqk1ZjLjU%k;)}1!^C^$`_`nw>O?c?_ zMe)U#y2S4fo;N-|6o^mp#aDcL#HT#s<3oY?6kmK9&!;@1<3kh4Y#vlk#TQ@I=h5*& zdFc4iR7UYBzW7pKM10Ca$A?1kDZcnBJfHF?^l$$uzW8cApYka5Z~rO2_=>-b>Zv@U z<3m%cS-)NsUwj$Qr#uS%+kc8LzBXw7eDPIzKIKv9 z-~Lm4@uj|w#-}_A{o8+vFTM)Tr##}{#%tmsSFbOMFTPsOr##}{#w!q?;)}2NUw;4d zyz%&&xMVaw#TQ@3^C^#bdV z1mkN`MHwI8mFW8wc=$Z4q2K;{$J+IMiq12#rWN|o_dO!v(^v9cQ4f8dtoYQYrxiqb zY+*9rF|FTz<80OZa~E}beXA_>y84+@vM*eFR(jEdvz>nOUB^5$`tf(u3s3%&(|i4V zO8VR{GwIv^9@+cdvswDFJ7z5!endBCM;~{?e1*{8A}`tOf_AO0r?~UZc6?kl;Z{s?;ZIok2 zXN}H4=;-p6+{!GR`$=D9&Z9+MJ&@VA6n{0$?h(aV)ytgEqnNZs=l67U*S3ao^nGPV z$0?bZaKnqQav%|av);1YDN9z!?>ek zYm~EO{E15(@|4URxwYcId)8Xn$oV$@6rX$id#^S3=X!{NJUB;7=YVjn`5-e=YsNtR zRBOhHOug8mQ&-iWu_DulIb*EU8Xww-M_g)8z3>6(w1e=WHt`XQwL|^cqLT-)m4j@w zwKWi%_Z-An&p*PetQroiLkmGaGrWW9wv=R$iz{e^p z;GDE7e@$V-Ls8>tOPc8sFW`chys+Zd<#Ehcq zDSf#01?hR)&Pm@i`H}ygleV1tB~AYu&PmgH(`SYi<6F3Or&<*HoHWH3U#;g;9)&(9 zP4UH7_gu7pDUU**lcxCMOHGLCsXPjOPMYG2FY{`Nn?L1I=yTE(Uwp-%M|{eo;OC_I z_~OfWU3nDxoHWH3U!CXEI17DFn&OME?2D+L$|FAKU{~#tzi-R`59QWX@x@o=`IJX| z&Ow3r6kmL)p3!kldBo=&6o^mpW4>J?KIIXebI?RGtE1|f^6|x2wR6O$Jao=MQyIml z_~I)QpU*?*92AOA@x@m++}G3d7W$kt#TQ@3^C^!)pOdEe;;Sf&#-}`@a}JtX&HDAC z_~J`>KIKv9bJ7%Fd{x7u@hOi&pOdEe;w$rf$|E}Gpm6n6eDT%oROspCVT_!M7!b%Xu>=Xnc# zPMYG2FXQ=?N1@M2Q+)9idp_k69Uq!n&HCem;)}0#kl+72Z=uggQ+)ANcs}J(=yTE( zUwkRgr#zzLL*eSF_~NVD(eHnrx8Ucb`S{{1^SbgV^f_sYFTRQae*g2l@i|*fJml*2 zMe)T~mX7$8M|{p!f%p_(e6{`k{P}Ul<7?uQ(fAZ!e06>O`20BI@fC_@%_V! ze(3ui@xZC?@6=Yvtv*jyOzQK3ePO3OI46zonA+TYNa_0@ACi9Sw^!2CYxJsZN@uLp zDb}}Hzg2aot2@T};4hEZ{^6QT`Xldu_+1mLXP4FYAA3Y~>7G50OphA)l#92`9a}$G zecR=+zSk9ts{3p)CDz|qa(L;xPySXa|5@WERew3-*I0k+?tbaJd!LqW+x|uu@4IW) ze(2igACL99qx)s=?LI5kKREB>(!(ZhRx1B@e)yoe%dQ*7dS&Iz^n^Zh(nI&JE@ix5 zJblDN|5&ml)(<^(X7=!BGO_;FEk)^T9{bxO`Oj*xq2o>QuKEgD-*{3nO>PM4NM!N6k&kKDqX+hkm~9yI3#Z=z{d@ z)y|FeGY+_D;m>cEr4M*zzKh>y<9DimxaQl^5B_?F)32>=Q`)QOl2~uiyIC6l7XtfN zm))3_Km7+xKPT2_U(%2J^LQC)q?^`!%@J;&K!`nb7^w}aR5pYh1F z*nhuIE-Z!bQ6KN(u8)?su=w(aZ`ExziP+GM9#K4*6y?)cHq zF+S;xr~5ghN~QOD{DJCyEMMu5U7bpY{RixQb7^QtzuEXg|1duBLOXny&7U9ppLyvk z)r@cO4&S+Y{&xRrrPQmt*B2c6FISJy&iG`fKjWAE%$Gh~#C*K}&?PS3$gww9Q_pq_ zBmLZMCzmomvZGUf>GY?*(tj*}xpe-$!lBoIh;X9x)4KI91Zl|Vr5GsqS06-5z50?4UC1VkVl z0kXRh5uh77{*_m{o zV*hykD;{~zJ}0lI;Wwk$=i-`VbF04@jfk!(uBT&-;&s~8p0qS|d!$J|#Kc=nyu-vt zoA?+LZ&{Ywp0|njHSqx^KGeiVn)nzKAFw>Ny$BN@ZQ{LGq?Y$J@eUL3{f%=XBy2y@#5+uUw26-~@o^?T z(ZruN@ku5=*~DAcr}od=#QU1~023c#;zLb*go%$d@eUIoZQ^50e4L3-H1VfRe3FSz zHu07XrtzD2UlSi-;zLY)sELm-@sTFpVdA4re2j^YGx3Qg{{Lg(`ygOz`iAec1H&7ysxHbKPH3z zyzi#hpZDSP+Ie42ububl^xApfPOqK!@$}kxUr(={_xbeNdEZa3osR?QwexWyy>>oM zq}R^Jjr7|2IFeqwuRN}#*Urb8^xFBjlU_R?htg~3<5GI>pX zrPm%Ik1Of5M`qCO$e=wsgZ7vV+T${4Pt2hGbO!B78MG&7&~A~(rS#(Gok6>A2JHbE zw1;HS9-2XWLgL?a3LmTjX&$-TY_J?wdh- zKnCq08MKFH&>oRNdt?UfjtttPGiZ;=pgk^w_QVX@PiN4cltFuP2JL(vmTvy#d0ATR z`0Y0Tei`30;p=+{Yzy}UfQF3m8`(<27p8Hq zM?Q#<3`*Sd0U9OWzXX~5sHS?~XS6WmV%d6dTYwv|MQ z`$|v`nIe)#9n&aTKlHIYY_T5bnj8uytPc8&X1DvA48xG(hM{ujvO{vGa% zdpll0HPxeRE@_l4Bn_Enl13fVu&L@~8uy5>Kkx-S+hyOXQjKvhS0(ZP)o`y4WPIrz z_mzOIPVZb_gJ|4ig8Jv^o%_}#8utgGz81Z6eQly~pAYJvr+4mKhiKM``%WqoAXAUt z*=~KJtpwEj(L3wMy(tw@P4x}vo%J^)+6UECk6TOFRwGek)HkNW3X)_3>7Dfi5zT#Z zzl*U7DvAF^#=dD(ZzZ5rr}ru#iEcvgtS6Xg*3(q9(i_QOOo%mT#HO005d*{;_3#(5 zW*Nj9G{%isgN7c&8Z^d@ShEab&Gm>iX!wp;gNA;@8nl((5o^%!0kH;+aUj;9VGFSa zjWHqCpy3Z<4H|JmtU<#c#2Pg0BG#a-^p04AhJM5vG;|`?prOYuX~Y$=Mm_ZCAc;nO z6NL^|Xv7*aI=!nj>qMPOb6?c652#aVwuO538Febnexjboh&q+liHC2f=a}dUjhwJ= zpgEqXV;XT~-#~LNP{%ZK!oGp#JfeCOk*yvpP+gEppI$y#kmH}^9^-O z!!MpEpm~0xj%oPCa||@kZPYOhzj%Iv=JkR)rr{UQZP2`qP{%aZ6Z;05*AD8KM#;W` z=5>cUrcttQpm|-Rj%nEB^$VKUJL;H*Ec*|d>ruya2+`gI%%7I@j$;GpR`gz6(ygg% zCh0a*mXdUHD$yVECFvdgK^LQU^aovn-q9a)3wlR?(2(W6XlHqptOxBZkCOG^9A*^p zd~eVKqO%c76aMU#>3fFdy?AT`ek{yO=O(fq_YC9Syc{F|JWI$!$W6d2J0Yi_Qs{id z=OR2Kfj*GK3;psFECh@XFw+I74m#C7tP6U{J>duYggV$jj$peW0XmRt*kgaeqrM0M zJ|K^<4H`O-Z|E*WfF9_BUerMj@{lb`fKJRY_{si)M}1)e`amD_LKpg#C%|?&0(`7M zfIRC1kNPqM_+TYK5A;DtMM-0>qK^*&wt2kZQD2q-JFo}4prOY~fL_cg=!8D#MO|yc zhXkyNnuHdFSV9{@oGihkz6PN=;Uhu_VK)KudlLa`&6j}KZ6mZI?2;vT)IUdn9oU22 zodoECKIpAZfF9_B-W>$^TbY2E)+Hd9)d(0b;(-`clJxUbA{SK%7#re=7*r)7hqVak z19|jAJ|G8q^s7UF9oU0i#0z#{4|Z!4pa=S(7xBhi#T>~-z?{SDSpw#5Ej>zF{-|$6z*trj&<;7&cP1c5?-0-qIq1hR z4P#qIfF0O_?>J^c=i3D2&X0hY=#pMcCFVo`0dp#t(15UzfLJspVD2KPkXb-L4gv|V z+na!~%qPG;{D9rQ1o$wI0Q>L*cKZ`Bme~aOq7z_uAOT~UL4bYu0lPg3m=n_oun#}1 z1k5dsFqr@!n-eM$+7g-&A_?%Z1;K~VjsTtG3Gfkf5_64cXFs7I+2rVx-X&Kr20fOsSBVFctGV+W78Bc4+Us7DEYIsx-x znk>Pi9`Qw-2M{oyUXVQU3Y}2|)T0DHlYsd)LzduCkNEl!`VlZ+ksI)sH;Csf0_stM zpF_ZWo-Ir8s7HJe=RO3i2gDRS<|X1emwNBuxTc|vyr z;@6LW_%tOTE^iP}j}rWwgxZ9KvILL%{)BRbPy*tII3Yew2#Cuf0_stMe~VC)uvnJh zQQwzPme7@e_#sYK0^)?YEFqvCCHSQTU&7n61dsaOgffIq1jG+eNoY;jPAEY@EFlMZ z^xH*%9OTgtxi3Z7LTE$SO@JNPgI(kldY}(_;|P$0Jo+J*{CR<&p|F7JvdMNT(tUY8 zFQhB+=M5J53=fuqA9_wY_mTB@X3Hqqm_w8)&uqb2l7;HHKA%DtQfTCeWe^Xhk$a}C z3SC*Dkz1CjsnEz7*JJ)L9iY%b3au-2GlfQ;xG!?TbO(j*tk9tf-AkeSDRdao6heAs z7stkDBt48urbjCD7=<3M(2)u~S)r#X^bCcbtN<5>P3 zg~ndP_1G_%#+qk3Mxi$;H0C|aY*T2gORmQnVtTJaI~6)Xp+8aRg9`n*LLXA-qY8ao zq5q}Orxp5Jg+8y)-zoHEh5kXIuPO8mh5kjMe^cnY3jMo6KUC<)3Z0$SD8~x_@mJ^og$`6` zyFxcn=w=GtLZR{hQdxgnh3=rxFDf+tcPh(wCz{uCAEJ4G7@*LD6q^5cD%L*#?^Mv9 zXGU2Di)STOratud#ILq5G@UeT$cV7;F}3T|ZS=(5o=EW7zu=isiSn6Ij+0%)Zx$_I zEH<7QHOqKrjOCZsq8`U^>{VR}n3tAVKk+OXIF4t-AVw(vufH&!>=ITmrkACe@qfS7 zaY97p|1Z)Q19AxD17mtQZk86mY5s4F4W|Dac3=ax{%qWqU;lgKW`F)0V^_zGb3^mX zvCY^ILUtI>wDFbCeyL^_{1cwXql&!s%u?s6jaO|xpMGmA(|FVWo*AVECj&(oy^upj zJb9d+XM*5EcqU%>{sN!TSwF|qS3X0H$LV<{NEX>+{cO)6?Xe%8XM$vrJ=V|mB8C{x zY-2x+XM$wX7{!s}$@&(;pY0_{p8YVM36j}prpy=C&-Oz0%V)$%yT&s?vd$jsXL~Wy z9{VAm36g1pJU>}K+p~OPjF0^=o(Ym^bDs6b`q^HDw8wsA^)sVbKif-`_SlcCer6Qw zXM4T}`;pbpjAH$4&-XLA{-j;w_>kFXrW_wwKihLid+djCe8@U`te@>A9yP|te#qlP zrVaA^Wc_T<_n2INa-7ETA=BnO#|PHW_8ih4`;pbpjAH$4FG<>CKeGCnQLNu%?}Ut} zwCj1MQYH_%oWCTgpY4U5H0-e-o@Xj$kv-PW_ALLB_M{)4@nv$#@c39i+l!F)*bmS6 zvdA9mXM2g#9{Zs@Qz=ssc!tDvwp;<4qd!sKX5G)*O*H89cTOAzTSE6 zyKUl`Ifu@DV(YUpo6W;(h4(J$aKv^*wzqxrfOFsW`$6399Xra|{jCk2{?u-x_V^E% zPZ9d4-4^!N1{?Ev`;K$ou&?*TzZAJPgpeyhU~=O?oNUVV_Q$cX45ca_hD zz6a|H+Fl#c)XDsFxAxk`jl3D3ZOAUseyUQQ#-kb@aQ+ct@UOg_-+A|OGmm`k_>FsJ zzcR+QL+a~$JS2%3Ule}}r z>vKIk*>jRT4^Q$=lK1E%drq>qm;00dHuB%u`?aycKl0y3{s%En@!C77;Q?EiY^QiS zDW0BoijR}x7;ym`qwE? z+d28*lz6Yv*KJs@gNuI?AMru~8}n?h_M|y>w6nbil4pC|&i0sRd(6)c_&O-p_<|n& zjlQdG8&+z($9}7a3mUC{6y#+4Xa8ul{Za2Nv0e|3iSc^Yk-T1mYxG!0j+2wc^=tTu zm-knx`DjynfT=yv)E;BvPn-B86Q6A2z5kWke$uz8dCS?HSqx^KE%X_n)nD4 zA8FzpCO+E4$C&sy6Q5|}Pn-B86Q6A2E$2+*H}SqEKET9>nD|f=A7SDnO}xXzN1ON< z6CY>d6HWYS6Q5+_lTEzkylMO<-q*wjnD`JAA8O(wOnjt?$2!7wP+m8D&ozGM=;f92 z%o|=u>9zB^O0S*QS$gff?$T@Lb(mf|ugmn>d7Y-$&g(Y4c3#Knwez}8uiYZ&eR}QQ z8MOOm&>oOMdq@WDp&7JCWY8X&LAxV^_UH`SV=`!u%b-0mgZ9%Iv?pcIo}59uMXvjF z^PfSxZwBoF8MKFF&>osWdqf89kr}i*GH8#^pgkso_P7k%6EkQ(ok4q22JOiiwDUfn zZvN%IoL)O02hwZj<3f7ve4I$HosS#owexW#y>>pXq}4u(jwJm1G5+1S@%u$_koimA z`1gFQ9LKrNxyTRsJ3l;s_c(=9e_aS5VTBH$I~q|NptLB2z@t7TtaX$!X_=A{&#yAjD(6Eb`f`;All7=nB6!p+= z1xYk?R+KdKAf~9tyfdO<=&3C0QS$usB^q&rBw|}#(vabL(5w@6Dvdtqi+c6}bt;Yg zv3{$hQK!=EC+c~Os8eb7AN3p))G>{muy3F_o~UCQab@2?b1qQFG;(4kz-Q2$N2@|3 zpX?iGo&%_38gqet1I_aXbxgxA&NXPBZ>VD$e(^j3&GQp=Ov6W>W1x9%qmF6##q$$1 zuMyNS4ZnD9gXXn^I;IN{&Ax%=wSzjQQL=BKdEKFoX_V|6XkNdlV;VML1GxZg?3KoT z$TVcxf6!cyI;QIqjkyjzHR=6%N!OvWhNPdP5^|7%Y;8%`qLSAIWVjwB+8~b-dRaf~ zM2SAAuSf4Z7Q_TR*KAXDmN@l~y5nvyFz%KHHbc5;Cm`N0$Pzs2k*AyltT&7cQRCpN+?ViB1`b7M|=_I$^^8d1P`AO&zA_OM+ts7p&((HEWx85@kN|{ z2xvzM9=S(6M-Wht68tCvd>tuE@Tf<85ogRvj2q*_xG+A9XEXuzD8Y{<jA@j*Ng&zS_&qaAXHFWMo8df0{ikp$R{!Hjk+AQ*3J{Ex=mK-z(Wj*d4?L*i0SeQT~VPcD|9u5Mjp8@a>z912@Q*0*~M{$>l;!H z(~XH{8M{*7M4_80G9gNRYlUv7$aGTbyC`(1B7^;yedw#u0~9)3p(7OfC50ZL(4!R^ z>zMUp%`uI2#5CqO(@_dNOQA8pS>_dmeodj@Q0PSpy+om-6?%n2uTto>3jLl!Z&2us z3jLu%Z&m16h2EvmdlWieq1_6-U!gx$=tPD7QlXD1^j8XfQlU>N^cjUdr_dJ_`g?`G zN;J>UYYKf+p>HYlU4_0+H0SOQqH_|k7XH)w&OP@)kBZ3jJ<#>)2`94b&GHxA10C}E z590s#SneD5I$O3M6ZbY-K9ToA|FSFr|KDdmjiNa1mDsmyWT$I|@5~bM!QgPOBQ^ck z?psak9#L{BOYCDjm`6ZmFs7FyK1_^T^#LL(|9_Fj7?48%$2RlJviP`|C;yGH!FFct zJn4LdEzBLD5E#?T;{5k~XV$kr>kspP)}B>uu*3PbfHA!+IogP4A2?=T+Ku(^`@T`) z{O?I$@RenYUnrr6m0RHqn*Xz{<<_xU_Z{c#pEo%Bzk8rLKu=Jr_dp|e|6h9Hyd39@ zJm2^?su4?!dBU*GBKif-`_SlcCz6YB1vpwIpjq$M`S$z*Q>t}loX^;KL>U*GB zKif-^_SlcCz6YB1v%Qd|Mm*V%tiA`D^|QSgX^;KL>U*GBKijiJ8{=a?vicrq*3b4L zq&@b-bB|jaUXf2%@_J|eY%fvTV?R9ixMh(&*3b5Qm&x@f?RxHU%OZQMpY1uMJ@&)6 z$1Rh_Cy+mn`{V?uv%WO1L<_qg*d%o|;^&stL^*zw6pY1uMJ@zB3?}29h zY%fXLV?T_0+_Em7te@?LtTf`ueq{AM(5#>B#YlVXM^@hh&HCA%WtCii(yr(DfG-9! z@#6i3^|QSQX^;Kz93QgC9_wd&iP9eX;W<8Jkv-PW_Iy{%^(XBb$A?TBqdagtSwGuz zNPFxDA0IMnk?pa5wwEOBu^)VV$SQlRpY4ULk?T*|&FXugSwGv0k@nb+tiA`D^|L+8 zTDktDUE}zW*=VNB7uL`ABBVX`BdhO$X8mk0QQBiaviu%ssh{onu9NFe)*Hu%tcxe> zXL}B5kH?wi_drYiY%fXj>_?X018uSw@~&Kevfgu#a3&AAyuYx19$$>K$K&+eBb-I{ zSU=mdyeHS6wCfpPCZ`OKkM*;?2x*V~@Qg2u?6H2fmniMAAId$#nTjZnkM*-X{+^Nj z$ntxj<@cCuk55t94`qCriYUg1@00oZ3hdxJyR`0s#xs8TdJ3+u7}v4Tm3ra_>R-R|{LOt@ahQ8?LapRtAT+_;VHCEeQu&?vpsarP4 zf&Y3yFPq9c9=&es{?con_B|y3)BUAEEPs;ZlNA0z=Y>v|ukPBd-8!A<$+GL;fboS3DUj^h16n$yb;B*j_(t?Y^(!VtM#mEnw50M}@iw z{S)82rfnTGJE+#tL7wp}YP@MrtJZG{eP~}t?On#^bix0ZFICjH<}YJAvUH%}Tj#H= zS7_AS{^;$tf=~LqvfgU+>-OtE%@_W?9bZXrcdoC!SC3eszuA!r`k1); zmeu=Z^LO2N%^1&n6)NaG%H?*|m|Ron3;nFT?%egA^POh%gnVeXT6&I_pYNN|K3udr zWBm1`=xMeV52gw}SBpma{0H}J%O4o_>$mgQ{Xbu0TRUQt=)ZAoO}&`o1Lq$TwhBI5 z{_1+?Qf*u#zSM=k{lBWN|G1!z%WryRp>N>A8v2Refv!PI96~<&cR&5T2eVwWeh(D# zkM}mvV>bp9v*}WlXVBT&* zeoEsY{m8PIz}}?|`E$bp^_M^Xz;$X|Z71TL|3qUw&}WIO{%Na_fAvOV{oM=EuGufH z68e{a+Enj%`k=E)bYmf3X+W^PaqmHAhfPg{{QRlG`n77Mj&L0qUsUj4u4tymyt~^qtx93R&!5~(ZyA2jHSa+l zk&pbnTI*e380uPkJ5H?s#BVz4@3-COntipHkbmoqmimyRmtFpj;(}lQC`5l?yW+Za z#mK+=nNIqIQ?ah^^FJf>?b!XIeyi$_u3o(ids@ME`Zu=h?%&Fn6#XABYpV~v;N^~c zJX^?D`|?G7{oE_AUgaVMA5}X<-+6I>tLTPMC-T4Vey|=tWs9qz-*mxOcr92D*uTT& zyyWdddztbrb)SUxuHl{P2!7+T_IiW&wzxj)(?-Z2-q}L`Jm_mzz>!vhAKj^iKBC_@ zuDchC3Hb`2x7Sa{zVEu&-B@qg7Io4mS32Vg9Y4#E-``q4(E6S$>D&y#M_zBG->Uq; z)vWh3qJPUdo%EAor(89w82%KwAEbZx>>Agh0wY8`r<@JaXVzZkvK%tvd1-M|{n7pV z&Y!NA6792UHPwATz2lr1UC@s4Oe+?w=P#4rRdcs7-`@QxSTE|A!?k^MN73%JFG!DF zw$#dcjh`FFf8vcU67pEVZt#3;M2}57gHMEO5;_Q&RA|x&`TZ zs;qQ*e>*~qr_1-j`XBKRoo^nTDEQ>pta{D$H=T2eydv~1YhO_x;E3E8vEA4&`Zlkw z*PC41^;VDhqP_N!8oGbMPo3}A-zNBz9V_WMuEjV%Uvow9eRgIYC8qLq+<!3I@UZWc>A#jTGgK$xn@l;;(vJ6AKI^pGhGjFRz7xOG@)TcWzp?PV9s1`MzOEH7aVPNY z3EvC;!;aUr+C$$_U)wAHRJI~5lCKGc5GCUhR; z`njBOyqPoQXRY4pH|#~c&kB8c_x_<>+S0?dxZ!5OKdkalJJY?E>&AnzLSLWjH?;lJ zci1nrFvfHB@Xwlmf%*0~JG>#<>z}x*Mb~&C$aVdK=zsUvW35)vWv;%LiVOQ&*4)#= zE`JbMYRXC>?>TRg*W>Yw3!Jxvlqi|%85bc%Q}YofKGMWHOnkJ7k1_FaCO*-`pEmJH zCO+B3TZ*Oj*W1MVn)m<{A7bJ|O?-rjk2LWP6CZ8jEnAG=Md7>{FNge{6rY>NnD|5! zpCozi@B4+@56Oeb=OxPa0cQ{Qk!HeH{IUctC1CU|?!K#MD30)E;4O|1v@7 z$M0S7a+umUM{TPcr#yiAgQ*D|z-e+BBXJQ+tGocbN3WnD|5! zpJd{b&Hfcny&qXVNbRq;soi(D5r5-%re0o2CO&dR>h{Eusrd-0pU0mp+j)J5j!Ke4>d@GVzwpsr?Bt=?^vW5hgy;#5+uUw26-~@gXMr5hgy)B%f&F zPn-A{Q-8~crtz8hBvb!n6K^q{U&NUDdz;#QO+3z9@Vi1jPvK`mOKi%@cp`G}>ELOKJB_qy6q% zDeVDiw3m3C(jJmVd*X?d_Ruui^E{j4e?%JXkItpEN2byK>eo{}<8!3Ze)oDx|L8Q@ zKfY-AhwpFsJP`52UWfTxtwuKu_sPBaSx!6Vu{Y6teh4}2X^^wu>8auOvUOV24}`p9 zZi0>F`TS9pf4OvjA%CNjpREAN*BiSmh~@eGQk7r$>GK-o8n^6khpz{(PZ0V+^ZBRh zcYIu=9r6Wl))(=C-+W%G>V4d=v(OuR;-C$F#}*&%(HkSrQ&qh&y}F2a_-xG~;vwv( zA?GzBhp^vdO1BjI@;pzqumAqG9dYaOUL|3F>N{2;Z>2gu4~9IBWr%ZwQ3Zwk_vPb- z{Pb%Lv|?0W^mIQFKP%CEzO43}9JkH}JF9!xH2A#rxfs#E>gxGoyy(a0)oQ;t{bEJG zRb}>xJUO~NwxR#g;l7@6@%cB83vqw!uB+i*u|khl3i%z?D~fnosgBRjRXbe=mKNiR z+kREpsqx70Z+Ao)j~zabXFG^TT=thmzwQ6J5`_HqZc$Un*MS{*o)3AP>qGuw<9tDg zL*EU$u(Pyy9^vQN>3PMx+A%J-9eOOgou2v2pAXnB`XBzTp@`#eUw0O9%+tPzXMFtm zK^T;peNGy17u@APMl}Ge*>K}Uio3;Fw> zP7?C)l|QdS9>+2G{rS83Z1DSWNbBuOmV(Uxk4lIsP6%mCH8nCn2}%a37Bxe?Oqg zRqOYgkZZj)MUKBWP~|2!=o1A020fbK(Z}B>sB#yd`7jW2Q%Xb%In0|RQ(nTHS}n)4f8Mur zuWo@@Gq%3@#qsY{*TbIUCVxK#d7kepzAYl`1s6Fcj?+JVQbqLZxuSxnAAgUf_PaQ` zp6J)2`+3oCg)NWh_oG*+XWsMoU8;YhdNddEpYBN(-|uM5YP3Hg%qstNfkpNEQh?mcRq*zb`y{$36Jc;1YgGg^#i)tUaH ze|OtGk*~ea>!SafPVGg1EA{8^;Z(cRe=RBck2!Wg^k4E`QDJ{di)x*R=JU*P4 zeq8@!aei61R9A7n9us`Tb6oOGL*DsiN%8rs;>cz~zTv=NF`w(eFZq2R^z-L}Z%>U7 z=jV$W$BXmrgMOt&zZ>5d67#D>(G$Yo+kUU7>~AK!I8W~!(?IlF@RddMTXNo=vR^6k z*smae?uKT^=cMtY+j`bLf4>O-ST1q%MH}Q2_Yd&M@%N6Z-1D)0h5b5Zu6XQP8h0TmVh+c8-Ht@w}|7oVvpmpZ9`iTmvZaIc;XV1hTNJQKQQVo6_z`qy4jzDaWPgG}`yPXygI* zW724E+BhXo{C%$)hdGVLi+FWSUM=PW;?Lg$t8)3ajTUmpi?kPUg}t~m>@}>D;!k24 z?IW6{w4Y9+J!z*gUiiV^C#!xGyJO6QZnykX@+-d=R^=Wv94q`C5}Vy47m|kDvPUlo zxz_cTc*d2KhF`}grQ|6&jrJn7Q}V>$Tf-mv6YF(-lrpX~{0I*iCj1E8y3peXf4{Bj z`EYZ}eB_9496nSI7c#k_Yrk0gI43(sgKZyXSqQh zCH6PaI48$5w?N}OAIAjHXlEMdDv(1x>R3PQt9sZLd|{oiWemxODC4{bdhpo~pCdt| z#4#K+zB9q`5;Q(nD63sEMN^~^>WPIrzJkzQzu7@3#uTFFgh*RI^=p8)M(8Y9JNuwXj!yoj4 z4C>fVjFY|pUUSY4Lh|ZU5m;(l14lG27PFQ9_Zk4!3UOyeb$2# z_8<@au*W_y4IM0lI<^%`H2djCv_AoTQ8th?>KjTLbxijn8f}p8P48^057BI^FVXC0 z0MU&IkVn~A(x?xVH0qd!ZnQxjb!->Du^z~?pZGDXjR1L+c1fdNlQim>hEAqY&;Fn< z>xX^xhfc(a^Vpqe9wX{GUpmoE2v~6_g9Yv5rBm5d(x_v)2hpq(CFi#%(K-Qko6-9V zl1ABF(x`7CY1A>T6OA_XMIGCMKdcAx>{|%YEeViE*-Fx=Z!KxmF%6weqn`aiU)B%% z=ntL!=$*&ahUm5g)T3-CY1FruH0li6DxNdTu^mA5h&B3lp!bfFM%hWysDDw?sAC%X z(T2XLV_Wcn^+2Be=}dGN0_0J4l{D(RNg8!bLnqUyXMfO_^}{~;L+3zxhfko9hfql) zN0?ivN8T_$K-Z^t%n8tFXBz!j9tc9dKX8ID5)(L)H3A4=~pDKy$pKaAdoOB#K_b3N;XK9+$k zrlF7Zk05#^0ewf&`)Gwm8|qQVGVlj-s7E{dgLc*l{j3MRp%3IC%YMQ)9vAwtZ>5MH zLxB85dVfXIm=EJ5J&nrZk}gZ-1W6a7@?}YTQyD4gl2lHTH0qd!9hQMD)-#@Hwl$V$ z_6PkS2j3tIIna=W9B9~|Ea@p!PL(w3z_Sd-0D3yTGaW@Vc+}6J_b8B(W<4{hhV{%M z8gh`CP4C=y4$wzrmhb-GgoR~&E#~F4w7Z_7M0(3$*#~=O|MK#qIpm!c4;*lTKURKXDjQN>>3#c|5 z!Pr+IllIH=4CAN%pAN<|I9WSBn{_BmpCRP4Iq^Ro@J!AeB#@Ipq!@#R%DjX;1dIV~ zka>oH`J7tW$^_J*4KX5jJ)iC1 z7h()K_y!<`@Tmj=>#rICa=^2MvIL9)ebBcI0sY_$WWb{y>k)dO4{ba~tbO_gAj}}Z9_%7cumgLrdzSz^#fXPp zw86(~1neQn1lWN+*u^}69oU22`-CV0?7=Q%#u0uXAUDqwkn_5Pv4q=%34|XB$VDvz z=16_Qc*1W4?72S?FlU}4z@|R|`|vFS^gtiy@#6^=Oc+|HiAhw+d7#HG(@pmD>9{hn{hzG{= zA_1}JNI*N}Adh@NzfM3N_7cJgM+k!naRm6o_Q9k6Fkuj34*_{zA_xr$P6Eb*aY1h%0@h#; z0^*H6(2sZ_ZhZ-uQ{4$zI~@q<2ftt+@kbu!5}*hA(61!{c^ycoN9ad@9_WJ~@CSZj z&0tKU2#D=i0_4#T{V^8gzZ(I0M~q+>=kA71Ku!lIq zpQ!}&Lx1SOcu)_2p$GEl2fdJkd=J8ILQldxf=-x4XiC^FOYl13H9|AOd_oJt0zxQZ z7Xf-$em2p;gsp_`gq;M)fk*vJLKDI^0_^S}Kn^_WXApD({Ao^jl>mEh5n2-#6Iv0L z5Of0M(eHJ_3j_xNe!^eagFhG#{DmIqgWfj?=m&ow2OjmW5TFP8S`ywQz#ikVilG13Ac}-`fP(L%iV6QUdy+KlETcsE5DM19|j=UdTbdAK^>FK*C1^>}e|q z?Fk2D2_E(D5;_pp5?&;{N9a%Zg3yn^^6wDcj_@gA0O4~2Jg?BFpqZ-Ft1|?sE4kagm41Z!A=6?AdmWKgy#u!2v|qE2#|w3 z>OsQ}^g-`#0^}fHngE${gnfiC!XX0s?j_VEz*cR-a6(@~0-+Zno`Cv(1o%0O(1-9b z0dkN>J;n$7FA;hZ_7flndDLU>zz+05?H2{0(4YcZ1AWl@C7~Yy@{xq=1aE>bp(Noa!X&~C0^)Rt zfViC`3?uxD5J5Oj7)m%t7)AJ&FoJNNFo2AM`=*F~ShSWdi&>Ll{jsOBhM`h5*0e zKja{fei#Sp(GIx_1lWN+*u6-A9_WK!!~ylE3D5(1^n)G9T_nJ7!~uT7AIL!-{V*Qn z;|2loZ%9Dya%MB;azQG`5r3O7mXM$Lc~s`3au${NpNJEv{EaZ4;74!}@>2b5Dlu1~ z_ZDFy;VR)3LLO>|9oU;p@FE^-2X;}Pjmnp)yhE5m_=7N$kW0!z9{uhSrV(-w553P) z3A>L8(+R&5q6p7WJ^Zpz3BMl^W)L0_rV_GKJ;s%rO4xxt*u6`DAMgi$!Ee}sJ=jeq zzz*!eF2)Tzuop$;eZmX^?7=SL20O3^yAKIt2sa7H1LBQ1V4U!yFqMeg&jiF7@q`_W z6Mi6`i2E-DPskeX@xGY&^`)XRiP0J z)?ZtpF`w9$zd{Enbf6-yDfPj!9yxEW(5)1@okDj~=&lOgL!tXfnhJ5eVV?&n^+OeU zghFF}vpm)c)0odpW6d%>Q=#W7^n8VWU7;5#^xF!(T%lJf^g5zBw(l!+j3TpHssBi+ zk5%a13cXLE-3tAQLVqS{GOqWpT(h6N(p?ws(szTRL=-LX6=f$x81`6F+ zq3sGCtkBIBx|Kq=Q|L|#-BqD`D0Cl%9-z>J6ndyak5K3_3OzxgUzRk5*=w3Y&s6BS z3O!$;Usvcw3jMZ1FIVVQ3cXID*DLf!h2Emj+lj{9qrz*atOvbEsgGCc6BPP@LMJNp zA%#As&?go8v_hX%=!*({S)r2@`X`0{MWJsibh1J}RA?L^dA{XPXp2JUQ|LkpT}+|9 z6&la#WSv%pu1s_;iX~n+R{IgJ9?Qrr5%>R<|BJ(aG6$N_0p3a-9C-$b|AEy|&LhmT zCwVpL)MU)?{^Q1v39ntJPTeQz@dPqfLne2#99~JK?r(XB6M(0DIW8R+|I>@h^wc<)VBX36G$32% zF{EYQ@yN($;>nXDUs>=!viyG^+Hv33y)Eo7ole-tbo|meb}z3eoI2waTX}I^6i(&5 zvRUa(zPRs`=|Lx<3k2CP^np?Jun%QdIBcMI-QI31zrtZhdEm`6K?mOK5rjTzzo6e^ zLlNQn^h@O8&+_5PpUem6&yqWhbws*2&x#X*%ED^Dx?ST zpG-b>+p^x1zaCrGi~KbU&1q{EI_GcAALonn7jj+hmol#y4_=(VkY|njI*#)C3&U4M znRrF{eR5?}V9cYpwJ{4;+Cks{!X@aGxf`{^Fu-#;O5_3U*{mt1vk%M_I3(e!b7@$d z#`KSMF3L5ao7OSaG^~>%8+(Q0JY~{?`0) zJ~)3Cmyws0s2CLI*>TQj=Q6YZ6R-B}f;(gS=h_$b?lBi)zo%cWeHZOJoXwMhU5MSW zJq=@fmn`i6uRZ>Ltv`;`E`2}R_4x~Vc2a2vpUrCal)mS(Y^J!=HSt2LZ4Z7rDRS`I zx%wQo=IyKI{%TJs z^u35zpK~+(yw(rg`S6drvC;mMw7V^b+IvhK=1-+-De;A^?4ePnAndi?+2E8WbBu@_bu@Eel*>^hU(B><4WxFl_~cRW$yD(`_wYY zp8O3jlPvOw+|65<`)|!3=OLr~@p?<#CfA=l-^}d)Y#crO!cHTm#`~PI%VST@ET#>w z{i=x9p%T%tVHeK($N%)qj!VJQv`<^!v^_KN{7x#J-xF`npOUl6(Pb9}ty))GNB-6i z^VwOT*6YUl^NNbu_VIaV-O1C$Iz1LvX(!R{SgL~;mnnbsDEHfB?vGRZ>A84DxN-jS z)!gFZJO;V@X#J|cHGiBB&R^oUY44w$XUlG*oy*Mr56l~0yVUg;mCO6p?MDl(vQN43 zO+4bYCGnNm3hQ?6w2tl@b5pyKCnJBKtY0P8 zAL?T7=zr_+kMqF!ga2v0IM3#`%>GZ&J4?I9sXD*D(0X6Ow+pnh3Cs7n9#7YvOKuqW z%?w+>tlJIuRVBXKx>xu0{BUI4Z);u+j37RnRqdUUvn!-$LBA^Tlef;+p{Li5%W-qg zG!*;eo20Awu4VE5^z1|Q{&D`WxQVx?yO&cP_@R_P2j#wk%>4x)ZC{783q0$u(b)xJ z{ULX^f4}tiu0OfnIDh;(+?+37e~x_e{8N^m|H0P=Bmd7|uav#wY+JjycBA5Am(O=O zY==Iop%rZMmMiRRPTPs&U&kK~o94c=`Mho7kJGit*Zvh>EEI^u*qOC1|>}tPVX1M29^rqu(PvUQvY`O!{%U8`99_)o-=m7Sz*g%iJixY~uX0 z=q~Wkvy1vhksj;;?O!^iWiPXt)|s*XDwNr5!{@&aALR~en>g!lU4NVp&L8|w>&5%3 zWvkpjW$F2!hEJKl(d)kwF=d;_+ck-|+^eQd`LvYnw;+deQTGwfSLhj?6Y?#NA31-z z*8kQJn;tgZZP`93K9|pYr@DWtIji3F1MT7)AH>JMTSl)`VX*V{$J5;B+T)l?zpGd@ zT`PZUh|}?1E|EW1_U1vCt@CXU!;SR_J)cni!YTL2*{7AqIvX_e^{l^k&3wiBL+(nn zFG=<3z4-jn+|Kpsmrr{B$@$1Af1GE_Rr&qPGsZgf{Ex_|%wMhI#{Z(>5$?*9!_}eX z7VWuDOW4Bb{NzE)1GeLx=Q-~@^RYAXz%;F2*x8_KcV4vxUfdA>C^Yr)Pt94u>NWMG zUvtqy+@~?@toVGo=Iv9#cJ1X5v36FT3yJrN+ux`-Jqs7@zLfiK zDfdpwJ@R*ZPrLZ474bh3jP>_i#g9b(kh^{PU4QHT$@$>?CH`RK2jfs*oad0sMmv|r z|J;1a@U@{VfAmjz z@%l=sF(|H2zkJg5$9drVnJqETc@Fu?*gv`C|6%&H8O(mXCSn@JcH-g+1fAH?#2y-4 zsL}7crfYjf1vv}8S~IB6OF_1G_D>KQ(7B{v4uj`e?bx z-{>3_+##z%+|egD;ZnHQ@2f(zY&k1X{tDFr-)usceFtLZ?kh{@IpD8S?w3&RIe%U! zHwE_mu?ej+BYy*bY$Ec9+%@{F#owDhnU9R}$9cy1)EECx*r&~3x2cPrq%Vqf23}}t z>-W`tTfn4QjY3Zi*Qy`eUVHV4Bea0Wzc=3OHQ)Kp(St$Tx~0yanzQn? z+Uoa@pA`A4v7(oIWrCO6vL4?-({HHrWYap7>P`9c-3I<&xrf<;_vg0V9d6{W6Xkv* z<(~6rO>j8duFd7iU*EO4ME;Pw84=z7*8K5)$@#OKkmo1zIEV4!<=H<^%k?MA%>EDd zQD<}N(+6(06*zFo>E9*T{{5Fl?B`;Bb)KrY#@;5Nrn6V_G_7pGb+)J-=j>Z=b$8b6 zF-W@{HArg}v)FcXh|>Hev2&>LQd#b*-31M@rYkLlHO`@fe@yno6(aQ@7enCCn*&t+!+ zr+xk{MG{yS%3PMNwsz;P*(`wrgT9zj-hFRsuzhclrrL!ZC+x-F7_7CkT(q^U_?$Z~ z@)LWX@Jep=_^0M9_n~U~squAP!Ou0+d+yBb9)7KZ`>n80k)Qluerz8Ue$93Dm$o8* zQ73ixffYOKeaaUQ{FjvbEtLC)lzZgQ@#uj2@QG}m{2e@zP2>-`Yq6*O-@5*Iy>b2$ z<$0w!U%dV-hvfQ{rSU(YpLX}8-&f}Tky>T~= zdG!0n+cxdaq-We+R~r9kYgcep_tnruSA0@c;fwkF#jA5Sr*WQX9RJ#coEG~h>Z1SV z^B2ws=a1uPE;-MMd5q5uTpIsV`|0!dYlG&9X%yQl|Iy2~xi8kWuR0gx+#BYn{ZeF- z^VQ$|w5^lgx5t+rs%`H&$+dDvMfbpyhwPUhRZo>aHD_CPw$wM%F>L7pU%hnE3hpl! zg}4(2uNCJn(IZ2&!gT&}Inns;xlZyg@g3fKHK_a<pMRdoK+xszk>iS5I+ zVgY+xJNj<2Ev>!R-h|Fy7Ouz^Kjz>v=gPR!wo=}y&tKG>O}^Due@N#q^?&o#!%p>b zf7;E<9rn;Tf4P0#OY@`im)=dbiM_989o@Ekcvsum-bVgDq}=1&HJWmd{CUkP;ToWv zzl@gWFUVcY-+lff_e*pBc)f94dH-~LZ0w(08vpbCsnh56#WuUx)7Z|4RzvN-f4$t^ z(yxW9aCd(#_rfhs-#+!Vg~M}e#n=05hqwLcTDs<%t9qgOT4-3lRO?Uu{KsqY=*&WT zshD}rhky9HV{P@t{+QS-NQ-Qr-5vAzvN#_8ysoBpXkxOx+$7`pm(l*Yui=@0%>K!F zFz1is$))GGc-Y8aqAc-0?d#Tl`uutOyd+}Eb_#^7vHg&*hJC8dZu7HyYl|9%JNLJ5 z>-JfG!oGKUKTWGP*}lb5N1Hj|wyWxhys7f1=8V^3t?Pw!Z`wZ((f+9``)46#|FqKn zIg0jAtFnJCr~UI76(*9YS^!(HJ zPrlw@&L4kY&3WSM4abg z*MW{XZQsnU82?P4ldFRoG^n0T>tnt1}Uu*1DXop|>{pW49>H0%%Yl=PS*-qa( z|3cRdpyy+vM;0opFCsm3cc%Yw`o0Fz(B;){zTW{+Pxsn>zHohgT!21-zm%vL6zACyE02G&^jv>< z%KQy#dp*_udF;#Sdk5SqqD`dh4-pQ_#J zo#-6o8*3YO*=<|)+BB_$v#sq{zj)^#18O*TZ%h6A2{mVfgKH-6^@mcki`YsfPS(Di z=r8g&{R_JObflckaiqCeJFT}b3#xt5&wljAWRbs0l>0Nv^@nYPg6*#^$N$(i^0#RD zR*^sC?(e?-!1>_(CAy9Eg;=UD&a)-EkvA@l>km(vJTh?baYYeRw$*H2xXbJ45_^NP ze$FcIwa|VYQBE7$@Ml}$w@bStzG$hnT5-#n{X1XR+56jrTqCBWI)71froEyi@b!l# zbp7G%_V(Ia^7_N1J?-6j#q|fn>Hxa_FzV-yLI0B19|lqGZ~n>khY$Zj*B>|!oIj4E zx#T=sJ~i^oC0~Dd+6?A1u0MFpCB_wqKS|dg3I!M1gX<56M+Mou>H5Q%mx7$Q{;+4{;mJb>jNNu*g|^aQ&g? zu|4*$>H5R|Mtfbj{;>0p-}i2z>ksiq55}9XKd3pI(7bH|Uw^1h*B{Z zXLKCPp3uC*P3Lv@C8uwpI`)3KTDt6Oe{~kwxX?ayUpafpjZ@rhrr2%s5{kRy+})i8 zpKq_-`lhw}`phz}O;?J$vsu|gdWMdzN&;VhsC=Sw0$+c4gRVbV8vbtkCH|ziR`5-M zVB1N${@^38Ka8O34>1$N+!f+3h`qcq>3Ko9{%~=FZdb2A-1!Gxf3V0raQ;%Fk(x^H zxG1kbJaqz-YZXgSEfwt0+llKBdd^#o^K>ZdZqs#{YxtHxZDYMNt`c_e{_xnplzZ)il5W__`{l>3!F2uM2YLPBWxD<_ zwwksY!LDwHR51c=aqq*cfJN6p+YwYZ?i&Nf3PO{1$AxL z!WGhGZ%|n7$=a=)hwc05`a|@_5w@F+!rlKmbHrZkZ7o&))STry+A@K!KNP3$&o(q| zp#`+xEk3Kz^#^xB`u^;Yy#C-v-=BTad9N*w{?|18I8C|7`3qlv*jm1lUA_K5wD|t> zpK$$w^N>;gIL{nsF6H%yr%vFh&+qZ)KcVua*Mgm^>HOvVwVnIk+%-(=O~0FHbM>li z_Psb4u0J%sQ`33%mAlT1*U#?lR4Mi6KQ(73^0rIh>ks|t`?JFIn~B6jHEb|K-=Deb z)A>s;`Tbb|`u@!8H}8G#?>4@l$w|3auRrM5%G%WH4`cp8*B^MlH0O`k8|R7lPsfk) z{xVq_*B_obeO^)Zy%X35%02^{x;lNo++MBMaM#R9LGEJ{yVTWNQnS6YkpMb{tP;bN4TV4nM0)TTv*# z-t&>|8eM-lI3YRyxpj|S6GtDhb!gkmefEzZ?0e|?!`$qZT`w23X~&oR;wrlEUaI`@ z{)wFNTEutRrD*?LPy1&bxqrSz`)2{#KTpd26W1RO(f+yDw14vThkx?^$@$1Af4uHw ztUZNs{o(2J_mye?45R(?UD`h{(*C)K_Rp@ge}>ThIhFR$rnG+s(*F59?VtUwrN4h7 zXS{#n`UCdQqO^Y=m;2{$w13v6{WDbVpT%hZTyx8N-v-nE$=4su`{&qy(EiEyYjXbh zzFNNDj}yiBZ-&In^(Ra5{dJr0!B2Y$>0;^eJDn%3+L~{Ay7XTgyW)u+biISS&X>`2 zT%_O5-f_eh>h+@E%&A|+{+>M9J@h9(d%jlJ?9f?V)d8f9QgA*{^q;=|`n25Ao{f zKQ(8d-EH&R9cMqI&va2*{^U7+Rm-=p&wkHu6Ko&e-N;||F^(NAx=+^jQytpT_b%mr zgv|YO)Lz>$+>^gXj^QGI$lcqy{@&lu^LjJq&zvt_e~Ay}{SUJAtiPw+Lhp7QS-)h1 zDsHU5S+xGViN^ZdCD-4{wO8vO8@$|qnzH_G%vtWA*81D^QDt{Ox&A<3Qr4fo`o|s6 zh5R^7>o3yh7e96Vu^wK3tEl~lG}hmCT7S#`=Jm(i*kBYwyNl{e{W(=M#QDwsND&{((Q_*n#!8g4SP9gGxK8{I~U|=4>bB?|`!Y*4%0C z_s-yNME*AZ_DcP^%KDq1+)b=Mmt23)bKbQ6YNWCLkh_I{^ZMiGO>q7aaKK=4ZkAIvmPyXa{c{s0ZIlcUe=f6DV+Jo*B z^lX)P@>Fu;`7bl)4Y92x8qa?@E1&-ow(wj3%DeOK=tR$dd5fOWvY4LHa^<@-{;yLx z!1Vl=XU|r0?~~7e$w_os<@qloD(~F^U9fqLp8rC6+|UQUfq%ok7o`1vm_==m=#{^sYu@N<+nfAAkK&a1(tJpYAxE|vB7lv#}O3$InkL%B#r*lnV8k)v)70^# z$3Lcev41Sj{+avXd2|CO4ICCew(o=?V}}eFGHl4WOg_LVl^^Lb>>vBX{;@p!o#G$2 zWUjOvG~$-@_~it3KISl~W&c>7{fm+PSsZI@Z2#e-hK2X-Keq3%A%i2vanoN~vVZIk z`>CxJkppQ|ZkLWQ@I2a&OFq&0!k?KP^yx4g12On_h~9kuYs^C#1jij?9kYJ#!%uepBIKVmkR<2X zFXed6`z71wXHqkt+0wD!h%c9{&+Iz;$8vw>pK-p!nle}VW}7U|Be-O3>G{WU>G_9# zVME4_qDdP*Jbc8s|JE@F@$t5M%w>Umsxa+Vo8E<(=m5>Ea<_Ra*}sp9Pa0k@+d;EpR)QLmzKWl=;R zH<4=GT2xf(3MhC{1PZ9Q$Nh*RpooAXalfvhqN1{CwT;z^wpFoJ!K&@G)|b}0&}y6i z{7&XK&&kP5a_;56AMyX1Hl8PwndkXF&pgk}$+;nCaI+xmmkzQ1{~Fcj8gQCXN_C zYS`##f7fgF?_3=2>gH@eSY_|U>iJ)FBbE>E-7(3hxAu}?z4o#jEQ^yjP6OtRnFp&o z%&BKv^~Cu^P3bv>>G$0THKBRLeGce9?gOFzpYXmd#v@91?78DdswF7e9Q*aUj;Emw z)Sdirj_Y09`XkI2D)-p2r=LAyVzH9hulYwrVf}wJcO9vZ&cB{-Gyh9l1$Dr@U?h%L zAH&XR{`4o-bv!vXNYwo>{|rVmj(&9h^?aN8*Xv+(9T*ro2Wf`c#JY~B%~px!!TkFd zMCj!AMvgppLQ}RIJ)(2^*nx)_-Axd- zQdx49dKRMRhr?elo*({KpC?|bU+ZHNOz5t-j)C*TZP&~5!%TF4(EhXDy|aq`NBur{ z?C9n%zDn7;jwerRo*!Hlt@m}aP=71y-L^{7`uG2s56wU7oqpe{>z&u7aKv>fvoGkp zFnKas*NM&_M&_8Y=bl-=4*SBojwgq@4jk+Y{#nsH3HF6UhBfnK!v8;#CykLjq2JsW z)G``+gz`k^g~=1HtC4l(Cu7xZ!(v|@2$|C2x{jyD|E=@QhW{gZ;)Tv1aK1^uUFVx> zO<7j5uIT(=zHnYVokx6q!iaVIQ|r2pCkOr|L~wngrO!7_zKEs&AEk!Kx>BS4=DI?k z!{?j9=MsHB7R?j(=droxhb=XpdT<@9(7yHm`RiDWM^leAp>?H?8qDiq)Ae~uR3AT& z@bibq^LTyzx3wOC^O-s&^Y3Q-J9}sSK9Bgc+%C~R;eGT;tn0?mi<_^9`#0xIvUTg$ zJ@MRlH*8WoRD5S| zyY04x9d_8Eu;Y$97IxZcr^3!V?;JcKqYY2I4=%DGHi!#if;l1w<_NhGCl+eVNa%&V zPMtaxI(P0|@-y;X(iHx8FYC05>@h59SDt$e~IMunqs9|3^RiQDN6zcP;F; z+ivEG*L>oAu%Rzl85eS3Zot7K4>cu%1DweN8FKl+{(xtHb?z@*g(T*qjfW^ka zc|k0YAIJ^l1sRan(Kx90M?xQT@3F@og+2G&GkDh2)D-aKYaH=D*vJy*;JhF|7>h6i zVxTz6>H>a1C-m>N*IvPM@4fdfgr95=^MEB9kMNvC8DJl=Kz%U=s(wepHuSrmg1sD(uE2)!4G|~0ejuMcMqPmwY3F2`5H&OA7&v(G#1ED zFmNuwfVBd-fSTYOsCbM-ETI$nVQ;_v_H#bNJYYdQ$iO+_d_);g7vUJFddJ#65;|ZT zc6;>b5j=bL>{*CDd-duSw4o3Bf`##bj_`bdAv_n%g>Vdz1M2l`=%8)bgdIG4_wF4$ z4>;g}!hr`KSm@igZ}5bSHuS+bup@q92G%JUkds)euud@sSf^oM<>qXRhfiUf`|rPh zU<0=L_wQdg_~3&JhaP%p;jqIF3y!H&s&LRj2Nlo{V_giWE6xLR5&H#df%Xwc_ziz) zw@;rwfvZzv|h7B85IOUX63WEm^E@U#9fDL29abP|mCpiy@0dkS` zh&)v13bR!lMq(^{kJ?TqlZC?%KfG|#NhcM~Ip>_hMHgM9{t|In;erb;D4c!v*@d6| z>}LT3;(+nY2j(R55%a(pa1O8@V1GdVDTbb~58pAT@EbAj*RNk;;J|@}8~dF7P>1LAHsIYwb^5BTq*=#m=AN@E7;XGhHa1NLQ@L#Qe=3GFnfjtp_*oPj( z9{wZW;opT9URanlYgXVl7#@4SJoXQx$0eg5t zMtoo!@q+I*`=~#}9=_w;(iM059zKTSz2r>&G+c7-)D}} z*Z(hnpPBx;zK^?c6lfh^=XHE}q`K&}AzVN5)tl^BiLWKU&z#raXXddgpYHGCgJ0j( z_nGUIZRSs<>w5Xb%L#AmJ4*gN82yeO&2_y{|LELB$EM{uKrP z+RF>)ojY#TIp@@`IQiru^TE_oEPL*`d)--QjhH`s)^v4lIbEGkPb=Jf^9_aR(b!nd;jX*p1jl)I+^WuTZwj8b z+;U^#=9_LPOq+UL5Q`gbxVmuNb!tx3yj-j11$V^kx2rjs zGw0^QER`3xD2|(^Utf%cnh(qY8IYqRM-E$tI&m?K9edWiCmw&W@c3g76nK2(k$VgC zAH7ep+!JtMJ`f8sAUEdD4KW1saea^z$W6o{s1cQ$$WJw|^KJH3P1maYTJ_|@ht+4t zM+5r{pLnS7xZ-(iL4%IRJ%tAzxU=x!19uhfy+_4B<;R_O&JJ?&_SXTX72 z+?ksl%)=db%v7KCW(4_w8s%IBwV~?sx}ZLbd3oG%N6!n#V7u+M?L2el)D^J5Wbr~} z|FPit)RT`Co_^}lfMbD*!J~Q}z<}IDEI1Fjd9#9fKt7;0l)mCRq3ZL-Dc1(I$+|fI z{PC;8xp>e)eFmxdUbAf3Q-vi<78Vvi_e9}Y#enCNPd;1##}nbXcvR)4VrWo#fLsV_ z;?81iq9zar#9)S6r;77{^`W>%VIH#C3)l4Pci=e2K&@3%pI`oTVY#x8BRG~Wd9v`_ zbB_lMu>aIkj|6jpybSU|#R7GKIY2Fh85o0LZBPu;reSSB42pRGhJgc9S=ztw!in=< zcz#jg7r%J6@QW9oQ4EU;%a%SB!~wa0I3ORuv2fu-K`x>`A67NNTnsqW+JJnx12IrF ziCpAbjXAhpt&f-k*gy5upUvNN)8c-m*0U9`{erR$j^&7fvJd|;A22KHZk@wp%lV0hvAXG}gaCl{%CKu)4Akq2NvExk^)riy#IM z6xSrIOLwVSz#etGT9dG6V_(2?3iim5edOQfo44(7=_Qlq(SIB<7cVJ}=T$BsA5aVM zAGWbJ1Tj!+6a0TntxL#*U_DUl680#>U>*otE zzr4I?8#$ozk#m7uSgiaHGav?7t6@J}3&=(Ik2-+=$N|(L&J)7^L6GXLiLC^6!QS&zbtlD=!50!GQSViTEQIxF#VE&pxZx1LZ%~0?Yx|X3PQn#~h#zgY$zq#kF|4 zT7!!AuP7XK)L~QUJCA$p(WO?!ausZ|4uAR5lERC>0K+rEnt(jux`_1v@i%i2S_@$R zPMw1|KjHZi)*qYiWKiqFeC7f3F~~#IfrfjmFHL&aX5|E)Rt=%c1?x#bp}skbS|wr$&V=y%Y8gVblA6)MJSU^_T3 zQT2!OfhU9W5{Z3~_iEjldCSx_XRFUlJMFY%E%&&l*tPZT+qci4QT1m`Re7~a&FiYb zwwibNt=iV8{btR@7oWEbpPBI4X|v5X?O@ZhE^UYho;X+Dcb~oMcG+d;T6|X8cH3<_ zcj~lDZPD&#O|r}Ddj9>*y{>oEuu)@hU2k1Hvya_eUDxwoTKDUE3;)tD`Q^B02fTOK zz~H;!<=6HC`?0R;E&5-+u19}e*QHwex?Y2hFOQsw=(QnU*JHm*d@XrhufBEhcM5o{ z#>ajxzVG_F9`mQtb-i(kpC`PUCSKPI^^eXSW468?+jtvvz;o|TZR&dO(QeRyeLD>0 zv(vU-?Pi;7(4;jjuAi`Z;9l(qjXQdmdDH6mSaHw!`>eU^+^%bK6Zc-zaN$0M`!Cq1 zFn!b>tF9Tg+lrqbv&(!iwG>P3ZkyL#aoTS4pYwYZ@O<{#9)%~btSvloMQve0wtL~x zOS=^wzIfll0~hUEXiyyYP1>h$@1(8;u=Lu!-JosSZ`3)ipW%MnZ@Ee5F@tuQ`s7vn zt@*``y$j2(?^SsIhF-yO$#p#o&s@`^@YGfN6&}C5d%*F?CEW@SDu#Q`>l(xZ9Jl{` zuQjI(+ic9=W9>#{dy9a#A7?7qSC)m!=$exsgX zd084YFE5H_n-ov zzrU?-;SJ5O;?@HTc!K4X83z>dK`eR~ma92gI=N?%7l;M&0vqL5N5?|N0I?v$i)t>A7hrf!<>n&Auu$bE za}zlcW|(m7F4pUMdv|JEyZnaUtKPq>f8m3B1_busyR(1ckBa4;JNgylp%@T@*VWu0 zCy*b%oVI_Elgm|pqBig>i9=*AZa!mOj zfmC2046E)QP*|;=U|6YQzm=c$AgMP#RCkh$bfuM9GDBl z0`-ZSg#STZ>bXEoAO^_AXbg~xcn{bC+os@4M9f9mR<%l`S; zz`_@g4=jAXU|?a5iovI99>4+n;J_2P2nMq@DF3l8AO^?<&PA~vigSQkfPKvW!bhwsKeSqus7*Cgni6C{GT(f>pc1&9MxR>LovYrAO@%f)TCLPR1CEL zhym9FtVyq+9#kBV1K6`+A2|@VAI<;KKkYPb?URQTzFt_I1K37B{$1At>Jt8k8IT89 ztAiM*T*N#e2G|?07T_7=px&dg4$*(u$N3xl#{)6=!@T~h{`J%$g|8ID=gNQNBaT=L zSP!g6tkEV9f_f;%fO&{D5q0qV6jcM-KGq@HzwUIL4{Y2yFs}b|!^l0S(mogv1I__* z0WtVQ%>&}_k*)=-1&II3xyAJeYjSaK>{GO_*CCuM!GG=pr~P>Qvg>-?cHO-89TfxE zW*x$RGY6myB5OhKJD_@X}e83zkG9_6<;qr zB&bE?0OtU<@q}$W!GSdy`x9~y_JciHpPOL*=|A#+{$<^QH77dnw9O;t;qAHoSJ8j0 zL#PGhAz~lofL;@e_KSNGV*m!&rvIov*q(gG?khT1IQOD`9KrC*>3vpwcwgNbtVdXf z;5+7@{ugUN?FTCUn0u@N$o~~Hi~AAwA=JM*4_q_pgwFHh+9t=oKia%@rrHuUFS@43nk&_3rroyRw3d5ZHCxr*&-B`QQ1;X=Q-3x6fK@L{ z*?$#mV;_Xys%?$(cg^gvdoLS%^e$67s?SbsH*NXPqV3dY6FhOQd_Yb6y4`o&thU!4 z?do>idXvt3?6g@eKC^A!M7xI_l}L0@7mK`0`#yu;AA3^;qAAw}4m;|Y5o7SXh{ueW zaPH{l|Ne|E{_WIty&oPwMBs-A{1AcfIs*AS-U)s;DSv1281Q(B#72pASfemhJ?-l) zeQWj|Qk8vIws;cpn!IxsLr#$>I0oSovgwIK3+O0ml&4h5N!A zN(P5NFSI|X*T;??JF)YAN?6AS?NLANHEMh02<-J(M`LO@?NLANWpYLT$Pw6U(!UV0 zv(X;)(_VwNM~=Xrz8jWZQ@mE%qkh`+^!`qcz+P|{w0L@5y=ag6X|G<}BS&Db1@7ZO zd(=;Rd2NpzfxQD<)bZPCkNRnk8psjY!w=l7TF zaI=H$hvpw5@IwTCh(J@%nY`CR_uYx{xpl%Zho)O1%D(>G%41Y#Mt^QyS>bbQof<^` zEtG8Y9lvpi|HXY^zr%Sd?YquX$^BiOr>4}eGIv&P<=c#W5}*HfbolS`J9Y3eV~3BO zc-p{WV@@BDXx6{T=1O5WGUu%lF~st-IR>% z@ZKugjj2B2P5i3l_w?H7f$iRYduce^hRppkxbA|FChxs_auQE;-EgPtlO4O{mXL?` z$>VDta%%O?gEz^})9H;zo+S^Cedd+&P~p_Sw@Pi>_t^gAp?$-%QqN1wIC&S_d5-(= z^S4MpC|6vh;h+ET(-|U8e4nEf23jc*6OE?=Y*r z%S?PCJl-GleCc-4XqUWJ9ZRkcl4tlaA3sAL99KSA$|KscN!9m2^3cACkLF>VWSm`U zY;EUxsolkk$pgm5`N9KD%pb%@_3cLyHG z<$;DGQ#PskE+!A{lgHOQ z7@uN3XS*<;X`ej4<{_txgYDiO)p;;SYa=`u&-{5}%85^qhxW);uoxEU`ha(t4+T^7xvEoETB*QNLMr9&jxR@j#z2mGaQO;Yk&Lub7;bSnytc z%k4|nL+)RAkMRliiwFRsD6Jckr{$cb^49*x~xJlWFy{f^@O7Nhm9^V!t9 zukDk^J4*9t&PptJ&+qQyk@z4^nFx>QEuAmgH$3Nk7Mu^0Q^wh)`u(c&V7~ki4>Y;* z$J6!=&-q0ja@r}R)$3862XmB<@IZqte>`oUJU)3er&zX24Ka8ya?%s=>Yet<<4w}> z(Oh;4Y0dPk9v|dSLx=~Xo%5yb8=i}G{>0?*dsXK_{?sp&_<#ow>wIbZhUfL$g5OnQ zoQfme|E=lm;)&GzymlB*k~~PM%1;M|ebUX}!}vdAv7td^DGgvrF~+ zyW%4}LA{4~pvjp(+CF)F{hFM13TbUPpgK=b?-3qhio_?vxjGPt9XNe8o$tG3br99uDeG?ze!#H6?rAMQK2V=;CV?M-#_k<}DA8nsJ-tTmL zbUVgYdiVqE@p&L|QZMo#f7V8Lpp*5k`Ytr_(e}yX>(_ovo|>ec=i8$`DY^br^Q5dl z5*tuuld7*=--z&dzt{26?HEz%k$3Q54C902kPr{v6Q`Y7L#3Ql6JCXQk`{eQU zYjR>lrAI>y9&j~=c%V<1N_l9XJlMlgww@Cy%dr7$+HLm+}stx7+PnvL5;&9%u^ZkLqjIL)s^gw^GMPbJ;1R zwf=B>d_LaBTgLbV`)Pznn6gRLH)-Od?HitViag{L%XX>Z2s;mYlLzw3P(Dj+@J?xc zdD=dCeEphn!iY+b%#qc3z~wz%T3^Cc%0v6)@&2IaOLJ9X!F!D{cu?McsJYu7pLXy5thC<$;jVYpSL!{&-o^&a67rfgF6HSpdoq|8=6|?}hxGl-EAG#PGnj_qjcP23~e} zYQ}@3gh$&akFQ^oQzBuPGXJvk4BceTK_&SU;i0ylJaA#%hPz&E&gW^LJl-dIzBCud zxsKj9cAgg(pVpr|!TDH-2fCO){a){tdi1HytCZ~&o?fGx*Jg0v0H^UR; zPlSg|<9~HX`o;EbBt8)y@2@&Ox}A1iNB_Teo@vV?e~G)_aj&mKoxptl}zil);LH>kz zpo{r)WQPmtUOZ%fsV{AxJidNSP8SE;{hXa=_==a!=l5X!iSUr=;=}sa_1np`l6EGxo3Ut)VSBOlKm^h16|CY zeK&ruZo=B1BspKSPaa?MFitKGw)=G#TY2WrnPu`h$e#!gnc9pURCmJ_zcKSg`-W$E z@%O*TNxQCN<{|s}82U3lkY|O?=NTKcM?dfQ+CF)_+?e3+Yve4BasM~}NOc}?P!#@L+V2sg#HI$>ZHwj1M`BR^9*2KW*nZqQ`{N zdjHB^@0c&uSJp#qpFF;PO-?sS@tyibcAmZ7on!JjsP_nu=*=coU&-ePk9U`jk8YQ7 zcFB9j&U50Fwo91L!G00qfu?Z&sJ?Q(s_mQj=+}&sokCi@rFNdx@7-wD!=T;Guy!MeiwLT_)aJAYJP$scW> zJidNSPO)s4GVi%~Wd92CC&DAzBI|R6$GcC*N4Lv3yOdv5ohQhj5Dzqk^GCgu>*3nI ziI0BGIN2$r)%)=dRlna7F4DLk9e0&s=ji*8sYI8bbNHXjI&D(Keh8@X4RB_UJmg< zQ#gNAU-`bMwr}F2U;8n6GK1_qZ++9Ibp46&2vatx`pWtf;qe~O@zL$}s7R}Sw4LXc zDT7P%=il!9QGF$Uw0#pF{o0SoQ*(@+=Y>II&G|;K{zQ1hqij<3HRl_Cl;=SmAKfnF z>{9))cAmPodzI!-hzFX&`J?)p{PDGY#>ac8$U{y$g|vEu&a$pQnfEp?&7TO5FlCdf zugM?UCy!4a%_)}cQsdEfp0fSx8+ZPM_b=KfkN2>iFU@7Akk*=GT|ANfE5aihO7}0? zCy%dr$SLFOQp0i8d4l~b!~;#?{82B>{zd!b@gC9f(Oh;4Y4uO2&J*lk5gur;?O(J{ z9$)j2Q!Lx1njv#mOcefe8={;0lY{h@vGc#rD%Xf8X2wB{W=GtStsbpML*h=y!Z z^)>q!?UTpXJmi#dcF8-@9-md+J}zB|eBR;-h&OCmCm#{E-fxu1iYuC&U9y;rtoUb&2GUwr_Y| z)bY{nb_!{&In&Ow`lZPJ72y#k_4)lJ*}r^k-|+mh$m7T4@kZHs%J#4Sy7Q-G|I+pi z&#yI)=9Ea-CI4&}Ph|g!@QAk3{ma+($>Y7Ec{HbtvrGBW)p>&bE5rj$;rvlA&HhFE z;Smklr0Q$-FWM)M_ZuA_%_ZaPQr^K+cfww!>raRW zn!@>``kM8J_Q~UG9&*|#q}7{Xk58{TZA;IeBRs;CO{%_T|Dt{Jc(3XBXfCmAmogLW zJTs#ESK^-F-_0Vo5u0#)RA0#-ZJ#{8eoan0g|yb6Z|8YF`uQuuBO0dY{%HH;@%3wR+9{+pbAg?w?DJQIN0>@Kf6+d9 zykzU(@7IizShh?4g?1kF4qrd4StQ?Qlh~kF>3%``OA1` zLp;zYOr<=uPaf~DMILfiV!?a)7(Cd&7Dae4w#Zby$4AGh5)0n*E^+Zl{sh-sB0Qou5+7~f@cezdVm@n58E2OoV((}J8Q%Gw~*3L7i&Gu$L4e}?#BO0RA2M=FWNWp(L9WkokCjcFSp0%7vNDl5guX6CRJa< z<7@lm@&2jfqua%@UGlE5^I#t!4@P<$xbvrE{n7Rf&zD6Wa@r}Rwed<9Ph|ax@Q8-e z`tr1W^7#5S<0RwkQq9%Xd4l{2@jz2Jf7DC!d58AN<9(&)OLN&Nq%|LdC&-@&4>VZ! zFKwSZzJ5(kv22&>ud&DHfUiy~&7Te3`J?)p^@sM!<9)5;qq*!9(%NvXo#$7Zw<*2e z65$aI*`(@g&R1!lJig{3r;M{p8Q;#+_{}?|pLaq$&=k%e)z_S_(mr{-wK_hU%T6J! zjSik2H~!lE{XNK^2#+vjld7-${axE9kFQ^oQ!Lvh|2lhoJ{UZvG=Da7=a1?u`4i#s z{-xui+wBz6S~JI`W|Gy&!+7&KHk5JJdBf!vrBmg&->Xu z&G%b^^(VvwP2v1eeU}&>ZJ#{8e$6=9DWtXjdV73kJo`*({zQ0$NnHP{?I;oe9c2nJB741ICvJ_yJzY86X6jJ*`(@g z)*sp@kN2&PkLHqbcByfSJwD5yxy$5pkUt?FXbR_#>MQxI?UTpXugPhrkXHXjJ5R@P zt;~D{>raG7n6gRL*UT5~lgImyj*sRN%XX<|s-0)%=i>|yMtYmL^GEeHJhV?9U-OXD zP9d#%2hWGk?`G;fSbrisq9L19eNDa7K6$+V>iB3b8E2R3r`hB4MQGywoe|fpyQ+4#j;&$aPX9! zKYQ-{DLH@E_Q~Vx*Nl^$LRvF7+2eyACHq%|M>Le4Khr*WyhR<0pWii?jI&Gr&DD8= z{0Z?uQ#gOrOLP8A`{ePTDe{ohP9d!|Gph3h>raFS8j4Jj_-Olv=UE*e9VfADm+}st z`qz&xU4J%p=a1@Z)*oNnCy!TG{P#GFlbu3Z>u<5gCs%mK)O)c0M0i9)HmUkby=(jA z@yVk(Wt?5|X4-i+to^guPlNmk@jz2Je^g)DPa`~Dsu&-}$xb1y4Gx~c54Jb+735EZ zN0_on)z{1y?VI>$9>z&5+ojB{_V^^1N4~GVnLB?}U&G^T`{eP`IzGDHP9d$04xY8A z^fc>pkUtR~(U485zGi)4ueiv>C&-@&k1%DEs;|UH+c!J|b-l;r$vb$;_OCYX{3+SLw0-jU z`ZePuk+4hkbL{a!?~?s1!Xw%u>vM$1`>~!c-7e$ol6PBmo*;ihJkS)*AN5l9FKwUk z@ea}R6_ckS22YSb5guVGjSuaU$JacJ6UJ3~c(p4_zJD10jZ@AS#&>)s-rt*OpFI4% zX>wL#!Fyhh>OA0@6ym`+=X~kkyCx6%;z7>hEBAlB{j2jJe;Oh@&{$-W`TDz>SpL5C zmFoR===9OAmOXpZWx;P(5Iy7lyyrCbhH$ZkI5<`FUPVzUe9+~Xf^U6BBUOIJt zo%HXJqI~B;-HnWXe6-#WKT2P&=d>5=QoSdfr+J1NxpQ))k*Nbmm!8$jN2MNaJYVY> zXXMVw^Nbu%&jlg7StGwbcdC)&=sEvOFD-u68oBYc1B@I;PsjcGr60WEV(r&JBX>?7 zZsa(6Rxdv)J?Up}4k-2=oW9|=6Z=E%oIEl8R+m{z!#W_l<9teb;dLi0EA~Ib$kRHV zY2>gjm!5H}>(htU9d%cV zTHJp188_ZABoY&i-1yo>Mvmxd)k-Z4(Yjc@{L1tTyS%OAIoZgs&b-OU5k0}nsGeS< zZcXDi=!<=GMt*f>gOMY8tUS1mSED-e_v)E)m6Gn6!K6cEA2`BBE88PgPT~FyY ze(c0y6Gx35bK1|(9X4^q_%TW>-e)RUNA&N~hu4w5vp-!7eRvpIts_P*z7_np(mEpj zJESOM9ibdj<4;dNv} zYX9fRU}Uw97@2RXW2JRO`gcfC#yUbdq^9CJ^4d$E>i7;#QN}t#IgTEzBWJz#8?9%Y zk+F_QyJ&Ld_v+;rgzRRGjCG_bJXlAhf315NQ`^8{Hv(^!PADHkua^fDfCN4%+ z>xhvBui`o){X3*6V;!L!Qd4mq>2~5#1B!izrYK__p&UmK){(8Y-B;@wXJo7+(k`0Z z^>#tXZq~?HN1DQebwv8t8X4<|v{&cBI`Z$C_vySGXk@G-(q3H;KG*Dbb~_!P!A8b9 zBJFYXoRa?c#fRwq_KbAH0XGHf8rG4v3-{2R)%9Q7O%l>iqYO97hlR?iJFMFz_7lQQ4Kv67$CKbNKqKPku2LqB;REsSZZpKH|s-X9Bo;#%=lf9bYd9m*c6 zy{@cLHMsm-=HD6WzuL%km;TMjYPfm0^en&ppLM2FB9WrpIk~Zs*@mM_&+6ryhWd9f zGR`}~<0-rJ{AB*;q5idL_P=hk6O7C@99??O|5EK;<}umGSno}9NO0-FdN18_M#g$C z?IOAKAfI+p-FP1~jA*5v;p2^q*{GUx+%ymuW&#LgjoAG&Z3iA9lqSck5x zLp1(>Ux#?VLHHUwhxEMzOLHjrdmYrMM^P^3P`JJLuB$$<4)q*yi9TmtZDg!N(q7bo z|GM;G9XkD2)RRb2#yUhd(tom% zu?|VQXmars_cG24_OhZ}+{@TrT@TNVc#lYoaO9BlZ!*EYrtke&nonY%Qgha%SDx5H zf2LktM;ZA3gsKZ{*X0DSzl$^kQ`#W#kj(;(Nvay8OiY z^v1_8Ydwh+W#kj(IC_vzVz+~lu|7$=XmasjeUkpQY06liD2tZL>yz}KY{p`Jl6KML z;%VviiTBur<)-Enu3_=sxyYy1>Zz`6Jg*LA4_xyoYg7#`KXI*2`paijT=S52kz9IE zH=R{C-n$ngqJADScI?D%M}|M8CW6nY@Q?T6UElrVy?~qQAMfqEzWdjx?=9R^|9G$9 zQvbr|g+;lTpW${`s+OPZFXx@b{A7DkMZ zxYSo@EPZ^JwFqB%A7oH9$ye2%zVaT&>$|VK$MU-P%KIm;@4oW>&FkVT@6EhE``TGK z$a_OOeLcp$pYUGK>$`uv*K||;Zba~dr_DA7hdm+@^`+U=zA+y^v|}R zr~zDKd7tZ2U!k$|@tyA{E#NEfp}oHQ>NnTd5i+m5Kll3XYreU@hR%t2@9(g$xbHVJ zm@;yhGT+0|^|=C{Po#g&$TtFh{40gpE)A(DTQ^) z`-z8r6w4xgmvsssc|UUyHPuJnqde>*?oUqdD8om}d=E!g{4tNxKWAjjqqK`Ammba| z?~#rXP4$uYJrDbcd!ExeW%x*$@8RfTgOAcbXJq&&?V`!0hd%PY>ljhN$C5K3<|^-{ z9`;czi}YRRD(~gq93Odq_OOq*H#@zf3?C`;Jse%}$2>~^oRKk)(k_}@dN_~jK6K{a z{WWzSdC&K-k78M*?|L42&v_6;?T7b~U*CP?eeC1=c+ObXBkxVW zzWdlw-%C8cj|p=g%lqAjeZ;-)p$iYn$W_Y4cZ&aY)e-7Z`sa*{dX#q2k>M{C>QRr~lnXB0(Ac zQjWuk`IY`TBg0>57fsdXH@`0ahCiBjGgkA0f#<7uNBZr&ttoHe`fX?%E(p9l7p4k zBI&RG?XlL0+N2yu57#2^AH{nYRGmlLUK6U#yv5(^YWqD&7RvCKGT+0oQf*5AT$(cc zr5r~O{mrb4zseBj*SoiTe*ZjtQ*&-l8U9j^!-=&>`sa)cf2CbCx#C8D{TA{!b6>f? zFOS{Wi)xR0lI)^T~$;ff^luD zanI&y(Nk+=e6K;;MUyMv@VzVPKiSCmUW2q(*MsjhFeZsa&dB&4pR`xk!+h{Qcdu{9 zjT<*&d}{nT!RJ2P`lif45Aq@Nukw5ty2%{#Z_g+rA1Di@JO7an(m!it$cmVfJi zN3p=j$Omb!t_S%b{cDYke316)dXNv&f3lI057J&;5As3ckTWv!LE5Y9VLtflk`JN% zBL8i9KAif=-%LJGMm|s$%F6j5{j({`$Op=C^dKMJd9aiId&dGJBOjz)G`aE$`5^sk zjf{Md_Ud|&57K|Kk&zG5UR@9JLE?}zGV(#%tLtGt?V_ntK1l!CG-c!iWzpjH3;7`ZCmR|0Anl^b z#e;m1IOL3se316)dYBL1KVzIf;B&6+{2{bHWD>>S1`MB1EWydT>7JwJy(x`>&sC{*5qY!Pq`EcK&&rCj0Mm|uE zqX+pQ{cDYke2{k0Ux+D`E|($Wd?eNZlKnO znO_FK&5r$|t$M0?U%vL}1NHixNKi&TP!`I{`5^tXDayzP%5n764$q|uYxgqellu;9 z9mvx~X=xWtE}qrPH%cG8^TA7sJ=+?2$ovjQ7A(qi9^ zMjkSMCnLwx(i|WKfAnsH@f0sT`%h2$7mV#OIax0 z`GEah`e%)d`jvLkX));ar2;l@BjM=TF)XQqkg4bG`V=jtzI0m zyWGgA-=^@Oex?7~RL7l93fNG;Umbp`i9d;4JgDD){O&B>zpar`ztT=E?)ad7YlqTq zM0d_et5|E!TwztS$6T=|LmZQp*9 z`MgFM^-Ec_xOq^&HGkr7b1yP7>Q~xDlZyxSD|VL~8TBje)%Bo$-~8_$+OM@nM*T{A zbv>xxQ;r<0^|UoI>Q~yU>p}gVw|zIQr=yWkztSE@Pp9<%bnL73bTKmOSK8y~=^Bbd zt&vf`O^FZcSNcyjGU`{_tMj0KC2w;^M*T{Abv>+Ke_iTV{lin9Kl{1z`W?RF<)tK{ zjQXX__i%LOIqFyXXN`>dm3GnO(u4Y4{rPtKd~AV{QNPk&T@UJa_fOB$dKMWO^(*ah z^ehh9U2bI5Z&P?szZ>7q--uspWYn*;SLZ?fKK`dx%ZfeQ8X5H~?bY?5e!KUs)p|M_ z8TBjearAUbx7w+T*3-qvs9$N1qo->q4z)%`{Wc{&s9)(n*~qA0X|K+M`jxy@zY(Zw z1ocZc4^ ztj`A)q~3n%+CWDA{$xN=7A@|$p?;&-ce7EO zXusAP8TBjeqRGXB`hEY;8Lg+Skx{?WUR@9Bx5qC0y_=3kM*T{A96g=V?_aWu=ILT& z)UUM1(bF{)hgu_}ewz{>)UWiPY-H50v{&ar{Yu{EjEwq~_Ud|AzuuQIKL5J@-l5E} ze$~GzD7$|3lb&DpCkbWLFJ-=mqbsjaztTTzWYn*;izb(zVE=ubf1h2D%5VKaz=rz$ z>iPE#CpEeBpnkXTSL*(YjEwq~c4~3^h597}>X$O=m$GPa>p}fqIAian#lCBejQW*! z(d6$f)0@#0T{&{U;k4^(*bwc~HNSw>cxDex<#-9@cMWUFx@RskwgTeSOh#e*cZv zpLc59OP~KF5|mNDl!elrpQvBypEWY-SK39BD?d@cFMAU;&jKT(ex<#-9@OukzfaJ5 z78x1!EA4UgEDqUSZe-MNQ+QCn!w=m@^Q<*8>Q~yU^PqnJ-Trv3r>&7uztUb^59;^Y z4|`}m9gU3omG(G#I;Drbe2~`D#mJ~%X^*3)YbXx2Mn?TMB|fNM=|9=Xs9$NX&V%}u zyv-RI^(*bw^{{^Zb*bO?E;IG(*IiszzjNoz(&s;k1ZC7OWudHGztTUOqKx{b97hl8 z_l6$N>wR;9kx{?WE}C5H0P6SfQ{FcFFJ;s(WzpjH3;VCwU2bI5ue6IM7Z2*U_sPAJ z#hz=8jQW-K>UvPW4{z35>uGCb)UUKx*Ms_9uzPo{r=yWkztSE@Pp5R-k9OC3x)>St zEA4UgbPdHp-Fr{R3iV4_w7BOB^(*}+8yWR0?V`ywU#MTnTlH6NT_dPp%A&>1!}`sy zOZ_TC(1YJ_$~;v5efuy6WqjYBa`@=-wL2}n__wy1{^g%8_wR@v6ZHC!NKi(8QjVLS(m$7`jDHV8IgTFWS@3&mU%5Ze zN`BhDym!WE=4$<-a$om)caGUhDZ^LFaTrmH(m!Wp_$uw9$yL+zHPdWgN0@cTd#2pi z6Q{J*p92#K%J7x45W3e#_$vK#MuxA_E}E+O>Nne0S!2DW<-V?d??!ztkw{R6uat$b zvaixVm!=F~DaX;ndCfQ5SIOZ9i?3rIPnmk93|}e7VZ^*j|D2KGtF()zYV+!SU8P=o zxc{{d>(zU^d|q!j^$vaQAd#R9UnvV=<$0CTlHd#gr%J7vk-^0#+3C85zDxyJ&K)=kztxY+ogZ8-7~uYi3rB`F9$W;VWe!tURyM zKbNKqUn$4YLtp)7`zq(dnL*{gzV%HTv%gY?uax63VqT?x&dBgp+C@{fdCfQ5SD9D; z=yG3gnZoajB@&e3D`g?9Jg?F}m!=F~DaX;ndG-EXrCwv457!)1?&}MK#+uJ`l;JDo zIE7O$)e3f?5RBc`}&G*%;P4&l?`&##QFH^6S;VWe!tURyMKbNKqUn$4Y!+G_a z@2h-PA9PmP{+fAjb5pOB;Vb1hjF?yHuhu556TVW8qldocTfo=Gqsx6=FVBa+an~!( zhu!5W*I;UnwfP#3cu~f@mbX^UVd>uj=flnRRrZ{Q@)+Wkv^l)wRzIE5@QKQaKzaZ`Y1PdMbTTR~L z>x?rtH0Q&V;Vb1hj97=If6mD8RoX>U)%~>veDzK&pVw90J~sO+W%x>22rJL4^v|U! z!&l02^l)DN7Vx#f;_JOb<@XgS!&l027%{KXKWAk4D(#}F+Pvmlz}K2n%I9@v=zN$m ze5EXemFHFZ=hBqnE9E$PIIrG+s@1D|hh@EHMwI(H;KecKe3&wPr5uM5^D6ywMuxA_ zE}E*%Yo-N!^+#HK?YhL&>i{hWzJ}UK>0%6gHT{*ZS`K_Q?bPDV56-LK0>0LqS?=rV zm*jkyGJK^hglHE6#^|#<&o=SP1(eD#~{tDGZd#(qD1&9{KB{ANi zr)A_YWy#ITIV}Cv_m{LDtWA{T=;7Mr72NfD%&0MEy3dAXUTc0{?(6x-wlU|!l;JDo zIE+|_rGL)I@KxGHlWXsyubF21I>uas$yzEY0Eh&GuF5)xWUZ*RU^? z;Vb3v(Up7fRr;;lIbvs}sAiG-UOUC_BbNMVzS(DLF8Y4>=Qq>8{P)DaMa}fD{$ktw zt%vuEY~|ihBtI*EU)H_YDgARshOg2tnp`oXubCF`HE;3t>bG_=-wmb=UnvV=<$0C53~ zUJu>CU9ZLO%eu-{)~3cQZN5spDPdkIhmWrLXvuTkV1I44ud=_^T>bs<)o%e`^WPI+ z^DW?O{WazDdcaqwnf;YAa+q@59G3pMG-a$!l;h~(+T?BMuGixCWvka~!?oqU{%Z3! z=I@P^;Vb1hjHp-XpEELim3GnOs(bpHX#rm|ez~uW-@Ie4hf;>Gl!dVJo+JHpY0B`G zavVMM)o-@1a?PR9;_HqZe?5R4l;JC7zK5f0J}|G+KWAk4D(#}lrH8)eo9(Nt!~S*U z^ZLQyF{WNA!&k~eSlL(UpG#AQuax8Hp|9RXRq8d)`S9d&UqAoT(a({CGJK`X_i%L0 z2j*4!=Zp+rrCl_+^w8H#^L;hnm(5#zeLuUW`8SZ1;VWe!tn91w&!s8DSITkp&{x0t zzM5-j_1Blr>x^fgG4)CrzEY0EhYBdR312D4(L-PJE#RwnL%FY==bmipl`?## z9ETCUO8=aZ;j6TZrfNCtZS1aBe*ez3!y;bz{Mumgb%FgOH+ogl;h~(yyjcL*P5y2zRvu7oT*pJ@Rf2LM$D`9SKq(WI^iqj zIC|)-w@I~nb#Jh&*Sy8o51-%7>^YR-E9E$h@KyTfj0|6;T{Kn8;Y22rK8X^v|U!!&l02^l)DN7Vy=ZUheCS4?JY{SIY2}avVm?tMtzq8NN!p zXsR}^`4;fC!Q$(BdA-SV*K6_Lqqufg`J9uvseE1~+9F|I<&{XT`M|tNuXX#|YH+=& z*}lrPHUH-Chp&FKeU8NO1EqlfeAZC1Hn z-DktHHZ@v&U3*GTbKXQ5zEY0EhfTsdCw!$GM-P3?G}~9HSASOdy!M!wHs}77 z;Vb1hjPO0)gzOI+&!)@I4TKxAYuKiW!wSG?dyoTdV8S_dxe01d=a#;Gc!1-{qeU*Ck zZu@@t>Nne0*>f7cC%)#_(bu)D)cr+kSDN3B*hD4W)vJdtm)sLcwb#ijkz6^vbn5(2 z{|+Yq?>wlxkwuGpjXUkdx={b2M&5FprACgU=ln0dP)*eux$(6Fj2uT#$Nl<+`VTbn zod=z6;XHeW5;LQ-%HMn77 z5yK|dCu;YLJy1Hf7O_;?V&I0Q4NZ9EWs3KL_u3uRz<=A-gyE*^Ppvyaam}BkdeKfd zB_nRWwrdnr5xfuP1oxO=w@tOM!+^b$;d!Wkwz#*b+af34d-vodp6L4G=-cX^c<=CK z*n_}B`{eNs({YH&<$Yx5=~dg`@PKPpga<5m)FgMTn>6IbrR1S~^7xvEoW)V@|9XG1 z^W1b|-zAI>63Bl};sb4r&lO)hS2u2}zb_^a?UTnlT*pUqxk-xecz?C?{C=~C4G-pM zQG^Gh!E}G>jk+U-bQ(Y&+9!{%dB|C`>i+ML*S-kOLD_>b*>}}tX1?%VV~8i5Klps9 z`bz$2`{ePC(DBjjwBtIib?^*7=Hq9`hGWOYGGCmlqRwnm^*zwUN82ZluU|7xE)KS5 zR@me7QoD;6lLz^e3Gra8FlCdfZ$I+TK6$(&b$m1z?YfSQ4xW>5_)|afAb%!Bc%ZMy zlufF>CZB1aJig{3r;CH_ncIt>P5GLO@ueqcdXoq5HE15RNo*2{(bw;G_ys5D7n=BJ z`{ePC((%#lwBtJZbL~9C@9MRTJUFfl@jw@uzNi^c_p^U4mip56$>ZzSjFXFl?KOAU zdA96-x#2;~yd}kaCL_j!>5J?ClkCvHRd7$CV1178ct6qc(e1SBI_4cbdw$uqq~7~z zp6GtD@jqXvoAK}COuf^-iI3)CoLn4i&)hkoWc^|OY<6FN6CcE+KElKCr{wpkYj@ug zGhe>8Paf~5IzGCc<6Or^2aoDs!?^+DtPl@$F@K8J+p_mFpJ|^wzUCn(##VaxciH1} zZNGL;k_U6NHo^m)V7g}L$iuJdcVjQ|&^~#*X`eie^BZzjV!?ZvUiqLu`@uFIU1z-A zlRS7`vsCg~VuN?oelg>OC&)wl?UTowuH&P*DzV@_Z+|;aVdKA;^%?o&M|dz6Oq;wlCH>!x&y@99+b55&Uy~E# zDm@w@Vqb*$-U=jpvkX}$O9yg_g;-f+yowEE2{>HeEYz0*E(>{58%|lKX2iqHxcAmfg<+#U~D?zVDt#;Fnu-s|{V`+QyU??)Gs2lF)~!h^A#FV$DhpS6ARcyn}obUVgXdiZtr zdf)bg|1sASFy0UGNNm*K2=}=zz3m5M%=HA?Cy%dr7^g}sc&|QX=jnImTvP9e)1nX$ z#*t~+zOUEyJ#%MM@3c=I?=~GD&4sa*9+|YA=i{y?n|#K6HAZ-#6HF)ne(TixUEl4` zdZ&Hz_?m~Dm00jz{%-r{uan*!Vm@D>pV!X5pQ650U-NmH_6^VNIzE~Ux+*>D@3Hf| zvg>qny#@1?iSUTdY*O`olJW7iee!s7i#+6%ads(lpPlFJcDt6Whkl3$nwUR`kLoMy zp|(#RpFEn=P9d$04xW#<@yzvvU_XuU2vatx`pWf|2#mDZuCg9VY!Zo051y93xXbDCc~ILo@zJjtCp(3-dIRk||eA`9$)kLF?s4UcAmuOtqf1_c{#+xxx(?9_Wh1{xBX!~P5x;6TCPt@$T0=n$u1ptql&IH*VRvG=CyI!jw&_zLG!MzTs)mJepH1+a>RTr6t$rH`3j@ zeKjG8?-=soZE4S+gCB2`+WMa3P5yY=K6!lonsK5&*U^8_&NHXy%_Zj>nFtT{9sS22 zrC0o6o>`x1pFG|JI-fNc$GMK)!*-sFU%cPUSCBs;9_V8JynNL|b(@`ko#CNvK@=As%Q7=a1^!pY=}rZD!Xr%Cr0OgCm$pwHZtW}NI4(mKS+Q@USdB0QoYvOY(6yxBTFx?RTE zrG^+hLH>kzpedX`>ZRFFY2U<0^Ds_!3Ta&%gD1$J2oE$^_b+XqJl-4~AKfmN?b57w z?D>QHvX|t~cJBO9eIk9V7nk8YQ7 zcB#)F?D4_<&B+twPlyMa!ug~6n$OF$Z{nkQ7$-Z0wE7Mn+=HAvLHEOYA!pVb?UPpKSsJ@av+CF)F{o0SoQ}ajreBoZ+kzpedX`s;}gawoe{kzb2=hLR#|<9^4Cs zJVE|Mc!VjNRDC6XB0Sz5IzGBxEZe1y@7m|9Z2#Kcoj)b}m$uLNc=L)pjFX*0TI-!W zk^L*eBN|HgFWM)MuXz|J8E2Ok#o!6@C&U9y;rvlA&HhFEOcyJ$O<}*fmJGk>l^)>4c?Hiu`b$m3JokCh0 z96Y$!F?n#zM0i9)HmUl`{-y1c$Lmw%A*YP9OKTlGxF0cjg8T{bKvOt>R9{(tw0-jU zeKn8fv{OjytoQBtgL?*(C&-@&k1%DEs;{g++CF)_gYG zE_s6d3GqNvIDgcAbtQkaee(GFHREKbkk*wB9^Av0JVE|Mc!WuPet#*ne?@q_{yILo zT`b$Bn$`CCD%-zya_3LU{-y1k_~_S+lbu3ZCpmd!eGc*`!Xp|=_b)%f;|sxbsK#mHg56$>Sf_H~4qyskj7?v+U%95WFf(U485zOsL5`{ePC*F2h2#@VIJhxYuz{Vd57``bz$2 z`-bPlA`jzar;yfJ4xX~lUlAT*D*gQBY5U~y^=rmSEZe0<2M>BPK9JXj{+=SS!8?)t zBEsXHq~}YwV??D#pO5VGg)!v8v8Ids{R{62Qz;MaoA_uR#tCC9J^UCv$e$q*9_TDG zmGb!7K6$*8b$oPtB^JE5(#aFK-rLDt?IrB%`H}TQ087Di1wB}>*1p7sVN0=hiFn(v22&> z|7@Qx+_#VQg^^xocmAlpvR`QXCO-PLACqU1g9rE4BM*+52#B{E6@gQ#Psk z8XjNUCy)2~(Z&5;w~J-FwAR6ceSka|>HWx^KPBsrwoe{kzb2=hLRvG8_W42&^5B?> z@Q8*;eMNY@H*|b-yNt6-jWKwF{0Z?uQ#gOrOZmK`?VI@M*Nl^$LR$NLQawIF{zQ17 z!McA%c)Z{0_~>@AY?oF#cyM1c&KE{{ySnp7^_BHU+c)viul<-jHGj3o2lpN$Pmn(m z9`P=lRDI=qHNxZlPRB>L%Q(9<$-#sBgOMl5pAZi;h4V-CmGf0?-^541_G9wo9Xz;) z7kPsGiSP(hHmUlWzrXtt9`E-$KDyl=6=|*i)IMLh&lY*m+uO~ZKdP_fkG5~(qhFI# zEZe0;4j$YKi##}HB0Qokn^b)d;`dwp2#@!sj*o7aadyf3n>{|bUlnU#LI&zSt7ee(F4hn!;BF3tMP9v|HI zi98tTb#dp9>TB|c_Q~VDt>;T~*(s#8(ZPdzGm!_!OoT@?WRt3|S$}AsJig{3r;M{p zeg4NDAKX8QJVE}1c%UhqKdP_fv$jtjZ>5fpZnslNtMA~!J&wo|oIk3sxqe9dkc22YSbAs%Q7=Z|_R=g-={iI0BGIN2$r)%%BQz9c?D z{zQ17p~w`8PlU&NU&lwci)Fhs#KD96v@oAB(%aLWKdP@;e`w#tNAvhGc^Vu%xR(lf zaLhz_#Jg-#^)>bGYy0H!R_XZYb{S`v);f4_zZ3EV`4i%Srf~kKzOtWc`{eQUYjWBt zq&4%oJ%4ad67mH36X6l2Y*O_#^F{mQ@e;+~S7n^UvR#_x;K6-E$b*qyjXQr-U&BND zTC8>+9!{%c^D@%r+P>jwTjXJ!>=e>E=?nXOp$Fp=7!t73I#!8&eP0Y}*7q(?k7d8F^Z#+l)*W99_S0KW_Eysilu}({^*I&%W*+$kRIA zo!TkAUu>RntM3W*Z!q$-P7fJ59?!#}{tHsi?OtS<*6E4ViJLZsXJHDzjb605$jH+= zJ!j-NJh;A8qdM|;)iDP3Hg4>M5#5H59W!FWNxSNMc8ni8F}P>PX+J-A*u)Xz$KduI z35*Z&TmN2qIKTVO{?yzj(8wykjSOS@Q7ON-?0dPs&()yRFSh+#AS1swe=ZSw{*d1X zf7(X(AD5bT>GpwKcj?8c_qNzGwjSj7@h@|p{M5ZO4+_R2zrQ&B$4$^PJ(Yg)2;F~{ zk&)lh&gfLm@1J%*K&l9Om z-;gtoo`os=)_$?)A|oTerMViF+H3qleDj8QSPA~S`ef&#`UPNO(pm2r?o!V+@X zv>62*ei@roOFKyrI1MzW)d3%Nmn2>(r=kY9~!_ooT%^r zI&QM$FO%o$`lDxk;Vo(8sMV-%Z7-AOn)f36?%dKyXpIQIPcgK@!0_yu!=WQgfUlWDu z3H81C_SZ@LqWa%9-69!#)b}4=d~oSHKz;x9&sR!(Y&Ggz+lzS~9lNx??a_w|&$9Yv zzyCt=x=`Q0JpJ?5H)aaGF5I7JeA{Z&x3)7&-g$C${d+gONM6^d)u?Z6FO%n*`p9XA z%Imt;YSg#3m&tQO9czK4kt?i5eQSGh9@aP45y27t)_2n!!}UQ>-#C7UowvOcYl9e> zJg7~3gPQxW4UHf9xUL9dvwTO)Kkc1n&9mVB6Zp7Zh|7nyLXZy+YVc8~d(xjpd|YP) z(Sz`D{ScQAYlo158hq5;hiwrb*BwFZAbea;#O1?UBFKjaHTbC0J?YOPKCVN8=zjSw zF~@Ly5%A%C<9d%DG-ftE57S=jX*3NXzQ4WXvt}x7zCn(uQ%`z&cdOlep**PLZA~E8 zH^DL4*V$M&?>*VI`|#ShR*C!C=5DsWocrcIt7yD7YUJl5#Dv#|^-d(NPL0<_%^cW< z^6<5BtrWzTeQjL##C>fzKKmGC+^}|vB1QHCzxJcx}PCH97ZWEtKxp=@O)RkTJyi zE|RHEjn_%-_EPjZxi$=r8RT_x&6nrnbs8jmOn^t-{7W=mCpC8PQS>^w<_zKod7WHK z=J|M?SdT_tN1Yn4lbWv)+oIRWwQLaE@9TWf9K$tg@H!{s-!Cs`t(j-~Fzr0ow9%*# z-(O$$3n#u-<3V?xeUsH}!#0#>#>L|`te2CJ;#x!9KDpPwRHx^i+n&#ro_CDLd6(++ykmQs13yEr z59b}$K}jPkb!zxTty#i(P*1y>K(3jDV}d^UuCVjYoR1th2e}?nw`a)M{rJC}tFTr} zN>iy*!!v4^G~_)!47fSU)QDrBbJcXVmGw^k zx>9SiXISGZb)-_KhG*32zVv62XIvi((Sv%%wXfD@&#(?x>PV$d4bQ04ed*6U&nhN_ zYj`2r>)Fx%`v|VdwKn^N^}+DEhRl5gHGHB@_oY9JeB#<-i0;QHt`)X6`-FANQWq+9 zYWPH*?n{3b`NTEP5Z#YYT*Iu}C-*v^>KDv?gsknA>yz(3!W_VBBG*~#_6!-jA1|2u zhzanFYr(C}o?%_ObObAPYMhVM?&#*7kGNlr2lZ^e)*08UTbn(@+IjSh8lF-2<5_1= z&*UB--%)T)J$QzB(0rDDC)U#=2Q@xpr{+Fv^TzcxWt5`v6-wYjqCU!wjVaU7w6i1V4H0Ina{OXnN82bw9CC@_pT6|`CQ+PukolCJ@o~v z*@kU6&%-sW6PU(U>eQ2-KFaDcc~B>qH@L&p;%*euX&zv`ZapEdxbR& zQ$H$oYIsGR?n{4$Jf~M&{}9pTygF+kj!iy~mNgC6X0NcmV(LVtP7SZ9(|zgBBCohM zBcjWBHI&@P;yWj*acm?(4-`j>wA#HC``ux+nda_j)zHQm4l2r7n|)ub1md z;+Qpgy<9KSeZ5#Kk}TACz0}-?ZQkqE_)47`ua~+^9==|#SBYc#e!aWCuZlbvU#>^# zzFw?FNfv6nUTW^cHt+Rnyg6qjCtfdgnLK>G9a#?($MpSro%aV^?-H+fviZ0EBTw7c zirjpdc6&~<4cqYR-LIx`ZL9A*`_ESE{&1e{4t!Z1Yj38pHLD-H`JGmm$phbT4duF> zI40;@*In9>eb3IdIM-&cur6roN3Bi`uc*^~>Ce#E(JQVgis*7)>GvdDCvN+CKcfh9PSB zMy-_LIS0OJeA{aHrtO+3&o`|5nnu>@)bNd3vxIr*TXRi(Gaa$sxF)RY+s{tj$iAne zhHunmIN_Vdx2=Y6+OC=Me8c**X=JTV4d19WOR;b91o_6bYhB;&UATcgPpRP>bs0|h zrtxj7;hVN=raa%UjxK$phHuoGrPw#F!HZ)CJx`N+W?5qwzTrKBc|Sge^>vXK4{H2g z8g;rS{TX__`29O4zD|wbW1=pThv!!$YsTW3?CU*l(W3c_>x<7y-e3FXC36h&;PrA1 zU-$K5{a)n7gBq`wI^C20%zM2~e4QGvm%2>q-NE>B4Py88V*O#VP~-Jdb04;Ouh)sMQ{(kgm&wD|+nIpZ zi_e_7#xY(m&I|Kd-NhS+5 zIH|d>3}KIW<%0b=mV*);h)!+4J|Xh40S4Zq6S{K6}A9a_wn%9M}KgHS&JejIC&lBehb7 z{l_?J{Fv1kM{U~_cTkI#VJBO?V2f64~riCQ2h&k zWPEHj{?D_v7w5tMc_xEdY24~c^>bEhmasn@$4J(4$1%ZqHFSS#j^Q}Q*QRU7yW@Do zo`012XK08T<49eG6XU4y)9Tb1N9rZ#F8hjQ)3*d zHA~okjHC9u%xa9Iwri#k567{&Cgb?rQhT2hUDrE~+m7$FiX_w+M{4fFHpGc>)c9$2 zYK$XwnLHTBcU-c!@U*PPIBL6Q3i054JZ}%?iLJ&sYI|`WjHC9u%xa9IwioB&IL2!- zj*pyg$1%RXcO0)=zRcd|P-7gal`?-EHGW#18skV^CJ)AO_6>*0{Lr!*sFjDu!*cq5n z$GYQTTyS08-gi@jk-8to=;a0VxJUTg3fAM4ANKlq@$App_juG`r0$2YIRT6#9>%R6 zJHwtc)L^9UhcOfN&lY? z7`xlky+w0R^bg^BoO#NbXYC#=M?Gg|c^O@&Qk^pPwihur2WA|yK8`-mjPbyX>f`F? znXxkgjLRPCt;fYjzSYi;)EH}Ocf^avI@&5+j|VT9H-Aa69*6Apdfdaf-ZwV3-ziap zk-8to=D>_P*3*B~>tl1-u>C%X8jRHaFvbHj>h(JIFh1~?P3-(g4MysI7&`+qs*ewP z7%w;L+LD7BjMUtRZDBo5DX7O1*X!Z`?j7qp&04qQpavr~_myF64$f%LnI#^^npyvr z9MoW>=DsqF@!*X19=GFR#G1I|pavr~_myGnOaSAQhkM7m`?|b_*Xymr^@!_rxE%Gp z!ibmAbt={HvA4DOoM{fssB_PX=b15{0LJJM-&prLHmHxCff;pvoaJG>=tBL!9@Jo@ zc3(l^{P>D+Jsy7gJo7&^!*`Ip4XC#)L^9Uhp{sOjN=~0E1c^!H5jS;VT`sZs>kpQ$$FgrlU^TZ z+`YhFuc^UE-4A1P0vKZ-<1Q<0JaTBOb;ppFGaKpP>dLwfhQ+&Y9?y;d(s!^abw-U#->06&}WSAG3+QUQ>gS zx*x{oz>Ev*dxhwyy<=TFekVIWQiGAYAI5lKM!mv6S*@eb$xH8mKi`(bPj%&24C{GZ->JlA=DO$|os zei-9{8P&&S9!B?lG&LBh`(f-1%&7G^?qPHpsKH3>Zu92HiV0j@_&sL#e>AoCjF0z@ zuiG#6pj^YUmKXB)zsK}*#m_U>tO2@<-Zyb~FeYXqGE9v+5p*QKKHI(@HE^kClRY z++%8}AniTP3J>GVO}4i83)Enw?uW5C0gTa;y*^&}z^m+c)6`(3?uRj+0LED!#-oor z!!lBXk-8to&IB;7_ApN9`_fm3>oNV_H&l*#&NRn+$J+IY8a`6H+oCzVIWVKnkK@lX zV>|(j(|`W_`q-HO#s@u&Z~D_5J3mrmtf~7M>uCFMJ*MCL7T4qOFM56a)=M_ApW9J` zk-8to<^(V<@i316J@GwMBd$HTbI27jJG4r(w`b04-v=XGabMm?`b ze%U+Lj~%OTGU9*rHjMV)w#uLC8dl;uInq=o5 zYA{mw!`PVs#s@u&S3I@IGE#$)x*x{qwMF$9o*-F|!@ubr>;Ji92RrvrgOR!)#^wYt zF7YrPaQoi&vu&%ks6HD?kgy|$BlLj*JJuUTc{rO9(UBk*naLIJNHn7k-8to=D>`4&aCz@t~c#x z_MUt!Cs>X`mNC~7cL_rn+s=htKS zYOUwYxQB7|+xE2A8EP<6_rus6m{IF-`tN(kdXIA&_MUV6nI6TtYOhjBu$*E@ylG5x)m(EO-AF8M?6Si3$^!$)d&TQr9^2WHfI>^#qm z@xY8a_e}ZY^Xp@04H@GXng5UbtL0@62lG?P}d7W+8hQ|7D zFWI)HaV@Jq_{aaSTK9+ZEZz8zwa@chMJCDC`h_kjA3R&Hd^ZRQH`R4+Wy ziEmkb?d*%JW)5sad1hYqvHF(B+$TKC>RaA^Pogi`?lbi*-*lI>Yo<`1MUQ^Ae%eWQ zOMKfNd&zcRsNb>WoqgoFy8gun?vVJ=`sI(WO7tb$eYJk!Q!D$(b4`70lUpVJ+WKdz zH%p%D>;HK6O?~9Kq5igi|Axe`u=W_!+7p)O5V%t0lf=k3~(dUc7^}BSZQK zqo%dJOrERj+pc1s(fTdFPBm(J)~m|Du0@Yt zQ-9lh#$Q`M>C#kRvfcId6IQal49^Yqt-r_k6;`9BwY@kGYkI{XHGSvWU(=`Ga;eP6 zm3rmcPbV5R{lhO_)z>_&@hy8SYFgWiy+%#{=Z&XHzsu^sd__IcsOdjk`i9axsOe{J zt4Vyj{*u#omposnS6;q-A9=2>A2fF>i65;$y7LnW_DLPM zwjOW0d-A>&HNDr2y;iC-sS2I*iypn9{4O?!TUYM~T*W z)5Aycdg5#K9=yb1Jt0RndYY03c)i3Z_A}LJ_!e!J_L~Hi_!0g0#;d@|$7UjK`Na8E zcO0g?c5)Io+3bJh;ghTQhtSjh-$jkv{`-Ba$ix206AKSHC4F!c9l3>Ll zTYKG?-nG*5NdM%Cewy--GsMC6=)bn{@|^g(X_g0L^k9Yu$AjsUXKb|pKmBgKw~&YZ zlP4yRaI)XfcKDFaO>X_5Z#;JUNq7B3uk+Ur-tU@NTR-fdJkkHicnMdUJ^V8|)X#J3 z(NEmW@xpd`hDS5DswVc$)*tDgJh40{ryl25;{l#~Ha*YQGwNr=;Xx+W5BxE)TF)7t z=yCB!+Wi#T+BwYc&&e16bRYVIFyJFAPujCzMK`8@D~x;b zcZ&HS51y|U9<-@9*pvL;YKuH^h9`PL{E>DXk@twd;pdsV+4?KVgYEE+X+5)zOuwIZ zU;XsMKCv%(*uV8hc*vQ@f_>2nKM(e?Z`9AM3=j5myo^+Tq<`{6KNEkX9mnN8;+sP} zt*+}uEIirkMLJ$Bd%m-O^2EXum*k0V4e_Wy@M%ScM_*&6ex!f$L{Ex8(yqt(ReW19 z55{ZU;Xx+WPqv=fKY3!|p-+AaZH-qI^I(jo4D0;NqrLSb{gWpe7k{Lk$A-3}hx_Lr z#Iv5Eo5F)O^#*&o$BX@wCl((1gd_4E@e##5U|8btAfGaI^RR#NL_ZgQge#8)`$mo| z=0W|8W_WO{Z@kz)d1B!qCyvW|G-rl*y3hA$=kz|e=zM4Ynd3-%2k72?tQ z!FV+@Jes$Ayx2c^V&NgD9_Lrl(ZxI%uh`*1reOU@|Ky2&DgH>ipF&&5OYmTfIvE~h z@Yav?Po7ww(c*y4>|K#urEHYm%rr^;-ll#vwDNQt*VK=-QGV-|JEOQ zPM?@BwC$W&><_qBJ3PpxOc{SNJkhlDJfKe@4z|Z9`+2bLJN-fZ4DYP-A&=$xZenk+ z{z(7UA9)@NCyxznN8aY=!CLR+!LhR(9`y!$T2&J}gFJDDC)!>7k#-!B_h`=c^WYfr zV7tQM!9Hc`9xwK9{Sh8=;@G^$@Eku6)^sNi#%nyogPa^M6T6r7NBSpEw1@a3?RhNN z7yWj%_wyF)BMvp^2744e7PT&2riM8*S**|%r zy~H2k%45O4`1gJutfft!WV|vwnzvOou{W}w**|$=;UT9U=T{?t@bh4;Yw{%5Q-=qc z-1;%Gc0OePy;1y;b{vuSi2hv61FmRSoqv!|nYww{zx79W=u;jG_Kla| zLH$h6@ZebA`5sIE)?_nIo}-~WOD1r#MQT$r<8c`*?r{>ynZu zsh2&;Cf+VwSJ_3>yJFA zPazJrj}ATUynBkepcw2jvH^I)At@+9@+@E{lK=djJ+Q@i`XH|qH%{gWq_=j04=uzfth zgY^Q*lhjX!hfL=kyl?Fd+l8~Noboqwc%@^|Z*7)K7+oOq(p6S-bdy z_u27c|Ky44;*W5Jd|`WYV?Ph}lLvalxu2&uXwSaiVgKZbg@>H!G2x%Xn-ueaYnj7? zY|7OAeuw>&Cu)d4!WAY-_e3u)=0W{*GCVjsW$NZ(|Ky2)U#M*jh|Ky2GTJX(KPFb|NBSpEbb$CH?S2YvZEoS`!5UBG!M2&<(G0Ds ziM7{L_D`Nzc<7TJ=U2m9`gyRP6M2&Qad?o)tsfI>>zVzNCpu945iUQ4wni`Y^I+{J z@+9?>;Zdem)x_%cOZq2IEYHcQJ^R&ofCuvmd2nR3f4F{3tkzG4Cpt*{k#;|Ywsv0T z_XiQ=!M2&<(F~dSS^BsB$aDIn$NAOh%ZquE`f+%W$*mvr)P5cwXLzEf_#^Fp3T=)5 zp_nJBp9~K&c&}g5zx79+lT&;4tC6kzJXlwWF;*{?d=`2E3}N94h_nc>lFt*VK&_Z#fr`XfB_NssfZ(QW-aSl@^|N&PrH z$mG_KiPid%{>c*^CjLmfpF&&XSNeJIx0cD1)K7*-nOap7tMw!OlP8wvaY>$$SNVB* zu3ra*>!<7bmEnmF7k{K(o$#yXb|Ie3^((`p*)rEJ>EHSz&*_sM=U2m(VxFXa93EtH z>&HCR>sN*+Izs%Bc0Yx-Mz1dBN$MxVgACs5m-KJ_k>_zqp78(=))`_wqwlC0t{)St z^G}8+I#T?RcI3)?bhh{VgEfW7gKaazqd8ku6RX!R>EHSz&*PFjqdWL{uznDElKOFY zz~a`AiPiZh!xPOEf23VshhN37@$+Dwl4s<#ejcm~ zL>}}V9UQJ76RY);;fanCf27^-incai=jXv1K;*%;nc>k4t*VLD>zDLz{gLN!NuJ>y z13YHEAMzyiakk(IzCpudEk#>C@eiaS-d9bz*d6N3c@FA8L#60V=F>sN*+I!64FcE2my+S$qP43U&5*f%N&nU#c^;SK z8Qr;ufwn6U5a^<`pNJhoA>%9{ab(Jd0djGxtpH{ z>+W#8(06obxPDBm&OaHR=s595+L0^oF+A1JgEe-@lhjX!M{~BSCRVRs(!cdbp2sD5 zqSyO*u$~TilKOFYz~a`AiPiZh!xOz#{E>Ej9ey<);KABCm-KJ_ zk>_zqp3XGCKUgP+Jm@<*EL=Y(R_iCj6CE%9NW0$^Z5`d+&x18_$b)S&!=o8mRTHb% zFX`XJ-~re0ZY6)ePWre0 z$a8Y$v0z_kMzKGrpII3m9P7JY#2KFGMDa)3aa`VGv>M{ceBOI__`X^5; z&&lbh(ALfYet)oL40)3J$?zyst7>8`5Bn!iG+X=;F74T`Mi2D!;5tAa92p%MuAi>? zNBSpEEYHd5r_k2;pb$@H{>kuYhRpeu;fdymKhmzp`PE3Xm?xPc;Y0j9SkI-aerAU2$HeOVBmI*n zmgjLvp6F0N57urW54Oz=kG{HA)x_HSRrXJw=oIlsxb!%`8V~Sbofh&W_2cj$lUqL~ z*50qOfAYk_Lry=1wssEl`-3%E$dlAhhDVuNRTHbQT>64#ATjL}AJXmXmJlHlfJer|ZHL>>Z5wU;rM5l>A!llRg z)yR>49;~ZEo}_*p9%ORs$HcC*zh5W)lP8wvnFn#%@cp5U3>Pc;iLRKSTBVqq*x{>XFs3UWolJTtR1gd`X^8HcJW8r{jO;1=&^nttZzadL`TPj>&L`e9`;Y3Sa`^(J^NLB zoSz44nUDwDW`;+zwW=o8p6~3RJkdMEAK}vD{A%Q_ejcn_LY}0493EtH>&L|E=ZDfi zd184^PCtdVHjnr7V2u*;B=wWwQKnYa#OnN$;fY4XA8FT~{c3oYpQq>k`Pgv%blpEo z|JEOQPM`b~+8Uh@;>p}UXLvM2=KeXu6P+&pNV^{ASK}pklKOFYkjbqd^HlGjrGM*> zJdaEAbWSYxC#jzdk1}Qa$?!yHh(FSfehBLs$4AG7>&L|E{3HEaf8=>w zk|#dd&x5r>$b)S&!=taURW-5pe2+6c(L2Q-Y1iZYYUFKx9;^#Oo}_*p9%ORs$Hdy} zDf_qn2oE{^6x!OH?dQQ7AmmBvC&Qymt*VK&;}uK)fAYk_Lry=1wl;6sVukPTXJTy+@?hJ{@Mwls)x_%jEd7%wI!pYKc0JCo zhHv%rU>y(gB=zI)Ad_1^Ce~h0**|$=;h|4{3T=&U^YdWM4)P@Rli^XOR@KC+Khi&W zq6OlQv}@0P)miE1>A8L#AFiLS>zDLTo>-pKCqIR@j*NwPGS{yRk7mfs&l#R*q4*>1 zdYoU4JXg$<)Q`i1Om6*{r+WR8{;faqoId#}v~}d0A5Z?SG+R(Be00@Mh6gbzQ^ubR zPjs#LBkjx`+D3O2|2;+=8#~{ps5jWt&BOk!Kf)82V4lith(o(7u1p+o1R?L__o!pXIEJ5q!|k3nR9oge*Lod3s24JO7%di zyYj4;{!Q{M&!b}p*H74VspOeyb*1`NtIOp%!RdFd)mOdce5=dknRaUI#4odY?#i!N zT_(@i2X3va&mF5P)%&gP$`cr`N5{5qXr7AImu$C#)n)RuCLiR)&$as6*{56Gl_xM> zQ+8j}SoxYIGG0rp?tJ_`R+q^$ebIXxAKLjnlIJ|DJ0E|))#dUmb^2{tea@2?SY0O1 z=*)k1;xD#(pLbtsb(uVO$Co*NF1Pxc<3DS4nLK9A1}DC4b?4((S>2Tp&&*k^Fso zcRs@3wnsiZtTywJ)#=>Ye&?q#(bphPM$!0 zV?O$~54>3N%(NQwk+zq~bAr?FT&po3^}~btNaL4TjrmC1i+L~~sh=IIF&}BWlP6G5 zn2$8RVm0O?Z7ew^n2)r*I1lC{jc;3x`AFNHJc0V* ze8hDyaKu3Kk*sgw&PQ0=f&^BZ`N(SSG~3WTg84|}+g4*f(soT0%7gi6>qXDl`G^|x z5w&IsuSbRX==>f2+s;SSn2)H- z^O3eYc>?u>`AFj{R%1TW_ToI4k2HR+)tHa8-N}Wzx5w^&mCCX%E;tF zZQAS9+=p%Gda`tjyPWt+gU8n2J=^LsdFI^Ra^l-oU-gz%R+q^`zqn2pj>-CU#Qf9V zY1UW`zQ2WETz|{;3u|wYff{~Mb04;OerbHAK@GpC%jAJy8sD}WerdaA3h~e{t|Nvc z2JwsQgSmcTZ7>p0!!K&?!#2c+`qKDHgBpHOm&pUaG`?*${L*&K6yl*@T;~i&4B{8p zFLV9E+GQl5hF{d&hi!-terbHAK@GpC%jAJy8sD}WerdaA3h~e{uET~S`t@tX9KiL} z;1||byJz;V#>}SYVcKgwjiw>Q_qmyGu50`D6kGdb0hO$Ze)9L9$wqHMkJ01j9KtrGr6|O+K=7i zr~Om?{hXWla}M8yaZOG5JjnQQt<1lVA1gK8hUSBboHH?gT!R$74m5tUw&>r-FC91j z{QqnGxMu4h?lzPM*PDt7DEXbj_qlVygcc7$9SA~sZP&3wioBY z`LnADVc(-TB*!_6H+(%4+XI%4qUGXd))U)|o zXIz(kUGc0lsAtYSKG%+SJ;S>3JTs|rZc=j}wxRd`_&h@6D-CMAFQP7!2k(nCzHK$$ z7iqg@3h`hrdW{L|$eQywB72WNbK#luk3D7aJLYFTmEb(ab?sfRv}aW>gn0~JtzHAK z;*--g++DA}Jap1tbcq^XQF9-*p>afAX?&$Yjk=;PlLvLB@olS7SK6+bLOiUiVOfhF zNAz1)BX8^V>e<78emi@ohF8?whi!-rUTJ)#K@G2{%jAJq8sD}WUTM2#3h~gZS>?Ry zx})bg*qq(#mG-Rag>nwASOc$y=k$8@{%5{+16`tqSJd2xZD<@(R~lbwP@}G>%j7{_ zX?)vi)RnetrVtP7YJ3g6ihi4};p@&<+Ow(`N?lEnwd?W9vh&q3BMZ+xbKd;Lq0i>W z*DqA9wWM#ahV1*q(WiR7I_U?q?CcGc$j_()db)8uJx( znLL=UG`?*$<|}R2OnLLwG!qu>8h&rxbq*H1zm9+3>)W@tzw`|xp@whN+=p$56Z5>r zR~pptjk-)8_@?n~tKplrYo-tn&MElToFLyu{?O~&4>#T3-lI~(H)^HK_f6v~4QlvC zT_z8F)A+X4@J-t_Q;~1+n)tTs`>LGNnt$x|?TMF1t4Kl(->A6{+Yl$}P2(#KYWPN7 zCJ%hm__o#XP1`k7h==voSrgx!&nJfe)a#p4sDf|$OjU>zzG)P`gG?XrjoRIY^1wHZ z!*`JB1HMta+fW|(hWC%7Wv&HXclmlPi2l58c@@c82kk3o%dkh)CG;~doZN`o5bAa$8MI0rSpZ8gq8ZP!eB=U{S=v0I6I z4AhnH9>bZtMxXBW?V@S>+(;5?_(sir*oHXao5oif)bNeEOdj~A@olT&o3?AF5D(}4 z=9>6sx?#P=&-D8Cvr{*+@4>0z8?{pA`=;@g1~q)6E|UkoX?)vi_@?cesmQl@f_xkK zORsPDF5JMLr_}I`S}F5=)A&k*8op7N$phaszHK#p({{~N_^-Xb&E0VMTS!6;->A6{+Yl$_H;p&{Z%1;%H|jEZ;G4#q|8FCC;2U+BJa&GY zAm5_D_4;=AZFBY|3pISB=00rmeADAew7vf31zJ2K^3_k^S7`u!Zl=zki!QWIvM}Ua!};^*?xx{XG_H_(rXi`Mzm< zr9lnfsLSMmZyMjW8op_}W-9Wnxn{oEdW+WY_3e+(j$7ZT;TyG5=KH4cl?F9@qb`#N zzG-~hYWSw@nyJXQcujnJk)EgH9^X#=?!ESRHmKnnwNmE$rty^qHGHEklLx+OeA{aH zrtO-k$hXd#_-4A{{MOl^x8C;q&8~KSqlRzPN}2DQ##b8D@Qu1m9{8s5ZL8s%wri#$ z-=cbn`7Lz+=J+rhl``KqjjuGQ;Tv_CJn&89+g8IjZP!dizIE2bH`5L0 zx8}yZzA1$&_@>WPg*Z`f8nt$O$5$~xBRSv296NIES*K=xiHJT8Z?bOrv}{11q8G1Q zK6M84Y24$}p?jQd?<=Tr-cjrMls{i-e5FB+`HH$s9?Vx7-?kd_m9}f9&^&D!!vEyOht9p8Q3#D zH%B(_^=!d)bvvI?!!v3n%&#+zuQaIP8FiUF@J!>|R>L!G*Gxs8Mf(-ZXM2Rd#p8U| z+@jaBi)Vk%z9*-KXVglV@0rF|8r1NNx=bE;rtxj7;hDB;rXtUp6X4nKmc5>B_1GEq z8bA%tsFg6^GmWn_sNorPnLO}JaPdW3f z-GkGaUIRKW>#Z{#HC3oHeWofju506a$>i^^4D8vEt+UaWuUnqQ1AC@*7XQP#(_3eE&Rn>X6x8sH zn)|Q~G2;GA<0}nnct%|&4?NTOw$<=V+ci^&hu47S;GWrQ!05KUp4EQ1mz~e3;Tg3O z=6j~`l?F9Dqb`#No@spBYIvsYnyJXMcyQ0`XO8hJdp&DD@)A3rQNuH8CCv9s6s}GI`*c#+z$^<*qF1k5o^=NH%(*}Fcy`f+`nM6Nac)xU zIhTKKYP|XXcajs=0O~S%a1GFS^Z)N853T{!W%BSE5H-W|+2N{0SOz;BSa` zw(qU8%jT_T-)~aGGwL#oxIfeQN`o4nQJ2XB&osVmH9XUH%~W)+(VPI!MtA7->?c#7 zu=i)w@Qhjs^Xp9GD-CLRMqMTkJk$8L)$mN)HB*si@dS7lzoyr-FV5A!hd>R_sFg6^ zGmWn_sNorPnLO}JWo>41dzGoU=X;8y6>N0uY znZ~!RhG*KYnTkA%4lbI{!gpw#&zi66^=!u73+($%YIsJig!!Ine5FARffoB@u zwi=#kyJjl#tT_Rm4e#jjY?qaGKARyr@yux_Wrz{i03JIuWIU5&6VLj{lXzz1jc1}0 z&ulws!ehwmW;_9&MZ>+GJ$jG6-=v0T)Jhm;gl8IWeqTd!!ZYeJdElAGo8Q-vJn)RV zOdfjHnE=nmJ)XUw-)|lgp3m@pbI%gr2XuDotuuGrs8MIs?l$k-)VQ_i{pP@)>3j6i zo!2eT;(y~Go3Gl4BTd!wVK6#w|K7bnMCbgb(`PTrAH^2WcIdKi3E|Uk> z0F5`l|1WuP4WKTQhu47U(C~bA^yv%U5q`I+bv8V;*RyvYvx$AbNe$1a%P``)sqvKt zH9Vs(lLww@eA{YxrtO+3G{4fb=D?mUu%B&5ukZD&cKlBE{)`%)Q7d77ooT%JK0tE9 zGwL#V;F-po?*k+cJfkj?hn~d)d#0Z`j(a@YYx#@q_W{)KjJgaX>P+J+4QhBsT_z7a z)A+X4@J!n^Q&F9D2KG#^o1JOBb#|Haev=xWQ7d77ooT%JEJ$*~GwL#V;F-po&w?Zm zJfkj?hjkVmmOr0`-)-uAHoAMSXLmT?Z&JfE>N1R|GmWn_sNorPnLO}JtNGirAm8dsc~8n<@5cd8hm)rJ2r ztm}_s+WU*~bN0uYnZ~!RhG*KYnTkAX zPJn0Q9?vd(;8phjW>Uj5Y9-9~OyesJYIsInCJ#K*__o#XOxrb6k!SG)c-A?fx6Y0} z?hNZ0H9VtM!hFv(zS5wEXVhi#z%z|+TMf^&T{9JV)|mj$Mi1=u>;?Tj+>zn=Ed76m zq3KMo0r5dT&ve|VP-oQcHZ-nl<9o^E{%l~+ocpupy5(6s0iHDvUbj5!On_&@hxB^( zra#TG_h-~NH>t~>n;KtfP~#dvT_z8%0UF=78rJ}A*G!?g!rq_F49{oj|1&I}&!R(n zJ^R*6HnG1aMGeoWl`#LhsqvKtH9Vs(lLww@eA{YxrtO-k$g}1IcsB0wZ2T_|+3(?~ z;Tg3O=6j~`l?F9Dqb`#No@spBYIvsYnyJXMcwo=;GuX~yy>+(D27k7HJA)dYQ7d7- zXBuB=P{T9oGI`*c#<#78XWFiriahHK?3rFSM-T7y?6G4P*!heao>41dzGoU=X;8y6 z>N0uYnZ~!RhG*KYnTkA%jw+bXO1$5UkLdO6sh=KoD=Dbq88!D|8)C%$nZ{Qd)bNbD zOdfcq@olT&nYL@D5D%{b&A~mh|Icvb$X?HW+}d$pQc%M)YVN}}#0bwczS5wEXVhi# zz%z|+TMf^&T{DGv=vh3tXZE|2=FDEtF1zJsJD*X*GioKw_e|r>cjS^2o>7;{1J5+x zd`B*M;2Cw9JoKzH0iF#X)$3X1%9HJUMh(xX%P^wOG``ZHhG*1e^1w5VZ(9w|v|Te5 z)me0Ocs}F*%L(0~!T03zv!i=GTk*vm?fn@wJfl{^{5sS4N`o4nQJ2XB&osVmH9XUH z%~a%Ba{@dY_jopT^XKg~fEu1rD`CE88eeHp!!znKdElAGx2=X}+OC<3Jc}p5v(7QS zbv9+uBztaB!!v3n%=b*=D-CLRMqMTkJk$8L)$mN)HB*sioeA)4^w?g{u6SyZ^^6*x zQ7d7-XBuz*UykI2XVhi#z%z|E|1U@Kz%%MHd3X(ojwzbY!Z&D~&*I~HJ^PZ~~do{hY<*Rum|-`oDKEHyl%R>J%` z)A&k*8lF*?$pg7h&tA~q2OJxo&(gmG5xP6m&o!bGe4gpJQK8PL-EC-G zF`sGN+VQ(($@c*Rd#2x^jIU#!#RGe$zx&@gaowu3&cL2&osFK<>)FWnj)HpY( z^_7;{1J5+RZ8bd8cFj~&XPtpP({popPOoRxFw!EnBR>?|8dwxvqZOp7wnJH9VtM!hFv(-mDQR zIpG;~nLO}JS+UwaK=QQklbZU4;U4{|orp8wq)bNbD zOdfcq@olT&nYL@DqB@HQ_Dt(6x~rCU#?}kcFct%|&4?NR& z^Shyv2cA)v$-_G9On_&dmA#(5px4+n;*Y1d)?IfQ1wEov(6fNHZsZluKOLg-$c*WHx^C) z<>h1Qlq%C+(`Twe*Qvk0>=#aa&7Rkf-TY3gHB0y$-+YG;I`PY_{{9sYSzRX2)YJdw z#IGH5#kK}K&iQ|8&H1Oy+x~4+&s=!gDd(KNF#TKwV;0}}_c3$FoH|sCxQ}4W&`{fI z+(&4;s!$%>M_|lW3@~QV-G3i5by!u%>xmw-@jk|^zMuJvZ#d63dwX&{*~Z|Sr==hD z{f=IE>cVs89CGeii-yd`Q`7$oJ=y3-9zMB> zf7re9pZDGG_tTfI!ubjw_D`PZy3_}9;+VWgb3;GR-k<*EZRi^hyv}BZC)r_s{9*Cl z^}Vk~+<{ap6+e*1mv5~qHofAT~(h(FSvW)J_2zUAjR_2?&VCJ(mJ>$~fj z86W=D#`TliKiP-l#s0|?3lDwbaiMM8@$=lX>3O%22lX@E;Xy9e&qkLo-*5Rg3oH-& zCr@;v_#<2)4z@@C;paK|;-BtA9@NiBh6l%k=_R$7?SJ3#kKbnfk^adO%X4zF-_SOi z{^0HI{Nv6!W(2JLsf@qj~H->mOJ9`;Y3=o{jXaN(G|N3^$}XVJ7z-cBBD zI}Q)>k!hPx{8ROF2aerN9`;Y3Sa`^p$AW#)K7JnTBM<6lb%qD~!L<3$Us_*1^#gXi z*gtuqo5UaC!f|YtFaV3-(W*Sa`^p$AW#)Scpgc zflsqCJj}btMPv09JN!}ok^adOtq^~toyUc?(VfLS7_ZpjK`#1(&$l)hn)_N?Khi&W zVtG!VLL6+5?k?uR`Mx5W%sn6$w;PW9@(UE=|R8H`C0lW zPjrj;Bkg%C*cbhFBk%nf_OWl&Pcy@V{b1T++NIU2zJ7|HU(!E$VtG!Va9rM_^ScmF z_xT>pO7C+~OW>eCzuWo4^)q*Sm*rvq&e6Z$rIfs{s>oygYEI8 zjSK4sW7Ns;;P{lORlV+=vn&t$Cr>OqsAhdUc2= zbG~PIn0Kl<-*Ve$rHmf=Cpl&MuUv39)Jzx79W=u;jG_I3Wl@6WS`|J=UcLEP}3 z;qfxD_Iesi|Ky2I6@R21x$+((mv8tD_x&L22glGKY-c$<*cTcv_D`Nzc*u!k^B&DB z{5Hv`jP(06P+ghNP8X&_6>i^&x3vBfnMS8s5jWtUC->F zJhAZ5CmfOYh?W)efNR|0K|W>b=3)QjiROtv!j;E@ed8r~FkVyk(t2i2j+c3E&v*7u zo>+Lu$zwy?&ZqtUob-cPUGJBp6Vun3y?@4dnOMEPOaJ7F=8Hel9^zp8=yE^Lx3|Ca z4Xht*n;9M+4;~Y1@5k6bd1B$APkNkR#h>x>{BYClyXM2#;Xx+1eoU;L57|F?qPL4b z!sVyX){)Qpd7gNAw2J;D_sDXk@pz>Trm&0mN-1fr%c^E?4LZb@X)6`7VL}urI-ij`)Gy-$NJ89 z_D`N@MEnsh9GCYP5AbaI@Cn`L`$=iu?D>A&d;hlIAt#++&v*7uo>+Luna6^Cownbf zi>B>!BkKp-W`>7(Z$0Yz`ph>!XumID|Ky2I7k`9{$Az||SNVB-ofGsMC6`15|AdlznC$16GCGdyH^@3bTKKc=#Q9WVAzp6Cqm zN4VH;XxrI5{T`J2`3>a#kH6pYU|)2yzka^F=~rt%9y-GEuz&Kz!b48>6WT^w`g!JV zxcn_$^^@UYzONp%qVd_~_iFt}|Ky3@DgH=1j|**^FZJ`>ecPOU$&=iVIXuY4`q}#F zGiq;tdbYh^W&h-fg@-d}y}q{k((^43`zKFyruZXV z>^HRSyxh-o$yoRIanalS^>f~vme#I5ysGsh{gWq_=j04=uszz!&vV0xTiNfwlKCgY z!{fKx?Wx*UcWSr zUHZ5F$aDG>;$VBcwV&rdFMnls{bYE^bit1o)wW;x8OsxAc%lX3kF>Mj&^G?I|NFrY z#joq0e`fpZ=bTqOTAy^y>$QHQf9sDtCufL*?a_UHp5JcvjQu4>E1TATyw(4k!FrCRfAU0yAKcQ{>0YA^zkKehw ze&+b==h=U`y1v&96|EoXpFFWVCufL*?a>8(o*QoXq#duMelk2f{-MzaYIohasU0u& zPoC&q;*W6gxX?EKke_GKKRstZZ%OLM;Xy9e&tLZVZEf2hzq-otV*licg@>FW4z@@C zOq^eH_i{Ihv=F%P&#-q8Jdf--f#-(mmciOv>(gey#v?iv1KF%RlzS%wEk zr%c^E?4LZb@Q^d@HT*OBS3l38dz{^UzF+G<-!Wb$)}HU|pFGhy;*W5JNzy&z0iK;6 zUT*6-Io~rpnzvOov9_MsKY3!|A*UYaSDi2U{WBP`X&97 zCzj{r)Smq+`Zqrh<`te_I5N5}Tt6mO>nFn#EfIgD-A|#d<3S$1ekJvj;n574`C0n6 z{>XFsq{sPH=W8K!=Qp7p-5argV>43B1L zRZXmZJ|_KJf8=>wlBao{pQpKOxcmLG!vhw#eoX9b_WG6KiQXsvNV~oczZ$;Y&-1`v zHtC*!GCa!Es+w4xf24owk35e{@dH-ziQ#A^Lyc%t*fA8GfyqOIcr zo;%H2ljKR}pA3&?XjM(Dz29K})*s=COY(GX^!rmY>o}4pnSUG}9V@6-)x>K3NdM%C z-Y@=0yS@&;8vTZ!2WuOWCz*dTJjllR$HZ#=NdM%C<#}9^C%(zg({ug0FsN*+ zI$!*ec0Yx-4&PGDlhjX!2N_sDMp_>BZ~YOTxFk<>tDonh3p3v@e7P7N zOZ<^`b;7U413Z__Td({2mEqBBt*VLD>zDLTo>-p8C3!lx`ThCH)F-;`nUebbNb|`(AM~NKhG7}>(@=; z`Z2LuKN+6rgW`|0YtMc)vdYghC@el>iDpXbqgGS{yRk20C}@Ao*@FX`XZzS%=Ih7qZu;SFX`X1`a1k-^zLGw#On1+`nUeb^SC5W^ILwN+VMMe z&p!?iSls!?#OnN$;felP{E>Ej9ey?3@$>Ap{Keh(&lw(NYE@0FUcaP&>yJE-OY%hb z`gtzPUcYV$*N=(S`pNJ_vG^nHepj@0Jiv2D_Wf6eM>DjlCRVRs(!cdbp2sD5I{)GK z2kX=G{Z2CfI6OL5P_3$o)%wZsL>Gxa(yp(=uSUP^=jnO>mEl1)&Ocr6zodWbk35e{ z^2Fcq^PoTa1HIb$K1I(h?8(f}8J_6F;*YfBh`h(heZ@TBn)SwV-#4*;>yPlnC3%|n z7xSQg#xp#4jT|o{?facr`X^8H5%EXbaa`VG_`4yV%;&wgrg?EK1BZ?o#!El%W&h-f zg@>GZEZ7%q{?@OR`F-_fhDY;e>PPw~Pjs>PBkg*eUyYaGNzQkN2bsG4(eYycOq z^vO@5t?^6!JXl+pJlHlfJer|ZHL>=3%Kpg{eN6ljE-of(@&wT(N-ZIy?!P2li|?}ne!{d6I~+yNV^{ASK}pklKOFY zkjbqd^VD81*uV8hc<7U#LR&jiiv3CIC&PmbDO1KD>7P8&rQ(mYYtMc)y0xDN>w9+9 z&+Xy*F|j)TNdM%C<#}9^Cw_&W2Ww}NC#jzdkG{HA)x_HSRrXJw=o8|PaOrV=HL{JL z2kTOjC#fHY2btXZF|qc3mHm?^79Mi?DYUh@t)B;LK9VP?pA3&OwW=mozkiqh$rF82 z{E>F;*{_CQ>F2?EisZqO(W-F$m{_eJ>7P8YJf}~73T=&E<>$d#h2+7unc>k4t*VLD z-w)65M3;#_(yqt()p&ph>j07`sUL?2ncVs@u`Bs~CHuGj2oHVoQ)p{vJHJ0zV~;#Z z{bYEQ$$TDu%gtIp(m#2k%f%mQ*Pi`qwBqN%`gP>Nk@wdYoU4Z13m6x@+V~>c`-of(@&wTo!9yO!5UKJN$MxVqfD);iPiZh!xMd4{E>F;*{?=- z^z-!GKi?UypRW67>EHSz&*_t&LR;hE5Krd*Im4qFGWUlWo@lxFBkg*eUybZk%#+lQ z!-Gt2{g|hE|1AAmf8=>wlBctMiZaZ~c+yaY>$NS3eKdnjsIi%?yve##YtD+Vefm@I;>#f23WH^Q-Xy57u!Z zPf|Y)4>Gy+V`A;~l>J+Ogom7d3T^G|=Jy9{q>v}6pA3&OwW=o8j#n)GlP9`T{E>F; z*{?>Y`gySa2zhX1ba%LZOswT$|Ky2QT>64#ATcbTgJeliPhDS4G z=I0Dg)E0lFU61pt@e({q{Wv_x64#ATRVFd`;*j9h6fo^ri?!s zp6Dv^N7}V#zl#6a|NpHJ4Guo0yh*>`QE!HZzIF9;jepy1NI&0@{;faqJTA!-U0%$C z^K6O314c4^;EWCHzj@|&_I&5xyCx4`4}IeE&^ErJm$G^1-f7=@Icv$`TK~<~MUCGZEO-@H;?tKqc@B7YS)jjvTpqA{| zc11P**Y``FTFvUG-=(@lp4Q|q)xQ6RrIKgN>aq7eVYQQHD8w`NfgS3aXPVV>S2nCJ zlc(J{yuQm%&J&)wR?l6z*y^r4>!p98AnQ+S^1JKmXKZz)`Z24!@&tUw=P<)2kbi$1 z$Dke;Ej)Yv)KeEOn1A+3+soPwix-}gtlco@UFV*1&ius-u#CeHjz=Bv?~1#1fWIk@ ze0W%G>cHxBSMoFD`;e)FBX509*1oFMtTuIEb%{Kvga7^fLy~9AYSe+YYoZVj>OkYC zS&cf-_ToIKgZ&TNQr4%MYc=XX+nqc)b)bI6b!yZBwPrDElN8he|L!}E8Ke&Q_uSn& zz~6EwgVm-EtmaO$4e_B4Hv9e?q@HRut4$qPT_O+aVC6r)Qu2&hjXKbFO%&om9ccVC zt5FBqUYrMY@YtpY2+v%rQ3u-Yi~a4 zpA1%;Ifkd!ThG>k)us-tE|CXyaMQET2+x?+r~_@+L?Is3fyPg>8g-!U z#d%N%`_A0V)&Vu@fLgN{e{$+T{fz6>r~~S*JO%Rs*Ezs3{nkOUzK^V5fI7h6PFU~p zgH~bf0_4Mk+O$`x(>>|W(Chr$OFmn@^f6x7hicT5p5EQ+GI^fc;E0;`+p>Dn(_go` zT%P-Dvm3l$8LCsidir#$%jEgVw~ntXPi*xT+g@#TnLIp?xh@2b$&TL<^G|!HS(7Pv zT~F53lJy?k@xxjVWT3|QQF9-*dEhtb2KUzv{^=ICOMIOg<45i03gyB0k-^*@TaEG4cFh#Z!|~%f8aQH*@#Fd! z?)YJC3=&Xd{HVDP+YlelzbPNRlzD2@7(ePVc`$x6x4BUAv}$C)_-(do4)fboAs&oh z^vX9gzD|wtqqa$MUKhrX3>ZIZj30HGJRCo+JAz{d89%Nk;*KBI5+MUM#*do&unqZz z@jLXNA7GvuHO7y+OdgEizyI5hB~PnH28`cL2mgZkZK@Ct#_x#Xrx;(S#`sa&Bss4O z<3|RJA2r60x=bF9AJ=KYG5wC;YI6YBU%~ic?G>~3;40+GK1{p4Cc67Vewj51t9SqU zqkE>YwHoyu@4e9KGI^>OE_dR`tZqH~pw(sav?f34#@ETydUlG{`Sd?8@3@H z)YG?HHgeV^S}IPX2#H4>8=k~MSO8p3)x$bknnYKS`Bll}}bp@vqT{8FjM zT8$btL|rBiYUtG2S4jPi)&8{6=M#+@`r3bfxu>o}Jg6ZjzD|uAqAtVJn!IEE+Vf(5 zW>BXmsG%7T{>u8sTp=FRkjBro8a1Tt%n}}Z)DRg^L)54tYRwYP!y4k+NI0f+4aqu3 zZVkD8QV%i@oit;m+-KEl)Tkk9=EgSUHEKxX$E-#TX}e|$&lze_K zFCf)v4Y}>AAdy7xmt}H^+e6w*oOQ2sHd^>X51!`b!yZTwPp$PU{2C_U56U=Gs==bZ8hJ10@2 zo~Yeip*#~gC*eB?t|5oM2dXDo8}9k2Cv&d6CiSFNqeeYZr~A^MA-_;h8b4+=>Pg$v z9QYZ^gL*Q%{}nOjVb0@Jr}L9-Pj=!*I1lD0KDz6Pt?o9I2lb?JbFD@_X}hXWo{6p} zt^tUiCz*eJ&rhR2zQ?Iiqn@bMt?>Avo-}^UYSfdqYo?IzsHbI*Y|8gm zb!yZTwPp$PV1Cm0?s{7G$j5rtjSlgko?icnlf<97R->M@U5P?@Cc2)uh9i3Jx1N&k z%4Kav)DyldU(bA3j$GM?Y45qmKuqX${_Q2()-*@U>ejQ%tk(VEJorvze8*$ zl+&(6q1T1`o{9-hpfea>r^fhGGY7WeJZtCpbFEUa_cQ)ndvt9c|D~@yN9LZf z+JUe9*F(w^71_C#@Bf)#-F-e z9*jTvaX*pjbpB_%W(vKobpB_&zVA)vf3_Fr!Ti6g3FBJ4I3hUyzIVR!&YX`NxEJ6$ zyWO>*3{?M@_X0cb^c(rCbj+;rnQGJmH5suDd5>#>6JMuBEl`)sv-Zses0FTrjMvat zEtq)L0@pZRn``02d%e`&7gD1ZsPX#w$g2e>zD|u=pk@wi!+F-uT8L+*zpD)PL2H5Q zORvqfaNRo}l=o(1HEPrXHC{g-;W2#Djj!`q)B-hgU>nY}cGd#d{|5V@wZOH#*XCN- z>zp6G0k3gr$ox+2N49(;6l&r2N8XfZJm(`khELXxoLXgkof@@3%^cW<^Q?WfP%*q* zdmZfFS~%MOevxabug&AX>@7p`K5wjcrSpFA^H^ZC=6r!u}yjq#^u z4s64D*3R+gTK8b@XZ*Q#{n|YKS8wrd`+ng7=l$Y}uYZs4|M0StM|j+xto>KzM~tsi zWBjR^1KV((wR8L*+&%rjMquw}{F_rYz8Qb}tb6?3_baLX`}@TUYVa1HKl44}(9l}+ ze({8Y`$EkBzW0mfHSs+{^lD!%;8k}&{x9Dz?&Q2*q{h7fwM!a4ho5xg>pT{70d=`N zYu{XuykDGvTIg)Q_SeFN&ih4b)B<(6F?`aEuk%>c0(H4OYiBLQgVw_QbY4h)hvJ(L z-p*?Te!tY5|EpY2s3&S(s4cy?#+>dpbUu%L;FC^#+v?iVYpgDp=la^wXC7hSYfz)6 zsLSNR=TT04ofwdMfdi4<@QZIQRg4LVorCP=at6kcc1V|+f{|~(5sO(@M_%S)t84( z;`hWu)bNU0$-?7^y3+WT)$mH&HB;y{qOLSPwi;e(dvP9mwQLQ%>ikDNM!+VxG2fIEg;W;>RdHOf2@ji+3 zmG-Rag>nwgS_7||SM+-I{%5{+16`tqSJd2xZD<^E4r+YMYSfjsYo<^h)Ro4^R->-8 zy*Ll+DqaJxhCkKomG-Rag;G~5a=bcbWZ}7I&YQp3_q#30b#U3b<<)rKUY)g&^VRq| z=G7EgOB`p+B=gVxJU_Glu1@;FEPhrvM2&NhTF=b<`AXwkHEPUP)MfHuzS8*E zYRp&Ku9-sf9%^Hn35!k)e^%7xRnF%{I3Gus_xkqj?Jt%0PeVi0@Qqq2^L^9!R*f3I zQJ2XB-!wk98op_}W-9WnIYGX~pXv4OhnsG1?@_7Y8?{pA`=;@&8Z~^QE|UkoX?$!o zeA9N#RODN{Ccf?ZzAERmk(`3=s~=9Rs^DTONdrq5J`IMd$=W1Rj@FnnXHyA9=mZyLAe?;o3Gt_59p z`RQj}@M`#T>y}qb`t~aQ{j%hK>|fR`uSWa!YR>7LyT(0UZTj#D_I#tpIY_PNME=~R z@vRy)&Oz!jd2kMDd~7x5E^XILp>x^ZW1Ljt9wYRflrwjA+PyV)(X@STBndTqqvk$r zL!6kqG`?jueA9N#6v_kNG(NT(zG-`L9{Sc?6W>fXthdptdVTxZsTkm!o4?(Do{zsKY5ZAr|Ks*=RSi+YH)^HK_f6wl zHER6r7wR&3@V8%nz3rSC+n!mWd z_^jmfkev5o@ITtJ*Z;X2F1P<*mm2<4D`md_8sDl>!++{BdEoz!M}EZq|72?TPhBPt z{MUYCtKq-4Yo;Rqn-k=J^QFE1-+kMheMv$M|EakT+Yl$NQySm08vbj$W(wtj|3klf zhM(=%so_8M|Ht09fXh);Szic`Qskkd0}3)Npmumfp7KsV0@4T~PeGm`ByfWvi2)2C zga*Yq$YVqS12bSmkqe`a3Me2U7X@jQhdLv|FGxVZi;vO4ABO=({#w<0oqf9coa&Rl z?Z=J${e4NTI<;$`wb$O~R8@E1KI$dR2l{Kj#$xEN?dmDS$Ijd1(chVW%$vLQKl<6< zS^bHjKe4h5#|!<{zG*S^*LL+3iWmBqkAM3zv8)k8e`56#<^%n;Ut=-!*LL+3;zRvg z1Jb|smTvt&Gs@qS7#bpm{=~{M953`&`=-UvU)$AFC|>A4|K|VY=gT!>=ufO(!hE2= z_G>JL{@SjdLVT!ya#Y0p-{U?xoB!AE*8ir%*XMjYL=63jm1Q_y=&$xoi=n@^tEW)B z(0`BDU1;YYV(3q-Uc!8!zxHb^hW^^FoX@o6Z)%t(_-ka?dmDy2l{Vy&Ib1WhZy=3tCuhz=&$`6i=n@^tEUhj>TkxQzdQfG zwOjw6T=sT5{}V%hVr3bQ7y7Gx(_-ka?dmBMFZ93rS8ufX6GMMu^%CX-{k30XG4$7V z^%UYm{afSFKkWv7vftqS(H-C6_jy)N+f$vFebT&(@eUC~e_~}Bju-l$_25q8C;iM< z@T$-K%HkM5udK07ZRU&Hh@Wb0=2Kf`7{6=t)GxQRJn1W>8-6$9#B*ssy>{_&TV&_r zcO&+=d2+NL{BDHmU@XS(BxyU@g!N$_lcU4ek$vYaEPjrdHI%)_p8Xc@{oQ%|V`F@I)baR0cOK9De$&Q9j64!6%dl?9 z!}UJzG^Y@sO`!qR1e6b+E1^&eA0S$Jf3m- zUu?Ye6!L>Sst(3ttGCzISVktq$Rjb2VH=7U zd0h2Nzr3Su+pPY+dVhwI$GgtFLE6<*$Pe;3?WOyMdqV`gl%v9>00$=61XgBag(&vN(^=ZPvb) zrGJ-FC9W;J!{Qh}%ghtik4<~C@L8`${KZ}0V{we1WA3Wf{(LpxXPaKTueGTikIy`z z_XG45sw?DCbubnqkJ?TzVSSj#ellW;?fB3ysGmJcL@X0gneJp*2{2-4d|I@qMcbV+ z5F?MoJXXXLdAx7kjbwgqR{wpUa)yz|UtGSUw5z9(Zph;+FH95r^=ib(Be8l3*A?pyl90y636gi9?f|2_}JMtkIARH^SH~;p0o8y zj64#@@I)RD_`|ovPqW%guF5d-_|kRfSpW1@l*a?!beh<&S0hFqiRlB|kRRmnlqY%r zHof-APhFGwL>}+F`P2!8Ngg5fW9f9~e;AVwaE z-EBxWP27R1nun8&aUaYUV|ebZuSrtRt}LtvF zb!Ph3Os|O}Bi))Uf9zuWEITQeP- z=K5~U=6f!!!Gi_1C&J)8C%~m%;u%4>Y4s zjWn!1G*f&1-BW0$?dqwQX34k0^V$B#&N*qD;B=_S5Lh(Gkt5O&zt2NyES|0rM2w+88I{?R))ozseQ9b z49$pR{6I6cHx@%PZC6jdG;8&(nbuiyQ@3W9U39RW&xoNJu`(>yOzoRhVrWJj;|H3l zy|EaYX}fyrrCD-QkNGU(d2`I8+4uIJXWt7VhGxXduvjy-Z&rz+8F7psXr}hYVrZuA z>ZzAzjs7*W*MQc|-E~&|`Sx}`BZg+g%CJ~7wQp95p&4EDlwPh10tWBlM6p!VtCkBc8% z1Bhe%@EVYOJ3OC#VD`-P7dXT3pz(d$F^^`K&RoqtZxTZ@;uwy2|4i+h7DF>_S5KjP z1ZbxA#$sru?Y;d_v&H~4Yu(;mXOB;L+TNcLLo;G!SX^gn->ectGvXLO&`j-(#n4RK z)l*1&YGwwY*~rpv&90rUzvn{?&4`s@v1V%DtP(>r;ut^BOzn-u&`jIaQ!mY01JKOe z(XH9#&hsWQG$U4q#hR&ovq}ujh-3UfGqpDsLo;nxPrWotzSCfmF|THB z=Gqu_*41xj2M~`E5l;V)IR+@Kzu?o;ut^BOzqRp1H=zBBaZRIIx~H1rtdkHTiu#%f8`tO z=K;jfj5vlP?$6Y|X)!d@cJ&mh4``2))?w_CGIo##zrXhy6I zi|b77)AxeJCp06D@dM4&K7B7p{6I6}7(di3xvh9U3qNh@d^YCM>`v$NO=4(99K#WH zruI#Xp_#U;r=mJjdt=XqX4>A{4>fD_t(lI^)(^Yu>{RFTO=4(9tPG3mOzoRhVrWJj z;|F!7_QqmprtRt}q&+n=eQTz5Hu9rx&D_roiJ=*>GA!0i?I+OZn|*7h^O?D?TQgS^ zVrWL}ZbNy+*i^fT@$6Je3BAzs@4`BN2pjd8{XORWuPvPpdvCA5GxY1!c;L0A(^#K6 zl^^u!^w(Qw7V~@O`$+B485+Ns-_?Eq-(&x7_&%cjcZQ)Ko6z|^S?@z%=WlBLt*<$i zeUH6w&GbHE>@}vD8GvT3H@>#&tkt(>IyOh%)UDa(ORDysff)A@#Cm^FeD9_9%_=eG zGvXLOn9tPSSd9BsZC6jBd6D<4$?ZMnvu&q@CN%q=h55T~&GujGt@inw7@83)!(z?U zzF8%PX2dampqbhmi=mmetEXO?H3p#B@SD3eJMW>5?;s0eXhzIq*oHXbepT(87DF>_ zS5F~7&`j-(#n4RKd;4LXnE`0lcuTit2Oe^w)r=UL5i7%D&D6eGC5C3iF@B(#+8c|Z znYOE^koMH9H2}@Zf8VW{-VcGx-`Mv)mxkxF_TL$XrZc?;BsvGD{_HiTnHhj)tqopVb=DeyW+QLy)@;W=A7jTMF~%lw?ATQMW|bJ%0OA-w zxCW@bu^86?ZC6jBxq{b#_S5F~6 z)U46JX7=w4$2^+-viWZNn^VNlj93{KYo_+iDls%8j`0J{)ZSPO&9q%T_0r7rubF*c zqxBEnb$01(x7zuP7@83)!(z?UKK+bbd_ptg7(dWV?bFZ5#Sb(ij`71fYYjlNky5v2 zrK^s#^BFNTBaY#Sv8nb=i=mmetEW(XKr^*B7DF>_@9l@0C8Oc_jDMFCx@C_R0v*exKn*Hj|P3^q~F*GBN;fQO1+BYqRX4X+YxS*})>(OrZq0^&e1IK?#2A~zI_8S60czi@65|>` z9ODPq0JS$3;~Jpt>M1l=@Vc3NKRlnc|1KvqpXqy!$(G%kEqUb;d#^zZ&4`s@v1V%D ztP(>r;ut^BOzn-u&`jIaQ!mXLeQT!IfH99|t8M*^eMTpSX2i;{STnUxf2&b^LNnqR zKhR9=)8A?oKhTUg#t-Yv3_!EiR^4@W@D1zQu}KWgh+{b7x~cX}i=mmetEW(XKr^*B z7DF>_@9l@0wffdf$L7eCZp|vAx^4$CG$U4q#dW6k%_=c8BaZO{&D7pl49&D%J%zNV zX31T}^I7<5Q?CK$J>8nUaMX78Ie-|N5i7%D&D6eGC5C3iF@B(#+8c|ZnYOE^UYa%f z)=cNK;jOzh+xC>YeMTpSX2i;{STnV6R*9h*af}~mruN2SXr}Gzsh4J^Z_TvM8ryVh zw)uk}w{=Dg&4`s@v1V$Yem7KnLNnqRKhR9=)9;3gA81A#@}vD>02|s*Jy43+N!hG zI5o@G`~Anxbtymj>nWo;rWB>^Rozn*x=u~K&^Y$XEpER2g2n13JjO@QnN<7QR!#Yy z(s~wG7j0y5oS#k8znkJ~Ym4tZ;}DB^6x&c-mGe$??D6+k?br0BS6a+AY(su=6nLV%lz6x@dTsHA@_JO6fRjz3up$@pp|Irh%us(XZOH z1Lw_{cgivQ%^Mn;vh~*6M4zaJt19e7Z{B0I&W07(ORj0(%b7PFoBh4!3Y^+u`oVg~ zuHC_$c3Qf}ex{@tx*6%$8ukFm0}tN;-T7Kmz-^yByF!enhb5=@o72Z0J+gv-Xg}xN zbEbX!vPH|thy9aJ@};&8Z@duaPCK>da`MRzr2pOU`CX?~ z@BPzmpByh&}&?C)7V?4Nv+FN;3H zE5w8C$?<+ZhrerU%Llv`=lI}!a5`|WbN1Nsf!%hs`bhueV`QH^*>7k&cGt-r`Ql*O z>hZHH?l^8aeBAoM^IU4H^^@b1Tr2uWdx!_y&E0-JGY)+ER`S92g&ZHA4?d~w&Q>4k z-|8d#)Jf0tqw){@eC}T7%-hKanw59fdWI*~5BQ|ET0c2H$yY=lY4@|x)>eSekrzI> z8~LDq4$JX@hc>5XCAGcX>LdMIePrK6@)_Bo{TxAt98=gQA7CSV(5BkpNcNd?lYC5$ zPja2;Bkee&=w;sP=QDlX)t8bFwzoKZ;E$aCH1q!2?0rAGJNdAGtB>#@&mt~3HoT*s z4~~%!>Srv+2gjMO6skVbKlvmhqK~xWyrNfQrx2fJ$9Qk-qV)_O)Tf=VrXBC>pL~q) zF_C=AJBRqFKG5m393PD_S3lA}`6SniKGLq|`BAbeNT`#s1012p{U? zXQ8cQ5qvP_l3g`lJlk78(m(kmH;6vc&T~WC;d}h^A?#Vtz&i;aw5c{Y(wQ&zPd-NY zP$!&G^h#R2`GCW)!w3G9QzswxPd>@Niax@thzpL5MDRiVv~qlKt}kEgpL~q)Ay1rF z^lIE2(x-F0CqHP%iCO}WPJMK|vw!kQzAE|%uOcouHWuWg^@Dsha(vWpXTI1!`556t zo_d}inIDGqQ9j6*arnSfr#?E~*+2OtUlVg2=z$;SvE z@@$_S{=e}+Z$7A>!*YD!(U&jwPd>@Ni9W(B%%pu}EXXG}-tTGq?KR%nKlvEpL!Q0( zlz-e?pKQG6_^98`e6fG>Nq#K)2roU)k6ICYvhnWlfu~^oNdM$xWS>0!EVOm_C%yH_ z#(R#Ba?0tG@rxTYZENdE(rnSNUgtK4-o7^_$2C`8qAf2R@mv)OLHTkMvJI$$yAG z(q6;`$65hCI7U9eBMu+c21j!GNdM$xWZy*c8GgvG4|*aWY#(;`;Fxmiv&#HTJ1P}*VEM2 zJ}@<|>NeS}wt2iqGD`}O(JCYS8s=Btt8!}C!;sqM|IXZBA%M);7Yp65qn0X{!p zXOoWk&^UbH$*rH%*3O6QpL~*^i$223&q7tfaOTtB>?g zK1TM*Q+xKK^8fMkfi?L6@8s08YJ(%W@s;C~JS_T1JI*M2wIcX{*RaC}{*+TEANFtc z5kAzZhzpJl|Dv}(81IX7d~mLByc_ACe3C~*A8E&VMX%&hKc96T{cz`azqjqzH{MfQ zJKotp`556to<&@6Y$U+v{H=Gpne~HhBgaSmHY=&^4y^25q*T0p65r6$Nc&{ zGvjp~*Hh#0fhV_qQd@gHW&h-3gb#W8S!k>IrJv8e^VYEW%Eo(+k8)~OQd^rZ_D?>^ zFGL^Vr9Jym`Efs=2cA01^1+$O5B>GC$Jy_%U3$Z8%ZL4wj}boP>1Uy>tpK0tYhJl) zNB!jZ(8GS$pHW?Hlk@ETJo_h~hZ&4qxAX{*}!?IX*led{SHe z{42*N`K9P1?RuUcnP2(!x$c6i?B`!u{WyH!iS+|MsjYtgCH-4{WZy*cDL>`s^OMWo z-dR66KFX*&e$~F8kkwC)4(`IpBm zpR9fyKJdl**>}Cut9QNcy*j?6fATT1Po5zjY;XM9ug{DJ@3qhG+5D5^Lr!Pzy?b?s z4M(Ti`pNN0o)CSco&AQkV*x(X{;*|d{oL=bpSNE5k?MK(K4A03{;fX3hdPCLu-%OL z_1SN`=k55)>LL3lyyIdwkouz&JN zo)Ue8SC~or$Vdbq%s+?a_~7g|r%pcXpL~q)A{eAI8VlG@sOX8+`4gb#V@d4AOTPd}f- zFM7jL)^m2faQMKJTR*9-yVK&}#e3VnOlG@t) z4fanyM);7Y_UuQ=)(gGgADjEFT4(({7_OhxR_jOlC!gfkqK~xuS!nA>fX{_TUv00a zS^ebrsE1}HwYAq%_D?=W_)sT3&yO10`1M);i4$!-XZ7RofhV_qQd?Wk?4Nv+G0{hO z`B`Y|Sb)zV?_1Mezq0zt@lj6AN@}auFX^9rjO>%A_UuPyTfaV-S2(_KW>RW@4;S2^ zjjK;;tM!xPlRPW>NV}hfwwAXG@zLv7RzEpD>LE8jOaE3M*{4o=o*%U$_+<6t@PVgJ zef0fsljD>8M)Z+(KMQRg-oCd!S^ebrD5so0(!bS5_Q_Lw_M>D6Kc593{7`58ydzvc zsjb#ej!*Ji(MQ_-EVQ-pUO%7JzWw^n=j9w9_0X)Ow)TFN{aby6kBQ_n7U0uZT<&~c zcKCpcTR*9-eO@-wKlvoT6MdvzkxCPR&YctMiZaPd-NW$!$0zx{=p*ggvmdnre7={iHAz0%{FCFO-kO!v*4}Th zf2)u1p-y_99}Vy9*Qc7U<48W){NwO}C%1l5Tdg1IpL~-46n&)K&q7<1UHp8owjuds z^G}YCa%y7TM6Dm`pL~q$n@Bz*0X|*VuT8`C({cUE@k#z5`bfJf;YVY;`t^Y|^~vfd z$49;8=4a{O>LdHqNzd~mvs-UIS^YSC;K{9@bk}~KYjS*&KZ-um?q{K`<=uPp$?7M^ z2Oip-a{5UBRv+0XPwm-{S^+-ipO^c5d9!f+q_#T$CDwS zuU|Pn>Y-UlZT0#k{abxx-$e3BDtUkF;wXel!x` zbM5rp=gT=h%BfjNZFT;U{;fW;ZzB0Ls(yVg&tJdZ8LpqyR_iCnC;5};Bkg`wv~?`N zXX;&ZJFj0kKI)-aNp1D|CH-4{WZy*cNmq9FKA%qAVrggnID9l$Kq;RsmTLXv_$2>V z^pSRr!;i{!KcD4y=dNElKJdo*C$-i3k^ZedvTq{!3{Ug(>AHTsD_lPv*RLF(jPj>xs_<)N$|D?Nm{mSu4{w(@PyT;*1 zjXitolU=`ZeBh1slS0df{aby6kBQ_n7T|N$uMg?Ge|~qkeo|YVf24o%N&Z{(k#@f- z+FIVrug}7R*6O@|<@l(FW+k=N>zDLTK1TLUB%fA*Pxa8ro%4^w2VC6xNo{rh$?-{E z5PhUwt%DPPykV>EG%j`zDf)*~hOB`lCL;#`!!&wZW0j z>xIejNnR3tq#b7zy~_Lc<^x{0IDFtwId$@3|5hL2L!L!kaI6)<2lX?S7RU(mqj0G$9YAs;SYrL$-Un@IXvFo`wjL_K1TSEXAu`1OZE%#(fY}rmvem7Z%!ZS zpL~-46@8>#&-0^^2tL_(clf|luzsX}@-ebco%}4cwei8;`egSTIX=oMr%#Sg@)yxZ z+O=mt8Vm5j`p52gPc{$NPikwg7wq5aBYdcnpM|!X{r&o2ZDI1s>L7RU(S41Ca*Yo_Se1M-1*6k&qtbQCm@Z{D{YO9}rN&n0!EVQ*1;Da@B$tSCy z93SP>#CpD#5Bn#dWT>5I>ZCpU(eQzOeQ+HhADo$N5w4$(`A7OEA0zwZ>1Uy>$w47L zx%nr@M?K`mSB_7zis&QldY&IO4(`n-s~?9CJh}Cg?&|Y}^l$Z%ed^?Ap{-*Pe6sq< z@qq{L^()6GSyl9rcJ0}Z%prb#u)b$U{cIVopVU_8AL-xfBl{+jPx(WBK3F@Ge6Vfg z_-J%Ztov#2S51yjGD-B2c0JFJS^+*-my&$4`f>QclUqNjt-W7m|5hL2L!N#X+B$rw zUmvXbNIqHp;MY$;Ze(dHPvsYxxL2AFQ!QK3Vo$KeA{ZvCXTww~F))kpZ4NIoM0K3LO?e6sq<@lj4qtjA{aWu$-d zNfOaV+Wo3%YvX9YK3Fe|d|;itCtN?Nt>wf1$;SvE^3m)=#>t_s`P5)kpSCB%hH8K3Vii@9TYY5TMDiI6@WEO$z6o$?C`915a-Kq_*~Y%KoiB!iPNlEVQ*e z%g+aEq>xWmKRG_isfqPnY`%>2Pd>>yqK~v|&wkVj@WJ{c#4iFHYIewO~pCs|kYk#;@LkCGGoe6Vf?`DFFu@PQ|{ zeo|X|J!SvoV}uWN^0Uy^kpLg8NkKkY{p9#4rzY0ZP<^C-@=4YceWYD`_M^tIU!Sh) z*LLCh>9~GL|Kww2pE~(jXzN&zPwx7am z)=#>t*DvYc>LdHq$d_ZOzY zgRc#$`u9Yt4UTs5VgFVi;bS8CG~V8uPd5KJe856Eb@JiwU6T)N@gmRmUig2rac@4T zpVM-D;4z=C7gFN1Z}RB@m=Y0aA2mn^O>+S208f&3TpbIe`)*KS;VmhhQx@sF?iM~gfBthSn$ z&*(XytF3t<<62^Isqz(zJNyLV+Iq$Sw4l&;<2jcfVnTOIq7#h>|yO)XY0=^7A$xbS{XIW^?(nByGO_567!&zdr0 z-rQLyAF+w70kL4-DcKqj$9(kEV^5j2U@n$}7{d8kJ<0EryY+PhWO7NefDT^)t|pq}2b<(a}~ zzQw2~ZFl?x@`rkwv;4)|+LlW!Mm=e}<0mkVQBT@$%VN}%wmW_Td@w(mTUL>{{%A4k zN!xq-K|QISC5ur{+V1!X#Kn5zIuJNxMeFG^={a2A0riCYuGJqq{Z6dykizUmoVM49 zc?{donoNt{_bCk8{6S$9*JS+bMWmN1R+Y!i^JaMvwzB z;w0uVY>VPl`%;}4aT3S)VVqpI0_XG-C)cBJcaMPPH%9i4iApj336ybvAI$ipKeHdJfmmK%A4( ze|I1K`_XjM^-9~TU5#l&D9)?*-?^sti!J`?<&Rjb$HRW6%>IjGU#jz***~gU%r2aC{X6C;D5cDZID9HD(Z3KYDTPnAeJ4Tmz?Hy|}i`Yeg@v(bJD!xX0z%JJ5^w z^6-^^;hrephW*g|g?o9m*K0iPiL_l&$Peb7QfkJve{e>A9KCPeyi=y^>t5xDviIf? zAJ_7k=<#tap%smfYXeR6__#LFipIyahC1VOuUl>0u1~ieJ@u+j+Us?(U7u`k`zZ2* z`rI-#;95tp4c4dc$~be*EcnQtt7RRd&RF5G^YvYEDGiWoO9f*+(0@JQT2T`{KCV@@qVaL= z^UC$TCVH%`eq(j+iMV!{8!OfgV+9kVhKYF$+t70bo=?=iR42y$A90KyjJ3^DL#|zh zbMp5@`_G#*>)>M-oG>f*l)`b!^~l^9-M)!m&2fqtxgHw$tZ0l}zgq)V#G+y zW7rnuQSD20V&suH#t-wzHP&#>ipI!w)7%)bW*Yk^MvTNfhHX)dYG0}oBSzvFKa7#< zw&9$D7(4EOSgTx%&5hCRoA}kPRjvisPmEmO&5aRjyD>Im#7N9z*cRnc?MroH(r3z_2HcS zcH(CRr^w%81WLv_+h+U4-n^!Ctj}i=f;b*{>X(G z@e=bGwng!(eW^~2c!^{DFkY@Vh;vpr-Yq{~;h5!mf^NK6OORZM5ic>1VOtch+L!9Y zh?h9V594jM!q1>R>)Cl;;d+H`ye+nU_!y6w&9TX~8r>MNP9uEbMT}Y{ZXap?UsS7VU#b&h zY!b)#;n+;>jToD#Ro}J78Jk=S(v27EK#~hF;w9!WY>VPm`%;}4@e;@QVZ2<666X|- zO<%m}Im|EDnsnpEI+Nr=jChH84BMi3)xK0GM!du^ei$#;y2Lr-iI;0xy76KiOL8Gb zyu>_)ZBe{xU#b%$Ug8)(jJGwQdgfZ0ZoF70lU#@qFENi{TNJO_m+HibmpH}`_)ZBe{xU#b%$Ug8)(jJGwO{5tV+tx`8$tW!!Z#E6%e$FMDmSM5u6V#G@vc)$8R>_4J@e=bGwng!(eW^~2c!^{DFkUmBc%6FYTCQ%qSjUxIh!HO_k6~LBuiBUD z#E6$T#t-9djVIo;Bj%TD#k%ogomg@qM!dv4hHX*2YG0}oBVOVdKa4lIFI><2&RbY~ z{hc*rpXIq0ts5`ap(Ph$#7oR$*cQdB_N6*8;w6sp!+0Cxi`V9tYu&o>Vx3!ZAx6B! zJceyiylP*n6C+;Y7(a~Hj3?goi;%2muBGe7i*6%4(~jT=_g7pi z*^SrbL5z5b-EC34YB#?3T1%-TuGI{m`TOUChfh6eM(#sB#>cgnCwhEb`*}s<<66!W zJwC1_y`u4PZD=<>tP2f4coCyMiQ7lo{}~p;eo$xG_qiVkUthF7eQ(ap zoWok>y3=l~SaX^@h!HC>k6~LBtJ;_9#E6wR#t&m{^d0LgJMVB!Yd2P`XH6c&h?SVf zuq}#J?bE+~7oUigIK~fSHGRkG%(1dwwi_$f$|etD#7fL#*cQdA_N6*8VkM68!&qB= z$Er2VwYc3_u?{zR5F=J%9>caMR<$qHi4iMtj336DJlJErZX13#6+Uq7cohZIK~fSZ44k*t|9KmiuJ?EgBY$;pElu@dtbwnee3eW^~2Sczl&FxFQ8vD&{O;@anKto)nbHkS$V z{q|D(H}b$KfB(1tv2#w^COBMmZsNM?ow2%#w=weC-CCSiwIAQTZua+5eaAXv^U5{b zCwi==?^rdjTn~Pt$J!b|tXyl}jTP(6v;K%t!^Av>ZPDDM_N6*8<|g78Kb)JAcZA35 z-g9TpIxRR}hwS|@*RgkF#TxeHL5x_5c?{d4Sk=B%Cq}HqF@6|pqwiR?hR5F9{*6&L z)^yE#`#A?OVkM5@h`(D<`%;}4u@cAlVXUU_ScmMrzuB=n)`wnN%U)lJ5i4;FN5rc3 zr8+TUC64jKSX+I^s(CH%)E(<(>AI5SL5x_5c?{d48dm#Kofxqa$M|8a$tFFR zs(qdv9<;f>xd`T z33)%fX?VQiemFc_^*xxeUAyz@>P?Ki61&@?ysF&*?uYx1b;#DR*=^;B)eIok^6o1~ ztgXId)$41^6YKfs>EC(~qlSsKHjC#bwNJl~Dn2nc5y$x9+>~q<91OzqC=r88Hv_rt`9l{khY?wi!UR3}EP#4&ytYhwVhCYA13AD{BHy}lA7R^k|r zh*j-Nbz;Oy9OH+vngPT*;)(Uz>H7B_#E6wRh9hEC`%;}4u@cAlVXUnI#M-EK=k;>u zewY}s631{vtZHAX6C+mQ7(a|Pd1tTj8ot892gd7|C)TNV&9(Q##E6wRh9hEC`%;}4 zu@cAlVXTb-#A<5Yd7Zk&QafIWvsj&W@+`WS;JHIXX{>T?7V8T5VXS5Vv6k!Iu`a(` z?}v#ID>385wkTG$Pe0EQpNN$>#t&m{4ItLxY2C3-$ot`Ug~u!IhqsG(zbVdMy$j!91*M9m+Hibl{m%^V{P>vtIos8-rccY>f8?#BUa)Vj)+z5(`P#I ziCBqa{4my}Ts&UGcf&efM?A6K>AXKoj97_dI3iZHFV%?=D{+h;#@gsRR;}U2`@8dc zs`LIZF=8c-;fPq(zEmehti&;X7^~?!R?X{}Csz0UTVljY9K#W@s{H`oAMQI=9j|7e z?!3CO5F=J%cUyGtuXf|RM=Yg|Or^c{AAGKo{&UwjXn!9OUv{4}@3?H4GOgS4zAHz3 zxAYlbYbA+qtk3v{Kj6#X1pK|q$>HmX)@SkeWt07Uv7+KTU$5p^%)ZalcdR-tM^=(p z%>ZI;d~oH+Ypd^AoolBj*3Fkx?eiKjYM9uq?xO37+L!9YxKALC@x%LsWb+>5b=xVy zzn+2*ydP@z@6PM~YrWOp*ApXF;uwy&Pf+_(ofxqa$M|8ajRC}3KA=0+^B&sRem9sH zu@c8{M67CGsuLqt;ut@S)eIokmM7K&4>{4sN{m>EV>lvKwJ+6)5i451W?K@VzzBUeCIbt;f zh;?iwiM2I=Sj{2bdEN2P$JpyDF>07tYqPk9)xK0G#@s|4#y$I)LwIl5i4;FN5rc3r8+TUC64jKSQ`U~)g05E*FEpp#eNrp7_kz^a73(X zU#b%$R^k{xjMWSv*7Ef3STDZ*U+wjk7_kz^a73(XU#b%$R^k{xjI}j@SX-W0C*=E0 zTZhMM`|nXgS6Amb=h*JNx_T2Muf*=Q=)G{Y8^HTbeaGs&w>D$th}HBRtA2NBWF?8U z)px9#*T!+(c^&@o0rvVzj2b4^+AMy*N$pE@V$4m%F@88VCEJ9@Yy10mq4BEE>SLZ* zm%Q?bz2*=jR^k|rn1|KAR3}EP#4&ytYoqU2b#5{cohZIK~fSZS@_i)^N)cYh_e_ zUyK;B631{vtZHAX6C+mQ7(a|P*|vDRhVO=TZW=zmJFhPswVl0hB1WvlF&q)A+NZy@ zD?Sk`af~0v+UPr09k0m=-LYUviXRM`F(lzcjI{xOD z7Yz;JcWqWn|J^m~yQ9pdyG`4x?mlt)Ot;D^sh{chKT^5>zI`j(ZTE@HZsQNU$*KAI z7FQR2(qj6+HsojYoC_TL#TM5VK4fu>pDDBd;@In$n*F0{-94W;#0PWbMyb&T)$sRs z8?%m`>3(?^=RoVm+yB4T)Yski(z?-y)@I}XueGZ&aWAb;TLGZ|NN8flM&WCe+vIFUVXWa3nJr_*= z{chyL{>jG(AM)gRp>6Wdem*DH-uqqhK>_VlYtQ9uqdxClcztb`7j{@mKJ1@-lDCLH z!Yjms?dD(nd=7ut)|L-AEY9)4`QY^VlmA#hWB=_{AL*ZbjO>#q`weYda-YNF986n1 zes;wj$1R7CTR*t3Pi?h+a(t4%7k#9i{e-r|A8Frr@i_U+IPmFP$p_mPa(rkHKB?`_ z%oqE&`UoHDr04ljGRM#7?sd+*oqWKtT=&-x_@uTrUq<>TpJaW}N80@?v~?uF=g146 z+>LxtKZoV`z(c#Ho0Zh|cB_x{Pd-NW$y0mwqvUFTJ;R!OfQ|4$8^;#fxB1A8H-K{>-ztub)VA5Xs`VrNlTY$C(MQ^O zUTABs?ac@I8gcl*7xg)Bbko{t8@6n|*gyFg;bS8CB-i!kgYo`wj*sPZ!Zr2E-Fxf& zBmI+4vZ3fB?KrRK)i}vNAL1O=GjOs;yME9{ZP0$rBiFtE`#Vj$$&N4SpL~q$Qzx8L z^csuc0}hKFKJZ6QFFv+b{gSrHC(jFQhv$d%Q9j6*arnR&^||kgqxbpr z_Mf|vd`yl{va#qR?I9j)Pd?h44|3GX@xl3RPR+`Y1b2ByFJ>2rI(%dBn3yOI9MC;11_N7^0tUf9^GHy`lY;C(v(z!5ne zGVc$SHGh#*m@oEEK1TSErj|=w;Zm0X_JfW{RaCdpQJAO2ru><+KvR~pZP2v zvy$4{dS?IRV}uWRhIp{Ov7q;S2wpEZd~mLE%KomtX~&oJPd>>s(MQ_ZZ)iIf;B)=Z z5!+KA)K9sg`p{RKQ?ruV+WCb6>-6_#=_owkgtbxd~h!Fl|nn-*+2Oh;X|G{ujn-v z;B&;!4(oVcPTt#&Gk-lrzEWFzJ!Svolk6q>2(Kb8IA%`s>+_>cF4=+goaHOWNBuS{ zsqM|IXZBA%M);7Yp65s9)BSvYzRo5c^PzG0z>`})sjZz4*+2Ot4bew<`B`XdE5PUJ zwbm?3Vh@M{Q%@uVeq@ zlk6?}2roYiZB3T{U+?Dyu%JHJ?&R>nG3IOe5B91*x%!v&{zv*JA0zwZiF1oy=84{X zFy4oAeBcwD4tnRz+83AnlfM6x@`CIRHvzxC*j*t3nR#Mv?INpu)Pd>>$qK~xed47~U)Ja4^vxvJ!SvoV}uWR`dMhJ`L&wetwVgKY~gb#VbSJ7)^H$R{0 zYhJl)NB!jZ(B~Ig->qFbTGR0*{gY4f0nta=d0uF1cK7qS>-J-ICm)P=O z{y^=N{g&zcBmI+)k$vhE;=%UeseV33Kl?k&C##7l@JnOIL&y03{9+&Lwub<_gx~jT(**;o7(!bS5_Nh~d2iwhW{CsXYe0}@*7xLD~ z@!|RB@48pDvd)F}dC=teBp(!gq@Cx5w#o1Oe6G9TD*O3YRzD6O_+tGW{ofZLYx}GsJ`K=J$R+Ke_Dfo%NIBLrynNo?N~DgrBKC(m(km`-?u(&VEDNM#W#x zFC4IQ=lrvazka?yWrNynCv2?sBmI+)k$n@%r(E^(S^eCv+V>N(`pNOJ@qOWt+Vb+J zRG%E5kwnr!T(a@#>tbUsti$cj=#ek^@B_X}28*K=O|E_jq`i-=LZHhb=qn zXIFpyTydbOe(vvf(E5@7$;ZgPiR9DR#Ls8H?Vh*eE32OzAM#)N{H*F3$89pz>XYM> z93=WkJI@Pk&8B`n8;wm~)^YuE_`nzI=Yhq?S3mOYyDcB~Z}kyA)G5S+?XAuHe6G53 zv90H7k&e1`vd5cbpuc(HRoPi@fN_5OzR zPd-NW$+LY<`2XbNz4?Gw%i#lW$|?8$MvhN%i0C8jVJ7V(jWc`m!Tj?=jt|ambL!;7 z{;fX3hdQ-;4gWtD;Iq%RCwGqb-TdPn`ATi=csJ5N`6M3_eWX3iq?k#;`|ZB6!HZ<+i2h1}qUHQC7r+eVI0W|aNctfaO&KTH2sAK9l)dY&JR1o&Xx zbn?mS$KeA{ZvCXTww_InPx4{WN80@?w6$@7UmvV(PCi-vLYx} zQ+xKKu>c>;E98SSld0kQNo}=$q<`{B4i|l--OoZ>&4GS>U_n0EHgbH_LvDVS{>jJ4 zzKP^hKBzaJtbQCm;NsR#x@+GLXaD4r93lD$FO9>GS`mD*`pNNuH}CaJ`X?VF`{e0o zp{>IQ`}M(k*vuEKlS;ULQd_N`9G~P!(MQ^~XFo~~@$q?VPRzD6Ocyj9}wbl2Zb9|DcL?3DQv(VPD5Bc@M+Rx;Z)lZI( za%xsmTb+NTf2)t|n@B$9P(L56&rCk(JNZnweo|YlpB$g$XwgU7RS7?89p>kQHI&H* z+eVI$dTUlvTYJC3{;fX3$3*fO{;;19);T7htbQCm;NsR#YOD1l{gY2}jOZin8iyYx zhx_?p4GQwf>L9~I7_$1RsA8GfiqOFZ1 z{QAIx`egN!LdHqNze16u?Rj{{WyH!$*rGsSFc|=KFP77kF@()XsbE0 zw?0|@!O-YndK>#33t`c6InF!2nJM~6 zyI&P;HOKn-V9itV!M2g(qaK=-)K;%w(!bS5_Dv+8@{9nVbX`*N$?C`911@g;r0bb# z{p9#0vqT?h*EsyB72t!lL&+zrpBx|Mls>=T?Oea4f2)t|n@B#x$NBZ?x_(_6uAh$U zSB_6|yyzqCepR$}WM+s@?)sJEqaJeCFX`XvBl{+jPh(bZK3V-Zd^A@;oqSA=PjZ6j zBkdZ8AB{!u$?7M^2j0BbFX`XvBm3m(XQ8d;c)vbaZ<6&4>*TU<{iL=!|K#{2!=jJ0 zYtMdEKEcliYeJF_wv8Mg_13JUwtD@N{;fW;ZzB1O%=Yuax{c(M)sMpmT-^C5wbl72 z$0wOB`bfLR;YW=V{d};tBKc(XljEbDnw8X6uV2!?)kpSCB%iSWAFO{!KIl8SJX}Ai zt=3PDPjaH@Bkg`ww6#3PuMgHJBp+-WIX>#4SxIg6`X&8aePrK6@@WP5U>!m7$?C`9 z11@g;q_$c=IX=lpL?3C_IQ%G?>({62`76gqIpvkNN0KGF_fMX$y@zdksJe6T&t;e%stzS3R$yu<#jKElUD@)?WZlhsd-k8;Xg zzodWiNlp@dq#frKz0CZOKDqaMKi~F?xd_^H>Z9-XvVZb1!pB7NDStG?r!!xT93PD_ zr;qecKFM6sN80r~KWatr$@1m!fu~M=ocfXe$;Ze(b@H>&*5L)c^~tUmIX=oMr%#Sg zGEel8cJ0}Zl9T;>uwEL+7tTx;hwCS`wbu*wZ}kyA)XC36TSo$Xu%;RLVB5&?Q4h^Z zYHP2jM*1h8WWMMl?RuUcHBRyCgLTKqC#xTa4?MZ`liKR%U(!GM7}+OJKMQRg3-H0( zVC0k4PmYgrYF1KP%ZL4wPx4XGM|f$^eq>Jd>x1h6`QXgtig5j;wpu^ZKlvEhCr>{M zZ7qK+#3wiZIU zd+U?cPmT{fv^nMU$?-`}7JZ~$d-kK`G(R7#C)H6uSBC2+wblBO{;fW;ZzB1O1o&Xh zC-T9zk>jJ$H7lvDym)=z3{?^oHs)kpY{ zr=Nwkjs^H&?IiNa>L-GyM8seIxS0naMwg>nF9< z`jP(0$H=~km)=z3{>zVyqeS{Bn^0Uy^a?{TTYs-*NRzEpD%BfjN zZEe1c^iMv?>7tLcYtMev3h=@DFXV$WldHn@liFH7?4Nv$@F7n>3vEr#_3MK*TF3|6 zMvjkqXjW2NJKotp`6OqEKEg}S^P`afAFQK7K3V-ZeBjBgpVU_0Ka~E-$H+c;`dMge z;}d>;u+|CrWc8Edqnw(R)K=%89G~RlqK~v|&wex(;L~;ge08{fI_{sPf2)t|Qzt(Q zZ8e|t>jMkwlhsd-k9x@6ALjTZXNo@3uIKqt`MlnIvifoOz>`})>8{>COaE3M**B4V zS`mD*`pNN8PC0#Ye3G+7A8E&VMX%%^{rX^i4CV{xCtnEHPim|4kMwW#k$n@%XC%M} zYgmv^RzEpD8e_AP+S>7Ma(t3SqK~xed4AL|etodc1o>q3&*0CU;-1RHRM?K``=NzBpT+v6`^*lc^7l-sw zK3V-ZeBjBgpLAERU(&zTNA{_cpM|!T|EV{htbTHQ;GxYar%#Sg@(IyL+O=mtY8CJS z558`3K2K3?aI}*T`?vZC9}~%E_%mUB$Oq$W%;5tT%BhnNfA5-nV2c-dw)evSCztf* zgZkNEU;R9e-tzexPKopPtv`_(Wxs9x)IH1a+txhC;-~hiSj>azcGXF374mBnEPCU# zs@gBHxVq@87CUaRHbJ(V_cMCVjn$zAi-gap#m$!=wm8O5bJDNferx3Rj-7V0IL^;P z$KF_6s$6Vwho68xc>koF8uE9;aSrNe{=AcCO_?!o?yQrK*hJPiSTOIDV^5hqZ|*T4 zJ@wdAW-XYT!uIc*BY*roa5sPWEpYh5%i=VD7Pk*&{}<&??Uz)Ekw0R`O=14Ne(Qe< zpV2BY@<$xw2l;dQtq~)C#BqKWI`+n5*%|m{PFkG-TdLV(;17!Y5pwc z!E{@cKeb;{B}V>;9XEyf+xv#^%9>H5Rbu3iIK~h1=k!}6M*fK7{48|rjm5~H*H4f9 z@ptLbcR%^#@6EgU!*9(q7K_vTS9m&2``H??dH}{aqc(kxR0c3;RLS} z*}CoWyZ>&S_|1QEAV!?TJceyioN8}s#E6qP#t-A)5+Da+#7WF! z*cQd9_NGRRIEiEYFix&#fOGnZlj{_?abk@Eav(;W#5{&=QJiXTYQ%_>IK~g-onCt9jB9V;jM$uU9St{U+M}X=#@TOne%|Jc7&#+W@8R4dXRrV0e@t!L zj#`YIX}fv~X^otH>|6gLex}#fpMF?|k+akOWnF3S?Pt*AjO&x2=h&Qat&z^0W$!mg z0BzjPncLnz9MTAL<0UtqA@gaqN{pNlO9$B|@qTME=Zo zPZs~Z{S0!)@D0Ley2Z$!wyUQQpF!u3YXqU^e)7k)fBqWzQ#Gp9D>gpoHtZvRS8lwD zeQ$s^A-ypEDtF&&^GA&Q5z_~@AwS69%xt_dcVI+x6+Tqo-c=Nqf|1 z8@KC|?QI`Leo$vyrUqP_3bw)e^xetKoHL8H%5|qYV}-}g*Q+@e5o=@n_UDmcwL-C` z_KcNlT21s=xu({5V&!^S6FpY0*VS*V-$;vrYjGh~ti!e1Q+s*eYf9T!=)I=Dto<*Q zEq{N{_TZMjs>G{4cbvuTL+$@V*OOP)xVZYd(mgk|twt?AZudzwZC6hrKd|56;LUev zTUH%B-NBBZzcW z1la0sLw+zmOQ|8(O2awcF@9k8^)}1DtbO*^$p1fo^#^kQJXF2(-`2|N3i*G>j3c{h zEEFH|zs4_&*pFI#=NYTlv>j2mUm-u0^WNszS8LDx+rF94=}Ql+J+tNJ(R`NFkE*S4 z$=4*V`L&n7vU!G)|5N^QQ1ts>gU&zKrh^JAl7Fr>_t(h(7rt=qR7p#f82KlbW7#H@ zH{}02>#ifZj#`ZTYrA?1`MLDSH`i{sc-2bVvRb=t@|qcb?v-O|+kN`=((d>PjDOhw z{VBhYxaQZs_0p0IBmdJ!j%U2qQHal=^Ut*a(eaAppKJSd=HI=aYUB3&@3yx?48@1} z|M`zBq^?zBlSFSHri%|>O-rEmq;i=}EgwK47Q48AM z+s~kD!Sq`Tvz&QB))W0})WVLt)p^fU{m*xOI>V@i&+YUm+pTD!9HJJ!v`PDO(V?MH zi%|>OZrucH3bmm2)!Ly~Ov>g6)WVGG|2LWsYT>@U9}xTbSW7j-sD)X_PmA_5=vrv? zTMN#69$d>6wZQiuv5qU%CB}Om#5{&=D2I6eQSFVzcz;LR)l*QW)e6^)T=&bl#Ty;(*_j2MY| z4BL?Y$fMdDi;+idS5F~7%p=#l#ThFaBiFHYW5gP^?41}f67v|gA#R9K?Ty8VQQOs1 z$PZ)WI=VQcAjXa-c-AV{#&u)Vo)x{?waT@5`-ze3?Yc2ytzAY(j2MY|4BL?Y$fMdD zi;+idS5F~7%p=$P#ThG_N3P-P#;83jdbRV&HH3XJ9yC1f)HyR}EeL(K!*zitdW>9` zI6lUc=5ai7t>K9tBiA~Pk8#L;7Qpq3-59ZUF{_jqwMxum*oMXt#-rLBi!mOxT|I^T zAQxMwW?VlRXXM9YN39k-7vp|_>m$4IVr^vj#fup661R`E{}1s*ylQVOM!edtoY zZ{xk;_W)KTUar6F#*4L=$%Gj367v|gA)bg=?Ty8VSKHN7$PeS?`p-CHh4Z`R$15DO zT;JJ^7i&9{2{Ga&<}qwTJQ1(j8;cRIwyUR*AI94nPk!ZjF8i*ktRIbd+utvK%c!zY zH0if*sp_7h5YJ`i9LGNWyES=L7u{zu+prD!89nFUt2-a{&E49z%Pnrc{DQ@CekRq_ zZas@v`JbA_dOXbMl{Job?Deyn=F9qB;}}24BktR|E;r6uCH?3A{hJ-99d~;}+1#6b ze@@ogp2)d-a`nyR8FzV=7`Y=x+*?J<5P)zp4Hi;+id@9hV9RC{AF@~G{-{V7kSk77(bJ0YQLVv$fLIR_JcgCy|Ea1)b`$fn8((5 z@+kA_!)bS|UE6D*V_uz<{#*UA|5^6HQ~cb1Xoxs%uMjKCa31lS8prQ)n$*J*i_d-j zZ8dFIPoX?6di|a??bld*=NVtKSiOY#P`6~K@b&1B`SWKjs4X}tdmpUey#?qtvRk)q z)7QLGK1WQyZ6+AH5y$X^ZX1_w*}ZMM#A4{C?dqvWH|^Kdh@l&?dg-lOV?er@-Me+W z>-J+L-k~94=tdmF6S`e{-h=Y|vczKOrtRvfNH^`*)QF)Qv3lvPn;DO8&Nv;O+O6Bs z&;HKpMhxAEV|YThH=ee?e73U0V(6yr>ZwRK?bp8)F90J>%O|Ic<_r&ddU zGw3s;`kM*FX?uk@hUZ__-l%%O*ikaSHLI6xy<>)}i!Q8w<=n08b&$S7x{aRmx$4z- ztZ(N@V*GXnF@0bg_H$!(`TI-aXS90Kv72SMw(u|2_xyAxA0O}E3tqkVJ~h?BSiH>< zH&`6Q$IiDqN6fb|_y51?&f`soug~wJ3=t!b#4$XP$Kh!kh(1k=kw7st%?`j64#nmvCJ%kB#x2+%s<)d2r=?VtX{&p;kUr1 zw7w~RMyuC+>Z%MQkDq;Mxvg9JO8Erys5+P$G4e=EAJ~TCVjj(S^5{N~|GqnqKe_Df z_IgB&JQBz7L>|??S$+O9?f12i$DjT4Wcz&;`U=I1d3E25_mOeE#A4)8+vz2&8}hjH zvAXyft^V=B-7<_k{^8RbOM8c(U>-FtQzJ$miPcM(5A)a>Pae~b;0O1yjY{WzMf$up z`oaM_+qj7F97i0(^MBubV)fj!CRyExXMB6J8gYytT#vM0V=;8ocJ&mh2kMsW622bo zJ8xm}{r{|??6W86R<3sIw)(kWwe?91-H4TCIKR+s{ue6ZXVhZortRt}6fbnsevQS@ zP1}3>p>B=w>1OLKsdel2{L5ojH)7~UtSpOlyXo}*vAPjMH{uvS&`tX_7DG2}S5G1R zshb&(Zs`~GI8Mhry3Kg--aE*I7`hSj7`7pvsJHSVQLtvFy0ylmTiOx)l+q6il6M?`^UI5dhVcFdezh~b9xa>phb`^=MvUteaSTsf zr*8W1|B`xZTHJhjvBfcdnv<@p?zhE9?L0|bTexwJIK~h3SN_Ig=&$YSDU?U*pX?ej zPloQ>({8ALW0P+E_uKAyd!IlI{fU)jxK5z|e&2Xp>c43*^w)Ov6p9!6e{RF?*!#3? z7aSw^X{r7WPkIXZf&OZ5EQbEtPA}oOsDEQT`oFR$yH270&8FS@Z!|Vp#{19^G4v-^ zmSLXI|BMB{6y2H@Lw{{oPa)l)|Dm7ywB&KL`h&f{l3~>UX%}r^dD2tJ5A;|5#$xEN z?er3ki~5`K=>OQ+R{z#!-TGg3VySpYe@(;@fB2lVw~F4H2V0iBThbh3X3RsrJTV)Tg%7OL(3^on^nfv0M0l zKtZHv8My%Sdo2)<)m4&a(4Pc~N() zhhOvtxn2$p5hGS&Wmp`m+NZxsEItt{af~0vYWj{<=h&7f*5!{~EZ3BwA!5Wz9K#WL zReNJGV%2u_)GM#8zGKxI9zMG}uXDe}>+sMJF=8cFhQ)bRds8Duti&;XnAc?Y9^-Y} z@TZ&{ugU)H^&9Z)#B1k;M_(=XYC}WBh?O{oBl4>D#$v>(?dqvlUK<05b;J|v`cIr_ z=O$vrN~{cv^Q!iyMvPdAWBf3$rvF%HGOvvTy7PL-`_{Dg+Qf*JIEEwgs`kcW#H#J; zsaIZG{l{v5>uJmrYvQ!-j#cy8T1jGU4ItLxgS+#( z;DaBs_rt`fVPaM$wna6p_NGRR@k$)yhx2e!36Iyk=gypUT5!A$+56$-knUJl`}XVY zXM@Cul{khY<|ef_79&<|S5G0$7;B^NSha>nJh3(wm+j}i#E6wx85ZYN?M;msu@cAl zVXUU_SoMB*>_gpoedwk3?`VMwF=8c-;fTDdy|EavYP)*smDg6^v1(q;q1~}wcG1Ch zyb>c;Vr5vISG6}aV#G=uZw;=8~w*>=ceHgcgI@&`Sy0a5+hb(Wmue7wKp|l#7Z3Fhj}&q$7-L|lf%1XZ9KNN zy&on*zcdX7eXC;Z%3?No>3gj2b4^ z+AN-j)js|EAn}R0i8#g&=ceSd;qm%`*)!8$A__mj!UsO9X?bG3bmnUIewY}s631}F zcvX92F=Ewr^%NTSjI}XZgKTM2RiDNh-uWD~BMy%Sdo_giAF@RXhGd!_wvDA*& zse-duop!Peam-@1_Gzqwvsi6Ay@cz7c{KxwwdIL*`Q3UyOpI8Gm0_48VpaR}^BnPs zSczl&FxJ)pVjVuNJFgS+e)!Vxc*Xtjb`kG4jm-4L>MGvG$ZL0NabDGa0QbXv$Lies z&ssTRHGRjb_lRRFNvy2_#A=T3&g)gbKE%G?M2s3H*4iwtVYN@cza%~}HxbA9;oOv5 z79Ot$&YpWh_-a%*|#O_$Pzw!N*u!x zc~yI3F=Ewr_0%h`t-fQ`d3ekd>!r^9Ffn2!R))oSRr~arPJAL(;ut^7YjSzKm@)>EDLhlvp@u`(>qtJ<3y zF=8c-@x#2DzGKzACUd*<>b`$Vj97_dI3llVKY;g#`;Jw|>xd^-Hx^>VO6+b!wTD>M zZhZHMrPNV*pI+aW?ffA&)C1mQYRv1-rSekrYR7k2pYe^YB=MO(<1_Pp`D=e>(azok z`TXMZ;p<8J`?8@wp3!-y{83-5TBAg-X02x5=jl6E=X$b|#A*f*>+ph=Bd@K#W7Qf? zPVSC%^CeaLyhe-~CT3k@TXa29ds8FEeFAZeAKoVkOx7z!9 zV#G=u!x7g`wKoPU+6;c@J%DzZ*=9Sc#QkabDHl)QAx)af~0v zY6cMNm?zc)4>{4sN{m>EV>lwOYHut?tlF-gdgZk>fLP6`-Fcmm?}uIy9ucY!I@i~gBvvzkScey`9C>XGAlBrx?pSyH^D*}N zN{kvNj;&#}H#K6+O~f&NI1eXRhR19B`?9^q>xd`Td)8jde%?roSczje{y*~01Wb;q zT>s5pfI=<^1c6ILE{p>C59aZG6Z~#Si z8_3EcjesJE1TKML9|Vz$s1b7EUvJg-p6_(^IbAiK$#c>4^Cb25sZ-zio%ejFs(P6| zn6FwtG#Y)?a*gDh_v~wB2>Mz%rFmQjK77`^A4-kBQY&HW>#Noe%hc#Ab)GovYuI*Q z^*x6HM_<=l@$cquZ>Z5%>O71XSFIl!jlOERM#>u3dfR=~^Kf`-^SHir;6yWDsnJ(z zC2T#eT0bmPqp#F?;&5DpD>LS6j{D(`)0+GG-pl*lM-FQAm74po^^FI{RqKaFqpw=7 zk$iF3*Gl_+HQ$%5JNo)UbyM>zwY>o5%H% zd+#yxl^T7e&cldt)%wwU`Vtd;rOp$FeXS2cU;EBz?(2xF4>j|Z8hxeC!-&3W{m^Li zRm(L}Rt^VO`R6OYf9Jcx!ms$f(P_Sf)l_sq@6)xK@Us zui=@^eVu#Hb>`eejlNRnVZ^v<{m^LiRm(L}*0_d4(ASP?b6xX4(^p!eK9FA*!2>M!g^!2j$CYiocqp#F?7%{F|Kl=Wi#6(}I^Tc6agR8UVtN#Ma z`5K(nJg%?I`@Fg4P@}KZc^J`GtsfeVzG}Hf%F5x&5cIXr(bq4}-obnqf*O6LR>Iaf zto6e(HTp`OCl1Fo9D=@9zSBIe7uwu%L zpUC%{uJO;;O72?uUbDe8hzDrjpUp6>}#d%zUsND5;l+P@|~}n zdw**5m0Af~kE_;?{uW(gqOa6>;;^sb5cGAx(bofSTHWL@HTp`OhY{nd^+Ti4S1s2_ zS>sx7yRVwV;knJ@TAZuDFGh{NQY&HWan<@^nHqhi&J%~@8q8=tU;TH(dTy$Jueq-a z58K+@H&LUn)Oi>&u3A6(4Qh#rzEbCj!@gG9?yJt%?(aAEb*m|(&AmT0`bwRL5q;JA zq0#88mTRP}91h#=tBz~mdCh(O;%`qg<4TRbQY&HW9M<~LcL*dV`bwQA4#%}V1bwZX z-`v+vkq;GaNB*=`5Io}8ds&H`oHdL z*mhs_n$z)ve~!M^hoG7KsW>b?!M(YN5je~!Kl-`oE^=k&OhcPSL&-@M-> z`OWF|qH$kFQzTQBciF$^=$ek^w~(8B?sc zxZwFii};7~cV3;cYj5|NbI8N?$rGHD_&`n^)9Miv-8_Y@elwFi*iKFHFy^iMJv6#L zy?00Quzm7`!b46T=i3IO-8>I{`ovk}K?3zzJcvbqCQSbH=;y!rk>O$cyD8@$mQ^54m~tr9Zv&KI4zH zZ~T$x^ohs$w!wkfJjkDJiwCioKR2AS{OG%P`Mc&%iYExgA1U{7usrx`HV?+Ap5npr zV7hF_f0SQ7WtEx6A8FtCBhSN}JYi{-d+hViiUPXnuRgz%oj(hO2W9jIRj>X`W%S0s z`mx~&Q#`@B;*XR|c<(0M-OY1e_Xj(Z2iuM@Nj|fTOt0}*} zg2;~WZ^@cvtha)Q(?%}0HQPr1j7{;N8kpYb-EQ<5Q|?fIQar);#2+b_@ZL>j54S(h z{A60gd=G^uJ>OS&c;+q>_SgB&_KiQn6XxUz$GCYun7sFw=nuBF6c0N%e%+Tw4|{8- zIUk17K6!%gi$7A%<9yp-e>cz9H>{g{#&``_Jcz~o+58XRD3AH&`)0h@K6ygnA*YXn z<>5hYo~PGX)?B|ZM#J~j^D~bH)7I6S%I}^y)$p)=@&xCJKf=XhecRxLE9TnseHE!# zJLrXNR<{nS2oJ`N-eC8tpPRDhXMS|(oo0SX`{W7bIXSJ6P8l5Bcs?Z0Uh5rITvd($ z!&Hk0#blbj{I`pxV9TAz!}iG&oG<bf z@h2Nr%>5YKCr>Cm)GK6!!*#2?}E zaj-o2FE`KJ1wY=|9p03JM z=6Z_!8I|I}vG8fDt1d0pS2#rHm$Xlw;5P9`%5hw)N4Wf(HT(P=Nagb`KgQhupeXop z5;r~Hj~KRL=_?-(H#}^gJfZN=r&cW37q0B)xwH3_hViPTco_H36Mk9TqPp9d{E_y_6WlHSNO>z3?5mG%yuM>@^FbawuUS0m4fdq` zk@m?G%5(aJBU(NBcFE=e*YKA$e-KZZ8hO}0d4gYxKf=|D1^dEXvw1LHl@t$-<#J&#;q2ky5@e2?UN@I9&+k&Zq>27o9CI;HflH@h87PZ+4&RIHRnUN zPoCf&@khAa6j~Zo+&r(X6`1>t_HlvRe&!cu?D(?^CUO zR&P)%HNT{N@&xyaKT?h(T0QD}xc$K~jFA z)Z#%b=Fg;2N0)~kwb_nleo6b}3FSF`@^P>{_>7z9k)9RJc*XNQ#Y3i3JICyLz=##i zc(Hx*1hd2+;bObKZBTvL`Fi>39V&(g#|AgJ^XI^y4ln<1^JL4weU@hiBtqX1wD3N%4^B%2k&uE&pz>;bHsa3Fe4D z!o_xd+u(a{p2r{frs2V{!B5@!bHbDtOFir8`w48HJfZNA)5pQ`;5;`^z59Ig{xkBZ zlH%d<%e}s4`N_RTs6Wy^d4m3};?LK~$>V(6;BRj@=ht_)T(dEMEFQ#S{%o=K1*HR@ zyFuUok@m?G%5(bU<6wF4ftzR14i_7qIDb++Wct*nS1SdtZKAKgQar&+;*XTGUEelX z=>ELv~Wu$HDU8we#+>-;d(wUoZaoZsSj! zKPes_f9)CT@3PNLcbNAR!W2*NviKwAJkGZbUw89twAli~6Q6%99>ik)JlY@Za`!;L z;bHs6AK@XVkAvmGfSYH(;HlX>Kgao#;vv(nTWz_^jE$er{E_y_6TBk+NIBc}ZNoR+ zJo~)AX=DD}=+2+lrfxC%TmQX<=8v>bo=~2LIeCJ&+&sN6Y-YwQ&Yu(y`#0y>V)>HU zKhyJ1iYItg{E>1V=i7#FyLr~!@K0vE;{36A5R3V<#@!p2e}2O1MRWfn?HhmOIeqeR zusnFj&9mAOYnbth^C!h)m`?hi(mtP_To}ZCm$Dq#Q9_KAd;Ow zQC*YIY@a;AAH^Txa#Lt&3SZq;#+n+N9= z^5DqeXa4+&>T3R^c!IaYA1QZJXld{jA5Y`^W0e$-Mo68XrG4X%Jf}~3oLdd#;ED6c z;z1-kf1;=6{qQiw6TB_{NV%IrOY2|F_9xDt6b~XeuV2!>@kgGMQ(JbczOil|tgqaV zKR@^9PgGa)C&d%|N&Jy=H-(mlUvu+d4P^3QTS@U~glaLWtM89V`^F!69_HlfKG@BJ zb&AOo=a0n$7CV2Uy88XG6i@Ju_#@@o54Wlu;^x6x!sLnbC&i;o)nZgv&p*<>@kgGA zIe9t`b@O1oU-F>s;1+-WM0GWPQar(*#UCkmyP~DRVQwC*$x9w=D=8k0P%TDv&HV=3 zH~t7un3HF~!-I8m$rI;~#RC>Qf1EP!<;;QN4R;Ku3x|K=TF1+E5#H1oA@K;ZdbH4JkrOLx_+g2G(zh7 zCG8u33ED_Rr=s>P_TUcaP$2Q2paC#tLGpA=8< zS@B28wI6O(ndIidT7Be+^C!ilOx0pkSFc~vzVSz%(#Hky8UT-{gvWTrqt^%Y2WxG&%>NNeN)^#XpjCt*R9V} z)En$+JRgQBo?rv!f}11phAnNO=}diyoQ{qxu=ffJ@|t^f1sXN|&L4{hk?j15>gwlT(mr`Yc}`9@g_d@o;pV|wQ{;*BC&i;o z)nZiF@UVUI1ow+S!lf;{Rb`r+2iF1e;K<+^fBrO_f24i#gz}u6ZVD~!IMc_II{&12 zG(u{ArFen|#2+cwB^HJc~SABzW(?EHzIn)mD2zVSzR=#!g5O9yiB#QBrrL4<@U z<&U&ap5Q_8N6NKjx2oUf_6O?|HRR8HfBr;u_5364lP8qtVNRaD+ub}^Lx?=sR#H6L zyJ|72YwlOsK6!$N#2?|(B|!_9+re#jH&kHv#XcK$?l&HXCdCr>Cmd9bDpd9badcr-$_7}eFkFG%qO4~suiuE)7m@Jlxj)}0|woIe&1BH8&9 z)tzO&UnlJwf8;rRa#LvOfQJWb!;mM=pA?TWMem2-dynQ%iYKUxKT@tOyH&mB_6O^? zkOxNw&-wEws;l`U?HhmOd6<)@?;bY~)>t7Awv`l*_O4ot>gxF?#S=Ut{z$nV=T_mp zZXT?ILY_E(EFMI%^Czln@|o=$e}pH@$Hhh7fBrPwKTG?@A9+rn+!R{cH`m9Lx_?gb zXoS@LVTvbsT>O!8J_Dfo{k6IJXjNfJlIxJJle-< zF{*3kdzj)0ek=Y+xgO_M!9#8ytXn{yIDaf2M6&ZIs%x&NY~T1JJmhp!Xz7552WtzE zC(fS~k1|z@QC%}$p|nq)pfed~a%#(NRsWUSAN>A5d2nR#f^R%en+1?asF65h-BwaRM%Wj z**x+hPZKPet%surWV>W{Qfo?t8SN6NKjx2pWw&C_)KdeNUh z4c9MepFE*Fr%!GQE$w*J$CJ8#rFb+#>inGI3APr0q+E}4tKhM0o;ZIj9z?S9Cwi*a zFKOTSBhTrRn?g$ma`43Ylj1>ygem1uiYM4c{E>2P*{%96bN`+UYV-HvphN5P6!ivs z8hO~h@ke;VoII5uX7ga44S%ii^BrYsC{5Avsy;z7jpc!g0# z{=W5hqW$r2Tfh479Q?L5>f=HE@yBNtsgpg)|I03i>?jnDeEiwsp5-Z83^it+#6I4fOX)t_s0b^G+LuXzw_tux%23%Re!qC(>J`>=sa;I^uE&xzwl9^Er>{E3HbB)^qE zxo1ke#=#NaJn5(@<0ntVat8$*ALqCHUb>y%_^ouriyxwq-woxm+vhvZGdo=V7UPtR zj`G`RiGhtbPL$tTf3DHUZ!OnAzBtJ5zG179r%a9frq(Ea9^|*S+iB@Bmexo<9^|*i zInrq4x0YweL4Irf=|&^JwLCix@>_kFVKnkp%d_L4zUE2IXymt+XUAcF^LP4jL|geS zzrSziH-3Ac1k}iHYVO06UL9S8ZXKFly0 z`KsmFaZq3Lq-HeoTg$WKFu%F(1CD4bzhylKJHN4(0|}^+-_+cPt$+Ng#e+BLWSo-G zQGOe31@gv;@>}cAH5&P?Hr2l@TnM_ZGpOpW}e)+qjdA-}cVPD_umv_|srAip)v zkwzoGwLCix@>}aqHyZh^<=JtN-|E8*qmi##o*f7EHBV|rBfqsgI}Y=k>xkfpw(?un z2Vs6M|LX2@y#IGL;QF(Mvo@k|u@?iO74W9J<_eY<;%C2AHv1h$=y3u*!%n7e8 zZSf-SoBNHfE?Q`Go;Y*Ay zzD8PhS-cu*etmM#L0>Mf*vtA|(bzubbgN+f6OxKqkl#D5*duW|%h&BcKGw+ZcfRoImo!46TIKiLuU%40YIT)oJvI9OO5TEfl7g=?U`tSL?0O%;Py9A;15z;yKc<8Ac<&wcOx%&udA3vmWw0 zc_jJGXS(%`JM)|C9N~zz@>|v~`XuM~L#v%WGjXK9M2-BWR=51;C*=29Zy$MYQg@iq z$ZsvzNIu_@-)paYvc%~uKf3C-V~zaYXVgu?X~k)A{(k5GZ6dF8y2^L&a&oMZ->W>k zy?On^NUic)eVAURMt)N>2DZL_A-|73@MGbhVKnkv%QccO&QRw!*Udu9ZRNMDhxJL$ z??cafS9Iq zIZn#0I4$yf(d(B>{jTzfcg>SH$Cp1f?GJ-GfA9L%_B$pmPA}6FrpEaW~_FAryd^{X`t~H1w@{D~q=Dn;N zh_T0SrefY>%|OJ!gBoK`o$N{e_c0CSygz-));tfDsWJA{d3Z4PuOI!Z?UTBl<*(j- zO{~xQ>#60z^s9KzMOOWq(eqASV00cHj@ejQ-w(&+88ggRuB~X#SFEeZcBwID)ZB+{ z>oMbdD>zptIyqOfTqF77V7`9tZ+s13D35p}(HOITzt@zTK)yH_vypqTey7oxuUc-x zc>DG7iqp#M3+5|zY1&VW)+l~|aIRKAYer+fYPm-8^^32$xb`KEh=#S{Gd($1d*4%t z&)c%5rJV;@&yobx$OCHb!`8<)lyi5THw)w`QzH+k^Y9=KezVe~nMvJFqmc(%u91BG zLLNMR%{g+^%(6M69Zq(8{LOyj-V+2`LZK58Ui9Gnj>d3-e)ze0JFJNJ(@^5C7Vf6HAaudeeFXVyNZ_Ie&4jXcou>^RH=uJwu| z+BzS|8m@L8VEtASP$Lhhxer^P@5qD7qAO*b8uQ?%7oReSdGnXj?OU!%f6E(8tA%Bu-;KeShI&x7#tGeLOg?{Bqt3cS!v*HS&qtj^&Gke471OeXFEy zXL-t>SC8+1OVf5NpY_+x&Al*V`Qps|_AXZaE~BsgQjgJ$fvqnN-fRBhn4VZrZ+K<-`ni9}-7kFRxW&Gok@1>Qe)N_dVvYAa zo_zf>=7Rx>c$QfvO5F5kad^)|^Q~qy-m}(n6UOTg=E;bt8rOct5$SodTTjoFk$c;( zb_?-$q2l==YdPEVW3Ol(XT-sS8uNoX*^~V5^BePHgFjAYoDwzW2X&q}m>=)fUX<6V zjq~GcAKY%v=Zxj!!TcD$(Z=R=^9G+jJJy&VeT7fY;ql!1;$VJ^J-1uxca~3B<@8u% zeq4Oc*}3CjerWwJqcK0UoHVWH$L!VKlzvSwfAx{%JqFB=3D4Y?+aJu2lWzRE)bB0d zdi{3sIUn<5gw57CDX?)claO#qIflb;a2>HRcC3_hIYHBg~I6YxDJ7p+t@OL7gWK=7-j6oF7`Q z%EyEGp|uL-?Vj2$zL&xLh_u`nXboQ+%#XA7zeW6s-c#IscC0Z!wwYBozA=(74(5l} z?=l+mL(3V(?=!9+$N%7Jxj&z7H0FnvXUD<(7&ENk;tT8_}S@m^SUdKVx z=Y7nN$+h>S{*3Z>yH<`p!Tk8<7Tdf0@y-v;w_2GR^MhJ@<@bm4qfgcn$1#JNAF|fH zJwLF{J=>HCr&ay_qo9&LhkZt>0xd=7*Lmkx&dvRm>z#>l6UHM(e6RIi$=~_db7k&=ADc0!#$2IBdwlp9F;{*v z>@LPBQDd%9GX}Q4I7{kU!nTvS!e_em@nEj3aaYfrq-JONo)y0pYs{67olceV>^PV! zTEEL^%oQ!qj+4w4*27#$9?4wcGu`_7mCO~^)7RR`Tw%Fx{c)};AOG-lxi-%zf4uv) z@qHTR%ES-;*LBb4ohzDewK6s43boSs^M`Y#((YV|8ZmR_+~&FRaP1L!9atz(W3Eu= zVZ>Z{{M8p2r$mjpLY*hhCvmRa_`Qqe^=D`Kpou?>HRj3&D_-GxT^e^YeSdCs z#$3^Ib;y^SpTxPM)HBT7#az*HRZ+jZb4By5R;I>Wq1Gtbb0r*#xpH3fTv=t0OPJ#Y zYRnbtJdBttdp`SP#wk%_u2AQR^GTd5`~PvQnJXRF9d72zrzRggsJWu`yUNsMiaYuVx+Y^CU5G$-cVHfzJ}$U%*{Ld|{H z`p$QlD<3|-3FDNgF;}Sb#KBw{xch%42m6h#E(*$8u918^)#cVHKl$^S+a@(T%N?7) z8Eae*4*15$Ql1?L*Ml`TT2A`aW%RsLPcmAg`2CqN`W)*#tM@#~=gxM7MEm-M&lhxD zqQ51TvBT#J)OIvq9OTr9sN(#r*S-5Uzi%4n(ecfB^g#Uvb5BZ*JfhCSg*>wAmtL86 zeH;^cwDoS+nfQ$5>mTxH?DtoY`u(NPOj$R6KM#4d;wq2xwF1L<^BQ@y`HiO+lZu_? zG3$o0MjqYt;dxSS!g%8#kFT}I# z_;1E3Q6rD2 z^Ta_O&Hnwxk}v&6BagIPBl&zn9&L5~Hp0_ce)~s<#Tt3E#|HX-5hMBHAdl9a$$acG z8hNDUjN@`ze#{5;H~QElW=Y1!zzYsL1{>Y=+$Vn!Tjy>laCXZ%y z9w+75agawBzuhhMqt9PNR~K~~tx^2rR$cDo^72w)ARnpo^o#dg!H+ZE_riJ2^}bis2x7#!xpH%E-r0M~ z9CA=2H>tS~TVF09H#c14G{z}WBR8q@#6fPpI`PTbN!|X^-~RW1;?Kd5n}2-lPp}1$Gz9{d(I();5_nED69CCB- z!`I+za}&zv8FKTSWuKG!o#nlj`+KaBo6pQ!_D&N(gf9+qQ|osbjoj37jpmDk+|>GE zdFE+_m;t$Y*li!>?iX^iy8EG0zcDwfyFb6Rk|fH zH9cyo@51Ca&~U#Q-F_Bcxq0<@lL{A^YYKH#UZz$;e{ODY;E`7Sn$hn){jt$`;-Ie^ zM-{tU+)sD)Oc;OQQIo$uKK1P=9E(0yE^6+h-8MD)NNsO@sPW6?(~*GO4?tS?3%J1%MNVWQ^lO~KCKiT!&Re066 z%hKi5@R$AP2e(%z^uT9$74Eupc~u#-SIK=hyz2gPvsZ^aJ=V-0YTS2IYsUNM4(6a$ zzeJ5WNS!AR=AhQE8I3up$J3BkE#BvWZm@aOqt3&Le6#A8 zsNoxRo;dJL>(`8iZ(6RAvhuCIIKIjI3Yez@4&RhQ6@1fYs(hTE)O(Dnd9DQwch!0= zsPEC7W9o#ee-*F#2JKby{Z`~v-=0gCSAnc2j4W6t`e%P9*jzUo^6H4=aPEpf6Ir(T zy}i|*|C*U^)W|Dpw8KZMIcU`{Q7`k4EsbUjY<+P)UU5umuQBAQ8a?mS>y6G6r+Ci2 zR=fR1<2^#M<%xs$2(^Bv(Rhzg%Qcd(UwDsD>vtJlns%ts8pZF=+;1OK{;%hFPdLfw z>Z0jJ=ZRBY?nhR;HKVJGo-jI399~<4>vP=8`98C=-+Nx6IX}wrGpRermZ!^7)4R z)OP!gMt*8}b{yoV*6%bL`KjgEagd){zsqRkrD>pq%c=}66u5?BGckalmMV;%)--&CaR(bW;yU&fk zQ(cJGe4<8PQEL>x|Hvz?-(@uNO3O8pj|X|x{Sp7py|>JMA+N4kVO!%rBl+SWuhh?) z(a0+;XB25Ykyl#IDE{$cUWJR3SN3%!_0N?JEG&fvdT&7 z>u;P_TEE0Nab9UT!+76^L0%PZq&lvkqTyfWq4Jj|>5Q03M4 zn)B-M2fk_YiW+%Et(1QMkylpz5;gLQI!_$r)%3aiorQj*alX=WjpQ3g8I8Qsa*gEUL0&0;uhGaWEzgdF^OgEpGa7lN<=JtVSHVwn++St9 zFBjimo!6XK_3rb{d4L*uMXi*6|B+W#{Sr0uiaJjmsFl*siM+Dv zm#C3f)Oq3{udWOhiU0kjpML-9IG2!DJKc9yZvT;2e_G*ca~-5cUQy@aL0)P7E~AlG zTCS0N`GCAS>f-B#zqibOA+Oe7=~n4)b{w3q)X$pH$SW<+j>EhP7bmam`>PL{^J>u! z{CmRayO*MoSJX<`I^R6P^)vd8 ztBe=&idv)i$BTJYU!1&(8bTcWR)4VY{a^BLBUXsYqx_lk@`C>~_gB=&E9yL)IA2-y zOVr3K>O66fS7-col<@Q$eb!%hC~LV!^5xaERmNNOdyU384kix9QTb~|V;r?Sn}_2V z^!e{+_U<{k_4`HR3-Nb&;&FWKyt~rl_~M`MHu*`7aimsCf4*THt@leMq?bcJUb4@v9fsMXy#-1dhMC4;V+~uNjST z)bi{&9LI2R#xeQ>9&=tDXdcJ?0={ohC{SY@sg<(zI9l~f)EGzVJaI6Ne=0pbJE_}m zG{#ZOHImPNjHA}?H5%in<=JsCj>=y%8sn(t*>O0I^~D*-s2Pr9_-6As?(_bp=K4sD zaimtt*5hc^FHvJ0sq@6aI6nQu&CK=jy^XhsHO6tp7dj0mW3?K`Ywq7#>i3qZF^<%X zfvqnN#!>leMq?bcTqF77a2$ghb6g*NulJ*7IF7+v&Ewen!e-{YN{w-(R!V<=aecJv zm#8t0)Oq4y954RbTIRg^>-(3DHO8@fub<_9{et;eSagJ}X%_vhq-cyIbsip!qw?2` z#yD!ZM#>t;%HoXU$J5MxPWX27IIg+jpX5ANC{SY@sg<(zI9l~f)EGzVJaI6NPwmw) zC#lPJ&soW5;ew=I!_#oV>P(i%tvaBBXyoQ7)Pz&Tke1A z{y1hlA5ZwL#Mel^e84y=fAsxE@e1Qetx^2`a2)H4GmcS1h=cDchkGqO-&GzF^>r~b%}K6UTCbotcXrcaf9mM))q+w`epOtVjWZFQnKe^TT8NgbuHdHBvV zxSz4=m#FbKJJcpfBz$r3H#=HCT6aR&@HacudE#LH#J?MUlm9+Y=bh_IHRC354hH)+ zd-jFruQ2CeYIsJShY_Ax^-I+7j5<#oc&7DhM#D2L*GN9^=~<<1&&Hec`+&o(`8iXIie2vOEjh_Drvd^{(bT>tAqzxre8Q zXVgmA+B2(ui5i|!=ZOQ)w0_NKc&6nVDa*5Z+n(v%?Ax!|vx&Et&H0QPo>41dYtO9u zC2Dv^ohJ@F)A}`|;hC0eq%6;Zn={U5Tlv2w$n#maf3s)j9e&MhQc%M)YVO0<$B4OU z)h|)QGwM8X;F;EsenU^#;2CwEIOcpd1fF#t(CpbdFCA~L0o3q}Iu9c}v+9?q;Td(F zIPgsC*Nlc|TCS0NV@S`!_B|WNJgXeo?AbxPtz=$!OZ>nmyCsr36dz8Nu4JS=BC!K8IALqmTM#*k2#41dYtO9uC2Dv^ohJ@F)A}`|;hC0eq%6-WZF{D3v-{v?&nmqg=KGG+@Qhjs zTYF~JFHyrY>O67anbxlv4bQY(BV~COw(Xg|-mDzb?AaeaT-Ds4QNuH8C2Z}PRlh_H zdnfoEF3W;8t0a*dSbS-ow~G|xH?ZT9Ta^A9xVGirE7t%R*Tv+9?q;Td(FIPgsC z*Nlc|TCS0@JPU5gIG^Qsy%`+V?AiTYJ?6b2YIsJigsnZZ>X)eD8FijG@J#F1jD}}g zu931ltF-T#xdse4JS#o3jX9rD!!v3nZ0(s;CMRX3x$&hrbh5C{SZ=QtMo4JvXiTC2Gt~>O65U zH?@BBdp^R3YXEhgIJ^b~Pi*%*zQ2vfGSNT#H_-MuZd~+>@&0$v5CiWy2IHDNyJXz* z=Jh5uJflW?eE7~oc#Uq=FHyrYYR16U7YCkc{hHD6Ov^QrFAhDc41s3@4$od3`I@;u zqlRbHO6cz^JhSSTsNorPo;dJK>qp;H7dCiCohJ@G3x~k7`uOHNyZ%W1y-;d+MxBQd zo>}!v)bNZtPaJrr^=n4MGcDIhS$S3;0?+!6ZT9Rk>-8ozJfl{^)_G>tFHyrY>O67a znbxlv4bQY(BV~CO{4VQ!=D$PZ`7Hc;vu8U$IMKY`q=skIO4!;ntA2?Zo>AwC1JAU6 z&1iV0&>lmd>#;d!{wPha*0NsHJ7%|Gpl}y8hJ)-Z+&wdd8YM- z;PqzPp6P4!fu+o|ux-!uwNrh<(v@fRA@Hp4gl5mKe&ZlB52-OXsdcWjo|{(v5;f)~ zb)Gnwn_9nSG_C41dYtO9uC2Dv^ zohJ@F)B4fp0m25)sPn{Oo`r3DrtdjcCN+Du%@v=TLk?}!v)bNZtPaJrr^`rNK zgbkii=ZQnlg5S43pZQ;H>iH~~-0ayr>+?-&ct)Lv5qW0SFHyrY>O67anbxlv4bQY( zBW2}TrESl2ZVos+JIVTdlNz2;D`D$Av+9?q;Td(FIPgsC*Nlc|TCS0@JPX_QO!KUM zVsoC^pBqxcGioJl?U_~Ie5MJ{ta1~|*H?I^^@rf|&9*($^I6}NX3tDVECJ7~r$+kv zin(bjMel1+!!v4o>x%==wBF*qb{Y`{s%QRPSi^!S=Kdl)Y3cH5VVgd6e{1RTY3!^1 zH4l@&pL6F^zN>J@&Re)BA|v zl%>nFa0ombSjs%Bx9ypA?{#W(o_%pf$=m}_V{TG&zGCaU{^LHvs$ZhUeFSx$IJl3{ z`Zc3*zpCXL$;V?}7yTjQe74m{@2}?|M!X)~Y0aK>t-7vx{Y?$esFkp_XIA|ZH9Vuv z69=AY{hHD6Ov^P=mS>eA@GLyN*|T&0@b9xpK@HESxer?(Bl66uU!sO*)Oq5-Gp%1U z8lGvnM)L76&%z<_tow{+&-Op)c;gv0Jfl{^)}C4QOVsd;I!_#UruAz^!!s?{NLilM zhrqMSv}Vu#r9X#z#(zFb{?5>MI@4=F$C)nAblj+rXVmuAH?E(=*OKx5S=*jj_h+{* zU7m$Q;Mu@Z=2?9RJgeW}wy1E}-F!8JhZ*NnzBK+83f z?_9y_W-#A>K1=@2F#CMgcYCvE4}W@9^F1kQct)*+t*@I_{Sq}iqs|ito@xD>(eO;m zHBy#ml_BsfyrbE(fe)WGpTkkZGioJl?U_}-L=DfV^TdH?TEAvAJkxTGl;v62wrBbt zZ12j?9XQdP)UwGy`W%&K3ahG*1y;=nVlUo#q>X}Lzq@+^2Z<9wFm^=8N2&7QsY z@_uGbIcj)Dt%R*Tv+9?q;Td(FIPgsC*Nlc|TCS0@JgcnDZGmJfl{^)}C4Q zOVsd;I!_#UruC!G$b}7_QRj(c&Syj5S-sYrXCtmY)SS<#;Td%vMqC4|`Xy?3Mx7@P zJk$C$qv4sBYox3^3!d|z&-iyazB@Gd9>0HfPqSyW>o+y`XVmbFS_xa{nN`0;4bQ0a z#DQm8zh*Q%({hcJduG)yQNuIpJaOQe)~^{2&$L`4WqDQ~0?#Tl zn?1Yiy-CJ1YIsJigsnZZ>X)eD8FijG@J#DRf0rX{@QgZ799{#0|IRv}`ESs8KI@p( z?Aa^xK5y|9qDG4utRSOyAe&o9psS$Bhbk zMs06>UpKA#C2Dv^ohJ@F)A}`|;hC0eq%6-WZF{EIfZ(BK&zA3e-MmJphG*1D*xECz zeu)~MQRj&R&$NE@TaCg7dnVV;FU;Mst~vjcBh-ONpDct)Lv5!X$teu)~MQRj&R z&$NEcXn3aO8YwH!>TP?bbF==d<~%FT)pa|l;Tg3Ow$3xFeu)~MQRj&R&$NEcXn3aO z8Y#=O;Dy%bGykhiy$19>-0a!H!?rfB1E}E{wGy`W%&K3ahG*1y;=nVlUo#q>X}Lzq z@~qOfXL>#h>&>2RHD$DUjZO{EsFkp_XIA|ZH9Vuv69=AY{hHD6Ov^P=mSqp-W6*hQAohJ^j0res9tn%w-&;F%f zZ@%b1pC!Ko;k!T6JnMMW<(WNliAJ6^m$uF`tA2?Zc}8t-eRB$Vru7!@Z<*rnpSA6o zp3j2EmM+i2wms8(je(`iv-)CsHa$xEzRQNq;X3Nka?XWUZeD$!&IMIbxiv?$JepI> zEVE3ZaOC6979W3X@8Z^5f2V}K8yt9~RbS`SdryCCm1oDnxqJPnTG-<6xhmt28n@A5 zBPaA6bJUdMdXo1a(6`DD|2g_L{5AjIs`>gh@K4gW${_ky-lb3&5#>^4(_`;KPOcY? zVe~ZjqrA)hJxBLUIpTohrc8-`(j)hARz*w!=VP5+mv@%uDGatvuA$*cJDA_R%yV42 z&c375CCPKlk*SenJ0l|v-$H3Osg?bI_g~+i&EMoiZF+z2MQ0YNQ9f11bh$_`c>d5L z{-J#N^}k;nzVVD%g@>Fx&bRIJ@SIrQ?mqG$f#zF0h(&*1d3{mwTgSe?GkMrPd4m5Ge}v1&!SaP3 zo`W~(G&~ri;PvGE0?h}QECm$4wmT*mkFQcszKby4#b7?UN@I9{Qxmxz&J& z=b_b3H~EbG>9u$eiTMMbsP2yBVf*9>wikbd%T1xBqZYcy>(KLF-jV(wXX+^)L`ayb z#i;H~^00mKgu+8kZP~5Dj^w=0y*%TN829lu?>!FH_0gIHv`b>Q2jBalF{R@ke;*lOE?*!#~J3UZKT zg@>H2Sg>zw4j#<+g()5!>zeOupFF`u;*W6QxK@uoFHdT||69KK&i2U@3J*D3v0&dq zFHdT|r+74O<9M-s@&p%)KfEP$?2xh(%u|A@qAD5 zC{xOx6i;x8_#@@ovRe&!cqSDt+L_lc92x#muczt__Ed{e-Pz{)F6|qC{-k(Q) zlV`q%XVE@y&bG&E_*?$*it6g?3u&J`!DZr)l%pT59vvU$8?TDRgLuC2V*BI?g(u9( zGu6X$#s|0DNq;b2Q&T+3R4qnzw_!fBeewi96n}&Z$F+LYJv=yuJkWg>k9vbWjrq*> z$rB0>IdMd*$MBEyjn{mO2l14tk%#S*C+HP_gsT+`_I2mr!FUC4Yd$k3=U4Px@1Lc8 z@`Uo7oIKXIt$BD3d3tQ){qq`0KVA3FQC+=%PVoeni$7BC<6wDk>-zC$KYY%dd32)- zx249mlHy@~{cwS~}mubL2`_e5oOSQap?>=EhS?%Wrg!xu0kI1@;uDRQ}gir?$Qk!^C!ilOx0pk*L2eFtxE3W&a@|HXQ z*6^@>wMm@i?xxP#Lu^Jl`JA1goh=7vR%7uzRKC_Lo! zaj-n-+O*;IAm{rgYdvE8iSsALL#Ew6>?^s-Q%^_Rwp*j_u=mOj`51>n)%N5$rJR6Kf;y8Q}^(E;rS~} zKF9Mt#iMbn#i*{yXSPqCPjv(9)WRr+>i(jrSWV9%ZT)qq^pPgYAA-=pHZBray81SUlKg=TG$1ydNG)`{W6JF8)Y4j&1em_$Zqv&Yu(yVkS%} zf24i#gz}u6tyr)x=-;d1{)e7S-uEkw`Lmusf1t(iY{1G1dq{q2cpNFT?+tK)X+2TPYJAb0O=Jj$Y?UN_?h4>@oZVD}3 z=;8UphpRT8e^NZkR4qnz_5364lP8qt2Yo~>Seb-rD**;^2FyK ziwBYH{E6yn{z&`e2|g$ONV%IrOG6J2)`BBXeEv!CC{q>d)oK1n`{W7bd6<)DzK5sj z`n7>Se;Te|DV|^>@kh$l3AgHa#XVlAO@HG2N%3g3)cIN3H~z?T`lQFX)zln3asF65 zh-Bwa^i;23DW2f-;*XTODYUengD1|P6b~XeuV2!>@kgGAIeCV^>K?Ci&q;m0{O|t! ziR$Y4C&d$NEdEHjI^kB`9-d3aE#G+kO7Up4YB8#-*Dq<`_#@B5oIJs6Zhu}J`C8-o z$KnBtoj*}sJ^!S5g8vYIq+I*qR%1Op*B_bsd^yFVOx0pkSIsN|LBUFn~UA=xu`^F!69_Hlfechcu zJ8w3tF@G!`9V@7aXR}$FKPjGIQ}IX2wI6OZ;Nj_iD0Tfx@gN${KT%!HA8FtCBhSN} zJfq%l`_pv&nx6C%=SFbY^Q+GtR316FOkex-OcLbVvx)$5nEZ~T$xVNRZI z4-eL<=lximKNgRU6;!nt)z$M)iYM4i{E>3)hg;P=JXiysJaPV{co2=}pQx^$f24im zk30`^@&tc$j~CWgClA^VF7oG3R9EvS#S?5U{zy4uwR(*8@L=t7@?cv@@o3B{)=}5% zm$YyEk>_Dfo<0u`)&(a|oIe&1SnTspR9EvS#S?5H{z$p@!>ty2c$!{+rFfJn_4-TN zH~z@;Fegv%TkiZpd-MmoVtt;X-e6DT`7liJ1S7>CDaR459s@aez%|w4K|Ez@(&D$e+Fx502%0kEFi-lJ?0H{6FzW%5hw)N9g5Az2AFr5;yC9gYAm3@-KOPgGa) zN7^S(D9_31rqI%{UY^wXC&i-?Qu8at6YMJfNVy*8R(&~m;{36A5XsJ;=&8A0uzll? z@X#kWg_c&{^^KSM6X#Eg2N9gtFKM4V!I#A!Dc6?WYQV#Tbrl=(=Q4l(M0GWPqNbv*}@kh$_IJc^M zc(DE&dE)%Bco50XpQ!FE^Zhz$-}ocX>64p6ONal(JziMbj68Avq2YpV`KvpBu)Yv^;{36A5XsJ;sII<$DD9Icl;`Ah zQ)p?ghX-r-kSETc6pu1hvF?zbe^NZbKH`s*Ys+pm;NfYyf4;(>KMnWK(!TLWp3^5c zg_e%`n|r)agZ{+%lj6|`sr%;?PcTOOk#arGt->5UasF65h-Bwa^i=PkrG4X%JP&j7 z%+J9S=TC}9nNt3wc!GV!A1TLitsWhJcaIm=A>nx8_~1%^{zP^4{3Goff8=?XlV_@j z2WxzgC(fS~kM^;O^-0Wp4^uqBB=JYe^*Fbxdw8&Z26^KAv3L;4&Y!5Rxt_9ph|zp-3RjE$lxk}{zP>R z58EeCC_LnJQ)p?;!-F*y$b)Sq#iJ3bSPw$a&(c15g302Kl*2v#0OX1DC&i;oRjgy6{z&`e2~HG$q+DBet3D4; z)Aj3WfBrOFzodQggz}s|xhb@Cp_eCh{Yvp@gw**t#S=^sf23TGbF0b+?)*V*j#r#N z77zB>`4c_W>zA}|{E_GM3CFg2^yc7+^C!iFmHJp8?D@}MprFV{||HUAb+}3JcyVc zuL|nP?^}N-(sBO1`tTh5wl(VGVf3rJ7mZGK#s4q69I~VNx9;)&Ku1*0-^E59#Gll2 z;`otA_e>mr;vpN!@3>FynG*j-{Sn_h>8L5=Cr`xB)faF)`1pI*HXnZL8u9QjI^r`r z*%kkv#mC>tM&)++`1{v3AAb9q2u4SIMssJh&En(lX5)}{`1pI+HXnWqn+Qfnd`5F; zw9Vq#_&eLE-3}jrKilTRZ)cN%8hq5;hiw)ge|H;o+u`Hy zY1@4GEp5cZgBpC)$)4o@EI$4YH);=-Z$xwee_tDX`0d|ly`8zyrs)xtmzoNr0w3R; za9#<&(JjZ6sYeu7G}?~mi_<@MWvl)$qem3iHacINb*=g%j2=KBG zYq>^h<+auyVUC5@TAm$;Uh{Y2aYSCP`J3;JUdQ)+(r}^^ukG?=x36!=Yq_6A#WFR# zrcNRx|NG*=>-QG&nmWvAc&+6c$scFkGJeNCsX4-Ec&+8xad>U!Z{XtykJsL7GtMXc zZTm*wwSB5~a-7FT+sNzWk@(JMy7l|Da(T?SNpdY7W;A@)@;q_YE#vq4g~I4K$4z;5 z9Qw}R`o|IN_|D((|0MhV-Q^0OPaGLmriSm->Q<|B)VdgZi8I1z_^#y|$(IN8oogE4 zh<1GES_PkE-*?z+o7)pdhLx$|JGHvi+IOtKkklPfriSm-dE(G_uHArR2J1UsgK|v< z_>SYFd)0Z-rs;uu*rvj$pjBRsi1u?W2OOK`+pVW(%E-O#yQzYCZOpY9mJ}D)bQmlb z*JxN$TwL2>uv}cLp^?kJ9w#~(N4p#?wH`;Tamh6vP}e(-t}EKO3FDEg$ThCt(8!30 zjgNM6EnbIH)-^!1K`}=4$;dSwmJ}n`f><0zuJf>@80$meBiDXd5{x4v^SEY&*GDW< zYgUNV@#Pv2ONxtYM+}yWYe6h2F0LUlST3#!(a2?=KNj=6B5M=0rL2fh?9P_lZRmAIu(dFST?RbVY6Y~39?ax zjhg$g&GLVxPAp{w_t0yVsK2kCWXyrw@uyd zm~t(PcG$Q+h0TVwDab?(Hfrv}Hp@4zYk|6h^^I#)*ldWCezfZw*SJ8uG}{5)Jts{V zH-573wFTF$SW;|U?;=0j2|dg=u3@pH*tqsZem1>6a6Jo~4Qp8-79P|XQ|e?-@_(y* z>x`-dvaSMZw;a=q`vIo$N{eZ#AazIY-uZK<$=e>N@AxxhHFF*qm5rgDlkGq~<?GDsl98RvyVRK?#4zf^#lbZXm&El*N(YTM2H8RM# z{HqVo#d;lNp^nN+)ZCYclWTpTZraa-CQTYYxjgxV_&&##pV2WK_xTP^tmA>Wcu<3r zI@y!_pXFyo*0@0J#o^={AGV)Z--9gF;H2h0Y_mAIHVEntn$w!^T>Hc3#JV4diw8A0 zsgpg)|5=>%#qrbP$u&k$d(fQGF`P$SL&WC9`XLELp+F5zYVN}}i!+q9K2UeioR*(lTg2wX zx*~{+2Q@gUlRe4*S)BF7;f$JqpVOl)*Bk*SzT0{B8zbi~+;4l6pVU!#i8`ug9zM>U z&kIXfwCYeZ7we)Vb^DD*f3;jA`Fz8;;J&CLYlh&6Hu`JEmusfj{r&5r0n=Y<^p{#G zTlW|1t|WE)OVsEub)GovZ@4)9jS`pn%r#l;{vQ3?M`n|S8vUi_K5TvCg7L+=E=k>f zqtRb2*GRrN>~DQ>`WrRF{&LM1yT7~qd2=&=snK6*rEJ|_tQ*7rQlr1rdE&6YfvjPI zW4!t7yG}*Nu)kbW#_sQ!t^aDytJLT(bskQPFV>|=>h>Fr{%W~K${OFw;`I09X?nhr zHEZntu0QaFIV7P*f2p|-TOTL-i*;|3y8T9@zgn)5d~rCw;o|gn!I`GNTocFc@6}iI z%D5B?)aWm@Qnv0d*41HusnK8RJaO3H`r`CAY6x-Szd?{SWzb*Di|F;#Jez|Wud}G_ zZL4t^5mn?`Jvb&k?+)xf>4c+GZ_<*BYws*6F0R=#ST3%?v!uAVcF$nBxE7Dig>`rk z2M=nDBXzPT`M=dT#^3*tHDOTOKaQ@O{BaXFrd*%LX2jY&+h)``%XNHgMy%mO4r(w`b6+0DdfSXT)?C}iX2iNake^T zsWH~n+=p$}xhIe{a8Ng$k9$lUH-4%&9}DJs&Gm?EMyy4In0QcwkviFv{GY{GX`4~U znrjr=00q*7{j(1^?J>9i)==$Sws$MFj8}09>#jxjOruTGO`)5juAPi z!AQ-0c^J6{66!khF~{|q>l@jOSlfsk)L^9MK5Vnbn(HE=?jRY>oZ*^BHY3(MLQFiU z!APC#N&e4b3}tO1)E*?GxyR)?NH!yWgPR=GV5H_gY_k~aL%_(jk!;2xy2J?Kx` zM_jM{>8P(2xL#5tBO*3F;A3M+@+`~8O52Qj?%^6rONucZ0!FT@w4@m8Z8Pfmk!vm4 zj96!h<4TROrsh6uv&K4*HGfbyosat*H!k`Htp5&@_u*WB$!5gbONfaFH5jRrJ<0!B zjFll^AF7ejb}G^EvAHyRtSfCZ>OBS5j9OBRVcU#)E#x{lsk&phXwdNz&ud*4jb`?3O!AQ-0*k;Wcu6u>LgJaaO=9*SEBi6G*OgyN;NS*9S z{?B3zFKRun^Z3Yhu53p8bu={?sRzSYZ<|r`k!xSsj5Y%`7^&@TD<4Nh6}e^>j!BPo z^0{y07v*`+$ThH*6c^Xd8Y~yr!dg;XTtjQHTwD{Yk!uM5KKEk(e9&=h{kbpK$!cUo zPa7ZYxrHA4=Yz{fW#oEVONz1HwvT!Z;Tl^uBi7gANK<31 zsksl^R_B%YdkB|gBk zM%$@G!^h^*>^W0un^Dg_T-$3&F@{6H$o0OK6k~k|7`Xw?*gSQ88}@t_7Hb+RYbyEi%+~j&+Xf zHP;!l8L`F~IjF%%&3)KrjWyRNL)}3#n$LZ?_L$9xb;l4B4{9({Cwr3rvlv5JBMh|% z$!Oju;d*2?Bi14#2Q?U}xewbc#`+L2a*Z;Z5$ltYgBpy~+?R)uYn!2N+DCrQ<~u|3 z{*mjJ*^F4Tj2zTpq~<i&4@M7 z$UzN8YVN}}%SW!ChPv%Ca&0u55$mEMCLYvaq)zrE|7S6VvW6LIx68=&(riYol|~L~ zFj8|LwponzAzT52pwYH=f!?qdqvlp(vwxk&AZ8NHmT$9aa#CmKTS89wkHTPkgHP&3q4RzD`n0(IW z%g2Jb@8LRaHY3((LrgrV!APC#N&e4btjPLhs69AFoikj!&1S^9ZRDT^BQ^J7o5dIo z0VCIQvl+3L8#$=KNX>nD80&2_>R5A)H=7abyODz$jMUthhmmW;p{{GJ{a0(9GhFx0 zX2hCr$1BaM+P=k>=*^~UA#Td$(ZKypsM)i^F z!`X~j8;%^*V5H_gY_k~aL%_&2<7~zuyuU z7l&^=!>o(S_Q?~RocKUa#`A6ae&goB8iC}&ww~f)ydS)~OnL1c4$yUerG4^*@|>JJ z&bJMJ>*m3lfaE~}Ewp$Li~hXw`l8~uj(vY;ju+b}PcT*d5iTDG%e$X&^I#1?@?eZQ z-c8ooK6ygnp-+08TLn+Kd9bD*d5}Lf ziwBXIKj4Y#>N?ueK6!#u#UCkmQ)uachX-r;kq0?5km5mvgsEDL>drL&Nc-do990{5S9S4 z22Av#AwiZgEz2qS1vk*m(=jGt&m6u$GE zexG`)tE*0*bnfpXlCOWiOD*My|;@zR(Qx6h42dP{c#>tArJCrdBB6? zVA}KIv9XoSPdFa-Pag9Y@khAOuIyz$)V)u$j@}{LJ?Hy`@Pzk^X`9!Nc=eNdzO#Sw zSm7Zj`$=s(A5QV8Kk(^+fQRuOJNM=De)f|Oe%a-Z^iLjhiTESsY?s=aj|}F)e9iuC z@AaAaBk!Nu2QXDLAeRMDn=4d?NL3^I>C;i(6pCb?ZCy%*Q{1Gnpo7#^5 zU~oR8?J55!e!lnlBmI-d%04+$94v1=FqjAVbBD)+wyF7I|Ku^3i9f={epB1g2M6;Y ze_Fp&e;AANmG5i*NdM%qvQJL7O>ND;r+9kLcXMlZoZH^`|?NncmBveebRPu)%jt{ALYS(S&s*ilKB(xn6t$nDUVZV>BK{W zc`!$vfCmwx`6K;1e`McQG`*;2b4>`+NaIE#?!92*H&g0e2cPssq$Gl4Xk@7)2qpMRq!TJ7yYUexq zCyx~#at`7#kED3?{K9;-0v?UqJ74UdJZ6#jBV5`pt|lw+6z99ggGkBzk^aeJWuKgJ z3N7vYWUxQQ`5y2nQ{Yd)V-|})Qm#G6)x=NZJXo&?^TgL0FQ3&L9Bnu9x-0p-!Ty~; z!b6`JFSWHli}Rp9d9WS*eee0DOo2brKY7fn#UCk8aj<;+(Krv*B_a><=ahg4?Ym6v zMqbyw-(mmcvBG03^0a;)=fPS;SmB{hXj}F&lW`ubIYb`J*X)1?F*#p(-Tj%*?4LYlT>KHP zG8P=0Oz_|sd7#HV9`y!C0)M1`@>tm?Ct8%fI=_hfgEr*BcKIKx%@_M8k2zQT5w0>8 z9Gm#%U>?lZM8Jc#oUdHE=MDBx9xFWLM7y$={Z*U?YY5RFv^OK&amJpX^Sb){Ed7(m zyjJ{?@-h}28~=|u57r7I54Npcotnkn$Z5LOq|BUlsO(60V_fwAt zku;~m(S`Aj`W`I?4LYliTER2Xi@f>tiS`V6;D(<-`PKTtniStj0ML! z|25bj8Y6aO9O*(zVx$Dc1Vts6IXzUOt_`Of~yV}*yD zWh^*me;en)Iz8mUwiWPb+;$_cdjQXO_D>$ORQwSxZ5LPL|0m9a^?1lrobMhFB56*y z8+l!KKV|>qvBE>nIE9wBei!G#x;x}4&i8;vnc9uKuA49RPad;O{1GngIj%+@kMm%C z9rB>1*(jbrhhFyb?D|if>v-5dd93h|GfttU=J#!HUkH>>p%%3^;?Vo-6_RaO@gVH~Ftn8CB#liB< zAL9OCT^#Zh`4jMv>4I+@-u&9End(o#V>0na%GqyfJFzCtgY|F7gSKYlc>X*-@(#jxcDPnY?s=OPmA+leG&2$`Qz~*7V~GxZ@!g%>lZsUIA83aJXUzfnc`r1Yoj<1 z*8d<+kv{}<#CyzN&{1L7+N%zRarg0vuxj`OmTLF*8Z8!3| zE}z*yd93h|Q`^Oroe}53`WoaZ&UcRoku;~Hlb6mAHi}PUJ4Dz6*nUT()ysqYt^iLjhl=vg%aSAOR z-8{~NH8RM9Z7bl>2<=8**WFLqKY6V1&?jvdS7v6M2kT*wr^p|V2a){z$?Lj&X8+_d zM~gqg6{pbB$pjD9z93JLKLL+2wHtX|y?;sn@oaSAP+*fPbV_pc&<0v?SJT%VJTysrCwQTFfr5guES$7~zt!Fm(qDe}kT0gInM zd0qGWqE`ARkJ)?r*NV@7q+G`lSCa`ItSv#FB7XuNWokF_x_bSQ{>fuypPX?DE$uuj z?hn?HAP;Jr&C~gl*VX(9c+5WHkCbcAaW(PmI1kp0AP=^!fJdXX8+l##yutpRKf*(w zv|U`;?czLGAA&qZ{&+lyVOk z(sprWo;#SQ$RCdfk^KD0clG`i@RaK_#@?rRrZ?LAPu+{>3iu?(9l&Rgw>+1DK z`gi`wzOBg9dVZV->ne~3eVZ-Q`IFbx{0Vr>f#Q#p$6e9V(VgNvSVMt4*tP;5jnHo7 zb@l!w{X2hT-&W)?^#o78UIKZF{PB3e;^$Ak&VuGoz++~MKT@vah^xs257tH?Pmw1FjcmBve zIpY*s+In%E2kRCvpHbV)O6N~rSFb+-k2zTUk#g-hu10r>^I(kv@?hHvcr;qOk=NDx zm-O%ak$qc{$GjxYgY^i=Q{<1w0~Y`Klh@VjPrzg5h(A)UAK|eTdCXped5}M?fCppj`V{yh{gcPc?|wf5Inl1{HJRiIKJR@& z`h54FH`qUUtngUjEMvj3&R-Aqr#RmO9*rCLBmI-d94G!rxwea|iM39V}*yDaSAPM?HA|4ck`2{$e(~mBeWZNU3Wia|Ku?zh(E%m?c!>5|2Pl6ho3w} z{&+ly2P7grMp z4(2KH$KydHKY#LF_jw)rcm4Cj;`Iv>$>Mv_D>#jviKuh z+Agj}503NTd+y0oJTyzasLeKz(_9`j1^N4T_ITusc4^Wb~w$y4Ny$Ad_I{^WJMcgvFp+g8A%5!#Ksu3mov9&?)bBjwsIu11@29(<2Hd5Zk;co50Y zpS^d}lm)iu?(9l&Rgw>$>@}0gpLd{E>3)Ij$xXJotWi z@}Q-8NjiV>x{inaJAZ_SoN)>*?Hn5S2j2xx9&B3yk49)W^1AMPx6(g(%o*a3lxw@V znm8=ZgYSJOPmwW&@<;k7kClCL#woPa9vlhB zYrD7_J#sKlkv|>}BKi4~@9Oij^zZzUeOr;oynHZEkv{>CG6ntwJmxy_N6OKz>@}I- z!S}B-pV8jzn$DlR?%nSElK!1PvTrN$bdHMqgYQ}=54Nprj) zYrD9bI6BUQ?^P#Hkv|>}BKi4~*FA(h?BDq#Jmid1XsLZgoCn{bPM#uv0v=^*H}bk} zzO3|59`j!DN6NM5xEenu&V%nuCl6Yh-O~A!*L6JXpFCE0$Qh^5($=wY9(*@Cd9ZB- zJQ|_h$m_1;e6fG>n7JRU^y^C#cc`b76sOQq zd*Wc8B7XuNMCdXF{scVcM)60=wdc4RUoe;lEth*gPf>4hq?d>NJAZ`7R^(}&G?)kZ zGvV=oLYaDb_=52OHo1*3pgq_PcznI56WoV_yA?`jem7+-Wb&NGeWm z{km&AcIh5YSBZn`|7c#3-_wmYn78wnUATC!bCxY#eBo(3%XidA7h*#?w40TqsI8Dy)dPIY5lru3%|5nBc*ug7r#dwEr#)n-w*El zh3^I@fz!EPPV->CP4U4muYN`izo@-1$vD0B>#i;QTAMiZi{EpOzK8LP-(T+gh3_us zSe(xNa+(M8ZJA$Q{frvpr}n}myrTTj#;g*De(`(a(I)ikn8mMp z-Ogw1ReJs6_rLpo;k)0-Kn=gBc?{b!zr6YxHT1{kqfeOUpG!!K$c!#2f-{POB&)bNYCN*ws5_3KW{kqfe zNXs=+iiaL??F6)_>JisL@I6}9+|7AJ4Uedm=r1pixSm5*kLtORT$=$NVO@sYi&gS| z5n=LI@m>w(W7I{l{Fj=yyX`%G{fuqvjZK}d5@+?Q&9ZO*iO()uJ6&%)$LS!>v}yUc z!uMXQS3TG37w2quT=m8-P6u&{-7pTXaa^kcmGbUl=WOzs1HN+&ieBG~E_?p#`rcDi z?3DZN>gQwYzPi41Pq(Q!@cm~$d4b%cwsso6Yk5zc65m-*ua#ZjS+3g@4}IsF9B46& z?_6tRefC}5*xH-F=Qyp$i)+;X6W_V^$gsY1O_BB4_mlqZx3Uo2))_VOo%-qUoolQN z<2#-cxONJB-#Gu*y}lz_?`v(ZGwb8CR6QRO*MPz3!t2A~%a&cV*U|pd)W`^~EAWYH zx@;&uac!El^@(fEY$!f)&78IMiEH5W`qX{qKrB9gtnG7E_q=m)@_Ry8ci+Rf@@^OT z|Ec?F_dN{j>+?|eJq*iTm}DHBKYQfWxRwuEB+t9p6Vak2i+K)mot|FLl!5A*aIJ!8 zTyF^DS}V`;`t*!z32i8zaZRLk@r>&oZ77~`-KKT%jB7G&2%gn*qqw$I(lab0?mm^# z^O0*sZ74oHAZuQ&txsITYD4jfYjLfuPh4B8*C+q}+||#R=LlJIs??|0b41>N_e8G$ z)$17|_P(Am&kQxL#3LiG%Bv)~`E_>y?&kq!bTwWAD5g*9%39@OssA4i0#K z&GkTi->?=aNvPo)HIHGN;)HKr{frvEQCEor-?V<+Y51n)8Y#s?-?*+RTC9t2T({Ks z4QrN?gc`n4^BA@%PWa~4)Wb(J{qP3za4hHqM~ky1SL&B}VGXtCD5?eXRYuW4Lg z)%Oi+tCEBozESfSwkb~d=GD)r;Tv_8IPgvD*PVuMTCR~&JoK%zF1~r6PjDSr-#4XD z1>bZ}Rf-e7X|0ir8op8c+f*F*ruFo9gW#K%t4hUzZ+QR6^=#2%TK>=fJxX_d>v_r_ zarXkQQM;jd#kF>;d!;|8<@&iBidS3*xVl$ooy%(%*Y@?j!n(eQg%>r>LF(?2?*FNr z$2sWL&!}+@QdfzCb5QHooyIw+F(u@Xf2AQNuUtDskYO)~`Da-?UsKrFiHY*HT7{we~GuZ1b@7dBI{Ut1J!g_o!#8Rk!#2eU z-@N)6HGHG45(mC%{kqfeP0KY>iif^+*2OpPJmuQczHeA}nk3Zljhe@>O>x3EuYN`i z->9p^fp1#B?lgSUa*dSYp>JG68!f_oTX6pQi!aD7IIs8|BX)hu+weT)8ri;YSRb1t z)bNd($FNOt!Z)veMh)MntHgnCT0j5$4Pk?C)K%ipH?H-KHtXUW*Yfs#!#dt%p@whN zJcezVZ(jY38op6ii38uXe%)#KrsWzb#Y5k$tO1S|YweqNo^s7`-#4r`P7-SPM$Kc` zrZ|yrUj2+3zEM|+1K+fM-D&uyiif^gS)(2;*4j5O-&#*}*AVx8``!)DbJsU&_(rXi z<-U3KGivxoT_p~D)B1I%;hUCgq(Q!Q*2TBH8|K^SpZa}!)~)-v>l-zEqgKjt-_{)W zGRDcM;Tv_8IPgvD*PVuMTCR}>`DSEId$b7i?dWB1D1UxiJmNl+G=J{*?O#54zxzEF zYWPO2l;yr{^0rSgPDTyisH?<*Z(6_ZGUf)vBZ+RQ~W=Fc; zspa2Kue*K$&#w_`_(rXi<@t8Zevk9MoKeF!>MC*Io7S&84d1j}BMtJcwJyFrb*Z~A zj8E(L?Jd9B?rxG$!#8Rk!#2fJ;G5R3I}P8oTq6zgt+Ot^<=yZ+ z9o@L!H>FSo-*it^iWB*!wbqZH@zrynHBWTcumW3neLHdd;`7c4eu;=anduvrPgd3* zTU(zdH!z>>7}BTCCjI$z)V>$;d0>PZ=N&cXg0Hgk&a0nM<9bES7}%!b;CiL?>rUf( zrR5qa6$j@}@%O!(rk^YHyo-G(v}g&>!HG@#J=^u6n|N&=p@wJFN|+uiu2o+Bj2fO% zSBV49w0_-bc&6nVX^>~Fp*>sduJ3k6zh|djv&CJcpoVAEJcezG5uSPVGirE7T_p}Y z)B5>$9|{{hqplK%d1i<9Oz(-~rr)#G4`0h?&Jk*OMqPywo_X~%YIsInB@R5(`gNz_ znU-s$)Ev^Y&d{Fex!KyR-?OEk&)oHl8lF)rVR@c;^)qUCMqMQiJk$Dhr{S5FYotM* znHdAFXZxmqipT5O=;r;NU47=QD@j2O&!~9}+Y}@6%&VVK!!znCap0NOuR9ITv|J;l zc$jCcDe%n9?DuT@M_%jh0o3q}S_#WN^Xg~R@Qk`j9C)Vn>rTTnE!RkcJhQ`lwupH) z8S!kv;WPMNaD*D3Q7d7&XI}k`8lF*Ci388He%)z!rsW!GkY}CYJ#*hB(%GUv&-h&_ zU6Nx z`+d>k`&WkcY{cc6-D<<~%nt3D=GpkH4a>966nNI!y5F-4jy=gepHbu7q}Fq#{2t)d z&!};3QdfzCb5rZroyI*t%QaH!9Otu!*(`lMJ96ow#mke|vk~`xb99@2&!*qLx%(Xi zYIsJigyo)j^)qUCMqMQiJk$Dhr{S5FYotM*wTAXg&rP#!zh|u#qwe<)sNos45|(@B z)z7Hm8FiI7@J#F1orY&xu8{_LW{37n-)~MvJiGsineO?F8lF)rVYz2s{fru(QCEor z&$NEsX?UjP8flPcouNI`JnKBGKhJKs=0tZrqlRbHN?7ihS3jeMXVg{Vz%#91cN(5) zxkeh~nb~~6^{m4C&539Cd-m1&%iL!{)bNa23Clh6>SxsOjJir3c&7F1PQx=T*GPjr zYYp$2y9e0q`aK)_!T#=gMh(xXm9X41uYN`i�@!foEF3?le5pa*Z^|GdsLz?lZ^n z=k$BldU#8BJ)?$a)JjejXY~8c znd$2p-fyNaXZqRpXf5WMKXYA;JnJtl&oi&Sd(VhG^U9q}om0p&S7~Gl-fs@=nLcZn z=WbY@*(vaBas%_MGqh*k^VwhZ=h>B4=yxMf{zMj3}+(r2}n592KY{i5ma{U$X$qpre;&(FO288tkk zt`Y~HY5ls>@J!1!QtG-$&stO9nccD9vmfvEsCz!6hG*1DSe|EI{fru(QCEor&$NEs zX?UjP8flPcb_zTje_p?5A6cm1hd>R_sFkqXGp~L|4bP~n#DQm8zwR_V({hb8$g|EA zc-DG;zh^gk?>DL88MP9Yd*;>8sNorPl{oNB>(`xzXIie226<+-9CSTPKcVq@Ho8;4 zXLG-{)V<%NhG*1DSniouKcj|c)K%iZGp%2D8lGvnMjGT^Z&JfEY9&lF z!ZWXaMh(xXtHgn4T0j4N4Pk?4)K%ipv(6NF)_FmHo;{=AZ*G;op5guGeieQmFtKyY zGk@l~8hO@VTApWKefORbdFGWnnL5XjXRgvzyx$z!GkuS4U$|j;W{37n-#d-JXv6ZX zGXMC*InbyyL9w2P+jJir3dS-|A zOh0p+jCi*HOMD%LGp~L|4bP~n#DQm8zwR_V({hb8D9<`Wd#3lz&S-z0 zUGKf$q=skIN?4v}Uj2+3o>5nc1JAU6{#lT)!87VAahPYOe#t$5y0qJ3TE6kWdk@#M z^t(;Ho=xo9@7X=x&o`;z8Fdv#u$nwGx)+nO8rfhG*1O;=nVlUw0awX}Lxk?K+tnE6z<F(B#0s3+w$NQCtV{{Vk(=Zdg9m{ygaKG2=d&y*4bLT0{CY8TIKI zy#Ch<($5jyzcWmId4XQvJAb|5_dB|&k+-QZ&;q$De%nhvtfDG8QL>FH^=ww z_iVS@#@y#T)Oe1d*86(-vzJ#tqsDUtb(J`Hj?nsbr}4b1Wh6Gin~gHpPhNRj+$M=%O^V!gzdCzADZdjh#De%nB-mpCDOo3Sxrr2T)gugL{D1uRD!MC*Inbxm64bQY(BMtJbH3goH9^CKQm`e(3ct*`**rph9 z-}LHd)bNbDN*s8m_3KWaJ(h@Qhjs%RTezXVmbF zx=I{)ruFMi!!s?{NP|2xFCK6`tMGoab4Y)l{q`rveVG*0@Qj+ruuU-{&%F8>H9VuP z5(l1X{kqfeOv^P=isw-IZnM_#p1Hp>oS571*^k;Uao;&b4bP~Ru-r4Rent(?sH?<* zXIj7RG(6LCjWozJJG^J^a}C?*_w4#F-RZ7p)bNa23Clh6>SxsOjJir3c&7F9pOFh2 zJfp4>hk4eS0?)?B`aP@Pa=N>oQNuIpDvUTcz4{q7Jfp4>2cBvDy3_DX%Qe!VJTtqb zuV?(boYWH41dd7gRoGirE7T_p}Y)B1I%;hC0eq(Ppw zrogk&X1`~v?);Fu2T;Q^Y9%c9%&VVK!!znCap0NOuR9ITv|J+%^2|41dxo2Mej2fO%SBV49w0_-bc&6nVX^>}~De!DE;@OSAJ>Pjo4bP~Ru-r4R zent(?sH?<*XIel1yBuMIXVg{V@E%}ZGU$4ien8{(taE68p8fQm7rJK+YIsIng%S4v zuYN`i�@!foEF3?le5pa*Z@7&stO9*~DS}o*lOGAoshn)bNa23Cr`$tDjNBGwLdF z;F;F1I}OjYTq6zg%ua!4_V9ksuKoBY-SZhWJfl{^a?iZ_88tkkt`Y~HY5ls>@J!1! z(jd<|Q{dV75&fP$qkkSSn!cWOzXu}qbf%wcv|2IGbl#|tXVm^SHLtjydG*}yhau0r zawk)9kY}#aRQz4e(4OhfP)3j3uspLvd*=P_|I0Tl&pJbUrg=6Q@ofCQ|Zs zd)AFmZIhXVdroxqFXJ4bP~R zu-r4Rent(?sH?<*XIel1u0~;lXVg{VFwg81cxGSG@7al;nB~q*YIsIng%S5nuYN`i z�@!foEF3?le5pa*Z@7&pJbUrswAPG5wx3R_VGO)bNa23Cr`$tDjNBGwLdF;F;F1 zI}OjYTq6zg%%TSI%M*R#?2{hsZ6QPaIgr-ov-0<{iK6oMjiCRs22Pg$vHF+)yj# zD_h53&pmW{{6K|L^S*_}cM#aeettKPyE6^&l~;DQ*+3}{>fv7hn#Ge+M0!HqW)Z%z4XiE zK?03?Jcvbq&i~PshraDoj~_%H_D>%3e(^`RQXDKdi{d<|?y|Sz!5pm!c+eh9yZ+#W zZ1rE?b_jXcKY6V1kdytUwiEMT(v#mjnfCl~Z~k;V9zTEZ{vfZb`6K<4$J`|TNO_8b z<*no5Jm(z$=$)J|Y)7B!&1bd;PhR(B&L8QYJXZGUleUYi$pp{6TfN2QGdw%R<3S|m z4|wvrE}z*ydCcF5Kf)EK&{BJR+@I60{>dErgZ!}p4&Ug0j{1G1dRK|j1_Ouj_K1aZliGWAr2KgiXlgHdD{z$pD zi>vX|2lHUQ9`JY&iTM-eGy5lx6&_oWr*+0)9?X%M*3BRP{EOy~^iLl10r5x5F_yB| z=n3)b58C*-J6m{Arrw}d?|iX;@>t=aPiRs0>QvwX!??$Tc*@kv!~V%*+K&|HB{|Dj zaBTdt|#QvN6p=F56Kh?LA9>7P92ZQ_rV$0@Ybo;;Wb zbJPiV5FwgB(m#2u?9(UhIj%-e8O(#09WS5N8yxAKFZNF!bGi57v$z$Fw{z&;Cp2^cv{siayp4HBG_D>!w zJoIT0Pv?vjkDgz}`5y3S+}`I=X_D>#j zmG~oEWh^+>slbEzGShqW2W>fDxpeQB**|%#@Q@Sj%3kB+aeq$x{wY20m(5=3`)OYH zZkIpOKY7f%#2+ayW5F?VZk*>|c7D$R%pYu90guLQH}bk)BoF&1j};#Jr0wErBEj>6 zt#$>Y9`zMdNTKo~NIE9wBUK{u4(e2FL^ryI=20Y5tZsc_v&L8QY zJXZF}sXfQlWP%5^$phW-@>#vXk>LCac+558kCdZD*=ux3+#j?d54NM5^qybJ)XT&E zoj<}upUPNpZ2Y{zJUHJ^33$*pcD`HbpFHMT@kh$huIy!&#(B1S=#{&(GPc z=YDEE9z^oL1kX2@ZR+N$INt*vWokF_x^BMMKY7gjpLRe0 z5iadHuI%}7f4=jxOB@ecnvutfxL|`f^ykpaUY=e5iE|wf`zMbT9&*Mhw6yd3IM2cv zHyzxQKLHOT9P{xvjZNSA3imwE{>fvG6Muw@?NZzE3*tOq`^s5|kO$|x^>`4A{>-^= z|LoJZZ+-~#nf;T;3J*C`94t2%#(B>C#eX=SB7XuNGF|YE!<%25HBfvG7k{Lj z{ie3&m7Cw`|2%h7sd(M0-k-;rY4QBodFijmCKsKd`6K<4$I3o^N^!8fb84LD3#V@H ze*T4dYXv-PzuC1*v-clyvzssWPaboE_#<3wm)e@K=S2Pa*t>3VKmRK7$KydP=FdKR zJvuh)Q#-r)V*li^!b8p!2g^HIoacKtJg+x@0ve*1?U5Bn#NIZ^x(F7}(+ zj-DC+dBK|FUeE6E=*PnogOpRqvW_#a$=UiS7t@KYGbCUQYUE={Kyrfnm^J%d93V{GsVGj^WSlvWA=O8onJ-%1Uzhi>Pcs2 zv;ThHUCy6?$DAzwNIBc3w)VGio*gH5y}RfB&#|jTQyKU0{w=Rm`-_e3R*xups;FvP?zTaX0fv7hn!_BI5zq>aet25_rl)ueR}+S$9&~=-TBV`$zx6xe}rogPba~% z>q9rWe8zmW0v?UqZsc`cKC^%FSm7b3wu`IrH^lup^_ng2V*V8O3y%kp{QSx5>hq8E zPaboc_#@?U3N1Cu<2aeuBp^Hz61E%GPe(FpBEUf11E**|%#@X#l17gv)B zp6wrbt;^>ke>@&U^7ALJ>++fXlgFGP{s>o`LQCzN;{Gf+e1^M!75NkJC{w$U*VX%% z^iLiu`{dM~<7)KHaUNV(cz&U!*)*L$d0ow)fXAFE{z!S8LQ6YIp5FUKE8x)x!Sz}C zcmBveebRPuHU5^AKgv_&kH>>Ze*WaU?(=XP@R+m2A1RMhXsNkmFi(*`0S_WX?_bit z^GEi{sXfQlM1tpnV^8YMpBd@=$?IzV1UzQ!(eCFzQXZ$!QhQn4pXs-6-ur$z;L!-} zMqYQ0dp?x@oj0na%5@xZ)k*N&|HRDR z>rcR=OzlQqSFbyj<{-F9`^@p+>xia{scVA)NbT;HGiak@>too6?yF2<2-%$ug%i=({uj{c+8>V zkCew<(bCZ?QapOUDDo%Z(Fno)OZs>I$Uc42c5yZSj=?-d{&+lyODEnrn5W2}fCmwx_b=(+`6K(bBG06a^IUmF@bl%(AB+CH0C|?z)$32dV-6R8 zq+FedD|=;}=lVs{d+%QXk49@Z^16EelK!1PvTrN$j9wMz`SD(l_FjKH9EtzWp1EIJ+I#;Bcr-%0k=NDxm-O%ak$qd0=e-G@-S6tnACE`p z3M%K>{VvU)fX5su{z$owBd$iTkMpd)H@JTVJc!2YPhMB^NBVdE$iA(}V{VM|VBEZ) zqHnXsW6Au<>uUZ4Jm%%%kCY=;*=r)n6WqT79*r5?zodWXkL=ruJgxVo{866b{^jw2 z#lQaKyL$f$c+64akCf{;;%c%2PjUYWco2>GlS{|L{+&O)!Vd2B&%{%o1f zpS-T-kMvI-bF}y)<#AWEboBjko;RE@v-kcL@Mwf~Bd@FXFX^8=R`zX0o=$>i?Brd0 zuRk6SSp59S>+1C<;4!Zdf23T;5m)B#;{IT5d!Fw_{scVA)NbT;_4*_IJAY)KKE)}t zbRxlnb>qo{+GeYC{^WHve*zwJjQAtv+H+h@-W>M_YqFCE+g8A%(b|o?uHL_-f9H?v z+loB)mN*aAJ10+Z{qcCf;^$9ZSMw*}F~^EOQm*5OtML!SdHUXe1w6_Wy#JE^ojnG8P=OPo#J>e~R-x;9=aq|IEk7HrX+AKQCbaCweqP7^$zz3woGA{L+b0L}6!(jOhfGi1FmmXE??3q^^00sM zn9Ic<;bOn3t+}rE_a*rI30P0pKi|#P@%*`X-n8sl^A~FVNdM%qvQN$w2g~hy;yhTZ zmOMrN1Uzj2sU1$r*V%o)=1;(5-Y)(~IoqYS=5OOXSO=CoMgDj^h{gOl{pe%zHDhnp z`b7l;U8yy*|!^HCD+}fvk_#<3wm)e^5$9b^MA$f}Y@pur6`O|sb5zXbFx?k^K(m#2u?2|La!E$?3oCj+N zlBdX@fQL+1p8xx?e_yqun=kfH9&@GmBV6n^we2MG8SC?r2W`!>;`xK~E3fN#*gtu! z@Q^db!Saa%;^!CEz9SE|t$>H^!IRfL*xlcyfAW~C#2+cwc5&5uX`Bb^%8{qYACCu- zm_Ojj>)u5k_D>!wJoG6}p{0`v9<13$o+5t&9%afu5C77g`uiKwKY7f%#2+cwp5w~g z9M5O0hejT>G|!Ib&rh~ID%;`pEzKY4pFCFf=~J9SOYJRj9;`J+9&B3y4tm?XPiPyClWkZlZZS;{scVA)NbT;_4*U=n0JdmQm#G6RqNom zKNvT8(9%37oj-Y9%^&IC`6K)EDNdoKlSv+ZUM=z`;L!-d^I^baR*OGUuI=K=&Pn;B zJVpL^Jc#7yPrj?4&rAQ#AKAARc}5Qz%v0n~z@tooKLL+fQMr=+cuitn)W4q{t0-@PsAT7 zXS>wad?e0;bwS8efQL*k zp0!_g>Q%eA`LfbKdCX76A1P1y!tzccpRt|=c~IBX;`xK?R9@Hduz&Jc;UOpcO>M{L z#?LRT6+s?sTLF*8Yd7+`dVQAu$zy&d{z$pDi>rx7oCoVLkf+EWj|Y*MKkz57>+YxQ zpFCE0=u@0ROIu@c9;|Udo+5t&9%X7b^1AAe^iLl1sQ4r0+H+jlEY5>*lLsx$bJO{g z*VX)y{>fuypFYJYv~;wY;?es>kv{>CMhLFY0gw5)_#@@oF0Ra>gL#Vl@pur)&!2o( z?_bit^GEjSQ=CFeCo1q1`4jLULYFD4B zvzLecJAZ`7R^+kg4CVosnV~=b0);a5^6>kv$%DFhk+Zv({(t;cgL#lYvjZMP4CiZ2 z4$kklzC1r(e7E(_zHv9c+Zy%pa{6aSG@R}pD*oSS)+<|wEvlwX?YQ%_%C0lW7@QwCyaB}*Y?gXe#={gXTH<*#z{_Vlww_(Xq?rn zUYVU>E|EA3ogTY%iPKf$ta|H(UcYUpx7qP#r>n&I%}+n+)$cfc+)e-Gbd@-(S8dZ& zKWk23x6=+z_ryue7e3D!%`5VI%+UsUeg3iw7w>h>vZaeJJZ)!L)$p;jeMf^!i<#V)4Qf^ zE^F1z&!~}4)K%gjpI-42BXJfwjeOE_jg;a+K54&gr;$%uJ~$5YN$Yo$1O%koL<-!?{#e4_Tkl;+c% zPhIk*Zr%AAHS&qNN*v_V>yN!r;w*F;`K09U`L-;dwEk^l)W|1lFHC7Z z9kJ|rE4y{)XVl0i>MC)NPs?7lmBd--H1bKyHBwnVX}@j9fPB*O!EumJTEF8o@=41F z$3Z@+pEakEPg?H98IVt02LdhDHlMD`+i-mcXbwq@hg`n4uC#z|cz4#&xLE6`?`adJHhf1Fs0f(+CcCpC{@TQ*LuUu#lhoYYm~ zaGYEZ18s&GC)c^~$B8vA$Uu#8Qu7$LW#iQPwI(&jNnIrl$H{dz&}MDN`9R)=>t|q` z8|VKvdH9X1@=f0>FCXhG%nMTEymXs?^6J+!w*BgbA9I>z*rwvlz4`&KeyzzikK8%W zX_l46fnQjojO&1)?zH@$|NP!uw&;zqHOjcAhwl;A^FR!|sNoTH_el5u)Y#zB>d!nX zaq1a0Jff}=2Oep^wI($@qOKB$9&v3Fw5jS5*BS9W^82KIrsNUVOIaI_xb{h}N5$tK zVoz6hJ@U)Dhs*LvKG(@B;#}*t?VfAZ<8;q8*2lTl)!lQA<${`TQarpiaSa!=NaRuS zo{DP|*JkPU3(o$aEA4ZYujunesPVSVf?}~F4x|H zUwoEFtnh_rqOc76sp|!v<+Z-v7(;mej?wAa!8U4Eqa zY!2h&T0ZOh__&tP+K!KF1Fi4l z7-eygpL^sLxYiNsCi63PXI!*oF=7<&s~5ba_*+ifF;=dl1gfo2YEW99l!>oQiZ?X*6RwVn@bc%c^&L{`J1ApZ5EStj$z9zS#XFZ@_CT*U9P~Dk_gde_+8K7N-ZK%`4)e!~b;Fp!)W~6K9>X^Ez5?%0w0^Bgjpsk= zDsgbGy(q89wad^Zd?uQ|Y{}vi&%WR_i-UJ5JWshEnLkFqZ|bM>JjEEf9vXPoc8px( z%pW7xH{;l-F-B@0!?tW5wSKKhjd`T55{L81HP+B(ZO6!U)BG`F%{2B;jWJU57`A0& z)cUn1HO5F?B@V~Pb=%OUWQ;veK+ILH#paLE@0w_S(ov0y+41vSnH20)EF-{k6~LjUaeniQe(W-RpM~GTyGF<)_S~qyt%=1mg@=n z%rLGc(Hc4qt6IO-q{g{PT_q0BO(Sa$qD^V8#_l!VxyiL4{qbTQNU~64 zywp5~ZP|FWeyvH3@lscb!|`%0O0-$)@#bwfzg%n5A1~IKBnvghOU+~0mW@~I*P7HA zFLjkT952_pM4NRPFW0j4$BT6=$wH0sQu7$LW#iTQwI(&jOI;-n$J?2reCAr2{&=xY zCRwO4UTPl0wrspwzt*J2c&V$z;dr@LC)$MbyWsru7hjNFa9;7gCU*YjZJ5toi_;%3 z*5M=zHO5QLW7w9BSL^4$e5mude3FG4?Y|F;0 z^=nOPjF-Af9FCW3jiSw3kJr0Cb1hMSyjVw+EYuh;HIHFiHeRh?Yf@vp)K%heyq$HK zUm5RbbECLcDaMO+O0QVGa23`mB@1<4K1R)B*rvvNw|!)6Y_o$U&ej=qz408UtHfEo z>bco&J2oUv-RZGQ_i(yOoKFf=cI$b5sPKW=!SN}ez?|IYXPFIO@TXU19 z`dM>&;a$5qT_q0lfosX4O)~%SEFHVPdHKM#V*Px;Ib zjeOAZ!Eu-mT+0_N);b^ZHi%PPA7rgwKOeA8FG;A857az{ZOVUKA5Q$=E9B=&TW8eB z2kI(ukPnCc$Mq7Y?lkg2%QaGp2l=4=jya8d(DK1?kPlk_KBtinT0S@q@nfOV2dLXCW&<}qwj{v#iDdAR287u3iH>MC)N5BER* zYQD{@+dq zaiV{df9%BXGVQn4q<-*|2RI$XDR#rZm7{M))^J9p@H{$t*&E8=M=TyGK9el|W=Phs z_I<;8+GL@IZ`3@7ZJBRhxYrqYmOEebatxO=|c?9mFYiQ@*v<%{P~CT>IPi z4eNfBg&Mw5^BA^ezJ2|Ne4oA|qlRzPL7b#-+Hb8%4d1APIK^(tH@hyr<-cIg^OS3p z`@Ug)aIb-~4fjvm&F0Z`47Yq;J}9{x_P!2H&WIIK^(tx6ZowmPdwf zGVted$H`sqCJ8m> zk($S_P5Fj-{F{R_iL-S^jd`T55(o3B^~apXJZiZ{O7UPGwf=ohV;;49a2(8|`cQKk z^Qh&6<8U7By3FIlm$`W~zw4jJTW;d}!I2Sa%prQ;q9U&%+*e0th+vR zlY4JOjj>XrKfY3o7^~K=HK{RHYR14e6^CPO4L#Py?z%JnhW>dy^_ng2A_q0bO3h(`pp7%O#^I2>zd z=&@=JPejJL^z)fJU#T%x>MD#FtJbeIsWDdSDsebgv&n$-b>H+)lMw^=&ej|I=k@9{ zZ*}j(`pp7%O#^I2>zd__4abp&os6|5*8* zCta5HdgD+tjlia#-uvn$);9 zQCEq>Ym=FgK3|Vqx@ht8ya{qs8g_RZbzGEifz)KwTUR;^!aQe&*tRpM~0 zt)a)NIXv3#A8TvHsQWoRHO5L^g%M-b`n4uC#!6i!4##ST9;-eNcOqlG|B0FI{z{Fp zQdeQbShaqwNsX~mSBb;1c7`6S&g=MF`{(tBd|hpFP-CprJce!A`KtA6O=^slx=I|5 z)tCY2YlY`w^S1u6el=gSn;g^_D>aW{TQ*j$Uu#lhtkhNFaICH2$Lg+46OplwJ*7fpvc(e5Ris#|<>8hW>j9w8NtM7PMV_y49%jZ?=Pr>u>&|@8O zIXwQ3zl^ckDH!X-JO47q+8KJRdVig?{bRlI3jJGGYUD7r=4N>gYyJH1aU>?LP1IH5 z@Y-ZHe=Pa=@GH(;lz)Rq`W-A{;PWPXW&c>OUo_o44^v~T)KwVq+@$quO=^slx=I|5 zwKWA}9lff5tUuoCQFni(##pJVFk-A)zt*J2SgEVT;aKeyjCK57{bT*eLjC(zYK)b- z3M0m<^=nOPjFq}d9FDa!1!JAKx__)Ude6hu7%O!ZMvPVK*P7HAD|MAP9IKi6*nsmj zeTPL1oUfDD^pAD!*Ot2HVQP$(x(Xx4s`YD4YK)b-N*s>0H5FrhZ)B{y-{sENxuT1) zdgbISdq!m2k&%3?(zY1uTEyX4?G%i4^!om>uD(~Fhp91EYK{}zvaxFY{O36m6Jw>W z5{F~$Ou<;qjs0U?FQ13Ecr1Co;(2(#3ZFMkM91no-qo1b{?hV!)%sKLJUsMR_1VAm zzSz7fCDqeCRy*`qy=TO~kg;~AV61jU|GeJv*aG)?6E)_Qnsbe9*|kaQ=fA%sF>!68 zt`djWCbMPwd_Df$rLRdp4ePv)zQ2F0Z#ZG5dmg67SgETp;{K}jYfWm5mAXnCjVJMqjj>W!VZ>Oq{uF#ZJoH$*=WFr% z>yP*T-t)%4x?z<+7V5lwjN0F(?yrA-_AkBqWA6F#g9o4Gbd@+~eeHa&zIFQb58m!{ zl{oL-<$Hd8w+6*$&)m%EDskq2a4WCAKKp+5%olj&8Y$%u^0l5ey?sbyo>R2icu9L@Yd!D>6vQFbVspW&?;5zBmZ&KqrNv%-^ z%^Ue~o$TuFb&};8DK#$M*UhZ-^O{~K)4z{i-0Pl*&7m?-J)QTuO?SLUe&5z+)R;qR zGGd$Z40Gt!Z&G6psjI}9%4=rvdw@gFVfQ(-xbOXW{hz}Z|F$XbV{Jx_Ii#-QJ?7A> z-=xMIQdfyH)pKa4U=Hn*>;D{{`u$7gJJf7OjX9*Q;yvcjtKX!?98y<_Gu3n08F~)8 z?~#i0@Vb*f{ijPuM)-HHc;B$wZDa0pIci)>sjG|?_d~5emQmw=NL?jP_k9EJGkD+7 z)tE!-DseD}UVZOAhW8DOuaQ#MWXz%R*PO=vP|F9$areWm2iy<$-7EQ5=zI@h-_t*b z^JhNSeJ(|fIiyy?^!*2OsP)G(YRn;Zl{o9;dq3r`HK{R&)K$jCIc!bA9GbuFpTjHe zf4=*h9cs)WbrnX;q1GSEs4<7sRpP9VbEy2aCN<`ey2`jXhjt3)&|cp^hsQ5?t(!w? z%pr9ZM$DnsAIqpQhtyT#tdDc3{Iw=E=8(F|xHyNMb(}-|ZiLx;-#d9f+$i7AKO4NK z_x{kmSKu?l-tsPK>b`-`47I+#kHY7pTCSr_#o<`ZHtG9D_wSBUw+;P#bRs%dWuWSg zm3z8PF?PqwdivQ~cdRVeZ7L4O+8TPS-sj%?#^zNSsGjbz+9??8=zf10W9_WtSc~V^ z8~d-h2mbyn_xwtYYc4hC8rxLv;hL-U$1>`B-n7{1DsdWDyeT_q`R>AVozt&>aHZ2# z;=Fs82fg~!n~XX6otaKoiBsEW>n6v7-&>-d{LW5JSBb-O(`=i*4t4*Ys{A@se4g{Z z{yF@{mNVV&qfuiHsjDzz4z>PRMvXb7t`Y}xIO}b<$j|Sua~gA~DPRMvXb7 zt`Y}xc-B#8xH+W898y<_gE`dt)0@Ym7`CbTz*x0@tx1isQdfz?v9^XEtNXjY z)=T@x`lI$s+;_E5W31Fw7%^6@Uu#lhtkhNFaIALtvAW+Wn~aS0`Y+w-u0zxqD|Hn{ zj8*IBzk4JxF;?m-aX8k_I*zq?e!aQ>np?l+ba%c|~7HJ7?d99(m?{`4j_uDR4z;^3OA{57X>&DC;^lsd;bhvwPo>k$9`DfI-4 z7{&AJE&X$N$46e`o?odkhtx`#z7Amywf&reF@u2m0r5)tw)5_Y!K%A+-{w$BH@B z`ePY2=8(Ed9L(YAZ(A%mb)D0gLoL@xDbFy6T7P~U9NOFZ=dgDEGBVS22XL#;oSQDY9NtHi+^zH#pZ+_jV%b4Xn!4(3qn zPj6CV4ymie!5k`o&1uY`mTROzbJ$tOIV}E`^uYe-oEv|8zMDg8-217OFg=I3_iO$9 z@9-oh?)}tN;xJ#$c7yI4=?7TEz*t9T_mB0b_q@}u3P?Tx@RQn<8HdcE7wRVp3f})u2+AJ z)60LoX_GNEN_t#&n=PBlUvql-uXlF3N*rEu&GzZ*Q1|Z(Q`e#HXMDx`%@6m_;cZVn z=~MKbBEr4ymie!5p^#{6_ig|2n5Jhgz34(3q# zYffVhwR~_K&S7ilIn>YE%}4s@aQfaqcb^$hV-Bg6FrBZML#;oSQDY9NtHi+^PF%Ew z{O-ziPGb(WTqBjuq1IpH+F}m1d~h7hq4MY7jVNAW4yiRt+8@rLoq{>EAMKyR6F)J_ zT}!DkhtyRVF^5_|Uo%i*Vh*XR#K9ace69Yxgc@^5T_p~#rCNWD)0jgo*GQ>cz#J-n z&1uY`mJg1@IqVEQhuv!_-h*`__x{Ez{T_d6Jm*j=VLH$7tgiKIO=>)=Q&)+@v6@=> zeIxyDQ=jR^=k`D6tT|&p_r8Q0W2LUbh_Pz@d@VeQiLp{wiNmqBh90Xvt4}og$GY!D zP50iK8e^rd!iceI{aTY6W2LSVhhw!vk5%WjHP%1Y-M;%~H?PzfD|Hn{j8*IBzw0b9 zF;?m-aX8k_6pYno{bOA(-)}xQeZF?T_a*h5r}H}6jE&WIysI&<{iWsCCapgO?>C1Y ztDdjs(7%ka+M&m)_ne8pkg;~wajaO6bmDA%M%p;vzhd?3RhoOM^71j=Qb2coG%#8G!St*akU$tR*GTd5 zQ9>!%(aUFd4q0<}q zhpLYpbk1Qb#xJ`Y*AVcqfAW}LbbTNv+LXP_4RM~`pZ&3~kO$ip0S{x&I`Z~;|9stw zIpksg^HSFx5jz)xvw{WMoss+ z8RpNWw{3seUSIU`C*U!^5`UyT#ldp(!8p&4<~9zdKiHlU@UZ;>$DcRvpFVoSmz_V- zzw<}-=@Z+fw&w5SJRf@M7MIV+AM5cT7W2nm`H#&*4*i4XPrzgTL;R8Q6bH-A$KpKo z$3N@*!5rNY@Sr`Ij(Fdvn*X$X^RGC6q<`m+?AwYwc86KT-z+&k5x2c)#vz<96io^b z%IFQsulm>bH@ErvhaHa%c+7u_KT?i1WiMNg^L*vV-B*$a+uCN`d}bM$R?hn2*tz#? zHP`tg{X2hTpPXeZIA&fL=h^0f-_K^>$e&iggSKF*ZT$1Y554{Qcap~jJm%NpkCdZb z*~{)0=dq7m+H=2HEwa92n<$_^S80e8zk|;PD_9^XI$AelgqZAAjfa znf;T;3J*C`94xmli}TFB{LSwAgE=yr>-Cv!!F2z#H*Vg1;c~~r{>fwJcRwp2C)=jB zW=@=EpZ~dcf6f=$&K4e&(HoTi^Ly>f&z-*G7nslNpFCE0$cZ*(FO$W24%@Hk@)-={ z9uMM?>9pyeYP63yPyLbp$zzTaf26#O1;@}?OU(W=fi-<954Pzc^M0i z*>}cy4uA9EbD2NbwgMi;{r!(`9P51ehB?k3>EHPy`}B$JQd@gET4;}fT?Kb``@o$u_QJZ6FTBV1@#_A*Q2JR7ebcRXlscId_p&-Y*L^to)K z^DlDeJNqY(6&`Yyu@wEZ<2---=)d(mkF^3G#yx)XBgcOC;jg>-V*lhZCy778#dfK! zc}JY*@}F+x?q9|E?(rZN^XKtnUwG8Qcl_@2E`Owd@>tm?XNrU6#>RP`^{xZm`Bj|n z0S}pO{*TEcH@$FocRywSI-Os{#9A4n{U+>xHu1&hK_}_Z@OmDh} z+l{=gn=kfH9xFWL^g>3ZJv`2X%KrI&hsT3s%2fOgV4L&B{>fub5r2dWZOdMxN5pw9 z+H(_keTHX~0S{t!nc9uK?tbK9|Kzd4L(Vc59P1=_K09MucYdK>Z415rs5dy$Zsc{{ z`yKXA9`j1^N4U_U>^0ts`-3+Ae6>6t98;#=`C|X%vBE=6v@Ls?BjY@F<_~-3Yk9zf zn4GV?uA49RPabos_#<3pEI8I$)cq`(^>A+UMSrkA;qj<9I1>0H{gcPaJ~`2%>@~W0 zFb}vM@OThUnRHL= zJg0sCl-~R4j@@yF_fyPQUf11E**|&A>Ee%Yr8rpLc}?7(f7$sx?tX#!Y6U!O51zcP zdmdx|Hk#e*vd)fEI z-+yiO&?|e-_vdxvhUfc&H$6G;<)^*Uo$u_QJXUzfS;m56=K46#m3z-vb`T z{qk}Dkj;PD1N!-x^iLjhpZFu?Y?s=a{}t!?`8k_?fjpQm>+v8K^XL58=VV)-y}Q1D zm;T9PWuHE!I9P5!6X*HnGJXG5obLe-nclSbF^8T|-_*?)`zMe2p7^HSFSH+*_ zXWVsWZ~i<#o!wJmgGqu-sf7=Q-x3-*P`MDDo%ZVf$OpxOv{( zhYxo@PhtP$G5;$52p8L>w&vP6&ja(X@A-VpdOV25{Mq;DA7npRy7gV|`YipE$I3oA zQyeTe?~U_ZwZ-$?=TSxe1UzJ#eZo=M?|!qw@vwjLnEx&Q2p9WJZO!#@p3kfd@@J=b z{?tz0v$<^h(=>mifAUz_CufR-S7Jm%lT zA1P%%gxPko{OJ% zr{gK|C*UE|$6kGUHtW${^!-=BW4OppFL@|=8yF6{E>ZIk;i^K&ePoI3-0_X@+aWo_^$ZQ1&1E-BgQl9dK<>u3Io;8o{(VIW_#PjE~?|sL-i+;GL=8yDG z9xMB{B9Hw{oag0pZgTTg%3L-9wrQXDL|=f`<=eJD8J10J>qPhQuZ@9dvER(Qy%?c!?u z^>Ln4ui3)g-;4W&$Ad_I{^WJt^9K7Tk9kP^5w19embNa4^Q?aOTKD{1obLgTGPN6d zUH3f3{>fv7hn(7TT#a5B=UMvstT%uDDV;xgUCkfqpFHMa@kh$z6k2L7it}83=B@62 zTHG%J9*xj$L*(3J-nKc5yYC;Mx9>*ShO-kv|>}BKi4~*LC^K{>fv0B>o6j zoI*=G7svfsaQF;&|0?n);8CV_Bd@FXFX^8=R`$uMJ;&9=-^6)v9{cwT^R;yTx%pdcr-$AeU|>6KeA7sv|U_{FCWZP@o8vswZ{OVgzE+Vx0gpy#H}bmr`Iz+Y{E>ZIk!Lc&(^@g=-oF?5 zy zgvVCo8NV#fGnTL0N}eKrJRY4ZsCFZ-tNA1SlgB(;{E>1UM_jeqaUQIRN}eKr0v<%; z^(U{Z`6K<4$I8B~$TRxZI8WdG>l^9(>A8OeJZ3xbN6O=_XsLNyiYK^#1w0xdxPM9i z&L7#QPuec7CM)n1`Qz~*l7IclclG`i@R;X_KT;m2(9+K3gZ(M;C*VN@=1(pi5Bqoi z2#>AEGx7E~&y`mMKVR;o^Cz#X*B|MhJZ5|GN6OWSxUyHod9GhHz4!hV@MyGlBd@FX zFX^8=R`zX0p7D3Yd49atqrKN3j|VJ%{^WJ_`V;V&n)oB-I*zz%y)(}9k%hs}mjfPU zYB%z_di|09ojIAnudDeJ@R;X{KT@te$CbG<&NKIGOMCBM z0gpy&H}blA|C0WlKeBHt@=T_9cE77Pe>@(r`1vz;_q#NI0v_{M;*XT;IO3{vRotJ| z_XhW`fJd3~_wV<5_b=(+`6K(bBG1IT;yiu#uYXSGPtW};;4wRhKT;ldMN93~DW2f| z74T?;;Ql53JAY)~R^%DKW-w22|MGZruAq8(Y`|l76n~^##}QYpYX|cb_pg8l(W3V+ z>EHPy`{ayMXzA#?<2<)KwxIX<`TvslC17$?W!IgBP(WB@5riOPKpeI>ghkd&6(F`O zAuJIL0|ZE5L?AKjh@nBzLIK$gQJ_JAVU=LOX32)Attjq_iaILbpvb3uE`WUS*Qvhe zeeYD&t*YtF|9ttFex9UG-+t@dQ|F$#RbAcF^`tvLQLWBD2|eEC#vjwJJ!d2DZ`pdz zIee|b$FGDQWvg{ZwR-$A{fj^5d00}{Cy)#I1xU;Hu8!;*UXUe4BYZSwf_J$HVhTFp;FkGF;K z$Fyg=qOIY7XX{y!eE*fuqYSm~s8)|(rhoCrJP%9i>HSBxp0kqgzbrjERzS7xs8;in z(BpL&e@we>M>eYdGh0vU{Z~Q{*f{?TdjDnm7k|w2u%w>Oe`V`Ie|$d(99o~J=)Q$l z67zFHkM}O)k7>smc@OX3#d@HtVd(*%Y8tGE{fj?FPgqjVKnXoKehnn_sHVYsLeoF> zcoU63rXB0$J({n${7GEz{l0sDx6e1&KlOx04|V2g!E1f5y7Xv%FkaP!9_3B=WBR8a zZ%gBkY1j3#QTST1p7?yj(gP-EeoX(=6PoArDO-iM_Wq|>Pkg?S(4(3X{v`BxTN!^$ zyY`%os;_73!Tpcz{oQ-You8;yju-4-{4sjyQ??3i?fjo?J-C-I^~Cu}=uw7RcT_9K z)6n!!J>I*GKc-#R%SPTC*?MqaTIz}OW9b2tou8;yKmRiQQ%`7~Q)jjcZ5?pv!97-~ zC(chok7}xQN427d{Zo&(weiR3(w?(X^UZ93a2%i>tm*y0ou5JTkLjO!Li3zDvsGwo zU*)Jlzuy8c_@Evvs|h`)`R;BQcs*8 zOAnar{6w{KzRLc^AESplvsGy8fI|=Nkw-moeiC|AQ>{Cy)z9Be|J37cYy2_o+H*E) zj?4B3_pzfMtm$3l&QDaU`7!-dPiUTpCH3^Jo~;M>nxh^ps|h{2b+zuOR{y>rp~u_K z_+#32y=)YE*?Mq4IO>V>W9b2tou8<7iG07#^e_IH=kzICg|_ytk*x>!Y@?nyKM6gm zDY_p1$iteSgdT5u|gvbdcu-= z1{`{Dk0a`d^OMk{nrhuqt&CS_`llXmlJUp1XS<@U&9$@r!F`CR2i4v+?)*fxqKExc zPiXW|r}mtU`qs(TgL?&050=%09%ZX_N42uQvw!OG-fR3Zx^%s46t0`C2lw-#o;W|2 z9x&PYiE8!wq3NG`Li3zDvsGwo?|RvKaL*m;iSv`tqnc{nQLWBD2|eCi0kUY&%=^>21@9O^OMk{niBpb^my}(Kc*e)aegd4V6yWQ)ynad{fj?F4|Qg%(AM6Kv-RMf3DgtkC!t3*)w-iv8L!av zPd(n5#vjwJJ!hlpCfRy$UjypFn%?#9{6w{)hy7DeX!KBLwhC?Sd`Gq(+kIDu3_5<9{;4N4&*@XP z3T^G1;L?*gekJrMLt=hT=<&`m{+M=MFB^rM7wd`hW9b2touBBb9=}Zg;*WVwpR!eG zYwtUY^~Cu}=mA4ZQ^KEw9`9V^k7?JQvr(Vp_heAbJOfwP)xRfFZ}7@sJ?vlnF?zz1 zda4Ix|Go`+q8=>Iwe;XM)ihWSfA5-lP>Tn3ww}5FA1I**`FT2_2aL(_3Zp{)zV#=g z*W=%|e)ikT@Y~j?$AkFIH!tiaZoSg_fAkonqf(jq{aikUoN=z`qP3fnu?33Hs(+M*UlF`0rAWw>kBT!Q{QLvK(?jQ zW#Oq7RwkF8O2w|9DeKnvyil;RME5vw_}q0u&8ojr@ReI!Be)FD-2N|FKGX$IpMJ04 zGCZi)oHPXc^Pdo0h6mS|I-^4Vt~%Diel~Z`8M7yyFz58yXB<7z+)rTMoHOHlE*$r< zvyMM=_Po<^`-KYDkMnJQFWt^Jek&b(cn~As#I09a|GRw0JhQ_ZUnP$(ILfzRi^$0n z#r1yd~16#5Av-()CD82+Fs0qdd*2gF!HVK#XQV6f2SX7w3To3`}=mj@!R`UK#Y77 z^EE8p<5%l`VEHoU$rl{uTd+ms} zW#Oq7RwkDopdIDnPn^;-g+l72k0PfPY_&&XZrMa z2e%7PS8$8Lci*)~u+^jFSEkXU@op&n{$%>}_Xl^}#rkQ%whLSM?z>J2cJE_*F%R;s z{#+@zqx*Wn%Hkfc{^d6Wo88BL>w@n;_D;d&coqd5>*!mMf;+mG3NFLLeDl6ESSOk9 zsk2Y~XmroB{PS;fzZpB+nkf$qH=YUu#;WeYb zE*SaN_A)$+0v+FmVB}ldi+PxD-Zu+tw3TmjKP)@nxEB@`2#)eCm@h_4SDuh>tKKI@ zzKJawClB&{$vPM8)vE0mjC^amGP(31-wV#a-OP*Y2}ZuPy_g62t{wDu!_yT!GGndS zE986m+rJUd!O9Z(lXHHu;kFMx(yE;nym;Bcj{+m#%h&vX_)jhu5Ayx#8#XikTp7?4 zew1Z*4E;VZM1EHmot!eEZRz4(W z*)G)U_%;Op?V0ZjF2jRdPKXM5KR&FJ%;i3FW}TP3S8$v~b8kI6i@2{I`z1yeiTN6q zd0Cvj-PMN2_lc1$;z2xPTlaNMex`%^%6f-B+^X%juoq~%GP(31i#OE9vi?G}$?yO71og}*ZUSST3T()MDWxL*8OAf6ywL-7aoI=&6T$fCAe zdK~*av&effVvVv{ay2c*!qSpI@ku7akro4Ia#2u-p-&gDf ziY*?8N66yO58uZempcScpT3J=WpQs8>i_b1V0irCi)T)WF|zoXZ|!W_mC3bT$l|{? z+{o02!Byi{i!rkG(6OUR^PpaRSRfeL(soOa!ynY^xHJSKi`rhy!z}VXoLHl57I{xi zJBzrVCVM4D7K!;9mU&sceluQk@%_DbYhh&Zb0@J~nOtLr`a{0K`hE+0fwo&b4zH2L zhYluBhhWTe+Fs0q`o)Kk#}A%7w1ttyN)OvDo}77c#dWL?gIyQ3FtT;d*V$gogL?I0 zfna1y+btf4Kd9I7Z3spdwY`{!=Z?GwE7ovi(RpnP$7|kO)y^gEt11c%MlJ`nn}WPt zUOD@bueZv4f7UMRKMahVJ#^|lF*X$OA}^Oe+-9t)@ApsMZ0w`0^}OKvx3}I;4B~P4 zj$H0Aaqq`kwH<9^e1-$7jbFxvV^}qwtd}FPFc+?jL)# z>iYf3TWn{r7x*V#Y{r8uZav856X$=#4msOwOBfI%XT-`9ZI^=w_3vETdS4aYDJY;n$XWkiw<+xp>UDe@f{{yYS4FOI zXD)e9W~|XpE_ttIJD0f6G8GUbm&AMxOP4<6^7^Us43Fa7uG;3Dy|_M?Emy>RpRC<5IIc4t#pEcVO3P#Sf-O`hj%RfJIlbIJ61oQ_v+j#1{(*B@c$G0IE zxzu(`Pr-c2dvjxrc5=ykZZnsaXQR(xqx)@BfnaDA?@@<0I4_|0_x`O*}IX1@r{Jnzx=S?u}%H-l%e$l?NZly9U*nN**KY+DrulEKU zJv}g48CuB2QyX(m@Q-zOHU5ObrB_{O{8@Y#}%Z}i!z8SD77(;6%9(O+N#wo4Ze*gac-m?T4d2IaXLUTPvnOuG&kIOFDWm&7TBe>+Q z-C~S9e*59wO?xp9@;KuZ)_2Le$m1mC+3lt$L3)Z$>T3K7j-@di;^c#kYOX)K3d${IV8fYf+%%+YpRAYP&MI#+~!yf;RIQUfjATp`FK_7aU@? zuTl{lapt)K3$PJZd{x+}njbYJDge zdDQk|9?XwAz74_1qqbW-IrHQ8QJJ@C>s}UE!#O`@T@jjf>TF~>{tV|c>1^)*@%?3A z6&z(#uu3e*rWE=_t!(m{F7va=I%Ko;z+6deFI`z^Ws{Z2X6w<)CZFlj#e-~qW&Ax$ zT9sYF&0qa{jFHXno_<)gG{MNGwkw;92ies6P%yHo?ZrIErjBnz zFtVxb7EeJotLO!)4Svs4$e&|d&Ul$x@Q?N=Lk=b1E5@%iWRuvk*_m<7 zn_3?VMmDuwnOqr1Hg$X(f{{&aw|EM&*<8tNz7m}Xcqg^)L5uUv)zNiyedx7QH`s+L z1V`Bv%on4j>sXBQ&3ks(ggn0BS9ergf(PfD^X|LC@bn9=z3v6tu0$^HYhyMJ=IpzX z;pqzc*86sR?F5;+eEM~zucKhTd+EHJO#QTA>cop{)P>Bf)$zTIS{GkX=7T z9$#>jBf%wjkfV=%=@7%y@Bi%!--yqrkfW!!*utEnDN$aIj(vHCd7sb~5FX zfE-=E4d2&I3!dzs9CIQ^pTFrJa!ySyx1Mu?iEC_Q>cgP#z5j~01vxtGu<`VbG|o8= zInr@y2u6;yU1*%+#rMCjw40+-(mDF=@5ULwE0y07M>*;zR#{G6n2(M-Xm#@VGl`=d z9Zy_>2RZuY(p?QtzhLA@+m*;YZW{;BY_Ovlx2|B(o7e7*wUDE)-N)}T7W2gCUl*?% zuZ!!KEuNg?T73Ouvx4g%$WhkykEjb|F+RrC(wTbg_MgVb5hxKHWlFG0%$=L8 zdS7sqDZwRpkSVS27mQ44yArv)$GOt0cb*_~6EQMHtSs&(R;-pXt)Y6Q3)|ctz(*2FK@0(r)n-WU4wGnfi1(Q=k0fXgQA*9A!$dO3cla zRqqRqG9|bK4>G0o{eqDxZC4_f_sG=GKlwTHp0`UdGNtXsJgZ`+)Q5(6f=p?K#WWgE6e|_c}jg~h$qODw(FL;&Q);S!m+Q}Zl=t&|0^So>ib*26^82s(bq#O zf4qC$6@R;GM|s~w9JL3;QJFkkW4QSdGp+iD;Fo{)reGn7fQtv)x>fXgm~*~3ZO*B) zk2rqb$+HvRPQbd@#&G}iHrjm?V;hO>rAr&OQR|}bP?`s}k=S0kc({#?mDtA452Uy8 z%pF#fYmvm*M&dHsu#H;Z5R7fqc4aEs#(|aC#%9Jg-oN_#a-K$vZ6sD@?u=m@wZ0)3 z+oS1 z-?UwsihK)K#<%T1-px5J{7Blj=Qi@>cu5T3h*f32Z&tle4Bv>$@W3~%ZwQ8O+OAAR zzBO0IH*-Az`)ThfY2Q?ZBKW4y6uES+s^=KHl{pp+I;(D7JAqf#kB%&_77W>|*7pJ7 zRp+TA%d6&)y*lnAn7iWdPF^+mJ3!ICwrrheJ}mniG4?@X-2?OIF00-r9u-}aBQC@9 z=D4Z;fm5lcCV0j9w+Sx8(|yS!R=@p%acyrXdT_m1>!aV&GG5`@9&s5xxL&OF(**nT zdjyx`IWhS3uXs*4S8(n1D+QO~sg1eW>bD`d_WBP6m*GLK;&ZvpOPtHOKHDdbh;V|Vh{AxO%-`U%H-4jO!%|u&xBWBa;%(x3XbwASY_sZCT!LF#Q57C zVvEMfgU^I-IN@ZYr(ZB~rR~b(`pgr#`t`d{G(25`kt=O4=0UEsewyGF=bt55S)%>d z@wt9|pGz$L3k3V~zaqE{55Bviel`T-GhuC4CYK)O%6q5#ym9*6xwGd5^G=Je!{=NB zjK33gSvpr!cm3X;R3bRam0-RYEnPbCeNC(0Cq}M_EgB~ea`n4=7aBeN{-I|b6=US; zge}(*o#b-qL9TxIuI<_+%Dv5 z?6S*CZx?cI(f>ow756G2O?-L_e#1@T{ z2f4cIzM1BHuisyD*Ue*$T>bH!r_z6y?U)~stB%?$=67$p1S414u8LgwM6R@cnqcHg z+lzUSt8X9EXY?-!xLwHAdCwhewzrrExl%tHf{`n2FXmyc!j;LDeH^?xovY7|-`srH zu~HEnoZFGk6bOk`jd~g`s@;n zTxq*1%FC73Pm^_#D{U|4L9VX;z+|I;LBQ=ot~OuoSEaWLxl%tHf{`n2FV@3cHHRx# z*QRsz)unoVB{<5JV3q0219D~6`^3l5nD7)o_M|*SZeh2`)?duk@;%Y;!R8M*T~hw zi%vH6U4oG-ZC6FEJRnzEKTR-lrR~K$$W_-nI*gtL0k;denl<<5;y;;OdXOvivmqF{ z(sr`A&l{Pm>dNFQ>IOVI#*|H{;+J9;3!vu zRi@K_Ekf1l%s%dxw7hgV&sb0qH*#d zS6iP#J^g|&{^uTnwkwm%|M_dqw(1uM#yAdz2ji&vqu-%4{$L!5RZa0Yddpu}_WJzR z8qshJx>xZ*bI!@XUNpNBe}@O-7~Yv4$9;BOJcU{WN8>1%FGfq3Zx~0b-Y3R55?eG* z9*pCMxBQyX(=Qm~sO`$+(t~l-`UQe9j@n+#gK<>-4Z#>kZ7=5G{#aePan$|s?({gG zHnm^IQE)Vlf>ox|e~hD5?-OGji7gr@55{ryCx0*FNQ`kLF2jRy)cOU2F^<}&@Rmu0O23w1qK_Hy=4kbdt+8 zju!8m?k2s{|apMP|L%&TX-czuj9j)xs_jp(E# zmmZAct!G_k>K6#cIBGjt;&IH}E{vmYS0mtdVH}6z567{(GUNE>d^ykY7Ny7W&mUcD z8MjDqG>(G#VzhMSALD4%`@|SWVvEMfgK_-WoDGbge!&<=ZC56j9*pBW;aL#8x#inp zPK@KEnd`~+lF7w`aa8>c!5BwvCyU!3j$^nokN+Y$7aWbFV3q0g zALD4%`@|SWVvEMfgK<3mA3rmC`UPVgwOyH9{$m`E6P^XZjJ2MLIWdl7Hu$Yc|J8yA6a=is!gBz=A?Z(a58_ds8S)u_u`17_{zieJv4kT&Z_r` z@i#lfLJ|QN5B_FH>l=dcH#^#{OfDYmKk@GjJ?=ga)I4Q z;Tf?ibZ;v>v+8|fct%`?2cBttLohtkc4aE^tlG9`vt@qo{aD(w8JCZj>z>5$j93-s zduG-9#PE!`3=cfh`i5Y5rtQj9U71{ZxNkPw_DuK9&NI@Uo&G@3O%=rOjF_)s>Cy<#ta_gqo)MSf zfoEFZ5Dd?>U71{Z=$ZG8g86K+`&)vX&%86!o?UkAO-reQ7@iUHH7s2k;h9zM6T>s& zGCc51>l=dMnYJsFOAkG(4ufX{8J=zY`%~mRoEV-FtHONGta_gqo)MSffoEFZ5Dd?> zU73nJ3)}Z>7IW4-E1k3H`>!tVL5blRu`0~>%&PZ^;Tdrm9(bnp4Z-kC+m)%vvu69A z#rJf;`=Y+H)1FlZ{Z3=+cOc=};P#eImqzTHR=rOQ&xl(L`8@DU>+$;|Ej{pzxC{^X zP45Z!d^YX)Q%~E`Ii2Y-AUr3_Gh0Oqw>(R?=X)mgK2ggvKGUU(r{x*zR_-@ja86ro z+p~(~toPiJc_YiS<}i5Hd4Afnd53&R&d-RkZxVA)!P2!)VLr3!ePZmJ z#ASG}Z)$x*Fy=FDS04c5treCXIkG749~P(nOu74S+#A?bl)7v@T|I^Q@$5S49|#FVZLWpy-y6! zh|BQ6Gp%n3hG*KYOhulBZF^Rc_vpV0B(MqGvmo@sqU zFg(+CWh(Nl*|ukzv%XKHJ-hDmBV;}!hG)d8FyAw)-Y14<#ASHknbtQ1!!vDHrXtV0 zZxzgECEjm_^V6O^K5dR%3nGSR#HujgGppVwhG)cOc;K1VHw42oZC9ou&#LWvCdYu@ z3)7zYzt~ykGh%o~tP1lzv+8|fct%`?2cBttLohtkc4aE^ENtI1na`@Vv}e`dti6mX zh~XJAU&GR+5ywrd-Y14<#ASHknbt?g0HY0_5trfNakDuLo^@W7_H6L`xVPB*&2PKs zGrZq)PiJ~<+q*c+GkfG(7&%L~=I6|+_lc1+VteVzD{`iF!|;ByZO`;vV_<}N77l}F z%}5kCUF@a?3-F2{hp7}hGPJ6 z86F-3yrz3TJNP5BqF;=6UqJ&0t~rL6q&>TK)>wJJNes`3%V@-L)2jE0;Tdrm9(bnp z4Z-kC+m*>RFVeH>FnHGc>9l9RoAjKVpAo||VpW)*GppVwhG)cOc;K1VNB4a)+Ta;+ z86J8T4ufaa&!j!OZKnQSC^0-EE~61Sv+8|fct%`?2cBttLohtkc4aEcS#uaX>kQML zU2nbLB!*|isxUuiR=rOQ&xp(Lz%#9H2!?0cu1rOqdEY6T&)jEdoX@;V)1FOv;&gex zNes`3RbjqoR=rOQ&xp(Lz%#9H2!?0cu1rOqRfoZ|fh^CqTO#w>6oX^Wtahq&X~e$C zb)#CJIQDD^Jh5j|A9-eQ?3uJvje87bJ{tzlnx9SQtpA((ev=rU5vxMCMtEk``^4~! zxC{?G)B5Q98b%vDBQC>3&zi&FS>NZ%pSQGM$Xc$ z`8l)dePZN{*j~E!apX+vhT;8Y+n(ur^xn%ymSJ=l)v*s{(*7^CgXE*+d zzsFLk5M$pY)_o;^-?Zv|V(gp5Wq7b}YJEd6jse=POs;*L#{ln1_k4EPM@~Q4{cclp z=KU@0**S-=CGR(h;Tf?i%=gTy_le;daTy+Xru7ZM@J!p4smQZx+n${+*R}^TJoAs- zM$XTO;Tf?i%=gTy_le;daTy+XruEV10Y)1*se^2LZ=Y@I?Zen;w zTt*|#&#Zc%7@iTA;elsb-w+JXv|X8sa@K6yGd*thy`1*!TI>BLF+3wyh50$N>V0B( zMqGvmo@sq_Ey!qtXT)WAm^1Ht`SY3k-KNfG;eV$+TVZ{^Nes`3%VU73n+?-wct)%W^K)j^`^4~!xC{?G)B5OlSBy4z zMqGx6ISbqNOmkNKXWBFSb3z-0G?S-1-iBso=H)3U4s~&5!*`_4?NSlm3!~h5fO!je-}3RMmxx7{P&pu9$7v$ z+Vp8)g!wekrccdRvhvjWE*N;u;{NDF!mS;LU5WN-Vs`z{C zZF{EY5#ehi%d>D8JnQ|>$nvb&wr9F;R$othwr$;)a{yxOo5b8-v2-2(aUNmS`@}ep zATGm$^9ZeP2*&xUwkwlM56@S_le;d zaTy+Xru7ZM@J!p4$)$&$g~Q<4K!#_BO+Q6EBZgR=rP*V*qg(9vlO-z9AUL0Bu*M zqGNz}<%`bW|84!9Vex#{IX3Otx7S`vz9&Tt&xloFzGqgwPYln9%kaQ6t#1g1XWFhz zMV?iM!832Qv}XhV`K5dgM-0!1RbjqoR=rOQ&xp(Lz%#9H2!?0cu1rOqg>8GL*I)-S zJlkyCzo$?IF+3yYYgoE8;+)p1_le;daTy+Xru7ZM@J!p4$)$(qw9U3X)8l4yTsmjJ zKjL(m&xqj}u`0~>%&PZ^;Tdrm9(bnp4Z-kC+m)%vGw-T``K-kI&A!#sp1u5sL(M&x zDivaQMyv|+J+tb4Vt7Veh6kQ$eM2xj({^Pl@~qmvXYzN3p_lgTx3%}kZ%z@zGh$Vk z@0nHa6T>s&GCc51>l=dMnYJrak!NB1p2>BM-Zj#mUHizxGM^E{Gh$Vk@0nHa6T>s& zGCc51>!Z)ejW&2jT!u&HvtjV8x@Ow5jvISqJ|l)_#AP(%7+}@=#PE!`3=cfh`i5Y5 zrtQj9lr!&Y_k6~`%W<8d!FPNn*SS{Ov&L=jk@GWRct)%W^K)j^`^4~!xC{?G)B1*B zc&6>jRODH87(DaFr#)N#@U3zTAckkesxaR(tKKJuXT)WA;F;D(_n$J_;2Cil9`2jr zFnBhQ;n^m0N6EfP49|$mXvDs0)%(QojJOOBJk$DyV0fnO%2brI<}i5HTsxh!>tCKL zo)N<{VpW)*GppVwhG)cOc;K1VM}L=Nw81mtGCa(gcTLfJ<~~8=eAc&4+Ot2d*iz0l zh~XJ=8I8!9RqqqSGvYEl@J#C)g5jCAD^pR9$}xNh3B_b=T;zAH-%&xloFe$K3V zpBSDIm*IhDTHg>1&$L~ciaZO4!L#1=(w<#$=iPFCMhwr0RbjqoR=rOQ&xp(Lz%#9H z2!?0cu1rOqHHX2o>f6$uy`?`7xYj+NwSEV}b#|uLH9FVN@=V8#2stCRm#%Ted}h`8 z#K;-3y>#&)XIi&%-z|$j4`|yn{S3w1U}Sj~w(Xhq-Tx8hS+i}=G-u5X(>d$?;i0l$ z5o6yZ)_o=a7+}@=#Mn29%kW^|)cS^C90RmnnOyt0oS$9ip3ho;m*bkx^qOPeMrqIL zZ~juwHHhIEu`0~>%&PZ^;Tdrm9(bnp4Z-kC+m)%vvufL(=`kRDd)l+HUC+yVbYgf$ ztP1lzv+8|fct%`?2cBtt^jnQa8$2T}!^50~!{Ax(#%a%vxNBY6H;LgHaT$#`Zd&y| zF+3wK!voK>z9AT%X}dBN<*eDZXS#1zH%WWeyU73nJ^RCaI&)n}e^%&6kjothre_ zXK(5En_qCxXRY6XaGjrN&iW=~d1jAX3nORg*8H4V^*%9jMr2KQgCv{Q=unJ>dVw zww>F?whjCr*|w*Lux-Jnc75^XZ`}TM)VC6Ui9=ZC$ZhIWO%J_IYaR1ZaJk!&Se<;1>r;qNt|FiR# zQ4jm49`F2?57cSsoeS>+*?RW4;V+L-50<+n^pJ4U&)yky9Q@#;)WiO%Cp3Dflk2&b z&1$xuGlE?or5*%mfu#pr^k;)*Q}(@k%g^sgJ?x))ypJ1yj4qcBwuc8~>zT22m*~M5 zH4}QUJ~Yj%jM~>*JX5&C?*`}R6-$qupXXnhvEQTz zFVy^){;9{i!1!a@T{_tAJ(jKKgu|YDnB#?I=cvJX=K6acefK`s+;N@e$MjD z<-zlh{#x^6`llYR-g+-gom|hg3?D7lgYoLK^ni>0{NbyI?KA%~UzGiw{Zmh9^w1}l z4z_zsiuGWOyfHc-a$RUz``49yesItm>W}H4dc0eVKc=1Qx|ZShv*#bw+u7|kdeBC1 zTAa1+`uTj%4_x*SGHJ6O-rXKcB zJ>IRxAEOKF<~_pE#d?sRR}y-_2~B@^?AGqDY*CftDf_3M(CDGgJS})Fe49&8ZSeWw zg4T8>_xDG4o9HjBY$kfxKlONDHvSl0g?irZ(ldCxstG;hJ?Z7qe&^n=P2qU4f9eU1 z9_r+Ju4TAcu^x<9Xz2kL^RwErpB}L3xJ?&R5BsMc?<>Y1qsyg(?cuwN^PN z^{{{H@oqEz7+v7Xdw5fe^+3Z>mLBkNynb}*K=W}H4dP4J@KCvIy(pz2`^!{1(XnrtW)r21Moz=b2e|gRk>Q6$CcZc!Ev~xYz z(tEsEPdr|h9&j-~>n=S1z+bGmcqfh*`xk$V9{S|c!FKOk#d_lXJ)wu1UU_ix0iQqm z>pN3VX!@rfug~~n+S#vbS>2*_J(jPdZ#?E!uAXA=?+q)@^rlr=>yBz=yx2eWghmf_ zw$^d~-`SC^2WwCdmYb|LUgjUXrkdj4eyWK-rhn@3?lk_GcC4HC@ZOcJ=g!K}J5vwx zQ%&drXG>G9JF1oA1^cI-(CDF0d0Oz=fJ4vg2fwhCdQf++rANKNE4A*ZR*t9apL)Ez zj6X&f*2sG_CuaMDb*Kl+K1&Z?Q%!@%i~UniX!KAg*3EnLZJDj-6R+L3n0hc?Pbc(% zlj9ZD>i%W=rylQaI9e@y?>6PoAr32Wp%df#2F z2fBJKJ>XMKgY~d~>hbO|{uo_(TJTzR>ta0^uLTJ`SeN4!LD}EgKlOx04|QU_yhrDI zvh^JOv!e!|KQC-;XYzQ8@rr8Yc*_2%$Gg|~V|3+d!E0V;ww@;^UbPGJgJm_LM|o@A zQLUVhv483bjUMXM^|H}`L(ebPnK)=Z3@trivhx$w%6!QFsmHs|_+xZstI*cwHrf6> zw~@Du{=~=AgdWvY>yB!>dAwl%)Ds##)TupZqrPpk^`M%1fSXpH)f>E$*uPBw)Z=~C z_+#3!M&2Xbu2>Iry<+JBpK2Pchy7DeX!OvhJS}*wcl%;J*xx%>A3x~$j&-y4clJ*` z-u=cOqYLZhJ;Fn?&*#^9=EH;c_b;}1ll%Mh^WWI_fTKSw`#bxmp3vx_&O9x6t$KL2 zo=dy-dXV|SvYOCC-bWApS}<+*r}gs-(?9iii;O>}o$I-l-t=re&!4cypyO$1=>Zq> zGk3QWg7uEyPOp!d{;4N4&*_s(2iuz;%GUFpIpbu!;{83Lhnmjsn!4ZN9phxY*gy4n z4;X)pF81qMhDT-VdFt6uh#su#eJMLXvsQdDcyQ@l(Zl|!Cp3Df)1`y$)fw4(X0E<) z&q4V~=;8X|%Mb6f*D2r9{Fwf!$NQS`$Fy@j*V60B*7L+;$L&o$*xy4-54f10FO8`L zCvSSCTn}ge)Ds##^vR`z?agDd^&I=ci=rpaPeKnhee}B@^Y6NT6Fq-6{Zo&(*!W}G z*{^FE9+$1>%H@MUkMnNK&d+IQ{?4DX@zI(e(?9iu<~e0o>H_-s9kXKXB=f5r1p zLJ!x!^`wJ>`mD>B@OaApsmFWJ_+xZ&J=fAZFZq>GyB*l{K-3P zAmhdUsV6jgsMDo`?af))dVX}>=7aN-&_hk%I&?q3zUwujhy7EJ_mJ_&=wiRFW%cgX z_b~W6zX9>7aO=VI&rR9+Idjwg;LW?=tNAhgQ%`7~Qz!dzEyH`V_55w!*W`LaoS%dq z^4)yWvB6P${A{V@C!xpty79-fb3NCxd2hC!3%*ekJ#l_4J>X(~UKqDk@Q1|@h#vMY z{un*<$)$ts-dD5rJQj>w#`!Rwe-e7A>Cg)=3U>a(i<%$PKlOMGVqR9zT0o{E73E&_hi>_{WssnU}t;{+Rx$$9u&1W7^rTYgyf)_dx7EE<5%nnV;kQB=jgltvjlf<0<>6p3vx_Pr6<< z>f9w;&&Iz$Me-cy$I=5PJ3mpaXTvjRqWgaNUdh7uNJ9y7Lp&YJL)Wynf@4Y0p-ntcYdN;%}+v)_Xp#TY0p-nt<^oV^^ARJ z&B5=N6MB@P)*aRA^)b`G_+y@jCG~Xfm93|`pmXs1WlImV*!hWS_4{K9J>DOUKc-!` zBO7^pXY2X-f7Tj2|0MLNrdoGYtMiZPU;Hu8!;*Ri9D1&c?)At09esOSx$_g%YJL)W zyl0I+rajvgZEa4;_UG~F9)Hvm&p!!0%24Z$YUO-`{fj?FPgqhy;r?@)AJaeegywlzQcrJ? zttWN-dbc}2gN|PbJ>K)iAJd-gindnwap_4MzY=2~joIdG#*{E~hVm)zw zEInYd^AkPQ<5xnD_b20zY0p-nt=@jcdgA;f^nfAr_+|PRf6Vi+q@DqXo=Y!De7?N3 zJ3mpa&OZq~-hlDPw5t=@sQLbEf3BT1cJT2lp-0(j-BGO`zfAw)k9i)J)YG?rww~Wj zdT#LiW9fkwJ3mpa&OZq~-V4Sb)2`c*jlvIP>$z=a;`8N%9@SLqj%s!OG5w1_=6P6B zPj59_&-KaU*L&RgiE1@J2|eDQjX$P6+ZAoCelT0llqXIfeEdr2QHEM~RIA4?)4%v* zo`)s%bROW)v)z)x`LXoqSOGM|2GrIuY?}3asG*F zH9w|*@y9$5OX?YL=t&*FI^Fphbo@%_@m@6knD%T}w6%Fqwm+!g@jX8ON$63A#PQ4Y zFaDV4VM#rG2N&y!k6)G^XtC#?=&2sR5_-J98h=c?Zbvo>rxxppk6#HrU}JtFD0Iu#Bu%w>OL$meZKK0CVoF7XMwAlHHYIXie=<)u`_+#32JF<~?ShgPA z1D<-~{3P_KrW)=?uk(-TU;Hu8=~K1}Z5?pv!Tr^#2i4xT?)*fxnxBLouhLqNK55U{ zsCjs{Ke%@}^>Wc`i{ufgZqM0Pn;i1518!yM75fq zgdT5{@yE1htI*bPdbXa_`>%u^)s%SuW%?I?%=56Mp56~->p_3?2Y6uh@$&sK^#-pD zK3;?gJ>F>Jk7>smd5`Lm#d_lWc&Z0{s%fwu_AmYzJ=B?}1+R4;Rjdd3IX9sP>$Wrv z))SiksmB{*{4woVFYn=f*rg|Ny?477FXkfX80?Q;?`8kg6B<3#nWqJ>4LJ2^e&X>; z=uzH;Kc;``@x~f|OuMd^jhZuD{-~aKf4B62X|O-m@!j-KJ)wC{pR!eGYv0kudg9|n zLXT=n_><7%t!Df&?b>rT3VX8k;J&l={_bt>&QDY;#|!o^{un*Iuzr>daQ5tpiRyy1&QyN$63A#Qv4g<9Ws()2{1fqvlmEe^gJLA4?CI?EFMe z_5FqEU;Hu8=~K1}ZSA|dSWldvgdQ-oG$s5==<(Jt{+M>{IU9x7Wb46w6$j;Kk~=?9 zt>(w{FaDV4VM#r`*JkU%JqxKP&QC&*Ze0!cTa@$FFrmj=)A(cBb-ipK6ccDHNE$`^Apu-eoX(=6Po8?Nj(D&J-A04^)%qOvs3*=( zLXT>Su7^MJu;wSB$6MR@W7@UnY!oic)`RIl{bIHr z+>?uXu&gHZ=+@P6KQWzu5_-IKj6bGb*ULuLFJvTFEo}7k`YN zu%w>O8?*J`UQ*N(=O>{@HPvumD;clQ^iMtBy2c;Vp6!aZdN*b3!F`&j2i4w=?)*fx zqKExcPiXW|r}mtU1{`{D4vTD^W~`lp`IJg3fV724WY&(?!`_fSuqpM)OORKtCTbpA=`@!n?qG40xO zHVSXa){{Dap6t%gpz~+bzxZRG)2D0|+S+@oOOKwf#`#I;QHI3%VM33$zVXMj>w4L! z`sHFhaegd4V6yWQJ=ODP)4%v*o`)s%bbh5+Pn@5G9@UiaC!xpN!1!a@v0mQ8yDeJ} z?nA;nV|{NYcYdN;oqtUK;*WVAmee!g(1Uw?P*0qngdW|-8tzXb`+Jzs<85gCG3~lu zHfr9U?GNssK|OJPEInYd^Apv|@s$0GKSmFAW~W-Op9~qV z(DY9|-bThB)2=;dqp&Yq5AORwJy_G**`1%LR`jrc>Isb=>daQ5t-W_<>%l!0s0Yhx zLXR@ka6bs0pH2VN7SY*f7~TMzD6Ks|AOEInYd^Apv|@s#~jPiXYer)(A4 z+Ie@j9^4CndgA;f^r)s9?qi_-nEt89+t~PH+O_9w|o}%91)xmn$ zzxZSHghnU%T+7abiuGVW^VTT$c@ux{ntIqTb@I7u*({+4p6!;<1HR;V-5&|(?^}N| zdOiMa>u0~c48LuSdOQS2_n{EndMW;Y^qAJD%>4c@yTAXvgSvOx>63o^!i0uZ@B6au zs~=Nbf~Pj-GyacvnQ!#eEquR)mB^{5a=+E@atjYwxD3yL|Ne`qrxFm?_uN9TvPAdk zaQL(QqH6;2q*52$(S5t%GCa5r(-{@=_s6jg@;G!L66#|6SWziE^OzzAre+f#4E6 z$ia)Z{?gpLsBYo=Ev!UNJy8y{-{lq_uy7fk0l)g{4tSpftTEIa_|b_3?_Yo% z;NAseqw@pYy8wK65J&CZ#I09a|GVym^Tyf}e66nuuD!lMa2cN3m^=JS|C@T23$DHX zwBRy4ci;7A|D(cF33?2^`>r*EMc*X9GL0VHIx|h4_e`I@Q7~=+>pOy@Pif)1@7g0c z=qk1s^BniYv_SotE4ZWkY{AMBVp0G z_XsY-!(8%y7g#5m%c-+Z{AhIVBIo%^oJ(^b3p1S%WF5wyiPtB4|2(MqqEF_o*Dv4gYM_`~DLzUA`063(mR8 zpD|_coox@marpTL0!F~*Fz z^-Al1mnMwalAaCB`1?LF#*DZO55{ci5&z~rn*(Bu8F3k&(W_4lRDVtIPd|5^;6Xf& zYkh<3>#{D!O!<|`b!yOs?(Xv5og=!y2Qae;IS5^F3;OY%}*>8$5p6KT$jT+<*P{bLL#Y z_lYrn#N@`(wVfEhr&gaEpVvfX0WrppxC~Eg{8*{Sw$}KuU6(FB!@1AlGZEg?4n4Os ze!N%R|BvyD^0-yY5#J}q_z|~WYyI!?3*$HcGn<*Yw-OLz{D{l&tg7S3djMh^+8ICI z+wcF+_|aM4Kk2DM<=pb6->x;JbITtbG~evUm4F!IN6cQZbmd@G9Y5YP5!#13ez?ZR zdm&={cAZwfBM z(|yUW{Kvl6oNQ`?GyiMdI8Xlk@xjIa+&;!-c$QzZfmPoj_?A7p1()H$wUjSR?Kayr zS1{hQYP&MIe8ziLty(pX3muUl-a8Z8lDXV}&YUwR9b~`jsl=a=$Jd^^ zEAu|A?r*;Kr(%pe_Epw4@2jxYt%qyekjEo0?PYy%+L{-{7iEkr9^6rZVW*erb%6$DNln*)Bvb9^_H$I|L(- z+AcJ?c|7IwH=1jJa|2@Jkyu&W$B$JtkGyv-wqZzlH21Ui>sc$C`U+8AHrIb}4v*2n!{g45F|ygY_xWrm zjWY|FE498uFtVxbLgVCVWs`NtX6w<)CZFljwOy;?T#0P*KHAuVA!ReFmu#AQWRHYw z-uc;U`2IMUe)<<;jBIW`ZXw%YW&Chu0om004#CK#wv)wuJb0^TllN4I6K!YH+%J73 zWb?2;AH@s?owpt<*&ILb1hz}4^0KM*9RV@2Nh~B0aPhp=v)OF-m>zkCc@y7RuO8I8 zHyE;szg>uq-KpbS)Qt9SERzqH_pa+hqh4L)Cpqxk#q;L4r~EArx_=k5PSCmi3o*ug z_x7ho@0#Rt@nF8&VB#24-ywL#`R^BObGdlBFPS07-{`zh@c-s@`i@9==h)WY&R`od z=c&We*JI+pS(v)&kN@^}Ucl@HQ{NHB7~_A}e=cJ?HamW}#tq}I^&NsS{@P9!cOF;O z@jt4~@oyfo>W}~2_ifE_4qm+Z7#aUa z(j_02@qcN|N7&vHlcNXw|1~f6n)>KEs=>9_cM~imIXty79|*>Nah~Cs8T5btT%+gI zVCrW-J_J2yM8^*LunJu#xS`-W0gfYC*9juea840tYI!C89n-;?vfXXr|IKrXmo{0% zYfQoZeNDOc^zxjavRwqb<^p6&>pKJ^Q`#;x&N&5{`tCt1OusXO_1`w%=s7j`;rI3& zf*xdwk5%K`Bz}KC9GQB2)z8$YM*qru-d70@9P{rO<2>aTD@HHosBoE=DXs62b&)A; zCyRUjMW(cVX7Jd@E3pQD{;9z+i{7A4A#&?kH8T~qo2l8>JoT+rKU1&&YLf@eAO(xN zc8D=Db>-3TV|$4)+`y{u5R6O>g$J3sz~9F7J2Tky@ZU+MwteijA!K~j%v7`8Oqpxs zPe&tB-JeyYHsCfnZt>3>?4)|jDTxD#QBug*94D#dCNfCmC4Q1W7S9B zqcz$_z4{};%Hr08V?Dln)yO&ToHpmw*+(2d@8sEuZv$Xmc-7gF_R8*?c(~3J;1xOT zWu8}BSC@6+m9{H#@zASHR&&2cFY?NJSK6yPD@X6l-ihHAF<-;dr46~#`kG*PrR~b( z=IOEO>w@9cPX?&XsO~qPNOE*tZg1^=+B<>JzWsC&xWvctxxZ=I2W5Yd$e@ zMO==j$EvRjMy`g!!(2VR5?+N{rMOE<%j{ezEa*c`@`yes*ODx^TuL-`^*92p} z(spHX^YmEtb-|dghQfnvbVc2H3*FbQ2JM46*RSB4*O~V1$%$9(!Yv|(Z^V2JOZWCp z@U^}s7`|z{GP!wrtopiO_%;+C`c@q#-v%;#`^7pFxAUZ74kSt+_J3nd>*$Py4n_`=(o?2)^kvMegmL;A@rsZ3BGMc13QU9;<%k-#<3X z918}WzT?mLG>kBa`;f1@eK%|W?3?jzWj)Dy^>xMjo_XnOt)+@?g~m#K;4&vbgh!JZOD~VB|sDmC2=N`9(Vg zXMf{R^Y=HtVB|sDi+PX-?YAx%dC>M^9_FFCGI@x);r!rDPv_zJ6V{MxCd9}Cv8r_Y zk3497O)&DH?aJixA9=9q17hTXSXtb9kO!^r5R5!%yE3`-AP*0`|8~g(G4epHEN(r> zgZ5h&j67(&GP(3H58*K7q4}Y79=E2%CLF;RRkq2#8CfD{N4_19Z zj64u4i(3!!p!FSskq2#8CYK)M;Ws~A&G_RBMjo`imCrVIG<*lLzZ~ z5gwJ!!&A?GLh?Y2JP@l&xBtk4*4G3h58AFwF8`5-8yB2NJpnQDK&&ioJ;;OBcL>J! z&$L~cTzc^RGu2-cjPIXndod4wCqnD%g7JGg+Fs1VJa}JnpFd2WJ9qZHVBTr*_0ODZ zFY)=qjC3AmuD;Ox_E)7sj64vlO1J;WgVxssBM;iHOfLVChkrhFsr=ptG4epHEN(r> zgVuKlMjo_XnOu612i0E_j67(2F%R;f^>x9>gSHp*Fb~yX%7fRF&chRr9mn5$s1PF$ z#H!NmKk}gUHNnV(wkwm%f8^o1HLCnc{uhn~KxoIemF55&si)`L7~eTQJ= zLEDwdr3ZOX{WZbJgSHp*AP-t!7mPe;dod65;N4i_{GrVChvU+6D%2W>CrVIHb0lZU7q@Wg-LbYeOWcYN+f`P~d+o%A32V&%bSXH|HM;^4kCK!3pc4cz;k35W8Z}VlQG9X4Ch?T{y z2YJx?4#CKSwkwlM5AvY;Yl4vnZ7=3Q9<;tL7iJukEVHl?VFo-Q+%AA2jEj{P+8_EAeOF@ISmK?f>7_eT~P*3NidAR+ahw zFPU|};i>t=@SnI05B%5qnqc^^?aJix4gRbCx?uRP?ZrIwzq)e%Oa7bprv1O*8#VEt z82%Hh%6$J1y!~16pBVlVm*Ii`T3-_k|FvD2^88o*^?(@u6Dx~*yXb$oGX9U&`N#We z+W*Ibah(4v#PFZEj84oyfAzn=v{hRZ4F9!VnOwfXf32?xhX2}L%!B-^{<>iJukFP= z^uM_>{zu(_C!YWBPy4^<$Zh$4twId{iB+XLC-DD~ZMT>C|K+WAh%x-XVS_H=Cs&^T zH$A=+c>-eiPfQLhT|Dq#_16W%e{EML7Z3gSZZ0wZyWX!w-O&GVQQH5-i?5OShZz17 zt4jCw!v9ldY$x;2cOM@eWB9*d>AG_MPcGN?BL9`wk2Ck=iTnVlezCE~69v|Kz~VWv$wpVEC`? z%H;A5{%d_L_|Mv##+>l~Yg0Ee{KY)*U-d_S_hhyU{u3*UdmQP1xHA6#=0fq`Tb%ZP zkDt9P`6q_|#AS5C|C)EBeJoK#Dwr9F;_U@GS?DPi$na_yf8L=wN&zaWOd}4S;T#l#5s;>)%XG7tk zXWqLC=CjG}Z~K5FKKI%=?b&6=-n5k4Lk!P|`5Kn)d`$4Qz9tx+X}dDHd3vn+x?p%V z6drn39R|-jcS(D;@$XNOV*oKcBi1d=_e|?+J~2EaF2~bj)z<~Xv!U?Nv#@>7W-({p zu4&Jv@4vddcO-^q#O1big0J;8!SGDml_@W0T3?rS;hDA<^U$+q`<}(W^^JXVAj32L zy)tMS-rt3TXXMl^&C4tPwu-->hQF<9VR%MdhKHVc6W#OKwBt`bZAa&HrpJKh`_ehH zH4r0b#P-sij|slktr~xi7XPkS+n!YBg-7PIE#33k2Tz|h`&{RIR+0Cc z)jiXmjeTfM`7RYPJR{aE&G$^}Yd$eNBQD3&W7XFM!?U6A(6egWp6R~XxmVh=>Vi)B zynz^=5trN63BK0X1j93JSEjt2X?+7;EJk$1K9(v|&RWP5Gc)!`4lFr%V)8@#vAYyn%tP1mUru8+S7@iTAw@9gPCp;rA$J1lgN5=re1J8)d@X)j7FnHD* zq&*w_KJG2{e)HY#`3&zj-P4&~+pg}D<(WNlEsUI{Tk~_K^{deP&9*($bB)e@N0w*d zFnH$eH?llyw(Xg9ewN|crI+w`KPnYs?3={8&*kr%S|9xmhT+69fVdn_k5wQ2UWDPn zF@U%X503%f*6#W2;E&9Tej(X?1q~dy=Gc6HI%n6;8Y}NNiQyS>8IAbdN$YEZ;hDB8 zQ{K6T*4JfSc&6>eJoKzO44(DvpZ4r`lb)0FGh%o~tP1mUru8+S7@iTAw@9g zPtYno@skA4?SxRgJ-?f zv}e~_?>C9z8L=wN&zaWOd}4S;T#l#5s;>)%XG7tkXWn~?<}>#h8aU$5H$Ry6Y|0a- z%ll1Yct%`qTPOHhUlR<^v|X9-1FICA36PG_q$EaS#?_4vvUq# zOWto1!!zP?+d9G5`kG*PrtQk)o)>$p`nq6vHWVIuR&Cp})8*QB=b>rO{3ExK^D|<2 zMyy+!pEIqGJ`XUQ@Qk<|Pmfg}eI8(V;2Cil9(oqG?U`P4^bSjVw)4XA%cz4Go)Pmk zEZzB-;A?$NFg(+CWpeZMSoL+m@N6hN^sL#oXL{Tm$nfl1>-{D%JR{aE&G$^}qiaEi z6P^*5J&a}Sf6T>s&ay&g&eO)j- z8ww9S3)}Wga~4idduD%bNDR-2%WZ4)9T)kWbQSu1vu)3GKI{EZ+B4e|Vt7VuFWvjp zs`uT{j!5`G;opS~`ayX)zo;HLvV5v~F?cC=Pny37~GaShD3|ycOoE_SIU}}Hn0Fqb^);Uu=c~l!czUe*x?r5I4uyy3 ztKRkn^V#G{&R>}U2R?`EJtpnhw6)$L@4t!R8F9I7o#1PIO)xyuc4cyV-(%I+1;ewU z@X)jBFnCryHtpFZKYyqEZ6+~1Bi1d=e_y2aHJ=!s5trlXvFhuB;n`4l=vg=no^>9V z_Uy3fr-*07@Qk?JwodT1z9tx+X}dDz&TLm&_^skuI1HZk zT|KfqYYu~F;WcT`-uJKLWIrTE&WP<4=IxtWU-OA^3?MGY(__`w1>+bn6doP}yh-l) zto3(>#q(M3wQ0}3z4lu2Jt<;%MqF-NC-_=l6AaI^U76h8_gM9H!SHM-JoKzO44zf5 zOM5o(pI^%7aK!M8ShqC){7ma>J~2EaF2~bj)z<~Xv!U?Nv#@Q?^crmE^=Z#G8~5)i z+#X_hM$Ff+bmwD&uk|&-@J!p4$<5Pa)z<~Xv!U?Nvu4|#>2cHhLfW(6A91?OXTuWwSJR>g0(__`w1;ewU@X)iceb3~xlD-?#o?ZLM!!n-{!!zP?+Zz47gz!Y4ksA!p zh|BRrpOFhs^clIq@Qk<&4?SxRgJ|jJVvkPVlup`nw#%3D1bj@kD=@BYL90%P|<95treiXWouQ^O^eu4IG%y1~NSR z%5tq@3V}RDz1j93JSEjsuQ|s%pEY#<{-t}!cV&s; z8L=wN-#4|s<`cs+;&MDaR()MCJR1rRJqw4yv%Y%Tvn%erTh7mj;TdtcZJpq2eN8Yt z({^Rb%bC{KWnFlt?ZrIwtT_yxg}0 zHJ=#A0OE2yJyv~PFpdF3;o&jB+sQqjwf-*0HJ|A<$AJvb>Tmv1&NYbP8F9I7o#1PI zO)xyuc4czUi#=9-T`)Ww3J*Q2w(Xf71Ddy|b2hf?d3ldc49|#lOY_gqv_AT+M#Blu zh|BRrztt#uqTgyX7@iTA;h|^YFnHE?N7}O^?pjy&O=5UPTt*|_2Wb8OWA9tQB&(`( zYj_?3QIj!ZM6IidHluQj0TB`C?m|r&5s;U>o1W>K>4u)}rXMrILu^#!pn`~qBZ?V~ zn)Xc$65b4oTD}lXM75w7k0=`mEKn&$Mr*3!Oge-=_O^5W{E0%CJ1o zwEp}6F?>cGXXi|({#J|Ov+c1%pCyNsk7wbhO`QWqKh){7N6tFfJ_iuPXT)(@_aD&u z^A^KrTCSGL@=WV*wfDkjTHf6beb(sNXF8rWKHTZE)w6Z`j7|)n5i7&;Jk$E~1H|wd zah#nqo%&lXhR?Rg4t@&@?zK?YJY}HpUvUx@fpApAt-G4yqo9~8-P56vB&W`zR zsO4k68!8w+BaX2{pS702XURu9efB&3y!rg_c$WVTL}-1cdA8{FnH#wrBhNZZ%loF* zUq-(J(X-EVJZpW-muIS$=y%0uY0o~>wZ_87f4_Xz+CiUPZIUQ``RZG_pZPB4eD??c zeH(MjJ(}_X_ng?Q5mu})e~5O@SH9iCb*?|8_IxzG9u@>cx`$9DQW7fo$hMgWEk zYx>W zd~j?P_|V`yhNc z_Q@v|KIF-JL&woq8?4AFQKIUCke9pL~+1ihrb>?SziWv3@>l zPI&lM#tX-^z(?)P_nW#ewEmIy$tRU_`bqEeqlEyU&+dD%&1d-R7KaZkF@NA6Q`hD* z+b5soY2qK@?sC!bW#$x~bQqx2K` z`i)oJd?g=T+i&kYpHZg1!IkW9f9A=D?UPUPbn%by!X0I=(Hs4I&UxJKcajf|4Tlfx zk<-f_XbpbuDc5TMlJ?0bm2>hemT7mKb3L8wd8L?eAGYi)3*wI)NUbvqlTRvq=qKD!_Ue0OcRt|I_gEc&U{5)<^I`ks zlk6(~5ng3naIF==2l;bqfe-HW#f$BePbz%K6Ze(9Mo$R&r@g-?hvwsKxBkxd$tT%O z{3E=|xZqlHVu+9C58~A*@KL+%@nZYrlL{a5)cgEsA%YL$l{$Q2DVRUfKKUfOi+`lt z&q7NZCw2D^V$>?|fdy~=Nc-fI$~pa{E&I{vtGe^SovD-0>Kj~Xj~Ck~pJWg5kMP1B zWv}Ga?tG9xw>W%YPdT;oVf*Bh3Lo+;Qo*B39gPd>?>;veCK`^sL8 z(?b3!^!FFU^>?;UKB@2_&oVB!mYyEsQ|RvnK5Dl;UTmLyl0^I?y!1Xl>N}%5pRB(- zd|)Y&i4D~KVR|FyT}J^f6L*czQL9G zepB}-@?rbrlk6@25ni~X?6nx+gL}va$K-LEKe(ox+T+Fc$tM*)1`z^mo(fj#Ba&WG)jPx4stkMJtvf@`D0-Ti}j^*z2le{e73WuSdtX8Yum z3Lo;sePyp?ji1jM-#E4Hc{w>OJfE7n_Ia7@lTWg*_(yn^aly65T0fsJJ>#vhOUjH-AiBJ07xq@<|>i{t;e& z7Fs&G&d=xJCnk5(KiPa*;G>-8`%T?`>mO;Kd{Q|lPi@(clJ$N*s7*e=r#ktpzQL73 z|0?iFs^T9h#~o#_g$O?2l{$Q2PdT;oVf)rU!iRn;VmS zwwOO}eC@=`f4t_3gKhtk_Q@xebNVU7gXN9e{CvJJxrdEc*53!JpC-Rbo4wwpR<4Z3(F_Vp8_A{G~aLPT0U%_e3CyE{|GN_*^kn7f0TW#W1i2~8ABPW$nLp1wxZ@veoC@Jaf_KT^*7LdV4oe*gUDKNl^ZEPot6u*Lj&?q1&;960^AmJi#v{t-U(Q-}x4 z`zHN-)_nB~_W3;liGPq}p9(hoWF zdx1}~O8g_`yf1W2w!O;hpZBl&zcyZ3{y2PKi}|zLX%`RNw{@e97u&b~5kB-&hzHA) z&-nRVT={#O&sqKy_>j}Dce`oeo&RyZ&F56wC!gdw;vXqzyP;z%kk6>k`5kz_C+PD# zeS`Ai^A6i5pH%peXMRuk|IsOb|H2*QljV=Y2iKHS`|}RlC!gfG;veCKd&^$Q`Q7;- ze>NBRz-G>=oe$e5pH%peXBihiaG=SPbHKBr%~kNrML*54gIu;k{CscXMa z!uH80IZ*r~y!@_$a6OepA=3SJ^)Kq{4?hwPio*oAvXV_~c-F z{`80Q$JEvQk@m?a`QPFnDfhF`(pG@ao6mla9iOxODezGX^Zll-j?dCQ`J{4AKk0pb zG&<+^&y&7?uFdBxe;hurgxC-?UPR`=V>IL#>IaByxr{e$NrAClU>63 zW9n-D6!;`3i+`lt?-eaw4Dk81+2fCVvhkH@gljV=Y2bSFYF?BV6qysL&r$bI zw9j7!K5AjU-_+IlOWL>ok@GZ?&q9dLbM9==ie{c`^Wb;>nkJ>DZ&(gm2kDRBGeEQPve6sn=;R7!2_+w6W{wnZE&KCbjx$5wv zRs^4H{wnZ+HRg|jmJi#v{t-TDB%jeM{QkM&CnvY(&z|A@F?BV6qvLG?ekZGk6M`TH+6OXlJ?0bmGd-`&q9C?_Niw+XZhps0T(xaOkEv+3Vf1n z;vXqj9e&h!lixqs1D<@c{3-BJPV?BmUdJD4-}*<+=_fx6EluC-=Y###$p^KQB%D8{ zuI5jHPjZj=N6NKjKkB>E&j)*#lMjxK0w1+DkA2s5{*v~sf8;!k$4w-eLRJKf;GRLp)f%5ad%Be+qnff5-eK?UPUP1@VuR>wSLIxH{w?<&))) z!v~g_KSe%lpL|l`lScAcjNp^yPl1neD$HNfKKUeH6#q!M-z!?0O#1T~`zp5O&p!VA z`R__R8pAYsdBp)0b1wOR)?sq+0Uw`|7+P?~Xk}rvW zq@4GKj>)v25B38jpDceIKCs37dC3hA4nB3m{GFUH*uM3T@S&eVJXoI2`1xS3J@U!& zr@)7tE`Qy@11tW$qMyG@`{a{+S^OjAY&UdF=KOrHj~)5o-sG|V{JG|bmHIs|b=E)9 zKKZ0_o<{OXH~RTtk2vzdu~Fbdeb@YGkNV$z{9F2bQQ(t&Mf@Y>yf1W2Hv9Qt|26W- z^2gx=Tg;z_zHoT`#uvOs^GDjZ{*iO~Da3>2=>>j1*xQVJvivFVA*av(;_n8Ic;Hmc zp8}ucKJkx~v)#}!xya84`-YJZ?oIaf=g*m|UO$-Z@BDp=v~T?*=V>IL^kP3B?8!ww zI5rA=sPD-y_?N-`YVXkTr@$xqs`y9Bd0*(5T;k`0{jSI-%O8giY%zZp_q}}Z@Bi#< z%^zvs`bW;wNIvOn{Cu#N6!~QNQ{Y2Rw|)A=fxed_U9KG?g5 ze6sv0@FA!B-}nB3g+G71=8v>bKFJTnKT^(iL&x;3q4i;5{ao|sPsjRM+9#h>&eKRf zebEbg!zm+lY{*EGj{ch27mm)^}2qR_N{;9 zJdNa&rhY!ymT7mo*^DAPv7L{gS{!pC(EA#A9DJ{ImZs%cjdf}Khi$= zBtI7aNIBaL9g{2le6a5W`QYAUwLgExZ@j#I=o>a^{z&`elgfD-$tS(a&j))dkPnWH z0w3zzwQu#{h5H<={weTDej@&na^4p@CY96L{yv58KV!cF^2zeY;R9RDpC4R$_u%s` zz22_p*}nCU@S&eVJXoHt@bkf50OXV9Pk|3P{o<>44LtUMtvbI;`{a`>ihrb>?S_uY zwW0B_Fn=BF&!3L@OWG%&RL;{#J_|uUh54(%NA(rP=K`PPr{W(e*Zcga@%E5^luwpF z4j))z{&dV=(!TYNoYPN!7FxO(!6(a~0w3j6@K1qH@-y*|lxxd=wD76+{*Jc!Vm{-1 z)cHI`eS<6QeAvGAkMKz&`J^{@=L23_96n&7oZ9*D_pZqYb@3w4{4D(c=-+ndgZya~ z_`qV$N%=fti1YWYUuUjozis`a&)<#TwnlxtEdJ4-^;?`@%KqPF*E~X=-?|^r`dcmj z^vtsdwOkE__SLDaNeAaHzF7EdvH0qzzsq8^65bbM>Y;ZJo|wE^>}<8T+W$d|W9&S% z?L$txH(LCJ{l9E+jGce_;XgR_AF;Ua6_2m0UbPhR&qLb|sH;C~7GM4JgDqAo#y^30 z;r+lqQ<1-8j(d>TQyO)K`2J>9lgjC|7a?skw* zuXuJMcD7oKeA4pnc92in?u`~BpR~NY9psbNf5c+sla_b4gM3nd)+|OoX}M#kOFr>; z=y69+`6RzD@8%PJTb>LoHu+>RFPdXnK56}}1H{NDV#f^j;>b>m;~etoh>yPdw!H2Z zi;+)Su9iY}kWc3ycb(YTYBBOj%e&h_K54r*T8w^QIInYm3EWfEd2Dm-d1kMm;Jgl<+&9A2i9I98ffzc8c@4)hom#(E zCx%Yq7(3L-eJgNJFFLs&g{u>LQIG>MbQ1F#j%7NveyvUnoy0MAsFV9);GSM|a-R!V zC-%4?2V&?X<~1D4bZY%toftZaW9(2T_u0Tb+pF_i<{s{!0iC;=f4e_;!8UW~c$xBn zjzUuq(s{`fKI7D{4)We-9CVw-EW~%>yslF6!!#!kGQ9Y>m%&v0ULM`!$-vVmHhvZHu&hFPdqGks)NMv5pj$i_(Htu z?bXMhsMf<_3cm+85y#j;U;7hNk$acnp5mJ5_{s6+NyF3Y zn}w$o_EYXh=4y1?CVn*gDKs`@zc29FUX9%2%+-kf&8UqS8i{!g$FewT{aT$EaU_nh z!#Hw}HQckk8o6(ps}Xyqv3+7_B<3|7%QR~JTAdggiDT?gBlm5?JtZ33RzS>E?#1S6 zblWC=v~!hv!S$k%`@6Xsv9}ww5kn&}ui;o0N3CD06C;kqF?JY7?k|UXwl|L4GtSlM zwoUwK$B}#1`81w5IypB!(wq*x_rrbYmb*sogBP!H!zBA7_qJQ^8o9S#yvB-M$8x_s zS0nbyV~!FdSBZHI$FlyY^=oxv^he?tJBUTiROEhrxTn}3+j6zbx}E#+xq7h|AGr`i zFEOv-Sf*F&*XqR3OB`c|dbuAU?%5H&-0#oTi@pBHg&2B?c@4)hy;{FkCx%|)7(3L< z{RVN*cI$oaMg6wV%6@{bUhE}EF2vAF%xgH7>DBtRIx+MT$Jn9Xmh5MUd$wDz^Sr|S z3SGS}4`S#gc8_Ixwcd_>#(?!L_e+G$px(AsQN@l?+yimBYvdk`@f!90Y3{4I+%tc%8*Dc7FM@dz|`-{XGBv8=howjGfw=YF+u)EWZDSev4!5aI8pVFGSp9+}QRV z96R@+@A&3E87ts_?oH|XANx|03o-mp%xgH7`=2%8|NNEvpJ#du*~$ISdhmb#%KgtX zJ%;SS|H{8+G5oLP-R;o-+^Z9JY`6c-J%ftW5 zzg8!P|B1=AOc(vny-87bNBqydM_vD8-%)ZQhX09q4ac3y|H{8sCx-utV|3B~ttA+L z@EI)kT8002G5^e(A~p@*>kaPyfyd04)k&_S646gafMyHh@qD_zmor7rdR8m?=Ol?=p~M^L%j{zD;4+b zh+gg$>*~cmvE)Jwy~MnRW0_v9U#k;CFL8_=>g8UwxM#cdI%{w4MeFLtKD6XQ486p> zhGUsttzWAXLoacR9qMiENPI=_jmA;j>lS*k&#f6Jw_%@Kav{d}Nz7|FhV+KkZv&U? zKghLQjGb+lK6CK)-;htu;%fh)7RT6mXxj^&b_Xne?~8^kjix=CixqLn#ITmEvJ<*pFAH}5BZS4@_gW#9z%Tcd|dGYIS1d z12J2{F~o=Y(2%{3QFpuZ!Q2Bo$OrEA?B>H?z3B$~Hz35w2V%6zSBNLZhqW(xx5T|Y zA6~WiM)?~@S_$h#KK$Y2JEVTiV&sFCcek?~^FjSts}mz1h*eeCKg@@8NAkg$FSysX zn-AD$n_P&I55&BNV_80Ez4m<2az!CK$cKymeT(E(&0^$(mUp+a9P>f_Su^{U=RQY1 z5UZ-NpP3J>9mxmN5bR{%!IZtlkq=z|W1n+!A;x-%nAdO&#S!a&tzWAXW4%NiV~2VZ z*}EI}6vxA3CO4I@|C<&2UNrYoclBZ)b#ftwUSeLuu}rVluhog6mpH}_^)_})uZ=JF zYIpTwpLTK~hF)S`!?8@S*00rxp_e$u4)vxxqSquY^O<|WyLzz?Jh>1|bTJNq3}sjGhyi>v+nS{!5Np>0p7Ge+jy zSr*@T_W!at#?JBYeO6ubq-ODrXTQ+mf}PSlPmav@tj0aTaXa)}WA3qe{;^ZC-(++1 z{IFv-+jT54@|?K9GdnHzM>9_i9MC*U#>3j+!$19ChLPucz3%gNoTRN#e?guf*V@JA zIWh8_m^N?>*+HI@L#49SV&u7&tEG?~zSJCf)BeMz=H$;R_- z`CdG3p1*(9|GkS$h>_>SyoO^aACTwIJp7gRxtkbyP8?$gdA`djyUF@|-eTmrmaC-@ zALO~VyVYXkxt4dggFM%ETNWeFwY<9>r+x|<8JSSF`<$3UTwKnazv*^)!`jz|K zNgl+|O3Z6GhT;LOTHpLOo7jX_;ut&Bn)a+!$JlhHGp-Lkc$Ixmn;2S&V>m*q*00rx zp_Mqs4z;#=)~Y$&H`}Ro;**26zY;?$aSTUj)%vwMF|-oL*rC>>ze|5z9safkY~a1= zmPhNG&wh`679xgL;uwz5s`YDiVrV6fu|utmCD1xL*BRF*eg9lLHW5QBaSTUj)%vwM zF|-oL*rC?6cda9gYqGIZ>&Y+O^KSAWhE`%;!?A2^()zVJF|-oL*rC=|?^^9|Tr7CB zwts(Onf(2ACI78{_@}u3JAQb4!$HCBs$)}QQ)gUVe-k6F#O|>yu3B%$*1Fm6dGxHc zV&l5FlW0wQ)~az$H}4#AZ7qS;z6(0FP9Mk5FDeyc~;pC#u zxHh)*+0PG%p_Mp>Bj#7FU#k;CD{+h+YE66As_WqekJbl%ySJTRiJ_G^h9k6U{aT$E zT8U%qP;0Aats2+H#hr0|`;{lz^)NBC631|aR;^#F6GJO;j2&uCcInbzBi6%<9<84~ ze$qbE5ko6+3`c0y`n5VSv=YbIq1Hz4TJ6}BzPdB61K&Kv_E%zPC63_;ty;fUCx%wy z7(3LO_O8`FtG6!c)Y^FPv9`YwLo0C%M`+dhW^59h&`KO*hgw@ppmp>$om!XWdU)4x zf5mz@++FoOm}EZR2WmOL_nWPUd)DgAIXj8gbP2RJF5Nle+Ui-W&aaCetyf&8zePZd zv57clOu^Ws_08V{icO47#4&a_HYK}-`|B&lM$9iPg`Z$yBm3K+*LKGB+L7JtdYBkm ziDNiItJbg8iJ_G^#tyYMmOyLY>pHc5@4$!c{7MY1#4#M9RqNO4#L!9{V~1MPCD7XP zX#K!B`uhXK&`KP`5n8o=txgQB#4&cLwY3CVM_=C=*Xx}1Ffp_e$8dyJtzWAXLo0EN z9coQ>@77<#Gc0Um-(9|}Q|nRpPT2J@F|-oLaD-N^U#k;CD{+h+YHcim)&-x|=iF)g z>rsL;txh?4mOZQU-b%%2m3uR-+hK=V(D8FcgEHAH!>kVFs`ZxO{o$Uq>e|0A z-8r7-cD{%}*Xw~|)Ix(~o z$Jn9PR?k{>98TZdsr6cCJxmO(#4#M9RqLB)I~C}aouS~b-cx_*ze{RaVYAL5prlkZuN_qzWlZpI1n3i*4f#+e@5MhuB#>@0N*TRq1xe@4ps;aF!3S8W}z z@5>QmEG3T7iuq9M*9M6(9}>sd$)6iI&)~Ts$A}?uj2*;K>*Kj0w}TiG$Jjv(m4D4* z%!gX8mO^8@oe%fwG9Ru!F!(DEe1=HR?Tq2^dq3H}FGY+P5-Y>-{DT;3{n{WgVn`fg zXE{FmDgRoX7%?P{(Zv`xmLP`7cxMbRd*EsIZ+3_gL*f{Yh@sZ64H6@U#4&c3V+@sl ztxk*>636Ib4AUiuVY;C+h9{hSu8kovVn`gr5i!*IwLxOUkT}N9a*Uz!uhoeWL*f`+ zjA3gBW0-w6;w_!;8Me<4`MhCb=*%a|GBnHNIObYePv2Y1wX$4~Av@IC=vk}2@7=iC7gyy#^k{2Mmq6>{ zPNKE7gIcro>tyGc`+|QRvg=o3jJd>&HIAX&!#8B(k z28j_v;ut%K;ouoxJTk9)wZ({`maCHp7;3p%3h`kK z)1G7Ke1Nu@fBi79Ak$uY%M_ylZ)D8Xx1*7~b-MXWvN{#E2m=ui+Tth!|@9+8{AvNE~AaF+AzY|08^^wiq$gap~2bf6ZdVP|MX)h!11fSb`WPmvqK(+pX`jYZ_w2kXRXpwIYUEzcxsW z7!t?WK@6v67G*5G+G4~|%hgiIXNaNJ-@i_b7!s?Mur9<<`PVE)47FS>h4?Ur=@P^+ zeNAT!_nTT_$5LX%kXRXpwIYUEzcxsW7!t?WK@1Nb|Dx@O#E2nrj2*;K>+fGDMhuB# z>>!59zh*IFsO4&@TMS!E5X0os_86MA%eL!&J!NA^j5Q6hG7QJ?J;%RrQ0s58_*dWg zn8h)69{j&KSPruEXu$h!G=( z#L6(7XNaNJ-(oRhsO4&@EQV*ES(W$N?jIyZ42jiBm=9v8?XI*KG1PLk6yk#zYW1)T z-?nqawbip$=liOcw~x6dSGSGc|4O@lCB`!dv75;yIqcN04f0+*gAm8q!81tn)!YaE zYK#B+RbR3=#?EmIzi{fWtkWjOT;doz7;}|>&0>tXTCSEt{^6LL926dh^1qJ{jYIkw zRho9jaO;16+pc|x5kq2S7@jxr9I5qdgT#m-af}_raORqd{hW~)F(i($gBWW4m33mo zkT}K;VyOIU79)mQu9mvRu+ei2^|RvSjh!*vt@dMCYg8)4h#|2uERUhquMH9-hQu*; z5W~YZTqZenwZ({`maC#xU(UhE6|R(HX;4U%kloLt?~`SQ&sMd1 zF(gI|iPcJ&4`Qh8uCy32)N-{H;)57!ee<_);w!|ESgnNlFovxijA8co8`t`tg_q&` zIR}U5jr?~3L(ftA9>;=LtLyO`oAsOX{)}D^#dXQw&*@pKzIWAlyDzS)h3L`Nn)a+U zU(+Fn7k3h^tsT_*Eu(eeQ@Zxq)tvA8^C#ZJSh`13zFj%|e^;LC)Zb?D;HLkw*tH+( z3q$ul-u!(QU;5sr`P)0EoY%|8xcyDF(URXSX*7pNo^jTJc)Z!vVLJyJ48Nt)uH4@B zkbH85{f#M9-c#Pc87F&5O?6HgMyO)g#G=?UPR`e8`jcg^tNBem*mU zhu%&;NT6FBKCngqyzpsXtzX@E;_c+a_Q@ytNAZvF3h`iha+{ye>CdWJK8Vp`fe-En zr{_Iw@aRLHvh_CdVf*Bh3Lo-hyP;!xUB33=b#|ubeXl)#_FL)CAAE*s>T3Q-`{a}S zQv4(3Y$tT=dxxLTniC$rmGQ!{QQ)KY=KD?E7h3;F`{a|#IsK&f`B5vt=d=4>Z1WjD z+wAaxCFT$OW9lA3K5Ut$&0M{Zz&U*IKuC_YeHErNBq+ z7V<~hC!eG){*iLM&yN=F=*|c6YB_vhiTP8^XSPp1sqjf7`J{Jt=YtqMQsASU3jUGy z$tO8l{3GSKuk4k)(?9;;9_BM}-ydoJQs3Z8J0G@BKB@4bpKwRnYaxOUc%AC-fj#Ba z&WG)jPx4~%kMJtvf@_WIyZZ)+b5q?_>iaG=SO|-?#?HRm%|5^g83uulTY$e z@sE`IS!ii1f)8TUD)4~?Z~jR8(!Te0=acpK0w3knp3iKbe3CyG{|GN_*^gQQ zK2w!zjzXOHYB>3!JZUd<9DQGR|A5zKhYxHir-FZ^eey|O zF8-175D%6of9dD*p~@MD&_BqZZx#69e)e}$_a^HfX`g&jIj5i4Zs@oW;Paoa_~~8b zgL{+xbo^1@;L3czse2Upuzm7LjuHO|FWgb~YP{d?AKXJeI3DKk!8PU79xt{}KB@2_ zPuyGfS`6@c^?!W)Ch|eNMhkpklkqZj53&A{_Q@xCh4@Fx%edfL`ho8LLA@;wAN38c z6#OIYlTRw=^b_tVd-Z*=J0JMz5r+@#DW`TmY@d9RW5qwhtBeb-wIcW+UVTs2e5Ost z%beTiWwuW~sqi6B-WxiO{*~W9XME$-w&&&K&|If)J~egi^D^5fpX504kMIidV0p6O z=kuj!y!H9aXB-;^KD;0KW9oj2`ONmoClx;AsrUKOLV(XV_kBj&c$hkTV9CuNQ`e4% zY@d9R$YJEyhx_ZQ@Lef{0kwf&v#lTRvq$g>-t z(U1E5b4BflPr31G6!@s!`F>OP`OIgwPd>?s;veCq_xVxsF+ZOluX#+{e409ZV9CuN zQ`gR?Y@d8m;X|H&7FxOx;PZvaJ#4(P{$AjtoaXyYT^ld9Pd>>>;veCqE&Ea9<9`3# z_oG)^KDaYEESx{4uI0n_$tM*)F?1yPpueXMA6Rnp$JD)(`ONmoClx;Qlb?l__TA{`bM{YvVfkeF zQ{bbV=KD=u%ZKfgPjZU*M|f$=e$)!^xq4gs=W)pk!}(+CYW_(3F8hk z{d3dlPqLqXA#RNVAGI*wZ|dsjUj;tNtHeK2uJ`#-@<~6Rzk1UR_VceSe;hurK`_?~lPCxlsXz4kk;ruamHGiai>mNB!Bl#=_`0W1LkJ|SWvivFVQFZhErmlTIAuaGpP80t~x!&hT z=}mtB{N_IwEuSoZ96qq*=8vgs`LKQKAK^oueimBV`jnr~ny-GrKEG$BX|e--#7XNZ5KT<`Ovg#e%Z7Z13*ZT@ojz>=Fkrf$ETzodQZA33L= z{4BJzaf{zSH(bBP=5v-m1wP7YzTecf`J5K`Bxj0$q+DC}qs0Ip)TV!c7oGLI`UY1D z&ll3Z^^csBC+;YFrMLS1gL}va$Nm1e{rv>x)Xpa@@JY@R|44Zm7hLPRtver#KaBz( z+?#W1=fn1`e}oTt;=Zz1E5PUFt7qE#`w`*(ZtB|po=W@VlbkL7k@7MwxHfvb-#-U@ z=X#sZh*zV)NA1q{o4Pii**^KC!iPNdK0iwC@bfwS%6;x+{$%ro!v~h!{4sTpWPfM- zcqb?tb__Q@v|KIEw_`%&Xgzken^IoO^* zM~3sq)Ybfv_Q@wXNBkq@eimA~7~u2fv)^Oq(=2}qeAL2xzo~2IQ?^e&sqmqn^gcgI z@ACWSN#8%$=5v-m4j))@^T*V+`ONmoCm9z12roYiE$zG8&*$Wq?rG<*EPo1ol+%2_ zsjKssv`;>%oRg=v>_@EtAIt;nU$`?lDx5#2uI5jHPqIe*BjtV;S~|MT?;q44pDce0 zeAGf=e3tgDf8?Bg();`i_kUCo~YpJYV*BjtV;TDlnEv)j#kwLdQx z_^5^XepA;zFSC8?AK{Zm@<~7A_fKO>U;Fd2!v|d4{4sUy^KvTflTXqV|46y&@T0za z{d^wy?cVL;oh2EKVn zd;U0lV9E85sjK-T?UPTkUi>5FeimA~5a82z@UiXrQ{bbV=KD=u%^zu>d{Q}2Bl$Ew z@Apr~{Pq2C{ii|`TmQ&;8p&rNz~_4h zKHNV3IDEjx%^y=&$Dab9);+o4PvwNc+}5a-K%= zSq$*G?#jaa^}}%fn7W!j1wP5R_(#h9UeVI@D}MhRb?-#`{8iwi7Uug+U7f$Aed`}N zPb2yC-522VoIBg|$Kj)~0y2D_bEoD{flsnQ{3GS6!;e}4J`a7iFn<;Jz#7LNQ&;mx z+PD6Z^E8sr=vV#z>6pKM6waTv`K!PunGpX-x!)^Vx-kEtzjgoK5_1DzZO>l?KCA=Y z2HW|9?OXo{pEQ!s;-%gBWb>E92VC6o$K{hs`{a{MihraW_m;i-Ue}#ZHh&fPz$Wv@ zK=qHbPd=%flV=$hTx$jR-0+i=+t<$zhx5nO)$ymmCz%reNO?Ctqp$b-XVZy$x6fY% zK5BQq-_+IlOWL>ok#qV<@AISNGC!YzR~^tk{y2PK$;}^ASI3_MpX7Y;kCgjaXz4U`?`{&x?{Pp8-{+PO&KLtL?wD?EL)f0ZS z7~peH@%gL3N3G5Go4PuGN&D76a-K%=NiX;NXRi4Cok@GZ?PbjAs-wU96q?FoZ9)Yed{0LL!P*|?3KK+J0Hwn$y3@t|AI}%%RoC{q|!e5 zBy-{)DaUjRGIF+a52rPd>>; z@sIG*`}}Azf={;IaQMJdFn^?d@=4{KJpC-RwC~F9{>j!G1wP8D;GY7YWRv(u%C%)b zY6bYL+B(p_-dGIhkEyHqBkf!N$T|JwXQ8E|SNZ*O{N7Kt^J$hp1wLwFzTecf^J!Y( zlWZ3MNV(qUN6B0Kd@g(7Y4-E4EPot6u;k{CsjHuVN&D76a!#Ip7FxOx;B&&s=UP5l z{uKBqr}=(U*YZgVe3A>qKT@tO`%&X+zke_faD2v{$xp-i(>DG{`_?~lPM&@iTDlnI z(>`A`3VhT;p??+lBo~T*q+IXwqx7vI|0tggVs$KKUfC7XL`Ow(LiZxBLBrJ%-5#cP2j%=Z~qY z`6KObKB=6ik$gtC`2B;uAjt>EMuCs2o9{Pub^Iyt zN$$^e&`)}wA0_Yf^TED`1Uy(ivd2^ zJB@sBY!vvYh53F{*Y@l#XrJJ@AISdU4H*yA29OC^2gx=OK$#{y88a1v`;>% zoRg=Yg_icc+s_AkT9HqdKLtL@X};go)$ymmC;2<^kCbc6e$)!^=~zGiGMqnc>t|`- z`bW;`CqD}<9lgQtAJm|KvivFVQ459jVS!Kb_u?NZ*Zce^c~5sfS^hYDV9CuNbE@lS zY2W%s&eKRf3lV&>{3-BJP6huI_$1#D|42FRD|BYT zKhi$=B;OYQNV&G`M~(OU{nIgj{c|{f+U755pL|j|r=R>Rv~)4Zr!apN_^5@#_*~$V z{2%d;l_@FT^541URreRBna{X(nDcpx`UY3p`LKQKAK{Yd#ob#1$_kZQf$Mhe3@aqO}KELapgId38@xUd|wHWpI3fURD z_xQoTcW%2zt zeAD7KJGT=dVjOMmFJ`zwp9{r_rlo1FlkhqgVr?)cf_tDpW%i(~BI{hU5ik-uY( zdyv;tlQYc&*Gx_{XU=$r>~SzXIh*ZqFm(Rh@N9E>0^1!_aDSFh^84g&KH;~?VGl2h zO+HziU&{UusKvCK54lc3fVzEt=W8n><>9*G4e^v9XrAJy>0NZ)m>zNp{*7p zpS0Yu6Nn%3N!x8%jC|5^$4-#X51n>@Wij$;d-x!q)Sp$0kxyFg@CoQ*KJj23<`aHfo(wED`D8IKnqw#rkWX5_YBBOj%hgcG4)W=i%a1-HuQ_Eg@=41bJHhz9 zZSbMb{j=!WYBBOj%N;ubzayWt-Im43CoOmE1o`~XY4=waBcHa15AsR`?^58&n#_68sWi%mXR%!}q2$^+z+)~{NOeA03?6taVSdgD!Bdr@9< z%3|b`mOFNW@q63gzkcvin@_~ZCt|e{)`fi1c3T!BpR`;ph3DTNI_>_-V&v2I@IgMQ zKdTlapS0ZJ(%4bx)7cis*kQln{t~#SsPow7+VjlbMZtL;I=OFzs}p-hkOMJv67w34WjeKf zwN4D3#4&cLllxZSo?divKMGeT_M#vMV(291H5|)yYW-@R7&?h#>`*87!@xbg=;S^Z zu1@T6K@P;wNz7|Fmg&^`)jBbB635t~PVTdTd$w06_TS?E8PK__`G>td&7tFkeIYvv zO+iQ}_J!2?TP*(ib>FsFuZQg%IQCyo{c4@}jD2aqVwT|;;sbx}Z?1D65Y$~^{<-V- z`sBz3zCFsgr-$n!?B@X+coD-##QBx{{}3nmsQQE-h@CAK!$(@KmO^&mBW<@@Cx(xR z)k=3Cac>gb6YC@HGvfNlZIk$SC6BmY%J%q(d!MxXD0}}y+>_(nM{aq3ITZiAudzP* zT8{I+#&Sj=9RIwpu|E1*j`P08^6qvxHgOLZ+!4s5;GBxFiF>oO`%Bv=`d!E=ytlwT zW}vGVe{t`a-xq&z51d~8#l3BQU;M>AdV29!w*Ro~bpwBKEe~787uH0@GMtCTFRbOY zzRvMj6KT1kkR6OWRa1?7|KN^dKYHopf>HR?fTq#E5~_$y5(r8TYj>>&Wm||vOKp@W(WECTvLI29ieV8KYcUf$aoVr zvgc~q$EaN^EVjQM&Ate&+(!wlwo9w2Pp#Y|X}N1n56Qnvxg%P+|I%{T%Ke{qL@W1p zTFzRlMj-c=3Tnl6_I77$&0FpjwcPb_ud40U$GxYPyFTuHwY~bdmsPtycf8N>qduQJ zzstCT{PfKy#vvSIxldNRR(P!a^{CG$OQ4l|a4mPOX|GzF8dvV`wcNF~de!QziMV%| zs}=i(F@uSb!^FIXW9Yd8&nH^HS|`T(k2uB-`r4nEirl*l_Y~Je$4`zoPa2+H-z+?( zu%B{2GFPM9Hu0m`Poa_fp@Gl#YUCbgu14%{Ms38!)SL?)xBXNu!#*urh z;hyc)$bHjXjo34d?Gr;IF|XlRrcvux>%`DV9Ak$Xxo;cpDbd)r0%ERmFE&@B+cxo| zovYjnt{08m-_6yCz1^sd7#fLr4ac%LYW-@R7;z+yvBNlWe>vQaB8ajr(UZQ@5e zj@+}(r}4zm$+_{7=5*-2AMQi9+%<9^ym*ZpCfOgkx7~8r$i4OAHCF68miy(o8nIU% zbCejlO3Z6Gmi0%iU#%0PKN82-K`d&fBKPaVJ;nammaARX?c9&g)r-CO$b}esiFpmj zGQC>AS|^5H;ut&B%l!ax&yMKjet)iB?Da=3#L!F3YdDta)%w*sG4vA0*r8tTH;8+- zTkmr(>Sv$jeuA!E>?KGp#L!F3YdDta)%w*sG4vA0*rDFmq2XuHuKDaduW-LYSFg*1 z7)H(bb538et1BV&p1uekK3EELXLDwN8w_NgQK`eKUDMMBfa(N96QP?gi=U#XgYa zLJYmcyoO_$UaeoP6GJa?j2-IbUX-|}v~PBsTV-!YS1UGkZGak_f34=1@0LoYF} z;aH|u>znVNiB0Gwj>X8x7EP+h&)2bElip_iD~a4gfS^{aJa=p~M^ zL%rOK758knUULuZWZyTEy-i)c*k_eoh@qF5*KjP;tM#jOV(2A~u|vJ-3nRv>i1>0Z zS646gaU~aG=q2Vg9Lw}-{c4>UdWmD~P;YBT^qPhkU+xv_>cu{>^LJYmc zyoO_$UafEb21sl|FL8_=>TT_aUegfua<5)jFZSsr7h>oo<~1D4^lJTTofvwFW9(3G z@}h|T8ycTYL)6Q?fL*=V2bf%lp_iD~a4gfS^{aJa=p~M^L%oe1(fi+**zt;c4ZC`= z&oH?VLoYF};aH|u>sRZ<&`TU+hkDZ;(fiTT_aUegfsnR_L>dR-pG&`azd%k*l!9eb~}Y7B9&X4ovQCr=uk+pwnap&s>d z@8#vLk9$9FuRiYOyxjG1FX`>o$GxFleb^TocJLxbeiG+b^8d^7Q|njj#ORO2F?Nt= z+4s4R2+uE?pT0L|M#h<|+;`g5ian>vgBV(gc@4)hty;fYCx%wy7(3L;eXns(xG(y& zHto2>J*{1>*w30gh@q93*KjP;s`bs^zKcz0C62K}t!dA3)iIX)WxHCjS2lSNLn|?_ z;aH|s>sRZ<&`KO*hgw@bYt*uZ-e+~?cXiaox`gBV(gc@4)hty;fYCx%wy7(3M3SOTrwL)_Jh{lm$F7+Q&W z4aYLATEAK+hF0PjJJg!?u62ZQ<^JTZR_skq9>mZ}%xgH7Y1R7GIx(~o$Jn9PR_|Kv z-w<){b5|?><~QfEOupZKRJgw$KRmwSpkQ~^v5EVtw`+Aho@2zdv$Q;}T7SpZy4l}L z^{lmG`9{C00UzY;?$ zaSTUj)%w*sF|-oL*rC=|&ssIEEsxf>n|&q8gBV(gc@4+19M<~PIx(~o$Jn9PME0u3 zJ;i6btiMLAheto#nZutpdsdPMF|-o%8jfXJwSKiu46Vd5cBr+{yH-0kC7MKS{FQ8 zzjxrnc77#>R^k|r(5m&Tbz*2Gj(5S`9_fFXL zFfp_e$8dyJtzWGZLo0EN9cpbXf!4nJJX)V~r|qvt3C^@S<>XnmM&!MfiqR_fW?Hwy z4z;FBpta@E`p{=}JxmO(#MFsnnO3cDKF<-G&`KO*hgw@ppmp@CopD{3>){`T`zzMN z2S>c$v@oCV^WwfQ;rg2xaV2(-WpUMdORyg9S*x!77cbp8w5C04)iq+@>vj&UttHUf z@@T!`CnwwYn~0Ia#G0GsW0Tf5-(M1&7@LS=>~L&K9uD`{6UHXihgZWI*U{H^#&y$) zd)xIeF|-oLaK!wo^{aJaXeExZL#>UTwNBXQ*W|KJtpl$*z|OD4&`KP`5n8prdA~_) zLMw5M9coQ`)~e6y3m&b9T)&Tfzlj)HiDNiItJbg9iJ_G^#tyZ%de*AraN`Y~alO`A z4--QxaSTUj)%xa{PHaLeaf}^mO@3V7U&E_m?XQa-t@k+Z4--QxaSTUj)%w*sF|-oL z*rC=&&ssHy)5|;KI_JDUObo5WF&v>)>sRZ<&`KO*hg#E~wQ5}Z(oU`J`?tis#niU-HJCLti3$6T<7;6TbzozGe7(l|Kp3Cz_wW?~v-a(|FVJ zk7f3Ko}RVpe6qNcXib+uYu}YSM_gMyYt94B~4F2_$om;NzjO+1xKiRJ9iJ_G^h9lMqTEAK+hF0PjJJi}(0`-gE1X>q7T2DCnT&tBBT8U#gLaWxV)`_8& zIK~dOww6F^r?jlH~Y6Y#L!9{!x37wezi^vt;8{Q zsI}FzRvm{MZ|{uj_fMLz{goJ6iDNiItJbg9iJ_G^#tyY6Kkw3CBi6%<9<9Ir!7F7y z;!1@WT8U#gLaWxV)`_8&IK~dOHhR};zb~6!*BRIE&Oh7!4J0wN631|aR;^#H6GJO; zj2&uCd)I0|tLb}3r`Bt4yVdqrVrV6f;Rvl--@K635t~*5nuA{>ty)g=SdT$le>>(izuVKJaWizY;?$aSTUj)%w*s zF|-oL*rC?O5@=1{*{OBgt?#pA6EU>wZ%!Yz`Ac zD{%}*Xw~}FIx(~o$Jn9P))HuKT;Cbj>wZ0DwGu-saSTUj)%xc9cVZJ-iDT?gYx0lX z`fGTCg$?xAMUU1W-t$a5=MY0HaSTUj)%w*sF|-oL*rC?O5@=1|)fv~9+;zD9E(9^O z631|aR;^#H6GJO;j2&uCmq2UZyF0aB^`Vd0`IQ)2iDNiItJbg9iJ_G^#tyZ%mOyLE zqjg!n-}K9Hf6f0MB{aM0bI#}uopE*jO^mn_yT`I;b*;Aq?>F_VRo`1n-m`OPO?%d= z-(6bRNwl_l)~a!B{6%M6NAG{7onMKO!^E1KUV-sR3AFaTw^Qp$AAX$8VPa?{j^PNcTEAK+hF0PjJJj0hS*zx7 z%cHe_oBqBSF|-oLaD-N^U#$~ED{+h+YE6Ds-e1G3VI7-B-`5$}N6tFfuA7LVl{khY zv}%3xTf1TtT8U%qP-~-St=eCczwFeydbV!Y{>0Eq9K#V>wSKiu46Vd5cBnP&S*yl% z!J~E6S1+=0C5Bew7>>}Y_04w(#3r;7$Jn9P))HuKyuUN9%W^&Z&*A=>|2;}*e$}`x zdbPS9&oScKSz5l9(E3ZT9`0GI_Sf_SzPKt&qDNb6+Ot-jbNW8Gb7*btpw{Y&N~Lwj zksrtRQ1LfayP1FYHgDg?Sh`13KH#1cho0%4_KH7na(>FT-&va{{dH#zmU zSUkAt0gGeo960u0PJQiDV_zDmyY~}^_+YHu-&8uRIF4_{DeI~y8rPEIYpW`}T+1C-1*r|b;5Mjg^cXNv<0oUSF9Q-q1Yb zP(yWQcw=*&55Kvv&&)JubD!nj zJ@l-KF+R3-a$?9-GIoZ?*UDjN&S;n%nwuDz9GaS&8D;VCIul8!c&0gnm=Dd(p&3`@ zyq$h2+bUQk&zIX$)>4Xv8GU}$W@NHa0XsNwR^rsnW@8mkI@u?kd&paplX z8yjzCLC*vnR9kI)gfhfqXn4)c8F|sFsbLdwB-ilT^O_^5kvp|^hS6-2V#*LefDK2` z&nlQ;yQe~Xh~~vKi^oQ?)H&l&V{jAlZ500@b#YufIy|x71PtLcLN`oh8Hq?`VExqG ztV#a4>1I}W@{EH`zGmHI21&l&B$v5sdUj~tG*Y%XF(PQ~2wK$JU-V zVIshrhc>R6tgKoyJiB((xP8KSH^*Xn6%wnG`6|oL3}sz8L(|}#U*0&@+>~|0?82Ji z^O|}#vo5dGl;fJU`T6i#j0ls{8-_;aOvgHZZg{q`X3eDGIn!Lf!Gy!m%+IHdPfSR) zU}|y(G1dYx)K)~JP-}Wq#iZ@b?DVi1TTBk0m-91!Ct-Y?;oJM%nn?Vua z#Cp@H0UIVqOyef~;7?NzRq0+i9T}UMa;|2lv%_QKPFd!$v5AeYfl;&CLTi~d)W^yA z*oLuLGc_n4-V8i5JGO2eQWGa*=b1n?O;Q?UC~j(wtsk8=*rcf7>B;f&gADy~GcT;2 zo8H)b*~F;?r{&Up?sD!E}I-T!%=0`*m>E2XO3o28{SYE z-Z)d4m@{5C7tJy2z9slflzZ1Tnu&VR~d`rirP~cqNEXHA$(ac=v#cFnJK7vVKNvUb zAT)Ro=809{yJ~D|(iv)wrpxlo$b4lI1gjIX9%*i#8pc?S1q{r<%Gks@vpm97H)C8> zY2v>%W{@8->#6K$a?p$-lg)MO#@3Ey!(^5}==P?sjbAV{IXz-xZDKY)HallzOqpCW z!=+idjhP~IDKE%V3~Q@7=3Az{vhJYDy46@XnS7cxi@Fgk!Pa1?HdZiP^r5H(S zav;ATC#&)$B~}8aTY(*Vbv9XL1S&#cq&a2AM`LZw2+9{`NXnt?mQ`jJ&$^Gyo@R8) zhC}!j!&g>6XO%h7#C>cei?o>!*EeTp&N%EWr2UYbX4#spD5uAUjXP&5CI)7H!K{A9 z>gOYhC>UBhiH@AXnK9$ZrkVQJW^?h#wCQ!z!WWsoYEAP(6OnaOvrPK)niu5Roi7^9 z%#Ach`V7r%FiW^m^E(ZByiNbkf+x$1Y`V_XAmfH6Fgva@xjAWqh5~UJ1_{$2k+qd| z(*LruYypw41+WSLp}e;lfb;3*RMSke`IM7oQx?UXVFvSTHcOIKGp4HP%0tG}=2zA- zzN;}iqJ0!=MZ*q=#z-B6J*;F$l zG=eAtuQ4*DzQCE|51eY3Ts1s0YlcBH&1d5ZCZG!EIZkoe5(bm$!DnIo9-A0#PLIvz z)4cQ_6II?dY%uQx)>#;5Sp#OEnXSmgx7jSuO>em{KTMl-KMYj>V5MRPB$J$W0hO&$bp8BdIV&Fb1v$}Nr?CZ@;A zK$X=W&DURRh9}O$z%@07mD%w6^)hUN)ST&m7nm%>1HtgvG#>tkav9m#@aDpqxh1b> zu9)Zb44dcOnMwFAo2?BkGc%q$XCA$BL)p1mjTBE|%3g!5?8IlxtTES`$$t_{j7+Z? z3C1?yNje`^F}x3to4F}VP4R#keeuLN&iH0)J2SB46Iph4?$|6AR~a@QFv+2??Kb0| zk;x5MOqeKSb)=--UOq>RZ!`-qEF{fy=E&GatVc2)d3VSL*X%rhe#hb^JDnXh)<-AJ zb4Xs>jNztBOwHt(fXJ~G=oKiNwB$%LpjsRn(3@3 zD83#w?>*7&=FJj$D@aQ6nI-c@J_mq- z>9OYN%gjAvo3Z#cPjY6#YaUW3Fa&KJn>BA$ND#$nR)4;6%@lYnWWHx|mO%%qP= zr}b+vp^Xktk7NVpy79?PX2QmMpqsNJMk6!oXRCVgnOXlpG??O%W6DhR=FPM0cAVvp zrsf^RCI)xB-zANVnCC{r$c*ylvCKerQRxciiNpjU`=5(N`Z*g{p-u~OeOdJWA7k$h zW!Z71>762gX>>Pd1W7$DXw;IR(MUq4#z0kp-Jpkh0vMuzn#w`}JzQGJy!mEj=FQGm zW!93>CQX_&xt1K5ZPKLKCQX_&X|~zseE#X~h3#qI3o-PpvArsSVR&Bo1<(a?5j-cP3$i9QVTo9^++hkPn745(jRG{` z5ZzflU3eONX+SiK>xDuMB-F@P&#PwI;O!64Ia=LU)4R6VTQ5P|6lCjETm+bLXqHH# zsM!R#N2K*A)gtit8#SzLS-|ihhcw7SNGI&t+T0OWg`RAECo+{}Fr*`K2dE!fqbB@w zSlEg5>-f9`xI+!qt~j9SlU4;?1C&tX!dhg~R=A~;sE_tuj!Hq-lWrIuBTQ}kh-u6H z%Nn}QtJXWAStx?1&jn&8f$1`bl((dSE$S?)ID`i#)~%+9F=dX@Ap53PT7L%ZDjJwc zzM3uwGYPFRL6FI0vE01uSlH;!w38=i$Dmx>Z?i71Ou)PbtAOqJbS*0gJH_nz$!M3B z03yPYIcB-9)iOU2k2~vwR3tMVynw=#7eE4tSW zHmkkTs0@t8DU;k7f=**P9Hsnl4s(tQzKa>op-g!#dH#fX*s|173U_@&WN8UPKmG87 zAES`CvKcNH8YJk)=`PBDiF-)VynG9BLRuqG_Z1t_+B(YK0jrFEHPqHCS}zgZTWR2G zB_L{HO_3BLDzLG&31_r#VePi|HWkz3ZEIy)Vl9d=OEAR&x6p<c zlAw~m20cZZWosJP_2KJR(7&SdbGm7quxys5lJ1%C<~9m|fh zwv9F5Xz1DA@Cfl9-?NK-#z0cTD^Qyia4dY5x*Y@-kX>V4kjh%A)-vwbs8~?-Y*iB& z_J|`H{7@PrKT8PGXS1#NSGprOLzF1TNY3F-ES*HJM+xg$+=>&Z(|7l_Y0*EI!{0q# z+gxsI3{ZV8w;8Shi$QNmN>scZjkS^6E3 z6AB{Ai^4&VOYWlutPR<5PIl=&ytj<+m4qc=^+6U&@-+5Y2+k|kN*~Y&QsYuk+F-P7 z?CQC7dfsGAwZugIt@PD3%Do5YOQfW=jV(mxAcy#fr(4I5ZJG?f%d4!}VlXov;1)|t z28`^e^)}C1r9H=$u1Rb;e<2lc=CgaYI_^136h34Jn7vQb-=+O zUl+V#kI>ctz3kt&Kqew9?C9RTw_zY!7*k6}b_0&rbJn_Wf(s@k)EgJf`u*TQ9{sCb8yfnFMGXN34{Tz6KP+lFY$q{flyK$)}9<% zHO(*s;ml=57Y7EnAXC>`_Q{Juq!OBXDK5ZFfJoh`EE7VxB&4my2nl6ZN+w8>D=Cxh zOgx*PNLt$Ksy_kLW3XPeG;GbA`5eeLa%^(X-vcpHY&sh+MPpy>!XqlNc*Wb4h}@~v zsl@&ZtvZR+ni83}*3dDsA<3fR?C~Ux%R`4RCh4kw!Iux0`m?d*Q}wmWm-Rx_n5f+I za=R>?46ji7ZA)`5(^Si33v0PcxaD3{>P+l8NKj&Mz1#I{6*CR=fbY}mcfEDFHIdQV z!%9!%m(4h}4qAZ%c{BWX{z zMyT=m-bVbKqK?K)vV3(IjVa_j2~0PV4r*Z}Gv`XS(3DFJD(!Ua1_)EIh)BC|B9#Kg zFeS{m85oA+y$i7&Vzc!Y)&J54#})Da=+uufx9LaLw_ze$$O3Ge0OM*;CTDPC+27KO z^gDl6Ze8j-H|H=J5%hc=yZZ=N6S}mhrtsooNS30wot*v3}>TMD~ZENm~HK_ zFpCJ6@hCYvaxM1Z@T%6~ERWHf#ag;|{ta69^S{%d|APJF8~*(r|Ne{K{<$PeZNv;~ zPcsw8>yKZvRh*AJo|#>ETX0M3R)~iTfI_-4pZPA#G&g8T^RqlH^nB()&(Z<8#S9np5u(GN z*ZBipNER2N6)G1y^;`-N395dmC04x>l=BQ!T_d28z?GefOdwVWIRsXEv-S?dVRjKt zpaJII#U_XSwOrIT8B2!cwidw%M3sD0>)n!JDNbdxjujGab6r+KDKh1uuzP#h#RhN} z(#9%5l_mbyz8?rEdvwdz>JrPW7+saqNX}DL>vo3KYuMuZO3-VK!+4#7+E6o@7ES~foyfjKh;k!RSZW5OF}2) z+ZM5I?Ulw@j-~w3$ZjuT(ze$R_VzD>nCFJ9_=nN=?_T{bNSrP-9E;_JK2bkvE9Tg< z{PJB;z1#Xee(%)pZ3sd(x`Ow-@a&Grnm>{;#QrH?*V-RNWe0T?&KhRJ^dvgL(pud8 zDXt*p&KSOI_qIrwxAQwKCC@-(gI1M=zGgai2Frw8_Zn>Q_HBO_3CDB_`K>w>iXwF- zNbFA8)dZb{e0>7-bQv!r8trT?Ap*F%uAeem3+xdmYo$kt7U<;iAQj?=DE<@!BHJ8& ziVCkM)=<97>ow2U&)P8)TrHxDgiouCxm>&8t{*NBFEJ3E$egU9FkeyhrPNDW|JpQu z1S`Zehj)dNCjrDC#!k+1z-I=@9qD9X=Yf^xLH=(0FV19QuKgFvc6g3bdnIPNNCEL_ z&2$}|`qyf2an8)Kd|Smgv6#gWMO3uvz!b8IBI>Q3?XmlTAH}6xacv=R$P-zo=2u}m zqU|0Z5UE>nwzQgQ%wOEKSDPr#W1OZtD1L0W&-+v~DlS9MBfGfgECE>-5Pl)uvj=CO z65;C|`23*2l~tI8%--EErIZe3O$fabS8B0Sl--#$zGqS72_q|%q#NZ64eKHjTstpey-$9W70RxiduG9! z=2@)mSeiNpfTs0x$%lXguf)pwqj|4rsRWO6HWBq$$9Te{ft{rO0xmI6w$%3C7OF-A z8=|6-o#$bN^iVH2o}(ZwQVKg)%Z&E#4S6biAVZ+mcaoXgb{K?WBZLaJG&-mp(cSe38M;GWr$BgU>EF-}Yo_ko4YNJlts}=3cYnXUB&cdixh-(xG%OAp*h$RJN_9JPX@{Kvcu&bqsQ8cc$qA$VeKNxYyD#+!4$2288&&=T5+*xcQqTG7>jZ( z59S0L0eyj--0Lt^@;aFSuu#naIGSyuOP8M`d%o3jxG!}p;0WvFe%sub4JK?N!q;pT zjDwq=SZzz{Qr|xmdwDK9I*vS-YjK@X!H+_8Vk2#;)3KtxAjhoa=tCHkfulRSjPl`z zcHT1%V4)qDf)Mu`sQ0i$GS|yiwsk0DWuEL1(cRSX$UBM2OGa|o2=)1I#Mt&`y%F_X z7=n|-%?>$TKhv6l@gJ^%!JV7HvbL|^SogCm!yj)w%+QFe< zmgf`-OLh7tol9YJnY{Z5*HKQ&K?s^*c~W?G1c%=Fis@sGSj^gDt5`_>lJ7o?aA-6# zTEv#~a&H;&A8c)!DS-L7~ zLZBPK4L{(+e8j;lH~rEZs<*R}2t5rXkGwo*{491nkVt9a-2-XwlEpv_N1!1-Gf*5H zm&cc-)JR2Rc#?ZQqN?Hhv4+o-%XB|U)?t$!tapJ;(t){+ZT5xcIP0300fbJb8EU2= zjf!v%2P-(u*~T@TIG^JS;_6|RCr9jdP+Am4#azKIEmjSCK^CJ*EzPq-LKW7GYgjcK z)a2tDG`q7U43Q}M1<`gdimW9gI6Lj~6%0r99c}OJC}`r+EWNIk}>94 zHW(cysj|3Lu5U1e=wC(tP&|Mo{!;xZ9wHi@rt9k6p`L6fdEEpmt07Dp5@Zxj<~TaL zlA%Lqe|Wwr50BA2Cvn{R@>Xbk%hU)lZr_$VK%8Lr&M-I!8X}z=nM_4X@*LNqUR4OH zo!YN%SPx6tP}ogI@!$ovQd=n^u^c@RM2>O5ltq|L(HVvla;1|wPQ@zkWVTW#H(3Z{)U&(lVxwtRR|$GYquA9K!{6`m|DtLnM9k!RW>H>eaW?irGJB&Hh@;5x z6Q`GG>_GNfW{6C@z`pOuZ5wh?gI8ML-;sfC_W+@VGZ&qvZ@xTUYevd)+lyJjP0LEL z64M85as~<9n~V8SCp*(lB%Gd<&jgA}9e*hjpP!Mvs(g2Vu6pQeIG1NmtOh!n==tQ< z!|37FQ0gVEBl#O+C~Sv#ZAIw0R&fM`cxTP*o}6oT{DGHQe0Eobh0r@-#ih!MaTcA? z|eLp&R^SgN^M2eRaBT-TK-& zXC~_S{BqhMr6&w`&Oevr99;9uw4{V_W2Q!4-cgWC3}1S> zG+bRZ_YM3;> z79c&TO;=H~FwTI<#`~r!OsTZMa?d#b>-YaUHt-y#<9G^cf0l<@2DdBM2eScM_hnmO z9*YN;KW~kYJ$rRb8R85VRp;)KOBF417Y5;Q;b}zQnr&gh_?%hKoGK9yU-ZC9Q3UT_ z;}R`O5H=}FKW#N8n;cI-QwtT6aqq--uj)-kZ-*6)r%1$<9N%Ykbwv6*DkkI5=CHXE~ zskIFROOi~!AkIefT%UogVD-*B^J=ILjKmlhkKN@NZj4BSKIpD5715S`rzqtpcX>k4 zRdTA-y)SM8X6Mw(-@+fW@yaE0G%O=vpQ)np9-z~ zW=#hzovg;Sf-QRq60mu^($g;fPiQN~XN~2l<9e)R*)h>W2~*&Z=gf~! z*u*?qq%ErTwq5L4W$eFej)AZf)?Pc2P_D8n3fsWRAiu#HGU%x+LPXXPl~skdwf6TwMx{<&Sm74J zq)D=D-vsf(4$n)FM@bM`3IJxDq1#&CMG%PUX#oLx=`O8r9&gBP3?Ba&fe19!&v}te zXc$Lg2Ev4yY@uvHRqNR28TT}M^0qlfJkkMKH3(iGl*!B!?N*L^E9kz31u^>>UB(H7>1K1sNo>Lli(c=?uEtov`JisS6tzzE z{jD9H22ZW=u@g?QiQd_ElZ_uuoOU7){rh~;)|P~qtjOsLE4Wv%#rQTU?T9RlWRkTqjJ_`Uzj`AO$+8JoZZ6HWKRTo`_TE#OSbbe0ii}bK;d z4wuonEngO5GATTbz7T#nX|{_>L3lS=L->C16rm$}3XM93*Elt4ER|tvhf}gf&{!gn zC=kC{usBfExm2IWbB}roLz~N4Hm@sbj|(iyL|yVxeS|e3h6ILP;zpR8A^uA#!K~9f zgd!wl-`|KwLfy0>C57|=76=-k8-f}@T8myi$`uKoyssXtOip)JCJrEsRVt2hI=U%b z@!@St%cmqR9UF!*>S+_5<0d{uLMd?dkl?(t2E-voH#W zg+KmfY;FK?Z_R?WETG`}H8MCq6 zN0K6?dNjuphjdeCKhzMnuWv)%^BVbS1!QTsbUpVYTkEBG?KXC+xjmf|Jv@w3&aa#; zq#WUhgeF6Y15R`~p;_hY*_gKd>v7IuGFNtVI-ceiiI9YztYpZJt0n0TJ03Q+p?XWK z6q}hpz_3aIlLAOvWz^PMNt2u`jZMj^op_p~EbCS@mT7=QK`9_E5K_CZ%V($9zvad$ z83|fB8;NrzyotQd8-u8m_Ep)(?->wVu$!H*a-}(|Tx+C4#j*FvCJSTZGlpw8!|JRd zt0KQT+R-#3?0Vpkm*hl`+mL$#3d<_#R>w4ue)&FYURrlGfYo!2P$i>?38hJOX(w;j zqbMm`Qs6ryxFrR?JAz3{4izV&)sMHtNKC*&)5^TSO{Y{5s=E(`O1K^DBzu#(lgj2{-s(4jR^-@5eGC%#DG&>OLq{x1l`ENX%|S0 z@ereWZ@<~Wve8?^Cf_A(6KN$fxc5C4En1NZS()fHijoA=e|9eQ0gp9bQcG+K?}l(x z9b3T>0vmpXwb)s#Yd^DSOmqonlaKs!mCIphQBsWKs5P+1Y!(i>D(* zoHI=cdX&_n+L{snn{S~H*>TG>T^?B4sTkI4eS#Dr=GR^cr zHLYns@x_?2(L!tZtA)6=v4B!sLexxi1d#U=rXV1wFwS;%ZCJr7%Z|H;O`*;;^6P1A z!n4NT>Hey7)<+#lilPNHaA+M#X@S_27rQS2VOoT_ETd7lhqB9ersS&9;^f3ZGEtG% z?uouv?q6=-4I|DmFo_|9JXx9xbro3%By$Al{9qwI(&1ow4a?6;m(Pp+vsZi;rbO7- zHW{IK%ayEIx$epBqk&*JYhiJYp;c}OF!5HczC!paM~6)-j&#yZ$0g8kaaM9|Lvz!U zI(P+LWH+dY82D@~5BDKuQ9H`zYfzIi=o?k08B}|eOWacHR@*iq#G9U0aUG(TRZ@P8 z99O!$0xIK7eY#|u_jTT>SBtb$?Sr0Z!0EI@kgeE2tkFca<42G2qmz@Gbwl|zm|w%s z-L(WfmRAd2r`e(Xm;wqp0ft4w!$PAVqz6&{tJz8kanv0n1)PPdX^M=y(OW}^@S&*K zmEw(wu&st5wych6UFZ+mfQzHokR3<+yTi2UOpAPLqZ-JRT1&v06sAYWn^opy zNJ9d?tCr_BehK&iH%pk3;*h*sHvew|Es9K2W*>{p@N+AYwZ57F{&_N}(}N-kJ!}&UC9YIpoQD~(dzvu|W85e^Lfs7M?&xAsg84CHv*AbV*DXFe92vRY7 zEW*u_Y;Fq0jdMB!^N6gWOXOHvN*r$I%Y}Uz#OjFElirhM&bhCo%~7>|1=jl5bVmt0 zOG!@AI-LMwFd6~O?!1P*i?da9hhS41gr^IRUh&rm46=&zTnnt{Bvw^)9@gaX-eAfv zeX=M`e&vjNVJEdb%^oJj7Mz8)NsId{sPC3%?Cld|UIf`nk)M%jZVVFOOc+-1XOyrV z_Xcge> ziVP|C%R@EW%X#tp25+AoZkSXW`Mp820=4l-o9O*qgf$zdQ*D>r%pP;36DD2SDsk>= zz8Rpz=EsY;#m{T`j14?BNEhw*xV9qpMnyR;I3e;89)R)xXTd0xDfFb*Pz1(AGE*v4 zTPeQ6MF~N?vMpyFjMC3zlP=e+-imD04@LLFFMVmNboSq(&5qrRCeCY56pRIj4m^vG zNENJ;tJH#MWY=It2Kg*pcbXuSgEAZ^JHXt)>E`A|6t-Xr@B4unhH^9rk*iFoTxApw z4Ge+!np*02%`gY@8KvvcJQQc=GMj1xu^*)4m=(JzkyZN6V?r>fxfLQE;hK@3Fq2KBLY5gF$%}F zm+R8Bvw*~>n3^6#2OM^3?dPadU$OncxgX;Li6<&IU^JlaTnK}r6dPpx6DNnX)V2|` z;|r?B^~*WPV<3A?P9|pV5;YnER0;I_>ZjG4P1S63M9Xl<+-ep9%}F?STD}L86HsNA zjA+(BdmpIh`(=u2TGzX(ye=<%<*p*Jfv_-=m2LQrPC`{n zd=x*G9HNEUp|qrjW=zqvn%SWaXzWrS-$^gl);$hW$f7|e-@Hcb+1ViA{cZ`C1L%;A zI=$v)GiEb*+M25z)Q4Bnp>?T=%n5a6bwhZku6E1tgSaS)BLK}vav;Jw^ zso`&=#cv*7#AXtY@EH;rs`~zRL;CX3$uH zNVN&D70S=-%Dqj*U)x1x-FdfF`?=OT`Zxa&c#XZ5i!sEe#n7>KaxBLX)PE#A_^n_Q zn&|=(F0lJzhMDz|Rp3J=CMisjh^EUe;>J*#WLOuC9n#2*@8V-+HuL6auRYvdTZF4M zwkAAzo7Yua0nB6#gxD0bc(9hj$Gp6!I8<9O4%t@936z6E06Sy}AhkuUyge=zIgHa_ z-Na;C0BeDK0Q;*^LavzuH9`P|lwsg}g4KdyJQ0~6=&HW5$6AW~TlNV}pl_r|LKRtJ zI^eIc2PbH6j*v++N?@mD)EcxPbuylO$|D!c=n|8rdLO*Jt%<=yj}wXSWdd*nkzure?%S z^-ez8o;3<`Q6902{Pb`C236`Q=r9uT!>4Lea};8CQz&02i!l6%cC_ROCrr5h7lf zSAcA~;G^Z_HY6{Cl);;+Ysv7eq8wExb+VAFn%K)_yKD{$X;ZY^uZV@d>k{Sh1mU+i z-QL#T#F;<>z~GB5C%VQnz}aGl75~8g!&;NQTwITorxc;?s?@YiRfzN~gFR+fN6}Uy z2cl7n!?fFPP@T`AVMvz5Nu+!5OFAi<$01mL=q5#3Wl&YxV3c?oZhI-~`i1@=WMNQz zSxzZQ`xOkAPo%&t;PEwTg-h5AF>(hL$2oU($Op^Ewc92%J|?VGsG)ih0ancaDvYyk zqv3n(p!9MeNZ=+k){*~F(4iTXap{g+O6zSI3^8AgZTEWP+ZEA?YaG}^_Oj^0XkHYH zi8&WX7G8{4@9%dOK8^@?KE=gkNwU3Nzi4lqzgxc_il0{et8@J^qOC<|+;W$}2)rD< ze$?|1EUzYSrwh>PQQ8YuTAHm-wq7MQ46i}+^KQIhN5rRDuPem@dQt(UWyGUlw7S#+N1NU7CxnA(S zub24$SYgSP69?-M5!9dbMCpWs5#`qS)J9!Ce*OCtQJ&G{_1zE35v>(@HnUNv5h^Cm zxp1xArUVgAw#Rkz;@Y|l%knE@pn5}QA%1#Sce>mja1S}XD|bEW=^!3B`AQ8H|I{8f#cn z=_JQwF7GKD=Q3X0-cA$7vnD}a0K*An5`EDmjg}L?4%zd5_j?sB^L;?=Z#E*j2 zCc9z=ZS1KYC}zquZ<(HMVJcM{R)g7hu4j;oqkk->zN?rT_a75RL?3wnOjA`ZPIi)D2iiJbweA`@VaVJ{M{H9K$1wqh%+=aIOl zpiw8|I7tp8>A;d}4kXE#GfPZ$6HGMlNTk;R%_qoVzFE+?jIlMs7GnA|_t*)sC}9{j zxE|2xO7Q_oVuVyb-u(1tuw7!fK(rQ@sOCh|9Q(t(Py>zn@Sp?c02Yu3&-87wDR=_4 zsoVcIWRAl+w99=>AfVJ4T3zTd(Df(<%ZG0WJQ0d6Mh3mAWS?S~b~nwh#0_qa=jlrE zbPED7Nu8zjxWmWftgf18=XQi2M&)}IS$ER%e z&}fUPokojmq-CsX$+Odz@Ph5lI7w%atS^IP#dXFqZs`@8+=M+ym-P8`^Ex-vfvlVD z9;Q*g;{wgb;Q>^R$M#LIIHCG5tnm}(1GqM;DFolBM1*+Zdy0G3)qCzyi~1k}p&=G8 zx+>nnsYfW_q=*YdDM2ize3|1-9GSp)!FKd)3sp9!TMzeMZ0%pvuRLy)e+_{>wr%w3 zxScK*ZdPw42^CgKQ}Ea(q+r7rVAh7*f+N#@2Gjgpvm~0c%5mv@9TjwVcYT zy+@8-=4g6^C-0m>7DVHh{u4^`8gw4EWvwet#__g**pOE7z~N48%L+TY4e@dmko5~u zKe3AbE%=%-0y3=v{T$J$wCWVrDr~V%!bB28Ma79Dp7KfB*In5SMYr7?=W=h?IzcJd znmJjBZ!WOnUE&EN@dtZbQuH^U>`hZ6Q2w?~svQbAvdyGvoD!CbORskr2MgKuRR$M* zLA68!(mr=zY2FouFrO?$z6Oo~ZiRu)#^}y+m6{m0D9V0%8_u!94{1I6oHX7Hj^&O` zDQ;9Nivk>+5a8Xi8i`08;$uC?IJ90 zpe!@X-yU0K2o>AikLt>`OV5A=0ux|lclW#3H+bzQ*@JC9l;p`qkWg6&tnR!RPAt{L ztEh%x4cLsimyUkI4v%$fAAb=|<35}pzfML2QyEDl zS$QA=C#GCOK&v>VO-KGCtE&S$i^}KLQCvWW$V=Xxpgn9-VjHaLh(1B>l?GFuZ*XW* zomvTq{(>&cyuyy{y(}mH@G_S@6_uqA8Tx)!8x_$>wujMlG@}SA#&U47^^r9wb+FzL zCk#XVLYYe^umvogA?VtweWmhbDX9^l6w<+Z&#t*{z!*d7kY?;v@2@aNuP?oy%kH7B z$ScI!@|z0T+$60}o)W7Og=@m3Gwr%#O{Dy`{@l@@yOQLW7pMpPQ`*+b#QFnYhE*SD z3?*_B@&i#-2TVeZfOuKgI&;PbiSI zbKK&|T|}kYo&`4I#VeY8`Q;X zgiZ_F{U_XzvS^GFo6^rf&joOSSG;Q2(Oe}?c$l1GQJ=GtpZX)%HNjT=|ZDtXQgVj5g9&gec`CdI-KJt6)(NqEg%#|v0# zN7m;nL`*oR!{%!j9=rl|DJRSf^Vlr4Xp+Qp>DZJp#-<>>b(MC^3yVm(^* zT1O5vnyw^yxbV4C?Cy%Sshm@8&?&uknuGmURlcU!QN6+3=*Wz!tZ|@gE=v|OifPq_qLsoY zv^4D4mzCJCdn0M$^hXJaxj4W;v{!$Q1P795Nvi$?Wu561#x!D!x>ORER7!Li6=mrwz zNV*`7a(J#I5)~-@t=HKPjS|nE)dbPTFy`eG2CFRHQyjd0dYh%n)l>=sd5r43duT#< zy?H~mM2+pB$6RPTFMuQ~*@7n^Or#tRWMbKm%!upy!ydojIiPxc!3!N z&k#P|2%01aS_!#}>r8!^`JA#eUk{tiUg#~dYwvQ}9dB|y!A#0KxaG7zBY@!-6`$cI zczK@(H^Q7S79&rBvb}Az@GsIcXqU*{w~?Z(UMuluaz{yL;;uCS5)}KOG+WHum1D5%t$H;|jnWUcD5qe`$ zkc~wE+Z!9tr61(?Q+7*^ED{djCtH_~wIm-Q9$u1fI=RhP_feGVTDMgzB`A6v-Q%mz zV7iV`L!Z|}Xk(=K-tbya&>m&EU@wfvs8Cc*z~Zn=!V2fzmwQ((EKw4oI_kKr|>v zx4<>38!1Mz|@Bt;&rJ!xP&+1583K5 zF<;059#ZY&cMlRP;Dp|A{gSr^1W&VjP5EI~TdI-$%cI>WELaqAbSV?v3&#@ zTj4Vy=v)ZyY2fbbEsP{?w$icJRHp1Wsg>)hetvW(^_55~k{>Dw)tv>}2roQQ;D`(8 zp*h)4%XO=LQwMxQ%rc#nBihNUz1Od!HfRu; zQ7fh!MsX~D9?sDEj2>GQFKUu-!)kAY+uT_P8fR0ipqjAlY-X7H_1B`X4kE83z+(hd z0@;$wWH4umGc+KgOvcQwA5D{ms23;b;x6?^@k}I5>`O0rwy3EUrzXWFh9gFvhI4TI zZ?)0jDCI`CkLBRU`jc|r5KY1d5-!R&1WW}lhYP$HhD-gaM;u}TXNxn`q&1T}a1c_W zu-VIgr`N_1EQ?3R&?#3+D$!k?Zicv2h7}W06b(|3e(cL7)r^kFYdXy-`Lt^V(LLJR zC{G3E8b!4K;*G#!hr>wjOtTVqxovLuR6Xs!+&S9QT%Btdw!B+8WX(D|teiWqF8#>k zC|~@&ZEpUEXFcZiIiUGJba;hv8Y@~3FuU99bkMb@L)`2c#5$fHj^;#?J)u||V*!># z#Z9KO?#S;)2+ZLYTAV$*|NXs3&byv}l zQ5x3?M%JD+d1AjAXO5x>vd(>CJ|?Ss+e?&{{}h=VT^*BV^itx_kTd(^u&PtzTy zN?;>a%CW#aDNgxXBlo;UVhdvgi`@$(YHu%U`H1-`d9Jj*P)d7lJS-v>9ccGtMqu;7 z2F6Tyj<1>IOe08*Bby#{wH*p8LrdG)nqmq8%6gA_UI-%154yL^X1!dbO2L9bVmT@t zmMaR!mf142LB?j|;dilvB2`(nVNz!&&8w&Qq#>tcwkt6|@)_X72qs6gYI!R_AA(bO zCW|G?rK^_kqIord>pT%mm4;pv5ZZ~cWDXK`XrID9?2*E~EQZr+Rcww3aoL*J%(EoI$3#3zXT-Zn%RpyMZz`GF)7;Tk8jy4-t=&@em1|tqy2c+@ z2I5J}{kP}zmN6q$-bjlnn&f6>ZI9FX)KMZe)6=1soXr%JwW*X~tcGl!PMLx?tjMsa zf?5$PS`K1iv&BUWRd2a-0=h-F+aY%v1ix>^ECw%or4H&dhikkE`lL%lB4?%C0I#v= zBS)PuU7Sad3~5h4**(WKq3 z#;iE8A_{*x2UB_C9$gLw=m~oY+xfrc{>{dRt-t4w9 zTW<7RneHP45-IU*&*m>7FY72=br0j`fzSMA89?H2w~8w~%n&l=4LFlEXPoy+0?$g` zVZhWjq%nu6P;I@8m}B0^^BRoH5+0wNL3PdhUfV`)EIB)5OyogHtwzezm*8n@AuhQs zm$Kg5km@TyXc0`LLfWHe&?zU0yV`HsMw`=_yk72+P4eby#lLq{bwY5MA#5XJjd8Qg zmH9G1<4ziVJ9y0!Wxny0S4xV%N?}=)bCdQX7ybPi#3gD(BN=s15d+!ugB(g3G;zyM z$pm04t3T!>S!c5(s}-0Afch~~8gX`5AU+lKgZA3ge+2ehZK)Hd$hW}Ee=X{WgWQ#q<~$JA5f|tcM^1Bh@BS@!$zE+c&_oV9}hU7 z4F*R}?;WXu$k1!p*B#Vh@*=Y#regpn&5iKWb)3>I62OI%u1{&86DDt(#ux@mJK5<^ zPLBJFm9~`M!|Vt$QgP+#Ixc%{E58lbx=q{_$=VU9p%EdXXmcnRIww16!^JQ0$El);nD3&ZB-M=ZW$IfimS&m{eV;n2u_wiab`%f$6e+X3e#@i-r*{*^&NZG?-)}=}iGTP-UfBfa8~Q2QY^& zdk7NS=2>169Juyfeg^ncKild{T8XTjYQT%LhzRYQ8%ey8 z6PgYuILnXH2zdy3>m;lR0+n(StQ1ZQgOHM@YCAA0Ix5ie-VF;48jOl~%M!~!(;$M4 z9^q`k!B>Lb*LWWj${A|0A_oL*UHFP1NyD3iN7?+bp{@``gnKs8+N}_kXoavuc?@L+ zXW!Zm>mm2O1r;LLe1nEcM+Y5;uN&O-5k2Ep_=B=RtdN!uM(LJRWM7VBQSNYnImBJv z*DE=%0@KDNTvAKtW#L2(=Zl%P)WLayNMWiSsT2k1n9F$V$>Us;y}0b%D~MUjN;@v0 z0SZ5o70tE^^%4P-ZBc$%)hDKf4d4Qnr%qJq10!-d0eOnt-~pG%VBbv~=ko9vW zU&}LGO5s#JuG5Sda05Z23!*O_UUI-*9&HBJT-AhAiZaC4k79kIHIHEys=^_fP1Q*l zSu_zPxYOt|@jzjleB*S4R%}(3z$MR<cq3ObPR?>vwR%+dR8tM%;sl2}5;g4C>higJuMd{)8Ii4p?}h1J zN-?ousP~AW;E<+Al{_%Uf;&Irrbc;p))6@;YcidNyrD89cGvP$w3%C8vFi9RGMAvA z=oWO-Ry3pH@q$G9f%#L@S58qAnb^mw>lk&*3g2W&UM4t)cn8E)@B*$O2_Lga5tAM=fH)QwH$$(N7jmhYMK8~-)KQU2W(boa7>i%rc?w?#r{R^@aclE&-l7AT4*fe zGiITLYi^pZ(MNBxMyMt#QgNQKJt%6PiFI9;Ek+4PLb)=-BPlJ;98$yIJN`>|w*KCP zGTTvWEmkhDLw#8;*pJerCr?BzOqRaRx%yJjku9x-aMULBp=QQ@*3$N5i?emD^~$W! z3UOuB3%*7XX1XCzE+ll&^dHHFkhyBEix;wf_wz2+9~+p-U#dkJB|EX9DGyl zu^e-K4SLVBM7PH!q-PvmE;?yXoZPVTYa>KiUf8PF*T>p+8RH5~egul1b?<@y_~)ClH;`v{k#q zXU?>dLH*0FG9p)(>n5P6HBjN}vRG|@KmE~d&7hJt=@^J^ep|L3Luag?7MBe>-1*{_ zrVLw{(76uUv!_*_N8~1{yj0DYlc$4mf8zA5zD}_h9;oM z-Bo>;x#svbB*BlLp>AJe!gRTG{SaOz`8BLKFwq!{6tG#kR1F+HB;#P#&4)G4iiTo! z5Ys-XRF3zV0m~9+4#Co5$|{-^X??$kxYEVZw_2u}tY{k!;C*a$-xR%`2Km+*nKhsN zmN@7{?TDGUHd?HDra|ls9JtIvrIvq+92h0Flfb z#{sVfHgXyU7lfs*7T+rU)#682hDu98bbR{G3{pXev^i9p(wg8k-){_vPkB^rYQ$us z9;+>gK7f&8hNXz$aBgFZ?0xLBanhveq=`9MOwZw}T1e!bU5G&pYp@wQP#50yxiCJ- z7clMXxDg@|n_D~GWo;hHBqWwu95^kEM6?$>8YEjxBde?`1?SV0Xau8AC?P788gEZF zNv_P`P`i-b)R8S9E73fH#~@sUlj)*Rl(TwAuu6^P3pa!Pti7%Qw8h+!@yzXcjaN}r zilGK2a7QonV>pA*_#bm}9c1LIMxG)02nxFNMV|~R)If_EhPP-_V@xm#^tosI z6iCq`%+XD#oVjsIgN=!y%|Iw0D&(@#MPAX!YhWNFP*~iGF#AddBJ4j=5K)lf=?J17 zuOAZvNCyfGQJ4dQ<4p8JD6g$@@*v66mqv_^3hIVHW=0R!_hfwOQ@H$Y+^tux(525c?!%N$xE0TvX-9W8N;zV54c11*?`05n7anFrFew zxj(yBr1%qGaU-iP97DBWB|c|eVV2nw#CH=jtN zW)MFr0kLL`+@#I8hR7Tdu_36_)tRayhL??td5i8 zPs&;VD=9r9V7i8l1MWL#SfK6XAwm&7rc$spjRg4>p*1Euoc!B(x!fKu>(>tD(Vddr91s17JcU86zpm-)GRxwskkb6gxCy})s zkSPYOAF-2n8Zt0b5K_I6(ISWV=o%Q6rp9T&G=HWbDlpASYWqXak_-oOaekbneJ$}M z@5_sm5AVWMM^y{Ec+rz(yp0DRaIhine4T64_n_pH*GR?cod7DvrD{~;@Bkub;^5?+ zsn3Q=!cjoT_a&|%UXp}6+uGk4tRWKfOF$EtOFYT^SB~M{zdTk<)XzCV&tGHB&tI1Z z0#$3PvqM6Rx2)rRu(TZMSngZ7 zzHWausw=0(qF>|^|56FbU_&59PS&q2O{u%Rt>0Ebel5yl72nh^p%mIPW)J;30t>2) zc3X9lVwnmW@tYbtVm?jC`8Llj!!tYBqr|-QJ#!vkf7;}$ zN8sdhoxCHne)vz6I~Q`=kd_C$Oo9mMY~qHH5Ot5U<2vUyFNTXmPaQ7nm$@MbTpYJ( zAtI5l7ur}Z)=NAEonuA~f9k#w`C7D1r_W7*;!5DPEj{}o%F)u3g^j5wRK6O~s(i7_ z+)K2Fqn&pCm$!$PcZQdDX-q~!xJMvk`VNkD_&x#4uTCm}uL4w}L+&=nhn3*MnbhG< z7#(=vZP0lZpoae#dFn)44$zWsG!By-C;#(=w6#4MXxZzvYgZDjoh%{>52L~4Mde`- zALgUv>z0mF>- zI<2t`R;%Pet^PXQJXFA`@>P+sf<2J^_{_TPj3`7JP6rYtWu*4)hS4a%*6~4?hcwKA zGtr4xCcdQQV$}9u`Aa6`k2Q9XeYiov*wZHPF~@=|5;el&5SLe%FSv0D6pRy#T9Zx* zvZ#$@Uy&HKYwE}ISeK0RBeYBo#k6_@8jNv?EU8U~M2C$|#L$_&Q@`utc=mU+HCsE@ z)XOInp)ez3*$_V!%ixZSDZQ3#ey+g#-J|cTFvgj-V_IBK8nC@pdUZ4fh#(4?TpuW$ z=km7KSqQnQX#Bdmfsrog(ae+t2kd7szE}#Hhqf069)^4tp(hpyl(j$lC%(kEx7)ZI zkCdcM6j|T&$dbBalQFzlsnC9+5XFCeOsAs5<*`^?!sj5hkW1}kX;sXrH%~=ikRMla z+#_8Hiyls6AR99|I3r`+m|AcQ1c{Q+hpMRYN=i!0 zWs(U8)gIZA|F$b5%l1TWU9CD0a95BNm=p)B_QjNqaCdN$o%DhJ>7c8d09BHR$elF2 zxPxdWXOrjn_|YEPmHy(`E&KOH)(MubZ5=GQVoC$1`ijD1Mn%mRDp4#gNX z1T-sLVr{V=D00}0j*T2Q73Zkr_f^m)B1I^Umz*X^auT7lI&Q3%W-2QB3pv*6GcH@= zbxTCWx4Wm(q$gbrVAiRCJZQFPj?Xz%Ly8xIx|cS9^&ALP{sbKqVs#~J^Kyr=D>UWm#iNYU-Dg} zsO5`8ETV7y{@z^nMoB|6ybWkZ%Mu(IlYyc}uFP73xzs8NywJEQw zk=m~{Qp!M$Q7{7b_Bd5}V*4*S zUfy%a0{#vcDKi;q!=wzu1{`Kyk4bM76i{OF?K0dRxG`xAw5iSkcz{n~{p==K#$gdL zXKH=~`@t~5pB|#J-O|5nfDO9Rji02!1E>K;5+63R4r|Em`VxTvng#b2$-&;Q!5SR!~5bG zrogzdP9X~SsoTt^u!b}fRBNr)i*6|C@4KO_yk{Af8mHD=d;9#EY*5vs+O5WC3nT)u>}kTiQ@g1@L&stCqKptZz)NdM@Z6Bn56Cr#EvAx#>SZR>;>v=N+?q% z7`Lb)`x|K>r1%E^aBk*9P?oBR5mu`C8LZsqXE1I3xEjtM_Lk?jj}6AAF|Sur`y3c2 zy*aFD-seyhBz9=_+Z}*i`tgP-hZp}%i~ItaBdBy_+Ur!7`N=A5WzFOeg>GVjVo~Ry839j zbTkL^yT*m0#S_jc5H&8EJLc&8B!~85lG9E!$(AyUHJ9y-ncO8>7nTA~hO_J2sM7Qn z=lbVgsny}VdN>)IT=X=jP8Nb94CTv=kq7s{!WKRB0Vaxi{PZy;{yW*?_@;S8jG!)hRG! zLQsvTj4BKFtmRt2`$|4N+Z#?L(F~^?M;uPs2skRMgUHe(@)wL5PMM$qFQLDeD+?(; z9(5X*&{CpPc&G9=0&%vv1t>c+38>S6ar_ARiD$8so}DOZg_VLtDRj1VaOg7+etHM5 zmQe;SM=v5gnXp#&$4%Jb7z8X&e18yu&sdi-hl}texPq?TE5=x>P@hflwdmSCq#dJi&gW4vz5q?7@{FGUz@xIMqk7c27lbwGl$=qHI5SOiG4=XHlD5cxXbX*Y1`ViHv5} z<``=}6ceFt$?MBEY)MnyGkApH{uGa}JD_&*v9k6`LVSV})$>P}T#848@5-8{_T1j> zkH(@{g`&1=j>g(O7$Q!(T!+L39G?cxIHSl$futa=uqgUkg`z>45@c-9YZ-JWOf_=6e>nv$5nsY{x4{dxesFswqC|AX+)87R{>#}R~R zxYliQ;}t)wSE;N_sj8~_F!#MvG&(r4GytjfyF~(WEKVtc-RaPWCPAZZ($kLr!JL8m zyRuv+ay)>oGbVNuvzg3c;^x@XbRd~jKdhyRynR>XFo)dils9H^q}apTMp8?uSv8$w z5=R?r=X`=b=M!`dpzsw$a}=)le96%s1c1g85C|kxGv^%30O#P3m8>ntn4#5DB_yXM zjDg)3%2F%k7rbQ|zxWng_N+D<5c>QYe#_H3?tpfrL{I3O3p|osO}&GA2+Ds&aF3ut z5FuI3Osh?C`yp77LwRBNmnq9e&*e{mJ)BlPTEXakrHx6xG2U^-EBYDz^+_43axfXj z@PwPmmh`3kqNKDX3eMU(D*+@+P>qhvpnyDl3fz4_Kpn!4SmfYaOBwYoA zFPl1MluBOx%N?W>Zm#B{J-JC^ne)w5p|>#)?(AEZHm4zWHu_pWt+`~o5{)E^e%Iy7 zik+PmOQ~q@q$g`oc7$ub^t)?_$yIE7rfacYdOBoG1t@0{1Q!t1!mZD&dnivcr z>Hr4sk}aZ|kP(RJBqq&TCdxyg9hRjn5W&VFpF(Z^F+tIl5M@tl=lgqp(too%-CfkE zUz-^eL_1D(6vni+?s?^Q8p1dBv@#c##^<$$%Q54|awFMT z0u#uY8|WLydWvv}8N)9o9(iozgv&NzFlPF3FI&et6Vom7?d9qb==^f_Idh659v2gB zpD8~Ze%Eu;>Hp+ zB3@tHuN5aE*WNBk^mJPioI!xIJleLFocQm$Nrcl4@CI_6fnfM&?=Xh&^$Y)&l`=nT z$R)Z$9IL&5T|)q$UE1G-iRH|pf4PZK20yC}DycW(s*Qtp5nO#XCfq&l1DDLC4r{RI zId=CdV+c8J-D)Z~mgzZk7TzM@)hA8iWuK<&@tzljV&GI&N%4oel_QtT3$ozoi-G5O zTNd^wb{gJ#V@FQmvK2vERH%%Ufg@0JM=teAT2&t>1X#7OtK;#Pmec8`@ z3>*A6-{{wAiF_nRSdKBZ!73%2EdyX8u7#$?PqAt3;)`JF(5)4I)5_5v-AL2~# z4}VVVKgc&eR&Gs5zF-PL!CO zy3K@wQ*12>EaoSAu9_P2z7diO*1)=2uf42J0|d1?loiF)=ZO>%MbQ^+l%r@5ADZJ{ zEs``*yH(G_-eH`KxdwYZ!baTfbT#N*;T%QLjvd@g#2?+}E|+3AZp;g|g4p(}P>R9T zTMIiFI3`lJnDnm53B=yWII~a+5yifdB%87%->?xU;T*MGbr&*R28@O9*TRD`d7bzT z%&#UK19dPTyW%dgY*A^! z8+hnVth5YtIHcS~QDU7yvxu*t+*QWptTR23iY%J^cU$1OgZsvF=M}hup6!xKmtmOrdg&PS7AC&x#CKqj*42K z`)LxFB$Sn57T+EQ--Vffe`2cOgw*4}`0yOjGkkJK--ht76E|v}SrT>5@uH^k=@3-8 zx`J=xlmkDQl7{68k4icu~QYEE0U{0a%kQ=HOouX)nrgH5W zUXvjRFAs>(waFD5Eav@O+pUzAF!Wf;d0)9PX4yK>CUxA&cflNN=>7_0#~ATPoU|6L zLs=Xz*&vP%xX@k?L-TSfp#Q zJpa3=kN&?m2AxsI;Nib891JBvef0xzhLyq4a^}A|Xy~A*y3+OZZ$4a;$9pBtbqg4o zj{rVo+8$yo{9HR=%M71Ad>TIP@~h8OdJXUy@4H&*u}?Q%g{~r!(yyyvE_)n7eua8) zAGDmnNq!0I!PdR?c}|SCZhZ+O8@K=OtaANfAlHG+scN;LwK82n}I11%fkvAR<&v5>9NNnEaji;{@WKY z6n7rB=ISel&oEyXz`aP~2AD)q(*_`#F4M{v6P^vhG^dJU4N z9w;!*NzzYkR2wu~0emiXK4)sUATfY1bf_`ORf5SL!81M7DTm|d*oZiSlFSQMbag*~ z;nBs>@zcxC_c-pxusnYxN<8;5qo1r&8D{b)j+_S$ z3_^#te4O!DeYSaT^Yl6Ujx3N2Uz1NBVMFnx8_A!;-z0%B_L$_xl!c-5R9caxN zmG7fT9iEEy4w{`lWjn%cULEuc4}#UA*SdN7uLv|;cLbGWK}RHr@NZ}@dbU=7nxCM$iM^LyGINtmh1@h zimyS@Br$(Af|VOSWe0JfAs%HB^HNcqC%&k6Q}kl`?q2XPwuT$a+<+O`Uc7i91(WX_ zZA51R8Ybyl<%h{f=H>pJfLW~@!{V7uS|S)z z=;z|r*f{6VRbE@slZY^ zFs05Tt)zIYB4A^+EbM!@B_eR1o!o04zi=sIrAPSl;Nl3W{`@nPKX?FfCT}cWdx8}I z1as5L@KA9(_clzOSADtB)(rN*7ohY-J+0?X`=dOF+Tdr8Htxdprtk4CF=;aQ_^vws zPtn8Sz9Kq88`%bqPuuSvOvUgudKUpi`aelr&c$l<9VUdsV_l#4aH*JJ{UwIYI`rh; z7timdF8ugN@ia66H_ALVf}Y$W8pL}G=Vd6@iI1gFb%(xv0~+GpPhBcOwdgQ ztJY%PRPtO`THIIwqka?IWcsFpFJT=~^xaf+k9gxZ7I=zodg61uU9g^iPtik%#WP}j zZmYyibsxepUxBaI(drwD+}IPMlq`?0Jmiv3(UaW-XHWL^P&bu*=^z$2m8{xF<))Ih z5#3bwM7|nF`!^2eb7=KV&xrhBXt}8<90SDV;=4=l$vAdXu~3+m^*u#d)K$-8xY{Z7 zO^=PG&si3JpveR`RbPDa^x+2d68*=Kd3FI#ob0^R2pW`xkBiBh1yt;WGp zXomNbr#2aVr69Ch0r8M^@bxf^G%UVE6bEEYRy zD6-(4Qt81;41J3Cv`4-O&3Gf(IoapBUe3jkt|t4Ak34$u=+VgT-uv>!nE71nv`=}W zYG}zPoXN&!zYAOMy+VS~3GEY7mC&l2Tf*E_XIT*(*H1dxRs$`4f|_NLb+VRe9%zMZ z6510Qm8Y;ECc_Q9c2AlpSUK==ta4BO8sA^V)L%!EzrWi2Wn#S)4(l=T$x2k|-I~Ai z`)Xc)_4n2O?jDpt^Z%meB_nF z>u ze*G2K>oktKFOya4lF?}N*IcWD0tpjh2-tVmTHV827|cgS#BH6kp}E!~3Omu(E3ACBTk6(F*W_+9&y`@}3$fU5~&U8{5FVVJS+Y39zuF64brt>X9gVxJi+UPq({ z*uVBLigJ&Js+lef?p-)TApJrG%xSufDztndF`HhPH}fV{6Pxe2R3X4ZLA- z@dw^VI#5G-4?Xvj&pBX(o^x$gR`{MBB*OGPU2>@a?)m)}pJsC!nYH^mx=zdxmq*li z@ZghylrG&Xo+h7d*67J29GHeDLW8_9f4_Q>Ul#OmpE`Nz@MP6SdQ^gsSt3LJK2fD7 z20XDXgdG+$1y=kgpB@{7h|-mTk|scq`|Ef(H-b_~VJ)e21~YgKn7{M7p!ut%+k(}e0)sY+l7E~Zchhd$;EMMZFF#}rjak6)CBe1JBn7fzr#zqP;d(!lCE30i@AaVw; zKL_eGG?y>WfhcQU%^+WHTpdJ12LBDg`kJUHvuCo@UwmRxQ+6y*cq@3acX8tb;PZtH zQ1ec)@ku8(ZAb)U1^_$YfP5lt2LF6r3;<;}tpFSmq^m#QO5sCdpsnYV9$YD!wfWVH zYhNr3@{_ZQ{pB~ziHwOBT4zsuy|uqN18CT?cWxce3chftmTM!dj;wC|{q?@A(uez) z<_9IbTht=Dp>GelG}624kO|w`%-gPXU~@$0j;xZ@=18s{JzqvMKWiYM8!20go-g(6 z?3r%x5vH&wHA_(2QYV(@Yh`2Y%&{w2duF6IOPIX+Mk`USRo3Pb+uXIHrX#r8P~-ImjnHESEplqq`_Sg{xKWGcp{bneUV;0m z`_S>-bWK&@SIp@s{fKSv=mc{hS}WyU%dBXg#M`|${Sc;n%-!GHXR|+E-=9J6;SX|e ze~%c$hdcejPtUei@ssH7+i}BhiiW6HG|W4etMmstC==tB9_+H=t~>es)$*IaJ84Pd zLUJNETiDz7^Td;<50(du)h#i9xH71D5upuy`;^9DK!Ev&T)5%vCqysIs6R(ml2eN+ zp_-O-mnv=twol3vmm0uLCG730jAs9wE*NU(3_~A!^}F1WQHk21-nPcFAJyj>?ex7D7mHb5lJiaVoB}g zUMqWYCL)a&XcdcpYw0KJJA?sM0iq0^ecco_fob_=Yhf#y!0DM~rH4M|csa~6W0Ox2 z0Em062OXejOqVW+ z+KxF`Na#;Ujp`4}sY&*%yJup5R?Ye6K&% zxW3r~QyySeFl8ua1(A^Lo72pt)6VI+2Ybh|Q(gmK?!y|PTe(&s53)HBuc!N$B0Zhj zv_a7eB>($?)mIAOGkexzWRwx!lBexLb6lMLo>k%aGa-nr{XD;QnmRX@X^6kGm=z0e z4ZDj2&h?J<(*6mv_x9qb-x&nw`x7qp$d2SHFO!1<{ALG~hCk*;?f z)<5KH*3R=~knwtbs~G3)hYVgNldZjH>z7tHT>Dn_Xx4sE^M)69`uhNnS!{;0r>r89 zcjacUHY@kq{hsoyTbxH^S(~E;BF)xmU(K^tAD*FikaLYRC~P_N2icu*hWw64y~9Bt zuYHNEKUY@KS1-Og^mR8Hz2>mU{!jB6g&8kki8%E5MICUljSKQG)E=IFf%@xyDh$#* z$eCshg3V>KJ&!tQ^U{;x)asE`RXoOa5g*fRCh_cCNTa0vASA65=QjB)%y^1KXhEwBhrpKa1@$F%47yfW zo>~p==`EdGhsg_i_35V`07-zwR%zuFLW>?f#l2`xjgrS@F+rhQt^DPrkR z8LWC?Akg)kZH~k8wQhWF6W0gJ(6Q2&i&o>_`Q8KYXTuD@0Bo1N&sEN@nM#u+0_C=>uFrALC~?oq$c_-sJ!f;Z71z0D=G{9y9S2Xol!$|J zLdo%}Hcob}@f#aNBiyS6mx62MyE%MyUeHgD^1@G!g8MC(;+D&FC- zX19EQF(9~vI+3=u*5P=4b5<)V*$$lW(4pq~++HgBC%f$nt~Nmnp+f>V3Tw>TV9YYS zVSM)K?xn8b=ryAlxLoC;?|ZVO#VTA-+mi1-6sP93EicPQwm5sn%b3rKt1Dx_94`a+ z^aas`5_cc+9XcGT$xvJOWL(#s6bC{DPTe1<&p3zZ!!W9E9BT?Vf46Z*eLA*&nqPHN zbmXbR*N<2u|B0<*$=zprv9-&)@)Y73%YdurAu|PMdtk2OR=D5+uhgx@Q6f+#ktaW` zHV_=RZv0egmS4U)8iy=(v(}2c+B^c`C+jt3xL)C z87i<((+6AE4iB^mzWOYN8eM1Glx8}~;P^QIiECoN=Kfb)+p$gHhUP@?u5;+F@i&kZ zo2;C@HM=;@w>Sd3VAOp$5%!wN#oG$@4QEiFtvy`56ewzH|^XFw!PI z5$@1cWq)6t*X$Nw?{ZqFW?Q78&aCMNrK$Xfhpt9zOfJmTxv?Zr z;tx6nuF6=ZgX0~|w1B=;utC>Ah!jp9&&v_}k4CegE9wx46C94{cz#^VStla)*DptS z!}VUEw=!X~UXZsIEW-RNp3`OH8F+ z9enBYO**VXK~XioTlx6_n4_F^K3Wu47pFO|E{Nz9dZG+crMt@0rw}rHY{j#N#fR3& zlIrcUN{A|nS2^;VA1pvJUvch0grSOb_8?tb9^L2Yi__X4&Q6krer`HEBuLCnC^!Ja zyUk&9hmn+GFzI zzL&Dq;+S9UxQ{{^dWYZwzcjd|q#^#r7+I^Ee2c?X};peXWkEn?mCytG+~O zELiq_?JFladiK?4`Sm0wNdu)&aZ^>U4LA9P&#YmP&{=tozpd+hLd33x(D{ijO6jT3 zbV75MTl*3NMyse|Jkzf}u_l6-TGK;SNMh`ZcF@BmeE;Pa>n()Ypav1YI`Xx=Kg<}` zAR-diq1aZ(_)t~VyqJPDZQR|Ci+^?eDB3V<<)2)WUT+B$wA#hEC{+chSplqSb$O@Xc}2ZGXo%$93GXFNjwM zU!$&CzRIZ}v8c(DKtzLZ$*=t@YJgR!n_nF~IoY|7b{(!4XBfypxBf^heRc3iXY%>| z?8zg)(apfj87W%kA*bSJ09_EwZ9?NKO&~|IZ_coBm3@$UA8JFOzD}q^A$!S~Qg5Es zQTE{*>@0PYsmc2{$2qk_M`hR)@BYoPP~vK-dp)T=@W1DuKlmCGQOX{Pr(~hi|I3YJTj$pDIJ`U((7Zso%cw z?X7=&Yv9>|H@rVwsI6N^!>7029-iKMfA|%UKMw!E-*^3|8)5Musa+Lf4KD?zyCxl1ASA;+wSGZKtH7ye?e=zYU^M9arnipqg(%B_~=hR zqVC&o{BHOwdiML_^WmL$-hK0Df6Bwu+oe~3_|vy})9;60{($eK?x*~x{#?+{ACi9m zFMjzKzozYv{q^6{i{CI3?+)M41O3OhkVfv|Kef0^uYSpQed9O&6~FHezXtZJ;T?wS zKakf~9{pe#-oK?0`~9248}AQq|KMLpAE{e!QoF|>DDjl-M2q@m)`$^-hDhg zee>g6@BO=fe*4#y|8Dr%pM1nOzf0dg(z|cHee3D1e;VH9@6%hm{2PXU=K0?-s(-xo z&aGbn`7NbKw|++I(_8;Y`7mgV-@V1x^94WTyLX5Gm-MfP-`#qL-;dODdhi=nrDsp+ z3#0$$dvAWj@4NI_dHwHgzFVX7546X)0r?o1#_40yyz2+=-}*J}zDJLL##@efm;MO@ z{sAoEUQ4G14ZPJ+P9qz5(#or*X8edAzRQ>XhW@{E>kqv9m;Cz`Zx=58mbbV6y+e&3 z0{@|gncw&5+neBx*1&K}D?+oQ*4`m~3#i`n>8%Ue{T1W*JNl`=-}2snVZ7hJ^*h=J zd-%Pe)*tFczw_^}`TI*+e0w;$^)2(Qr+>kW{{v6_Uf%?C_bpfb!EczM-!R)62{4@2 zzrFQ`H-FE&eha?;arouinjvBHZ)xvW^x)UL?d>-{06OsQU+^WrW8A+5JO4*%fj2>fl{@B@1G z4*%XG_tfi6BX*0m{>$NmH#CgDedBj;{L35v#DBl5f1mQiKfU=ag&*_Z2YlfV_{OJi ze#)B!dGpPWd7JR${ox-eQ+q$)-{=5hF{qfd&)E>aUr{sUb zb4PFe{+*8*QH`(K(bNA3F6a$EtN(UsP4wf}ZsmPO^A~TZH}4Ib=&V{`c`U5bP_WtG8zud~&VpjPIwe>A{_bW!{A1VI>xo^SX55Q8^I{kQ; z6jX-)7zy6@{@YLA76$&>Px6P7qrd)i_|*^o`K^z6_RZmEJjI@H>xdq`PwH*{sVDkx zm-@eC>;?IhzP=C6d`#+H&&UNeGzxzNF6{r~8y9bAuHShGt$kyc`rlIT*SDU6SNbZo z{Lb))yIS@7->*G)Kclse-_mUB>p%Th?+^bB{Eu&Z`_?zCO8xIW|NRZWs-^kWU$B&Z z{SGSkba?NUdb;}j*@J)2v;P_l`#0qO&2Ve@Zw9?Yd#ir6zWxl@TcrLK|Ng|VH|g1% z!=Li+&-nM}{1a9B*Ta8HT6N!^)cq5e{TpEV?@#&nXZ-tf{{8FWzvDfsKk)az_q*RR zMf-E!`fq9NO=`SJ$zM|cFDdy;paXx0;eX)CclrGvDgSqr{7?M;761NcTKZoo{WJdk z1^@mn_5N4a)%qWX|BZkDo`3&4b^inZ{tsID6aI;QsO|rgzyBBY-y{7#<$ui^{)9KI z0RO-F{g3?nubjOFc-*-5xGmc=nPeuJq=7OsLy9RgGjq4h%*?c9W@ct)W@ct)#%=%a zv1iim-n;krJ>S#5kt{eml4Y?yX-F+Cev*DVGgTXR)21>&IAlZ%4fjl@W-esAh#ga9 zMzbtvla+YcOr30`V^a~39dbZU$OXB{WggPbOT2u<&riq#$Sp_?G;&Y~S^oGHCMQLZ zR}_ljR-9aw;LgS8x+HQ+kxpqSL%f2x$~`R$ZkVHgZY{s`nH$37B9!Dtu* zV__VOhY2tdCc$Kw0#jicOotgT6K26|m;-ZR9?XXYun-o3^o>hkDW$oL8nzt!3d*V= zwaiUER#LlG;U>M1)JxHA4XlNA_^+odH^4^XZ-UL-mo3yr>8mzUBjx$sNLV-Vx575; z+hGUxou*8O+Qs#5*aLg1!43$8F#hc$estJR+A*ln2dF(({R~qFP5r{uA{H2V{n|hc!FBuOPxMxhR>-^;eVPCXW%S|{Bt}VI_{Fk^VE_kJSCwZI$Xf*B3y#Y zJULg;;VLq(A^*CW2c>S{cN1>GZMcKHf;?fq5DH=B=Ppm%J#6>QG_5w~0kzHzp%6xl zhm_$X;!C}YYRcAW)s=ceT2J8_Jck$XlBd;<`3iZj;SJm{Wf!KMbf~xR4&K8D+@qR) zMa($9xcR|H()a|QdD87X@s9}e1^2IDg(-%MuD-!{_zQke({);dpXPH?l4rjZJ(HB9 zlzVA2ZB2719SpE(y0U|hR>n-*VY)eZ#vMH45>NX4n2gA!om9$?uukkQ@W(FqCqOGl zm~xa;Im$`Or>t`09|%@CIT(+YCk*X7Zo!&Mg=iI2C}{=3Q)F2AuZX@56&9&)L1bE3 z_o%#zs)ea&S|w%x6;%wveIag4Q;yXZ#6o^-h=X5T%%u3o!@m}C<6|b!DwCVa$gPYv zVJe}PT_r+hVpFc@Zr#fygiQ*`AUULfl#mLn`>U(ejP8_d1hsveZpdXg!B)iH@zVo1GUUbffJwFuC_)e22 zFH*k>A-6F3E~3@sO8TD~jG{8o@?=1E24qW{oPnAbriz(5IaG1Nmw=K`$|H*@JaxvQ z%0O8t2j!sxRD?=Uo${}YSp}-X|G#!q+HaexhThdlvj)^ehgw|MhC1lJ1bvrMD}p2Q z6UG=Xv#QH|t4A5sCrksawrWV+M$p*I1N|29n?f_Ln?p5dfu1e3I@H}dlt&%2tYgsU zJLwTyY0{_HRjrZH2HJv7h<3}4{MlymU|Da5zm`9tktLWBz!OEZN{}RCTwh`(;2tE&=2~f zPj`A}OO}MCZ`7oxrhmjf2%QJR4SH@jgn~RrLnyPM@F(#*A-^y&q-_{RoZ*o=I@AcR zM{3O(rHP(4HHz!eFb2lLIFNM4gS1f-U?NO{$uI?`!Zer;Ghimng4r+!9p-A*C}JMh z^I?Hm#;xcz>07y81dCw_EQMvT99EF8m9Pp{!y4SyVyb}-VJs|u;;y;4N==TJkVy}oi%}m28gJ;;E@g)v1h}Kz|0OgASdL4+_>j~yx8+We(Y<}uK;F2CGb+{#ZB7pFjX4)Whf&Xqhy%@ zlqF6%y_3hkn>p4mPnZgNciMvP$mvcjR$}!Yq}PM%9_Do=!pL*JLF&!{bgVD?2Dc)RcpN;tC;;&TYZ3PhrhH7qDOn|3#jAf zJVGDH^AxT+l1?YwJ3|-f3f<7Bdt@4e%rftRdr#;Ey`hgjn10<`23DT>nsI1b@$U}< zU?2>F!7v1d!k;h5Vtc7*39yXB2 zjp(-t`)1feS#5=FupM^bzY})BZrB5RVIS;=1Nty^5c3cmCjJpP3di6$oPd*X3Qofr z!ks0(wA{aQT%U&va1poe%+!J@n@e0@hAVItuEBM<0XI$Ebae|kw}~S&@H^P=!ac6j zBlkYx9_S;e`y;4BBd9~t=Z@rhB-bOk&Z!>iIa#N$o~K9Lr^mXqqcSFNs3*vhIu#$D zBJ&wMhZpb?zgL8R4R7GBJ_=c*)jNF*v3XWW`-3O`M|~{yn0bJCZS{qp@&5u};Tv%t zG7J5V`4`te;HTc2@tyTtD%P4b(2Y{c0GlyZ*$s#C0SEYk9}TUOMS>^HVkMr;>s+|| zLjbrT5P~2I1QY%SI>~b{?PrKF-dwW@C7m#cN|*_><p&*LWyU=VakGNlW0~V*!>uAp*~}4Z2^C?;nAo}p*^Mxj!w6Sa-07@ViCtGYJ@Ip4 z<~HSqt2~%_k(JMs=TP}E3*awl+(4%a+6?N0#COm}>#87rg*@gC7lYzZ z!k9_D^_HpVGK+Df$gruBPzttDFX?^B+w4d`xd)}urwk-F{pJwHiYNEJq^a}V-`p72 zn{M+=SrT97-m)$+LYvP$^2W7f#-Y428w(=SD~GN&>W#$9j7}T0h15rhW97d*Zb|W% zIhi&8wZe*ilIIGB%;hBRqDY-85~q@>)8gOUym?*XNn6ro-a*(Zq*W2B8cP|!d-dg+ z#;-d1)qua3t)#b%v9N>-2Ags|B^84*UUip&rzS2BabRe8Q@Z@mo3@wH1FW zCut8Fn)wX|;j2&<@%|2j~c$pfhxVuF%a` zB3BQ@xJ=I9!k21EX(U@b5N~}pSe<92`kh$!5+$R{C znh2BN6D`$en2h@rm`b>5AnQQWxt;-Ww5?{j%#4&H@syfHzNg?OGI>&|U)yMpw$UD0 z&&6!h-)fKNkiWTRI9<)deLgIJg|G+~BWDRLHMXi{=pCK1TaNn*;;l5cBcJ&X*Rzo! zpu34JH4O5Sl!m|A1(Qfn!jb+8^bnC<6owb5v!HW_=^ee$lUioQy1 zLH2pl*^2u%*iPCzNOLFbGVc%bCfxVHUfjI-*oS>T{s-V7G7iCEI08rE7;GRv$BFlv zdvF3%#y}@APr+$8183nJoJYu|E5=@R6gfSYj3*r9F{<__GY ztnR^m!oqPT-W>6zbop8qJFSW2(qJNW2N;C zW#*)O-jd&Uq@@w>J$%;>s1ICA{gwXwBYZLrQde#2vvEj$fv@;~gYU*+R?hd+etPfI zLG_m>?ho_6NLefO6Nt|La9Ji)x=r*vLZ4@?J6P>8`$9HW?ZFN{;2>-avrj#$d~L_f ze%Q};i28d(Iq`RyGT9@-9sq9JaTQ3oAlnJr7^jM2bE;qnflvsuon*EDB=uTWf={Wa zw$t?2IaM^9j1AAQ8yKcyASb4ouC8J+F_bs<;T1iS31ume-)?-)o`m0HJ~Qcg4$3A{(!nr59&h$^fCJ~?2VAw7@9y+Xa>!p1+;`#u#xAu zHD()VOWbzQp12)sS5!xvjMuL+w!4bnSO1IN^lgOg3^G2IwxkR8{@A->if-MYvh5oE zHgg8j=m9;U7wJ4<=b*RkhU#M*t@`pj)Z@YEM}GTLPY2*X5d3U6*-N>l2H9?_!7zky zL(%I`7zV>_N!17#38P>%jDfK*4#vX-mf}cJ9@tB`eXyT$I>7Zo?1$hm9N}8(_)$0p$KeF- zCrS4d;ZJjY2JNx4ztwr zyDEcS+OM-JocI|DlL<33WPz-ZjqnkeU#K^--;y1B4#)|)AUARHVCIEr|7lzv8jxXU2QQm|Nn7tLA1|vJZnkQhwcOyQH0LN!hi6)@EL$ zyhImoncFz^Bzn9y@7b^WVTGw?`g!+3nLip*|ccV@L;+N6K#J`(KQ6{$8g4S)SIMK-Pz(?U4KY zp1Kv4=eIun$3*)N&$IIHZO$a(O@=8j6{e8~X*Z^u<;*yQe6T~~DI2NlzhwR1cFi>7 zF z!xKZ-#_5K3z;4r|&6j7RA9LS>|FvAK=lBpZ4ionX93`D&n8)D+$QhE8qVzvk;_33CpAjkDGUr#H@Hzd*R|*aQ9% zt{nN5J=c#&k#_VVaWBDTxI&!t$SBC^4>><{)$Sssi@dwoab>P-ro$RLd9>EG{52nT zEJ8u{hfA~eaxKy?Ky#`}>IQMFG#S64*MCcsnIkz2&~D*>+Y>)ICrccpd58FSalZ%m z;ekC!duW&S;UFyzCrd&hjC@u@=STQIh9}5qXv)#)OMTT-^7RaNSwne_Jnw$(3+yl9 z6}*NwFqv>~3IEO>MSD*=;b`%}?$$oq11T4m`eYBLj2Ryi?u$J{`wEQO&?CewgKxNh z_oV$7VL91C8HM5>YL?*-kKa$yU?ts@mEG)DqiV`0s+O43FPw)oWobSkn(kwbEes#I z$5Qsh!%gy6gZfyUduaDz%!!)=d;fmYHdA#RL!2_xqiN@JGsNra36BBg(TeG=Zi*lT}_Zb+N2-$l;krd`4!W<{#ioveC>DMk7(Kt@3@s)ncD4 z+7h3%+EVhp5Pg;*e>tq6d{&b7Dp-v!(r&GxjalmxRju=Rq}Ka{Ya58O(I=y}33Ibg zCT$DmR%C7S$*i{fjAFb!iZn-g(qw(y%$s#zH=yB8!te6Qs>xY#>pY&fp3C^)?`w;@ z(Pt0rg?+Fe4nPIk_Jf#*;4mD4qi_t4oA*%0;kuGBdJ)=g@A-uj`1hp_pTs#< zoP~45I}aDIU&Q|sT!t%f6|Uia9d4L)M^`to-y-~N{O-VAxJMb@$9w<};SoHBC-4-W z!E>|S$fmvUiO~Le=0fWKOJu$x+-rCPGOzIJ|CZ}_@E$&p)<^gRpNSJezxjo>Zl0D? zeZ~J9e8>GSpX^9rf1PWQ`x9P*a&TS=bVm;AIcI8F7`F+8w&U&t4)6s(aDof`ApqPE z2tkgVv|CA46huCvJ_cikKqw^V#HJfJsi$GcmUcBNW;BQnF(4-4Vj&|o#DTbu>{>i@ zitoscCb_BCa?;bP@3KEBXKrQA@kGnRvyoe?V(+LDIP%gnh+WQziN8EY`OxckH}>k$ zHxcP^Ldg?e;YM}-|lz$|8l=8 z{D*uiZz5k#0m?m){8{xUm7~%>WN;?VQTab)SanAHs?g4g4903^9jY36oyNSb7FiZ) z$*=5>vVBV0)g#@-P1dy2nL5<))CDvERK4SdM72M?4elufAu}}m31q|j9H174dzlV z5t!K_hnaqZ$no3xwj2}Lf=>hx3W+U%0mUHNcc*Q_NuZ& z+H1y&jxbdfszG%}BkE%#+NVaePmRfIW9*Hw%Q#3z3@%zN7yFA%C@*jR!c+}p)`VJM ztv^b>q@FY-x4f5xdsM>9x%xVk&mZVp7wS2hkp_EG*jF>gkIpG>yEzw?xHi=Q|AvmH zS|djb(rbY(Ezrg4^BW_l338h{nlq=AFwM1Qj+Ug?l5i~vM_+}E=7gz9+bR0^YprOz zTjAdde`$ZiR15Om68Wu&-x@vSeDV`$gZz=0{vdJNI$HA_S!Wm9;nyBIAiE=Ua5OC-j2e#FzP8AMAahAM}R-Fc1bISJra}V;_Qj zDCP`%Tgu1k3uN8oPh<>(4V*tt8F@df@{#j+`B-<&4+UU2@khW&7zI)fM`MnGu`mwC zBTM@K3Fs>IXd-4tP3nn^X=QG}SQEF&Fa@T^mWarD-XI{IkG z9DTLp#5n;cN$(VC%lhUJ>W`eWIPEZ~5A3O-+gbR@xL@}D&tX3g7vQ3!pLWU7U%TuW zpj~kc)UJ}|HRN4K)(yCc{TAFNY=pVK(t&wafBG7ix`Y2+{O;i=b@o2@M%Kq=PW!+i zZzsq*gqGh!!X)Qy1*uPui1V21C-9Is(Nz!A{VDGIx%Qqv;ao8_nf8YM@ELg(dq$*2 zHQ%0K-+}Ni9D^9EGahpcHrJ+JIff99ZwVm#jbkWo8ujr{On>#tG0c?l7QHKKQD_T> zY44En9-{N60xKozgK6(&+CQ3hd1FFRw-t3;AxlxWN0@1zvya4&@gw2-X`FAz{Nfm8 zwlUHc^rLRrc*6k$`C~lhKtg)UfPF;beDmb_J32=vmgMIy_e0Vhj@z%a$KuC%d&f8} zgtsL^A&j>r{vy72o-1W;^)Z}v;Te&47=Mu8pTIy=X`q9g6PGgp26mh81mt+@YM8S7 zGNNorE6>tI-l&;~o)eL8 zja~oyzL>oK>Eg_ayy-EK@xfW;@(ox1Y-9vLR`xpFzBU!;JK2;cYwbb23=jo^VH@iW zA(){MMtFH+H7e;x^OZM7M4rqsrTvvPPOH6=F!i*l|M15T8B&*7A;W(fZLq{=tVc-i z9#9N)sb-TmP%POoNwW`i-Z~e{Sy$4or_G@4xBO$1mpBj?;!!@bM%v zAmhFkxF_{h3L3+pl;lQisDidrVjm)Hx1!QeGD{c|?Jt`aNr7&)xk)(H1Mfl!P z*?sS+9E6j#V_6fdr_J%?{on4R+y_oxBQq=Ua-p+zo|ZO&H+ZDZ$UV!Ae|o~@AxvJ# z2l=6Z#53!J+^2b-@Qhv1zYuw$WyPp`$D5!tYFf{x%+qBb;n!Z8%-=8dS7m+AsB%2}F>K4V^2DhC6^UO7v$F3Bt%~nTPuktIs=nQ{YET_&KuxISyGpA~ z_&V?haq2=n%A!8_Ypb;ezH2;rv*I`Om2(Aaakueqnw%+Er};+aP1bcdFGSpUJdZm; zp3BC*>v@8C7f*E4{GD*v(26^HRCd?50hnoIdJ@<|A7;eL0IE;Xi zAoKR*)OBeKrA?Ifu!wz|r+wJ2jWT8I@LXGM@&;|EN2axpHJUhMNOLUlCbK=uQrJ(&zsNJrL7 zDkFa?_GzB@(=lg&v_ms-p9QliYk7|6px0ck=fQm6y^*>vz<(hufw55b!2FqauaaMvno2xMGq`w+-4XlNAupXqX+km+dHW6<# zY(d|xuno4u4%i8j_7M7qUFfl!aB_A|L+&2@yd%Up3di6$@lK%2N#C>T6!K4VeTM6^nCGCo zdEX94$~uqR1>#+VOW5Vy{bkI{v^oEmEXjWz{Ya#&tfnrU9;Ysb4?`cne%D5I8Z_N74`Ur97qm}HDwb#SdJN({L zo*ytj!WhVEJL9QeRvx9CKH>fuyzk|G!QP4Sg`BndDse#G)t2uxG$y~_NT&kN=XcyI z>1Q>ojb>dG{eM78!v3UPk(VjiQo^<6NiHzjD z>1x&EaHadPcLz4GgAX|T7;AzbIQd4Jth2jt_lGj*+LPgKR&?9O^Pa-E7};iKrF{A^ z53*HD+WVEjTL)bFaNU$~})`H2o!w5Wdayvi7W^<0mP=$J7;&L0@)q?_Xp&f)l6 z_mq`&f2?w1eTDdbzVf!OylZ~bQ+KmzG5v06v5+6zOygE$8!3A&anSEJWfPbD!~;2l z6Cbw(@P+i!GolLDZX;RhyVWiwM5jdfOPiAzGl}1w$o5~xOY*$kL8d%2zspZ*raypp zEMy;4SIM~l$^Gu4r&1}frv&M%Qemd{yGKcQzn?QerSVIu()!&eZ_I!F`m-nEoukM; zq>X(>YmYHK`dIsfvS0du{mEEb2ET_|IB91jtxV`Hddb{h&V<*}Wk2yg^Vmn+6Vj(X zWF|}&$O_r~9%~VP9aMJ6f$W^t?-V#p@p&GEN%;|vF7Am_eY;MNjaL2GD(E^SR6*`Gyrd)zueN8BRprBo-s zQmQlVU7#y;!@WE7@Oy{O@6m+!w6#~X39r!o6}rFCdZJ4&^4A;sAYbZOU%uj)II^C8 z@Z{~I*3VDsBx61F9RLFfFMBzI%y_yQOqva`55XJ?e_|g7!_jL5;jO%X(nk8d*G8ci zJ676g%6SZoCEPg7@h|}SX2d{Ex8^y3YD%Z>RL~I@8nZ) z{a2*l6m+)ye&EL#&F?4en#d9zl$z!zO;os=&c|Ui)+DF<>6ke&4a~EcHq17dc4p~T zUpd3iN0)X}&P2%i?M(7B3uXgtF@BukB;C1?jQcqcbH15pU-8oy_&M~2um~38w*;2@ z`RQ`@N!I_DalIT?z)C--DbKqmBI9oBy#UeQ^*{Xl(O1stDYeQkKo3Iq)!dgg0`1sZ z%yl69_v=!oKN*|uz`m34GH=~Q z_zhYhPY(4M_dOnWz6qzxy0x^idvV|A7sb5ZkNp4y^Th+X7e)E%!9mip*2F`}n^K4T zLiAY7mA&iahs|;f(|yU$5#%4``WVQ&_s7k!j0?FgVvlO-dlLDl;55j)RA(^H!a3re zhYNnuOj#GPUxLeU1^=sX4X(otzvz^ucU>w5^+BHXn7C>5nS7_hFP1LfySqiY^32_a zJIIl8zDs;LV{;GlK3MPV%QG9B_%8K;dj8NajxO(Vgi?kwzLBz&xxdJg3jGN4F>#;3 zQ;>3ehWQ-#2zy-gr%mNr<{?twUefQr%`Zdf>NPsOfw#m<&X*!2-aEp{ zdd_>y54g*{`-uG$eD+JgJ>WbEalUf>4Zg!)@WU@5`m-iW9OX=8mdQvhF>X8;PTuiy zCNbS^@Z|}5P2F(vYe=Q|COMLkr z0{hIQ6M}yzW*9^zjC|EXGsC1NoRwBI{Gt<1$|r_14SD*f42dss(^4<3ID;8~$rmxa z*D+21^pP@SA~P1q>S}CfhRASnaEs{-j|^9Wx*Hd_co5&2F*2Nt=^1r#ZS@=0_&+nb zvf9-IgimOunT5I`I_WAA*K&qFF?ku!bGeLhhpan=@j;O!kQD!9&a8TJ;-Uv&hKIF+dn%MK3 zI_7m2FwK0^>eZ^-|~} zZlwul`IT~(G2Kc#%VIK?cb3ECT{mZW(>!OdfXVj^ofR>q{itM`<(-vH8FBnHRl!-s z<5tx)D>|#0W*ooj9a%3GtH{b z`kpWi%rMo+*A2c_BH=B2BaeS$kJ-dyHuadzJZ5u`*}`MC^q8$YW^0crPpv=Y<*!Pa zY03Fzd2?A))t%z5sT#P~bhh(^Z|^bXM4mTZ$KULo@E7;ari?>uw$~#55zN}oE@rq= z$i2Z=P$XO_XE%>2UqkV>89h8QdSYs-j6JJdl z#Is$Ox)H$|w~YDf@eJ072Gp&Fn2n$@_9mE3p&9n(&;nXg&ssriXoC#d+iZ)y9e(Yh z19Zf_6J}@V;*>EqV>{fsL3iAG5WXj7FX)ZE56D`3U#|N>e`gY7ye>CP87z^WIJYgrmM3@AVMHl2x!9EqH!E~4b zGhr6YhB+`7=D~be01IIeEQTep6qdnqSOF_x6|8oi#(nf4Wuvc zZEeJqb>>aDZN|>%lQkB3H;Qk%;IDGcYd35kiOkH zSl@vRIjgyoc)N(do9jK8dto2!hXdgK-pqK?nGcJRT7kjLLE;_4|1caO?oqfyT`Eue zcFfsdKTbYQKvnW`k}#*>G@K#cSvUvh;R0NQOK=&kIEUz033m;yI|t}DoZdX-R5zXS zzOh5y!tXZRLH=FmP};_!w2eb)<*as5%Hcd;?Xg2B{M~&#<737v#FcQ}`La(eM&b9- z{{cLN1o%I~|1owMcRg`(7TqcPcrn#8XH50nIb46?ysKWqE8Jf@V`*>TE!XdyJJoyV z2>k?6^OF$ey#hOm{sLH6wZxwkDM=OqE0HyovZC5$|G4#wf% zi2EJ>LMI;|IX@n*@-lDz;S5(lLAk6ss(d$4b7fMxOXip|r;+c9CRcqJiy5vkb)Wtr zK6{5Y?9rGd*)e^E?%v0LBrV1CciR2W3XJsL!Z7!VWWyRNbD zi;cZ6_hK@{L4I6_hnt+Gh>w|oFmmSL9(%0`xo(S@2)D#Ahw}#Q$#)X+nG}+_`s&GD z1N9WHoGPVjyOs*K)UMHb8rK*-EppSj`lHS!p!WF`PxW*fg`h)#WhSX z$QPg75DM}QB01A9d6sn{>wPI}>|^cm(D$R4+)snD8EzkE)&seo$@o*sCYwv<&Ew5+ zgPb>xaE;R+sl?{mjC|iMyLnG1Fcy)0ds*X_Jr>S*Qh#JDlEXC-nU>!l)VZAGB^P|; z?P2-8OKw+FzLYHQ5_8sw{B)-@uUGjA*)`74$V&A(UaSK6VC}M`6%J_6D z`JHO!SH5%T-9M4FHPNT2YZ@}95oVev4DXJcd!%x&_4j{UeFu*KwtEf?*vqYe%SjXcL2J|UO{!r-uumf zfrK3dgJB2^g+E~!42KaQHAePIMq(d?ISO+$jDfMp9*5imq%$7-1l-;dej?^1m<&^3 zDog|E550ZkbgpL*XD0FA@l4I)dN#~~xiAmr!va_ci(oMG}blubzDA?I8Y#T(kAVaD+6E!ZFuU z?YQf%Isqq1`;_agI*lpcZOuWSDrY0)n*#EU&@-fSmVC%NrspuF9?ShakNpB%gi9db z_`Zz3SKunTO8h8FFUn zF7InTBhBaV0$#!^c#VGYtcwh3=a16%Cm;_p_jx0JET9}`e*TuQ@3@wCqu#r`<-35L z1@C&amCh$!&TPpUEk@~%U+)za;@s3rV-4Dw2C!h>3VzPD* z2H3#Pf=h&bG3kuq%XQ(($3I*-_@Ji9^rquW7(b9b4<~V4;12=dhCm2{C=d)G5DH-s z6{10OhygJn7Q}`)5EtS>d`JKZArT~oB>pay)PIQ{z&%ZdJ-I*gaYzZNAT^|cw2%(c zLk9GeHL!61rL_N6oymxMCddp~AS+};egtNA$N_TZCMRYt$PIZQFXV&#q+L&2#yS{% z1@Q_(At;Pr5z5LM56kz>q#a(){ajAnTu$ATIwbFWSnstIC44ck-Vfn4IrcTIX|3d) zkamosO8Bp!&s{;>6`r`P>l3CFc}lM(XRpLct2EbTpe)4ELUdX4D2H9vLds)`KC%W@ z0eeNLgpQS=3OZNix*Al68X#rWlJ}-+Vy^|Y{U@k8{u9+7{*zQ)|COwF%37kV7wD|1 zGl${%_(>g;`d1Hm_0g{Z@ft!SXberDDZHdUH}hYmS2ATR(VKHmS`c2&cv$O@%-^_f z1+AeCv^C3s1 z&2=B>>t9Os^Ov;?&NOf>=T!%g&OjIhg9$$bb0~8DgkdloM!-nIkHT*>jKMyZu;Xwa zkDcOB6L6mhlenG?Q(!7gBg}M|0WL~sZ;81SbD4i@wcLL-^>H#xHX=((i$tu zcjShf-zQvajsw>a?>#4_LuG80|&pKp9*t@Frq`iUXVI$X@aF^$1 zGv*fZvDHj(tzM9?fx7uB=r&}^*l9cF4%i912z!J5f!+Qs)gFjSU$d9E`*80>8S-Ue zByz?9S^io}b-=%$I_STN=R)qitX?o5M2`1654yIQwNNXa!?+)Tql7<(j>nOE0y*80 zdJ_4kpeb$VX@7YiQ})=x)fxXStlx>QR$k6x#z%v5#66Gw0_H`y1ecK|-^jY+zm+nQ zINLntcFNF_A${ZxzIOWSTH@^d$Mvp%T<<1DiEll-@@IC!~MF8tb6|Z(6zO?@4sJUZudW88H^9kltc!vEs<_l!Mgjety-r)8Yzjv7L;RAexPw*MO zz*qkR`ZxcBwB<^DCyumHe_{TB>C8>#1lLc(@j{f+KnDYCU=KKC#*_S~rH=aG<^W&t z11CaVP@nwxa~%L~2n;w(y|J0!F9-_YoM-@R-4Fty0Y~)me2F$JV7DGMU<-5LZF;nT z?RxZp9eRv_oqEiGU3#p5t$J+a#DTcTj|cJLBswR+ObCe}F(e^OQqo9H!D!8r+MTT-PGZZJ#}=Hf3BV;HbGr^apNrp&oJN{?^Cd02)Fgu<@A@cw1zh5)0Q~xpgla{i@+TMPU)GECGtAr z-w8SswhMHHZqOZi;NBB@L2s`6Kwszw{b2wMgh4QvFhgJ{_CH}5_TiW#U?hx!(J%(a z!Z;WY6JR1tg2^xirV=*?W3y?P(_sc~IqBPGVxI-GVNO6UZ7$3sPxJAYZ&xe`I86`5 zoC7z$>OdJ;W0^%6c9%F4Hz#reC~Fzj#@{5^zPoYSvp>=a5nJRs61+ ze%Asn>(}w?N8cjl$5+Q_zkk>5I`b6TebVBuely^vekyjriZ;yn3$3fKZS!w#xCc{ukx> zgM0il;Dz3iJYFQOe7s)+9Sk;cZE(tz%Wu*?f(}{Ta(?P7;|h6iBXQpl)$|t?BlDcrO>A|{_@)+itdsgL1D6^`JiT8bCv61dZLLR1>#m zjH6yO<@$$BH=1!cvAO#vI!XI)umV9pik@?sTePGsTe)q9tn0SMtqrt=cEoKD9o%-q z7r&0Ub%M^&1-jx_kTZR9HlUl^hY<*UD*7Os)lm9Y!t^AJ%msR3FABY(kK5M_t5HvN zr=L95s;_&W>POiA_zwX2X3ap%K`uY$H~4mmQN{jAA4R;;?f_)Sb!#=o?f%Uk=m|$(PPn;# zLF{@;xG0hC?AGvHk7Jc^ zEvAg4)=>u8m@ln&=i$4(|Moj}3DEi9{FpCMC;hdA)SrI~BmEuU(B+xm1e;+C$eAkX zgQOm8MW6T70{I=QZSI(AJJ&nhiHx-T;zG3lDVJ}LxR0w{=&;*8PTzxE={xsg?t}ff zAAl#Mage;G#(oG6<9EcJ1o=sMwk#dw%V0;{$&8@?nO+WcjJUFna|`XIjvc3*Payjw za_KpZg66M6{99Q_Jnz^|WJ%eiFxwOPHyr+c~62S8K{fAcYvGR(+~7tAKl^T`EU2c3M=zjc{BSHdHPIRU(h{;kKi!*hB^lX9FyCU+tk-pgdzzHt!2dR5X?Dq|3`Vr?}_l;UhZEq(v&?y zX=7p$KQ{5>KwO9i@gV^ughbGo`j;3p31yWOQ#m52UpdfA+BMeJ16f;#6p#{9AyfMD z)Pzj~X#;ce<@$fSk2wtaMy}|RjKn|mO0 z-@Me3eB9xD=#|gxcNeoqBj5VZK^&PUN9C7R-2a+S&MBIC;7e11VJa8)+>nR3c_Ck5 z0s8cRDl7YHn9#Qz`RqLNXobfZbi|l7-dkLG>gy=N?n!x&C717mxXv{|4rAz|HH2c_lz~9z@mgH zN;wv#9E%vfEXvDwiq7L#GO!r6v>1NH%rcfWJ9avFHjB}}O8;EKV`mL2uwtOx?b-|iPSatG_Do1&gBkgi# z+BuL}551&rbvH*0*7%_W&sKfh<=ssACAH&5Iio>fIin$Nji7O$uWAz5UghWBHs#(m zgXYj8P|8itZppi!%tesj3R**T7Kz(nwguVqYlm(_Fxz8xz%2o?IwGSJbjI!XZ*%Ns z%-99DuFws-Ll5W)y`VSrfxgfW`ojPi2!miS41uBWCk%t(Fak!xD3CpO*`ptgeGH6+ zaWEbxz(kk?lVJ)>g=sJyW(1a}T>q}KoW+*k>rh5VeArJRUmRRfv&idgh(`t8VlQvZ z3CzlQE30pl_a5gWZ#=cF1Hq+CtaHBfi{>{0n742ScpkFmBO{s9r4|r=AuPgeF)V?l z_}wAZWwu-1&r`8(|EVFPT0 zO|TiZz*g7>+hGUnM2B6l8}`6n*a!RJ07$za{dsxzR1OmU5FCah)UBg%j4;RH1jM5M z?#}#P-i7-0O-jzt5xy?P8)26|()$e-`M!+gQ`YXR^+SIx8S4cyH{o7WzSh|PpLJ8( zRO_75Dde7}9(U1_vv$!@o#EP=Z%Nr${pVTSZqdG-qimRwGe%_2PaRFeJvST`*{yP^ z^XPs7f2r#i34aML<8}o%+4H)}^)WoFIlh5j&d|F}TOPTS4I7b*%%}794OUD{!ACxetCS#JC#HmRfc^6OWjI|Ds zh_LeA^2C@)ASomZszqB;i}1Aw&l($U*6+edyxNQlL_b}npgf|}Hm4-reYm9x690!< z0KW2-5_!EY~ za2Nq2VH9#k!x-%8wT8w$@+o;73*(?M`5%ud* zU@pvq`LF;M2GutfVJ?OxuoRZTa##T?VHK=~HLw=e!Ft#L8(|Y{hApraw!wDT0XtzA z?1nwC7xuw^H~xhA;3HzQK3+3x2>) z;A8ko0}gd618iUiA8>##_`x*xq@9>9@P`0!Lm&h}6bOb82!$|+3eg}s#DJI(3t~eY zhzs!`J|uvIkO&e(5=aWkAUULfl#mKiGXPA3nHJJPddL9bkP$LLX2=3rAsa+McE|xa zAs6I^I}|}4%)F2f@pfXf}s!$E8 zLk*}2wV*cCfj^)w)Pwra02)FgXberDDKvxT&;nXQt0)odb20|TF3-by+5>B>-^lDA z&eOl+Ch8h03D$dbcQa_6}mxp=m9;U7xacc&=>ll zL;om^i~&&^8v~>84sVpEw1rJctErjR;3%?J(2PE>8E(x?w;|{&dYNaf*NUFYavvR~b!3<^_>D!c zahTot1xQX_ZVwU#GGlgV-C;Q9Q$OL0#jicOy|e7 zB@dVRB}sW%lX2Wt;x}f!@ES6%!ws%)QU`9qZMXw>;U3(F2k;Oc5$-YJpJ1kC zto9VS&$xaLFW@D-!tZsIw#FO$-eQ-vs&|<0;RF63;S+p@FYpz|%;C4oi;0{L4;EqPF;7&&F;PysARhPAU z&WQ#aDlg;<)>M8d00o1ukbB1Y{QaPJ;TiG1^W4=O-+SLdKWl%%DjVlgcrv3vF9qk{6i>mO1H=U~mU(~Ay_b}3+ zQw`+Rgj!G=>cAgR7wSQMXb?PJYe@J;!99(}!M!~3IroEKQ)mXwp#`)A`7U`Y%+}Bb zWL={zX}5#+AnWNJFgrpg=nV23E?v;ID|91HciehFPv`}`aq9zpgU^}!>++ix{kZNA z17INWcm;?2o#fnr)#eT&-e4GlpR{X3vHuCfU^pblZ3N~>7=?_{Fb2lLI2aETU?NO{ z$uI?`!Zer;GpK(vi9ah?`U_eA?rq52QJ9VY9GDC92sa-VU|$G}U@?y94)~m~wV^8TD%y*F8B;vm0{{>;=j1M(+DS z?oJNw%|7(q5AA3tx6s*3`*i>r2jLJLh9hv4xX0i)VNV3hdOr0P_fsI>SU-*X88{m( z=f!%n9^8lLw;z39f8!i_%liZ833~x9!lmE=X5aTkWF) zw(P^)B77n8+<>x3Y`!crR%weh%Dy!4EelxWFF*zzuz=H-Y#Eg^V$xKyb)dBP1kDg@T;N z3B!yUB4;%?rxD^((L=@=d?yt>$C+cF2)@9RhjNR7jF=FWQ^moMll72TT*rnu5EtTs zw2ARC3lk?f=erVw$X5f#BX2zN#u*8bpD1L45x}p7go2y_8AczS7+oV+XG=odq}Y>r zWF-%oXrzFY=$0yE60#-{Z&IXg?f5lnUkC*m8%FS)r6x~lD7&<#3|TYC#Cmro!eok! zmxo`LzRmt}I^^az^&D)ZCtijSew92#&YoFq7{39?8r(C^q&zdvq=@{C$c;}vhtl`o zphWWUYtw1@g=!n{9v*4Py_5BoY4l;!aGPeP8-v|>f3wbqsZ6Am8L~jukQ*u+;pG{L zKyTSk%FcC;5ZOnZK|P* zK3DS2s*Go?_u}%i@lXH?LLo@Xvsf5&BIPb;D&#jzi*Q{Oia~KG0VO@&^A=UtupzQw#;f1 zXHs4>$?HtBysCuEVy$Zy*Rwq3NBcl|RU=+?bgF?IS=+41b*+%u#GOsp*=E? zSZhO8ANBtRns1E<>Prw7V@MRf%ajB(CmD!SfH)P1(|~g6K=U$yI1`AofMVG|F$Un? if*j#Rz_?fhG}P*GuL!3%E%4^$LT!9@|2L&0m;8}B0zyl=%0Gqs|G z9qgpCGPATav(lnd>u3My;{D(Cot+tWITu6S(Z`$j=6k*G`@Hv^c{4i`Fr?qGzWsta z&FR{sXAs^E6a?YM*ZWQc;GAquJdMD4@RZ2dNK#qKZB)PDQO;Om-mIM2;Y2XrNM_;* zXW@?I@PPvZ4h%Rj;J|#3^*|0z<>h-4h%Rj;J|#3^*|0z<>h-4h%Rj;J|#3^*|0z<>h-4h%Rj;J|B-kv|-M2*=HY1bxn0 z{s8}N(xkB%8aly9(ykRVq_=iYN1mQOrXD_eWAo;%^xCFxpV?D_x+YB;iIXNxG(vi7 z_l$hP>)p-QDlhO=z?}4VxFS_R6U)b>#*G{4GcsO6kk+l=g2E%>(DWJekWLIVDYY6$ zyBdFbZ3DvZOrUR1@bT$p%*r~8AZ^&V4TVmej-sch6yR0Lsi6De*e*y9&G;vMap1tg ze4G$TYu9c<@-*|k!CpxvXyPTFy?uf>X43Jv4Zet`#Bcmb~i66ohsUq4~YMWL>w9a1^ z-WtV3PCzj+(@|8|G!!{03awnW8Vv{>W|EH0kkFSK5J@lZ2t~0$?x;t*dZywibZ`2o z{zys}^y2a!a!QOGj20|NL9=Gf#^-`VP|PR~6xy%3R2+H#Ax${e4T<34f!NwMc~smv zt3&?2#7=0RaR}PFb}`zub1&MxYcJZgc_(S7lQC|95&EC>JxNWTtl@9P0Y*Zvt>2&2 zF<;sL3ucZ+qelm$(9lp6HliDf9PEnbhqgzIZn& zGd^$fMz5y&plx&f(ZYrE@YtV4)5M9PXj1>?C~`e@BDppy-`5d#-?S>^K;wc3ZKjZ0(D@L{8n zuaB+(2A*5gHGgPN_1vIwf*zNxVk2UptMR$TJyDZ-kNA`Iq{fY#>(@?eCuOecfsUql zBE!&@Xy%C4sE2!9lSXDmD3bmTSEPzylU-~G>{icsW|=qb`;1Lv(9vD1(6(*c(c)R- zQHzF;>Xk~4rg-F!^ewdimE@}ZsY6>Ex6NZ~e|Vix95Qrxe!@5pDSNFCB5CoYcC`QJ zPiS9J&jG@5gCIRHE(0sxx?z!tMix6n??Jc^#AtQF0}uH{hDOkl0h~6 zH-8@3@lV`2sb3SLvi}JN(w7dP7?=||1T|^mswZ?8H~Hja3=}p#37yUUe`faz+W&Ry z))#j!pPZkzZV{6B_$L^M9oZW7Y+oVgf71MLMS7sdznjY=dg%Yxw+^9m0Kq_7VlOn? z5JB2X`}ERNJ@WT&--N~obTHvM!L8w=Vt`izBcyYwJ@S`KY%4*T-;I<=&$eD1L%ynRLO{|9>flm1z6XfGWz^`m`3`X1uI^ci#H z{y&=3DL-Pua1;{~h~k3W@HtyEn`zqMW+-J`JG694C$uEm4=tS^i!$3F1mzl(9o)Ez zV&KTm<@v{tAIA6b!_W)sgHh(8WI3JN9VYLS`qNvcpgpP4G{uIGKnv$4pfzhYAwOMz z)A6U~ z`elqMZK(kJ55@S``kCCjH%@FLy|g1nJ_jT;rRjgnM*Ehm{iQ9Po*y-+c{Z-7sQs;V z1u2Is(gQ#KNrPd#`|;8I)aiXCD3_)GcgO3@$%f$w-&6DldG|s7P>z4-7inv_lqJ0< zuicim|H&HgoH@xTX=RdnE7C(X{>kq;5Z!gw#4_A&TO8D4jwpAX#c;pW2D^wi=+Ef`u%I-Q2gk2 z*;vf|bj?SaO*Y5)XMP}moi=ZJD*w=$sT2d&#=olnr}syg9vKX=*@Tw}xKFomY&WEb za{Md$jr60_{PC@%^!Z)!J>2f9ba<{8+MD2s=8kQP8rOToL}(Sf_v7wEH~t$pbrXB^ z3NRLxd@zkMc5MEOYx<(otNNfL3woh>Xy~w^R@1Oy zBhm1oqYI>IhB+v1_F`3nXG|+71|FvIuTNbRjn=PUDW}ybHOyjxZER)2An($fzVak@pD=4DgdGXy1cbF!o9Qz>a^?*nYkpjC)sx z;&>j4wylXr+i_C%y>kETt|+u_-CC<@@#1A@VWI)gBl8QS3B&uM2@?!*nie{|BHt5w zXvRP3|302|@b7g!(b{Ma6dlwVt()$NE@#Ffl=qCB^8YlWfB(FI-p}5FaS~xQZC*GL zEl(I%AkCjK3QZZ^Ury74x)J|Y^lQX$Y$K!xdi=L{t7B}@vK4CCstszVj2}_}1Gf4>9ps_zL7?t}g(*bG;s2X_3sxVRX*bm@Y8eSJ~;_U%#Iwrx?17OhaTW^T9-TcP|v zEHQx01J?RKX0!+9X^Gr0eq3E$F(1yGx}lb?tx<;#UT8x9wkizV2mSxhjDHUgk9;2= zALQ@vkMKU~)TuMZK}XvEjT^h74Qr!SbAYw}C-~{nqX+Wx@d`|J&m+-x;-T?T(t_IiPv-)~I(kkNd6vADZ#ss#PngfB*hy&YU?YAfPYm)yof$ z<=!YLI0SX*=!xEW`z3__{sf`Fvgtga9shA5U6Ed|M^RBx6hq$L-SIr)MN^wL9q{gmHXki0O^5@|D?}{4#oHK3Q65@EGOsi%DI5__pZe5XzoOBlsu_BP4VOLI?tGH zsx)b^CmOErfWij4QykcMhp5u$6=naS82{Ya^x3Yub?fT!+K&lRAKxw#!3F96X1M=P zF81Qd8|U3n>{w44D@eR3DtUKonSCGiJzy{#??8GW$3HaGR^F@q6Q=%NPny<7wMW}x zJEHwbU3l`uxhI;2&li+wWI#uxOx~SZX1lmFG}Wwm-#r8VFpYm(-LI^tbA(^VIwq3V zO>2*qhPOj0A#LO|A)sltU&p%mI7`jzTYX=&Qf5Wp561>VdML+#Ass~VfR8KlLdfsC zLyqx(-*uVY4GzZ=Avwms-Q8QE1vaBLwY$M~=C-DOt; z9pm4w?z}IBg=4EBImZ8e*@dMx+S>RB3^;P;)(71WF!mj}Wgc|+!r==C1{@f0V8DR^ z2L>D%aA3fJ0S5*g7;s>~fdK~w92jt5z<~h=1{@f0V8DR^2L>D%aA3fJ0S5*g7;s>~ zfdK~w92jt5z<~h=1{@f0V8DR^2L>D%aA3fJ0S5*g7;s>~fdK~w92jt5z<~h=1{@f0 zV8DR^2L>D%_)jq~V94+QeHB5taed#306`F(@!L+x3s3Pk&O8L+ai?l{pKR`5z7`vf z54g<#hUP+Tp^{0cB20ee^muYS%6saRo>4ugcK4Y~ib9`Wp`#Nb;}h`Tq=@+FxX`|l z2{RJoW`&N8i=Po15EB_YH8MUltbcrDao-R_ z`$UGk!TC8#SbXZ@-6iIP5NfS&3J@yn-n zv6GESsB{e3PiuL%wqc z=XT0^?fwW{LHeHL%8i_nv9mIAl*(4g$90u%IQa^$_$Xkf8s=C>sH+QtPZd07@jsY6 zU4$p_k@1vvB;IQ-xY0v+S1^%3nASz8LHD>mybo>C=9~;?=||5~JVMT_A@r6+?94qvcymjWYSeu1kGwfH5^sRW{JJeRSFY!?)XeTcc_Bt&&V`%hyH;W z-Dg~%A^n1du%Ld!`t}R59EZIp=y@fS9=^Owmxr&-jutyS;P3$VKqa9PxpE;JxyBQm zD#0ZTCn#N}=WK?RpV8|n=M^jmX-p$4En5dA?RA-@%;JKn596TmK;ru}&-rx=T~7e{ z4|i^fm&(|w^a;7vC&!|WK7)N~5-lq$;BzH)!F~N2whkuv9#r}LedCC@GE1WI1p=K)m(vZl0i#}2ZNq`O*cpcoYuMb01ecx?&idjF`XqonLF z2>bon8b`LC`AxvQO>n&+xYFc=*A}V@pW}UBoRjrRzUgtUC+9VU4>w<4c>p<_PR?Hc zy~#O3r`;YAgf`kbQXUqE7X{0(^^J^*NQy};g9O4_5E}_W0|Lq-w5R_tu*8&EWddR`4F|ix4zSC82D1z7C3Yo5%$#L6hiU9% zYnGNuunX(}yPStJyTD#z*R1%+Inj}G!^*=x=s&Ow>;gN$uC(dQF0hx{6`dRz6BZR6 zlNcFq@BSq6l^fUvc7R=h}B^>;iir2fM%yunXopu$S01B_b&yF?vcuSbStuOyrcr=sD## zlYw1e2iOI6gS}e2;v?>jT@ZJ)c0qiy#G$Y$N%3>a0b$??mJ@%u<6XG#cnt3o-=y;U zl8nl7Z-^Y*B#45`hwS+x;0Jo(cOJ~{Yl2+D2iV>uBnx@Zs`-Il<{!fEi*h>%KU^R! z65<4(Z5n<~FY^y&{2-U`SHX#FOR)ZQ7xtVK*ze5gfj5>N|fWxC;`S&!40(XL)keCe3xBNhJ45$u$}snfN9uy?09Z@zxM~i(I=k zWTQsxigPHF9Lglen+SBIpxnCBg30P}m=6Ri*EdQT%KfxTKdRbckOrx)gYi;|iL{?2 zTU#Y^mmIIZ*g9TVeRVtwRK+VcT`8IY(E|1l*@v=8Q`R0YZW$~I41hiCD;?Q~_7&X- zlUU<&OJ7F!7riDYea^1Ev+;HF9sj_u=HYt-1PA0glcheDgvym=fhiNtUGsAd z_|5CRb84>D*q0`8WwodKbg%n=eIN%KC)!AuDTyyE+tU%dNtuQLvYtVX5Wf+Y*D_A| zk-Dq3-oa_0p3JM^xE{u#g!c(u?vCv${JQ*K$?#hT%P4FkdQDgn$bBF06TKwCX4!}K z70el4EbD&%tI|y(=`K%KcCS^n17ts(Iq{#hWbp>+7Z+RvzrBY$f%(`%W08TM3F7#H z`P&wL0)J8CCnu-y`LCc(rGBQD;0NTE7k=FLI`Cuhyfy5=m}FFdAMjgo@G}duLfp^7 z`()gw-d4{)SF+UjK~txa?x(aQw_uXlW5OU-AJ_X+GWtia$7dpnvIS3|r@ZhKxWj=b zOFtAJPvE!W;E4bO&bP{|A2S^Df~6a+#uV%G0{E>s{g{n4z;l+*;e9fmwObJV;m?qW z6uc;Ck7YrG;OYh6?=)Jj<+)zbIfAWcY3)$PD|U`52tT;j*Jr?bG|%aI4`v2A+VvHX zORmA`lirF3x(@8?Ix9YIR%Co)bV(OnlB1d3kb@rZDcA+a3$x3f_aB`3`ZMIV?1JOA z>>}+*N{9@b8W|fGoq(5NK~Op6;4kQ3a)T6nsZ=>6IyN#mvh?@W@Fg={PXO6uQMtNO z0pAY{+e*)KiCz0~9o$zxf%gfo_WpWIWK`*m!t%m~;6HZUz;3V;>;@Iko_Mm>4wa?I z!-7h|R0s>PyA?hT&liw+jr1AYYDUYvV zK~6AYz-@u@1RMPvOzu;bVd|n6{1{&r{JHuQ7Y@fgv>zXYwb&iFj`$narU{L;ZFqox z{LIcngvj{L9vJkPT~{r!yZ-BS(p zO4*-Yj1+eTRny(s`z4=;EOU^B4(dp5YttUnmIbY3rS9EJRn2>M`{;?>qZd=MCYy}n&gx@) z34g}Vi=;+37GwogT8-BP@$&~$*Z7& zWcAkTYGV8K*k&`flh94YZK;Iq-->M}<+-?B^>JJNJ;?0;tA1nspsLNJSH3xm+iOiG z;VS+HD8n51_mHI5*#`9_C+7!57hX%H>!kQx6hGH=-j9{(LB$`FNPa(ud%|B3LIViD z6J9fB@~UD5#OAWwZBYpFc&=xfEXT>7l16=`yi3ksu-`nUZre=9J3iNJqtwa#qpT_4 zeG)fJSOv_Rw$6Rwex1FnF-&FibRN$^E+7u<0y?luOHb^w=d}XZVJio_zz(nr=)qo# zU14+ZwZi1+nCQe5AS#EP*hPM)0P{9cVGa3&4u?wt=5-|<#Bhk&5X0gA9>jLSquf{w z527*Lv#0V%3HB8+JUyy}F}#%L+a-7Cf`v~9I2*Icn;crK6Ksq54+Jao`1$@#-6 znSZ7?)+^gG`P*1&)!!?!On-kX1}`+|ASr2zH+O31b8VSORMsi?@xA5c7yJggZuaen zhS3gTv;Csp-qJ&R1DU<(+zFrP=gc$5zvk(u^nAHnB%BmccxFKa|99nAeqb z5W^v6Lkx#!d?2>V!uZbcfc|lDiTFYsUx$0z;W|9c#u~QvI-G0Onp5m@Nn?2G(8907 zLq4lmF+7mR?sUaIpU@pRv*8P)wE27$dL4Xm^l5?-0n{OMDrIbj^LP2`Ker{3;r@) zP-3~QI#QpTo{tc=n{2y@y<$Ex(#LRcj*l5%zzOV^aDV7u{JiwJ`~rV(%F4KAFXC7tJ-7HiiQo*rlbn#7eNVd~=B5Uo5fIQk1~e;jAMAc->-EebTO@7J+mCg@Qpl% zM{c~@Z4%yJ!((`L-0u1~hKDY*XAC#F;~3qD=*RY}VmPZ41c>3-esWIbh~eNE`8Q4Fq@}+H1|HWF&yZ?E-k$! zR)VGFn?no-JHRf`3iev;y0t!s6T{tCvEKyctz-F>mU;BL z0CKR)z+b136k63!S<>gw8fC zNMpGzI$QN>ZrgfF#*gjqjh|1*#Lp*WTbHTTTXrHL60_u10C3a{psVTrymK^*E@ZOI@dZ;& zQ)bvfo!mZ<5BdUAnLe;1FfwAQJ@tWn(07IV6YK~!#3jYxH@4uj<}z*IPmmA#GI;yI zjX0SF*CcC)tOi+v=8KiJ{Nwj z4R#ETo;o!q(zDDOK_AEmea=gnKfw;Buk0-ZeIOt7<;`aLz>ZOdq?wb;+&YUtK|bh9 z<^BXasJ^l{PwqdE5Bd!8tbJey!AseD&(c1S5BdTVm_D#$ApQi@BN zpf6_*(+76Q@u3X&D99WN;~nIKKA*WvAJ`$s2RrEl`JgY4`w{Gr* zJ6L=uqcVj*K|bia!u5e25Fg5_5&940gTBCdtbJey#0Pun1NopYgX;r3SbQj}HVZyM zKIqHc$Jz&W@c2+>h2T$+5BdTRGkstOiw|X1Cyzgn5Bf5=KCpwu2m9#*`Jm4^jr)`9 zt3Hd_5#`T3JIb^dHEV^=)MOzz#ZJ+fyHx5Bfs(Gksu(+`clp6xs*!L0>W- z4{6*ExqWuh2l8con^^n6juCM&k)tBwr$v(I7t63g`;xhQ(3i*0ufYzw{!+%a5i>y_ z$OnDU1h7NVzA|Yg`f{*vxVJ$T1+1-!Ipg}eU6FaAV*7Mp;Nbq(AlO1X)L!zXRBV#ZCg*t_#OA$lzy%F_uMkBUtqsq`I+Sv<(?bN zo7#Iw;9Yy)bGx#V&C?-E)$_IXo*U4CU0Qm}y)Ce`d~*`VO;`ok0d|2_u-9VOy}joK z^ER;uVm;VKa$-LYmjcY|qz>rdoCsnBoD*g6YXm||HhbT51NopY7tU#Utg`n#H;@nd zLUyoN2zJ=}o*T#qea=UjKCr{y_uN1}=yTu8^no4rzUKz=L0`@>rVs3}_dPd|5Bie1 zKfw-r-*W@`pwF3KqktXuzUKz=L7&e~=1;IgUQ;d86_@222;_slG>#XrLtaz0lRl6S z`f_>yfgSRis-5(Ke9-5zi}@4mU~8&nREFn8m_I>2=*!{r3)lf`s%6y(`anMDbLaCW z*a2&*_R8yQVhrRE)fqc-H%-aWc*!!Lv$OnBne2on3U~8)O^C!p$eR*4$Kfw-r-*W@`pwH(3 z9}m2p_P*x^@^q4mm#9OCOgH z`dm0(zz!B4%BV~kAGmzbm&)T&I=2JjLs>OKd;s~NFOT2TfObNBu$MlN5BgHMKCr{y z_uN1}=*#2&1Uu|~&kf{*KErM{-oXwQAIj=Y%lHHNpzjKge_)5b@410|&}UfB;}LJC zz3;h!e9(7=>j67he6XKCK|bh9<>L|Tu=hPTkPrIOd3*;u?0wG-DgB^1F?4%FmgFbhD z&lK!X+>H@LU)px$$Xdu(ve z4Zg_)_u{B?6@TzOH}aHl*}k)=xaXF!LcN9ysexs~?S7+#PQQkIZZ_RRkKGLuArloq-?~G&$ zvP14CK~f!16twr8v}KS3Eb8OC78^yE*BfQk${a}F3HO?B8^05ijAD3?>GXXuiBt4h zFaB?~U*mr4eJzI9MrR4;Exo=vAFU_0W#RX@Y`oj!>{qK}GKhxBXKrS+d}&AK+`C4J zl$U?rqj$HvMsWkKBjr_x9?i^uW?A;1&yx4T+G?BHM#9Hv*`DREN!*X^m$1zV*iKU4 z9Jl2pw*M@)nUw3`c1^%-X~gYscde3n=nrFb@q?qYiA=xv={jGp6fwhh#2G@rXqAsH zd3UF*c2CWx_cD`jCW`M)jWTc8?1$e^gV)=8t;kP|uf?u=ch3!fhDd;Uo7htr zjz~Mmy*HTOmGtoZ48(4T{+`I)>_H8wz(HkQlh*rhxJ zEpaR_;vMcGYwpeSGvJS+a%cYhj6w7K%)iex7vCMS%D2YJoisLp9Q}dz;@8QaWJM;; zpfSNRrve4Zi!+B38s(-flVi#{<-Q=?`hC8*ty(wTODF&6O(=gidzrg7nlFwz^el~y z^)I}XxuWYaznk?~EN^_Vm3X25kC}>;St! zE7)tX>)yq3@(y>Hw~0N9u?*(P&(FZTuB3w)4lx^IIDBInV!JGi@30uo-{J0~JzC7N z;(9!1i5e3i!Db9Ez#m2Bh6el@*g7mHV~b$I=+{y@ZzYD$ zeZQ-;Y{Gk4z2^S6F?`?DIb!pi)#eM=#?lzL+hvvRhbxKV`Ubzy81D3fx8IdJU-;eh zTSQ~>kVEaIja8n?T%5*Y_-iuF`Ej=}lvHJ=~T@KTi}J-u{A?vp*l38QSQV ztgGDqH~wxTzWvP8y3?ySJ7PEmHJnHBTyJeAQ_>VQ66||C#D!gtyX>u4?|+4PQyY_@ z_4bb8hB<7WPFt>?ueC88=)f*5y(LzHrRAGL3FF3<}0TI{;FF&yS?VvjZC!*#ej zpVyUi5W^v6Lku^(^9?=UgxC&kg&fCl@}>xOF;3sO2}eqhD`L1yvKkX0MH|BvT7jfQ zc}Zh*C;Aqb5Yb~DHIoG-TcB#p*E_vHP(Pb}DxRcQdb z4!_g2i|BK1f;lnXOk*&)4)?eb*t^5Y=V%NcfUm=IoAl91Ls<+TCRFY%44*HpbL)fY z@pV7G4u7}F(zBH+*Tpd%+DDEkH?@uIi1oUY*#7#ImB?V1MC8=25zs#u6rB9VcsV8 zD8@3FCtrudyso5!7!ENTVmQP)i0!g4zN3iYK|&Ah(PEYrF+6p131j%Zx(;73ijG-Z zE%Iqnv97~&Tv!ZG=Q+f1c;~q`h70&z=4WvX&mQ>S#_+x|bHrU~tIeKA`_b$0CC69m zUOt&5#vW%ee9NtxncWZW$g0inE7$(Fm$Ya?u=%<9EQXWo@Z4kmy^mCSfyK~X*=LV* z?4#R1n8k3j_c+fw;d zlDO?Ki{XtA_scwVEY0t1Ulzk({l>kJgTIyRj;KftW1s(Ij|}BNu9P_ z(z^Sb-|eM2V#d>{x+?o#W>1$y-OZf6{hHsDNOtdF$%iX-GukIfdv4vN>k=Es`AN~A zoX%?VL{B~O9qD7<&kx9TIO%`wM*0|%_L#lO#UCw7jSBxOtIKU4ViL@o+L#22?H$8? zwy@X@Ip_hefn9LCFuU&cH_Z*$ScosR@ks}<*=6;2s(1tKNe*UlC~ulN9>MXFbh? z>;AFDN6!kv#zw@7PKZyiCgI{Gok6?ZV|A%yDeukX*nczX~ z=guA;^h=ngymrSY>pd`w!B%JEF{lHnQrD*HK%&rjU{8LhaVc~1OHe$!CE z&n5h3qU>ibx43?Gd9GxB9*EzLPCR6vEwB%hL-G>(nfq1t^9(+3a9n2$;C|*g@iV@R zDd=bKlC?zkGnZRjKVR8VGC#BVc|=m|6cUZfx|PAt+^^v0yio3Ez_kbSa~{u$pR3|j z0M8}72Mm4&xyAK!+KSTnIXF5o(H?QomCqX_FBzV}ui)q8soc-p|7rfr&yW*8lXJ!b ze(sJdOR#~TL2hyVZ0mf?mTHD)d(9sE1DGC?m(0)LSMW2OH-i6@1DKy7Cw{JuQvpAh z@O}vR8RQn*&-+S)XZj2OgwbU_-*Dl6CV9#H41NVa11`Y-K6RO&At!!zw&v#&KK}`R z2D!!cbI3EL@$)cyd^!iW+Lim6y+|fKXdzX-PKwHvlU5?_i0iHEaooJTTr+ARVVsQJTk5b-T+@R z-qtd_0S;{`v&%aRm3aG&;jJjWw(KcbU$E_#e$yV)mWecN9VzF!;{)xOuHETe9{*mo z2_G|g!uP5f4gHIc6JC>4TX*2hrI8c8N$pMi?dnyE?^Pd3pXPlKf3JEi|6cWZ{G8TH z_#SWT!ODBQ3aNBgYa^+&UZ<4z0hZkae*?S;{sy=LYXo=p$J;dx-Z;JhZ-L7h-T;T% z7DCxB@2phd&F60U;VryYRr(AV?{_QX(0U(1TUlNtO&3APZiVn~%Q^k?IPc1I6JC<^ zh0Jf{m}P#eJf&K1L-w~>O?}UB-XisLbFI@U<-vZO^EI)&@s|Fj8gGCvz+3VrjyH}& z-iQL3UEW!$z+2$fUuaxU<9fl40*Z@WVcqW*%qa!gqAi?;V@t7eVZy6LAj{Mifzl6x@Bz}@O3d!#A zH2o_z-k@IrZ#iiUZ-7H+nKrk}JBtb4@O28=0#`A-fnKnK9WBjYTYW+5wa2t&qEA~V zm&rLC-8i;j1)QAEowCH!#<=Y2rJUc6#Fd1tj^ zehW-vcq>Y8K}&4a$oP;67#2vq`dFtgjpF2>Td%zN_=3#PwO`ZUFKB7bE-;GyYF5r_ zml{T&8yKgn>=$w!&ea6rt?6M|`mVQnjoHiUPF!rDyHRuZS>eN6{4AnLB>W2>t(?`F z^LL4uoORptL}vb4M!&21KbiYJG)g~=%B0UODluUXyeZ|y4_=%3OJ5(~k>mB{j<@uW z)OZ7Y0p4=%GQ0r}<<8RVl=WR)RGboTdl}w}(yK9}kfIt>jL;-nFlS+Xb+b|Iwd2Rk z1pn@5$G7po>knj8^Y7;GsH&}bM^%L89aZp-r_bF5U6OYXv+28Y6mK(=jiR`+QP$Hv zMpL}L^xLqk_RqEU+cAgXt>vW_Sx@gWN@rFyrN@<+um|3h^3?DB@_t}ldGo}) zQ2f6I$1&k`zo^C=;0y4UQG-2?2RO8)PktwHDc=`Eq%$HLTl0T?ZNB1Y}cz^@2-rZ$HV*AKSOYEVbmddy^ymq zO1#nIu-9!&N4Q!z-^O%H&x~4do6e`_Cq+w_dOX_QHTfcbmKy*5EBT@>`}QU8_i;Tb zhyL;1i#R?@>uVf)k(7saep1Z*Yn5+6%|?2A;7uvduUa$l11!6F>nF>7esjO6#vAl2 z;LUK2&u@I5BW<^~$?v2dB9!=930a;3Z-Kk`{KoZy9Wrf+chs0-?AG!2!OewYc=esV z-^dK}oAmupbbis!Zv=1iT2I%l{c{q<2bOpGvE6gUJf|fTUvFc)eeL>n){kQ@({d|} zw?&WK@!R^v9g4TWY4!cy|6;!Mc(@nETd(BLbx$JE!FGh;Hf~x86?ESL*uTM4WZ_uxRx3pSpeghoZ>Sp>oT>R8A{tBXp2b?k6 zBDPhpz+27^hBwe_sLbrJ#aeW|ZM<2oA(@oCkv+w#OPFb~?q!V%OeT^^@C37u^CdzVLc(qMP{b4;}R9AMHW$ z_WkKu()qR1&2jlAD&Kkev%2o4-qQNS22`HM8-ZC~En7*|AMZ=~m6!m$iKkA#kdNlm z`ShP#sa;4r?hM^Y*P9WldpbPz06xy*^S=lYIP27yfwghyTtK!3*+rOjJLNq-u`zyUivI- zoo;e^3yRMUP4}B?cj+$Oden{Lt#fj6*1oXTQrvllS0yF@Zx(A{&sH1ZIOHZ#DQ|G? zePYg_P0|KG?{ddmx(091uYk8eXMX<1=Q*%go5ME=2;u~Khek_LjIt7M`xxE;|6oTk zq}U)S*caql!P@o0s^uZ#mZ?=TzwCU=_ei+)^S4z~dr7Af{xn~_@qcuFN`&*X5s$~~pVeI#L*MFx@nsStzdw*QCNEH|5!jR-_y2dH zRkSK`&vD}LB9gt z@&txAz@d%K6yTc_7;hdPR_@Z+;#1Wt@D{k4;jJjWD*i$X*70Vo@3yTQCF6=L;Ohb{op4k{X0QT&HLT!O1kW! zw5-I0HJh1b1>2~#8|aksen?p^9^JfDdL_`m-0|lAtr~BDFTk5~9fmi+A+xvOU-nIs zvRs^EnF_q+Jf)7OU`IjC#jUZ`7o=W&Y;e~EvE8+uy7B93X5IK=z18c5LCd0a3%(7I zemvG#A{Jbm>Mw4)yVSh9dn1aE$5%9!>J4*|hEA?R%OeNX)Bk+9wch30397HZIa=rO z+$hQQ|C}iQve>E8AkVkV*FKs_@%MVv+vY0op4VNRI*J}2_VFIEPhhO{X5dE3ueF(Z zLEVnR>XhOecyoV@;SF%e+EDN>|0Ye@0H?rP;9-Wh zqVyKD#8!>1z9@*d*i-)E+DUuO4ZfPL8{NM;?Qd=W&KmfL?vSpR^n$)4Jzido8nK231*9}_GsI0olYt5+4(EQ#FL3qPL&nq zkGId%^Bdp`@OGst!yDjG+ZrgdZxZ2)(-Qt}1o&yLz+27{_522QsOU>nuAmK2K5 zzaDESd0e`jdCi5zQ|0-+b(@K8Mcd6_()UT1Y1yym+gbYtHJ6_JoLw)pnl@MO z_RA&Feajy>KAXho-ukSUT?UHn{*PTR=sk^+$>`lXBk5okY1N|*mu796-c0&SJ5+^}=f-;*+ieq#sy+u-!@s_{p&{g z4PNuI5{<*eo5uPSU&|XdHsAbTeW`nc!L+Qzggx-4l$SAJx%l}1o|4|&8Cm{#`&^AT zz!%`n=$c}l+3mFX!c)^EqOIm@ zEk&2X0a8Z&Kk4;KdB)S=qx;2QHZPIff*vb(yro}I;|=;1@RoX+ulE+Qz7OB5jh#wY zd`owSL5a839B*7N*g^WDbekG4M22PUO?_<7ccaC*!H;I@$F}qJ-dU6OzZP$47xhxp zr++pd+4BpHpRc915r2twliqy!3N4>l_;%Jey_!o0Zn1c}^NTt9PtRQv4Ri0(cq;C4 z&+=?MP|SO*8J)k@_#36%5AO6%9kPqY)1iZwX5F3IOnQAbi>FFV*kgWE%JZC`DvpX= zBGo$bNV((9{R=hTpkD!RA-^%a0S;~THGH#}++HXh*sx>;-U8P$ycMO_mOTaQ)tF+( ztv?^K1;^8KIG!dI5>GdOH(A>F{wKQgpAMn()A65j&9TYe(u~0@p3Yf$GHdH=b)=Qs zSv(z|F;D;Z@B5_W)zz{IUfLd8ApY~4Pgadt%_#q}w8>d7t{5f`+r{E(v#O2Foi5dv zvb(T&s>Fmn@TQb^ZS6`i`B1dv8sD`1@%DonZ-6hrTk_95p7MFFU_Y`N`T39_JKe9} z!0-mozkwaL^cP)k9dFk9?(u8m#8n=?FMd1uku&eaRipC{qqAsfx9gT}t;@To4_=r` zkGo!Jg72!B#AWCA(z|)PFHDt8J@$IF!E}V{Qp+DOo$1V`pYD)cG~SMsR@FSwZPJrp z(c_^{eq~Oco$0+8*Ax0r>%2gp4-q1#(z`VisiceuV9BY-Nmc3jyFq8wMf~RZ4}Rp zZ;*Lx%^u&6wzB8Xw0P@)an$v@{eCxpoFmM(s2UOl|7dC&m%d`Q!;!lYpr(sWl} zdY$qsF~MxH-X2RUV9z?8Qr_fmW2IGpugEg}{cX9=Z|PUm^BeRl;4SYnhBv?=v_hN9 z&xhCvZy{$H-av2eZDxm7Z}H0o{Z5{pu-rqnNa}ODqd3g#cfTgN?W8?B(`bKd@#gko zq^`;9&BU--dWyI8EfS^7hPyLQpW8v{XN3>d{rP&Jq#w@WW8W7yOG!Vs(?1dNJjI*4 z`&{X_PoK~oiVdUmgMyctU+{O6DjR!C#1fB}s{5Tv>?8fzxHXlp#DqQYrj++)gCo+S z&^YP0euK*$Z|>*ScmsR^-U7d3{R}v?^on|qJs(mQuwi+M^S3KcGrWObutTl2B&C9Q zv-F$xn6^yxsO#Ebyfwmjo207 z`hiZ_x{rSS+x%m_ffR4oU)JfK>pMvL!}uh{n-UWiy9uXqQ`^XnSg%vc^AC7R+EZ(T zbm4$d{&>5t#vAl2;O)wFhBv^WvUSRRcs`^|h*RS2C5AW93w9`VmTcd;->vnX>z+_C z<4D`A>{kw+b(!Tv`&)~*wx_+NzY`2PpSL>Db5PNNcqzbr|JeT z9xSz-_bBC8V#1ou%on!WsP&XdRLa}%+gxeFr>mt2gMKRa-`A&W<~Qh9z?=KG3~zu# zt>sXL=R@pKHyiqZ@Ei_We0g|+jyZNJSDzd6^^ub;VB=KAB$(0HfCo7>f7$-P@+ z^NMlo`H*@S60@@Ysw>qVGlJqLbg)5Jr_WI7o0X4IynOliQ`u<~qjias>f+-jvC)Ab zG4bqp^Po?wQTp-6YKZkG))k#EHKFu3lRb39Y7df{e!=3g5)<~on^N9~FGot#+ANk1 zy+*OiEjH?UoUSJ(j_*(LHuc?) zbU(j8P&!ljM>@aV+!HRve>B|OaMN{KR>nW;HWS;5wwtxUBr4@qxwBrnJ@lZo`C!BH z$D8K&Sf zxqpA=+ql{vR_C|GhMv-{#G|^owb=Q5$V@LOZgLgLu;Oouk1Ah#>dv1WB&Ghq&c6(g z#Ak14@tL@-wt?zf8&S=?a<8*g{l;jDx8=L%i><4-^6Ng(hw_K-s-o-WA0n;$g+0%t z#DqQYrj(b^D^{94`hev6eeH6`Tl%N!`3?FN@CMg~fJ0k7%2y&oZlce491uX*R!TF?hF5kkle(sc!wtmFWD_v_Zbv zZ(<)Q<>!xSJiOdwldk*64J6&qdU|~O?6ui}vloetDm{khLzDPnuXp@<4)>CJ|LINf zb@P)X@$0n3I-h=b=z8&(lgoAeK5Z{;Sl^ZMD=}dYyeZ}N>$O;NT`^P2$BQtEcZX!- zeR98nrqcPlpwH7a&m%#<0^V}_F}wi|ZRutAPwj}eV+?Oa>9u7~!TN%2w_Ka5kBQj{ zV%|hAUDw0_zxE%_w;E49YAllKRIQ$s^Va|H{dfF4dE~yVLznAG{{*x34P}9mNx5jY^FvMnUQzDS2$sT4dpL{{_-Qeaxaoh)2bx{+)qIhf* zv{~Bp++r#7$0Qpz6WfZmo3+3sD&^_Uu9PZ=u9Nz%UQ+(&Z{MinDfBDg&AAber+l8X zcwD{5f4@++2~LT(Q#_s)p;v81VI}K*L4G5w{k?Ph{d#f4qYb6ypEffGjh;^XTichX z9?LWDyk1WnJghCn%l_3bWgR@$UmEZkJKujXXOj4c>pb0=YF8brUHh%RRFY)RjG8;!3@OjsWyM2ht` zk|XNPBr4@yn=@PL`qxUy@av*-$6LDQeGY&xz*|mBhBv^W+HOl}L|jbdsEGJ!k%{%D+48E=Ca-T;RM zZD%#~`4ILscUvTd*SEnh4_4ysS%x=wzhw@$qi`XG9kqoa*7s^oTeKFcao!6=-|#NF zY00g8N$q$4bTnUFIZr&{!_N2rt?_&2GLP^50)5!~+|uW)^4sRJ(VVS6MBk4Tg83(5 z{;%gU{`Ak{{oc&oWY+(XNHNy-YAf^Wvu2AMf89doyJi#I#8E9a`1M|wZ5O;L<>?Q! z7r##aBr7s$M!Dn7{X7-!HuD;lzgZ5Syp1{=u2P==2Zr>VK*>|GI`?z7;zjM4P&9JRXmJcszRX z@x10}7xVJD^TitbcGBy!8(sr@dtBX>^{T%Sk3YP%t>+itpuA396Fx+f6>8Q&pSc<{G$~#-idh3-N0im3XeH8u1v?MeENsB=ai*P z_EdJ0XCLu!q+E%*0vr)%El2>1fLrFPqx+Z6Gj^r%ywuN*HQy!|uudm8^2{&fPMtI~ew^M$fiq+Yve z>nN|{{MgivuV0;3b22`zS9N||lJ~!d2d|Utd48;v2mANr{P+_!-T+^Kw=3QZZ-7I! zFD)hc{J6Ae)0`h)$yA>ogB=!)CEe5Fh453%lnQS*o#u!`)~++x*L9}vA9(uz*65s~ z6U8@-?EHA~#?F3~+I{7BsXIGAHl4hY^~0~vn}sIFXgvKI^N+{;F@B8S@Nq^~(3|Vb z`tR8JalhQ7&G~ zJ$*Zy^}Scq@q7!9=l*y+pXcMb&1I9{#ow3c-u<1OAD{homCm*8OtF79J3l64ZV4ZA zig_p9GNavfHZ@7c5W`P27v4iP*{u6lTYAneBKFjf`H|@_~*1Cy5{r!yZ>us9om9mxZ zr&Ww!&3US=^9!XU7|&3rEt7g>opK+419S0p-Qi7NdF5@0k_hFEbC)ryOk$eXNW9)G z2zNI`iFX2rpQ(ZANO?>9C&eFedzLh9q$hMgCSS_D`#ql1IvOecrPV_Bo5r2qcpjqi ze*GTjDVH*Z3;)vhO80KwRKLAeTgj!$YF|sciDc!bwvipNUgu2gGCkt`f7nh!*EjVs zY==qwtd1`|H~*EkOYdEk+y8;b@8VzoJd-)9IjmPH^_F~};Er!qoal-iD?vTUr_w5u zSmSa_yjEfo-#>p|hm#*Gb8a~^i92^R#J(oqKn)Eb{7(3n?@V4*3@hRXYrc>LlyC>+ z@m$X|sf8$M)JMv@lzwD09an+VpQZUDG+tS>GXGf0<-JF~<7dK3VBW;`&~dKSi&yvT zx*1a0Je|5qJztY~hHPLL(1BfAdScgzVc}B?3s^g?RB{>HJIrho}c??glqmJP>?zE7s z#c<_sh%NK7c5W`t+-Z$R+bSiEavx)u|MvfY=C}W;n&1An<-h%3_C!nRXB@+u)MGLH z&1G(xAOC~n^jjNg%$zfBZ?|R7u9jo?g@26VeoP~rJVeoYb$OHQja7O`Zg*GNC5AJ* zzVpKGE}-WOe+_*M+hGzbxBrNq;o^4};FuRGzq?@F^>a~17P5IdXN!8i*2Zw41G}{JmRJdv zmTwL*9P9wQKr7g5vFqN(aG1A=J&Lgm=FxK_62oC$SJFWYhnNj99GD@tLmBen0e1MJ zpW-?^d7T;)php|S%lcj3lE!f7NrlJo^iM0+b$D)V7P}2D>KJ}0&-3j2hW)-x=GD@J zZ%MBFR_=cp!>z|LYq5>hk@mcU+jBkgG29+f2ej~v7UY&^vMe-$a6F>g*&jS^NYn5) zh}HZ`?|{tW)Rlx*5I)ZH?2YZu#P(+^?NVwg$-ZLTJ7RcA99K+QmyIlj2PRjn7!LEM zHYP!{>>a}cH?n#93eQ20HiiQo*rlbn#7dA*zB$BjumkJ@tzfUku6rB9VcsV8D8@3F zC&zG@*Ohd^!vx_Du5dreuDcPJ;r=Bn%R)3e!9iYqUB^U6&UNjd6ceL4Sj^%wOsBQi zWye~2U7NFq<+*Fv8o&D%mIp5Vj2?HM&vLCQm-*~BkCyu4p_ufsD`o=y#4uu8-cDu* z-nF^`pG?R3+;=fPS)R<@gPcy$K5@l4(1E@T&L_(w#CS7SSMvcK@TGBo$?_N*=3_fx zC(r>O_*s_6`2?XUjs;G#oj?bCKqkwR^;!7baUJxZ6ZeaO_b2c}JGFKbdkH_#%ltbT z4nQvWS8Kn_&*_0beFx(QxxlY&2k-+u@Ed@i%LV&w@dLfgzlrGwxzKLezuYmP-@xgC zAI1U5g?8KG2YTQKzk^(`+Zz9VObp|#8t*^txYPO%LK14RGD`bN!IS=ld_If1Qyf#V>D=T`gWicm!$zAnjT zO}-PAQcY+f3o5Bpnem?NK)*mgK)=8|2>nHkq2uKl#dy){1^otECXlYjEl%NhJ;=*2 zUMgvh^CD!dUof04E9m*eI!NXxM5I#UCWNI(BlV3jmtdh7Ady(WAEw( zMQ#`4&S7~X;U_#NF&#PPjb*LJ{Dg;;U%_&TtPY%C7uMI`EBU$XgbSbO3IC}*w{)Ew z50w&+kCdkm4UFkqN@`qj7m2ETI)KPQpu_2#Uk*G^ry;(v8-)z`lJdtde!-QxqA z{_)AVo?lcBXjo-Gw!yJg1P}b$nY^n~y%o2P;3!nrL-cv|hWY3BwwNpb^n;mT1EhH$ zPw55wkdx~L<)%DGSJo5R>az6`8K3D`CY;xQh{-f6tTGZBFE1}EI<$IS{og-7L9-qihq}?MNlA z4ExfLJWEB+AIL`fZ$HlA`VX!L$uY9Qbs}6(!gaB&>s!J@;!iUiWys1#_39>kZA3go zzEwgq`o~axGM(QccmI)}m;T0b3#Iivh}Xae@jA~Mzy2ui3w}LFujPnbC|lP7wGdB> zcReUnsUeUylT^G-=`Sq@lVX8N@ug}d-J3G>3-kl@3uuS_qU9U6dB3FNaf7!TKa(=v zuM!Z4F$CA)^q7@DvHUvS${}1u<^YpOhL{t+PRBXz7bhW(m{jb|2V&!A8=W|bEe6l=Qqbo+FMIX?CzGU8RSsv#zR#)=@ z9q^^`^>A69%*S>Jeq8Y`&;egMU)PuAF>?I3n#xX~1HKTx9xlt{e8%c(KA;1>WUfz^ z$NBp3+W}xF&;ei0cIHo6p3JA#2Xw%f&ihZ6C-Z$kK>_-J4*2po4rO_A`_z0u2Yk8Q zFS0z&CkU%BiDlg$=w!aVtbMXP*-i}~rvtt`?iX1e=Q~+R%?EVAm&)bjf;^c|?N6Ws zzT|z}POevOp0GgWPoM)n7mjOLo~%#J2Xw#(^MEXm^Yy9Llb!-W{{bEFfnBmZUSX_m z;Uo7kV19vhcmtnrfFH)8c3cs?^galu2mV}sUjXC+zjmAeKhVqka36!q1-;twBJ*>4 z;5YDj666A(E&V_b{JFr-<-&N=&SSEEPA~IsL9n_|rN5KrZ-CJ3jzF(98VWxc|8w;5VE6oL=VV_uoJ+@Y%9IU2`AM!23TP z?65H}g8e`b`rY|`OppuuweuYC13mDkbNwI}=1p7U2k3#{o%cV;1%BuUZT|zk%+K2m za)IBb{Tl!0a({wcm~UcdQ&jr89IA-zE7C+DfzhMKP zKe$}5+ZI321AjWq-`ozsE%bvHe?SlXa2(_U{;c)ieoPGKszb*rWgAXyDaMrCA$w@vUP`=rCN6Ye=$2Pf5|$7FFqM!SGVN)R=t{$ z)0g=(r_DL)N60M_O1GEyJ3mk7d2yvczd%1gzW_h<7cE;~cfg0{_f@Vt{Kyx{NFCYW zz8b7Mz;SjI{#svm_^EPS^`BtT4M+>g-8hc5Bn8(UeyU7QoH=1$AJ#8gzOt6nK4$%r zah2u8l}GwTkBMR2!MX#CKU;E&T6ci{PL_V4_=EnwlEca>xu!Ap8;2A-_<0b>`8pHP z$DRi%-TEKFMpfMB+6~tKppQuz_6eSW*6aoA22;j@SPL{Spi$%l+*;!m>&v$s!8fA@U8_+1AE6 zlR};cVcQ)+Jr13FTkn^gpTDPg4Xkekr&!dLW#7@`A%|Jc#oNr%Km451LvD)@`UUy{ z`X%)Yvjh5z@rU8(bTVJJ$iije3@t1BWttL*7GmK#-?wp`^$T7uRrZU)=}YSC|7Y)8 z0P85K{x?mVhEkx2LU<^bhY$gIgAq%UG${n=i>8I3)o`2KG?ym1G54mViZ;T9hh2P`^*2fb#i_ih}QaBJw|H&-`|0PxdBv@1{`XZhJHP+nIA_ z&Tr1l&d%(^Qj>QV^&Y1GNmq-1KL}&Hm_zk>>gAVGFFQ?`H}e^eK)s+IP%lmQOTD1J zB-**2~Zzj&S(CR^1 zCm7rDk_vxiJW)vy=a&l7ct`n`iV>p^DZ@h43&$aUI~{A%*shm>Gk3pw>7@c={O}0v z+vUYL1LqmkA693o_y4}zU&7|5(?0567XarAtN$$5CH&qA-q=50Ob-k|-@~RHO9px~ zeYwP0i(B$(ig&!>jJ0dBslF`z?N9clGiw`D*$uf&@7fD9eH+#;?n?Evr~1~mp4FF1 zEz7j0y0U9q*YtFxd(zp?RQuXhnRS`mS}NSyc%>*wml!B9@O)*!?FEkSSU2FbKVMBK zmAk}1(F`2p`u;J;cjt_;Vs{e$#J_hOgTx59Uj6tfiLf4N+R_c&*`cvCQ?3n^5bCid!C% zxy0eWN1sz1q{5;eP%p?I^(C0IL!8 zW5SR*riEp&oKP>UD@;FYspCkqiB=%G_Aqn3Rr^W3G@U8o*ysM~ zUei?nL)LE4rYDyg$RTB6VO;r!Vlq`ldxZKz_N61e<41XWdWZW%<_Wxb zJPiv^;7`k~TC%)x$ttF)&%Mmk@zdeh@dDeQhLPUv7gxTqs9b|qT=Z|@>4D&3XT8DgRJQ@D9sBdRB zm+a~;wmA6}Mdcc_;$u&XKfNP(*hwqQ5Ilhw@YHg<@h9U~TGY3*gQlOdxlLUu zon%TJSDq)mqURbc()$fhe3YNBgg;#pOy(l+1YSIz?h>BBpB8O!-!;0*?M=Bi&r;*- zO80~{Z7u5Jxbn>r^9w7)m;WXHbhnD!pfE#^C&LSP8n|D00)JXm+qpiK=}zVPHtBTv za9sJbBjy)YjvEb6h=V_cU3r2h@B*HOcN(4yf6#?c+u4;~-qrxdO{L|C-4HE#=a#yfj=#(?d(W*g;j$V^Ke}Gvm)l_Wo+#nVtCRIKxV8o z*ac7E1w2hWAUuITq(`Xj>`nD`Wcs?3J#B@Z_u|U0jF=zFZHKchf8yilpTb6i;0e5d zr_qOnC-7(M=Y6r0&vd<+EOktMnYL6mTlfhtuKY^MHL)^ku0bmve@f1GI0~NLhyzv# zp1=!u8h%`O0)Li>yQ}?SU2;QVj${d?pW1ZC(n02Rwln@HDoE@C5#R3E7)$XL~x^+eHnJW;Y9%jfyMZ z7%4YhdcnPhrz2H#yTS~?6Lw<2mWdf{j1wwLB)kDRxNFGPj)h)-+F1LuPZDA z2&gcwyi!*b95wy8&l4@)(H3?)8F&IO9#4NV{S*Ak$;VR>g90jx&r@;oGchjMI_Z2T zJYD3n75wAzWOxBj6BEXtj9)dKF~99%IyYdGc0)JTb+tzy6p)g*@-s0m7|O%bYhk1n zf+z6e@ib5Rrz*qWxHBbB(N5}JK>X=-7C+eacrv_zr=75oF9zzcX9 z_`UE1{#uTly4^(`_9>pSQRQc1oO%Bwp6DS0+TIYP$VK1@ynv_SUl@Nfex*ehuUNjs z9u}<2^b|bCWL15e-uJXMuTKMwx1DD2GjC3~_wlc^I- zgX7AtE;3)cSo;~C-fthI{S^u`1W(|_7Ol7FXWmDX}zC zzP24tQM+S*!ZzFNti%B;^msD7fTx`QgIFT{#k+PLzoipeB3@r`>PaY=<#HD0Z%ReGUq$vS6UQy%GGbd%fOzRxbkux96evJ0JQV*JnK@; zb|SM6p1=!un)tQh$?&H|VQ0E$BRwgUYTc0TN%5=J+6r{6xbo*nzKQc9=4;z=WuA4Z z)pjDq6Lm5kEdegXJWqK4Hi#qAI^6dXk!V3C-4HETK*zDfj=z@p8C@3(;b^y*Y)Re zk?uTLEUPkNkrVIY+N@C07K)95dSC-A35S1dkvNyBP;M4nl9 zNvf@YnM~0XS6-K=7@TiBULT?iJb@SRH1;dgKN-K$qQ0HQjmYE5d*e)9un72QGalDx zP4fVIKofWZFW_n5ufh}f3kVVL?W6}@bII1IY{ivtBbp+#AmWaFS+vzbB>B=RpyvIlU7W3cGG;)W#A9Pv8YSjXCBz*!Yze_3bQf zL>^auCdT8N3{M@(xFrfR1W(`vJPrR%+8y}Qq9Jy+(!(2l$pXijapgUp5=$cF8nhyr z$Dhs$CUX&Z0xup<|2F44<5ybLw{v}8roZs}FQ6x`{Mkk2irgi9x#3BV3(gKEbCJiB z;RQUk%#n5n{PdARY z$y>B_s{A;vd}C3$2FtV1@TC3eoM18+fhX_+o<^S$p1_|L_3bow`WAjmw5uwvyt&^u z){W6eTF)bJp)f8uR~t(hJb@SRR5dO=xW1d8ho|$ju@+C@1w6Iv zE&UVt)1tCovckPq;j5S8?jJ8+ekSf8|EqN!Y|j@g)y7&pffw*Ju#fNr{xwH+&uyd_%BFXfSvx zR(>YdrF!%D(`!P?44%LXcxw4y;R*a{QP^4BRg2&5ij_Cx%vjegEqQoasEs8Ip1=!u zs`;((1pc%rc(SirS|bdi;>yp&bxU0yp8Wd|eXGF}cmYo>zZ0IopD!VMuoLT2QBQku z<;}WOtkYg~9-io9r;|4D1YW??&|gn>?-vJuT2R^9mE1&qR8O+I(A!7i${U`Ra=wY> zh39MAu^OA&THn-~~L5>?J&b zKP?)xvxi=*9jzaaE5AH)j@kmc?~vvTn5U$QIz?fI;0e5dr=fD;3H)hMuYWO*-?h;Y zrcY<6g)AIb-cnWcZoc`<>JW|K3A}hbjZ6Oo{^R?|b+{|ab z)jml3uTYqw$CKd&JXIfT`X}R8S`>Ch^&{wsE8h^&f03^p-frgU^nHfM*on+Ocmglr zsq@8#C&Ql>4cZxf`Zl)w49xo;Wq8`Cj5}6ghTsXjfT!`FOS=PqT2$GYO?IScx*%2P z?J91*JfaYPq}R4Zly4@1M16z|wE=|pHau~Zy?%FGNLug&Ucl4POT?eRpBD9avd5Vb z8ev@dnHXpOJ~y zOpW8puel(xEE)aw*+8LAH$3Tea(y2n3xOx_0-h=>#h-Q>{sI{oIUWy~cGs5a%JfBX zDezRRylHn0QS-I!__Y}qXr5kfCo=os3A}hb{nCubjbCX|*eUCG(cc6fS03;8O*9t& z67uin8D}1)jV0{yWOxBj<4+4u;7^MN?UZ-;wMH03#g(6l@i?BNhHW_C9gPE42%f+T zcxu{D+FhmL&k}KW<^ADH>o1azB)IE$QQBQxd0Dtynl9=l5M#`hWzIPoyhEaJQ-fVQ_~^hPvB3BYCGFgJsEnDKF||aUYDmB z{2|ki>*szB3{eK2zzcY){*|;l@TWza8*c-SzBD_P3`qI(B*-SH8ZuYy$;5D-Tcm1yTtBPv8YSjXY-hapPB7)U&gw@hm)w}n#d~gJp5ck-QFvT{&ECf`@C07K)6V}Dp1>c{BlPU#8xPxC*Jau_ zMS8_=TzS6lu$|r~MlTg}UkZzIMF}xqz&zR8OKf2F!4r4^Pffoz?aug>7WM3uiIr$C z{U>E{<#*+L!T#DX^&T=V0DoH4f4+BF<7qVYOD}k!sm1l1mJIY}`U)6O#g(UA z=?xE@S0a~5toHKP<(vw?#eA1UzsIjCL^@THr$^13|5;1kXD-Di}rnL-F01yA4wJT;XWe=>d* z5F%jP8U1eG`0_JxzwZ_^ei~B7g?=~Md_NexfTzkoh(Ccpr7Fl%x;I7NoYaqxYLNgA z@LYtv%s3}j8qvgxf@hrlVzlNNkMH3p@%G?P-~~KY|511Xf1ZHZvF+q%Cwkg-im7p2 z`Q{?>)Frwt&-)AJ>4b#A6LmHfqfYM z1YW??&VLC{;Ln!f{eYdF$@ao$#zj+HdAbiHa-Q0T4znJ-Qbo*Bm?3xqFW{;3Nz*?W zztW<%o!nJqBR)kDSAMmdC*3*Q(^mM!k}~2c(?794+1D) zJG;|;eVItDF6fU%$rq8Qw&536Kh8X%y~$S+f~mn1cmYp4=SV*e{(@3LJG0rSqw)Ci z&Hd}lH(wIX^fEL4lw-kbVzA|bJUmq*ffa%$@B*Hy|6%$k<5!l5yKDPX;j87ombmi8 zjF7Kgw%%zAaEQ~A9{dm+ob;)ka^BvRq zIw4`^X(xrlO%v|(<+JJHhOci1P8F<0Jp0xxP7+$(mV%3=FFu#0wJAMAmBqpueGU^lW~ zwxqdGB$?a$OBG}`y+Q8T1*XnU$p?)Ho9sBBl*Tom$}i4gX|9@y7% zn%D=sk(bqf7P%4+O^(-pJ}dQS?16nK1?&b+w(*^Y?)ibL4@Zj&pRM|9q2i5Q`EU<^ zA99X4zv~kBr_D@YE5328Q%sF<8OQ#zfbtmMN5<_xBr*2Ui47lc^UtRR*9Q6Gch}>y zX!OG4mszfrjETaqVrTmuuPc#{;kZmwTLf8YE(dA3WC`cg;2RGW4+EfW38Z zY!v)py@XFH97sJ0Y&I8M?)_VjJwdWN>m7v7V6Ef+*oN|Qs)yP0oWZ#VxxY!OLJzS1 z$7vuphlKUUn%D!_2Y3@K`@7>`)C=kX^-^`8)C=lM%HG>Rtlj6^!T2y2|BMbkX(Ha9 zXMp>})bUg=PTl3iZJ9IIp?h0qSCGbc%;KLBoF-uXokaZad62C((K5$D)RS%vszkdH zM7^LMP%q;TNWGxGq{v(kz1#ibGroNXW4m6~!zulQ?jGtTPkd;-be28oa;;Q_!m0F& zmI~)^BU!lC(5!a(LDUQC0rfKSQ>ho!mlWg&jS@C34#!g$_bM~N=DtVfvG0?v#>Ec8 z(0*)7#mNr;rr)@&azAzlHHfhGB3fp9{a9gIr_Xm+r9i!)9#AjH5A`MZQEDG)s~_Wf zy2QnsiSsS=qyESFDsjUb>H+s9GPdI-75>Wkh3B$xeyJdhca(3b7%}>gGAu;Ba2)ct z)3K(hupPHM>~p>JQb8~t;(K50ZV2NhoM%vfn1dgEt&FRRwS~u=ym2{RObd_D_pm8P zeOntnnP05->E#;ESi2@mw@uLB{$yV|v$ipn-H^-lu3g>Pm${VYPS;+T?Aox_=}V>P zR)~ zQkN#q%5>4h(bUQQQ)BrI;kaOK{EGB&^(eg&RL%!twtS++sq}CVv;3YZ<<0Zl8S`D&AxAEfM1 zERlKt!X9^y$(5x!CWMo&&F0e_Q$xi;?k}&(Ktw=)Acmf~Bu(|iAEreZ+gr_vRqh%ZD!NwV z4cE|u@E1Jc+Ke$@oAtN}_*#tQF1xyk!|#0i+w=mf1 z;}6~Th;ZaX(yw)18Q#B*+$sH9%RRyV4RUBl;qreo?FMt`H!3`JCB4Qtg4@3heuQMH ze?u7Bzbz~~$l>4g8~8euxMIHa##$Pvrc4ngX&pL7+R?-j(!U|pdZOa0$E03Ii)crK zR9Msl>IM0uz64)OT;94JagM|FGO~j)KQqkF>vR9L!WfTwK>r4ecwZ0i$=uTor?-QtH1{jA%+AROqBwYObW&aunax%n~A3n&xhS4|`{ z&#UMU{{~GtI9-Aso&-MH#7p_goT@kf!qvAQ9pKgv%=c2-_$4lKym*%9_{xrlTzP&z z(@u~0rca1|;IS2dbklpd{D}bG`SjHBW7DM9^27H}t#9Hp!SYS~^bb>(XXJ}2{jHA& z(obDJT8mZX8F*06PjosX{-{1Z%xv0NR)78YauqM@27de;6-V72ZeT1q$!0@QW1QPvI9U%pPmov%kUzC|srRfeIg_@WBcnqVP)h@RWjQ>|qzU9Zk_CC5J7(Z}*Fn&7yEu%sCmiI-GpWHu8 zm)~<6Pv`kSe@r9RK>s~mJk`f)xu+cd)bxn$Pt(hfRBaFV-)M`?cYb+(9*+GN{XakV zWn&(S*_f_d73Nr9em0H%f|&bfJD&Sz8*~3`WA2}A%>A>CW!?zoolP@I!FcYU?eyF~ z+nD=j8*~3`WA2}A%>A>Cxqr4X_s=%w{@KRdKiio5XB*3i!ncR}XFHzzXB%_>Y-8@9 zZOr|%jk$leG560l=Kk5n+&|lx`)3;`6h2I0?w{@S+&|lx`)3<-|7>IKpKZ+jvyHib zwlVk5Hs=1>$K3z(xHssoZU}J6%Hs1Dj`$IE!#$1+~`v5eP!EaP<_%Xr<#GG6zwjMsfE<8>d)c-_Y`UiYz#*L^JG zbsx)k-N!Os_pyxEeJta3AIo^%$J`!lzPLTunA?MmxjopJ+k=g{J=mDrgO6!?DS#`T z@p3zUZ-?89pMGzL+l!64z1W!Bi;pXv-v;#Y^KQ1k>-=@Rv>(5HkhrUV;sbJzGT&Ro z`fz%vFl&XAAWi{fB0C=A3o;xV#~|} z_*l*#!0_MEo5g>5-H-EwKLaD)OaDPh4?OUxAg=msP@j$;yFM7N(<9!~|KXti;dce` z=oLYI!FXz~?+vD}x+$1m$B(^F#hdgfPs^u+`bOUwOpkc(dt5%-;)%4v zD}#7oy~3>un`c6xe`2kQZx7-LqYvvEq`}_<;S=rU)m$^MIKZ8W$jybYkRa13c&&5Pe=N+;>Rr3}JcfK`zy{hxO zvQ9PpwcvUcu`xvco}5`)FzZ!=?Lq98JpZ2$nDq)-uVPtVuQFlideznC2RZzk zezWI|P7pXZx)f-;_{8;LDU_!z|HL*VpQoW)cP%k5I z4%V~iOEfrjv+whD6COOn#aGe6t@Pvds$&iRSJFOjSg*QCpz~XhW3UNoDOAsVY;}G& zi+{m{VTkL6e}|AQVb9`}xmx&{4@yAWR%e~@a?v(=y3~sx|L@43%{x6N%sVSy{}Z@=aDa%T73brdQp^2am$JOAR(ONHfl!8uDVJf)G}(w58cM93mI_N7b}X*^ zO3E*>nzIhmKK(IX-?@_B?Q;c%ua&BrQV2YO7x2_%>I?j7CECtTdg?vXw@D|7k5j+`Eb9Beir@ic&GHc z=#Qny3mP2f*E>-B-*hZ!c-?FNyqz)nUEas*I&{AajCf9KZPM@3g^>TO-BxFSG!Iyb z^6jGEy_FIi96}V0gek^q7MV)t25^0wQZ50&5ITyFFsVOYpx zU;eTGhL0PVjM?LEfk@;N3OEI6GgxTwT7d{4A^R zj4kN29J_1kWvuM|?)Y!wpM{OHn?Ch*H$B(O5S0-4yHr^Aqo!BZF0#wb`(ej2FM9i( zCfy)~<~LZo#{rkse*N0aYr1PscJ;jFoRb$_z3!7=d$97dyE*+g@BGq9SN&;o?Gfdl z`5NOt^_^LJ&gYN2XaAPD+el6AJd$UbhaUOD$sF%omwC+z5=Vc11_ze~CjM*eq4 zoP78Ds&C%5&!*$*SpNR{g^Par`;*VO=3S@X!RcRr1oBlF%yPyf?^{wwc3 zar5D|i=H~-^EHec-?Z1F7yaG2`_}8`ox$?MJ4fo8@9L?&^r%0a;-%x)m|NO3l zUjOMj#(&)R`0dvvHa6__)F)j0Tgv~vl>fdaf7T&CPVG29e|mH6F+2YDnnUhf=<;>> z-{;=hlzr%~Uwr7TE+5yg{P&&jzWS}T>(dY2#p&O;@N=~X9eD5E|2p+O#Lr;uZO7Iw zdhyU#&UokI`d4v$`HLR->dSt$^=t2Jx!kqqWxv1cj*AXiwCGnKdgRp{U-jtai++B} zmTiCCdym*x>%ipw9mk$Y zeph=%_05YGY&!0q+OyWxBM#xyPdm;(?>x2kE1#^o`!l_lE&AW?YfQTutbKWJbFK63 zw=De4O>df1M#yvf9H9n@d5-T<1h-GjcOab(In61joD!l}`$D4rJKtWq2xCr`v@BX~sH0qx7Xw(bp0ri4{p}qv;T}bb4{MH5EzN`D|SJduv^Q^mvR^A(O zK7s~6yxMf0!I85cA7>B!*i_?pTfZK^+i|>p;^E?J}wC|q8F+b${Ew>V^ zGVis(doD)bEbqC%`!Ddli6Qg8i-DWWdoB$2H9F4c=-~^#ul~)H^{iQ+pY>;R2PePh zVxs!Mbr;j|p?}JAr`31K`?s3<%*QSp!c}$koQ}3-WPn&1>|u5Zp&bZ{5_i+ zrF`Z+7tV1C+ohkp&-UO~g)C20JdPwR8^(FdR8YtZE>%NF^ z%MQjn>Gxw3@sJ7cHzD40F{a`TGyy*wGFZ#sHkJ3X^S4cZ?2Ng;V9I=m_1{WA_G7)V zrY6>GA{g7-*dlo@a;VAFiwW&C#q5vnCG|1>lJX3epbzy$H@DwQPjD}$H>%u2zq+G3XVo6ErEziN z=bd|UUwX)W4uRVRKflfGg~J~i|G;s^h^OaV&)}c?otBjiXH#-_OJj#gbRH@TdbJ;^ z_qeT2RWGdR97{-Rp4dgtcUS*=mfKE-4vyYlDv2+gr~Ze^&UE5@WtkG#CD(jjZUe8E z_h3yfJXu6u$6Mvbv^Tb3zwM=CkV_Id%n!aF!0C6z#(CHq+E3H_mmUX*UAxa;)C=kX^)mV%GY&BIB_(d6^Zw55e(4#X<&FdV zWB#5V2QWsz=BL7*i~}kO$~a&ajRSVl*?On*4J4T+Wc~ThH>}CkY;*{oM&WZ9je0>n zpk4;vF7<-?dJc=Tf{p|K& zj{{hrJr02W$DY}Ij#GEYS@Rw`Vj+LG?OchNwSYd^Hk&jSIu77*`&8oqtx02)XLkv? zUdE{~IPd?6_U&^J#s@h6p#J!M(P106|;o_@rfp;qz5;Jv?GUBVKecGEc_!l_o4y5&B!y1L~#fgHkW3FVXj^2Iu0M zulu^MZMf%GH{Lh+zN7B*kNJE2O*!5$Pr;b`;~q4AHx_@w#wW*>Wo8{$_WIEDkn=;< ze;xhUkM+iy&a>tz7~5%fo-g$>cB<41LOV?{`_%_Xy^Jr2e%&=lwxAwRFKBP5uVTvd zY|3DKlCNH-o2RHgUFzl8Eb~-(p}n9UP%n@{eK}6zUV3`J5$$EgSG%@1cK>iO+Dn$| z_8s)I+e05=-q2nc+wpEBe@`}Kd_rSy8lSvFd9^DM`sX;(&2^KnURcvoOHykdI(667 zOZ6erPC92tZ!axm6V6ZTXrKO1yUm|A#qOB^?<3#U^9=gU5V-L6#vx|hfpG}f!uX`h ze76?w10ON(1;_VnksjawJfmFt?=t#s=(kM`+V2IQc;Z@lAD#&--zn#*>YKyH5%k?F zGyWLZ861Z|4(&HwzC(>eK1SCUPMtq*g3^bF=NJb(fTIU*P+!2iYiQR?*f@l}dYM&t z#`dW(BbFHF* z&#QyAKPCCoNah|&$H&)@uKy+Z8j@i=kMy!G$S=L}`*nAucii*VH(goB^1s`&uJ&6W z-{bCkKYsMxREE1B_}Y23SG;R(?Fkng!g94|{HXqGpFC!J?YsW(7u!h3HrC18=;sct zr*nJ#U*4UleQ?DwwRNW-PWd1G<*z?jyRG{t3m08>&Roj>=~KV=xXxoe!?3XZY<;P$;)_Gv^q{)KH}Pga=g&Oe?9izI&E{I$G8sv zw0~3W@{j&~+i{hz-By0Yr^!ze@9+e6oG0ter`f$jd~Oq8@D5RrJ=Z<< z$1i<;(bxs|FY27Vx9ih>3V)CLO@5Cg&Wm`Dq@N;uk3{|>-XqBuacoPavxWZ3dn9G< z?)mW^$>!zc6;$%-pUeFp><(;C^&^6-eMP9sDOXBZvr*=mhWaJ6WqE({c+`C(nNBtdvmYnuDDvlm zaoJj~nfkrQkn_?Rxz4R3=jkI|vL)Qv0mva8@*6VwdHRgh=D9&Rq=Vd;8P|IHJUQ7x zen^-+IZvOLUr-L|AlGT+Jbgy))QWC*5BVV-#tAgV9)7C* z&XYIkA>WDiY(mIuKY~2cLw@{&rav%x;Ya!MM|#M2en{k@7x_XD?DFzA=^7W-?_hpwqB}V5f#0{_JZHuoXjjOO!_73TpG%_;9!bodJ2)3J zGS582qgdIh=C5Cw4@H!jY%)i1mn=dUPTKxNf3fHPNJv$inf_gx`Aa~T4 zi=XUXLf3@%AkKO052l`+x(snXXn5!G0B?MZH>?}EF=rVO)rDKE_4p=THpzMGqa|b(7h%&np|Y zz6*?W>q(ytAgaEAJ~$IHX-cR~LKUwppw zZ>R^D=GG`zL^4ElL#^6uDv{QHpftjQME*p9RJN1l@Frs~?l{M?p}v(N2( z0r7?W@qNniimA)7c+Kk7E0#C&hk@HNJ-O6CE?Oz#%F|a{qi3p$_{cNQJj1?nq&gl3 z!4r4^Pn~-TPvB3>u3ECZamgyCsV~*jp6b)_)8W|iMdcbS)k4G5Q1Ec*c@R8-7x1)m zZ{Z32DOHYdXM4Ik)sszUdLn)CIj($TQMm@KxQM=Y%Kr2~@Nnpr5_keH;Hh$7;R*aH zRTfX#T(X<*SdO;&JQXLuER{>PCv(YYxdv0$8=i2Kh6C;v=s3+W@C07KQ}v64C-7&I zdx39fra#x)AF+3$v9Z@~i<4hbRIXm}-JPrGJJak>Hv|tm;LAP?p1=!u8d@zpfj?V@ z_kBA%`ckQEZc|rECz%q*l|QSfT!Tfr)bPYd`TN}9hfeX6Ht+;qz|;5=;R*a{!G=w3 zU8(FUw>Rb9JWGwQiS59bp1=!us%{dVz@HY>cCJrl zx>LEnO*&mZ99RDAi1~$;k%KP{*2>`nD`Wcs?3J#B@Z_u|U0jF_J;KjY6Do)CwAd`}#( zLhuA$z!R=B!Jj4K?uwm!rlXsXsAKBOw577y!cTZ{esRS7P;NVXOCFx~vJ;to@PzrfAJTCeqE5TPN2g7= znpLTZ0{3+&D_4+vOH^90PRL`f2qf^X4?c+pv zSy?sRxg^ZXJ*JX!V$bW`^&y0nH=6TCMndSz7jn4=NiF0sPuH?OOun~K^tkh4go6T3)`jBYx*M|zzI(@#oDh28V^?-UA zzDeo@^(Fc5)8N#db)R3BeHxzGEchxqxRrjq{&TG1|4Q2D4fCgr?RYoR|KrY|@;TYe zpE?z!@woD$9BD3Y7*qD7sNx433a{Dy!KXSz`4rlPzl2z>V@0Z2SO5*ktVul)j8aYW4!xr zXfMO29#AjUrXNIoi3LZ{dPA4hUI;s*J2>tZ`rS@HZVzuW{`yhc=MC+Ju^sP5`hRr2 z5R11vmzUk*^oIDqFD853=kt&2CA__`lG*gH0A*VFbAVAVs0Y*wih}wA-d#hSU-H#U zb@}@P6{7L(V%N|YzV40}xL$_J={;Zuf0qg<226eq>`#~OU8l=`$X>zcz|MVWsryXT z=lA{DqJO>a727^OxQ^R_GpoC!_8Wh><;?Vwhu!$IU;69YUU}=AYkza}!bKdv$AUxZ zuIgHM_my|l-Rsa8;438m@2(D#tviL||4h2RP4dT(4C7l!Z<2KV&dA?)`~mf+%>V5@ zx$~CPv3$!@e_6C`@N3(CeBHXcsSJ0YG550CeJ?wG(NACZ@FFV1qKeNyb=yDY&96J| zH4C><8MeLm3&+&mF#BJ3|M7-1>!}R&xBuyZMU9_bMR}e3WoPhRUpn#?!*_jg*?wpK z@+Ci~@~*vY=uZo`-@SF;#h<)-6kRP9^0%)9&V+djMP z{?~ke+Yh>rtB0O5SHAc)Z%%*y%zeIcOx=$UegEChJoCi8W@wHi?mb);pE|B~*=2v) zHv7Gw+;+{mk8hLqHCTJ(*I&EnN3Z(EqJ{f+O)4W4aQk$h12g0M7us`R)HS*NQDvq7 z9GKjz^!#`ZjO%YJ*?{Z63aWgre>`V}>qOkIjQfspUk$DcNyOWDT);lT_tPv`aB9UH zc+!G4`+9J!wa4w>5svoCxODX25_-z=?BIM2Ih?P@vSIRT8buB6r};(@=g9+yaeeDv zuU*WtbUldB?L(y>_3t-6wc;R$e}!$}>t3NLAS7D+>%qdc2Gl%j0QG`;K)p2m#?-H= z=Vz@zF*Dk3{8+1z`;DJ}*Mq1B)C<}h>PzfAwZU;3cK9|k-uFJnx6qHTJ9s;PmV~!S z;9YkBH<)-6jeyT3b^Kmv|30VNoyJ-GLz4XA;2gQ`$X73-g|9ngD@-fRhaD;Yi7SXPwoW;o12In-+%FH>r0RLry&7?n{|FR}d zgH2-`-(5nkmtpc>oX3Al1tU1^wqV*6yDkCOU$NTtbLR7DQF*~jl4^WBSo{A*7c|c5 zxtaRSsW&`LpXpnkX-}=DyH2J~uVV}0+Lqz^+DZp7zQcGAX)q4Nb7>g&;W;_13m`p* z^W0`2^FS39{Nys1sxmO%8$aULGXLK!@7=7b6kNT(gtor0eYAhbq5aqF86sbI0=0kM z%y{pf#X&5QdH=#5cf9Ay(s&PH=y-2Wn)~NpVOv1I5)m!_c&~7+O3||fQ7@`NbTW)9e&Zy^Dy6=_=t(PCXZ8pYwCD+T*2J>H+lvJ5gVP2dRDVHrUYr^3(4A z+SNa)&88l|>%wnbsm?c&O8?!H{F%4ljISJyZ zpu|9lff55H21*Q+7$`ANVxYu8iGdOWB?d|ilo%*6P-39OK#74u8Hh5Uc;J$o)jg>^ zBbk50edCq?l;^H+AME%(VfXXDu~9Uc`}v(eDx5zbj=^_I-23_6Z<^5k{0JxA&(Gf_ z>7{SQ@UO6q^*>7Y+Wy_#Z);157XRxcg=@97JbU}77t{mlrRoW(C)AgesF&_fo`0WT zI>L1?wXh!(w)ccNm+sGdO*D(Kh&4tN2%XR zS^XB(OI?SHGsO8I{rGv%PZ2k~p&szOC}TTbQsJ*WKg#c(mG=WU^!}^CicgmhR$OoN zA!S&Idf_M8TQ(-%9ci888;fIy+Tm?V#Wp^`p{sQM2)Sr1o*O|CQ&O^o9!cSy8 z^9+~c#q^?E=zG|dV?}SOCvi@9a(%?-FbgeD!x?MWWK(_FwHKy)cxP>6D!U<<>0P^h z>)OR#?rXf)HYPWw+gEpH`g+zDs8m#VeW~Mn={5gd$FJjF;{{@Q z$;=W1B?jU$K*OIydCbM*Am|#TojsgNdUHdWbE5iCLi2L^x-P|6KeF9@#+8LwzWPgo ztA8#b9SUsCs~S@s$^Ncf;Ak}P4oYs14_UhAhAZ51)R=D`kDG5TSAR^tRaV(6@3P*x zUP7IteuoFYKemvK$BYR|i2S)=T((a24QM(P*dXVnGjhX5&eP{fC1&$3ewG4~> zo<1YDd2Ub+=^!^`dC(9!}?OE98+L^3_|+x7Upxz-)Q8GUhoe+@N49c^pNlTgekw#i*o0aM|#MQ z8{VK7{-fI=@<)2eSAWXnZ_0uC&nJ)ckRQKA${i-Zl@jCpi+4+nZw#J?VL$qjeEk9E%ikWKOJ^BAv(245NNY0Oqw%|?{6&D# z`RnA}Qm>PDOYNL5bFX9Lh0PT)Z-b;3a+oU&m4EC*ja)A8-BQkz3QKIhzxbBAyB0P0 zNs{GnOPDZx4wr0Zy9?WFzO;mB@!u^~xK^#{*}w8JJx`dQSMSi@_Lv+k39{>%6<7ypb9 zUq|xxc5!t9)?cg*=7|qodwd723BMzcK1;Q`i#6e}en7O?Yr?yKJLK~g>H+mqxu4Vv z>PtA_dYHSz=Y#R>F1G7sJ)F`{=<&uodE!Is<%PZ`jCw%54D2iQg8CAQX|2)SuqONi zg(WuM->b|uap7yiTWCF)f75T=R=JYzd9+ zxZPo&>!p_pg7Jf16UI0L=NZ%=)`Y7+8!|qKVAyPTCe&XOhCZwbHzzly67^a7*s8le zo1kxs&gkl}wyOUk?ncTJwi3>7a z$Df>7w<%F~et$BT>g!4OXGJJmT0V~81*N1V2A;nRH0P3ix&B^nHMq1U{QRxUQprmU zJlhQLnlP^i^Mqu_m6C!{xkSooe^N zs29`&>IEf4eYtq>3v|Ek`QEiz72!jis ze@7r!R#Vl`O)-)Bi=p_uE1zfXkx@qMDs6Q*u&i`T4Py<&MYf1;=@)03k+)(Xv0(#4f;C?-=? z#7C$vWPdtR9S?)x3A})(ku|~-_|vkhmMm{vvWjWyb1yD+{B$_Bd{MauOSRDOG!#7S zWE5ryp1=!u>TDLCz@HZN?QBnXr+TvKOixy)nF`01Z!9X;V2LjJxA+r(>%`u8?n7_j z3A})(@neK1@TcW`JCi-Rbh0a*%%q9w=312Smx$VQ%jL@mx7&P zcw)pn&6dMXm>nKZh8OVEStC4wKcm76d^@w9$@WOD&WzVCwtnWOcKVGUs+VHK`Z9v;VJCY4W7UYc&a?x zv^(Qh!5jiSWwN=}-o8v*DjT7TvUn;=ekDmIRz}S=XvLqOmi`Gx;ZOZIV1?ibynv^j z7Z`sser1WcyS|;-9CZ~sdRiP;epxD)Y)|Hr(`Gb9f_m~N9zS6NPaAQ-3c(Y20Z$_h z!V~zjMBH89&P;!ugiTipA$HX;^J8@3OhRWV$C8c0Sc| zz_I1mU6N|cCFnA*h|EnPM(MhR=L(KQp-kALFhlSJUcgh+Il>e8)1oZLEOlqv(;aDg z@KYz42FI1B1;e89wTqRHr*1ou*#}SH1w3`0D?EWeEgJ9#o-Sz3xHYWP<->91^XZLp zJReUNhbRG0-~~K2yWn`b zztW<Dtsg*`ieaQk_pwClr@?QUxSP~)EpcQq7CmcmT-iZTN z2%f-;$5W&51pX`$ch|Sm%=`8xbDafp#0uidoB7_A(Q*x1QJsgUbTFBVJe~|M;A!L} z;R*a{QQywC{%kJO%`;D8l^C-4HE zDlalT8U8F0celA=$?_$9ftPJd^@N=uw1C2~<%`QUP>LS2es@9OsN<|rm?3xqFW_nD zXyFO`X;IJ4{+@K?Njg#%SAGp;TZ|9cc6@`z1w2o;#Xd;;nm3LHPv8YSH61TJfj_0n zw{t^k6RoziW>e9M?eSEk{CRG+iROse2F%!Kc&Z5;b({+oW(c0Z3wRo-7M{SL7WM3G z%XDS>T4}k3UfD$p!-e`*T=@phHnA*fwgEHVV0gko^iM4~V1?ibynv^juMnQVpC#h% zdUhuJ+tXnS@>)RQxbpSIWg95PS$TN6D3D43cmglrsrr?se=>fhMLj!vJ2z$1ZIQc* zxbkQB(M#WQQLh4mR`B{A?pNV?Uu>_%junC@@B*HOP86QNpC#h%dUp1u*QeWC*Jau_ zwO*R(>uT2t^WeDht2o=lV#+pgVT5c0rTH$63&4{d@j4rreeeWcz|+o?g(vW*MLj#S zX;)a>Av6ye!4r4^PnD;cc4z#`5^;AMHnnx7vdui&b4Qy|t2eHEbJPsA0fe{z zOWGYj-(~yL8$!~8C-4HEI_rfe@TWz^&K2(3fx9~8^2Woc91W+Y@78BVp2tsx<)_Qf z_%Y%s?7F2ZBr$jbFW_ltvG4@`w5YZ->RnKA<>TD<1Y0I7p4gwz?lvHS6@n-50-l;y z2~Xh95^;AgT(N3tqw7d=LusUw9chRwugg;m&bKZ_uXUTWfhX_+o@#E8`;Nh%7WC~b zYP!JUsW|zW=*PF3bt%{ee_DY9RtTQJ3wUbTDLjEcOT^ukcH<6R*sFT`QXA8$up2$J zpuutFS4Pb+q#Q$ecv=~f7CeC$@HFsE;R*a{QDtYgzpo=1<%YVr^372*)CLef#qe~d ziaTFnhTsXjfT!Vmg(vW*MU|b&?saLp2yV@^wRQFLP0~8O#&PBACBwvuLNn9_AdL09 zBWd3r7pw|N3!cCWcpAGycmjV~RN2{=+|;@;olUPxcNKQk6<2;$a#P}hC>d%42>*<( z@506f%^_*Q6LA5>yzDa;T&ffw*p^BLg@{Atl;^{bbxI;VcA%Ty6Vpt$lg zF@8EJ&$!@hZLGx;cmYqtA2rEjJNz)1?>ukvu$|8E+uKY}l3-ZmYEY`*n z22bDxJXKw9cryHHQE4|z$%~?o30PNL`I#6OCh^&{wsE8h^&f03^p-fr5RzAtQ{oyhEiC-4HE2JR7_z@HWk+SwZUCh*wuGjRX- zQHCe|Jg~mrW81+KcmYpi*9uSIZyKJm$&OTOvM*KWZP9MNZvW=`MQddFX38*8A0b0+ z0O7q2PaI{hb1n)=3!cCWcxw4C;R*a{QO_Ujab|=@7*~EK#+iT5bH6XY&`EFL3A})( zs*f6fGJd7y35@{Ot9TxTNFK`)_A6@1f`pS`=c((rVpI$ozR zL+}J%Jf1!+Jb^zg>iL6NHt$Yl!*1i(Vjhkw?EDTa9{Ct#q?|#HJTlp5ANJ zn@Z5#_7U^7?RZxno=&q9nSJmCUcl4nSA{3=r$u3B|Hf9+baaZTaa{Q|7tl@iQ64-B zDbDGJC;dFa=^<&s6LyB0K z+dkcSC@yzvrnk^H8N`)$dD4Z7h;KIi`2Q%mS1Qa9Jb@SRG;ycl$?&H|!|m)%X0wG& z0LPVIS#VCe&`&c97r_0ZHt9icVZWZ|^ zg&Be;@B*GjZ#3=B_>~sbJgqn58J#X4jw_GxQ$GEL9DnF3c@F3cCNcV_H-`v=C-4HE zYHkspz@HY?c6O!Lchc)d3x4fFT=}J(AAO)H(#s{35uY_Y?NB^jrqVG4Pv8YS4SdD; zlkqDps_iW9IUv8?MU|h4=YZxIo|@^|e7hfii#FEc3A})(mRp4<@TWyJPaWy5NH1QC zD^K4XiX)$PI)_|u|dCy&H?i+Yc7T=|)J53*x;f+joG z@XeVf-#GHRJjGyMZ{cs49I1p4SC}Dq0x#fc;+?`1_|u}`N#=c{vLyL3PsPf+^S-)J z5%J&Xx`lcAw30hQVTRxdynv^H_nQ96_>~p~PcB24`i-#a5?6jE)>|GmJi#`c@3eo} zJb@RFrw9|60T=}z#%-1g4s|-)alKu8pZ3D9pp1=!u8eL|1GW=;#*x8%xORn!r z_C{JXCuMQvR~DJCZO7|qoXP%lpM8+_U!gEV@C07KQ%g#C0)JW*cJiVWy@h6=u-Q3Z zOI&&GpXfC-k@NLS;5h$H<0s}xzn1A3JCWH3Pv8YS4fhI9;7^OfPFZg;>i`k@sJQYo zv5xR1<4>>+{rIssV1?ibynv_CHwsVS&k}KWVJFWJBd{)B?G;gz5>pG zapfsjdJ`+>mB?ihtG)bnIj6#JG1qsmROh?a5FzjcUcgh$D&YzIY0(VvadI|X?vM!vC#Jhct4HazM3Ru9w(ZJrD-9#7{B zPvB3B+B{`@Q*HfS$*|AHX)zDSm2WO0PcL(`gZKFH`Oe-BvVqz6{K@bFo+efpo(z9l zG+<{D-=d2vPxA#ue048qMF))wxZUaZ^Bf#Z<|6O}Ucgh!)rKd-pBA<4biX7K@hPvk z^6qye=%Ojr(-!qri=HV@nfY-&emcZU;qQPa@B*GDt`wfYpD!VMww>MSzP?PPRu}A_ zijprPPi@053{QOAUT=A+PDmI$ffw*J_I}|B{Ap2}r))OLodu#Pu6%R$^jBLc$(Th8OTuvr%{ge_GVGv+%o~d@XV1R}_4wm7k~Hd%%pxA5;+ug&Be;@B*HO z-)DF-{Ap3!PMYIx&qR1P9qWoK&*!_Sd1@POGd$rq{OK?putM+zUcgh!pzs9#ED?8? zC95GaGT`n~TiX zwqp;&({=Vi+Mlm5L+}J%z*Eaw;R*a{QP>&zK~AG6w)_k{&pBkSTl9BG_R_{$Jb@SR zG<>=61pc%r>|B?j7a>P}x<%@T+ikJ(iz(lDPq#SEo`xrVWxBUE*5V1gfTxyAjXxQ` z(xTvr?k-5RcBR+#CHo?ci{r}E{RJtyEjn_(wjF;l*DW}PetaJsutF{`uSm^yXBU~TU9=&?lfDjKU?(#B;0e5dr_r|?e=>fhMPX-p z1I=U7LoTi9TrwN>fjuo~<;(4^Sow2$C{uE&^Ju!6-XK`S7aAO=H;+H<8&YQQ1YW?? z@Fw92{Ap3}WbT72{DwotQ?c^qe!5sU9`g6fcwB%D?e0Z5V1?ibynv_CHwjPR&k}KW z!Bb#eD#9QtuKY}_OY!&0!g$(G8%r2Gffw*J)@S-B<5yY~cJk*oIyOZ=ZN-)6?`?E! zqLt1Vr#=2&Sr||D8WrpYPv8YSjb32<$@mr0BUE;}&jGde&`TQ%oH~drZ+Mc&ftDAZ zFUrp$f3GZzrxwH6e~Ox~ZO5HvzCgbx zWPdx6*#}SH1w2)4G5%!yN{cRDA)j??Giy>jUesx(!g1xN(p_j=zUw=yYxh)vC-4HE z#x4||z@H~zc04=ze#3ShJ3WpqPfxT-_Y}d2Rk0e(cwDb@;=E@k2T$MyJdL&rPvFm% z;eFWYeO*&0m(_1O4zOl%BZ9B@X^PRnp zP-!PJ`|v050-i=YjXxQ`(xR}_9%t$_Q{lMsGcnHmeV+O8Jwgf&p1=!un#c-I;7^Of zPIv9D2$za0@2=k!k*{q>otdY**c?RvJDs<+{CfL&WR$xbibGUvRM1Ke2uA zr@7i#!tf{X0-lf*`~f3`ow&Y>IxdbYZ^q-RH}ysu-lF(!_&L)*9T_<4IP(-{$UN<& zaN^*Jb$svZZ2Iwdbo8ZyIgP#9?`K%Ew3XKP+f#k+)m=91HZ)r_bU$qD8=A1lgRl?w zz`mLj#6H*!^~yfJtC7CBQk-q9^VzZw_IUQ4F80B0*ypVWQsvRSUA8Mzym8cRJrmZK zBd?hVQGYeY9@sZlBlf{=j!@%}s3KMp_Uj+Ga14@*aQ292gE+ujXYr=t#zCCfE91z zuCxo~g?+FG_EimweXtwpVPF5oZhDPY>qdGpRkl?Ck`x@mK7<7eMo0+zU=QpY{-oFk zyP-1FKG#a5C_IE+2n&}iJSFUdJ+N=$y<#8ihDN?mKGWm&FVqTgw78J%dbZ9Fum|?l ze9Y9JsYh5~^`FHq$wJjG?KhDL>JRq7KE%UrV9P!;#LLl8tGKx;uD`IgbmTP?A=}qN zikhx^*gYT2A%_X`nf|o7BfJ&gVv3eKZV%=Bc-^*Uvs_R7kkoK~1DAZj%|D-3jK%ZC z@2r zQDHA-=uXC5;JEjjKlTTb-C6G-bOvi3_s2Grmy<2C=Q+J|4|0E@>Lwjz{g2Sa=HEgt zI9U^0#?m^>zw|!FKcnB1CL)_>ko&~c z@l-EP-Q~n>nKRd+wc6Pgq%k*(e@1Ycfc1A0@eha8i)fkSAnHlC2B%T@97cQfV(J0) z(s_u~3+hXX$o25ZcE7lcZ{NY#u9x+2N<*g+WDj}2Cw z?C@{;joT{sW1pf15!PNr%WSV7D@^P3`R=L|s29`&>IM0sz63u??IY#(TY|ZsI$WF~ z&Ijqo_lht#yrCX&pABP<|D=g`6M28+{K9JhIKNbo#!r1sbd zm+N=|SYI-+#6XFGI1JG6=TII`@i+*225Dyxr?QM^Do#{4tngTAE5(oHe(a9DDrmsQ zW47T-1&?nu;qVo1{HO^hJ|ejD1_?>T-R9GHc{5#WG}Fj+&TNNfU2dEjW)3}W3mCg)WoOLI(K zsOOmG(;QQ->>&3C4f#Xx^|;|nr({j`9Ft(3YWKmY7t{ml1tmm%3EoOvKDq;O&fpgW z4{q7P_k7@c40!iVtEmTEo2@e#@tz)cN9yGUY7BgBMo+-e98<3BdZ&$k({HF3_T%5v zPj9Tr-ZZx3c87hgmzBg9+S`9qp$vUf7B9r~Dbw$+k<~Z-Qm!rOxtwNtgGg^N4^ZVX z4%e?7%{(ul5cyRT$;|U&`oq5yO*uGSf^H-PKH9{y4xVqXdh;*b^6p2N`)y#p_t3^K zagpQ2vpmOFc0AQS<{(-1@liW73b6KM}w?pZ;S2PaV%%{f+b2>HSzK zUsOEuBY&=d_3QFYd`8*#(?7W7=lc*jJ>vPk9~*Ny!t`nV8V}zeV9Y9Di~jb$~;k1tp8GLPxU&r$Ie z3eQ!zQsF%mo~Q7h3eQ(~FNOD3cprrqD7>%2FH(3vgp|d5Bfs|jiiI|L4T-lg8ooJPu>U92mPVK3Hn2Y6ZD4)C+H6qPS77JXm&cNKj;q? z^wyhTe9#{%oS;8cI6;4?aDx6&;ROAm!U_6Ag%k9L3Mc3f6;99}Dx9D{unTYv>;A4~ zG}xcFyifXbq=&x%Yx&U~LHU8}r;?`;zB=EH|5s4H<;S)>T|WiRms(!O)A@N@P~S*> z@chyF&0u`ZA;I|Jn}hMg``ht4zv;^3$5%LqDt}2Re3-(AD}02)M=Jbsg^yDBXoag4 zeucuvD15BK$0__ug^ySG1cgsj_#}l-4&XV8zd25jzd25jzd5uTYPZihPLRJj^oIFh ze2~96PLRJj^k&dt`XGOE=*`B#_#l7$UI*!6xh2u}yj{c+vhD5mw>IYf*2di5+L-%W z8>0t@KJIVrcAv6xxckB_qR6Y{?^9a-`ZF#^6laN){f`?*2di5+L-%W8*_ha zWA1Nlj1fBQ<^I-==l<5l+~3-m`&%1xe`{mzZ*9!|t&O?AwK4a%Hs=v}ZqF+Ov4+Afg^AFD(Zl!}*pTT;~&h@fRgLRydn`B*P zNUht{Yz?m0u>N^;kzQifau1SP$YEVz=+LnBp58{$W!7shzBh=c%826F9^Tybnv1U{Sz50_IO$$GUa#4+ z;vn}Ya?||Df2+qe**a!`?20ci+@k>P0r^E zTG|-?koafKkc3Sa{lxWuggT#@!dx%>tO4@JGrD8%2x~8Xt{0rMGwevu;W@%qNwY3=%b6sDAE&J4AEbA|Fi&GDny+#62A;qRcpAG{cmjV~PTSc@Pmg9Iyq+FHP}>nsv%>99RBAH?QKEVxHb$ctQ!mkG)=w zIQSEI0Z&z3!V~-o=@Du>&4Y{Wsf|c6br@IPypy6awXry^n5TJ$rz2HS^SzUT#|7ZU z0w8)GM;|*@w1VqP~mUDt-PTLtiEDD6pMyfyU_kw3R}>|8Ket{tJKg{MXE< z{eSkZ1Td;1X}=smzyk!7LzIXIL01F;1tBvb2@n+#ITXA&13?2phzeeeipuYz;tifr zRN{f4qJkzf;ZQ+jJ=Rsc@mP(zD!X3bLH??){@yijk{LoE$ZL4%tE#%Xs=K;gzxR4D zu7%E_IYvdv8jI$kIZ4Jim0w7O%XG!xwWI!^&SU%{k9v!aO3C;or_A~mdzmpeWg%TI zNRKSldgx=-ToIw{^^7{L-%1XTG)1 zAH4^3c>4r~Kf7n7{H2c1_5Nni0+qkES@tuBoqcrR+m+ue=lbWbx~Htg|9u|*xMF%w z#(!togIS{w{j}orKQ4cc>;L}SoWP-f^b7o>uq=!7vj)AIv!?qkIq!e^$x6;YPVFZh zd~JEEZl7t$#kGb}TY1#BKf=YH_jh#1scO8~dtP1Ht6x5IOt*!PWIi>saTe?EmIn_H z9&%5+$T_V_0zFQ9H1Kt0tG=K$;roGoNbh0NdnW00j`g=W%tOSwQYje< zwmUN?@Y&682flgyf+!vdKn0$kr8FV&oal8brK~YyaP?>7Fif{@-8|^>xU`4#z8hW} z7{EOYoakqwAPfL>zMd+KFRKeGpVfL=2G zl=%XEDKS^f)piSK_PpiFdnm(Jbd!D{*_F#mm=>McgV@uK&7<>r_^LVZTxC1Qsy-*^@Mj)fL^%Ni#}7bW4QD7y84i2y-X!W;HTlt9=zuR ze-7&m^oM6S8^5vMbKRc)q^d8~tuuQ-4`=oa9dl8Bdf(~#*q%v+ac?lk$xO^Mdkj9a zr*r4C4L-AnVo4TG*J+it(wtQ=obZD$0bc@pjs%7jjV&rxC-%^xJ>w0P+H-nOJq1dPl)anNBr#EtEv3OW3R^O zMZPnK9q=x7NrY(eUX6}D?{WNhsi6nl3X+(f7L&0s}3=t-AAZH?oreSuZ0x(L09vTc0neUHgc_UIFYG z^ruAJEX&x^_`m9zt)1QRwhVu9XN6`~Ql&Qc<>;;j3)C}Q#^badUxE35d?gMAcZlnqRQvmm(arPwmL*h- zGu&~eJ0Nk^dr$payExk&XSt8K&-+Yw#_vs6)24eh?4#egwEqV0@|!2B_cWJ3qR(_c zLE;hnNR0Ml#dv?WRO8|OT~GYccjXG59q=7O7M}-C-ytC0LEj-Tf9gyMSBH1kGu@#F z=mp(^zUVlI3d6V}N6Psdcy}HcR?P1?bHKOOoab+?4^gH3ZFOGurm9&daxCYcr1#BJ zbw+Y39R!v}=j?Zod|ZE-KA-!+RDR<3uh6F+&|cf<#}RPFdoy6@~|_ZDLc+i;5Q!-N7;$(`|Sso{9nmVKo8K%oWY_O z=!^Sn&~H5$#>~}{rnCMi-^l2-&g_I9pcf1g=nMJ! z94GGYXoFjiI#v3}OO7);k;j>xZQgh)>del6|MRjecNb!v*_k_UNnqc|gW--Rk5%_s z;Y+3l?=AT*vtf^i!%QRjkI~&5e>pX{HMlsy`4O9b$y_?~lgJUDc8~D6d^v<)PB?AT zZtBkUO^DWggwHsfKb+_$5iQ`q@bFiC?%BFe_=JCNrutCu+s}UN^U4b)kt zOk0%s>XY9Dn*OcR37k*wch9l!d_6g(eReWoE7V;janZDvGH zo_BKjMYqjS_^rObKJd`09^vqp(^Y@YYI0$BBk%HH+loG_{-BC>E1#IZHS6oQ=dENq zmq*@tu~}Zn7w-9BFU9Yut2UpIea`+b_FuVG<$rARc9*_~4(_vVz{(Ze-j>DJmG9eb zL*}YMElH2e%bps%`W}DTmIX%zn+^JJc=7tuaLX2(!{^;}VqZw|<6oxtsd)ISvb(qL z6X^Bt&5{1$*DFwmnD6Ds9+nr}@Xd$e^ykkFPkU)ySn5K-%>`#>8i)4@Zftj2O#SeB z#%Ffo{^NB*W@|EIik#Vr`xN+ru&)fWJ!f_@Z-1FHJ6Q-jDb#dkXTd4X`>LLYdfZn< zo!R-l^FFJFz7!>VX6IBouamF%0@MG9eEnZM{C%Nfo!J>b)%e=p(NORcp$GUQTiS_U zpf72c&w^~E?xW0n)XX*XR_|NPN(RrPG)f{-PvK@PzW`Wmy5h~W1o$*nvx!t;gfL@>n=mqUT zUy}cbej7C3X2(b`Srb&=>&#B*0sBLo=Y8bIJS~X$yUsH^Lk%BK2{l~c2@eh>hF&=5 zMV~3zG2D53U42NiUZxTw@Y8T+C!XWLpTjx>{o%~cIrn<_2MKg6$#rHY=+G zhEJ#CIgR}3=NA`EJ=+*Q^cbTse+r#McZ@M++O(O+B+x4#)R%xSfjvb6IYotq`4cBy zIC+RN7kl2BoqLL+{eJoqr~?VmVV&*Oft~aRCDc&uHt*amu@}8%NHxw_cS}; zT^XF!iP*MxZ2i7U;%9d5M|%QCdhCJiuJ6h~575i}zlmO;FX*_w#{ArfucJsO@%#>7 zeeDPM!o4T-0KIHHR`ddW0lvP+)$7H2H=(}w1A2iTpcn8BeMug_3w2%Y{vYfc9{a(~ z!K*K5UHkBWHVn1Kt*` zH-A}wg))iX)wd2XX07yWl$OBUy>fz zrH{!x1U|;Sd)%>H86WV#uY5r8K^OS$aRhw81Ha%wZ3i?TXxBZSfDd@!mp&}`pbNZM z?E@b81-f0(#dwT`4|w30KBW2Ad;s4)KEXfWfuB*T<%j;q7^lDoJn$0>>K)laRGe51HS?NHvt4{Pv(kHee?}n;BdFTatfL_2M^riBfC-ISFmnk{eeAq zuV?M(HVF3uw-JM>sl&)a6i%%#zYSx~pJFEvmkyQu{C(dgHIk5dpw{Jm$*B z6pyMHss*jiFb^#zT(TdCqrMMk`!Ou3t-7kG%e9c#rPKz?%N>{!YZ_IOcSF^mJnRSb z0KI@i=u7esOn`xa+V(*;>2fY z(pg!l4T<8VNjY@6AU(3s3s<3iI~Y@{E^n{1r?-swQmvb5$=|s45A0AoKFxY*MjWI4 zWJ+@fdY&cx#A-2n_^@H84jMXY@UT(iil!9j&nS)s>0Mr1{G9D+)Wyn&bmjz)mBU=+ zURlTqa)F%YpDS{L{M@)h`VGqMH-yDBO&yTquCFOyYy9nM)yY*KEvGroV$=56iyN~+^>h5B$@x>JPb!)+-3?P+zP9+e+tsR*qmkQWJgs*Yn?9bs7;=JK zAg3*3L{5;O9f#9aKc^RunaroxB^GLne@cGwnDJwZ$0XLO6S}XK6H3VyQy~Qfwit4P zTp*{`<3vu7pAAJNt$r3=SUmN@1g(=@&fAB7>UOp2?8Qxg%6PiVS!_UF_F~8ha)F$B zj@R}{k1IP4r!6^6n3kVEy?AC}z8j?4d~NajZ&#~MmM+$E;?kCMK|VOA(GCqcK`xNf zj0qwq$j^?$>71G43iGGSA!lsOr_=x|gaUGc zTueC?iJTxmcRTKWPRuWwoL@X`rW-C+zP9)S5^T?lj{z~{YH^ZSngj*q#?JFa06w-3hMBCayeZAt%TMa?0Bza)SKasP2AF&7U@* zXxij4Q^v)&?$s85aDwf*`RDv%Ehp5$j_-v6TMRitE|3%MGa)}4ib_g9d8M0LG@T67 zw4!nO)2GM3;MEp?FtwUKIMG&}y|_Jwoc4BtsT|}4xj;?;h5V35>>dwe$DAMEK+*(i zi=Ulfd*It1UminF``CeW8gjz^+&`(D_X}zY7^5}8)z|vu7`?1I+AvBym}WU~IrdAj ze>$T;@1N@DFnIom=UC=Gs(!9yC*4*%YP=t~-+uh&fk@uIf0}XD`)dCPalwIlea)8G zPR}dNe20iE@UTx;ndOClx^usD^y*xpiTzZs7OXP#N9~_(T0^+Be~Q?1|Mb9yZ4JKS z3l91&6ro~0e~F)}i_lKWLNCw*^fE*175Wl(&L+KBzh!6`ln=@NDeq12Irkj?PfuJv zZ=M78Pm%Z9-y|^EKP6|_+CTl!xHO3`MW)6-OrLe=wXZEyQt3A)CB#d0ZIz5q#2UTM zBiKW<>s~ivF);Ad0Vn3D<#k#S`5?uU^5gUSL9e#>w6tzdvuIw$YiM7P$I@YrQtu4L zFUBjzFQ755RledTvITdnLfwrG*X6GM_OsQTKb~&t^XQ^|dvp9AM|lnyzX6?>@r&oU zzx6i^zIgmr1o_NL9>2}h{(rE+rAd4#GS&X!{<2+(HGbVx-1%yW?}AF!OCGh0_2L^! z#~}t&*3m3iIc}4gehC3#-NQPVF-z7#tdDpP1MfrNeG)uRL_4foevWttG0DGi)wuDo z$zyoZQLwLrrH{U$)~s>xHkS zdK|l9<;gCugC0tA1J{E*^a4FVFJKM&Qu)xcITF9G;;Zg9)O-m~YM|#0u)Jf;m;N4d zZu0J-edx)oqC@Y8Fadh*er)o9WWK!V*-J5BV(GhSld1L4_7$z zHo9KPt;)<9hVmb>LWE2H17c7A;kT5w246J?yS%*o2ccs55Ajj0x!t;gfL@>n=w*Hj z(F^n??X$noe~raU)10bVl867m_aR4V`NLnpeF*36^%fnK690jc+J7(_8YRZxQf@NZ zYI-hsN0#-%R|eU3uitu2GG3?J-S77hpaa7}!{10EN(O1VgNe-f`WrA`pa)$fnysl2ZGpekpdCH3#E<~}uu!p5<%!td~Ay!$e^7!z#gMS<0Q1H!Z z<$Y}hD*3{nBcjTxsy{VKG^exP2um>-xf~!&o|*DRgdxEH7+ayJU>eb0e=1KR3-Qp zj8}?bx^?Ro$9zlgyWzEg0W3+3$Lp1M0I!I>p>{;6@L{MO5l*M2&7;42XR}T=y!IN& zUG_}8+iGSsF=&M$ySB4JvVLopWSM!_f;Is(&H5`LCUF0U`$6!9`$RlX#rJKvFT{N! z;5qI~0>S-YN=kN;*Zts@ynibDj(F{1;+OX9<8eP{C$7_2uYrg4+8h7p96`f#RQeu5 z1>`l+7!mh}gSsCKF)n|XqGOvKwZ3c1rmso(iZ4K3UiX7S#kwDipK5$AoX7 zX+UyUx}1=)a;%qFeiT!(8P5p3pLed`AMZ?p%`$oO{xOaQI#dd4Au~p5I^P$bQ?- zN#Xjm3GXWVO5hrTQxfFfOw#*PWT(fa_ImF;ms-I+j;hP^KDfP}-$k_NnQF~>CxkN> z4?eeIh3NiTb(~6^z7xZl+Y{(Hd+f~Zi2ge%I&-@N{o0N%3xCJVCyO4ZqUGr zrfUyFhv+XGhD{qY?)>=Q>$cOFO&L3{Tvwpa^V*`%1u9)6eoW~?iAP)`@BJ*FDzTj& zr|o`$)X~dUaRl~H(g_ajGh@u##!@-gijtlIcqSb1aQ>U_kA-}EDyL064TtvT>vObB zdKz(7BPSl;5o z4{*R+uH|XcGuz8kw-#s*aKLN*py(8Wz292fk7M*q89^io28t3ilc7ZmlkrNMaz=Ity>1o8=hE6>I4tSt% z(lha__SE@q+P);|d0VN^Wdc6t;YQcIVm&fG;DKMD&xL~B!u$Xo&;>r=fnRx_*g4R} zJc-%9h6jFzKHm#;fe$|1?E@b8mHJ#M@Bw@1wh!PR@Fu=K-wkvze?Z6Gex-&7eulR5 zpo@7Ft9`%&zX0cpX}V}1blmL&9{3r0Jb*6dV@&zE#!o5E1=Dm`#2vEd}zA{ zI#KPr#!sovgD&`wg%5bNp8@_|0Permc~v|kiU{(uL5Ddvyn1Nbrfui=3Y{y-P}gAVF>{FG1z{w$tDWK44U zwU{?pKQ_kjpP23}+Fv`Eydm}!8#M~)^Yzjk=j-LQmh<%xuRUF!KaBV(vG>}6hy89( z{H#ZEnJ@Fbc6BzMJ+CTb{^9;`P|qREbMm`d=!hCy+(xj#uL#!vhK!p8(y@35#tIrjOU4h>PI}AJO_NSOG=G&>1ZrU_N zcRIqU{kxcgJ0_}qcEpuGi``g$Kw{4^froMGi66SX-rEnBo7g8#Sz&jzAJ7Bzf*wI% zhLKfa7+2&-d0zI2_b0B(xb+4z4|scwafRoYoagsidnh2b<9XS}Ww+30WO>>0E3Hl%(_PK@cD6rD z_Yd|B_6+uI&UxY=!S2DX!M}r_1;5MAMh@*Uokj&qXz6I#GPmIOpKL6!Cas{|I`3UgrO%*DtMSVI`gPvU!!o7Ue@pO>pcY*hAYeT&!_7%S2<^e ziuK-p{8T-N?Svxe1$uy9(zeQcfxd(l*2`~By=3jThU@Hl*{DY%^x)P@uC8}=&r>h` zlH(iM57EmiY{ytH-t&d$M=Jf35Fb?+hdVI~y+9ApOU56f7wAh^;_r{9C_PA?^i(m) zmUlAlr5El0THKBbdulc zIp_s?fL`D?KwrYxC2Yr5($&{Hp9uRQ0;Y@n=c=k%vw%4Xi3H;kg6FGxz0l&do9P95 zfL^ErDnMV73z1$pP#iKUj8ImV5$X^zvfgAHRAWdbTf@O?#r6KKJhef;WWEHiy1yk< zxsd3`(uvrF#qQq?13M;AXoU)_hX~4cILr+jJPfJgM+#s z%VJ#qj#|f2@6Gf5*nz2Z9vNLV2fMt+dMHb%SodS`QLVY%x`M!Z2|YkB8J$Hh&{tix zzoY1~UiccP$FZAklJNo+?S9`;Ll4kPX`1K-`f~TPzUA$DVcoccSZ%%^^IAFF(7Qg5 z`2syaFR+8q*RJQZdJfp;^A&r(us-;HZ1>U&^Z>m;3eXqw^)<%&5=$=!rm`Kgf;(^3 z?Alb?Lg#nX$m2U|!gx*qDQzj z!+Nzt_EQu)j}F|nb@kG116HAY?5wRh&s7b`M&6Hi ztppBfxF|}G@s@dwLm6Mvr{Q~jae}*d{Cy1P4u>knYTZ0=pKVfoaYpyp6zOu?OaZ^uib;5WXSsp zF^IZ~WBE~X-@zM&Jp1c^fL@>n=%sZp(F^pYdZP4Ve{RZpk-S|mF`mB}&`XSZPrdND zT8ZrQRj(I*puT(Q1$uy9D)$w=Kwp+#j$UnzHr9*e?Rt5_0&t|Dml*Y)dRa_*S?r)v zeWrT7@W$Hir5EVI)JsRv3-l$@u=F7Lbka+PO;1jl-LJFu=cfF8ZiN#}=fn@QJ)cwO z(7ixUcS=3sqYJscSb8Z@`&zVeX*9`4)2J^cuw#{456}zz2I$M+=W?O5avy(ewq9Q( z54}8-Y#3|kvdjHx%5#8TIB&04mHPRd_;`8QC#gasxf#9UmP!!y8b~aYaJdaAK z68B%YAH#RjxKG1-lDIF!b2<0-E!+<8(>_IWW&kzbvSp~zoq7b=pU-9F==(7pr!|*# zZp0B1Go9)J=0ET-|MOaV;78q$@wXrxaDOJbSbw=c9O8dplHcNv|H}`^>s4)Mq&4}w zyS@(&JwPuT+lXGEFX^4-=Ue%=R&m zvF^uqm-i*12k51By66S^620(zvA-|L`Q2&0SodSQ%Y179J0_I0<3R)yqZ}^>I}Q$dfgYe2v;cicK16nl1IHN^^p#SUVcZ!oLfv`I zt}$e>d8gpDj(gvaWi^~+@KyWZz8~8$RBO*yEDEd_bw8&4?A`Z%%#yrUU*&!*=`V9X zhWjtvkD<@FPs4X=_-+mNVfaoL@cn46u$?)L3ivyQ)YRTdUf-{!?Iph(k9fod+W#0Y zG3dpL&H1nKF#k7t;;+>6AK$Ot>&(Y$M3o+ytkHnjPGrX zPSw}D@5g9gxpz{(lN80sc zo-?S=6|AC49%dQ;)aMN9^9SuXoE|!4=(uTC)8=5J!GC!RR4vLeJ6X z!Y$WvsXjNR;8{68W`3c(*FK`ic^(T0)b^Y~w9kw&Z>9SDH_&&-JkHXo(1g=?YxVgy zCOs38Pl2({83Y{KtJLS|ne;TC`($jdk4MXacE3^kV;LXu^COwV!N3yd$zyWWq?w?7| zY_GLLE`S3b=7~v9n7$5M! zFL+AkG3Wx{J&u46c;J^lr_Yhne4yO|EkE!De82<0;03`4U9=kuAMn7h1pk^3jPF?Z zfCqlY`(O5JbJ1zxQF10MJVnqG#EF<)Zw4|w2bEYssh^8tMD;T}JLH}RKf{pt2G?_%+v zq2W#ZC$#)EACMpDM76KsfnNZ8@S*j-HirHI5ByR+{xUQjjK5g;fCqlY)4Kl}AAHBc z2R!gAH9qKK{F(Mi3jpH>@W3z7_@E2<#li=?i4Xm2I?#Vid<}2nYd-{ZF>YdxKMfCj z=(j-A#r*XeKP3bVe+T!ec{7}T599|s8p|GXyL_Me5v>Y1hwdz13@F&28|Jmp=g{f6 zv{3F(myb#8J{5SlAM(Wi3!FnI66F2Ec^8Iglb_gU_Z88E%pO;1$M`6$FVM; z0qhTOz7^RJ-scj})MY>36+4lqQwg`vE+RQ*ZnoxX%^3KSzE~OUF^~f$zOt z`&`>-fU~^ZVRzl+Jhnf3gvvfPF9659~jp zUZBjsvd={mz&fX?cha_GzD$^y3HvzqqVJ~S&c`IYiz;}Y^F9~mo4?QHz0U=E2>S}we-Qp9{5+_c#}1!gbS26D1DfASNdr>O@;J9{d6k@#fH-fQ*uk`O60?WE z!@QeumIuE3oP>(g^tv72dszLool^V$A;zVBf5e{lZ~%Q1!&lA0F0Z>h7C50|eHRlS z)tcL_D+nG>>`y@t(94Jsq8I2(+NbwO>gn&zvtIaWK^Ko>cSthUQ7?DDa}uBj=p|#c z=mq+cepj8R!9fZE5pY;3kp&OOIVCIdsD!-5l zmeIxUjm*??c~F4N`w!0B>vc6EjEsuQAq_Sb7!8`pnV)f?V?B^v?d*zP*t~%=@qQ zVBhn!mox43YkCEKm~?2c@Y8*w<`rPs|5Lv=ul@VislwF+Kpio|c%PHtK9^zl`@MO2 zH}JO{M~5Am`gcwO?*DK{dw%~<314kX7=u}U%+7&DYiTAX7(nsMoV5HZaYo-53p^*b+# zpJUGW@}3!|tgySDV-7t)FX$2UrSeLKoEjqf8T~TJmcuhS-DnK)I}IH2V;GcUtF8Ci zU(angA*mZ(Lbs;BJdXLEp2x8}FeQ7LJ8!S64{4q+>OBPgy!KmS%FzMMB+4qA7pnOQ z-WflYGWK%7a@`!tU@reB25rzn%=acF*nT|wz+PWLjA!cDJ8qLA_kJlUy^~h!(PZ~0mY@7jHHHwc zEfU*4|J=lEJn&#kyzzf>+4x$#>lo1K>RsPQNiUB&0X=ijOE=Pq*B*wOiaYN~bQcz~UIvq1FyG!LUSOlEW6NVmRtH{h z>k9NcU3I~rU5+g0X4DAU%b-fG=XHy(shSUl+hogkBp_>TOEP~8yamDVoBnubm(M7$ z=l54R=WmB|ecFU~%{AKkQh<;(zNF-DUDoff+XMgH?-DA*`hxXFII-3t+)Loy9lU#l zXHS6V_;)gnc*fMY@gK=v&zMT@dqc?s@s^3Q&TPC~zB#ee^GY+{A<_}>VAooo@3CHG zC23z3&y<3$yjKfW8TW^S^7$(QgiGrUV$b#FkL0!nU-1RV%j+4FP_dpd#ZNW9ws$lX z^a4FVFVHLWrP}2)zOtIFv3THTPLj8O>z{?i)&f+i8+ATLJ>Tcq&t9YTro`x*d=p(R zLe*qr?aj&@O{Fu7*qYS;0KGsD&19G?A(JD8s)A-y+-tCoh^lfjD ze^7J4eDTtY$G4dDj>;d&eUtgBnKnZ({aAW&Q*r0xYhmX>v0h54Kd^)Url#W=QH|qZ z@n8+Jw;^v{G2K)feg1vKuc+`xx-OyXGP;mwIL`aQmw-rMZ%;v(-(u-+uc`=&hfT&N z{VkUMM$3&nbja}2)BBB|q&~)SSH_v2O>piZTB^S(s?^`KWIV6m{a&o&3uLc!?KFwq zO~Eg+%Js&TRp6Q8d7Mr)XrCEVa?rmy?}!f#!J)R0Gaem0?u!-0EhPS^tWFo zJ&ou777K8|TaIssbh~Jd-&^GXJ^=^35t?t4o`BQegHm~o1RmgkH%H@{^fcbAMov7y z0S|gL>1n(-Qk-~z10L8i>6v(vU_N=z4dVsxz8C261ANHUl)J6~KH!00xm15Uq3HnM z-EZIn9{8pBc0uE#-?7>Uyos;zKo|J2+Ar1cz%S6>UVtv-7YiToz%SMJSD*{MxW_g6 z4|w2LKB?!e<^%1wb-zA?hIx+hJJdD$|4|w2Lq~vn@))|k*uDsO7`iuU^KG1Z+<$WL>d+r0hLGKy! z6<=`BccBOsYab|nsxCr1Da+$2L<19gfL@kgB6@+ogq^c#Ut#Jhi#5uR4z1z*db&Q; z70IcR_(13J+yQyi>uN-5AISL|*&iAyseAz>cKj2W1=b7ekjJq*2o>u$f8(R-rn2+$ z&Uz2~VjNK{y@*}k z{oVnG9-tS90Q!==bynJh37sR|y1g5mFyV$sw`Uq=0lvDJ$25m{#=k%&_V-7kA4@Mk zB|BSn=M!tG?xA_UjOawqLG(KH1vMGZdaHH~nq_%1INny8?4ZU^s_bENU(VgNV1atC zmGL-j$G1cKXPtbcw||GQ zrTLxeJPO1ax5#_yYftz1jsV1MG2W{N9^T(A9qNG}b>{i`xk3u(Q`iW5B%jpZRmgV; zjLY94=-Bf+geCM10$();yS%*4xDzVYcL?!Ot-0O0f`DG22k52eb)pyOOWIGT?+~6? zZSg|+im_`rZ$FRXdM*F^H6Xr2KpyoL9hFk=yF0%_C^7z?!j}uuBg=Z3O5^tys`L5| zL8$Qi?h;D2^X=}u)8(v3#2&2dkRsO0V4{R}_y+^MEW@y(qQdF5Xc78{zM=ma#iD=(_l*dJx)4MbHcM0KLH8LSMoTpRIm{nuo|&MI^tF3UIdi-ZVcrJSb7r+3Kj* z)rgc{+-Ixrle*CO9g_)=9x<;ie8u|S0q4Y|dX8=ng1fi~dVwCG7qA6=AzxqP3ur7< zri zIwH_EC}*p${rv8-f829P@cLoSv(?9s9g;;nWLIm-AWi70*mRW&Fx!i`Qj6d)t1jOoIYxj3-VA;Ikx9?QlLYt#9PsAo_Dp&vpU!iI00+E{x;>Mg*`5;*aKKAjD0(&N znRsc*oCKeM171&!XVTMnhEb?ezyloc=Ie8vOnMq`nCsjmzyWWq#xv=e?K$}b9Plu1 zO?qZ~PCUQ?59CdHx?ol#tABh>Q>mU0I437V|7J1pF>c)BfaRmkd2;>6ai#ukXwZc| zK?ih!4|ud+iQg#KbfMQ+?E~J#$2m>-jcPp}!ADg4u5+47an6wD1MR!V3HS#*+Aqa# zOl!IrC+>M`;=6uhx>C0fy5QSAZh#MXv|p*|fe*Cr9zQ0&yM28=6zBraJ&sI#4UhIq zwcfx7_>P4Sc;Ht8U(pvDg;L(1@ayjP;d_aD&@Bt6} zQa$cK*KFTq_rX8lfuHe|KIcsHf$;}9y2mfzO?A&3X&bnhZSL*L&jo=WKaMux^T=7w7?cL2J;L9h7d&%~L!})YIZ5kp^i9L4b^Q}3z zO;q<0h+E$)cBA!>#Ow#~Fit)3L$~X3isxAvx5z8@V*WvY&;#^>_MtC23cbQGuAt`|9P+ZKmhV^bOaIHw zyp%itLQZ(T!TII{a+(5TJ>+E@m)(-bvE1vY_oWO6KEf$AjdU_Z@^SrT`n-?MHcxQODGL{2KzSR60vWvd$4Qpui$UF z{Z;NTkBi9^^MQ|PX`x1Uv(rYwzGqjk?lxuL5HEj1>{`K7iP<;cVIHNGdf)p-Th9q0ji z!F+?h=&gk{bkTfiL~$dE-TCcgHPpMmLDv&6bv)h}yn298At7%^SV(BB6ED5U^II}N zaar%}XnY5wKrhe(^nzxfFUem*^F@WeUU{Auyx#MC;F#sa0p*-Wy+QXNR239|z{r;0 z`B*Lr z#3Ih7I*`oVL8w^wQ}I)EQ`$Ls=mmO!US`~&{SB>W;btV;&s7#@ zod0t*=hxE3bDZttRz2unp)?2h8+=c|0rir@U(w6;wDidDB^u;!yl4ED#1~+ykD30V zdK|kUg$hyD74cJbQ`$Ls=mmO!UNGOFubsoq_P4@wg|Aq8*VXnO*GgY%eAMa{21fiLB649*!K^bEu9Zy3g< z$??t=o~zaA#ZAebNBi|9=J}FFa>V-dFJg?cH3lv68sX|2z>L#xR`>tWd{K|I-)gU8 z`Anq(;Ef^SuzaF_|H+P3`?OmJ%tr~4g}(y-1uem^fu91u1<#SNZvyz?WLtQ@1pZ4} z+EJ-q{>#QyvR{HY?{-<=*3R+rKS14zVUK}_dEaxm2Yy8lr@u1#8fV@j>zO(-Rf?7W zqHxK7LG0=mC1ce1pE| zIS1)uGwI4J#XxiicVv`IQ+`k`z18Hj!OM4iLYo{?h?$G zSb7mEq&OYoqYCJqA7j2i56}xZfxaXk-+sx-BuDmB*17x_&T~1}YoDjO{SqV1cr|%o z96DiYH3ji28EOX+tf|3T*0Wx+h*hjl?4QNM`CnzfgujiV`|_+E?T?*tDvdlMir*uD zBu=@Waq1aG(+bBcu4?lKI!v2BFNVK{{fJQTmE#WUgUi{b>{nplg6lTC)~>I>j5b0> z11Gq;-{5D+o*_$0r@HFpp5*N(*911aHjv(97Me}oUD#XTL;8Jn@GIkUN9)Uz<2oqJ z*k9f+ibY!4Nvee2`1gmQOFNE-t8K4NINkbs87HMW1_8(!d;t!8x#5|waWlsi=1(7W z(WL2<#!f1nR6G;J>L3PR-~)W2De!CYg__#MY1e3Z-RhiQ2 zOrBbpKWfbMQH7Hx78Gj=brXRv@L}?$`vHC}zH0MTXzV_aGvo*+Uz!i_1t9Qi@il7l zn5mrRv>(vWL(;FfF3< zkCgA>-zH2(?irH28y@-WA$`Y>Hwrp;>CzQNiNN~0=@)%3B)4-Ua5`Xq4X!!?QBpPpF?mmT@xlu z(BDs5cF0;g2p~pCvWzaTlT1UcH-}IL7bP9v+TMKWf9U?kYR5@BltXnfFXbOmn^=!G zP@4Xmz6ktup~-Dk@R+>X@m`|cHyA)bHtk?Ygq3ExE8>x=K>KUX*E-1x(!DK>0}iM+4ETfa|KJafxLN!`_=$pN{mmYdU05{MsD(R7 zkG(1ISgIi?{x>f1{Xx+ibZ3v0=@)%3B)4- zwajUM&|0B^S)D&fFna9qR<;HIisk{VaxwiuEw@SZ(4O> zOn(smAN;{N6T}~cpJ=r#DfS01oMzDeV0U*_y!nLu!5NSE{-EX8v54(+%$-Iaa2P+K zpzjY_vXFsl&%4vn@)I2YuP*^#0=@*|kw6{q4_YoJuk#7{gUFPRI^Ec=mk1NuA6!1h_XjOsg>}H= z0{&&7==+=e~`aV)FGxnSaR_o zy;cY$-XE-uKZy0%%O6YuM~*d$3)!wp?A{-AKQAuz{Xr3xmY22mV{L*g1q}Wm(i0Ka!-VXns1naSvKWOm@pCvkW z`-AT1#g)E4Xo=DFjL-57EfDyFkXtN&Fy80I@c-Zsrgad15PqW7vZTl#oR&XkdTkuS zg#5vcchtQ<=cNx#VKNu|`l=%K2B;m)t1bhkj5`YA3`-2Uwm;F#%{r(`%Gy-&V zth`MrPS_u;&Hf4{jVN{viBBt7S=L59vRtu+UwaP(HE!!AHd( zZ07NnT0*VH0Nzucclz>k&nJ7{o#w&fRTg6q`Nh5jd(0F@x2@~@N=hglGpu3Mw zUqF6eBcy@+{XwU+_7s2G*=s-NOTd?aFM;YLpq9Bh^9T9&({xC9e~{NVea;Ndnjso- zJ}=hmv6nw+Ju`;S5*>T_gO2m4m-_pIP^{ArvHOJ<1pL7aEw|eGgYf_052igX{viBB zt7S=%KUg@XxHx~xF*!w3il>d4UYxK~n2HWT*OrSdZ(9%lP;I`V#OZ;7g!p35Y+)zftTJs`m$-YrYqL1FJAme-L|uj@`K#$akTun#1Fm%d%OAvgTvz^J&KM&4d8LEX!$9T`+s~1_!96X5SIk%)*rNvn=umf2j$&Cd4JH}k@)rpu^!izKUiAf`-7HN zupYT%3j+Qi0*Xhr24?d_w-8d^1bhkj60pvj@z@?Ts(W5+omCcFib>W3gNy58R(w96@4K2CXh)pMC2 zd+Zan7FdwvcPF&}Wt}y{XU@1oV&}~`{X_OU_`Dgc$6o&6jyP{7 zW1a60T6(juS@yIA0e=v3i{%f-d;TE&Khq!lSo}fwiB`*!B7bnw45PqZoJc+)e=zT| z`r!|TbPso}Kgdk*xIpYqBX6&lape1hB8O=C*~^iRmY)*2|HqerF9BZyaYDDOTd?aFM;YLpte2g>^$ndtfmw72WxfS4Ax^We^B*U z@dbMs9lQ4j-M?2?8S(u=OSM>!+_42=`h!|-vHZb!pBKabgFm?OU*ZqKPqbQ=RQ8a; zlctO-IOdcw6Q@j?FmrqS!i4q*=UnCcgO+c{Iwp}2Q691NVezQ>1g>0 zj{nz}fG+`G0`W+oZv8=Pd(cSGAIu_nT;^Fb>?7t>cd_5W?+;=Ze|ARj`=Lhiz;U`)xODcQF&~ekoOwB)LaQ>L{r*DrtnArJ)J*W8o zpzjZ29_*-?$8vSFyy|8A`+t22_!96XP_qQ;)*s~WlXXbY9~8T{y}wt7^|-G5!2;hO zgkC9*wMJPW@CPBc+WLd=|KJa<-EaPs(R3dGKhbJgQtS_2SU9yfciNbX-F0#1)kIN$ zsz=~)Zj2b=`-9fFV2SV;LF`T=Z?BgzU~Vywqr{-CvI3I3yFujj>o8&&lJKR0eQrs{=^GJk*2 z(mc!0>#`u=4?=FW^#|eq!5?hBMfL~bCt58_iu}RJ`D1q8dDLr*>fRr;%`M$syABNF zgQn}ULb^uAynUPlmI06s1+902tulN4_NCDV;#hpx>!p3)AGGTsT0TyFc-3>6AAAY; z67VGuj|7@AkbZwK8ot)&56WXU)dsh-;kDP)@6R1V8C;Z9Wemgt{-9;+q+I^S_q-VE zv6nw+QM0d0Tx#Xr{-FE(!GbluKj@UBeceLZf`C5=xyAAaADlAg{QUI1qN4M+(1-dD!J=FGt#|9&+_#Ujn`ad={$Q!UKWJ$-$`7=!PXY#i5OS-nKM4O1{$Sc~;(x(Uv@xfWZe|ARj`Vv_iS@Dr_;C6zs7SpJM!d|XUa)JV35v%3W| zukJBMTwnM8pn0cdHVwIR?7u%~ip3-0`-2`r@r!&3_!8K|CE(Z}OmhpPy643^>-XyT z%o%;|46SqV?GIu-_VNd}_xs|``upN(^nG#VtG+*IjTO5eVozHT@CPBcSpHzV?+?QN zgFje#f%t>)6RnmdMgCyHg_Fl_=c|K?Mne081rzGtA9VN8>6iQdV6=R!65k)RWZ|cL z3HTE5B_I+|%UqD(ACzaLPPtU)4-%x>hj3yzb87cdiWqjMy+4Tc*vlWZ_IP9;Mb=Sf z(#pG^7rVbFsq}xpShTAC+I>KK+5$2C!5V(QI9`7c{vZ57v;;p9dBo%oUOZ`f@lNvx z=M47!LCZg7Em(6aI*mHOS!2k5f6$VJjAeV?osO2D;P`)i3HTE5B@mAUEPs%oqaHz3 z?+>y)sy^&%Rqs@P5bJSW`Gciv{ry2pweEEZc?$ymAmp~a`-7*@`5$xWqxaHxD%Jaf zy{HKuv|AqfkIGvuI#Z|g%eioDe&;UR|2c`!i<%(1nVxfR_G~|Gzd}cO>$y%o*PiOk zJGqX|t=%g7pv%+a!`m{&;;U*m2;5_qF<&h?;|eoQlm6Jd!XWm1eM&QL=#TB*n2Z0C zVaAwf{nV8{Sb(*q%`~dh&zv8hQ1iF8?Zk^;+dkR*6ZNodsmonVaDgOeLnd7T88`LxM$T_dJpY4CbD-c!)SAr zVSKdou)xPl9||}7bZcOI)2r64n`RhYZ@oX<_15;08_$9bm5ZA|WM80TDI7~Fr9 z-MU7~G_I1%J)1=?2X<`3ed}8J#VL{NN)2P)gND&C^~-RBjF-YJ=g)87_r>ll3}e;X zUnzP|e!Mo&?N-B>L<4nw$zG9H&;B96a(RE#k*#}^On=?5Bpe7O2d_&yGtywwbH@+7 znQ2?FuZiL(Yz5PjRb-Q8an{gLzN?X9~zA8K`-1j#LuI_I@`uo3J?r)>iF9Re4Rd&#M zQburGrsQ(ac_Nn^mfsM*Vfj80BW171iVwe^v+ZERpuFk_mwkR2(Q3zKHw3tx%LW|U z{9+BKWY1*>T`h1VKkkQ;-|y{B`Efs#{1$E!`Dr<;{x;Yo{as0NUa9+gPJ8LEZddU) z;85_Vcvifu_)vIZ8Dl&f##3GQzMe}|nHtZAaq2%%hVf?@&-V(@ct+sy%;}_%lwhtJ z#^0{&Mf02k#9q8Lnu-IxbVV%YFl{qNm^nHW0W~ZRZ6}X?(GoM@E<2gZ`v`=rT1w zj4oxLaTy1mOFXv>^B-U|(* z)BCT6Z~kPl8k2J$E`I3%8j~6>AaM7#x4|i$&wY6A2^ua4dP9bo^a7w)e5Of{;S{|g zLri)Mr|1=*DfE6#-V*-jZEuHp{9S+Nb>)TWG=HTmAZ0VNbyMsY(Rm1r96^7%0u}drFZ45{Bx?pY@M#|N< z9jMBdVjdaBpOltKil(7dTy}=&frm^faz2quKl)#+~uN{P2tiT1P(k{)kA?_Z4f~(b%AT@RdKh zJk4eEvJB&zDGDyY0*`&mrI#p-1G3*hYe$Eif5#Lq;JLD zT12{!_#@CI|A<9@PUDV+`Gyg>twnI{%})dl{lC9OZd+S5swznD%Je!Y_`$O0RoT<64xIkn zu@>BuZI4xONfj0xmoeN)8_3_#?N0l=yJ|NnWVOp>-0p368OFegt%FB>ba#Mp3y)7K zVR{97cL)ZIMXGF0(bo5w9^jZihGYH!$7Kx1{53c-99Vp3+aUV8jr!Z>ly!lZ`y8gq z8h5-mhx_~d>(2&$G!6?a?(gu~F9#-cZXIOYs!_MCWqOkqy&0JG&qGw%fzzLE#q9*s?&^dk%^muINdBHxGX$FsN@w zRd(R?L6uAoaKfMT7jRt0aLgaty~w6tw994OF84R{O1`9r(*{-Rwj<94~f zS6rHQPcc2fao-q@`wKWO zV>sq-LWA{7nV!ABT*maI?7OG9zmJ`ER~f@GZb!N=(Dcd}j?0cL6?%YUUKmdEr{K7Z z;h4X@`|cn4Y*08XeIvM8hSBlMgCjp*uuheYD-->`^ltOWF^|5i^(*rG^s;u5+}GYz zWrHe3et=_MU|#^oWemst<#s=%c8m3RP7X=Czfike#_e){hdkCQa_djeYW>Q1nR4nO zkwvXmsmKmHoO==5OHI((pdT z>3aU^`SIv8;fyv%s3AaG;*Q zVh{d%&YJLD{SQ}V|DG%M0C3zl*i*o98N+dZPy4y-g=6pT5VY&JE0y&RA03oA-C_`r z=c@Ea${3DuF<${O1`+J6N7y3;i{T{E|m9iFtlzy+e zu@AS)xR@WqsEpg?vUko9dP6iE^Md&XI4)y2<}dTt7sFp)bfCE&@pwM1?JMC1?G9IE zziyQEXzpoi!VC9lqt_$RZ%Og%;mFe+RN24hioFLM^8$McI4)y2?l0Q4uSaN?%eY$Fz(rJc;g^2p9H#?%M}No}Ij0l?|#C{{?W&AJz}RaT&vLf8XBH zwfBaClCrzMd^BKC;Bi&@UgN+%=l-YskIO#_+;{eKWvgGN`Iebee)qqQ4gW@EB_FoG zbrqFe{kOxyjJy989|f+tf&6m~SMXw!fWUoP$Z$+=QKx${h2BXy3|D&D$V{e}by@4+ z=tJAz8lZGM2%)&kYK58FoR|Y}=ICx!F;wtoO;& z=QG?B;m%>u>vyftJMNQzl>_djWdg@#zY{M^@17QD zLS>H+ma_2#_tVlgfklP=GKVk8?O(RSFdihhEmT%UWseV*vV4MT{N~~nFQ-o|8@_~S z-6L?%uUovLfXW^pEM*M0`rNi*p;xv7^j1=t(33KT`|YSR!;KVwOik$hN@aU1{-kW2 zrdRar)6|c$6}kNduAJyir?STfOBwg~me&`r*yol`#E#fk0qJkPw4UL`W^yz>!n94`F%g(yJx`j?`s#Y zcy#uE`g}jZFj@>0{@R6pDHq)JJs6kijqKDp%(DM}0z*kzTY_tQ#6l`t-=oK?(#AE! zbL6fvDl-gjh52J%1g^)c47aF7Lz6$q;1MbVe~%9q`7s>x_W)S+%lm3|1tI9Ag@0Afu&pkSD&s*7% z=lXs!_Q*F3W7F|vff@h%J#*OjVIo->S7jz1un@y<_~!6(j&WVHUa-`qQC zZ+qb4zzuJm(z{?upGe1L6O6CON<6c2-{9LXEM77E*Zjy$4WFtyIwbV^@Bc$N(;K_) z&WE3Wp6+#t-kn{4Dep`4Mz$KTqLS#HNc=rL?#M9n_r|u=(4q9)B}Dwa_ur$#%-`~? z4)=UU{GFbj9AtWXJ!M3`=>Gb{Z=YxwPxn4S(c8MEQ)I|_V^_>;Y#2Y>-Z=OH@%O*= z{|SF^a=y`;EYZnN^$m_&zIesI7q^c@E`6%%>2#sT{bhQykGuWM!}APdDbYKO`n!eb zeSO~R{k}}2`;_5*@y^?0?@4~B6C@t4%KiIU&Qq2E1o*J^rR^ks|S z86<;6ADtWdlPp*_>c@+O+x5;h3w9>B>}{PR zuXJ1AV=t0v)46>V&o{ktL}b|!G)S){T@qY5@zPhp^>~%x4*p?3#owJ@502b;UfGJ9 zNCuyslCJpcy?!FrT~N|p@pnv&VF9Q40e1U2IBCszEr|n+^_J!$b`=@we z`^UU!`)Be88IT<^`Gfq(hAIBo{y_#@2L8}rE(3oU8(aqdFb=p4@CTi98SEeQTlwQ> zVc5U9+r<72zP(cf_V1*y*uQIj2!~<+uI?`OFa6e55!k&5{#{M< zVE-xK?H0Kvij^?FV0-+xbPq4;APHtlw?f6qR8 zh~kg!-;-gne}|v3PXzYw+}UFP{ztg5f75Ok`}Z8-!u~xO7W;QeS{KD1+qG}L68o3) zM-Rmx+rMpF#Qx17IM}~$z7qR)d%qTu&l)^Yo^xQg$ZI((7xMkbhNev-6+Qpchs&NU z+A-N$-_bQP@~p**Ua#_0 zMeppbjU#7&l~#W3zXwK|Pww>*-&+k2wTR?zcp#I@UdjIBY`(X;_|2}7?=LP5n0@F!3pzxKwyi6F zhRPNg>9=xO=W$0xe!gL_!u{;M4_;;5k4id3uK9gJ<_MzqP}N&~U#2$R=+iQC^MVDL zOz-Mo(Fn#}{YRHb#uYOZFOye(+Ts&}YuCJME&dHX6=)?wlXbv%26 zUwcKad?lpHn#_OnB!+8t_fe54Jp^~{yH_jRL9g_R{5tkV)yBv3yQzjh9UY9cnlVQ4 zk{lXH9vt}_sVyU~f3~o^ZKqQsmuB@V<$J&R*?UD!`M*#4aM?K%kMF=`&7SNKnK?C_ z*_z5GR&6Teve&-u6}fomy9%z~{1%6BS)cd2MCNsTSkb$@;^swMc2e7xk$o?ItnA|J zMnu*GD!(0Yi(wpdQHRLYt*$7aPG$X#%fow7*~ibciTrv&uS|x!@Q9Kg!>R1KYmSQC zxaoLRw)cDg>V7bl-TXtBNYm?=D0)Awyx`lPXutHK8#+W@+I!9mm;QWc9yOmV^C)A|F+t3uSF&Xuy|%tl@LWBQzQ0B0(V8Lq1u>6$mCHQ(a^W#S z%%j(GWF9SfuV-+yo<~axWgb1;D?NyLG^$GG(brq|3%1bnXwgcUM=y_S6`ZT*(JAlA zJX%_n7JNa^qki*c9u@XZ4`LootdeuJZk@*%%fvEG!9}OeLP?0 zQSrLtgP2FZOq6*vYjUR`=26cKnMY*{jt^oUb(kpg=(KK`!H#+!ZJQ7V ziO4)^w14B^$$B0woGsYq! z-)kXc$e3Af%PeG0rduJA%$Z}ELWEF;Oqpq!LMhWCMJ1UsWVmEXrY2KTk(83^X`cP} z-*uhGdd{BrdEfuLeed>e+xLBI>$vv)+|T1Yj^jM8;l6Ow-b(RY?mo&^Ke~_dB*)^H z+vgRchOWg#W7X2h8bkfoKhHi1-wnzO^F)eRraXT;Q`DMg` z=XE@b(DW|i6+PYZ;#0usS%p?-SMds-ZhgSVdYet$t^s&u=MUF!`{*Yr0bbkj>_W>0 zxAOwG9>T5u8^rB8f!n!*+jRo3AR|`}9qqb>mqT{CUAO2Jot{%@K5+fd`}C6(eiyfv z$8BG^q}eqLw|&8DI)AubSGmRQo`Q$Jsjv9(>N0Y{EpHxiyXN6`9l~#w;SaYsw~Je^ zz_<*bCOX>v0ydDLgH{iChYY>Djt_-A7G5vJMZa4|J71vb&Bg7!gLlgCAEYDK2UjWA zZ{v_BB{US;H4mE3I_oFcNJbp4!G0cRJ^DR5+O-Fo-a_2^1~!o4Pd{Q%@njw83!Hry zcO1OSaq3BO9Bk@1b(L@&Z02}Np*<5oi_bc{_d)Y-C2sdPXz^KBMn-nf>Y-0|?}MBV ztKY6$kn@4ge(gR2=@&Zn+r0zQFLdg%dk17c=;XJ11f*{CQ95=Fc`STxMfJoIdP*DS z^jm~c-dI>ybnnE%XG2@(!@4StgXT|NRhE_bGXdm!LZ|=sOaT47(LEju z@Ac@s#KP;-o(Z7stAV)P`=Hf1K>T_cS9?syK2YTys$-9k#}e)rW^$Z+vv0_g5*`tD zaysYzKBseSc5|HTq`Tv2LqZ-4&x6J1TyY&x1J_8zqi0KW%_JpskskRcg?SiLXJ;KP zZ?ZUJbn3DiSQpiql+aP!uZJ=I9dt~Fij$u)yqAu>J#PJ>C)NjeKj+h5_<+aFhd$d{ z_(13L;J@gMU1Id@T2CKl4^I`>-0w zZ~J0CI;XL)$N4p8E%~EsCKmScmQas{=RArN3uAO}I_Ev3rf|CB=*H2_2YlT5qzIWK4%~Eb7P9o&Ix=V6 zba0N-=L+$|2W~nz4_W#XIx=V6bdXxnsTV)^d>t1${-kh$s;@Ix=V6bnrQ+KQF`&AGqn@YGmnabY#xB>EH`ae^H1ZK5)}PYDG`gkvaHFI=<}q zI^kN!(T$^<5BQ4nSubRcIB?U!4am|r>d2gN)4@$n-z>xrAGqn@tH{!~=*XOL(?M!Q zr(XQvTXo#-_-n##j-wk#Hy?0^^Vun6jyQ1B!ClDGck9TUanr%T>3f9u;j>3a)4|te z=zDeC19jYVkXq5H7eDwu9S=BuP`KZ5bmQpe1HR#W4hfkf4%~F`O=RhZb!5)C>EIEk z9~I(<58QO{7_#)^Ix=V6bdXxnsTV)^TROhu_`AZl9Y;5gZa(09&gX=XIpV-g2TvkP zKcypc#!UxLJN=9hKYZY(gYP3tKdU2i#!UyQ6`gwVgMXmohmLypE=W7i8$4=y)FLxar_Ur++HM4$J6;+8j?_^(dSE^hH}5dY2TIm9h~PVwKJo=e3PI0W?pfNbECMe&FA^& z7q{99h}+tN;(xiAg~TmRVR4I7MBMVSCwfiK!=7mlP7mlcoOt zui@mP*Kp#~YdA6KHJmu~8ctv7HJmu~+H`ttoL(EJ*KmA&ZZXk(Zfnu41~i{rE_ACI z&F5Apy6q9o=T<+u^#sj0=frA*TR+i^(^snvZs!8cIOoJ_gWGvTGtT+6+TeB#pc&^{ zu-f2u{h%4AE<4w7yWY@@Qzp2Hju#acb37)z z$7|9t;R81vEaUXDLgw&+n+}$9dU+vp z_`ppEZ+3bGA#?b^O$X@(I{Ri0UQt-d@mqw9(JKoX!>b4x!>bAz!>b7y!>bD!!->Zj z&YmLwSm+zo9}DwGKKeDje4=v^3-d=kkA;1WYS8aV$|ve!EX)(_F_ut6dQBO*ZWS_y z*Ag;@-zH=XuPtN@uOnm(uPbB>Cmv%s{jiwCv!1Y5^T*fr%U&%$v8_Mkw;r-ztGS+d zTt==2!upP*8%H-E@OI~Ohmbke!c7MoI=zvQIeg%zgN>bjr;s^(;HHC3oPL*(Ieg%z zgH4^@OvoHQaMQuNo!(r?96oTJkcIw3HM5G zFC$k6A!B$)A!B$aA!B%FA!B$KA!B$~A!9i47{lp@#U!5fguR+SzP4ZXYVnC}{UN{g zkp5WB_lb9xkt{d zpV-zP@>>t-kJUU@e7uZY6NKX&M>mdcKHy`{XQGff*1}B(CpmqxkU4zdrh`+QK2^vZ zK5)~)X-=OmWDXy=>EH~f&lEC;58QO{ai^yUnZpNeI>?^U**A0WS;EivOg`^^}k?c^3&8!=Dy1hCd@@3|}l{3|}H-3@09AIQ_7g#Iv5TSM$f$ z_RC%^KC!JoYD=p&qc&__7+&__6V=_8z6^bt;c`Uoc` zeS{N-KEhf1y5k`ILWi3Vn$PJkKIj$`&F7pCe9)~1G@o<+@Ikkl(R^;Tq1ztOjMHPQ z4Q~BGGftnZHn{Z`%{V=_+TeCx(2R3_tTwovQ#9k8TdNIj*8`ey>aueOx9bPZICa^1 zhuigrW}Le0nt|K3ie{X;?0SRSHIHVTy6jqo+r5Bhoac$v2Df_w%{XYa!R!yGP)5 z4Zkb?jg0v4HynRYc*yY+o_u3`H=zWv+6>{gNY!e6NhS56Ftk(#iM%84r-%B^x0lA9-0z8Kq^z zWvyg$WYuIDWXyw%XUm$)I?D>mu9GnjGVUU~Q#MeRB+Dpc9%THWtf}l_8TrUdZCPcd zWJ6`l4T zk+qb~l2w&ulQ9o6PLZ{c-7hO9%PwOcWZYNQK*o9EeBB^p9%RgUZ6bR}Mn3Y=+dQ%= zvYE2GWus(eWjSTcgN(WO8q2!N3d?fIm~R_S$a|eE zzf6~A!U`Gt8!x+AmRH6+$atKrk!+c)j*LAq4>DdZyI1y%>^2$KB=aES#j^IYR2lim z%e6u6!dGgKU?qr)-0a^TT;6DdU=B9%THGjQ;kQCChfoh+jw+ll7Cu zWn*OIBX1^IF~_?L8_VvKv8ITuqilz)o9ty7*XK}KDOph&^B`lcE3U6~GGe_ZBR;*y zr&!?;|qSuxI8$#v^2R$=1rq zwM9mJYRfO~t0Ai@V;*EYU)D~xL`FXHW|!S6<9xJ|(SObZ{i!Zv9%M}4=-*5kIf=xV~D;$Vc8AWOZZ}WD{iccd)F4td@*<-yz8P{Ar8S^0H zM`g`rvt{(QzKnU0@f;aFo+Kk5d2`Ad$~b?VSJo4UcnxICgN(^b{*^Lva;}&M89ys) zBU>b+XZSJ?GJaatRyIRMKJw<0HIdPaWitBPLsrCb=Fu1@%eX$5%IIlh8S^0HC9-z1 z1v1V#zRZJ+7s~FHEtipxyt!p}%c!4yQ7bxtVy)>l?c#<^e~WPHDj>uZIKYvLXm^C06?S$o-& zGOlfWnFkp^C8Iv-Bp-PhqchJs)-RTEO+O=}7xb;BjK0!?`LY_aCuD79oL9~-=binr zPxeb6y2v)mIHzl5LuF@VBW0h;^2n7VF>nQs~_OR?@*$~+) zvZAt^WSL|y$mr4QvVO9jvhK1?vaYhpvQe_rvWH~smAJ3T@MDbrk&Ha#%POlNBMWRJ)`l+h>p#lDKj z3dvrSac%68u}}8Px@|K0L_g@uIT>+?N1Z2R)I(j=cU;z6_NI(^_QSr8%Gf{s!Jl;x z%HET4zRt*~>zIsZ(Hk=656U=Shh>9h@5}IK9s7J&HbQn*#yo!bbDo)}AN1v z*BAb*>m_?jHe7aIMt#&ty@8DR-7@+~U+M1!8UC!hMOI0cRmQbKzv(;vmt-AeJ7vkT z&t$|uFJt}ZvW&6vornI><*}>SrHQWm#lpW%*?nWt_KzGS2%~vd*$CGU^~7dC$r?e^+HOSs@wwpkC^x zZ}e}qELpZyMjZTE$N3*E`%K2#BpLglUh3W_>nnRv#=gqSsFQlA>kZihvM*(i%D$D2 zk$oc@C;NwNtn6!tjM2ZBO_2Q{8!!9LA>#?MpJWqdKg%AI{pgS}`meIdvfpHrWWUIW zOJ4F{lhM;ZWccCFI^s@{k)L&}XC6QN|CCLU{qB%4I`N3h{@CYVvT3q(iP2|yqHw0h z>4h_d2@V;fUnfivW)way%;1nQdS>Bl;q}56QdmZ~LRiuvWAt*uRl@SZXN6@Q zGDfc;d|p^l_?+-&hm6s05v~zd7Ooan5)zlZslpn<7ll=Y_~Fkw;u4?ytYba%_~Bno z_=2#CL&oUDOBJ#|_E}x{lCYL=t?*XiI^k`?mxVPQGDfc>TraFEd_`E>A!GFV!i~bX zaD%X(L&oU03pWez5N;AS5E7TXSU*i5)h*i`tM@NVID;av_Hqu(RkDQqF!A#CoDF?uWEZeeTTE@4ZDjM3W) z_Xyhw17RB>amh>mPQtyy_QE|v{8>j_;*+0stluNV5C0Cr*M;{wWQ&Mjs(OBOEC_Eqp{sT=J5CtnjRG zv=Bf1Sw~#rlb?00XC6QN9~HhY9OaNPI`N3h{@CXj;RnJA!gIp$!jFWH2|pB$bI2Hd zlJLB6vhZW!M2C#grwTt2P7_`bPI1T>eTMK;;Y{I0;dCK!$xHq@!q0>$Lj3S&9dXYK z$2;bq}G;g`bs!Y_n#9Wq8=DEvzJr0|Mx zfkVdVi-cbbpB7#fKIM=x`eNa?!X?6QgwF_xOJ4G?5Pm0ICd3ba))ANZFLCuli|<&HDPY)8N^pR z9b}wCdPd<3P6ruhlb&9jO`_LKBA!s}$|rDqqe zm;EgLMPVjcg7h51jj~@fd`XyD_NOeDY_p8Kki8`%FL}uK zgDj`)9T|DaL%x5=a?9S6k(WH=`&yPoc0|Uz;uc{Z*%es<*$&xn(zgop$}YmC54`qMJZ>Q7o!~eXD zdHfikk&%bE=r(^roI1|Qh(kQ)se^hh%9zKG@o5?Rpl)=V|5TiPejpNR97pIT!%ZNif=IJB-{6fY&evD7bI1f97=r;eQIQ`lwBM$MH=R9$~ zzLGJIALA1;`h8Z2&OGP&s*L`x}E|I~nu%F@9S{pXoO`^IWIj%jn-W8S|Tj=s(D~o_~}vk00aXGWtxv(V6Ey z_(?|pw#t~_AVmLJ#{Ke(jCuSRAC=MPlR|XnxzB!;(Z4Nl&A%c<|4qjI_`8gG{1_jW z(Z>@)bmr;XA2Ry!DqQpL3eo?Ralij1V;(=oZ_2n|-xH!UPv5S|=*K1*{h^QOJV$t* zBnp|wkMSWH&x3b_=*-i%bVB+;U+52gMCUoh^D2Xz$B*$r8PA)ygsfwpzFjAzAM}O( z&_{HhgFFv2xq18;@0Ia9J0@fu^YksVkn8Sc8U3S==sah6-ez(0_%YrmkA8!Y{=Fz;9zS$GCo%|gx_SH! z`pr7#(fR-0(!Vt_=J7-4bBNEQJZ>I8#(OkJpAQOI$2|MZD`X$f%h(tDN9S{n&%u0d z9zVvf%lO>eFJv9_#Lq9}JgkzDhkWRKj&BF4+3c_;2@=j+R>uU?E2x|(f3hM~*!=H7PgcXFh2yYfvbjTRJijY3g5BgHs zq4mM`Yx}bLtxoD8pT%YUEgDx7Runc9)(|!n-YUFXNIvqCzpk*l@HSy3VLc&z=e*#@ z7`>{Hc=d(!mA=#exR89+g_VU3gzTHX(Lee@Uv3pv6W%VQZ=4t6;Lo}mLi$KQsf)f5 z2Y=Sp5>hAil9%g(I;n@c*e`WZ5Bt4CSX0gIf* zb3UkpeB@;x_~B1o^piTsM_%?tozzQR^piTsM_%?xozz=T*iKkq*jCs;c&{)nY~zqI zdI#Yh!j8h*h3y?OM(-?aBtVt8jv_i*UTKvv8cSlW?rCqi~Fn{?I4#kdM5R zg{)&eai$2_59gD5rwWNfJmO9hvLE_EJ@lKn#3#>mA@$Nv`Z_~MozzR+oG*0F2X&B- zyzB!%{Hcq6QV03S%f6_SdZ~+kQV03S%RZ@-dhZj?6m}QR5+(~D7j_e-IDNLThsJY- zJ%w|G_~DN}PuNR1U)Wptgb+Xc(H98&2%i+*FI*_Z4}bKhgnfli3;PKd3Gu@p{TX3@ z;S%8k!o@=T%>N~ghid%1@FC%gLe>+9bxVZtbvS|RzV zgFdel;=fXeAO5W4JkiJX!Xd&{Lh`*Lr2m`;_PIegOt@M|pXe9;qmT57e$lt*gv22p zaXC-aK|R#9Mo1ju5tlxahkWE^Kh#OR)XjCme%Uws-z*#{d{sC~xJ5WxxK;S5aGP+9 zaJ%pk;YJ~OcR8JT{B{bdgL>FcAS4d)h`U?Jez+dk&mJLp$xj`e59*~~)XVvxPV$lW zbs>GWeltd=F6yHm>f(G+2lY@F`=Ac$p)UGO9n?cz?3+5Mhq~xD&+UW4$ApK36NPUG zCkPKX{Y~K{jgJT?3l9tN!yo;qaEkD_aH{Z_5I_9U-x5v}z9XD2d|QYg{^;)tX9!OS zXA0jF;%ELBHGV?lkA(Atp9on`9M+u_J}x{hOc9jiby6>N zbDr5R`)2=N2^R^k3ZE8!Eqq4!jc~E>Tj3JnKZH*TzZ8=9N2fE7-w#6SpdR+~vyeE% zBkoT^_QUyPKfegcOMdF$d{8g_qF&Aib&`*~zY6KI^_wv|bx|MnP#5QmI;e-b*avk` z4|UOR>YyI#V&Bw3J=8_NR|^NGkA5xro5pK2?yK=q;qPwTTjLis{!`<1!UvrH0O4AV zy9r+s_IEn-_;nEyhjpy)sPP72I^i>Z~E8{%USqPRRb)54z2>Pxf0&NFQptaakdKqA%z+Prv9}9U=R=&5g?l z*|+VJdHP*f$bRVu^XZ+=`QW_Nck}o$E+wQ7^n-QGb3Wrj`ar*#&+2r}H|PC!H;*6V z5<>bzpIFB{*UKG3`osBT{(7f#J#k$%a`X5xE-s{R^pADSb3HZ|(l@Rb<}*2+>y_)a ziJQldaWNtNq_3=Fp6mTCA^qffWd1s*b3brjG;{O#F)k{kkMx^$%+t5Kh4hKO&>#AU z-dxCicaNLLk8u$p_YL<6>zJo+Erj%mzR(}~h~84jecj5<kDe@~e>{(w#}B=Skk5^t zZXQ3zHwx)9{bn8W?6;SYeXt+)&HmAQ3;De1$i*C*=9IUpU0+4+?oMRTT2v zddTUlV|`5_>sU`5*0H{_kaes_XC3Qr6TTz-Sop4RsPG%%r@}Xc4+~ES&$;6|B0Mg9 zTzFNuR`|Jas_-pgituaU3&P97X~MUKtAtmC&kMg0PIu$yg!r#^y3I32CmwO1b>lTc z^1dh}zU5(zPF>Wu(v4pdQvb_B>a=p-|T<6kiI|a#_XRyY<4=xc#DwpV&{i3I{T*&ONE@*Np4Jk>BCm1gN(Ne zIq!D<8Kct|`m za^2eX%NU(L($A-aT=!$$nCp*z?r}QEc(0KA!tM{o==7I9FBH;0`b59z6aCufbdd1@ zA@|*Whm6s=Z@7OJ2yhj7 zEvJKw-x2cMv*#aUbe&HqlEPHU8jSLPY8Mb+H;mMI{l^3Pq=xI@kk;4 zJn3|h@o6E??^6yLqtjpdJlE+U;}JsodB*7=vy z{!k&$FP?8a_dXMTEBsu@bCc&P&!0<9zbxeWwNA)7{P6!m$oe%x*5QXvJl1azvJO9V z;@RJ89@%5wpk!&B2mKfReKEm*`jhten*URe|9YZ8%2#1N@*gdrcPlr(UCVOm{e+(k z5rk+wv!`eCcutS!^|(T(#VO+Pn8!hV<` zU*qw$9$)YA%^u(8@tq#u<}E8Ka}#FJn4b(D&+PGR9?$9VydE#$@gg3NdAy{@%X++m$18ihy2opIyspO^h-Zt+ z5C4pfy+{iGoXB`P=a1ge<6S+T?D5_n@8|J>9v|ZI;T|99@i88s;PJ^GpYHJ#kI(h^ z0*^29_!5t&dVH0~*LZxb$Jcv&v&Xl2e5c3vcznOd4|)8k$KUq&36G!g_&LWpedj&? zsmCvS{44Q{IP2#-Pyfl|zj^#GkEheK-mdeE9?v3fF>`u)UXK^>coC1sJYLe{WyLLK zB~P#F@tPj5?eY2^zr*8qid)R)p5Dsi?L6Mm<6S+T?D5{>7V`m5ALQ|&9)HB+qdh*( z;}gX#=5$X_@%UViFYx#xk1z3fs<_2`-qT<7_&Seo^!OH!zvl7X;udqiryuh8QIEgv z@e>|D}-ack*~wk9YTYPmlNUct4L1@c1B)Kjd-V>-Asw!>>7vkM=mfRx^Eq z$0vDws>f$|JjLVunneGFKm2;hIKP%NzR2T?J zcoC1s#4T4@Pp{zd${w%o@me0Q>+uF2Z{+d2Jl@>ntvue&;~hQT)#J$?@9pt^9v|rO zAs!#@@sS=Mkd3=w@_j&xF$KUk$QIEgn@pnCb(&J}5{(;9o^7sXhf9mniJ^rP~uX_AjkALs+ zpFIAn$N%v7HIJv$3!(M-I*(`ecvg?!;PG4@&+GC09xvqaq8^WVyoAR~d%T>-D|q}C zk5~104UgCIcpZ<|_xSA|Z{+bN9&hIHdpzFC<83|O-s7D--qqvXJ>JvfeLUXJ;{!ZC z$m0)re7MI)czm?S$9jB%$0vDws>f$|JjLU4JpP2o7kYe=#}|8ina5Xne3i#nd;CR@ zzwGh#9^d5gEgs+Q@tqzIJigcC2Rwer<3~Jx+~eD4^>}TM*YkJ-k2m!AogQ!M@#Y?H>G3umzt`g(J>JFR-8|mI zYXmeTJq<|Bnd2e))Gl+h#_8=JT&}zidp2>bh}Yi2Z6@ z4bl8A{}t`S#{V~5^kU?{=-vO`x{+=ES=Y7y5B#uydW_{|@Gn2Vw2SuRZv_{B{Qo9S zZMGl#J$$tPzy6FI{2$NH|JM1ShyT;q#Q3kyPv-B}hWG3LG|zu`{r_}N{(ks%(7*m% z(`H|BFV^gvKV#?Jp~l}GH=WA-Lj3&5)$ud`XdCyZ8fw70C$ePpbbC{e{tUc;=Iy!1 zTKvzX|0e9?p+CP4anTzQ@@Ll6xyC&o&7XSo+rt0+Gj;bIvp-YE?|<{hp0m{VRT+Ix z#65S}G(DWP;JJAGuR`?(wyPM_AF1!3>JK_iTb@6CP`Ho2i_${hC;o9t{NSTC1N^G> zeD1XyZt-%F9t+)f|j-``9}zZ6BeA-=$}wedJKQCq8HzXCKFw zc4^E$OpD?buFXEE<*q;Hg?{fG>-r%3FfFQYeP{MDT>CY@TdGEWrr|fFT;!KW{hQ|U zR_ht%H4VSkZKAwIZ)ja@dfpXbUVS@c`>D;SJ$+-FN_y_?Bb2u5yjj0SdPHf#?M7-l_MDAa(uHk z%5h})*kIk5c$~bWc66#tEv#+TeP*b|&&_K=-o)g_)$%^SDAe?gtq$Zpp?$1$Yo|4e z)*evJw)XWB(c05hN5tPvA{ z93A>i%KP4X_dTNT;2llc{71{jLrvd!JhuE{eRrtph~T)3x8-aU@3o(@2DWzWm1wPU z1P{9$x4jqTcu#AWUaK7^@2h8%3(Xsmn84bOd8UQhm@Btb==w-vLiH&Ps-3^KAk_2? z&#Ft6_nh|egj;)eDc0({daBvhPR`3({e6nzmv(2U);(T2W9E3%4+gbf+a)ofQ);uI zdX~bWR$$xsDvR$+OnCj_(ZSHlh3nzh>_vSazq|g9f!6+Z^zHzy`3FnZZRw)#OKWYZ zRhdGq$hL{cm#g=GeA1&@z0{tera#Sn{%iTw8Gfn0t=(KQTKmSsqvH)-jxG1r*{_rboj15)<-w`b_cq%CEeu`BIbdRhBAm6XmeIZ7tcq^?Lc; zUu$c%(+P!w2J36+5mxx*Q+j5V8}-HR0lSLE50@zsKYPA-=~C)Ttx;dpZ<`uvC5sg( zT~^wVarvu1R!A6lw# zJ@Qu7bM(w8{hgRhrGnhQ>$;M5{LEk1(AquFw)%$?E5^|h6RwtDsQOM`$rjHwXH2kX zMy%ej;_qyEL|=Nj@yimMhc{0NHJ!zEGnVT6XctGu3)ie0$8TZ$lM21`%$oS_)A72x zuFyVduJ5-#t?x))92t~xegpPIeji-P77Y4%TDW%BN4GR32b$%b^-+}fpw@OcRW{5! zV9!nOkt6rX+pBf_V_~>f-+*jQEg!08i#M!z6i;j8UEJD*@o25;3r4v5ZrT*p*Zax5 z@s<6j1PKpkua{b;*b4`Ay?s4DPrUS~$-$5&UF%;ewPo$NPWrBBn~CuXjjM#(_078+ z{-lGx*O_lc{5HkIZ@R95WNFLx+`M^Fnfalgu9WBccMjJEW!>7JH$`i|pPwhN9EHzD zITSA#>EexC8O7VxW@7O3szz~g{JiP88suedmU6R0t?=1qZK-e2;%3#xoL&-Y`i5s` z^6pX1SKQj`n@4MZ)ZT3ErBcz_Yc(guJAc(UP7j*3{PETlJp+HQIVtGlv@Po+?SUur z2G*n|(~*Xr5cZHNA0#YI;uY(83LLD*A$F04S?S$SpnTqDP0dWPSq ze?W17;!Q6&CtRC+=0baV|EKaUOsN{K-LgLGA!>P0=g9n0%cS|mpLTxDT1I}#5sXs* z$T5CdlwpJRkLx_H&a=@gg~ghT8G` z2M*_yRx5YW&F#ic4K;m}_P|fG)R!ii0`seSIr3Aypt6hi)>~0LuPcG67T!16GN@)D-%i~A?EL1Pc#q0s^YS;AK;JxyI)AF>?-^OK z@=NcuJS|Rj*i!YMemPqd3H=iH{L>E&zh=iGKl1h~Qe1xeT%7RFylCWIp!=7+0|u_E z6U8et&*D{gTAmi=*I2w>(xUpx%(MFNL&J~yzWHKGyw2s4@$0?){#bW*{L17?p|-T) z^<~)aEhi_%_Y|!VPuAK6hpu%eUiGpm@xiav2(^v*3lAfn`Q^~w@S{HSL&J~yEN@?L zzm^w`yzF=VU(@2-HWrPucHpgvsl}KncFz@R(+6iNMLhG%rTXyWe3%~^e$+?anabPW zyPhpC8hP38hs7qwvscO&uvWVbujlhaXU2CQE*5GV^Iu9Kp7~K9ew<(PL&J~yCe+DZ zW%%3k|8{;;rImbesnf1*V80D2Usn%5diDMwJ=YZPc4_#bo$7FP1M$p{^MN1xH9s`` zs4wB=HS3AzpWi)e*Q_UAq+Q*>eid&QeylB{e-xT{acTIWm0uIZGe2sZ05wD^&YSG{PwC8D1!D;xhU(4(7 z*Ycu~m;K_`R-XY;y!QvOU;IqFx*_d(A8PE)`}_#nxz7*$*zeUZrUWCW=la|E{l4z( zpiHU4q1J0>bUjx*IVt#kSAp>1wM}6jph9CPS?+E4f z_iK64$jg4${WUH4u}zlnT-x(~Q|r0Gs&Ddy+Q_-l{b7FW7eCIg`Jv&*e*3N|5Omr; z?r;0e-#95)|H`yb`}s`ty!v!#u3*qZkA-`)=ih=Hg@d8{XM|d&Qql9k{Meg4Pt6Yv zKlW>R{ry^AH1e|FD<2jMPR|(~@Ofm<`^BTn1>Zh1CEV|&l<4_qeth=W=ZE>RU;Nnb zm^B5eS3b8iZqF;;H?qH4tUAAs6=``|aQ_z8J%b%q~V8FeodYh#4|shFLu9~ z9~yq_cVKe(KCX)thMLC{nE&bcB(@Z&-|#xo-gKyh9C8H ztyMTU|N6|hfB$AXSu*%6@BC2v`Aqcrd;Y%mI^`1gn6NMDJA-R zGe6FyeSVl98h-58^7`l3@XU;O9h3gH-)E<#1b_W-bEx&&8NFZiXgW1GoK!g6n?3LQESViV z8k7vRk#nQ>7yORMkNWKM!~D?jW51Ty->>CGBQNLo<6;wocW0FgWki=%@57qpPy+tFOkrv*)S#q1pSh<@NV#dC~0s`GdKI~Z$QwSNGv$nz`eR%W^-oU( z#kV0`aW(?ZSOpZhpJ>a*vm`JvhSbN1&a#0!6zs)Mw8Z^Fy=uXUl8*P1JrZFPgnS-yRf`dib%{ zKHsiL!w=2gpUsb2?DNw6(CqzrLWX7QiRbS(K^pNQEqZ_c;+e!<8RW-W`#eXRAU`yF ze>OjAvG)V>L$mj1J=cQ!_5SQ%&w8$f@6TxV{*2!zh5y6zFL>W+_Wq3DS!vW_@6W`; z56#}6Ew8^{%Zq03&-ks@XFwD$?Rg)pahkn9n;-Y8_xTa}q1pTMqLvebUR|=MonL-E z*rfl=;Nw^Gg_^xTS7+jd{ zqS^cN$+?AtY&lmGUv-jsFS@Q=eWv2aYzx(>cg4(yw2{n6v zE}7$|AXa=zxHo&=r}i%syj^=%sM-6o`B96#Kbs$#y+2!C|MS=KqLG)+qXC(620vAt z5$?@C-#+hAFnGSnyil|EXY*rk_Wo>sX!ib`v1@j z^Sdw*jd(Q^s=0X08lo*rY!k+t+vdl5$t8--uYX%&Lc>L;>ysC)=d)XO*1JuA4knGY zk=Cps8f(cBt-Ua?zO>O#P7n97zV(mw9v!NGBTky_tyQ~qX}>l|SEf|F-<4Ux`rnT1 zu5l%CPqF#>_bh7nh#xOIIan)gNX=nq(9SP!6|cEPzqd4J#_s2e4_~xJe+hPHr>S9% zR?;pE)W5f~Gg|v%>85V&tTL?Cci2O}6ve{_ZTrcO>JL)hc3OL7b>bd$Y8k$whO6aT z-2D1Wi~O#|@uQZrS@XGCQu?ALKl*t5UDe!R>yTYLMh!coGZ$(rRZFDJD#Q7t2l};h zs6}TtF=6t_hvIi?Z}`=@d)Ok@jxYE^eT&z4D)IF1Hg);FzWE*49QmPbQ!T7Luz6Nr z{8(#w8&8e$lEeJ!+#UH*%e1NKTrGSu>fg92FeXl4GVd!?|Hh7qdzv*&OjvobP}uv* z(z<*!d=XmS)Lr4V_RHQv_32YHeaQwt`V#r2^hF~c{fpvVi=$2KdS`gf&mEmp|481% zJr@QhCLA6+AUsDqrQyT*{Q9me;dOxD+&71vF^%&ZX|u|p5s&j0#k&?q%iisdc*hDe z!gF~zcZGx9=OiZd&sQ_vKKFv)M4^ql*Ib|b#g!`(6SlYM9Pjq#>>x{*^}9EgKfW1F zeF^@}Js8+M$i3k1!NBf8?iqIv?zVf7>)zdif!%}2CDQJ}!0y4G&!*jjVLa|5?rC=q z?zVf7d*9uIyX_ua)-~-O40A+l)9%4=AKZgH58OQ%YX5Q%26hi}J~*Gc2LrnY={=gR z`@rr&t^;=uM)7nX;lsVaTDu45zL|CphJM_GoL_u(5AL>mkZaA|gMr zys=~2Js8+M*yW?NdocL>Jrmx8!N1-MoZo+Q4+j5wZ|OeTZSkUeI=lyWo1fjQx(9dv z>%GNV%e!M#+C3PUpWUm}@^9|J!0thwP3|5Hd(XYa^UvLb;kCv+$Td%2bq|JpiTu*; z!NBgpE34D)!SI}O&+y!J_h5LAc1q)1xce=<4)EhyY?|GJLu#hogW-9L;-%e#f!%{O zucqCDf!%{0e@eRt1G@)XoQ&?lyN5qn|D*IPgMlTE)vt8A$BO*=#}IPVTvUI}qLsl- zy|TrZoISndV3Wjz^Eq1AADTEVNS*pl{k}_^ms_$uF`>`=cJ=E%IW5?-;D-3g)V(F| zz9%uE@&4KI=TaUBCVzCO{>|O%fAH$Y#DrI?&xzl*WkB$JgDmlbtpnT77}lcQQ4tzQF41yELu7!0KCaHm$zE>Z^1*s?YviAN%gH zeFrf;5BdMaAN!6s`KwpZcNyGwzUwNdeTS3pNa|msOrRFNX9?%WIJ)_O`m2iiNlLg* z$Ef~@o1Ra4MqK2RUq}4lnRGNAjJ{i%6#hS*nZxILPiH<0vh=JvGH2X$Fq_k}3-QAT zZaR1avh*A}GH2ZMyi#&H{YD{v_~g>jbTBuv^gKE;XWaA%Bc1-?hYxwkVKK=82TiAc z^bVbV&^tJ2I(5Zl=)`=;@dCnvju#R##;353jNwIeEb918LdNLDbYu*V>BtyfT*nwx zoGYS)Pybgbd{okHtF7xBnHmT;H2?U8l&$~Ys{Qow$go-!U(fY&MUdYlp z4|eYGwQECPQBCV}9r4>__}{8yZO1|8(3!97I9Mg*B|@y~bg;VPAp2x3d#&j>Sj%yc zeE8JW(R!layh_W(FHWq6xHx|J6Q==GoIJNX4q6SwZ{T#$)`I4beus=WXbl~2BqSd? zXg-x#hy}-&y}{9pgHb<|!rt3j(AIKYHwFcn4(Z z9d%?3@1!GRcxN3M!?`yZ!|4Hifs@C2f`%VDv7>u7DWRLV`E(bzn91T+1LwllvaYAh z;`ef#{B~Zbw~y1wd%xrCwXfssqo3m-xrvD{HNo+vcW`nhL&b@0YspD{=+t8I(JXJW zxYa`*tCQHa7joKO%8Fah=>gA2;&Y8yUgFp_#`Taav)b(1;Mr{Fi}+j<_}r|c^_d#D zZqTVEx_)Be^V`m8Md`QUBA=2v)^fa*@K(ob3%LjI;hwPPQcdyF$kHq5$n%k>F11w+ zc~W?PQxiJRxiTS73ZGT%4EaD1TfLC3*Cj)Q|82ZuNgKIHgN;V{R+ z;f{mkqXvBOgOihdaI}cW!r0lIo-a+0{4+S68nQT^S78S!@?1cj}$)Q>BiB`2OQ;mMhlrEKHPNhQDo_3bY#xB>EKwWj}zjD58QNc zJhJo&Ix=V6bdWuuQ||<*_+vUwa(uFIqT}es(ai^(;(VqGnIjI|bZ{E7^yxY>XWVpf zhSO&X@xuphI`}xU^b{SLGj2Lat?1N?AAFXMa~z*5ob5Qeadh(m=Q*Dzgv=2KZaO$0 zS^5GUnKN!WxX|fO3h~1SZaVlBvh+ndGH2X$kXq5H7eDyZIxcp6iSQZ6(T$^<54hC% zEE6(E9JuM=a%AbLIx=V6bZ~{!R|-=-ZaVm^(^m=cBM#hjkXq5HmpS-zI<9tnjqrKL z(T$^<5BP%fc~Qt5ap0zdFCj}`t0Qy9O$T3g`Z^(g_`ppEUqP0>UPtDPn+{SdI`!fQ z-=O0r$2SW%I*x7}-F(1TozE5_bHst04sJ!3zD-BwjGGQ_clv8W{P2OB4(>pfzEel$ zjGGQpD?0V!2j8V*;P@WlZpYD$qni)-x{S4Zb=(7W+;niC)AtMU!v}6UcmP@YK^>Vh zZaVme(+>&p!v}6UNUiA9%N+bo9gjGERCw5NbmQpe10Hie$A!!h2W~p}7P9oWb!5)C z>EJs~e^-bfK5)~)_mHKZ(2+Ugri0XqPQCcSPwIHu@iW3xj-wk#Hy`kQ=W|xb9C6^L zgC8JEKc^#e#!Ux5boxg^{P2OB4t|U*{k)FM88;oIR&?q;4;8ByXM(?M!Qr(XQvS9Scx z@o$A+JC1G~-F(1*IG^u?%n=7}I`}=Z^dEF&&baB|k52zdh#x+1)4`vSrT?NMbH+^v zsTG}i@q_=W-8j1WfHyjyd_v}k12-Ma@ALvf z=J0`=4io>;av6nS<95-s*TQ zVNFjrj&45SZO*5*kU8SOO$X~Zy{?cseBh>o^_*T`$Q(X!(?R_u;81TMWDXy==^%SR zr(WjZw+kCO-bi?dryEB%AF#3Wxl_m-ap0zdO`Lw0kU4zdrh`qL-b~0GK5)~)yPe)# z$Q(X!(?RxtPQA>*?-90iyp^zpryEB%AF#FaX(MEgIB?U!woY#+WDXy=>EOLiZ!csH zAGqmY2d8%wGKUY`bdWuuQ!jJyPQosZcNKQ_bmQpe1K#I+x(S&h4%~FGyVH|}%;5t! z9qi%sok5ftwBv zaQZ+YbNIkb2Oo6$AR%-3z)c4SJAH_dIeg%zgX{sFdYOYiBpl}WaN$r-H;!&T;KR=6 z5g~KLftwDFaQa9gbNIkb2S+)5w2(P`;HHC*I(>|gIeg%zgX{sFdYOZd6^?g&f^eLt z8%H-E@G<8zQOF!|;HHCAph0GBLZaO%}>2rn5;R81voagi>gv{XsHyxbs^aVoZ@PV5S zvIlhPWe&bVxX|&H!Y3VnR`^uNON9SlNYkH__Kfp+UTAAqi!XBe8c%;g{As7ZD73XN ziAOO@g#UlR<<942q2*mCzBtrNg#Qmji@#pll2F(0V+bw&M)9Ri-z2nLo5hzo{Z*mG z+#+sqwu;-@ZQ|y?UEFGWP2AS*5KooS!<|BlvrF9K>=w5;?1^6EPtWKzoF33?em|Ug zS2-OYdW{eJrq^)d&}%sRrq|{}uT7`daB8R5aN@6ZoPE%1@=^o6hO>`XLLTL!*XZnt zURy4D4QGG!8csd*8ctq%4JQ}9w)pfKotX3*P8@m-r?2!HP8@n|I=wbduZ`1dIKDo& zm}ow?wdhs@n$Im4y48&4bE^|Q+M|9iD~*>3xz&#z^`t~tr}c+(Vzt4opJ>MEtJMa# zbAe`@b7Hl@?L49x=X~0^fZH{IW}Ium>V!wvPl-@7PF;4c;dZ^D8K*A0PT+QZq8X>I z=o(83YqV<{%{X=0^$EB81y?s4KV$F;pMjmJVQT3bBKNi2NOSkJx2x`-DK@ySpx9zH`N z|5*6{JdNTP5C1=edqbb3@c(VN&+)f~`$JwL{D01w{;sq)oX>kgTYEzMfYVQU`YG{) zPCqTQwP(bmm?gsh*V|F&b5>}1KM+3@>LtSePoTyBP}-ZJuHU;8TKtd24?F$5&~jZ6 zKjQRHgckFnxW)NY+}2(aH~-JXt+vm_ZS7_8V>0^xh0x-BDQ zdO)u^chvii)A6C#__J?%4JQu0hO=*aZ9eqcbb1Y^c6tpb{`-!z4|+{rYM|F}_Hi!c zQ7(Fo&YtMC<)YVc_D8Sb)I+b~q1UFvM~V=5t$%ZZ)9!+;X8?&1gQiI?8sTSw{wAJoO5Ee!RVwJ;RBG4;t&a*H{;P8-+LVW2d9I~;X^Lt z=$7|a@t>U!_>1GbMyGC0d8EFsG;OOj=hT`$824l;&kbv&Cevn-24#`xSI!zYJ~x$F)Zqvvv3ZXs)O zI%JG*UKzeO%7~Rmh8B~d6_XL8fQ+@&jz2!EuP(!wc)^Vs=9X`czy-t`B4bWoKjqWanhmNo~|Y58?lZy)%Kgv25G^w#k&tgrsGgX9=0s z-rJNhAt4bmhNP&7v>S+`0ZAokE|q2>YZIC$%_>Q=#;2#Jdel?-j{kjM=W;5Kr?({Z5<2tYNI?wAm@9VzTaIcnxz?`ZHjHiawotoi8y@^L${IM~f`6h%$0)0ma z)QP!~r0>_o(&Kz0@ z)Z%b~TC^0XE4AbJ9VxJ;1_E`a-t?ha2lHj#O$FB2N?;swXMeO7sA+wHIK;z_5Az@& zd6_GI_!krk1hf%z%xVi9)3yThrmobtoxm}!E3gjMgB=hDf5uT0{O~6>IjM0{pcYL8 z@{kWZzO0k=u&%}ee)uzIYMm>bED&?JK<*O-YBE}AFN_hW*$|bA?hu9w zs|9Mn7}oNT&|kPx7$B??ICk@dqlG+yoc9XkY9_FUx(cl20)d=Mg%bq!3^mLZs0Fc( z6BY@5g_*)J!X*MRu#FJz5m*CrrUqSvTEf{vKjB(ouyCQkUd|E7Lp)+E6pj^U2+aFp zp}TOtFj814u#P-|c^@T^hj_%jMqnMRhx+2%P8cUpE7nfEiOI2|rnv&ghvP(TnOC;J zyf+E#!C?aa8wJ|f>1Tc9-z=~fs6Ba~7N}dU!1^8+h|lrn+~_FO7oHS27Sx<`>M4PE ztc&$MCa@<53dAQ5@u(Z~WIoJ`c@v*JtnXQY{n1Te9?XY%v2OBq5b6rg2<$g%$2^!1 z`=YbZPakTzgbtUR?$JXk+g$Fi+-7JmM0cnllgP!@M{)#3L^8I|`=%y52!S}vpMKidIbOsi{$T>gpqg-;&|8=# zuy@W8vIO>I4dGa!moQuCB1{*m2z7)?LO&_76uBp3&VuF1lGxVSl4v|^IR^l?mGqMK|b;>6sQCBpl&M!*1>$4_ca1_ zq@L8}4uN@)kGxk2)QNi46*>s@g+~PXnIH3fNa!NW7T7P%1oDuNyz2z^0sDb{v05M> z$AfyDFA%q*&_GBDBsK{;S^zuFj06>I8}H_m?XR`oF=?36chPXWQ79r1XNSNz*2g;E5*Sw~ zV4IkFJueW4cx8pE0uSO5mwHjR_XXBoS-_7rcIrxfKM+_qbHI-__E!YzKt0Y7J{Hat zJ`$!1JB71^4+GQ2{;9w*{!ExAd=i*8_AdnX#FxT!;q$<>v417Z6#g!pBm7MuE_uoS zoscK|L%Blcm_(nKa_&PA{Jb`$`WqqvkTj4z6d*OUxmvEu*gK&ZH z&%m^?|0v8BeiCL0{|ZbS`)*;5z?+JTL_Z5KZS0xii^T=Sb1{pFOJ4F9RePTJfZ&He zg!nRXvB0#kmlQ7$ zml9tgJ}5A4>}AA@#AU?`#iaw&#$H~$SX@DTrMR4!xa1{&74Z`B!D9UIXB=^fPkzQR zo__rBuOz-oTrn_h?8GB3>tmgj#aD|D6<;H+DqbqCCcajDNMPF76XIp!>f-CfhXtmM zy@q(XxTg4eaaLg3*lUY#6xR{oAg(1QE_un{P<*qvo)|y;my3x@eDdEQW<34);a^{T zlelhR+SrLlT-L`r8;EZaHxb_|ZY;iC+*EvsB8AwE1XZR{I&OnicP zgP6SJKS}(gc&HdZ{251F;*+0ojHe$z{D+C35T6*BHg@6>m-VsE;o_&nqr^{(M~a^n zj}|{89ub%}_OasU#V3oO6ORc@8~b?i7V!iz&$;6Q)5bng{F3-o@r&Y9#Ka{p`KO3q z5uYZ;4}ZoHm-yso9OLQ75C7A}FN-Gyrj4C=#ASV~bF%nV@mb>6#Ak}Pil>TS7oQQB zHukf{Z;Gdj-w;m=OdI<-;%(xY;Q`CH_&oQp{_oI|I|key{jv@qOZ-#P} zhVY=kYohxD)5g9^$P^wH3J4Dg#3e8J*NF=WYXto8XB=^fPkzQRo__rBUn>+8RtKhy zop{7$eXR2l@d3hmp|FrI6csiIMTAsf+SoS=#f42mG2zj`w6Q-Xln@>l4iq*Arj7kc zp``GXaFFnXKwR>Y|9PRb@Qi>T{){6o@yX9P#?y}<{?7`fgr@`3#!fuqvOd=NoKQx1 zQ79{H5y}fM3FU+r0@KF+icnE_Rj44m9GEut*M&;LR^edbwZOEozbRA^-V!PcZwSOC zFZp)}RfV?&{P1TSafwfU#xb6L{P5o{93pHBOdC7#h|Bs|=R3lo!uvus;XNTCd>|Yq zyc?J{_K$=t;bWn?@L^!u*gp|!3ZDu!gq?wDWB**JEqo!=53QdF`0@KF+v(QY~EgUZVBoLRptme-)ovj!B5o-zEN&$(Dn3$tKw#R~i;LTc4-~f+7Yj@q z`$6J%;*#RF;u3*rV=pbv7MBqx#ihi=B`^6aigU%~#Q5RQIN}nY{ETBf{rKTuUYsK? z8<;kB;t`kivCazO_TtLo4&qAUPU0%!j^cv@)5cy^e3bZ5acA)%foWquOx#tR5O)z* z3rrh(mbkmPhPa!!x|q1+C4XIUPjM|Ve)uzvxWp$v;}}mre)!iG_Yl_%OdC7#h|Bs| zXC3j;;s)Yg;`-v=;)dd5#PtHx#@<-mSKLJ0N8BhdZS04O`-z*0j}>b6!#GS-L#T^3E#(tFeByktm zp5l?>ZeslKXB=^fPkzQRo__rB?=Bu8?i!djcH$A2^|8(#;!)yb#G}Q%#AC(1#bd-r z2d0g^uXvpJSn;uIU#m9?J5f2a(m%QXZQ9Ma}f*3#i z8An{=lb>;nryoE32a8V?4+=~hJMoCi`dH@>@oD1W;?u>$#8bp4i6@JP2BwXDr1(tn zDDfHM5rJuAA0wVB9xFaeJUTFK?Bm2|i^q$niBA?2m%QYkB%UEYMT{T*j3X}b$p4=X9T8= zeVX_}@!8@F#8U&)#y&$lTYQdqmUz0Dxa1}O`Qka^bH(`K&p6@|pZtttJpK6LpC`UZ zJTowD?8GB3>tmhgiRX%Ei7yskD4r*tExts2L15b0=ZG&A&lS%XUlf=&_DjTT6kjjCN_>_0Ch_;;yTvz%mx!+w-zNS}e2@4h@zvt%#CM4Q zA-+$1i})JxGVu!WSKuM{s8uMn>iza?HLzE^ym_)hV|;;rJ8 z_&)J6@m=E8;#bA_;`_zdi|-b%5x*pUMEqRf7sdE(5ib|t6YTim|8nTZkM=WS@(`DC z*8hsy%;NlyZrrOl;Sut^lM?ZC>p4&n{ezczya~!BU@)V;hoK)o z+MC4GnR+vhe)j1{V(P~}qW{)lXCJelcZPoaXg?~Z&eWT6^m873BBp-qQ~GZX_D{u} zFQ0{e{AjNiQ)lYUIQluyJ{MEJJ3{}B!TyDq^YP2jk00%nm^xB##?epR{wAhQ)P?#` zN9j>AAZ$dwQv>y>K5p(`Cj(+O) zt(ZDd7wSVDv2&f`diBrHk00$d;;Y15Ul>O}b=xJTPSl0^QAg}t2e}^p5c=_>{g8OE znClng=%;S~60;xKf7FjUV&^)`_4cRGk00$-;%mfQrx-^+*Xf_d)NfhnzdG1=i@Ba> z=sHh7ezez$sWbJ)PCxbnV(NEY=*JH`&l5af6b$|N(O#{7>P)>EM?dyLV(NEo=*JH` z&qF*P6%PIQ(SAVv)R}rSj(*l#M9lhF59?c3L_u$Xc56Th^0 zgSfnyJmkZEklO3T6+%CL#96F9UBMy16)4x%CnfP<@Jn;+S7UE6f%HkKrbA$b0 zG5&{$Ulz|6KQFEr?1ze9Rr`X_k01W6#4m~SLO*`QPl&gQ&k>V{e4E8t;y1--i?@g? zi60Z!6mJusC4NO*Rs6WPws^aEiug5gHSrVTy5b$;)5Ncf4-=EOz4$HhL^1i92YEY+ z-xiM-lb?Bzx3l;i@mMkWnFo2hh~E{D5|f{KkT*yChWI2g`I*O);wIvc#Y4sKiR+7> z5+5%9L_AphzPN$-X>l|0r{d$qABY=@pAjD+{!H9o{Gqs!_*ro;@lNrv;*Z3QL%XT? zIko$U8P_-1VcPw~_}e(z*va#ZxMyfHo;bb5%pIxW;}6@ z5wq@r!4A_NBxb#~Zra$X(^KN^q0M;W94)4i8dx)5NT3u;l=QwaYx`j65i9?;J z--*Ev(;g<~c-e8Ijh%W^_pV}&clFSw?$mR5u*0-Rh}jReFKA<@zSQ|BG5e`rXmebt z=g44(X^$4O-)!H}#!h{yb0;zTwN7ZWZ>Z;(V25d+EM`C3zNU?x`cmf(V$O$Jq0PRe zp5uZYraeK-`7%B*ZS2&SI_HWx-)e+*eKGYsCD>uwr;0frCkCdCo%&MeY%%qtPSlG! zQLjnC4%0qe%=vy=VA|L@Umq7I#ng{FQ7`I5y(R}cO#2Km*Au(0(8kX7;4yJKG4-QP z)QdV%uQP)krae{6^~$bWw6Sx&*(`1&rhe3kdQm6pH7(d-+SA2c5AC{08#~vtP2!ef z>PMZZ7yFTYIV0F%+B3ymZ|%BE8#~vnC&WjJxn5TZ?Zd>>^W0#EX`d(NdY>1VHg@Vu zo!bUGOuL$xdY&KbFzpM)JfB<;m^OCmOPyN>J50N(n0n3%c9`}>VxEs?2d0gk`cmiS zV%E+2SSRaaopXX6rhT!P=fSywX=86C-Y9M%CJ*t5d!_gb@fE=i(_Sq8TzsjRc=N?y ziWiAFUI&N%%f!U3C?+oc9OngM{1=Leb9rbJhj{aXoqqiAXB_*0_~c=Jm4f{eG4rzd z(#D^-#Am*L6E6{eCB9nxwfGwG-^Eu2rj326_&f1+;%~**2BwYudhs{n<>G&cmj$Mc z{YEi)$Vc8A#N@F!=8v6z@)MW5D#CM5*6yGVv4}a`;i^)Sg;;t0qhd+7APd|S66OXv$B@XfMXB^|1 zANiSw{eN&%PrYK|FxfaS^9daTnEoSqc4DEbbm4t?}EOMk7l)15xo=?k2`$m!@iifVI=_`adV;k$#T zuX6fYr>_rMlyQ?_^SRa8S2(@W>HD2t<@8#o^PS%4^y5xH?ez0bzm(RQ(f3cICUOjBU3L3H?(`z*BB3VNI(?(_xz*_vPOo(Oey3MCz1Hb`r+I$2 zW5H`7)4cw)y~K0BX|B1ZUvv6R>B5>nj~&kbfzvyk{@m%Woc_jXo`WnV&rg;&Q=c1{ zF6?x1r%O6r*6E7U7PG3eC!DV7bX}(#I^ERi=F%3kjk9Mv-NEUjobK*)FQ@xTTg-vZ zKG^ADPLFhYtkV;mo+NEC&vf>)ou29R`A*Mv`eLUqmA06RoPCMYOPyZsG@n!2apW^p z(|1W*%=?|4&&SM%&+bg;JH65A$DMv!+G1{T_E((V>hw0JcR2ll(>tXtCZ7r0eE6K! z^mk7G;546Anop)K;HC>pTg(#9UfSvMPV*Yt#`6Bdbi(PH(iXG6vp06SnbW*Zw>Z2< zF`eyn2WgAh)!BPG-P`GYP7ic?u+zh&E#_!vALsN$r+NQub34=Nvz?wPZ83TOX8ydV zGCj{}-gB6p_bH}%FJPM2`4*Gc_-5yIx@lfpo96Yj>HD2t<@8!$fLPJiU|r%r$A^w&;*Cv7o*baobKYb@k+QKw5dUE1mL(iXFdvsZIE z%jw!q*LS+H)6JwUW-DiJ=X9>qot*CKbWf*yOIytT&OXTL6P-TE>CsM)b9$n*#hl{o zQ=OjSbe_`}Iz7kfdD0ehfwM1m`Wk5+z6@Q4GOlxaxzjf}eXG-VIDJ=IXJ*`!*19j{ z^g~XsPV3B!N1V=2Yu!U|db86{q;+P-(`l{GNS)r|^vh1a=JXp*Z*%$`r{8n>L#KB- z{h8BWI{kO2zj6AXX`PwzLt5)SxYHR2>^Uw4oi6NjF{ev7UCQaQPFHZclGBGcUCrt0 zX`Pu-)9E@+*LS*6TI;iFr;l*DrPHmQZs&B4(;b}d>~vSBd!%({MlYxPINdL;bsxa# zK~4{GdYIEAoF47;$xcsj`c$V+clr#cr#d~|>6uQS=k$e6U*z<~PR~#4%#6#^TA$CQ zb!NumwASZSX`Pv|G_CbHmD4vmeT&n#JAJ3qE7LkN2^iye_nel8|>$69vUvm0Yr?)!&mebpve%I*_oc`G9Po4h4>93srhtuCV z{e4}amcX#^ew9d@v?ewuu_fKoxzjJ!9(?gv;$>~u} zk9B&y(-WOO&FLvlpXKz~PM_m+p3@gNJ=^KIPS11tvb4_3SdiBGTs^HbGnS;aKF?0; z%#3BydK%K}fy|5>oxauS6;7{o`hKTZIlb2De5W@${kYRlJAHI#-T%<}&~@9+==0y! znwvfX$XGov{yccs&7Vhhnf7`d-MQnm2`5h+IqLMrO`Gze>+gJ!YhRDmm&Bic|FW-Z znF)K%XKSxgBlx_@9(DV~^S1VX;+^lt>mJzSw~gD?XV1KL|9|jc{gemrIs7jlyZXlK z@k_zSxBn+;=4R_DLE|s}n0oxLkI$cVJSgQKjZKXG9iKS`?~cyjKbq&aj{l=;^6TMV z*DoKrGMw#yBI>~RsRTJwE*G)df zGg}JV^_$|c6Yjb+rFtuG_Zt7aP~T6{F&G!wrlsbO>t18V#yz&lTc#zkT^No|?8mX{ zUZa?9#A$2_+o|%Kd}ZwCY$IbMit(^9Vu_~O3q`k&{S3pVm% zTh%Nc8~c&>i~A;wit}P4Z~fb2Kk^1!3LEiwkhkpQC$-)?7OmFvdi5oDM>)z)?wY!O z(;bVpW$k54VypRfJeGJGtGstC>bf@ai*jgea(KJgPw~u_!bUtEhtK#V73YoTOx`Au zt!p$t*5+){I&99dA9>?ikt2+a>VluMMfD@zxl8kti>sANwHQ_{_2s!=X7ZhNv$Y&n zEs3qsPx1V;|B~_kTkxvef3mUv9(~2_zhu1s9(~2_KgMc)3tn~mFB$K@1+TjOhfVu$ z!K-fnCF6atYMa}Cva$bGZFBoC8SlSU+uZ(Rtn#kf=JsDQ-hZ)Q*nilx|KfP8I9z8^ z`i5vPxBqnQVgHru<@R58y#LDea{Dh_qssMi`!74*f3aWKf7o<<<9L=g-UkE5yZx6P z@4o@#-TuR-{WoB|+kdjL4+e~P`!Dhv-Y)iIA84OO@vyo5myGkqa}Ik=HuhgUXJ^y? z+cRhFHO4A$T&u9x7_0pk`#D=wKezvq@%}6OliPpEc>k6CiT#(pE{E4yc3tK@=b!no z*R$kbt?RP2`A&pAU-3MmZ@EU#KRSIP4aalKikSB*_`nJi>6l}!w-V9)RD8Iv8b3#7 zMO+LQ+2P_r!v_WpGaesenjIg@%N&V|PwX3O=4kQBY4hhb;X$~_A1)a*TqE@sEa=1)$GLrhyQbF{p~uyvJ{E+>!+j$eakMXaycM0U7h(D1=Q!;EJv zIb;7sG&WvWBFY)ZPei`){1Z`(c>alKjdA=$^m~t$Lrl0z(C{Ha!&QTZ4-Fcw6g2Cw zvBa@8GH3H=tgVZ=TYO^Jx~fSZCa^}1p=s>4KNHf`1$?lXhO;n-v8Le~!OlK04c82I z_Lph6R3H+i%%|F zV?*gi0v5P&&~THW;if^uhX)Nap0Uiu?D*LH$!T$jY3pTmfdHGH5vdNF$!VjU`SzhpZ^C`7_qm#oR4EF>GB4>G+|4$5SFZYUtmW6vr?8 zv!cCkn&WI5ANB|HwC59hA7#%c@wpf|?ekCi`+>jnck=CX74qj5(S8V@vvg3luLRB~ z!1+@`I8Z1az;)3ycFzAYLPepp!1co(1tdxeN)cT{3yVt$nE{;7j437*4ImbI0T28c zM=rp87zdc6&4(C(c;p4-As(@r17JRYYaX#VXDbK^0UPJE&4qXz3;gI~9M?VO1Bl0U zkvWtVm>btin=3xFv2!hDF4W8BN*$>eb7l_YA)n2ad6191T$9N|J~Y=>=G0VROap;f zhYRFmKKR!b@MoW5t0OQE=7S$?>|C3$)fJcr^I=}~1deTW0UPxqE-{%G^pLw(ph|Bu%qm7-sGyeFglF7x1HveV{--=F5Ce5ZH6Q1@^`ufqbl&c~cMS za-4u4{)}Ti#33GWj}@pR^<*8ahd9I|ZXbbpFdyc{df1bv3e@ssfnz>VU_CtcOl1>&40;D z9ADNqSs*X*h)eyLKlLFF@rcX3@WY>R)Q334BQEn~9?XY%Q6KizRDr#BrjRX66WE80 zsU>t262cUL#{mHy#P+(tJ}$3YVKbhtnG0pxq%^oz(zBJ7qGR;0QeYVpxoj%{`*-l^V^rcQOaC))R z*Eqe*=^LHC)#(*ZuXOr;r&l?>*6DnwH#+^e(@#77ymVoK$18%>WUJHLoZjK|2Tt#F z`g5o49;w|E<=ihIJUJ=8XK43MW!mep*F91O{muuu_Vrkux947@?;l&?7xzeyEHz@7 z3)1T3-*&IlVh%hp_8&Xqw~gBs?pZPZx8A4uf9c0ODG%V9^UKH7LBGBShtL1ZIqxqw zbF=l7pz#-fOda;??DMdsG9iO5Y=mY}}*5=I&8(e^oC% z)AvY|c8_$?*z`S8v!$@PdsM%?M;hJdWh5vU{l- zYqs<~SH|w+9%*!Mk-Tt@-21e=@x7w-J<=#gd@n0~kJN1Gd#;S#$30TJ&l$}x+#@wxw2uAU zBenahx|bENE!^Kq#rM488t8s#%I=Yt%}d`SjcnYb!nU7#r0lim9_fIq()UOsTfG1F zdyka87TqIVG&X&Y)NI;+`@KiX{)_IB+C9y%|Dt=O*!FXel)V<+Bjx@oeqsMb_ekxY zE5~CW_eeSB(LGYTKN$93bdS{TBQiGjx<|@ALyjeRgH6YG&;3U7#`h}I z_ei6>@%~HSBQ=}uz3=xPX_O=0f9ZRqX4C%L?>$oXT6B-p=IrisYX9w-^FHp8vj3uc zq*kl2|Dt$StFZr~d!%La()UQ&f6+ZsZ2lgpz0R`hazTpyCqC@;EZ1f3ky@Mgf4raK z!Se|3&f(0U;dpNUdylkCs0r`eiw9jyTq@|2V&3ah!y@{7P|&FR>*Rnu@*urt;)TqD@o%ckL)!Or_=(=eYw5rNO0 zOv4EQdu{#Yb4N7HXE11(&nwYz{h(n!=fn;(9!*TM<70DREfyc&gn&PDw)o_-H8zyy zb4Dz1+IpFzo9NXl%T$M3j@bR0iL8{)uR8JpV+r#yEZ=`iwIESmLjF zkJR2r+4D)2s5bxpPx`aa-+7PJK3C!SB(I1*0}h|FbWqB#1kNX}f5ih16qgV2rOf1fC`p82*;sN3ij|YAD;mcP6!nQ&Rud~!~YP07__l-Ovpta^Ep&tj$Hf5&1b#j zqK`RK4{{zX;D^8U5r=rhB{yrKo~(mD;t-FxJkKx>=0k4!upKTirh!1LrUG;2noGVq z0(0Zqi+^o_c-WX%U4glAO~QshcQNq3moHm0`tU19O4m|eB`B`%o7`a_%kj` zASU_9i;dbe5vWOHfweXgSYtzh^{_71S6^Vv5dwbrGp?CHT;h|T^)rs~#Az-t*Mz|O zS_tGN9&xE3bEQ7SAs%s=7k>CNj`|RXc*JF%%!BzbFX}_Bas~DuV2^eXs7ZT)7})}Q z6X268FfK=64ef-?0DSOgTvA8~Qv`bykZ2>2yLDjN*jov#=?sAw(*%yukpl6_L%yj3 zxz7@?(~nz8oxYURBv3}-9 zeaK6G<}q0yF6+mSHg@uohkV3k{mg@Ww^6`?Wb>Rxc@@_=pJckx7Qff!=q44>1!R$qRw8z z>C#S@ce;|(Rh>>aUDN5hPP0dC4(uz_9COngFVh?w)45J}a=NS2J*5i^Jo*YYw!hPy zb7p6sndV$D%|18H-ZRbqGtFKx&HgZbw$n46KHurtPG9WwrA{w!da={jIK9m28=bz@ z=@m||bozd$S2?}b>3pX*I{moQPdoj*bYX$VD}vQztJB+@-r@8IPVaR3bEoYdsofLh z+%F&uyd=J7X!lNK+Uw!(ksi**`<)MR?d#F%-~4~1bHY8+t`*~ZrCp2M9^G@X7)8(j zZTC9Of9#pDf6?i`ZQQQmd+y!-(fdjN9phOiH3nRBe)*Vs(y#Bq;q%`yx8IY4x!HQS zw*J*0yK;Yhe11>ee#;nY`CI(<=R=JB9iO6^cmJjCk+#u#7qMtPSU1mqbtS0L$WpU% zR{y1{b{bPQL0zc@vm3gqecpr$>TQ)>Df zsT~DxYO`$cs&$+64%w%`7c99HA7h1q&yesIvWQQlUvopjTxb;)33 zY;5b^5gTL45s!U$0%Px<^l&tnzkTyVa=Kn<;b&_*H`zU#we3DvueU4RlJn4UFQp3Y z9+YbFS%D3QE!6Atp7-RG>2Y1^+n)!eCbgWr6s zwvQKo*fzG!=WS$EgKMvDoR3ZK{PVDtt#*G-Z>hj|7 zqS+M&7TC~T7mU(_Zi;HZO|}!ZPg#PkaqX+4nm#WZbuvHd68p^?gN=C9FOIi62irmG zRz$~qcwUR_XUZ4Yz_II7{Jtn&583d+)@aW1XdmEr`BRg(nvLTd+veB7Mm&yN9B+3H zwjs4^=S=JM+`8$vG)UH2+WqnAPwug8tnfs#(bAreU!gae_;slKY&-n=EiKVzR`A1y z-|~$ykzbdTvj^TA^JLUAxE4+)&lr5f5d|%Tv z_V|$%QT!fZ@BjCc=M3IoTO6f|G+ZQT;+e+RG(HxS*N{BV5T}Cv77MzfxOmV9J9{PR zgMz)X*v3|oJ}}r1arUaxC4&7>v5l=JZMhDUE*pFjV#`}yx@53Z35#Dtx>T^&6kGgS z(!3{UEw%O6a@CP8gIV^v`fD-kNn4!y(l)k%wD~ubwz)Nuwy}++%L&Bf^COGHTPKTi zxU|J#P4VmX`2WeJ){LbF)EdovsWqCqQfoBprq*cUP-`^nrq<>|t<6rY(afD%qlr(g z(X1nZlx7ap8qGT5T^Hw~)>v2*wMH`^YK>-n)Edovs5P3r)EZ4LYKkR&mJ7Sh8JpKOPwcivY+l>^v0F{B zndX?-+|X7}Y^JHJ%?)kG0-I@$iOmgd#}S)pj;GBHoe=27mOx6g7i?~5+dtS$GcP;V zXxnetOfxUrCurNB*i17o+hb_k+t^Geq?reEOX#niFW5{oFWcK_JCCrL=6tfbq3ztk zW*WA+q3yiGW*WA+q3yiJW}2LK{-W)C$7UK|n?Ks@*i1K*K0;va=;lGgErO=s^Z`y| zFA#hn#uJCJg@SG=J~GlZqR)F<1r4_jnts!ajeX*K)s~lBah$9uj^(oFVSArw&%<&3 zq6GW=lm4vtciscG&yC2RTTIVW;d7)8%EtRS)_MJg$uJ}?< zeB-)G=z*`KyDc%C9; z1#NTTycs9d6wo$z&bcu{ZGjx*B@cR}P+ed@k(WFPVYpC3U@wxFoa8%Es3o)&$V(pb zogmZ^k^*^&O}>Ew*9G=2Yf1=ph2w-q0|al z)>Ghlg|XCzxil2I3Dme#;I3l)x(FN#=0-n$_;(lR$B%Xgfjq>;Zv8!^na5EAafnAh z^I$$l3-sehyS>0Vm^XIo?M2kU;%7&h!%lJNt~-g9ZBWquoX*El_Xl^s~kx0`+4b z(a*VnoqddLs6an{v|9<(Iak0=KXVu+P(Su5{hZ6#hYJbeB!T{6LPBUMQ0E*0JN=9u zAyB`BK!0Tc`$&OvW|Tlbezcnl)RB5)r=Plw7N`exp+3|R`xt>Wjuq&~kM>lb$VsoUuSwPF8JKkA5mvVd)h zKtF!8+3N=hT&J+pUr9Japf(i*`l%82GX>UqmOwv#v=0}kGxf$!KlZ5t^(!yXk017F z0&6{6pdUZlO$6#py|L4eeY!yXSQGvDVV@xo`y7FO{Aja>sWbJ)PCx6NDX>3QV?`eFVLxAB+yw&t_|Ya8$BW~Jo&EwsVc`H_ zj?hB5NT7yuLwmNkxp2AAO1M;LEnFcK3J_&nEKsvq0{)i>_8<@Ohc7HI+ui{v{y~dmv#!)jKuxa**Y4*Bl_L}MP(gg(` z>^sx!QPZ41rV~!raJshB^_*sZ*;vjI)702B$HO$WHr>|gY^Pbb`E+u+i__hmKHBNt zP9N)Zf2WUkda%<&o#uJO<}k|Xu}+V7dZN>(IX%Vcvz$KLX|5HPE6?c*oaQ_+AI>e) z?0?hjb<^y3(^opZ#OZ6DUgq=-PT%bGZBDOn`fjK1b@~CPS2?}L>2*%8cY34Kk2(FM z)6Y2lywfi_{fg7CJN>59Z#%ui>Gz%f$mvg<{@m%mIsLWM-#Wd^>3=!>v(p810kvaS zNZQWjqE44^y0p{fo!;|rz-4CqmVX1zzGq?ItKj;na2Zptj=y(c-@lM)ug6~Bvp9UO zuK!YlT>E;gzJAa58GQeo@coLSmE!MN6uoNgUf;9Wb@^}mzJ$e``sLjx4bvoczAW``Ie|>y@ zS(E=;ZEE>PV-aJ2$7jwDcm1WlXJM~DZQZ=)D5(NaM{GP66}&3-`ihT|S&zMyJfzK? zyzEIoI!aNm|M)$NaNT1d{{QZK7Is}UKVFmZNWabv*DrgW8~6Tsf9?88e*WDtYv(8> zwW~68O6vXWTT^epdtZL@b&uY}cMP6=dsFJf6$O&m`Yimu#zt&8W!hZX`}ySavfYp| z{dD|}zG8Fok^6sWhhN!Kp2)&h>48bf;}_i)#e1Sdp(vjGQu14O_cb%}zg*DzZN6h5 zztqS@wj0>9E~luZbj=0 z=boN?Q1OOjOzA+pp`VRRT|VskB=OGKaOyzDZYnn>S>}X=(OmlEG>zsm@Z_<{Er%?N z{HEtVG626byR1!izva7VELQ^_fdjbBS!N>Rw=DlNy^MUe=f+qQsnAn#)>jKcCw( zI`6dJl-8RXGH-JJto5C5=eq)0Z%XTJhwY{%M`mGD?b}Q{a&vN|Y*%OOn1D^kD5+x< z`8_hGTy<=!b5eDV;$4t)#ckMB=alNaZfDtE`2&{}d*>|Kbd1*M7^VIx+t}ST7xtA+ z$1u+OM5*UL-UX;+M30|Nor42j=}QpT5=AU)tycBaSt% zNw_{^KPci@mlVaX>ErvwnNeSSUpQWGe81Ry_Pkd6_4gSpKJV@MY=vBv^w(lmme#WL z$cjFfviOI{QY6@`iY*tP1@O57{?+u?Vjd=KaT3xtwz{u2PazQi4xTbN; z3c=2ND+UcwQ(`h-YKmrV)D+F!`5XmJE^3Nq9n=&}UTTUaFEvGzi<+W|pFm0zlbWK5 z6IUSq|A(n57UI+l8n*peSDHHFLv8B?jgQ&U789G-jKPB4=77y>j-SO(1dYvWny$0Q*T4QuZRZX)(_D*e zZfHAqu$hK!ZfHC2u$hK!ZfHAyv6&_(IXD*R_*xl%m&P={Hh;9)v6((xn!Qe(hWgti z=%(WOK{pWNhY!AugKi|Y^8z2U!}Q?~6W8KfOqem)o9VBug__VdyVZpGd%MlwUVq#B zAA7Eg_iPkspMTPyGyl%-57_4+o@yYZDKc_Gp282<$_AG6jW?_QNnSdzhLO6u5Sk6^00`m)en&TCwK_346|+I1l*-2<&Z+0eQ(o zzJ3Dd1;>lLU;Jje3uqHI;c2|LG6l19YIk_%$ z7P!7Kwur#o5(2fXEs%p6Fqi5=j=*)5nACtdX9?{Du6suc#OoxGucg2oSu6XSwcv+8 ztQyT%l4njicDBy>GLO4PwEKo1(^s~+`0`p`(%!>!>X8rhe7O3}80{+y8Hs{p= z0>=eA{j7)iGY{s&yb21eoAu+@RiH-Q1oD!fHn}-290%<5vj@5hSpvt4e)c+c_6uw2 zDbSA}ZEC`C;CNuCpBf%5R2SF}^s}e2vmaT*F#`Sg(QYJAe~tro`l)$uAtA8e=;vI* z-bWyIUm+pjN4tSQ-Kl>7juYs|k9IwQdQ*4o^fQP40`;RN z^m9$bK0siN0|ol=qs=j*j?^1F{nYJvfqLLWeW)Y$K|(p<1c841XmgA?w(J$`^i#LN z0`;IS)Q37^A0l8oQJ^0`+O>pCf$Icz`l;JcfqGCE>O&o|4->Er7wE^2HhZgpz;y^a z{nYIwfqGCE>O&o|j}X{PBL({Lqn!|VaGk?WKQ$R8P-A?kA9ch&S~y4;BhZf@?dn1y zp{{_Pe&#S%pnk;#`l%82lZ7(EIDvlrXx9{|Gxf$!Klbqg^4Ax~%^oGlzKoF#B9 zrVEXQbA(3141qnt+KEFv;+`uIhj_&0K^)={_dJ0(#3L@pfH=e>?o5F?P7uln7YNOS z3kAlLpYizNPd@VI3FIX{>!u#mWtPDB*#dQ>p7hhke!hSo$Bn$?!Ol4Hv%eUJA9m(r zuY>G!ZF?QW`Ln-A`gPF$oc7aa_ld|~w2<~*xNp>6WBP}2)RPBny0Fu>@9`<&?5y4V z%R0?DXm+k6rn%;ruI6-gr)xT0$7zm}jpZ1cX1{1z(WANkn*B(p+en*FwzE?M^Y7$* zx;lFgr+Yb{zRrG}(*vCk*IkQ0)ajF)9_92{r^h=z(dpBi=6tpIoO7l*Z%niIP3Jj% zfz#}3^O@`PJf|;ndV$kdI=#f{Yn@)^^bJnm?DTC;uWw z;E`EikNp6rxvy^i2Rd!{!|nceYsFA^#?JiszOvmXC$@j=bw9kB@Atp{`xBRwoQ~zrB!<+v4ek*nQqxVJstnti^ zJp*`7`l~;7b^rD8`7>+#M`Lq5_IG@W{>AQxAFcKB++&YP5~r&G)G=eyQ>)*3b6|Ge z6;+du9&|wY+|BkcNQuQO%(Qy+Hy&Hi-DxN;NpV!LmVGO3vbYDTs$@7LWnRWD+iRV

    Xe5cT|X!L6OHYW@oJ01^ulMtJ)@ErbdBTnZ1Q1Q;te<@l{&8J zso8yUpUVDbRf{T5>cv&FyPC94t++a}P0JYC=UKh@>VM6;WXswUBio2>V=JfR_vS|v zQtv*oJIUC&udI9gL%oPUZ^-E6S2KT(Z12}^|M)wyZNIl|{=F^E&n{p6rtD9zTAlTA z`;3h5t}or@`WGIHY}aITI<<1d17ulwdT~vL${64ts)71D;)sl?uu;KPQ z@;hf_?D+nJWWAo1Biqy2U*En)wk1pQ@<+71IJ@(SspN$h@2)*Jk&*GYXHIFe?W2z( z+kF|w%$T7U&?ldpmp$RxIg#zNGajs!C%^5tCi0hUerX+J>pooQx(zk7j_QYIKev8I zWZT@O_0mUV+jQ9r$pIAxCyO*Hl;5kuWgl$0Q13_k9-jS4(}}5`i2Gm7lz*&5z5!i)<@0mdtqQ zJ$>F$<%;|Zid`Dn&Y$~KtxfWqHFROyAK&SkJ@nO;`5Af?$kXRvhwMmxzwF!O2T#w+ zx8HBLK2;`lc;d>)#_u=qU5J7$GV&R_MSip9YYnpD_xG!A%4Y2HZSn6n5HDxyC6R5} z;Q03&F1WH~l6XBHE1Yk?-_ZG<=ThXwX20Knjj^#U{{05Vk|Q1)|9-;{_!ZMagMDRmsL?zu)lA{Km=DIX5J8 zuD>MTe!t=6Pcx$&tz~<6Lj3y;jGejh%rG|o{f5DxEl4q!O^U~N8Th?{MfEqO3MwyS z?e`m)%Mq8&jmEb4EdKol{MyMD`^CTCz+C3#EzgeU690Zf>g6)o6E)|#jk@LA?>At( zM01I4@$V>5gGQJ1Nn(qSuHMT(_Hc5L)`rb~zhU!rMe{9Q`u7`-za?dU>ECaV?H0|2 zvG)57jJ3S>`wiqUKl}X#=5p1_mC@MvXy`rs;#ZT@rDN;wQug}|yB{i+qV|nsv)^x6 zeeyq|n&M}_-+&)=iT&c=Zy+A^i{r(=-!S{-58H9f-(P!W%6`A$m;tvWIYyNpxiZCf zC^(*%9x*P(KETg@zX2P^H@3yU-#|Q$TO2R`{e~X~zLHw`z_952J>%2oK6p;&@0^YG zvi~(;W;W;aDK%a==&4!yEtAX3w>e^9DzdfD__WT**}4vWQ!hX3ki5wD?6#&QH_7jX z*T=T$wB&>AD&MY2ot@FH@8{R)v#UBYvajnnC$jDC{zK)(dQ;cCL6z)ouRXF3ze^it zZmy^|eYY+@G=FZjm62`ROBZcEP_~K3&&YrD*16HxQ4c*;<$R4ze+Cj>Yua;DK~9!G z@nNs&$$wW-ef}O^b zUgG12Ke5e+I5r>VVlm4}^BEWZaOt37J`ci$6QrpasbVJ@_pCwXmdwWX_I zmc5St^7$9KuKrdHx}MnV^`#FB_6A}b+fe$@U~lB?jivQ6K#%zQIySbcEEe-{>6*C6 zr})@asE ztMzaoTjV2ehMpGMVjb=X78qNBs zHJbTQYczS=1f395YwX0Q)@WiuPqmLn=>}AZJyX|jo7@-RhzkCx0>WS%`vgLp{<_SOjB2z8`_QqHq#svn;Y7W zBR10;;=A+V&4N)6C0`HQM$YHq*?@_6gedCpOc}%k~)B_BJ-t%**yC z+RhhjrkR)RZM2<7*i3Ui+1$`}?qD+w+uYE0-eEHh+uYE0USl&&PCI|mcD`dXjjzog zZFX#?yGmCQ*gxI$*PbJ~OIN@wdk_8X6m(B<<)DueGajFV^_TJJ^7_kobVdDTJi3ei zG9HbujiulGVT(h*`NI~+UPIaEC-xeuihTC>NZ&{Lz2Bz^pQn=l@&omKn$YI_<#Ucg z;sb=tFo|N~;zALDJ|5_T0fogS1RKM+qJlz2Kp!L!hX;1k_%g1fU}K0&OmYEoX_}FZ zKHAKO7#v&1mJ*l)#|%69h{th5mlg2CpV-7B4*rZQEnwsLGY((o#hxNpIe|FD!_FAu z;Lo@+0)F`8hd=GZ1mJ1?&$A!~Af%nlc*bxncVP%qX~OQ7aW z1jf`5s0a1Nk2dy30>`elz&Pqb9^w(VsX!d!k(YWB2Y<#j7MKU~u{~Qzy0Bn-QZm|m z1;p$ja`X`xbGtySW&(3RLco5Dz}i~~_|uQyjRG}0Qos(=zFuH0tp)5b?WF?8sGmT5 z;t}_1fwl46#+sT79IM3wxp?lRR;>i~z(N6^T!FpSMquri3oV5X0(-QrK+We1jO7@U zmptUVSYVAq1oD!Hd>0AKZKy!rT!DNS3aoLsKwk2Y?>vDuju6O89`el;*b^rR3E?P# zxlR{ygdRe5p^Ly=rwZ+bqlGMiJ;+?o5XjX_V6U(zlfvmjN8uQurof(N4W|mZ!nnW_ z#rT~fuy;6jas~YGpCr(aAMLRMd5DYM`cIQ)9uoxO5RZQ5!F(nQ^y5c+jF2TTZ|v4T zMVfVv7l=bV`dKIIJyW0`KiZ=O>O@_zTmMWeU-~)D=Lpn~<3K+(z&=x8Kb$Mjk00%m1nNw^ zvD42!$`csNai+hsfc-py{dT@Uf1W^ln2;q@jso@z1@`kSfqwjG zpD0ups5f@{IS*zF)Q^2ie|rJ@MFQu`9D#oPXb%<=!YBbd{hViW1?qQ{kPxUb_KOA1 z$4dnI@uNLRppGL2?DSK&c>?vIE;WRc1nl#LCc>ox{rJ&7UZ^IF5U|rv-7XWT2X&!7 z!vyS?3yp;<1p4u#Jy1AQ7%pI^pSmp&s0VeaE>K783x$TlB7uJVX!jSY3PT0#^i#Jh z1?oXv5(0I^zF4R$TqV$tAML)vA;J&=JN?vciNL;NFHk?~i2Z7zo^XvoKYp~26%G?l z5U|r^BJ1ua!VQe%NmmY6~|B^y5dnw?Lh#H+K42@67`1U_Gph^<%$9s3Y7e z(2pPOK0+0tpMafy;@>8495~kGAs_bJ1;*VW(2pN&j?p0k#|=CE_Lz z#3Sy*LQi3(KwOR)ezdW#7RWvvbavUh8zq=?zYA za{6(npK|(Hr(baTC8u9?daKiKIlbNKcb)#g>5rZM)afsr{>tfpIQ^Z|-#h)I)4QF{ zEU>3mzvVk){C_l7+cHk`yDVncPkE15bSI~~INjapqn+;U^s!F&clvl~JC{$Cw(G-5PLFnaoYNDXw(sfL_wrin_^CT% z;2ZJx*6jOxGVS%)>w9`U@%_#Rx%Tx~ed)fxr&qMx-rv)k+IsJQAE@q@-}Zew%Q|gh(-!^V)w>|Uvqu*)z?-x;_9_r3W3Slu#3qp|mHniG9z%lYNRe)0FmLcHuaUi|&BXD_)fidSg!oNT_2 z#_!>*Zjxnrv-#c{zlRfSjFm0^{#Y2B6OWC*KUV(PXKgOov-Dz`v2JZStc~xFP5tSt z^{YQ#l3%H8=6b$M#CMT4e|h`*9lP7*mzRz24Pk43!P50b$Nn?F=o8EH`3@7`18X<= zsVK)z)dnAIVQh9hHvazDq|SS{X)NE9Vr-a8c08B(`(wc` zJNApeKNjYa9nU5H{@B#Si|b2gB=fbb2kiS^!ImA{;_r`9gK%sg(5L2k>dTlh=%MwK zv^M4{G4y`?E7fFziFBFv+tP&Ta?4T7Z%1wwnwJLc|(rKX5TXlbKzKMF7dzr zv!qDM>XOa(>ZsSw8^>Dhv-$2Gwk;b=TJ5vn)f+bFM_uF>e}62*qkf7Pe}8P}tBayz zu49zLcLo{%`L*}jF^c|;C2UVVbByhS9KJ{BY#d+N;_r`zc#)0o{o%Lym8YZge9Psp zr0#56_O73_LbLUr+a&w2mY1$LwO4H89E|irt2dM&ps zZ1>t8+w!*GCr33q<;pkofd_t!js2QVFh6Xu-@wt=+yH$x&;@QUhv@X1>%KP5r1fnsrlaG;ydk znsrla^P$#er`BlZPOZ_zr`BlJkw8i_2WpLG9n_k?#OAe)#cp%J=C$R* zZga-wwapW|tr45oHh=6^6KtkACN?*;)f1a(>S}XC+p)lAnqy*fL)&r0W}4$^b3-Qt zda)&t((DDB8`|~{Hq*?@jy2l$8#dF-%k~M{_9r&e%**x|+V(a!(+O$j!Q2x1Yv&6# z)6C2EHrmc3Y^FJ%Y;I^fcd(g;ZEk2g@35JMZEk2gud$gXr=7oOJKwRH#@FVLHaj-c z&7_YII6CO&k&eG}*&@=3s6WZ)JU0IBW@J_k3rSdlZog^H2Id!teaPk$o;i{zNhD=kPgE2W8{^ z8Z`hs3JRG*5uvbfpkOv?h>e;S7w}QI4D5`-&+Grg-g$sYQLXEGh9r`+h=d^r0VN8| zbWM|o93)B<$sz_MC^-)Z7*H5QKtXU6MU))6rbU541>A~)0TDq&5mZz_;{D$0TEk*C zo3qc}$9vDSpIZ-KuW$YJr}eK@)m1f3x4PbjJ|G7E@R3!(9zWROhdy%l!|{nfWH|)Z z#Uz3Cz_E@^Zhy121ooqcoO2NCafHBr^zdIu;20Vv z5Qli=jBbqZkbq7ufw3`{rGy83&N-Xd95ZEv!9FJ^a>aK!VSvD~#~6sq8Y(ID70@9D zaam)fg?oh@2nzJE$4^6ny0#OL)6&0NU<{_N^pP_UxrBJXk3D{<6Lskzphru;lfW3v zywOKay{Wq+nDak<>P|g7`*K?PT?Jy8bwM9F^`*{n0%OD%edd*V-XkEVrQcm3hFN#? zkyBslTu)$(tardKn@(U^jVkGv$uepmVO^0M!45+ z=_8L9sB>)rTh0meV+87XpMac}em{X@rLW)8M^1gIYfXXrQ77s{ov2HH0XZ%GfdXS6 z;J5UV2ZS6#4T1VmC+dSPbr~cer=@?tP*fP~xAc*7KFBUq7pNa~qCV7#x(pGJ)6yR* z6cHZqTl&a3-((Z+6k-JGM180eb$L*T@mu;23x$M-{FXj)&Sy6ZtR?D4ov7C>0yTO> zKu$~lQGtEK{FXj)&RaPJ)_!4u+R!g3P@~}ja$5Q$1;#wWZ|NhazSOyzz!&&Qwv;GZ# z&f-ZHUuf~g7Jtd&FI)T-i@#>^H!Qx|;%h9v-r}1qzSZI>7T;#^?G{h9_y-o>WAVRP ze4oYlTl@=)AGG*47C&O~V;29x;y+pZXN#Y<_#YNOXYmUbzijbnok-2xWfC{X^1uH3 zK6($c{KQz?d^W;-7J_r7c1NB2I`UZo^O*^WuGy~nYy>CNe{mz$^|m^zuYLx>-k;$A zmXDDgNcr%Z-{MLA=(TJ5M(-cB=LY=f=~vkB#Xn_tF79exH~Jg#|CW7>n;HY&3({|+ zTKxadMx?LF|EB)A>&(wzwYC`dzv~+xX1#Gul736f@P>Ds{kw7gS$pFDv%1&Q!`_UC z>#g)#ehEriLax0U2>6O>>u#)YI7=w*%yaI5qhpw7m;OuW{Hbl z>CpFw+Umj$e`f|C--^FIiQ_NaUp11;!4a3Xl1pers|#~<5lhY$`@HCq^YQv)4!!T& zzW+%}_t=$B-5S%*AAP21#J1`1HlA!i--k}kdply=tMNJa=b6*oeGQ!D$$l;=ouX5) z-Mejk@&md6BE}v!B5NCW@Ri|`k)MeXJs~n-rdHU_=vty`rnj!v3fb(am-P3ZMe}VUn6H6=%It1k>?gSemI}x70{v0=W|+9E9z!+7&kWP zqh4_I&}XiwEq>59YlWIH_Q?7W(S{B{AG^prN9xNO!G?I`V|0-N=D@@z272flPCf+% zY&jnigX=K%)6y>_V1q6&>xlbx?64;W<78iqz!(Y(#Ap72PD$R_nLBiE6Oc1jz}khL^Hzuy(n)|jonIn1IHaFr)NBz&npNq7k zevY*N$Y#{C@6+OOjYs~p|64c4VUDRX*BR8gR{#J0?>c4qUn3fI*Wb_2U$xd4^uOyH z9~={a^IrNb@!WsbXX9sTfCa6S*lhJH>42k-1rHSp_bs+C6gTtdK(p1u;=8Ur=T-T!?gxj8@a{>X9vS8wM0Nv!KVU;3YlZ_dlS52xy4!^Gu2 zlR3zIY)#6B+`U3)_n!__8*|~rQeBiF`>ap*Ktz_gfRQC$o)hSL|ExgLn6_NvczT;; z`_uXPaoF}e9+B;n?Qu_*xqy*f=LgMPw5v z?F=A$r%|ZM9AEZanZgdXpEOxGxQ*rq+uvpT>g8KQgBx#H-|DT1Y`kn&NfvB2wsB`) zws&t~7u)CR-||dN*$xbyOgZuAzR(YiW1UXjimm^!y8f?o-tSYEJd@2q_Q230h4bi! ztLFRPr7RxN!dbPUK_E0@=N)CQL~KvVw&cDwp?y8pZ@hU=#CDNvD@wNGq2u}Y?a<$3 zyZ@Zh`bZ@g+t<3Te0hf+Qj>G}Ix9C_&ePEu*XXy)C5Gs4uP^_Yvbz72xGH=5JB#`p zKeDi8MDLk;D>pT7@pLF_^X!)w&X4FVm0rhTncV^X8z1ezQGaj!wq$s9&f5Ze=HKUL zjL!bs$_e@mN6s?g5_Ow6m*=%|Z~bX}r_^Qoy!fs%;pPv|aZda+Gw|3q8OFSGQQs-B zwRE`k!Cp@Hd)7EFegAFyhkELscTb6M|8A87ZBD=G?2D>*a^W~#4mXw%l%K(Dl%8s{Y~qJXI?lPc>co7 zz;9dn58-)xjn=iowRXK2cxmWLC)b|g!Q?*rf9Wq(4i`v#HFRmjYNv11{-XuP>iExD zG2G$SmhR9Gf`J(&exLk_^1JtM6~jNwzSFHazKq+m@RAp6tkP!_o~ji7q(`jV|2aJq zDOP>%bj@$lpEbiTUzqGp{xdf4SnS(#?zyCM&*s|UH7f_XE4o#8%ay3Q_U%Rb+(@0; z;gq+BxDy5}b*h$rdDGdxQBj`_4~FO0UJ-Y5Lb<@eC4byqPxE{6N^H32hF9Y5%ih`D zvt?wh-7o2RL%q20o(0LVJ&G)KHix#>N$wF9b#O>*`0;mMb@xA7HPrRzo|6mfn92D` zy>M))=iC|xV%)!7IQv4DH}!Yjll8*O4kfu$?q2Lnz5LAL%kA~u4BH!qdmq>lDw|X< z)L~-Pn~MCb&mi<^5Pog*j!@f`_1#Zi92))LYdWsSHV9A8{#mGZo0puL%Vy^~-ARA1 ztrr_EJ?Ullu{uqiihl$qX3>qr(fJL-sqI3c2R8+s*h#PC+ay`j{Y}IBKJMwB%D>R5 z`rg98k6If=mo*5VEAWc@P0|8q#hu}8ZThJ$V;Y2?ulkldzEMx7)Lr{lef)<0-@xvK z@RO~&x&=D5clvG^=M-P5=PM7i3E#GAgF803*eSYZPUT|V^?kHa&BCpZ?{~lGzr?wx z?rppJtEL@}B!uUWIOIMSH^>=X|EYC3RD)Y~%C_G+_iWRy&dn>kmTQq56}6{-yYOA> z4!XO4c;3mqc;BI~TIg@xftKOX&!2TWeYVgkmgD@wpIhtm47pl{hvvHEcIYwO$(rZ# z?EI-wQJ)-W7cQN8(5)P}->IAQ%NO6dQBlWoHV?ZEhq~_#&+i=HwjeI7@qVzdQ8;Y-x#-Y?cUfY zHQv1k_&8I5|yXv$2Pc{n=+I!Tkl~~YuH+FVPcCCXImlMJl0^hl( zzl#kd)m{DOO0CnBjP1iC_OEe|6M&iy2#(!4R?o6m;1r(-aW4~c5&wY`4aS* zoZPL$p9eCe{8s!Hr+B8{llG_vR~EDi4?Yx?vhm84z*|{ko;jtlmH*)G@QP{Qx!sG6 z3ha6Q@lPvDHlb=lc-=RB-2AU}3H`QnY}}E1qoTH)ZxG%(W|f<}=J?R0S_M-^^w#~; ztOnsa+t#|FZwtGp>nvY0N$cvi;?2UP!fo6K+TZE62@MRs`-+as1#QB$UtZzaml#eL@1+0#5!re3S<+Z5yB_D#b>dw=epIaI(c+u+Q)I-1`SyV`^g ztXb}U)3vf&?9FbKo62AGGws8pOMl^Z89q65dO^W2zt9`~!`oVf-)nK!J$`Uv=u(z% z=e?yGj5^Uge7ej9w^5Ib?vY#8JvnHnKKnMceRyBr{ch#5h23roJATFU!hGlB!iR5q z(fu&j(747a2}v>f1Ll~o;=+@vE^xDcSiqV4PM*c@>ptw;xeddY&z}$dbmG>yD^up2 zId)K={ixC~Tw(hkp^-_sonq7P_;Z487{?W85YBm9PPg)g1gZwqLSj(r^)URr0iJNb)S+!?92y_R8({y$acxNx?z zi`>Fr4h?<0X8ZHC^q`>Qkp|&QTQ7#5-Z3&1H>Jlz2qUEDu%0%uIfH}&rGN2m1@sTZKBV{?5Py4 zm3w<=d5yQ6CMipgo*x_)wXa?2aHb!VL%Ux5&Y9|5Xm>zs?NVyRaFLoB+^^dlckWLb z9Jj4>TlzJPC5ANXs6=AZO*wl#cNC{q4$LYf24d^vqxOJ$~&A~^Fmph zHPz?TKDv-n?&rGhzot%dFPFUt=Axh4 z?Nlz+cKn>O`_#iu!p@;?_NTwBeg7dnW7vE+<>-vr&XPZ#cJtl+LA^e2M@4L$D^m?}A_p+@p@^DJFg3mZ7pZePQrCsNG<=g19KzUE36fE>d?7R_2oX=Yf z`RY9#zi+fVky3Tg*1GjNyyzZmIpuJx#rl5z&cCL#{kE#x{Pj_8DA?l7SJvwPj8_k;F%12gK(nEmk?*~b5tGNMC&_vd17I+K=FQU9UU$EUNi_i-6t-%U(?RqH7iRwW)|Gbu@x8ppXO4mmZK z_KGghS-*Gi(Up`c`4_mozAYH|enHDMUC!z|XkI*<()Z}AvA2#{D>GUh)y;i?My8Xw~^#`W& zKQNtrYP$WY{&N}=zk>gr2Bx2w&OSL^|9HCoz;yYfrt2rB>mN@SKWn=Dl5e?o{HcE5 z+%qL@oaH^|;n^G8#y76*7hZL3Kh+7R{Zzl+CjRks`4;x$8~v>5#@i*`{0>Y%f9d8o zYrY{~{nF;oUvH+L^_Fz|)2;ss>5os}pE*8~)2-)(boCpRZam59`bp{PF)&@e71HIK zm~Q{TbmL7-*H2E@ADHg^H1M`-$Dj4>$o!|(KYf1b_NS&_AL;xhr0XZ&nl4_^Yx{}m z@=Gld*>CEdnBHGH{p6jIH2lnMeI#I&!wwpg>>hKm+88UM%A)hjXG{tD^r zxo**AOWJjcE}^{VnuXOqaCUsW*`KvQWFGDPFMW4)e{!L}-+$@&)&1tW#-f+92Wv1#MWdY$?mKXKLGT=&`ba}GuF<8QO( zI*|PwORV46+@0e~s{MU#W`}i*%v>L$$2k;PuAjQa^Y`cpn=8h8`UAoNqi?PsZT&4p z?)CIbe^)(FRCZ0Wz7S{h&Gn_N-){09@#wXBsgF}!ZMKplJ^xU}qq#1%{hrF!!Snn2t-Au`y}IwvG{5G$6@BBk!0jEqdaN3s$*TwPD_C_mdau?x z?~Uv2(_Pczo9kRV{^zadIn-_EmEvCfzx9am^kXD5*TLxXUdb4KKA+3e@BM1X(~tYK zc6=$xvpu-ft6z+`xn8#Ip3AX3K%Ax@#l=&rAw{16}DjrPx5s(&_`&U-{+-e>*Zb&m%}*U; z>iiK)J)Yj%#EWxmb5<|U)zfl#>+806b36FSbl25&Z0-|GTeud?+ky>}{(_4`3m zQ7^BepDjq6FLU2z=XKA6Grhb@-&x9wGv(17UYxg9pYX;*t<3$I9mh#}*3%DvbF-&^ zveesYd71k+TmR`hGI{k{P`QeiSNjf=((*F*cXnQ{x0>zc6`Q4>XLqV#M$hi%uYXIc zv$+qn?N*l@=h^LSUE8zE-=VN)SAYJxwDFkxMUx-(dUr_s0PC&Q=l$Yi)LyC|lcsO( zCvE+#%jS6c!Dd}N{r+z}pQdl_H*Nj({g!+ByWel->8}p%PSZE{qqhDpRi5?q$7PH7 zAK!g~r_cHLc)Irk^4>qVM1Z^>-7(BNFQ8}cWAV#*8@;_>KjG=M+B+moFTuL*FnUvG zE%5YmR2iJ6XYPA#f8SOd?dkpS#86L<>%;^rzi9Pig!`IaeIGaX$M`Y(8{`>t)!x5f zwfCQCF*8Ph-#f3dhkM@!bO)e|MJc6whbg9@sL?TOa#YWlVct zHuv4wnd5Knib0;;vC=I)yX4QmNSilvUvBFMQX=}T?r7-gm)M;*ZJnF@b~~>YJ@51U zJ0042`tLm$dEfeGhFg#G7xQTD@3CWk7)R_2TLX-v-|~KG^JDJ&ZN0rGcY1o+qxz=l zndbqv-b*p?vbXKcpbe$uS|_2zxoqLr0FGJM=!i% zQ7n23OONvOSU=`@1NpH&*x&wQWWQGr|9;-i|M{)Eym*sZ4)^?U-ZIZ6&|^MJ3iA&= z7$~K7*0g)#OC!rO#hcF<@vQ!RHu`4W&Ec2kD^n6?fE`uFd4 zoaqmb^z=Xdtc9n4{?4n{m*)A0t>3Nwcu)Vts?MJNXJ16#hZt{iy77|#qF#vs@~_(T zfwcOX=O*}NzR*jG{l(M!b70Rjy=4FX($@PdK8u6D0`1Flr18u$jZ>M_xF25dG zPt2Ej{(>I!g`ZZ7uO6?d{{5!CzsKgs(&CusGj^N_iOXU=KaH-gi)8=4+xAnmPQ*_I z#Wc@x*w3>Zj{7w^GRAYf1ome1u6u_4QptPYdbV3b@BO#U#uvSL^z8lTKDK?cWf?sC z`{rfv?0Xk|)Zsk3utzt~Pvsg7z4Hh5<~fmVzqiaq@BL`r_n&*`o8_Tip54aBrg`yi zjvbs9-#k~c?U#S>yJuhck#8dJXQPw6_yhKr_x!YaEnnI?G|!>1&nPhdV7HU8yuZEw zS)R!CK>h7$=Ogo+3VpNQ4<_8^#d~32M(;Xe`?)fnU6uQar`eh3S+-rcOD)f?_<>cP zUDNTAcoXZzdv+X;=DC-hU-^39c=}IIit*y?PnegcZ=Q?U`n$5+>y5itQj~YyG2;Ed zrRkgJX10F8r8j%}wVGV?>b+&l*JS#wjPdNsR{g=Vn~-g2nw@$6 zX4~EJYP@F`JEo0yeZHh_K`%e2P_4B5%yT-EA2m5PYpr)Z+1u&jU7ts7=@e`36U}ox zTmSd?0^WV!$%TzQ{WlKR@#g&^Px1Ykq6(U5`6&?2Ica`R9dh_3RdJ&g-q` zpPo7BjkEgXr_$Duc|K_3Vt0>Q!?XLfN-ocC#@!!y`kbH4^F#DG@1XxiL&qtmHsk45 zX~+4%bmtxPK3?);0KFp#_onF`zmDFlNqxNdd%}N9i))@k+HptZY3s#(`*6_nM~xG$ z`wfl@WcUA?(X0Dg(H%X%&%gO~+W5`$OZ=iwo`V{f^7JM*d%+ujuXcO9twz4+`;NO%5Y|BS@Q^^R9R|M?*HrhZS1Y~t1B-D8iW)g>X_`Ix*`|1dKa ze>H}VOw+4i#ls)tt=#kx?>RuXP?5Cy4ZMzCwv7Wly?19???2bkd+O5xo?iTkwVpr5 znVhZ;#O-_U)${kj>+IjzDRTbC-aMD34%89*w$-oJG0_^o*DDFtr&S&9)#0;a%e?jA&F6LWS}%Io(<>Nin^rIL zT-Y9OtIAjNNwn&2#`~^wb$@~t$LvpB85u8mBwG8;IvD(AWF3^wUoUMPBwF)g^lra> zn3wPJi<#2&DqKfz@MrgXddYvAmo~1fmY&J0$HQ0WDfv3{@OtH|^JJb^+jZ$U?&`RX zTXiw{t}8mk%j5OWpH9oeJlD4UoLqEuy;ZnQ9vgm)BA@%{}*Mor*c14VMa_IJ728~IyTP%Y~l9rhcpl6n6p2|2nZ7P%Q6^i6K$U}E9ljD>OG3qOqAj0?G`Kl{lQ9b`5) z<6^wnqeCu+lN)0&{teg1n!R%#a<1LDZjFkH@nrOoqf^M|oc|5y9*I6W#q>(w@Q9yM zQ4=I#oXCgi)ySuak3cN>aJ?E^cp_rSN9xtY8ZU0?-*7fLGe-2#LD%F=4U8Xro1Cc|`JqoPrk*k4oX3%u z(`$L3S5QkIIoytAPw zuGh*wuc7u1pA)C5&#S1t)936rx#5p}_`!!67daSx;u}A-#6utd#4|Y_1?*A>vGt><&(u|7v;IKJ73 zJ~9)R+>9UeO`dV$yr-Z~>-Zdbyw8ytj!(mpn|!b}{)vx0KB<$L$FAaLjL6M=1;y(N z9C5S_JYFg)sJ5Zckr{rExbaDA=C_-8P#|t2y*BnaZ4;j(Z|ZYoh6lyjhb=M_i+qe9 z^i6IF;>`r~X`A~Tc?+K-GaR3WBRBbAYy1-*dwllRs~J~I@m2zI+SWct-p1$1B0R?X zPC--KK9W;wY}@L!ozH38`yBb*K1XIa{@I5uG82m&j34w(J{`n63h2{z@;UO(K1XIa zJ`G21^1;^lCqDN0yic#>1ZN()_?$UnZ6RmgSRe2jdSy+(*>5;@Mju_{hyBL3l=uJv z+p>D?@AH0Yi}<{l+P*%orj}zHeU7;vKIb^&*hbDV$gvHlHTvu~a_o#g`;8xJ-&??z zdf)4F=7Kp!eurME5geJ}=o+2g;>MQ!^wDP=j1A6M&Dd#;934}Kf#QP%^atzpev7jY z`4GK6;B##08@cg`Kchn|!|`YQ4;6n->^J$a-}uD8@k4Id zppUM}libX>urs;cD*lLo{z$#f@Hy+@QJ;@fThQl4)sFCa9<`7Ays+9)KEFlnV?IY_ zIB|>)v5cSL;wIKGagz^r=#d+`=)uuN4^I5iJ|ClYtk03rH#&>~K3=bePY|b%e4<__ zKz(lfOp?U-nJkVTI#cv&Y^REw9Hxnzn&4x)fc^x%CPKxDZ|aOKHpq!ZAI^Saz_BAX z9DnG+u`_XqR`7 zm04|WfqggoU_ECPur*sgkr*M10B576PtCB$5B`v0Uq&b(lo0SmOhA0{05=IrJ1Sbh z2L18^$5|nPT&N-W18i;+@PRI4D=OfJedwT%9mgMf=(CR;K{0`t#2{ymeKU5(R7zmJ zOA3swxWJfV{FXlQ!UBHqhhJpmK|bV#4}d@XvX60=_WNqQ4H;uGW5kwvQ8(<+M~7JW z0OU*F8_Wu+(mX8bMNL`47zlmxu2or=m1yd6*j`rjCj6fdbL(a@Qaqx%Va{}j* z9|hvFk2v_7Aek1m^4qfphI?f%9!G zfn07ESW7<%`2AIQMEFi%4k`-7As&8z5y*ph#H}h2hj{q?Ss)MM5x25H9OB{kl)yRZ zgg`AT3B(~Dei{g@iGaYIHWcu~IPlX*V2#EKtf9sN`7;juG!aS)bp_UDP#}NCLB90` z){G+%hj_$g9K<0WapMHy5RbTwgM6C`F+x3oIK(3^;~)?6A+I_@jDS9N6$Iio_jxn5 zthE*bd6<0YBTo>@3oQlmp&rP42;?$BpeEe~^w4LF-2~P|7lAqHD-btcpdK9rav=}; z)QNGl7pP$yf&J()e(FGc@*qw}f$>p)_R~k+PM`*N3;5kA3=sAR{e;Z|^AHrsXOqxh z*e#%kK6d2EJZ%xkeXoEX`qY-w-%^jNVrQF zDAW`N3)J#mf$_kp?>z#?Vo!lMj1&KD1&)cX0&Az2K;5Vxdgx;}O<+El7vhsAdgx=< zTc|GFCosRw1o9dtP~(RL#>%lnp5%*uXMuIVSg^yM`My^mFY+T##!FmkNlva%TNoq| zhdSV6n?Oz}0>>(If*$(Vy(3T)>O>yQ33}*b7Z#}XXu%O46XJ!(1;+8PfF0vtJVS-L z!lMFy@JGDi0)EiPjyjMh`J#tDb`J{Zp^qK;280m;Yj%`CeDWZlkpgyO1oY@5Ctv*F zk9g#Z9rpMcE0C|52l~i~gCG3im+{;qye%{oCJRAfy3kG7ARwPDG!AAZ*hU4^%V`obguJ@m0#ClH5tL1ChR9{Sj=6`Bao3C)GMLJQ$pfqb42 zf&%%FSEA5Zm?Pi^f5dxIzz_P^F&^?HU-Zz&Ziav!`q)hpnh8${3Bof1@yUaHo))m1 zC!j|kIr-uTf5anS?6Akre1UvTJ?SGS4u0^5U&hm0cu!~}EEG7Wy(!!$d>|lyS-4wx zNoXr96VOBdUSYe?T6jS~AAh}t_k}*f4uNC*c>z82vD+mOhj{o~AfSgncBw*p;dP;t z@S4zBSSgUtQUN>iA+IGuJ7KwiAN&z-g@7OQv12^sNxtZzkKKy`dgx1 z0`bX%e3Aw1-Vo5EkDPq*^P)gJ^2H8&{Hzkl*VL0fa^m0zfB0oQHH0++>v*lex*ac6 z7pM^}edOx|)_p&rvOr8)`pDM{95+*iJA`q5OCR|LfjGn?E;dDk%>q6?6#5EVgkl0d zstH?#?m}4TB|IQh708K}KJs^jp284;ynFjCedODO9>V87F?w$LeLX_`~l3fjr3wUg9d5?Hf;3gd+<0(sn| z{lv+nc7oaq!bIV+z&QA>cJj=qcC6Yn0&(z%-Jl4{Wx8x0cIrG%S;>|7I z+T!gk-pS(kSiGmt)p+j%rY8L@Ip-xKA8K*dm65YX4QE{$?j1|&vrQ6=e452)T6~Vh zpRxEni$8Di#TH*GZfd*S;>niI>z4dYOTNb98!W!X;wct?*Wx>U&b)kJ$@g0P6N~S+ z_?JG%_K;xm|JKquX6gKB@lzH*ZSgY}KX38N7U%rLTo9LYk>Q;04CmZpIM1UDFJ$o| z7B6n`QWh^~@k$o2YVqn8uWj*q7I!S(z~W6T-rVA?E#BVZoh*Kj#d})3kH!02e6Yob zTKo}F%rLyLc8@qHFQVDYak{*A?tTKu@hf3o;57XRJi=PZ8F;=GBO z^_;9w*lIwmQqN|2@Bn{^wndO_wDfrhV@&vaFx}_kQOkaZu!UUJ_FN$=`Wt zdNr?q_d8MlZ`e~4Y77d{Nw=MA{P*7@i^~6v-2Rmq{1;6tW&F{QyHox=o z&=r2?C7RzZam??$OzFz+yhQUmFOK=0mw8qAotKEr{Lah1hWyS;L}q^HtSp<%q{=Be|9K zBwXL1gUoQw`P?fbhjafA&t`G%tBwDg+3jr+KR5ZD>mTD2S>$@CwD&i8o8t@r#55eA zk@ud`Y0o9l!4G-BbLlmw#f?7s8aY1EGx-}i_y5KZ=aams$R}++pVOLJQ8%N*xUoSW z|AwQFU*zOzxOrw`&dX+v6!CQb@k`n>lYjBEG3Gjr_*~1HeglOB)R^3<8}mWk7$clI z(b7k5>dqM9*f3X&ks6?b41f5g=F|xtKp#1ybWr;M?S2_95eX3*Vdv z@ljA97h?m*MgpKeI?WqkGSN?_{g7eV2>aC5s$d!g+6xpW*qord~Bv>rnV+; zlOH-}oto<*vrhl&XM5)Ug7{(OVfyvO86z8cN2}VjhEqSoSqp|^V>t6|I5jbx`JyKH zq-KVfvUpi>qhHbDRV-f3;yl|UCwvCfvd-Y<{?FX+nft%w`jPuTb6+U+Yqo3d|9DvV zFK*27^|p=w;CEh9{qGwyHeKSb{au%hcV62!djF_BP}0B8E&tEiooo3YcK>&s-w~l! zH@e3Ef4Ps`%z1(1>OXxOb)Eb3zpDP!^56B1j~mTT;+faI=QaOsoPXB--yM@Z9?qZX zw^kBtvV;$b^X!%Uq8{2+=g=2joC>cM4ZM@{X=iqgna<0Dw>g%&_t_Kb^vszP|FQQ|OMt`G*XTj%t5X z-oUu%GM;Qx(F$LU(mi4ILODWZ7M1p7uOB(^C9;OUJaE@LDT&SxvR!xO(^@4SjgA`f zX1~zngowYjAAOe(f1Mu)1v=jOpwlk6%gNEKR*}?x^7r~3n?kQG@MP1k^lJB^9-w!6 zeru?9xuKqH(fWb6hNSn!u_1xOJFf(=9eMlK9p4R#j+)q`f9RKSmpxgrPwVeEB-z)i zW1ZEtCOHL4E^`VE+*s=S`q5EmmgL#IWbbxQ*8fVQ2OGpkN6l&<>#nIX(UVP`|7^*x zrFZnn9|QOID;~hsjZc2nZ5$o-&7Vg@6lHE1;`ct@itT}f zOpC&$HI9k7nVf2LPsM@!E z6Z&)UAAzIqPj&k&Tfg<)9nn!sLU{w#VxIG4y-SyvyG^o;wW1tsKa$?)NA%eeU+>rX z%N%Tbmp(iM8UEr&J>kjvENk8n+2rSHhw#^8dlvV_uS(2y>PAO3esgz#xX2cFU%xfr z%dm~enm0s-EiodthlgN$e(=pMxeSx-)Yp-=Ljzxb}p14b{lMMMwTa^-?; zY>U|gjID{g<*`UyVi>*cYa@E(GJn-7&z9N1cG|p8L)4{V-7}#RuSAENH;j(DvhL;p zwXY)C@{*4xA=^0UH?OAX9Uh|3@cDYwC89TL05behzlgsp@t*9H@3u1MM>anfs&yhd ze0WH7RGZGrL(Ea(EzgD4Z`J2{B%4!XaDa7y-b1yA9WXNHHzJ!|9@#YcW8NbEuEZld z-(_E5)tcVk@%u!<-A8xp_)Xka(Y?@loWn6a_p7}*cTLrEqo)dNuF)mr$x^QTSibKx zoriv}=#;u`f+wq4`p#VMN^kG}ftwp89d(NQu_-j|N?iM&7DY$pD?i3v)G*PLMXx)5 z>%4{0QEhK8;x_wi%U1Lr`6R=8g_5G9R=ilunNfU|Co5BB`g_?W8`^b@^UjJHp6$Zs zyNXPbZQ8w?f35ks-oSVdb z^DpkVv86T7jL^x6SaM{BV`t*xA3b#NZFKNs@*x-FlY4yb<| zBQ>Q_~AG+wmfr)90jGl&#jx>;w6s}*c;Sq zS*Un@y&69a#EqYZ-d?>(hcQuWIJGJ7b80}Xk&`dAHagTA&bX;H96!_=&bXO`&KV z$rHI5BQl!@)h9RPrY1p)GbbiDxTz;H!>Oyu4Q}QFnc>Wd$qjDi5t-r4r^yX&)&MfY zSqmmlxLH5Q3@0x$*Ko7mkQq*1W}U#z`b1_pd6_i^H)|W2;pAo3C)^w_$P6bhv$o;p zI6`JP$CJqoZjK#fhSQqd;O4kPW;m_M4Q`HWWQG&d9KUdLydyIlU6ViD$dMVIAYNEt z{WQ}n?+x(gdM)Vl7HS*$yrtS(ecnVZ_UPZDSM1>h^ol*akY2HeH`OclaCD6=`;9)W z@xy+jPiy>`Ybf*l#9TuakisvbM<6h|>2ua7Hl+k&oAJ^|UQ{3s@rVn@PmF+H))GGPhhJnZgf|3^ zg~~!xVU5sI*yy+PkyjF$2yY4r!UlnLy;9&9s~}Ljw}j@xdcUQQ{C0sj#3SxH0YCV| zZ$$w=_`~m7fqcsd)U=wwT$UFYFZG}XrG0*fTIQmxz}ToKH7F-AhgAe@(8rGXKo5QF z?i7eaJmOL>;t-FxRR#Ru55Lr#waOZa7Fcs^HwmoUEJ7wBvw*Ma0=CHlKHCZ8SW95+ z%LU{$1oZC`sM%5hJM59y6&TABf&J(q?;tQoF9_^M5C6QEF}4K)afnCmyjS9Lp1|DI z6sSqi=X2GvCh7>RsRlxA;VFSy)Du{{%qcpv1?C`DAZ`zVvCI^RPaeeWC6L2(f%xP> z+)JYodaR=hA;Ajc*`NuiZcUl=8jV^g7& z&|1LfaDg0IldO?8LPOyZAt10O%Ls#opzxqT?yU8oz%j#GdO*N#s1Wp9`VR@{8$0^Q ziBnn_==ZV5&k%vU9v0A}r9Vs{ACnh-E~rjMLD zl@$8>eeCfwNT9AG1@vg?KPFI5Q&;-PnTHsmpFkgb{7|Q$@VJ1SHYkh{m@hML^n(KR zrtW9a1W=QII1E&W7+<7K+v z(nsD;h!MIA9BnLK)#6f%^3kN(hh$3oRxO ztiL1yIW7I?g^I!gzon17n@~cyTc{*Z3;N6@^;{?*r=`D0s3N@JxAc)yU+UahU_UMW zdII%aEFh<)|Dtfau*7faBd5N_gpLCHY3bJysOL)pa$5S!gmS`Czon0y`cmh%0^??U zjEnIxo|gsWwDea9<%Q*bOCNa$p@`5Ax!AC)sc5BWIp&6PUXK z0y5SzIW!T7)l?w&Mgn6jEW9Qp2y2CA!fN3bfw?Uxye>2s)(I_yw}nCiwL}kn?A8nD zp^qJNf2*)cXen$Eh(kQ$GN<^#AAUCq=%J4tb7}4q%-@a8eFFP$w58oA+(_vL`^@Js zh#$@YhscdoP z%E(zahS#xpoW+9{Z)9=i$JjC#hPSnN2a9*Hcz28Uws>E0l~7v-?>#Z2&xfcr{6UL9 zV)5Y?A7$~;79VHvi58z?@#z+yY4O<>f6C%>Ek4iU3oQPE#W}~Av2dO+oMYYaWQ(t~ zIP2Z$yk&8YMABS1g_(`f9B*TRf}9`G3Wxp1CZ}|K~MwzJto}TP$A0 z;>9c;WAV}!FK6+J7O!k^eM~zoR&|TlvUnYf$6DO6czugEvUpRA^MClvxLR4ft;O%Q zIRBr|=yw%2$8t|`bAIS!@%|P!|DTUzf2bulpVKp+%d4yT)9$DPZ6cprGoRm+=$h@C z&*@?GFK)!T-d1PM)z8D(`?LDrhi_y_uW!EQ|7j&O{pUV!XY7;DztN|+l1Tlq$2ES; z{`Bpr;MKhT+0P37ztkgtY7BV4OTUe3^7sD_2%Z0zIo~KYxta0gpz|NTjjHzd^K+y6 z{5$(7<-cQ(Ek15EKZ$?*XMIkOYkaP0%+^VS_c2oo1NKKT%TXmo0I9sD=M`q((BQ{5obl{;NiF2 z4;#L-jL%Mec<|jo&)1>@$le_CXK6l*^(S6jXN4!Lxm7>DCz)~y-TPW}2)%Z5Z~BVQVkwt;&$itYdRMZSqNdJ9Z*#DJAm7lW zxRkQH+*{(mE%3rK(NPzc<_^pn+AxHyTlq)dM|S(^&4EWBJhe4?(0X^vfRkVIS*QJ_ zv(TSLe5aR7mq7zVAKkXt)0>*Ir89aXnr;p?Uw+2R zWm(d$gUFVg%M*Cu;uoQ$Taxsj2**xi^;IpKBc!$I$x3{l%agHQH}o!5553FdxdM)k zI@fJWVA7mvp5B=Oopl@P*?Q|5y~E{BKZdN{*P8<87A6Lel^U>Hx0If3XyTk{o-A9^ ztGYe)atSFHPnNoWvat=x){F6VQVj)G{a!m3QZAeEmtjInon!P_z(E588gHn_tYPlx zVB=&yC#CTQG~PI5JqFxW3YluZ`LVm+3-pz2z?E-?Ak!R$G)JCZb&)MzSeH?fSV7EluUC}S926PD_K%hnYryH(_98LmjU$J$2aJ*Ml!99 zkk*DLTc0wg^IFNYhC*6Hp1%!A-ya+$*`2wnhDPrgvo-4B6M?1E#_oUNVszAif}d|4 z*f>`JS^l+u_76yAa;f^*RHuBuTfcc(7m_BI+LFzUDl_l2WF{B%z6(6jsjOrs7i8t$ z`Lbha$xJTDsxQ3w^-GdXsqn^z*7b3rPQ8^|YknF2Vb>F_+W7V3d3m>_ha57A|`uQw#jq$loT%5Zi*RW{)HJiDX z=QH%-+~3oOXZP9&=bD2tAkXQw5uQs;`Ya-)~e2&TJT)h^He75LjU!UhN#y`(=BG>ql&m|e3w@R*IX^VV5#rWr$ z(@nm-s9F#+}_^E|KH5xbH$8G5bwrceR`fYKMh-VVk=Yy@Y>}HBklCF1GyY?Ij?CsJPvmBd$ZT%%M{a6@ z%y8z!W3)^l{-m$c{O|Kk65HqSeV|By~Drr$u(xDR3DT*TZwB9Lz;fep?Y>MyV#Jxt)N zzxxE{i1S(off}%Odkd@qh4apZ)QWNT5b_C}6N$lkEM~5+^!+EW- zV9pOWiQ~JRKrLega%Rlfaz2e0N($t}nC}ue-^B@~1jd42a%Vj=Mslt#kVhi{{a68i z0RcN>Paiq)h+9iQzm8B%7%4E;x&nH}k0VarL4o{Cp7fD3uA79Ke!retj-Lzyd6F-A z*B8*IrQcA95gPa{edN?Hi%>&gT#S!#vXLkGl6O-9ea4Mm6M=d+_FMYMnWxMG#~I^d ze2g=rK%V4FUW^lc{NlfvK&=w|mOgUkSfm(CS&}V(c2yF%Av@t?^ff()lmVS&t zy{Y@{0%OVJ_c?Z`>)ir!TKXLY_L<|0K62_zy(b4LBB6ZJutx^xwg)6(xI+QGnk#k;(7FcW4k2+Bw>O@`o2*_#a_Z3)U z_xUY-9s3gkd-=w}nC=Rg5DE&ahl zUSW{m(nn5xsdE{D{j~JSiF)2IAg87OfRI}l;aRb7Le1@A0}iM9ue?|9rnx(HGNb-PD_7;fc|j5rH?#D zU?Wy#VS-Recucrcz~>#pDB*TttWZ@LEie}&g=)g%LKR`WfFJa+qZX{0F#>)k3iv@E zJ2v7F55MCC{Gg8=b3z>AQO7Albz!oQR~RDT55M@IAP}EC@Pj}6k~ia@CSX5Rpgz=t zdQK9MvmZU`%YO8b6PNf83dAKoa_q2YeVOYZ^W5582XP$TXiK{ex{=Zi_L;u}5kG4t zeHO<5yHI`Ebn@+}BeOwkcovJZ?u|T$#Tm1a=d(D+qLFhRF`RRc;l(Xp(&A+-Uf$x& zpRr}`3}=04SlTjp?;l2fmn4SQ6E`}JC8q{Pr;(+TV98rpytSp%-ja8;co$2D^RV&X z%i?`3-p}F#Eq=enhg$q$i*x)M{~T+EbNm?2+BSTG#V1>wb!~KJSbUbnpS1Wh7Jts- zNfuvd@x>N@$>J|t{1uD8X7M*HzS`nzEWY02n=HQ7;wcv2X7TM7Pqp|57T;s>zgc{r z#rIqM3yUAL_%{|mV)0|*W_|u>@lzH*ZSgY}KQC_P?vi+Bfn(vn`xyiC_ipp|?Yi<3 z?fHtln7@NtB9GdC@2*}w{oa4RZ^yE&>dd_QcV%0i;Qwy9k)GO^{AvHUZqzA*k1}-9ZKLY`{ohkj`QJPqY7Tf$`cL1^ zU1xs&s=1+-|E_O*+-QCh|NPJTd-qI@w;%)dx7lj>%}MH5@KB*(-(m|xaWj7oG+R9^ zzU$h9&aT>DaVe6vH59}AzxsQ(ecnU=dVlZ!NOE(2;yRP#{;%H5`IA@${=#79yv+4% zYBt?V_~-3-#bgdLA6pZ@A$PCP+5M*jdnaEwvGkUR?6W@I0})x~0!Eg2c}}3~{j&lC zC$}v|%Ghp_?N8_D$6?#^cto~Ow#PkL<^o1`p~F+5i0$fcv90KCh(CQGHI(s0b!VhI zZv8v$BC?5-b_S5eZwWP-b+h~5U{av=NUcNOnZ}W!rt=@{r#>;k< zWa0H=8+Z0)d-oQ0vAy5D<(ZnY9T++pf8x)5p&uH@I9s$3l=m!+JB?&`Mxa~T%Gf_ zz@GW{xtA_y|83<2J!i^UCRn0w6X){0R_^!t$9GCyrr*KbRVLW{;W^HUpJoQ8m&h>Y zos0T^0b5H4TOaJ@biZef)2r~e?H}r?`>j1Cg8jQy4zxM_rc?P!y^{;a>G{(eMT5Pg zYq=+9H3($8lx@O1)!^k4C4%=9yVko%sXf)l)aw_|h z=QVF#I#&s1YPM_3#5VohLw~-1;@ye*ExhDf!Q)Be0!=TBb=OvSqE?Z)dXCbxZgA#> zvw`O?%namg)PG1(%~6fkwSu*Fy%>0D=t}2wwc)|!KKhLLOO=BK5?>8n8nN1W;mZD_ z1$3bvowH)F!>ujdp&tYTBft55@+Zpg-oI50{xJJax90dV?$YmX7J?;likUG#s;Rp_x7B7bTN2kbM4@om4n277LJ zCGNiLo!#nlJ2|f#Rc~)NRI7MWU14n!PYv-J@k7;Lt=xEzw@fQ|Iwum6v+y9>yNHw>n>3xyus6m+(aeI?%}$(rtO8r=7B zPxn;*h0e#Z3j;rDZ4_PBAb768EABT*3!HoQhPSoRO<1Qf4T8^Ceaju+sHgLF>b_MU z>&D}!-3h@bTXl5{bZYOskTA|EzEaO69%vK1ZPf;MY;du2wB?-2#k%vGQO$y_kMDQC z=)c5SvE#N~{Z-QrM-qbbM;vk=iyP!jb)Q<7Lp8W%r)>MJbI&&I>YVM+wOos2{q9u% zcEP*W9dvj9@VxWuz55P*r5nyUftJD1&!2TWeYVg!^4s}^zqHmfnp`b|LvvknJM!Jm@wY>b^HTzf(A{ATF%&ez34n zFnQSqcU;-LPP2~|ZdtMy@gy4n1 zckb!$VgmzrtbTK))@e${_Q4VR*SJS=W(@qWG3`6I zd$Cc0>ir)7w6bIqswM>2ebdLy|4Nro&N^e`j@+x?U_0L+xOL1bH+RkPp>gjQOc~Kz z&%kCi2-exQ)(w4I*v++b`I<>uSGN^!7AzHR<37;-PWP<_1B37CCiJxhZGyF5UghrW z*)nvh*_6uDRlnWqng(~pedgBL(>(Om_Ey`sDaOO?n+AvW{@gutsDS%r=*+r0n%@$; z+5``*S?+$*wX%DpZMVuztAI?+6My37T)QICvnvE%EW9JEuWSd?YXH<^lWcW zrC_bx+e6E1yya{TE%ikKeZ1BG5#_sDma>PIP z^{%MddT7~Ur_T4=V%@1jvc#Y4`9hA99keD(lnvgLJk_1^^d+ax_A~Rps3N`D6@zz| zpW^mg`IGZ`vkCKaU()ZvjsH3R;IE^diU+qjSsyN5V@e6VFC6$I{==F*;@VZ-;hY{6 z%G#`{{ucDnh4^wm*L5e4Sm0zEk?_OW+qIq-UyA=Vb&`AWRN27c5%WHpcv-)L_sIG9 z{af<6?;UIAt{GPD$B#6>`DdPqpVi@9?1YymyQ>Fw$x~hL)2GUuiVv<_~9l~VDBGn-9BxU)^T@iS{qlVecO}h-_h&zi!E{b z#>u+tZ z#wA%lZ9m5{q30t5^K0ie3{)Jy`tLi-(n)dY->zV#f z<$I_%{rhw0HCw~7tJ9wPw49~l(8m%ZZ_@Y4Hr=!hxwfNq#cAu)eEXMt(2}5^7jN<< zpOfbhR+@tINFoA9h;xU0l*cQeqUW`*{P$Y;esmSj`vIo=0rK~K4Sm!7%*vki6U_Hb z*Ab@nGEDU&kFM@{9g*azA1kj5=)>)kV7k6!x{fs2 zbINP3pJ}?E@a8qqpMtXe*u$RoeP#c+{-sp?K3_*1^}KebcwT3ud0sn@cwWcK_L$#Q z$MpMrooUh!FkSna^kdCd&HSy_Z!h1>fpYev6_=z{+i#PdWh6!(iN#ECeE$18eKf#1I z+RTp$Plmi^e_AE`$M|oV^b;gsY%kL^9=>LKCVajod5OzlUQ#v|aEK40{6 zcKTfZanI}MS3R#2PI_KPn8qv8yX}$%Ru6<4OA=-2uZyL`GlRw6r@MW0v6HNXdA@B3}XPT}v zO!GChE$aPc*WLGdAEVy?D${+R_ciMMQNOwG z^FBwtU*Id{epP+mtM@HWGn0o)0m)!SxU#8wKuetB@ zK25!!alw6`_igI^B8A-e6Qtj(_itZz-;bAZLA^i!z2}VnMaa0I-v8xmcYWUXsrQdx z)$x@gUky;|V{XBJ>(=XSD@*B;Hy*UYkNqgm`$M#YH4A#ypP#f9r8Ka8v)0g$pPg=D zecnH+`co*H}6Z;cC~>$MZ1X?Ubg^uV!4qEf(0>*wM!$If&HV!JnyZPeB&DTpuNV4Bk5m_Y`BNG1&s96Uo~|VN!BFG{_PtG{Xoj|J|Fs6+oAtQt74(x;i>I* zVP|EzA_C_}aYcn+C5Sucmrf56IKRl+WZ2{9 z8?FbO^?X+gJ{L_36*vps>>zN?kE>+VLK31UfVMVg?@T)LxIn}teK(D&r_=Y-~zh@53aa^0@r6fPZ>P$^OlM$y=O0h zYh~$*!XM85T2R=jdh>$OAAT-V>jh;vEa=a`Lajs`>@>58sF!NrW8mZGHx*xz&DRCK z3SZP0_?|oYf`N~p?^HXLs?Qhrf;){BeE(_t!wSD>y1%$l&nfq@YQ1Jl#)^7hoth!) zy&F?e*z33Tpy(I)=I2Gm2mk5avV{fhzcKk48y#EvEbCR;(9e|nSykULX|&MqvAnO) zk1D4758CJFO;vwm@6AI0^3)DO|J^6vG1}+nQB}W=-y)%((zlP$|Lx>tp%1L_rg#k8 zr(P~*0d6gJyoe9b%ar?2w2Sx-y&l__3%%Zn&l-Bsru7ASjTUYedb`RGGxQSVK2dG& z^Mo-%&*#w?p-0DCJ`u{gf<3dqHCI{3P3t`F&)YTCsGq6W;rgF{KgGD8q4b~chXhR% z_x-6Yex3$StSgvL2fl10{PxPlNLz8bPTM?F_{Y_^4!7oYnV-9P98hn=(P5%q`QT2X zUbk9TjdA1Wa#g=;P_)p0dwRIgf9Kjm#=PL?c2$4Ty0Sw5nU{JC{e^Y6yT{oyE|_=A z4>>Kse|g$a(O=;Y3f~xK=&klSEA)z0d)m;;PGZo%g@9f#1g4vri~1X1`bGFt z4 zGnS`{i}mkfueXeKlh3E1&;Gvl!!p8Nc$xEJo&M&v+M?c*YpWUc_#8{EcXdotQLlZU zk43$;mLj6wmp+k(-}8Bwif?q^c0&K;kz1l)6O*qS`XTasOV$7GnXaPWUq4$w>^Dk` zJz?PD^D-6JmGi5GeuX)8h5q$Zk;2dYMsE@E9^=O6YN*G4Gya7!qCXox7$oZVvD_5n zb+oZv)Zf&jtElf!b@&`kwHxj|V`_tOjHTuWrb=*JfOOH1@F7}sA zD)til^@Q-V#=6Akd8&Tu=?dcct9ndZq2J=^aN*B^)E1xjL7$%sKD;X>~hB|Q+$E`xSI`Qyf8itxtClcK3W1 zF51Joz~_|QAMjr2S~U>AAPj8(7S0_CHg=6=e1%U`qDjL z1qb33`g3xFd*2eB!~Iqt>iZV4j+^2x>>b$NS@81kmazse8K!j?dYcM-*-F^Eq{mIs zo@qSL-{hWS#d*MUJ^T#*GE8=%H|$ch(CglKr=gc%+GjwodO=;U&(He)8azZO?STjA zy))Lm&S&Ouzu+J}o}+$-Vu$O8hq!sn%;Elm&F=fYIoyAvf_q&GPF+%U1GF=6)!k%fJM0+g)-FO0W z;A>P{<$ezR%}mz&3mlnB`;7OYpLIXz{bR5(z5z786j}A-oMzu5wGYmjy=2G z{uZBuo!_%`oYBM|e~zBN-^G8&qFaB9``wqH3oVp zM;W&d`)WIE3s|^K*aBA6#Y_JiRSof-P3-?sjubHtc|4mU&LNL|BK#Y9+~+)=3!sN` z=&~(fV;$%-Heg^|sKy@CMtAjrsJa66_IBUWeZ6%6tH&xJEhh zsx2;u9oBz@^7TldU!UG_&3V-4d=r^RJ=O;|r~@68F;4WC>!L5JEq}U(ep7mHAoI;A zZ7lP!(@5qUQW_}pxX;+o9`2zXw88xX2G)mtZU-ssK_BhI9%JA<+F%`&v8_nTGtMB& z2a}*K(&jRc@)k0WGS2s-JnliiKfSZ90hDK3Pf?z6hETpG3G|V+l6jQ3mU)zM9&O_u z^ijrkfsNaNKI06fJobjrM{1RMl-pz;Wt>NwoJTq1L0xVi_E8^gf+rrwK9uJ^qMXOe zPWdnr%s8asB45MDPH7vNM;Yh)Ql8sH%H#Va_vf+C%11{j$Y+&=81KH7Ym-hl~uj6|WY%16-qNSQ}nTyr_MiS}3rwm6UWxcyO- zk0C+b(eyq>$>Sc%QN}vJ13i@EKI6fCZWHZuJHUoI(1$ML1UBv$>M^#8lpjk1{Ymsb zSLWdl<7Iw2rR8M43Z)Zez7(ZXWZsX`SedUt>13Hl8Rub#bzqCznLv5AHIDL(2lb!_ zY|w=s^3a7I@~}Tu=BH8moXn#P*Q|p+ARkBXoOe(j*C>CU-W|xwJhwB0O1Pbwl!qR4 zX3;y>eSz|fVK(KN6SOgh1o{qok4KjBu+KcBF6y8hws4I+?!yN1s0Z7~qdn*$k9yn= zbh&-#vR&}Rd6YBHu)|}4J{2QDn`oQ)2mZ1srt%W>&V2+Q#Zm0zZ~W#4f7g_s$@I7) zlyZDCesd#_zdgp^J;yUyx6-s%l;1@UCu{glXaPzeB%!Rp?n`M=k|HF)j(gC_PXa$K zECo6xN$@H|Qjo-7<|z>`l%YQKpbzd)27R;#n`pZl$wMTyNKl4*`A7(^@k|F?;1+to z1_5`#RGtL$t}Y4mAcaV(kf0B!gSwSTP!Cw3gKLyy9-7NP>3I9@>3`1YBPy8B6j53EDw>XtxmwZ~zZ* zeNTdR&>q@tKmr`V16)}oXb0_~-FhV8>}!%(lJ7{+E^wn>8xoAMg#u?V(*O34ADw1a^TNcF-Q$4J82%-~nFX zh8?trc3YA7le8xRt^g9~;o6@hgakN&7j|F|b~};4r#g_JPDc`yqrb5C7)f1{2omVA zKCV&Tj|5yfNPxLN3Hm;S1Y;CQ0v~yj1a{Fr?DQl7FE*Lt8s%L{z- z9`FDc_&|RiCjl4TNpK%}(8qY7eLD%p;V4NI$yt(TNSq|V!}f8F^0!Eak{ltyIE9fw z57#I^M}oc`Bk?EcMS}izCIJrM0WR>3esm$hedzgj|%}LToVEZ@;^l*)Gj1A_riv)d7CxITWQ9hIeIKd-09zz10Q%GzilS!;3Q%Rr) zebgII(voBp3HpbA!XEGd7y5~I&>q^IK!SR}13g@$Jcb1Apgjx8Bof$zUD%H$fzM7P zfgbcx4|t#lebk#q0(;;Yc%CCcJ=8}#=nu+)7wteF_0TT#px>9|Fv*i7b4ctYGfCQz z9F!@p?Ia6G+LFv8X-~3{B$DJ13EE-(S(Fba*-z4kB$)(yxJLO5k}#43B(R%A0zF)# z{CN^P3GlQdnNI?HFOhU2Sw_;4WI2hQ1p27=BFUp9@g%?rys!s6=nwFs9khpb7m=VI z@IVjOD4$D$cF z2VS%Tebhs{(1ZR!lG7wllk6iwOj}FRmE?p>agFkqNxG42CV8CX6_PY>Ju2H^;1nr=`CrA=VU=Ma-e+NlVlI4fq zK{?uLKoUiQd5}y3J?Nu+I!R-a7f3LV4v|0)`Y1;pcF-Q$Jxl^U=vN|v&ch_fNQRJ{ zAwk`vBuz+Qs}adalBY=0N&1nbkf3}Z32=@e89;KJ1bWa%Ir<0t&yw^fd6fiu&__9Z z2X@dN+I@`#de8?p^bh^qK!Ud5Pw=ZZNKoz|X-Kl31Z}||p$C1GqpyJ^t4Ictz@MQ9 zeU#58fgQAmc2AQGB!PY`$=4))BmpE9NWLMNO!6%WcsfG@-rgk{LGmAxXp(nGhLc<- z8BOvb$taSKNrsZVMe+>EMUs&u?~$NB+Ch7tkf1)=L3^K)K>sWW?7|N8FOZ-<+CjZ@ zBxwIL5^(r73EDw>(1Skep*^&Fo@5xwH4@nB4|cDTpdGY_cEJP6FOi@f=%XI&K<_FEaDxZn1Rm%?AN9~5jK{Ym;J*b4#t#1u z9(=hZrQ<2RK{Ae{I9<=7v>2r`DaCJhPo(rGk_jY1B=IEp_wr^@3SULLKaxx$`J7}f zNfEjaJFqvE#D}h74|Y+WkJ2fW{!B8B?IF4oZI`d7cFJU>Cf>4(!41A0%T*z9YdnfN$`CegX&neL?V+MFO6|C+wh~zyUtN z`}ZUmCyWQ|pr62j@xZwKKmt2x5AC9#z=82BK`EYJ!6*1ef4?FrMN)x;Zu#UVd4S|W zk^(Z1GUQQKkOVL2;+!R#QvQuJKIf?jgtiUPsuk@@)%dP6|Ce#lzeMN-=>s@ z%W`nvPRVyv@?DgC4<+AA$@f+A17w~O5%-ysp-MU85|@us^6)b*htF~ze#Lpr9nQla zIX_#;&r|X*D*2^Keua`>qvSUz`7M-ZZnrA=1Vv}JQoc_qPgL@UmHaU!?^5!wDfu^K z9^-OaDL<#=FDUtoO8!HcN8KwVjQ>+b=W|8pD<%J(lK)Z3|Dxo7Q}VZ!JmM0@0(KFD zIFGo;dBhmb~Odf47v1d zDEaeB{#_-1Ny&et99vPyZ~KG45F8*ar$Id0!=8Ov#s0 z@((F_KP6v9$@?q$T9hwHT;dgmpiDSo&tDL~U(uWL^#2`SVLigejvO?8!q})rfq_l# zsP0w4n=R)z3F6$8{Ak8wU_~-8g8ex;Ea5RKMr2omlcg=!qqLL(x#qFWB)Ea11f{ zmwYn+Avbh>&F%3xnakV7#EftBn7Q=suir1(!QR5BrtP86$E3B2A7*Jz`I|}8(tgZ% z4g9;_?cP7=%1ldA$CYi97N04QI+wniUbF=AW!;>FHh}2blJ!AU--UK?y*1&PAZjBOZQ+{RsZH&Gx2c4!_ug#= z|9$sv^WeX2{6B!v$Mf2cZq2 zb7_5J)I&SCPH7MpL~XcGrnli}Oli|1q8*3rDBX|gonY|)Oz#8_{(I~$XsK|x5Z(9o z@{B#`k2(G@kIcWfmUx_aJ>oQM@Q|31QFuzp+pAu~XFdG0?(@43ZkYXp^?0ezM85}I zem>~-)~AzwZZ}C>6+GE?v%_#}r(!@T{q`+KKJrA%l^rQ?db1sM-~R3 zpHSqWuH^{0TH&|W6>sbD#`oC5;0m|nT$`v2_usjm*tiyr-5+x7iqQS%D&I2rA6fa9 z2mj!H&f>!N#Xs|SC;oYyB39}CAF)VJd&Z0%7o&GBPw!(!UP)Ff&aSd1@jo*jvc)u- zFZzAH{HnwuS3V9-`6hqTr{U9WZ*=(1l7G_2$&{vkM%UH+_1X&OwN_PG8oFUiBRlxt zHljvynT9Xw{_o>R*nj-v)FxA>3;%y-NX_KHsncD_REAdG{5-L7Ga9=Ca_n&b((H0r zhs65(&g^m`#We)n@7uQF{`i-CGXEJ1bB;eeP7xa<|1#|o^)!zA^8EVN=aB0EjzWI( z51uQv!8+yJ_fycv-5GNet8Gb6t}$j{V&M{Twwv$lOHEEp4yM$CYu*1H3g>iA^+e;) zsuRQQs2{T^BDmYxWlbf(glp?kQZ|1l{eOK#@TTwLTsNo;_cxONucNWsD95fa-Pifm z`G0NO1`q$oy~LmG_ZR;qkIcWsnJrC3yd`AjjTXo1(6wdD{A1ns#5fCYp5Ho}t~vfQ=hvzJ!hy9@rtY6* zNBv$&*PJhW&_cwQCDcaQL#tDQ>Gz9Z^UAyt&WSfD{!kgNyAl7hY3!=WvAa^kcC6b+ z3l0Bo`O!iT{|EOse*N^m{Ga`u`RBM|=G*Z9wbK7(`gnBAgb_oc#^!Yox}eJS)Xt5{ z*}kp5EUo${1uSRw)w7ihdnxVdj~=vKc;~&8w}wo2-Q4}LWzv^%w%7&#Nh$MjtJXf} z2L~@s|JV|QIO0!#H>3KAzs(Je^RHq@{dCF?`J=M^Ce`JLsq7vHvf72-j#Qh3Yz}9n77s&z`Y8PLO=JPXZa&apATr~6!9NLV+YPp)*6@E zylsHt|6SS!c=$iKFW62Loz>=g6xq%hB(_ z;eG&(-G6B8QfchK|BWMEQtDLSXYk*k`aTc-!Tqt~X&(5|j{9{N{7XKW|BPiiukJSV z9Q%95M#;ZSyA2vKvT0tOpPJsjrjy6~t#oUcwbjYx*35`W#{!RbuzkAfVORC=GAYhw z58EuM9c**w*GVaTs!!;T@6~GUL%+iZ|5rKxBF1K4`E_U-_hWh!@idS1 z7x%UVe&T^2?YLie!N25@`FGd#%(34`EY=h&Jv;T zB!yWWVWnFBdMM7eZ**vC>G=&pdp;X#S$8zf_SG0`t0|ccQfAzkcBJngI{zx?k7q^L zckNpv_#acCnydYW2v^d(yYOcPpWik_*dBPW8u8y*@V|886UPz~W*@79b(~&kXBCYd z^Y8QSuGUX}8AkrC^FR2@Fc1F0eako6-xvSP;~n{z;{=@QOXolDGa&N0WvSGjgWC+f z(#F#7?cXfbC(mpdd2ytz;rSzJ7d9=myw>ulHDJ;xTS@KLR(pKrrEWdinaf z-86R0zkhmsYUj-b4gULYF6hBOxPLyn_kHot@rL=&*ec_Tj7z|Y7ssE7SET>Tv}g2$ zk%RN>{q$Laj~=u;p5ARKbK=v~peMqu*G`wQevU-5US;vPoNbP%Tx~)OUEtco# z-%{HDW1rMIeTUjUbquxjPFQC7YUhV(*#CT1v$nnC7eiA0UJtOJIa15Muh=)K&0h($ zFRn1%=GS+tCF{GRg8%PIgr}Yv@o`$EHbaHK&&j%O3ECdzDsk?EH1rYew4-(^QakW@ zdnG@M_ES;8|66CySbqAcs1+&hL0sQF6}rghEwQ&n{p_zh#~wu`5F0ubnI4^vruKnVKCTZdITiE+17j}*Ks+()=kVwIA!PCdBgQC7lyZU`+ z!GGMlc30ZkBx_{V65<+Tx0A-M1&tl}jlX@u_11-a2LBls@_FzN?%N;fdVl^e{hj&G zkoy`l--iEhmi{kOtiO4W{`A?Zf>lJ{c??>tA8y^;V7>L%zRj&=7KYg>cIcM2CajvP z@B2k;%L})%IWJzc7FvAFdihpe*V*d<+1B6ZL+m1kx9k^S&umpMEpb{K*RI2e`Sclt z8y4Holk>ZJt=IoevP0o@Twg?Hq#etwBQTiPU!R`6Q}z9g9{)Q>T=IxNxVP#)*WU=q zBlFKZm{TJzZISUwrX5BNiHg0C`13>acHqXr_A2e0VwwGT6YILmp{d7)1lfKpGdXqs zuR*rmleb!rRT^&F*K2axhNSAQf$yHNUcdc_;9uo@Z*m6_!&jaNuvaQu&GqKe2$v&z zvsiyu#YEUj()xQNLq9WHeCzuZ`z!N9{Xfv-KiXMA>u)O>JNW;mX|qzFP}bj`ru7%x z54g|uH$(DxC;oYym?usJ|MwAp_RmR3rM4X0-s%f&ESqTkZQdjP=sO2T+DeBUNjv@2 zF3U@ej#|TL{T;hDU&`FqSEp`uRFck``P3gf+y9{Tx7kkt_Q4nXx!&mQu+q>0TDB51llbJNA|K@?yvKYcCdF^MPmp4eP)(V8?3CqV@>NXxKFs> z^;gClGym-G%nQe#jCs=kWs3OI>^}Ve%>z~u8`w@v$KlrB-dkgBAJjgrRG(m5iN$+U z0|qp+EgD(aR(@Ns?ac#UroFW3tF(Yp&1`*#6!RK?&dn%gua+<;_4hx6U5S=vBEFnz z8)}=>^#Rvb?V4B*zT48kc52csYqiOG{J&HDNon!HUmky$Pc#3_2dBI)L@bc;U#2~V zje6#O;(z%X&kAnXPKk(3mM@Fdvre&CEx}el+wPW8sjjY_T~*gyupWsUXbY)3)w(A> z&^CSWjkJJKMLpsVIOn<0@as}`KZ-vYh(AO1__KoIPYH@Y*JS+hq4+b}6o0V)KTqxa zx8e`;c}M=GzcWvqg8%ynKR)rM__KlH&lZY5>nQ$Qp!ids;?FjUKdUMJgj4*vK=Ee~ z#h)z{e=3^d4>;%ebE;x#5r0ln{24Ce&rcM8no#`dE8|Zf#h(Qfe?C^?5BC3!sGXeR z&$z!K{_uIHnSVZSW1ji^Gh?xgKQcAWKYOs|y`{6FKe0Snu%_$Ts>{+Yeq6xPuaU*I zbMvya*SbGwd4E>*l>D*NZ3|ou7?CkFL+^{23=SSBKU9@B9{p~*`k7oYOEb=%-9@F{f)HwglBmd8I z{@Nq{oIf3R^yweV*e22W=ahbP(%SwSXX*3QM3=qy$43Ll$JuryykIGRZKBQ3xh8Z+ zfjMc||ErwWDw*Ko^Uv!M6H2DI}g@jsfz4xCpgFw^P`FKcD~&xe=wIDZ58k1Y42``%u6e*R(}@5Dd*{iU5c z|Cf&Fsd4_9NB$d*cVhp8mjiE~4=iBqyJnwl(02B6M>g0e#%ZO_(%bEq# zZ9P*vTM7iHq~0D}FZJ+&=_3BCoIexZAf3-YtIR56shBa<_Q9lJ!T*dm>D>D4!hvQf5UZ zSJaywY;D&5n0otaKwA252Sd|FO%wd9oZIHw($(|N_YZcpEtcn>laF+D>F1x~t291! z==^hkR`*ao|3o`k|K;;f=9Bqn9?U6^)1{M=f0^?6XI@93qJI8qm`it-NWuAMNSjhe zaQ=B>bf~2^oqrB{HZ&FIpN+>`k1nC}&nkaTYsu%ID(Aa=JEZgZ=U6)bY)5CFkr#~f zPgj0%{+ZDP21Dum^Gw3*)_nencK+MvpUmeS`Ir7~5G6Bw{+U<)d+Fz&MzbMT+N2Jk z{ZB~j%p-m1{Il-)Bi485{4=%X(KMWYKK|#gM|aZs=izg&r||iw%K5~0ozwaJv%Y@* zX{#d7KU)>a=jtlXKbtm%!H?e;kxuE~GR`wG z9DgDY>i0R-&p-3ZKCOZ0A4qTC?Yq=3U7x1bEfr`TRIo!@=;j|%OYdB49dPVnYx$ki zTt3sRmYM10T>D*pQhghDwf*pZC)cMlDyQwdPJc`Br|$(@YNzx0XRQmh()s*z37vm_ z)%;h>_eb9qXO!=j2)CT2^Uo^s{IegOe{P>N#O3e2B4QxgQO`fGZns<2^Ut6EhVxJ6 zk@;u7%_)zQxm=!q=6L{aHhd^uZL>tT{;7}C`KM38A6xl$tK#a^YjxV_y{&C8H~k>3 z0-b-}T0bhZ_9yjR4-H?N=3A(s4X7pOjH~kY%B~J&TG>j)zif?Y*wQv7 zqJgbxld59>sjk2D&sd8Xj`PnS==`(bmS57AwCyMOFMPw|`i0Ivd&~3B!*u?c|BYZ< z<&N`(|C`s}&-yg5tLL9x|AzBV=9BqXeNoNxI9=K=`Io7F{+W0FHy!^J+&I`);&!am>S-x)><+}92S!aS;^|09Eu8E0om8SLgm^^>zM&}QgdLFeLrGGCB_R-F2<@|Gh)tXlI z{PXd@;rx^Nyc7RCPRtXh^87Q;190usP2T^DCWkEuPhCsv?~h+4AA9l82-{QicgH@T ze{Pv`)0x(W*57VFH%R?u?k}mEzWV5B_nP|tU*-Hl(Jtxg`DY3Go8yTy^(<)o=GQ*1 z7PS60lIIU)>HNX|li#uJhxO-Uw6jM!|FnNq#iE{n#+cUMzvBFp@xaB!QnZlA5O*6KGhZN}tK*Op1WEw!8tY|ENoPg|T6Y#ZIG zsO#oe`CKP2G;@7FUGo}$a2BU+pOZSicO_T9xt)Z6zu7j_h4arH%K2xVM;~?-Nq;-X z_|vjp%XBsV9Qzy2KQBoh&HSsJAkX7;X}jcKruzA3-uW;5Rw28ze>6CDE{4{|*BcH(ch zrn*La6J&LK-zJFCw7$Pi75o3HPgb|}tg*+8+r+klli_lf4dL<|5J9U{4SB+@{sFQf)bbaw-u|gOZ5BTGjWN< z3av`A`aKd<`8@smN~4|D-@4BTqBITH*#E1XBk$^5+#3JU8?>i)*iLny9kjKdy_slz z>F5JX9}Blm?4$F)U~GKS`94!^zEp<$sC$dXZj>Cm`gFfh{78fU#_=OP_y_kZ3f|x6 z582<%{G0h^|Bp=5{hw3A|MM7vGJC}|tI)i*3;sWo{NIoA@c%>7|J!Z(yjj)gHNnp- z{vS4bO|a_!hxXNS4V3X4qlpCDhdAH zhWx*6^P0(&8vd_xo=p6oQ2c*qvkpNo2D~cxZ}j4jW{Xw-zu8;(e?jU0Xy>ZQ|Lf)A z|KNV{efmFt-;w!`Jg)mU`#bZ^-+$z5PP@g7i0V0L>@!h${1)m1K8~Q6kNj-~mvpqv zIRAkS`Hc4$h&JFt!WB_751i}d*a(4(H;f9clx&(LnIpA%;n?zYw3o#Q|AZSXI@ zSIy(Xt$E{LeE&C(zK^yq(06Rt6{+dM_kU;18D?2ed3^u(Bl-Q`A&Wl@u64L*QV;t6 z?@RPu-DUJ$-Rqxx5d0#ggXQ;s3w>16bxeN$_d&{6QNI5>s@BmYv;kXR(f5C;z6|)KEMCV-(zR~fdeld7yZip{a?Q3 zRPp~jj)Eg7s!?qh{C^txKl1Q@)-KZw4CbxZ~p!- z*Jc05{oMS&EBSxdJoh;m4F7DcYZ4T|@mCyLS9{pP2uv4Bmz~_?n)cqM{ zsg+~$cVo`c=p74VtF6f9kEj$|a4?rkF3e4Fe>_w24GZ`fI&_;dMyz9Qlh^TSg!rz+$my=Bh6gk5fBG8ZiQnep{>4)s{osBwzFVDUhLxk=x8j$5#*+na#;JUF zoiKj%gz=pRjgJ~TY!H4r^{!s5@2^+Ct2(Mu8$vrx6x=S!<)3Y>Y-0(ic|`o)Rb=4< zB4wStrz_gfuSk;=BgqIS=1Bf`UKnr86Xs=(9Pf;>P7i*3KTh-*@5d*M9XjagywBSE zt1vZ2lDbftmhz%+t?Cjn`*B>*YO#_j`Y-^WD2v_B+fw^US=nU-Eba zeCWx0ub2@NM&_M%pZl%PJ<0E5|3`Ue9QT?s@60pv&h7AcM1JbYd)r~-M$-dd6#d4~ zc-Fm_^nUBRZKVs|V}p*1_08???MB25$`drvbVB_Ra$ldjzq@hVTMoSA+0bn#r}fbx z{vB<1Al#}b^XKi^{4#*^!mfR@O+F9@%;V4%&VUBu}#c~ zn6dXxmQmw=>vKa7$T;X*)4cxNpM{J^{yX#0ZAYJveBOi7@A@^IK$A``iMZAm*&M{uL8}#(}Jo!;Xz5mYmxb5iUV+0k^LHGFZ zc<}ge|9RYEuXv76ml*n4*}LAAb8GJOe(Q5f`FzsF_&8gd$ER0R9*0IW9erf}-@cyt zktMeseSElI?i7F5agP(ndGk22e!}OTg5{;_>FLmqta!PWWm=N}3|dA16FK>^w=RH)GE zB1MXvDORkQRC&aOGG)qWWy_Wo zDXvjovSdlEc=6)dZ~zlwK9g4*#O+XO|4g}Z<+KVFDrkOwejb7{+Dpja06bz3C4(V zV2t1^cf$qM8H#pcuUfTgn!mrlhoB5nwQ5xZ5A-WmtSE2*H{$>w>?26wL%CsqZS)WA zSFc`Ot5KtdRkO7 z#|8euT&Nhp0pZ9tFVGLPiS{3PebU!;c^uZu$cK!$Ha^Q_Q3__7jr=KcPMP5 zUD&H%zrK(L4H{^wa5;2k5SFc-i8F5nl80sa7=1O^g=1M;6UXbbJbUc-hBg*0l^ zNMqqL#sl4)a4;9_7r?+{0e?UYL;n%8(0>|>p}>#!U<39VH*PE>FfdSq@HJB0SFtb- zGZ*krVBoO;2E+>Z0_Fsd0r4>uT%t|14|`3TH1R4b9$*0_<`s_v`y$p0%mua&9?@^~m+gjx zgowV{?RH_aSFc{$fB^%vL4yWq{rmUVdiLz8MMOjhY^bC1!2STA^qpM9L|!`Cq0;C$1jO`6l` z)Y8+_MT%>O!y)cNkHSs_`|4CqqKhg`ic1gu7I8Gt7Cwe$o+@^VgAt`DBJ!6ILIf5vhTw_+J$ce zL-+38g}tYqdP=15d(2H>1ZIwh@IkiE{A2v#f0%z1?>%_u{&Pw?t5ptw0XD+J!-bzC z<^T^kM|{9|q3`ft?!S5b5rZ)HwDzvPn`b2ADH{ghfISHE;0LzB7y6!)eat^_kG^AX z>5X^pyPB#zus^^i@(|btPv|@BWB!19jzQoab02NMk23XKv?lbpW9e4Db#_tUF74+Pdr#|O~jkp{NJP;b& zGvwQ6$iL3e{%SS(*HEk{xz#t{3aXYjpYd^NAR_f8v!C_qegXm{rZn|YuYq$AWOxHeu1=C$@$~__t`JlY}BYhwY^Xm~$_@5SKM` z=5tvyo}ZdEH-3f|Kj(RE;)KyxMvja=*RNmXK4AK*u+*(v+dh1F)V|fLme4cf5_(Qv ztSwu%KwGk8p0?=4cx~ar+1mX1FKF}T&ZK9!=e0Sr9olRXV5wIxpj+wE_@?Q-zaDz1 ztbgCWy%v$Jtc@F2YU|d%q^)1KT%>DOFV$AASfstQY@xPfG2vJguPs0oZrrevo?Vv; zS-$*5ZQ0TV+TulX1s4kzyr9jUOJhRgGKaXHnLsYvwJJq-=ggz90>`Vv>hbdw{6t6Ze6dvyk)K6VjYdinl;O`Rpb}T z3CGeU^K~w0JTL}~0X{l(=pYy7i8l-}F~c_|9^9!NJg`IKbkClx+P=Np2+J!12gU`t=G1F&^`UPrx_9gP0@aoA6H>*L^wJr#T%+{&gieX*Zn-?G^Tu5_f3_3D1H3 z36hUjv>iJ(Ydd#r(YC%q9FTu(-n?4)$7Svz*@puhn8hYsx#V*$Su{y}E4C#9!jviFH@skXzybUa zez276WBeD$bqf7w`}B94QZ!dw8E>Qs`zKBu6FdOJtH+NR{>VP*q;Y^xVqU@zfB|y> z_R)Wi39v8XB5{B*(0zd7k&Fc@|Ac4XsSKBvK?3{W0Jh-=@JBHxQW7=H&m&HX4a5QB z5(Hx*IM|`bB*djHG#9W&t)rNPH5=;!WFgi_#Xg*`c=2MTrau?Ek^7GnV{wvj94B9Z zKVUAP|FDhNAUL4dg#I6(xCB2C@qpqI)+lhW5k5fk5cZe9^rEn@&Oz1x`$YB}efrJg z+No3Nx^4IX`6G`7d?AJUuVMfPh}E#K&IR})`j2^l{=)|_53x^B`;Yw_&;R1&1GMH` zIenTi&=|Z<_Q3&C%mWb ze}wpxQ{Nc_#fN?D2kei+4>1pj2XKHn06)MQjWNI)$^M5}gtd{^qzxNZh+?(Wwn2S6QL`)>xSRY^;BGzgV3-*ZpisApX9>6~M#~eie z;s3O+5;5ma`#_8X&FL%Lf3YUeT;TCfOG_4OH0*-|@Q-+iJ(7-Lldz3ADEd$H53zsr z=x5JWs^op|#T+25Q@d6m&7pI&?qp#OiuH*4&SQ`MW1oUG32|_rT#tqS(;gB2FXkW3 zf0}byg9r8BSE*7(ulqjLHs@>Ct{I4Rk@}Yf|3kkq7sYxY)+4NmGzVlH7W0o{An%Vi z5cjnI%j({}^P)0kO8axW_mUPXRz3W<+2q20Qr4VcPsgPlD-JF{XF`p^>T-{;x^Z!IopY~DYLK;MLj9~wn`)4wbDC<&GXu568G+h< zM`La8^hVn5X$`du^Nj_LJsxD(ch|Pk-j*27t!$}5 zfaUZ{Ewl{5MX;7mW0E$nneYp60lxqjd&n=gksqiSo_n&ot6Bx$K(k%Gj(@t&#t%1H zv=26hYMcVYd+P|x8p#E504^BA2^tId1uz^V-*gg&B=S%8P56Y0Vcg?Yb3CuFSFKoJ z`hwsqA8)Z}pS)rf_AhU?Xde=m_cw&Uoe9!z7g zY_fl@WBAY7)&heV3*rLy;hV26q&Y$yz#mi$ z-~s-yV~WlL{Bqc1RXon?tCaKgcg}8h4)(v;5hm;d!v{$&rie&%umD!j04XLaG?5Phv0|0(RgQlwJS`^B>SHy*ae1bgyG{Y zR)OOq@(W`u;1A>%XIJRHfH}cDh!`RL5p#n5QE(uAVZ+#ZSsjDRj%L4mVNmTw-|P<8 zzTOqCAr&}w5)R@67_Kk|_yggee^AHcDq+AlTq4`(zrg{`M__;;HepV}4-g|U7cd|9 zAub8~x*txbSitrVz0l0{(}A|yje~8qANRM_vWSDPX&it9_JIQeUjzmtHctg^6;DY;)v4Agp@`~=0 zAJI6#4=@Js1K5WTa7^Gaz?z9M;F!qqNacU~l*aphA^X7a1K9@$-|ZnkkYfQ1Uyyxp z@EOGi@PN61xFF&*Vc?j=V}LaZV*vXHpT|55)Wq5(=OOH4?9u-y*oP0O_Rao3qHDF$x02gwHgC2AGRv9~gKXV4pD{CSe}%J_Y>42N4I^{}F$3>O1qW>$$*v><8?R z-~jx?55WQE0PJJ@F$S=YIE22#zVJcfA2Ato5cA(TyNTEjY@JkJQ{z6{<+NIb!v3e0 zg`B&Y)K1Jr_yCUqY(roh0vw3RSfAj7urJnRxi`W1bN}K0`{Ejjm}4Gyw#_NV;o^GB z74ARc5at5>5ZntNkTFrWudhkW0WiQe_aE~Qw&xA0ea=6Zy%*a@3Jk9=2|4%KHhUK0 z5#kW~j`8RI>vMqC1L7ZJj~D>|Kes|(kFXA5{?k4%E4H`)zPq)}I2u+j9=L+m#mk$m zSy&U%Z{8n>I7qP=KEVFR_IZDdvEMtran_Iyl^2ySuUgPd$>rVG0f*$JT7*K1`8%tVTd39m) zE3l1q5dEgIEb4F8>X>@2nC?{<`O&jevBH1-vuFu=Hi2NT+#;Z)y>{gyfkAaj*egF& z$iGgNB7t~jD}Il5J9MVc1{R{vQx^U~e4f4<5rK1okn;MqH(ok)?h!@*HgdPw}t;*25|1*jeL;(q6&2-XMg#kTeEwei-q=@2KQr$u!tkr#WW;aJ7vlVKAWa9%vDkI`wb?#jhqdVuGdO1aQ*8&0 zdOFG{unCvnLn?j}m46qN>&3QOB5=gF$^HD4^}VHC9#BN_g#>z@4AUFMrg}27zvYD)ViAwKk<1IHX+zM}&@p@u5y` zugufCv*D=r_MW4_0ZDk(69>1o==*$O&F>pw`29C}W$6fLg`+?{9fR86W`>_=; zU`#VlxN!gj^YPi|f7m7tyhwV^ims1s@oT=wqHZ&=#;P zoA7Yh^3`v;$3@y@9JhE}B);4*;NGP_7Krg@J#f(QN*w*$oBAvBlpBZYA3pTic+zt* zA8eO##CDZ@NPOJBx76=;b{vCyTtCA8k$Kz`hr1nzjr3YF4z|lUZpnGc_}uk!&N(Ai zc=##!I9Xy^is2s_ZX9TV{R4cEUMS;WyNn~Ywd6zclCyPpX-KE+IQlL4G!!`C9|6jI z;aWNthmG`77zf*B98Sv)G2V=i>v~Hw6Z?4h2mQ%Nyg0oje{USP++08f=bSzc#=&+O zM@S1D2jfGH++M5R%#LGV$p=%x1#E{XIM9w83;i49B_A~62irApMCdpeAL{1zy7gXm z9MA_2_)4r(|NV7f;unt#T^oFaNV|-~8Ls1Cd|cOCns6p391a_B;FakmuD4#&w$18; z(~Wt{b{R)VJBdT$(_8cY?%b0T4xJBh5dFG)zPQK4$7kL3fnlc;ml$!3?J^E0kBj8R zI|BVq#3$ZyU|gb8-8fLgjYXWt*E0OnG%g`+B_EO(-J19JTgl#Wh2LI6}fCAF>=Za(k_M)H@Df ziB)i*9XA$F9Bh|yID>Q?&b!1B66zg?n70Srd~knsES@;nF5?JkFZqzXa9wX{WN=P6 z@NZq~uK;EL0sEeOIE;DAb`2bzB_EO(Z}z!-Ro9$w=>7pL32AOWbz?wnPd-AVT?0q3 zz`RXJar41-a`X>rmvMwV zDsf1BT-RF~*}*#w)Coy(`)RJ@BJCPDY|=j@K5zE9JS5CJ4){mJF*gp>abq$0hqTK$ zoS~8riI44hOGCPO#{vIXWm@;qPLAUu?HV{Zj!Asp>~nc+cuqLn^EO(E>#*<1hkM>S zrCr7mB5^SrV4l*?({wIu=hHA=0jaqnn&B5+B#~mfp%Cu8;OvYUQ|| zDRJQ1%>`O>*hnwUSoft}1BYGCm%EJXms)rq7ub%L{=vB1SWJ9KyNtuxQR0yJfIPR? ztsLUm4xcB3U{}QTH+4T{9B7Njh4hk`54OuVLO8BVe7Rx3z1WcKeEiY*OXK__TEXGw z0yP~r(leg-*)HR7c98xdc|ncbUYBxso`XL64+(I2^5MqfIWBD1z`^V7UB>lGIqZX; z`)zL$k4vUp=W-es(hD-i#k}5HB_EO(^ewm7tybB`1^R3^L9Y9_c5?v@6Cctp^AXZi z;<(H8Huf>^IAA+d!2#=TES@;nF5_^@_01{qp>A$3zs}xqi1{M-W6TfQ(y@5rV7rVX zq>G%ll9${t;9lh8*>ODipGaeW8g1Gas(g^1dw(kJ8aU*+1LM0B4rdO0oUXAX82yJt z%lU%6nHvLIb=XKRnf)|G+GQNhPLdCa4>fXoMcT6Saj`@VBaS5~IM9w8i^E2G?l>mx zGL8_&A@Svg0rw(vm@n7M`MSUbIEYSnulvx)i~WQ2Yz7}D98URM%e?3{y}!qH&&~(5 z*)J2$yW^Of3uu`9RN6K8kn@CjL5T zwf4>j=54}To;ciCJaMpHgOA6gpWbEsh_z(LQR1UM#=K2<$L$~BLgj<>-1Ao2HE{4c zcbEHQKWlaza|1(@jdA&pCl2tTzZ^Ew3pV^z+BIat^sy+Dl@jD50G+GRdMy2^F`Zuf=XtjNa4iIk@-z=3(2X&M*ykI?f$VS5i1 zai1qky9SOn631Phf5iG`=VQQ^-v}Jy+~KU759R_`#=g-m{6xv31|K2PF5_?p%J?Di zab0ietv|E#u{hoRcc5q)PkFs%KE5i{JFG#{2)BPoy9N%4i}880&*jdCvh$JDw}SZl z3&tkX6vwz;r}~razrXmL!H3htM~K{~GcR1vTk8BFJ0IKUuNQxR5&JP^zMu`}V`161 z!wNf|b)RcXy9OV8PABnsv(M$R4`=7&?d1;}{*h_I!B~PDoej$f+hE{un$PK)NdLIY z=a+~hc8|Y{;qUyny0$80!${v%W15bM6e z2ijpQkCmEiKhdtad%l?CM@tz$?s9*cP(3@2!RJni=UWj!&bayD{s7Cg4i}N4wgnuaUl*a^ZeHnkn9{sh;`1>u}WgJfFx6ZqKj=EJpJB})ukGPEWMZp34 zjHO}0%l2`%y4ZMJ*sg(t`H(5<=JxV!l^w_WHLHZ5Vk|P>&DlSS#&okUnDLf-T%FhWj`Dl$1*AtZen0rjnD~FBr+kA!VwZJ`H0RQ8y_tQ! zEEupstaHE;tvt`UF`!ot8|k^%Icb-1I0s8UBtCCEaJjQkc0MY6GFOIQmOI zBro3VbNQtr+5H3ez=w$Irg8D)L%|{KG7jexk`IZG?RiW69`KF>{t@lvxdCHGw+%kn zu7RVw#3Au{v(M$R`Lg2}+qIY5KTPAI@KHTN;wXI=6gb zPq%+4IACApgY*plV7rXN8729U_`KQYa=#qbxmUZzh;<*F1StD;HwM&p*htS<_t~z2 zgV)=;jHe+FW}h$ii$QMxP|n9--HpXzBR#i&nAh7F$%o_xb#r@#{GNlKUM_C##s%&< zbJ(Av95{r3q~-i{(*Kp`A~33y9SPta$F?7 z+%Vu?$S>Lb1LfdCtS<^5D94NagR*Wuq+J6C`-j9QD(3R$ypH)|X5Z z4tIMF8|k^%7c&kX7g_GEmvb(1g6DY@d|cOC>Nh+)4$MhjUlJ5QHTwtYx&1@h zWgJe4D@3N=>~nd@(>dXAuP>P<99$1rDesP_CO(|}TzR+qmtqkc zWNG%M>6QeW?zY(qMMwoSX}5+!rbL-ZM;%jTMpV>dZxH{MGom=7(m0}xg60f5Goom@ zfA2)xbMMQDcoC7^__LXB;@4eq>cxHco_pVYOGIYH>*JBQj~ZPnd0O(ma(Y>s$BjQZ zUh|_Cc&?3hadMU)Vwdq4JwbSsy^lI4&Ew+NZ>94~Grup?$=UoOcFAK}erH@* zD|DXC7(d2N+)c-i?wR5IaB?<&BzTO@mvLRnL-tudd44(```xg$u1~vX_wztK=7*EB z@k8t~9;2sAyOiWHv>?snI&XeyE;b&?@x#ekeu!Q2n3mre7y2DKj~p{i^HS&(p z)6+cMeTn8r&5H;1FhBb4UlQx&(FKwp!jE-B=g}2u9+2hyGJQ+Q&ppI0<1sD2GcFSd$(XNGy8U79)Ly1Q)cB6i7R;=>XTiA%P1E1k|AOx}o!QMxO588Pj>c>CG?6{BUwQu1|*8gc!*u{nD{5*QL+e^TWwmeu!Q2n7K)K2tWEAI?tY(#lyyr+8GuPYZGWoj2~iGdAwipRQQGLvwZ68 zFpp^b@bVOOj7x%t*i{~%mUsw1+6$d0=Y@Gh_YFDu;dI(KCU(hV;va;E@C(^z`B27v z!^?klhUP~%F|PaZaB`L(VwXH-IF1QF+6$fMADfQHSGL(opF7mf4d;iG({&;3DvwV| zeh9yieU{H=@aX;SA-!*Cdh;#n`0;RZx-Og%yUK(8R^mc?q4VUTbUa?T`LCnTZ=>$lVqb7|F`oPK^gljye-%v0gVx}o#b!ZeRZj$5kvQS-)A*!Sb%736F)rWc8p{A-}TnX#sOZn>Q3$;eotva?6O@Zu9f^KnWv-8G>`AMc8$L8fbpaI z2Fp`eH!iKZle<%Sh+XCJH^M{sp>EzW_4+iA>u$SM^P`)SA5Kp5V?ykb#|+~k=R6L$ zHk9%FWBA5XH9wl?SUi&X;p8+wXqP-D{#xQ8{EB!?o{^5n^v8C!@ncax57XDGJ2@Rc zXjgg2I7)t2FB?t0Da~W`t$!coX*9p&a@HGLt#C65{5L4G0oEFW5&j>mgH@hKfYYB}otxy`kM`?h9^{95 zdB=1{o;Dvi#`41($6()&hm+I%pk3uL7V{v#JPx=vosl1%KRu++wR<`1QzxhWwoSXP zF%ir5i4qTqOA(Lx8P8!h9r&^{?R&wsoafq3PI=HSc~noA{E)bW z?6Z6}gU9S?`$pp_+PRzH!Fm|$oScrQYM1$On#4omV)e4onvCa#b1xXz^=U2V`IwW_ zd9E#XH6ABPJR~mJ)~$3+#`EOezws8GUz&+LMNQ_1le2ixF5_{i#6$R*#cVXWG@T#* z{L=I8=V0IOFaG?}7Q2i``&fyG@XNMtrSq3(@v!-&mYC>+eDrkm;QLruJV{K?ILk8i`l69 zQ1*M!&}VT>d4JzvvA~rC587p(%KKHx4>j_RiPd2q;8II`-e6q%dGNiij0bI2g&%(p zo#)>h=7IdEeX;m+kNx%;+jFOS~YieD8!`kK08T##eqO^^kZ z%7XhJs^9;$qw4$ad&L-jzw3RU9y9sv8pIzyXm7=kc%&l&fPHTFOMBH^<@ z@o@bOim3zVke;=x&uQG$yIAxrS3F$5NO3tmmw0wtiXXV~eTvKIx%kQIxcN1rxcBv! zD=wpFXrF_}Kl$!U#jjT?9@szi zuLC$7=hMb-x%$MrWBEmjSIs$Fv9BwnXZhOZ_-jUv5j{)B+pnodc;1?MbVdd8bBJoAFZE0#J~si1x|j>!9%{c+^L z4?KPc^x;tK#u3HwrRZngIAZeKHHa~e5PNEZd5Ljk?smIKycRUBSTYh}j3Wykn=9p} zDU?qbM@)XXVvHkJUPjL)%E8T1iZPCmn`sH>CB_kxA5n~P#L7)m$S;f|CjZLu_s-o_ z;&a6KA0C)9Kzv?1e$@#ZiTrWnd;jxC?}T-XBOm|zHW4;9VZYX{K5hJytFDd4ph{(t zVvHkJZkj@VVH|1iOg&4+cRc3GNGHaTfBnXrMSrm#j3fIzXP(F}SB!DQ%FF0kp&T%d zkPpTYV$%}EC$ryT9O?b+S;A-K_(oTL`C!z;IMTj;iwGz5M89T@BY1CmEZZ3;#yCQp z(38nW$B}#E=as0lF5?K_r|yp<=Qj?xog5UqaYQjMx^vz*V)ENHh%t^3dunpWk*|OG zaf$DO1~JAF;xc+Lj-31T`$f-k#TZAd+%$#qd+q8=Ji9H$7)J)-gK@;P5vn3(W3G9 z|L)2NV;njBzqczru3;QGXtOKth%GKrjB&)ui}_$2S@Y2SqG!2cj3ZWFM$d}z|2+D7 z(Q|=vz&LW?>;FD0&YArddiJs+(F^*WdX$tqZOFX+R#TZ8h;e&C+ z#t|zo)`R<+ect#?iO+G0F^*Vy89k?s|Mt>X%Y3_Ny!-NNqj3%6$kw;KRCpHa!8lUs zym>U1T%s7`h?N)XiSBD2es;vct=7lK1V{HZT3$xa3eWBZiZPA^_+<85j3XaAWk&d{ zRE%-N%8U769KpUNv1CUv#t|#;)04@E;|TXXL5)GjkyWk^_cOsbg6ArmJ^Y({u%C&8 z(LwCW8^pYZbEv;v_N>FkO}?YJaq;&Rm(fE%xvvW9B>g;k@mX)oyMKe9+%LuV6Z@r* z12Oz0<~5x2{51K_I5GSrE~7{NyeGWAEaNBlWAXjOek|lb3_po^4d*;RO};Zu3_pp> z=utms*3VDw_u~7B{a(m{7=9A-8qRrsntW%R7=99$(L+DE?+oe;nzysA0{4@FpV&j@ z%Af4D+MQB|D<4Z0x`L3Oceg+6$*)yBcG*uAyXty`^sIi{FUC};QfUwm*Ed(pGMqzt z)~?>tliyA;)*XI*;*g%BKmCL!KQd1FtDkYAVwT|?(sS{XZtl`!rDBZtTI_+49*p-U z)=`Y{-pUPy^dO&haAMp)2sJix|NZA!N)x%g&0PV@1!dkVD)H$j>3{1q2;9 zh>=6Y@s;>zh!b+iqFcKFA@H?r_vzCI7Dn(!g??cnceLaV;r(_(-h)^95VTlabk=^#HJ<82jh^*uT+e2 z$jVJqh)+D1v0X5i#WCt8;=>0dtgEV-v% zJ4TFrBBpMfL-9gB9q@t8MNg$cjC>+4qX+q9cDGZEe6n)W6yk$?GWn5lV&oICX$kv< zd@}i!ijhxNZkj@Tp31cmKI7xwd9b`L`NTc({v7#a(O5f1ypC(_k4gQ8J4aa7+AOE~ zU|#;;T00I=C@(oaaSudDuS-60Z^J)FKHdDD@9rCWv386Y`9vIFi+_gv#Q5~h$G$3h zDh*=f6LA?m$S1SAonnknR&JU?d@w$l{Kz;l#wTLal0QC~{7S8h@yW_fQ;5$~JwDaG z9De2@_in-Y4$rW-M)iEC}e~Anbi{)Em;g5QLf57& z?2GV~`;vmyK)yP8`pP{=H*{aQ_vw21D*GdD=)Q7))%EiA@OZDC4cXUW=OFiR4f=X) z_I$!USvPe5xHs&;{>k2^8@hkob9PYwxQA@NfBtwMpSEs_i|n*A7y`Lza6nZGj!n~MmrM6SK^?2Ml#PFAx*KiK;guf== zQ4D{r+%$#s&|mInjv52|>)o$#zjEJSV_^vX+HZzJJmIfNt>0%1STk_nbm$EF+qWvJ z=)E@gLf_DR{l#AKo%Qi|YAk)f z=JvGLd?tp!#ASG5>^1q0V)$$2rYRIR`rDMfM%Kw+$>*D0pk}v^_v%LbZtnhX`si0y zfBjMY{#}LGm5&h{%W(Vtahn|)Chz`cRCF%5^IFAa^mN8=Y@EFOc!~ebiWh$2mx{~i z`Nl`L88qG{_~Uz5CcjcKzSm&o#d`3)2F9dP=_tnc_^iBG5A&fbdn2Mo zGXIZXzI^eD#)|Wz`@XF4&DDV(qGf`C#%ZwJ!3(%8T_NA1n^;`{fez==p?!4{D@-YgOwNS!T4bED-|Ojth`tc^12Vm-(Qi$g~-^1;fB^)Mg0vNtGd3_Lz~`7ry3^!Tu9 zwf*fXG4g@fSmx)0$+sHB$OqywdXNwQ{Bhoo+^iV+VCAMMFCR>PWSkiJKx|sVejy)B zex+jMgO!`65Fg}&#i64Z`C#S6dYBJQ*|Qckg82~oT-nQq>F1`$hg(j5f&T3)G4g@f zScc<<`NHH|ijfaiZkj@ILq0sV%SO6>AVxkAo0c#ij1MM1q8RyL<)$gb2l-&~D-|Oj zth`tc^1G9#?@48X{Mwl4+Kx{0-@kc(G zd`mI%!OBfjDE`QY;TPQ}^ViLakq=g0tOxmE@*|3o4_02R2l-&~D-|Ojth`tc^1d>}5P2l?<1(?8RE zAVxkAm(hcKF!>S1$OkJoO`-fmKA8MU#mEOMFV=&6usC!SBOk21SP%1|w?6sc%s|fq z_gc@1TgRfk*l>Tbwfpbdef8`yuaNQCJ)aPad>}TK`T1b-tp+jjfw+vGk%gVcpJsQ{ z_2kRW+&IFk=6tTP`A>hTc}rcPc3i&p7EgX!@v1quE2a*d!+P#$kVB=iTJZxnenW8? zJ=^X4!^V5(@!eFjiihhPk6XEE3h`OHdb9DvUUb-2BA^z7lqVMOutlGi9MqX+%h;<-{W`mdFn zrcj*Ge=To2iqU_qyjTzWZ!_ol@KMVy&VOFCxDtIfhyFYJ=7pFERQrv1tkOLI3^u|C}g#rWK?ATDfTo z@maq14$tmt#pu6-@In7I`Ps(Mi%yBSq5uBsh|@LxWEA3q{`>QPJ6GhlR*e2@<Sod`mIeXS-w{8n82#7Ei}|4ce)fkO-5pDA ztr-2+%8T`&|L(p2i0BzojQ(roW%SG$-)M(jM9(gY(SNPHjGjHbIE*Mp{~aVg=)Wev zQZf3kl^63t|Fyhz?+BDWg8oZvTEhK8`|sDn-{mWuf1M%r-=UYJ`|n}@y{C+yl?pNX zFL4>3=)Wf4QjGp<<)$gG|6cjVm9ieVx$)qa-x*=_-=hzWv1tj%4gJ^Trxm0BTDfTo z@xlB{4!Hj%M*k%?Enzl3-N1KG5W8So2C#S^xyCQctZ4Str-2+%8T`&|IXc! z@4FdNjQ(roW%SG$|NdKd6h6BsM*p?)GJ5v#;xM8Z{dbV~p#PfuO2z2ER$j~p{nzrg zqZs|y%8T`||Mngz(SJkFGo2as-*zqCe~;ex$wSD582y)+*KiKy75cBqw-lrQTDfTo z>51mw&+_xxn;SFFeI(+B{`;$YzOOu~DWnJe_gU>VB0sGd{nyH=CF~daFFBz95~KeT zo0hO1^xrpMxBFeO?5tw+Un@6FA-~Xn&;IwT?u;e3R*e2@<;8l?f46?#i-gaRV)S1t zFQaG9_#N-rQuOSi82#7E%jns|i^GUw^xr|^gZ^vsD;1;vT6r-a^k2){j$-s*D=*f= z{@cv?{95PVQ2+hYW%m4Pk97atp|_i?e<~GX^j~6Q8O~4iUz2YsM*p>P(-g{2^xv;m zFAzRAD@Omd@?t&czw`d*e9<$l82#7E%jmhov%6X``tKlo(0>=cW>4WWs~G*)%8U7+ z|NeH{6GYF}iqU_qyjTzV?>m1qSM&@iM*p?)GJ58WpZV2;M9(gY(SNPHjGjHbIE*Mp z{~aVg=)WevQZf3kl^63t|FyjBC`SLa@?t&gzrFS8zYkxo{kM8xy8qsI!<4LlDivb% zUt(jK-+xWM)gVUyB`%`}{rBB-?~!@)X2s~gR&JU?;{f{a5vM(<^Di;_FR^I}`-S<} z?5uD;1;vTDfTowHNxY z<*oaaTlxt4FR^I}^U?nMt@U3&;~h@(fB8ZE{MS9!s<`jf-{bQj#`o=s{d36Qr}A%| zhMmEg&&7LP!glg``*F<+&s&)IqguoX{>@fD^z`uW)^+o5>bFmi_G`-Ct%J7T)HBoV zcjVmjWqhbqh>@Sfu)!lQKTW_?A*K$TLwb;Bc*j`p{&0V^{LK3D|Dq+#)uCsl zeckPQH|ShS3}1=Ma714;`Ho`vYUQS>$k%SpcbW5jT^!xFqhGG6qD7SJo1h3Nd^oHir4WntW%R z7`_sh(Zd|>=FBPizFH1XZ=Lpa>1P|-Uy0!>aT$(iSCj83hObs`nu^-Bmh=6}$k(~y zzt&-Yo&0rr?0xs?e=E-pDivb*N^A`C+tuVd^UC@FRWzj}+i$$}WZ67w3)A&&6X zCpSv_y5e;w==@5Ib|tpf&d*_!?~D^;Y$7hBhhtMOCx;JTx@hrau)kJxJv{Yf zy1#Dvg=gsBvJk^p;xZgD4x4;OF?_Xh(-a!d>1+4F@Y+A$SIgn4Kc#)0n5yaDX%NF# zVq=)!t|s3ZCx)-YW%SV3TF!k=zOS|(p3WGDzw^7Tbbcjq4#t{8EgN0 z=PlEFI%4=rT!tgs)#N*h;j5LKrlNMOF!Yyh*XBC6tIe;|vuR(izRKRUNDN;)EOKv<4&T8-2c9EN{yy#Nb&EFD^)NAfB`(7e{ng|s9l>+g|FMBeZAgW4->;zVq=)!t|s3ZCx)-YW%Ovf z=8VH}yM||2_Sg1yXg?_iQy}88IEXIlkX^ouU2lFirTgJ6!<#-Li-*FKSw@v zk2|%$5=Xvz#QmAH%^`dZ8RoUW{|L+7S_-4NHq-JG!r>!y86JZ~C3E6Z0u;xR_Mrc3kN z)#TTIJ=|MoUv2F_b=uRz*V=c(^J{*)dTYcBo*uq7bDpi0_H|j>*BgJr_cyxl;|WF% z6I*WP=dj7U?=OiSj7`L4^yt`>^Zoy0&slnQcr|S8I(bgo*Nb1jm9B@0;VW?&ju?kc zzM~kvTDfTo&0X}hxz4^W)n_oX8T0GdiM#3iN(^6#jbVPfn!J17Bs$?MaTz`IwR>i` zzvj2A-K$TYpKjOvZrEC%Hxa{E;xZi3t|s463}3C>G!?aL_w04`)yCnW1!-Te^VY+} z@RisY=C`ZKyL&p(315lJ=wZ7y7w7vLUJYA+9X&4X>pkA{VPg17T!tgs)#N*h;j5LK zrlNLjp0%#NS`Lq1oc8rX@A)t>d?hx9`R!`*opECLN?b+{+qJfEU48Z1_4Kr_{_|U6 z_)1)cBihyE*Z=u&ZJpcI`s?V5w6DG|#PF5aKZkM;zM9XIXI{3ZKh^xfd*y83GUa{if54_})(>%aVVHD9MQpE)EN)&2UHZrARq>)NiC z!_yh#(e9lw{XP#dd?ofHkvE^1d}o{(>jdI5dbmz#=B#z)dzGOMtM@Qd7o~G}-d5Y| zx}F%m5|`nKxzpr3is7r3o2F3A=xeX2UFYsq=<9UGy8f!~?5N)jCWf!X#xQ^VXY!qK zV)#m2MvwY>N(D{`ZIZRwOhfTgSPK>dMxQrf-!`+-UV*Fjh(3E5S zb^fvGcKymWTj}SG#PF543`g`=lkX^ouU2lFLj9h;*3J$0*RsCOX879s-9!33lo-Ae z8^iqh)#N+l#PF54j2`;h%(>6W_tl;`OfE{d>kBvkwfkX=A3+Ay>WP9+Sf;pTdMt)7`_r4!~AwN`OY{od?hZUhwWO+SvN(#mRL7U9-sF0 zi60(wJ9!YpS7KhnIn*9#SCj83hObs`nnHT$Yd7b)S%I(iec5K(*YCG>)xUuxhOflN zFyB{`?~D_}SK=~y)Ymr@j7=rlb?WtLU$49ER_(9E@Rhg>N3^TSyJz~M6TT9c(WAZ= z_16-<4rP3{I(*})+Fyy`D{&c)@YUoyis7r3o2H^1Zk`iv*Z8{-;T19B6@CBijC8wB ze{5HsUy0!>u`$fgVUzES6T?^HGJ4pqwMFai>(ralzOKIY?{#bN3^TScND`{D>qF=?OMyZf93b@LU(n{;n|EF-VmQRb#vxd+^dH( z*zR+NPDr<_McWYC)qXP+Y7exlNv+>!t9ahjEO_1&_SNpyrD9)|wX?b^*5oAQ0NcAd@`o0<pjNvU~OEjQ-mB(?dGv5W`pEG91xgO}?WTzFN6y3iW&X+RJ&~l<%vJP4hE8YuhOflNFuz?*-u*4Q=!CDtW%SV3TF!ICvc68uPmfKErCf^w+hOfkB^k};le*Z4iUu|rf z&G2>hw0(8mL=0bv%Wy=yn!MW^OLW3l;xc;ZYj>S}wf?#$<9jf3FBsRgKQVkIF2fPN zntVqwe6@1ZRFuQDobO8@hs&%b)?|DqZTD}yMcb7az7iY5{2VrU_Z1KA?vJ&^z9l|?oxeQGS3lx0M!Tj<^V`+r*MB`+D_Hx7eYO5N z`!w;jmoqlyx2w%LlP5nt+O?N6HkI*pcB4wA`p`+bKj9WG>8@J4V729*Ay+QT2WIyft^@<<|6+~$*6-InppjRIfNJ{k5HTaZ_lAx&Ud3j zIW+&?|I49d{^0lj-{#O4!{4t@=1}7>_x^DgtH}@jB$~?>xG~B7s|>A-e;@kC_>6e^ zN8_+#mn~d&!5ObyeC7qs%E&(cwUT&a$ibF>RO>&Fc(kvH&;GSR@atp*$2hy(Rixgk zvkIHl;|J3}|Ini%UgWT)1Ales?sf97$>$ty+v3W@-#fLA|B$_J$p^`p84vBbxOH;nPK_1jqtG{q;a{hgHk%!uqc(h+FJW9r8&8_h| zkNOdhYfnD#PH;fO*5vsGc;H|>-u}zm$Cf|mf9_Bow99sB#5~9^t{wh4^>=9=-+IPP z5f9j|$#)9L1De3);K#$s9jrXWF5^*so$wHT+19PJdSaT#sqg;b!Qg=y z^yFD_GC$BRPEPZbcFCiCr0@`aW-%KLozkDD=p&b%I-~ zr_S7>@esS@Q9UN+L4J|4oFCH}JQn`_es{wz{@3Jh&&Z2hepx$o?4)1ZVf}@6*>7Jj z@eqDF6rxM><@?u^Lp+{+$lD?wz}@M14kHi9fy*!7^@sX)hinw>(G!j9Vwdq~&zE=z zKh(=Rrmsl1%dSuEq5ZZipL4RmFdm!#;@+|AetW$2TiTU)v`-Qq!Y_{lt_`)*Jf86n zhiJQW1LOK>GkcCb^B?Zic2T=*mm{QIgkKSl**B+oxH>hoL)0$v`5^lX;}JinYJHq} zS{1w6E;6r>UlEU~tI|B)dGIqo4<4{x3-sG}EO^5aS04P{{m7%8m~X4Ely(t*MLc@% zJ-csQhwVMCdvIUo<@`XujmGusPW&8ss9m;;%=_e5#AEh7X&$4a+eLW_F0-A&<+_JpQ!#PonV@`7tZ^z~lja;IhSc-ZTEk&Cj;+ zly=GEFySHmig?Wbn>3Ff{mGkey*!0|#^a%@Z#?`Lf3o?uO1tE7obV8SMLb4Vrg@A# z`0E4VKmK>+St-X4>bv)VD_=SG;KjFTe$cM)XtVzaKh_JKs~P!m!+VBq1_!iDZAJY3 zs;qu{<9nv|CJ(hs9_%l|FJzzPYu=ZR$HMWop9T-u?p@H&1NGvzY1PjdUtm0Fm+dkZ zw+s29UfwbPk~EK{FFbKS@W8R=^ESUQH&A}!@k2+x>4gtkzZJV2$EvRp9wqzB#8jHc zT(92_-EKT!pIokS{dUCfw_;a$yh`#^_@QpzG5UctkNvl>ep~%QKM&{vm$l!1&-fu* zc>R`knIESL58;=`0oR5;nC9X4+vcR@2kbK*$$s0WUGk81VO97Q@u+?%&BO1v&GY+t zP+zj&wrQ6}r2GL3orL$9kuvdB6(n;(tx@gS?DO^ge$N;HnSl&kclUC0BvVlFN}DyP_e1MNzlR$nXqR`}&{z_r>@>3ID9wf}J!?bc*m z$L|&k)NIwAoUTvRF8hnjvy2OB+Q?d1C7Sc!+k1$Fa| z_UqC--ul~rx&<82E?qBAsfX?2h%4~Dc_#2_m#cnHlD&hX5IM(&%eQ@yO;pB82qg{@t)e~i0FFB4) zUy|nW*z>Bs_{#AIm_;&Xjk&1dYbSMe)J)9Zr+mSvHY61YQL>5>+dgYm&Sn`H7>g54|g&@ z)GqUsyyP6R&+;{QqHN~BUB=^ZiHF36_Cn{$Z>D+7 zf3e+Hb`$GD#^cbhoznR1WlOZ*R%w^HN}8@Tk63;vwa<7dlUUKh2}EHE_>M*pd+&-r_a@dNb`mv?^l*s<+yS*Y_%RqV3gwpU0zgdgpN z&a)Zg*w-I@tBvc4cCof;)t#K?huUR+yg_&fzmR>FuX$H`99yu(4F@4NqH$f;L1-7_ zfZDCPle?Ym(xzRu%lXnS5*O6SJJ!4{&Evj1&Nu`dkRRQ|{l0N&)t#J;A7Yp7Qaw|6 z2){fIxVGlfG>_B&r>j=m_|fy` z7ufgX;p8+wsKr zzak#BW79l7{;nJKd0{tkU+Krg$=N(dyNt(0k{`mah(~oCvWBi_xjcOb$oveN7Lis=f`26-Kz22kDj3Up?29W zZ?8L@dy8furF7uRmTXH<@{b!oTzu&Q!&M)1-eZ!XDz4Wm2pLm(& z2knx_1riV8M;}7x-p|rJM(_Qijvqa_zfI0BKimGy#_I9cSbw2i^0-j)v}Av2KA7fl z_NoJ;^}178X?i^13-jaR$DVAAA3V3N^PJe#Je7H+E&M`pV0rVWX&$HFa+HoAP47O6 zb-?9^f2cHe+h?xjX>z{3RN^80ST}U8U6LNhAd5W3G3q_XWh~+={m(zDVwdq~zghC5 zA^c+Q;h)XPFc0L%MBsB|%yaItJ*T5x=Bca) z$uG`{;0K)W=(^(m|ymsZ`$9m0NF6}!ySs`OX#L*2Zi zeQufu_K)Yd-t+F~s3+#qsyjIy$7q*4+U!5Vk99-mUIq{B?aqGN_2L2hemtC<*?7I#g_}|UR4=1PdT$^^uV^Z>?WWSwRk>-It(ZK=v(erq?U;FaI z$yuI?UA9YgiSQ79sGE1x&P(&ae&yhSxveJ83xRnsKb)M-FSJV@ZRxLV;m5k6b2W23 zZE1cqJs!#YaDCVEBQcIuIqykaLiSnSJS!a!T+{qW@L;{vcuKpHA62f`OO9g`Zw&K5 zesmM_t>0hVWgFL%>-96Fzm%MB+d1KoGWz<$i&5se?-^B~58`QhYr{Aefd z8%~#TtYp7!XS56UO4j`FcqH?~$yt7gUB;t&vG6E)-_XtIx7f#+`O)>B7h0PjGOfCk zvw2_al1H07gdbxWI!|P*Kd{Fz^P}hSfDYr*syjK$j|7h@=RGM8*=Kp}{B(X`-v`YP z?|zPTm>*6~^F!^j-*P=xl1KZZG!MjF^TT^Smdp<)XZev>kF}S|cv^D4ZD!7MHor8z zcvzbtGX3jywJY(c3XgWl^;k2b-@=OKM?3quLy`yWGEb{q2MHhO%sVDB*6Wbh{8-t~ z1J{@z4%$4ISO>NF9J3^knT-1=>;=pG=)J>uB=f_`S$@zi+vO6;kCN-4_Sxw?#Xhaz zf&bmB`gyn_Ia+lmr}ImdcFALzjAJFApH3`I^S~ae;NVnNdT%!#AYi%LsyjIyKdNF^ z^JAgJL-?U?-qFq&Kd`?kcwm0%w)%NM7xTl(+4w=bjK^idL-?Uy-cifAf5G0P;DMue zwed*ihm+I%sM0QZa9t$)Vr}7{)r>qn)a^S89{AsTXFm_q)T%o!q4hu zqh`ka3-<2>2joZ7%Ts9Z^TWw$eyCmYU^|svk7bPO*iREYqV;Lc_~GO|T)Ww~z)8{O`UkoFDG8ji-sv zJ=*NY!q3U(9@UKeKm~9>e)KLi9w_(o!~Lo})Gp)k7U{PoKlkWnjAO6|9>|YgPJXz{ z%A-oVJRw#oEF@XEJzT-$U@g z|5_qXO;fAx0R0oaKku z)%+ljlI_yWXcz3W2Ojv}yt1DMxWz}S?&NfSX(xD8nWs|DTA}mA#p(RO9(dpo+GRY%KKZe3=sc6rZxM60OYN=w{iQ!1Ht)BY55lARH_|Q=mymsy z&*a?CflJq$=Zpht_qPk}vR$fTpZsVqbna!W*CES(+w|r+lpB`>59R~o@z;_c5*O6X zJ9-)SQIH3ZXk7QkG3v?6k1Fkw$J>QR$#&^w^TWv*53#HLyIBz zZyl7(4<~2&A$FCA#Gx(xigy$K}#4!Y|Aq zzS4YWng{mo0T1LyGbcZsoURMow99sBNxPJsU)pa^^T573%#Uv3`6<{lKb)L?ep?m0 zj7Rky!bA9>UfwZtWts=}ykUOyym%zX4<~2&A$FBVTiQkV<#E8Z-lb_C*pG(!(aae? zoZLbBeFxfQzg;CfgkK?#jCR3ZF`6HVe(R4PPVP?fXwxq9SmU4&l| zkM3L3@xZ=UZ5NJXCD(=BR+NboRyt-6!5ek*o09+IE!lH=G!M!R4y4CY5SCqJB=Js(T{ODzjr|_Hk(ev8H`Ya^+?*6~+ohdc7hWTITJnCbma!hgJMx(y zwVd(8$tjPj*kzu!B|ph8_8|OoB4eJz`|Ft>%>)nA)T%o<9ZzXjdGPaG;b---Q9EN? z$Ghb3j5WE4qEgxVvGW?=z4L18FSJV@98aViy7G?M z&!l;5|G>^;%#SsRzq25hH&-X>+duFT9mn`ySLO$8woCGuz9-G&$9u=$|3H3BC*EHO z{owNd_WIP=cYm-+)Nk8PkneMy>#ifFl}EpF_ifi+uKU0!e)KhU#gLO@<4xiWOuh^J z?)I}9CO@xn?v4v1onx1s+Bo?@+dEYCY`SR#lX_Oa?X1ShM{{qb<&C$0Y+UqQ+W6$} z_f?)nde*LfTVvB7zCh&Ljrp6!c)0%F#!a>751{87Pku`AsyQE2OfEQw-o3nh?S~q7 z{o`I@x6}C9ulJ7dsyQEN>@dDhX+F!>e$Mt*N|d!=;yA-@lMd@GS(-dJ_n_7NVt?9#@=&)T)L9_054Uu8SB8y~y=pr|hL z`EV{`$8sm%Y#CHHeYl#GabqIEVZ`@uZ`qpDb65{I+t_6dEs)-+%ng z5u&Hvxb{Cbk1+E4sLx)ddp=Q9SkE<$OOM;`u2^nLG4k8WsU<(ZO}^8peS6DDC-VDS z$8RF~%jo%NW9@k#IVhIwDn@==c^N%7HNNu6o20#NR*do6%FF1vrGfXC$CA^Ek>6Hc ztcUr{_n)K2y5u+CYwqXw$uAwz{8sGpTd~p0&2N*R*ZAZ&deNMK{NC}>EyaIR6v`*$ z_jWsdR^*pA{`kyiMQxA#{^iz(m(By^_aApYSBLM*M}DLJ&b#k$FW#5$U~~|> z@^NBb!#UJ`%@6Z=b@2JuF}*+;RjFQf-~yn_?u zz5u9`^mBaKqDzu{O2Ie2+1N;TtjZ^9XT*Z`dn991B@JTuMl9E&Q%DcTR_=)a;ec z#Jwsu8a-6WYM;wLW1~KA99AArn z7RD)Of6Tmm;$9#9aq{QC7{~q2FVE){_dCj?-^DoYcPuv#L;j)PVGn~?a=1Z^I1!tc zaQ{U^~DnWVk_$ zm=VX<;-5t^vF>7%?M`uf;#}Vm9nVxYrJ(Gh=pa_Bw)l z=WNJv!d^b`tK!xX#E27dd@cT26esQl1nEKI#JznsDw-2zf1>`F#qH$({NJ*U;J#1m6Ep7Vv?0d~ zdrZacH{2jbe_BSy@K2a1`z_k7@($r>M@ zQE;y;#0=x0dzOAL_R4}z9K?9WP8?r}f9AF8E>47dZ9zIY&dyu5Wbtult~h&f;wb>u z(%{DZvwUvQlRUsJS%%*WxygH5Av-8H?y2Q-!=75ug@YK}h~q2q&mwNzlMB*=a^pT* zJ~!;MMf=3yM$BtC7jff0U69S;*0&;Kn{w|hpBwZfk9BTSc?T}^4a$xCarxY^9~Ze1 zgBvlg;an6q?&k&BLAc?0829J`H#|3V&$aJ%r_|xfWi8o%Da6e^*Z1YeSZm`aKBAaq zIEVFoZwz~e#j?W<;*EZDjN&qSkS7>7xaSz^BxCjZ=CTWyELyxG^z4xPh;7KR!oFs4 zJcb*@h!t^sE&dsbIb+3r(I8zeR_854UE~$_I@^$Ag}v9}NDMcK5i8>OTKuypR@}P{ z(&b`RiJtA?GbirHhFIbA-pwAqWHt6ui+fxcG59wf zL7R>^LGBBOI=MdPtlQx)_kHvI#lCOQi-Q>c6318KpLzb8{BVO9{t}nbqyEZ!oKa_e z{N?^~zQ5RCj$DZ0FEOv-oae8}4>yS6FL4d91)V{Kho$oL9 zu_G5^_)E-dIOq9m@~+Q{PWVe)MvwX{``Mt*!0qeZA8@}s_=`Dovxk49zCt$-SFU}U zWjKfZJ#5V6I*RW-|JRD`dRWh#S3kFb{rh6sF~yJE`bNcN^dN50+Ph4@=v$>#^f@;7 z^xKeQg*^rtD`Lcoc%WFxyPQ!cm}7mhvd>Al@8E_UE9`^FSP>&u!~?}D=e|?NpT1a~ zaSn14>kaOW=*Q}r-#bbB8!`GIaT#uxJ?pSBlkX@-AGC7Q6za?9gV?JvmK;-zSXp_o z9v!=K?lJpfl{Hp-V;A>w^y7B*$ie(xLxmV|BQ}=dF$Zxo`Ho`5&B{$vUfi&^B;!Vm zxDlI{FdxRPDesy`o!tJI6*pIh`Nq8`{kZ*j;g)(&M~t`;m*I)HnS4hv;%4QhDKBo= z3p17+YY-!D#HOW0+{%t)1##nEnSR{9x@>dpr^JXGaT%V7o5^<+BW_l1n)2d?y*n8< zV#JNuv=qm!S7uFxF)b@@(ods%1lijYal<`=yB}YTy*{C2{3Yf!ob&wk2MuP5IihQGvR^w3}KnTk5e@w3ohFQ2(5 zs_!rML?stu_)E-dIOqB6$v24MFL4r>g^h!IOKp z`aH3BEA-+Z22bMnO8m2kC-;7Z^uRnN&!c$CJH36L*xQv{h{2PX*KjW4$-QAAJ1|dI z2k~sWiro7ZJU4UyH+lSdt7ngSh2|%*D<30vVtRyl{^7ajdh)9kZ}gj=D^?{Ag!HsF z*`k5HXk*!7#l!WNDlVgE?dshczrAvW=owKwcG-dtRXp1q(Qxp%JLj+?#xCb^$=Z7UeEthI1(XXh)M@tr+cS<)$g52knTxq+`ip#b`$>FV=&0{Mr?NEqX>2qaCfhSP$CK z?9Nk+cC_+hJ#5GB`t(OdO`&{PzV^z-N1mj7TQUCT*~*Lc z;BTJE!MzDc@o@cb6`PiDe6$^9?={p3_Q%lr*45E=l)c0Kc079DCuRJpREW`z#ASG* z9Zi0uL5y}JE~5wSh`r2X*_L9oqm`Scymq|lq_*g3H;B=W#HJ-2f3&07ol=Z;v~trF z;=^|2-t4F`a6A6#GF|6z?{&W&cj)bQH<=Kl9f^4j=MYb{qsfmnh|!M3W%QsOu@`(S z+fs~nv~trF;)DKp>0Ih*D@Hq7d9fa}quHHOjCQp0Vm)j}?ro161GnSDms>l^-t~Sv z-gv{5u5*adj>N_?za33}q(O{!Brc-|?RfevN6GlmQjB)Aa?_O8j&p9lNc6ND#Arui z(-QUz?PzwV6r&xj+%$#wXglV7mZ$d}1>cq4mFbbc3ktq^A;w)v;+UZYex3;{B!ZD-Wu=iGbpd7>=pRu;x!@fyc?L8?78Lh zI&v=WyDJr9v?DQW@W^Y&=WnWUC<8I%(LB|YYa3m%_oQpU% zbJqU(92ZCH4CGx`_W1QVp8TGzWqn+!5Q8J}Kpb${f=ABPK0^$S#Dj3`tvkm>jAKpq>GU}s|LQGtO-Brl#Dj3G zT^xRwK$MT+I7WZFiat}#oKMn!rvr}so83V;Hgh=6J9Ei-`vlX``b<;yJ@ez}b0Cg6 z@;5sdjgQ@R=2+1RRe>zcB0Z2uY8G|}ItEDh&lS&lOq^Yx0?ouK0*G1{8g zTDo{H?B;MheCeXalfitf=zQIieH#6?-t-I4(9e{J!I5|nj=go}Xnm%gG4@PM)%5cu zVsIoLgkvq|djJJ-)HOv__7U{s_?_QvrQ;(pI1&%UG3T@Ue2!imGurz7Zm(4GAO=Tb zUc?z_H9>X<`-Hfs4Ubl}bc@TpmF|U>4nDbqkD2`!{+Gi#*){kRuk5%#@21jCE zE5ot3?i@9aHQ7(n&qwUPN*=`ENX%+yd2Nn;MiXtGcC-= z75QU$xT)=4p_#d+@FNxDm}9!Uh+{J+j+ke{9PM7AnKAaHnMC*f1K;0A_l2_O1#~9w z@5^$W$ha?jDmm6OSRra?Y#As_`--n{M?#W(VSsY(`&La00n&A~B z*Wqp1kJRro*Dcyq=WAkcBp!rgSN6|ZAC42Ucf8N>2YWrH<0COR5)Z<$Cwp40565=K z`1r8}_ID7(;7B|O$FA&qu|6DY8TZ%Md-F9hI1&%Su_t>^hB<~uALe5gw8j~^#u#F5xP7satF`+}lQ)?66o zXlsgUMn3u+h}V^4PxcNQoTJT!-Hb7uy!`VzbIi%d{I)iZ&5ZBw-T0H^^?rsJ9EpAI zMQz>78N-h~XX)AD+1fbHWN^Ir^;_wDO$?62gK(_n^qG8)OLZMymA$n6J~MXWZaO{^ zgCp@k9A(c%)JgW4e2%upt!6w++3$v}^?rsJ9Ek_v*p+?D2j}SZ>x^f6*Lm|bF*p(r z!m%fNt%W#-M{vu>W=31z}lffyW#{d3;fq0!c7Ti0B<7gZwn(2HbFaNyG z9CM!0mX2cv$Cv-=3>_bd!I9YKUewmj%fp{*#GeI*hDR?SW#3+Zjs2Bvw$kTz#NbFg z2*+;DeQ8;aP1%dv=h*w*Lwa9I435NuaBSw}V?IY)W4C3WZlB`|H~;ma3;ph50BA(>xjXTco2@w zobfS=W0<3U?%T^~>+iRA)$imHgCp@k9COB=0*?ADr*>63j@R9GtL7syI1&%Sv6r)c zEX#4?iZsXJ8*PtcVsIoLgkv{nzUJp_p%Ien#~Inj+wU{eAKO*OM`Ca!9*AR3TbJgT zF+Q%o)%GkT21nvSI5usQ@%pg!v+21nw7I2Pt( zaU6T?bR6INiBIYHNDPj|gK+E>jy=U3C*G0f_*CA{baS2~V~rbTXzQL{#ysQ4i5PJt z_RmG@o?cE{=X10*ZZ)IN_#BAWm1C{obGC3C?X#C|##~5V{&`(FHrJV>#j$;PIv<-4 z9HZkSF*p+Y+>6?}o5L~wd?++q+Zwl<(Puh;dPv6}VsIoLgkvpd9iGq8>oXZ~+;rr} zx~3ooN8*7v=B&-ia;#>oDUSQ(w%XRj;7B|O$L2c6(b~F}u@0}Vw!ecS21nvSIM#B; z$N00CP(E6pnaEgE%$~Nd&NIZ|NIVF~<~nnx?$*EIz(I1&%Sv6l0kqHG*{ z8Q=5Y{Tpx5I1+;+@gN+#PXWhj#{KnEIbZj3IASi`x5V?uiHzqxew>IAM`Hh6G=|r5 zKKsb$X!+R8jHAyY#_P&)cCTM7U4SnjxFh*JgnW)R_Ea z7d}&sXW#YZ2(Oy+!N#*+{v|0lO(8wY*M6vR)+zUid`Ii9n)8vyJaoVT9AoA0S zSIxOyu|+2wXWYx~-~^{~J_nyzeC8tmO9QBboSyy6hM&{(-?!yh9Eah?>K8sg!pP}b zcPA;2ui|GYhmh0lXTMnFTUr-6J-cFODTjvm2AA_{}p58^!y#m`?Y-SO^wqQQ~u`0Dc8h!)tt{YPP&ujW%%6E z*!}^^Pb)@FTY0e_=JcAJzv&O<^z=O&eomir+ch#C4>yKy{y>D0(+_^^!vl@eCg0M! z$Z0Dtjx}=no1Z^R>`pZv|EorXk<-7tX8+QBkkh}tV@%{bjcv}^NA!H8G5oxp2GH}- z#%mV5K;*lPwYxti-9*eAMgMNaQ`=ze!vWMUNR z^UK%X()jV-{Om+pfL54qZ5AcZ%1j*sJ2<@10u5f0Uo~ zZs@$YZBn>=V&d9-658XBrdEQI`{th?7n=3?U(() z@?&&29R?MYUcXD?y9&OrXyS!iWRQR!O=-m8nyk_8aybl-$c;FcNd^~33cZ&tC zM1PypB9E%rWuCUlqhx;c)}(nX*mko!nIAowJIIS%ezEBO#yLk_dkA^ZE_qb1m;4Yu zV4in0znSI%dDumM%>RtV1LZi_E)H5eXqWBs0cn?#JbK^A;?dG^eR5aJQ`l!b;&y3S zzZJV0kG&)w!Y^_l=SS~fvv|buKul)$?&o2u68S;9jK{vhL-<*}Y}EW#m1BT^4MQ^2tU>hotyuf9zP(<{xNyG z<*CI25{Y)9UE$GwsqhefsF8Qf+!^KpE?tiY^ca_Z9%`37_7EPzFOLJR4gEOG!>O#y zPmCX^lhrP5+9i)y#5~9k_41DC?`83@emlFfmBk_>9zxpC#zl5F5~eM ziHGn*y}YCQ(<~m=Zzs$4Td}M0c$vgQ;*!S!*Jgi`#l!mTv^T$4o1kWYyHshH@z_z~ zA^cDy@0j{om`ZUol)m@}rO3wT9|1He}R$v$ZXXXBd zyo^hKyU?z-i>!azCEKO?)ie+6Q4YJvkJ*=4JlwC*!FF+Sw`e?)>%v2%zerqIH*}tU zJk109k<E-W^w?c(J2V?5NZ#G|^aIckRUR*vb`gGg9B^&w8(|(%yCm`h zb=WQrT0Cf%?XtV@5Pqnacg)_F=7GJUwOwQ!!`KS@emtC<-B;4C@MueatqQ+94!Ab? z-)SD$w;4R}e>!o0i#qYqsyn&QYo5|B#+0<5A`P8{36>TXNo?_*R++?12Z4 z(U)0&u~^_re}8GyF5|JcQ3 zIT8=yhq`%3Z8{wf>_G}1Xp3o?U$XkGlhbx-i(QQe=i8F={?M&y9@x_pJa9}V?i(17 zS6+TZ}Y`ZJYP|!Y|eq{#jj<=7GI7!2|!Ny>=l7Xgm57Zywupr>kt8oV?$c z=TYsF^KI|;G!N{72@c4QQSW{Z_Q~bo2li|H(-)p$&y$nS>2{Xqsla2@Wo&*mZliqz4>@yx1*PWc^ z2kj~k-v0}~SX=n#Y({=yZ%OdL|7ovXj05zw>P}8Sf2q+ z!2$U(?ZpFnj7zKTxrJg|Qmc;J}yc=-9T{S9v%yXvc7w{}U6V+TmPl)Rste_xsh_S^yw{GawdH}&)5 zKiWSSYd(IrjUTkjc*yVVlH=IKm(x73XB0RfKV}2t$ICW`ReYwO#;u0y#`7!nTFb~*nCqBP5E{XdF+GT$5_mZE#RDTubf&7^I zoP9n=cVaI6JleEN9?TCZXWh_w`nPEw*vp6QGMnfxemtCgej)oTpZQIi z2lnd$5BwkX*2%^JwOe&3r+L~=KL3#Mt6efb*371PU{4-!K)XzOJYe0pwCYYy*M-&O zxX#ZBOFsYT{W{G9`|f~8)Ngavl^oZ*zf1GL-a6ocW7-=(5DPyZ zPR{a!b|pWmHR-n{KPQ}cBFzK)=YR+PPkZq&4q(`-J2{)@XqR~^zcVhVk$1HJkmi9s za=-!kG5zxR`3Kiy#-&wva+aqF9#!7&N_id!T${}pKd=uDc)<4bXg?3siMh1uPEO|+ zwJY)HzAXM;TDv5Vp(oSvK+M4d$Lzkw1NQxRI62J^+U0#iZM&EU`Ni78Kj;5Ei$|Or z;4(Va&%-qJkEd!^;!)c%=0Sc|FB?t%G0X$`F?opbfQJ5f*z+LTWjwlj$2`a{+q#vG z{vyl+`7wKNKM&MoemJPlrD&HtYUCktLA|`A_RBO6>^;N$nDqKfGC!Q0<_GPPM|XE= z7vYn~0oR)Uljec_WxylKj{_|p)+VUgsyjJd7t*fskUS+n)W|!gU!LZHJ!HTk%8vt# z2lN=1R^7?zePx&ZgL%4(#6#ke#{t)>`=)tdpBV6n#*ajvq7L)J$?5n}6T2FZ=Liqs zhkAL(bS=#Td%=JQj!`cj$^39~njc-Ut32jNe-VDMw(!sC?bAH4-wSx)|Ew1eYZK^e z)t#Kq`(3e19<|*h9>Nbb@{Z=q(mb%I3pgM@W)tJOacR|^oaP7ZDv#$058;=`0oQ8# zqp6z%gC7{*ufOCujK~cFChA z^IT2%#oEF@yML9%!{(Ps@B4+;CeW9dU&OBRc$TzF$?$;p8+wx?-2{sB!&Ka(-#=kmhly+qVQf@P9HVKb)M-FSJV@ z-519BL4NQl@0i*>gNNIL1RSFA!;1&>`0;T2mRNp>UGk{yDtTIR{n6Vm%>(<4fJZcb zBzPE?R^7>Ie$Xy?bUDunKh(=RYCENQpihDaj@g{`sgtw(NX~OFmUu{9Vr}7{GY4ex zu=!=yi-)xd^!3j#U9rn})b^14C^^rq*+0x9%8&i6zreb2>E}Va%7g1d;fK0;$K;E` zJfi&Y`U~~MToODI>%!Vgq+KK~tQ$J7d1;yl_V!?Yj3(BFemtC<<_GO+Ja!NsCD(9P&=1;5c^|I0Yz0y3eM+Z0{KSsU#eQ5CG;pBAu zsEJ+j=*oOw6Mm?hcMR>E=7D`UzytX)dT4*1LKpMH$?5n(yW~+Dl6VL|)XO`no2PkT zuMP0PF`Kxr^yA^=EI-69d341-`Ni78KWCqp=7Ie*zytp$=UP0B1N61(PEO|++9i+L zUJ?)ChZ=cDvy$e4Ju|=|8b9_k9?)Z4T6HI9`5|_d$8gMp{PH;9+L~&bhkySv>8%T? zi}}%a{~~tDqbt8NF030mH+M|)fUM?+_x*K?1tj{%G1^rg9LIzYYUCX=yM}py%Y^K$ zL0-ls!9(mS4_P0RUmgcso7g+d1Nl+ArQGKp-Nb!E_ob2_Wyi5OSv+jspY+BL#G*eQ z-n=h%H6GGW$ge0K)m^f9*t|dL<*9Li)zj^0bs@xVS1;1R706L|_w;CucmwuJYjXLgAOk0oSHp zk>-Jzg9nbuoO$2LS$-s+7w#@~P3&qsULZV5J}(^F zEX*Uy4=+!_!E!ahL+mOK-bV>PsC6ob(#nE zUSNJqd+|t)A5Kp5gLX9@GT+on-bb|$O!L703gChNvk4yNL96cMbX{0W&U4Zpwv(-nfCv6hC)UZ}79Xv;lhg5|D|VF!pYuyOYlY6!&q(vY9s}SIjUOHl z=)c;FcI#?xeeI637(yX4V5K>BUT=P+wtkmiB?0KfzPrxQHDEk0UxC#U_EcFCjm za_KK6pW{wEE6oG%{0E09KaQ|X#~{i47=4}bK)IhE?pOWXv@3SW zqb9t_4|Vg7=E323MDxpQ`guSX^TR>qLA%Q1brKKZm&XCuCNEC&z`OJj4;-^zJd*k0 z(bm*Zdea9?)Z4-1mbcT`s3(+9>yiXL+mP#BP1RYm)Ps@&))aL??FSY_809r zzr_Mq5~>Z;_obsOFs|VB@f!H2|xZGI`;N+^xwDD#U8jwY2{Afqi8gR5&CoJlfC0$iPXayl`(kv#OkU#<<9Yc@; zLnu(RSQP|pGvbttO9_sn$Z`=vS_wn=6kKM|M%iTvjx#E5C7}H8Q};aYd+VM$r%nR% z`|?kFe@Wk`Prdg&Pd(>7?>XmI^*zgspXq`NgYOp{=I6Lfy(5A@zu$R+L;OscUC-E$ z2_9K-z2Fc(Q9_2ILyzoOuaS1OP=|N z;1EB}=^xM7Un+S2v#t;v;%8O;s*Ik?1%LI}&k7FllRQg2W8VQu{H%$VXbbY7l@{yKj{5awX z`AF>xf{~B3ytg0ZBefq9jC`c!89$EpLOyz3dDO&VRWS0AmiP99e6;7Xn(?z#F!GU> zhxl2RskbH=`DktUARnpyrGk->f{~B3Jmbd^Kg>tf zUDEj}u2`3R#OJ$b^AVo!P6mRLd?c7JCTCY3K|WIZhG66)Emub_KgdU~U-Z1>BVyzu zV)c^nal|+B(Rn-lO!5&i@)2>EpW`z1jtE9RS{pvdM`}MN82L!cm67XxAs?xps|6z; zX?ezvBc70t)V?4X`AExq`$0ZZ`w_v&M_Qio<7hABqp__%WcKc;3PwKC^4@-sj~@La z_as>=82L!cL;Nhu)LRpbe6%)vkdM^~{T`=;ImWTK`E>rJ_VC18<;e&jn_G5yPkF>luALJv| zbG2aPBQ4MPal{kyk=hpoBOhsbZ$HRKYCj?v`AEw%ejM$Ed^EoBgxgZfs$k?JE${6I z`Dp3w_ZvS;1tTA6d5E87nR;u2k&o7f5Au=PUn&^+NXvWkK|WGFmkUNd((>MZkdM^9 zAsG2c%QJo)@gw=jJZ}M4tZhD;NUq_2FUUu@6ZEQw&;B;{dr4sXAWq85#C#2B*SNjx zHFss~3l&~lK5K^H5I-|l)iU-C!Jpr6rQi@h)QkJq;F`Q%hs{0dv}Er;=lT|UabKFO zUf7q09EhP8F<-;krIK&V1Qu~Hr=%wZA$;F3yRn7DFaK*al#r<=#dSU+@ zG9ZRt#C#2B7eB<8+7~Ls(2F?45A;&|hG6KWKYPsoOUAxX;Wc}nP!Y^BoLzjr|Ak{R z_Eo|C+gvO-#82xtS7huPg8R4mn&1#WXulm2L+Hsd~5**4P}75%lJeWV06Ug>_ueZ=59w9U9D zSZABHKR<+<NnvjV$l&C(Vtt zM{Z1Unj2Z(+fUkNv`22FALK@s_x2-Wn|W3*u5h%ObFSIWlV&fq&i2##iT>K>Uim)P z{w&Nq!**TUk9*Mlwfz{W>DiSJUdfz^_TwIZ>)L+YyYH{<_y4H<%rofMrTws0=bndX zKi=Eud?)E|SW9&)cf~2zQfjYrdaR|iT#?HU)>3MP@g_a6mLkrcU4Ag$6cR)3;fQPU z%qf7QUG&>vb>%(=>iF-0m7M-}qFngI%zcXe1Am7#BO~v6KC9m zbuwsHNZvR1ly%10k=^GJ+#`0f=)^s5*H$O)VLMrL;-0)~t&`bfw^OI=_?+SmWgfvj zgS|TS$W`1AxKlHD>^wGKp&9oso-CSiFXpw@Y_86|xX=V z753JqO2p8Ln6KgNYDdJC+7~Lsh%4d{KZq-}ZwN+QX}NlG@nKwXuW($kwpwxj@2pnX z|C_ZFLn~swhO>(sv{L&*g&0~9hxmb3YTpnHt+ZS{x%f~k?q`lGJX&?k=-CIkM|oB& zty$5A(g(Tc`nqVveblpBVIOs>L=3Hn`5Mlyc0^pMeW5~(xFQbmgSb-rhG4{%ma8Wh zAI25;g~t_Z8&}-xJ*$=0tY|}tEACz2O{=5kjh?w+&fLYWd-L3ne$r@V_EQVj>hw|O ztFiB=f9pP3w2F6i|7}#SxN4bwhO=6o@W8Nq@0S>TkXU=BKVPYRp+by&MI7P>`AY2@ zf|0MZTs`^n)s)1nW%ggi75RMCp;eEyV)a*cjD2^dUEkWpMq|&=Q}KGqg(w4wr)S!W=EN$5<@p)W$D*V?F$uR=tdmk z2fC?!LojsHa`n_px9ZD@x7|KG$egxjjjh{Luj^k)CdANwcQKKQZ*5wrhv6uEdpH?_iNkm&*4h_h#x zALyob_zW^Vpc`@a?D7NMuzrlpewVn$sawY^zfV3#i*B7{T6zAaL2_D$R{A+@^*fVG zt7=Gp?9giFeCDo!pV(S$^U!fJ#~?-@Bo2vr^g*>RREW_Bi9`IL52}4bF#4dDt0!L{ z#8?^a5ilQvR^8?pncNjUVC#0#l>Kie6JqE_%-3*s@q}(_U#Jj6H{uXK&`s?df}xw1 zt0xy9$@w9343}<6HH^3ZXKdY`p1p;v!HJ<8v9k2*ruKyjF?1si@dMq|z9AU8X}Nmp zrCTdBk9CW;jBdr)*6rTW&7_|aLpNe&>DNu|3l(DMMjYY?x~Y9bFm%&$_0&tZIAo3K zinmNZ9k|Wb?fxhEtgF60V(3P!Ed9EveW5}O-H1c{KsU8-2!?K2uAX}7R`q-z^O!}8 z<}NNTKD~V(qer~;PuRMRY`%OyG9iX;#C#2B7f<9jwNL(T$M}S9#36p5o7yLTZ)5yG zH{uXK%x|%$pRSE=Yi_r7TXo0G1IUFKx)Jj=oPD~feW5}O-H1c{KsU8-2!?K2uAW?c zs9Us?dpzx_Tc)4(ce&Rw!$XF3Ndse4)Fus)V?7Yx@oz3>ZM!k`FplQN6+%l-{$toJ;||gTenwT@KyO93o&#f zR+fI<)V@$5hHk_mexRG$Hv~gBEmu!Hb@SW<3D8aAZOwzWZohwVjp#-U-H4T?UpKWc zREVJ)afl!2ruGfN&`rzLQ!m{j&pq&-x^1lewEk^dx7pvnSH81B4Bd#8rC&F-FI0%3 z8*zvq=%)4!!O%_1)l*O1-s+zB_S7w@hWTyYUAAu1ezlwAH)7~UtStSysePeB4Bd!B z{6IIgZwQ8NTCSdY=~fNVEnprpyvo+?u)TjT<0&z8BUYAv-PFENA%yFr4v06GZZ9p9abf6gTelt7?7Wgph@l%XU&GnO6YF}lFI0%38*zvq=%)4! z!O%_1)su@4*QYJd{7w3)OSgy56WyYJw{^RA`Iw9g#L$gcS^9NT`$B~nx)F!?fo^Kw z5DeY4Ts`&Dt?GGZSx?=PYM9@O_t?5A3q{aPe^cb*iFi}1_2V( zN6$O+^x3&D5mBeH7blla^>3`3PW^wFTsn=Jy)M=^p5o_ioetUS44GFDqu&vu4j#UK zr}l*kG4d5LecZzAzk>@iOzh-kKzfZJm&5pl}&)o0p zBZg+g%FwTw+9%ImH9nykaflyiruNCRJdGb{MjYaYaaLVt&2&s$^G92=#~!|1=B~uh zj5vfN;!N!e6=G;c9O4I>seMB*G}ChR)GN-ao_j(5IMcq_e8JXi;f-aVKz zhG1x>DJzE&8E*VKzhG1x>ZMui83TgjtY~X?^<_s(J|l)^#LCdG znc5dB#L$d5#1Ax6`-Wg>rse9Xmu69~I15-i#jm$D`_A;y6=XpS&4~FL&MuCapQ(MJ zLJZA_L;OH9wQmT9W?HVETzohNw0gyvTQeO4cDFSv{bV1>XT;EqSQ+{?Q~N@N7@84> z_-w+JVv|K&))XcMXYWGdIW^&K5xTmez(8F6xJ|l)^#LCdGnc62~fbj{mOyYqlY;H{(I~xQRKfJIwUncJuGLX_jrd6eG^;Qh%JO z{rd6QCDxnODeJ14&NWsKOftNh@lyAh#zRC_6@<%Ov}|%FU?w>xkjjF)!pnk`{IZ`4}ln(5i3K# zW@=xk5JNNK5I@jN?HhujnU<@ko|<{`S*T`XZ?HAHCbQlohGxXd(65==7b?Wij5x#( zG*kPAU}&c0>ZzAz(Vo5frhA6Qd{*DV)@ZzAz)t>pRw`OCzbkS_r6_U@U8r;?_Q%;sHju-=YZC_uanYp&D*;@E%YbN%I zW(K!4lX9|gw;{*P>MIauYxc13^MKXw=%!h=snh@lyAh#zRC_6@<%Ov}}iYwjbOdBy<0W((!s_V6CIW~F0ymiZYmG$U4q z{y0pL`x*{6I6}5I>@s=X*hZ&Gi1+z)rSi`z(L8d>%jy&4@!d zBF@ylP$7n9#36p5nc6o5Lo+Q`Pd(#os(TFZYo_C7|7NykS7p|l#L$db8T#W)?UQ>! z#wRo*4)Fuc)IPZvWc)xg;t)TKvzF((2hhyDa?*U(dY!G=w=o_cDw&$?=+aW>I!YnJ`okQkZ~D?`6#YQO$`zFGC$+xGWO&1du8WNVhy zgczC;XV0#7Mc-7r^<(W+NC>rV>Gk(>_@mYZqbJQ;GJkZT_Q%z4n_M~#-MDT#4gKTf z(y4gUy6H6h=59J|!25rB=3eRFGP(YkT=RSXTe@kcS%zps$W`s{v3v5nUo*{H&1jNo z=9zniYBp4uT$;5!_j&x9Y2U1GZELnmqa^ovh|xER*CnS$`8lGh$`v*G%mT6=G;c9O4I> zseMB*G}ChR)Kjzl+;jX;&01Z?fD3>8Ci&Y;VrWLJ4E>s^eW5}O&4@$%Kr^*(2!>`_ zuAX{nR`uNH3Ds=PKiF}0_-w+JVv|K&)(k$*Z z1_boYv3J>;ZOEU)RXz84aIb$)XFAg{VBWUfG}Cq?LYxt2&#rb|AJ>wYpT+B}naPXR#;F(!XVLP0uu+)%UeEyJzdI*YA#Qgo2 zE1}7HGup(~?71h7xScGBp&2n>!`a0V^E0(CREVJ&aflyiruGfN&`itKlZ%gx0qd@r z{GDNOQ(Ln~YHvG$EQp~QF<-;k#SxmReW5}O&4@$%Kr^*(2!>`_uAW?cs9EHBra@?& zwYq&q{;k_2pIv1zG$U4qe$CWA`HbB7fo8-ZexRA!C!diUKhTUg#1A#Az5<%9+1!q^ z!nG$#J|l)^#339JXKG)l5JNNK5I@jN?HhujnU<@kUU3$CG~;*wT?q}krse9Xmu9V3K(pcPY|RE1Z6tk@7@83)L%(KfU#Jj6 zGvW|G&`j+cf}xp~tEZltdB)AqI9v07Y|XBDZjoq449$p@p|5oDpZwu69K}Q@i!!yJhX?K)i2V zHPd^J{awB*tM66OhN4+?__}JQ-~FGr|0IjEgV$9vjkD@mwr2BwaD>dyh!JPR8h!pT zKPp(|S`B}>oXX$tUU8_yK=Qyy?)~xZ;gEH43hGxXd z(65==7b?Wij5x#(G*kPAU}&c0>ZzAz(R6p5`8CrqV9hLBvrVTwEo*dQXhy6I{hFzL z@~lSV6Pgi+_pFFG4_VKz zhG1x>s-{7B#QteyOtX+E2nZELpIl8UU+iJ=*B2uJiywJ%hNp&43}H}?+s{@L)U-89R#T#6BAcBwzk)PDV0Z^oWD z^J}L0Z0NMfrCIBcb&WHfYYZPSxipIodwH78+o-Q^%_Gk(97)c5B^~Ki$^T9M>*{gs zQ;L%ElK!U1HBSBcZ-0@oFG;_C{I=5ss~2}4-)^T1GWKJFe{{`*fGf!XCm;B#j%9K6xHa&jd7_YZ~WDJ{!{jSZUxkY-;ytG#Ea1-r_ z<3-7p^xL0Qi1*h#cMN(nTHNf+y9@R+fR6yddZ_%N_mi9dbRs! z>kl@M#8u2d47|@_;9B`JetTPW#W}ZWkV`S9lB>9)_QrLHf$PpQkyGc9>u6 z{^)swXcHXOvhv8r$5nrtHtn8IX5z!tOCCkj9^{vLbN^iPq|IaYkx$(Q9_{$Z#VI{K z^xs>SPi#E8zqCs}PWLzQVfgVn*LmL4HjlfvS$YRJwBsYg13nlZTU@<-+VVFnR34^Y z>d`d6Qx{kLEMN1C&Ev#NpV%Kf5ECtP4=W!ZTbKU!!26DU@($|3ddZ_W#l(l{j~!_Gi{S_UzGL;j zb>orh0SzKYUf=7Y@p8P=?^O>|ujp~8;Srk0R=IhgU8)%#ct6ww_k}j=8~F!`4^yx3 z*wgSZ{BW)B=#Skz&=ytmeTsa)1&@1wdh3nC!_-S2#i@pe;pgLk*P<8g@df3K%P144 zszqPlbq{{|y>~^^ZXyrXD?Hw3co=^0={w?|yLo`iuz3aud6CQW4{ueu;^1FsJ~j0U z5B6K5i;n|dtNzx_1MyMM@xV3U@|r1E41WF_v!uTibN#l+ao_O6b-p9|eK#K3ZzJ>h zbiUvIW|xc03wHmY@-X#^9z$unke`nOUaM|o&&N=XcEJCt`OKF*@EVTYH@T>^>&~N7 zB|c2OZnj@SNT>Lrh+`JMdS4ANI7Zs^8C`)!=- zFVrL7Z&|PKFzrNLdhrqyK*Y~M{MR}_?=p$wza{;PUoklUh*iKd`f=w?>a{dY#y*h zz4+fUpB0gpa%n$5t0wuw)GIvpF?tw&@aa2NpJem+O5X|lpx$c{I&^2kPSEfY*jkuz9@rzGvI(2D~1d_y7;p0nV7p8lW$W>iZFZDA7CXi{@JQ@5kLAZX<`2V9`FBVC6`RLXuj^k4Euc*+H&2Ea z#z$f|$bQ?*=a17(y9ADNYZlo&P#5DeGT(W@?;0Dh=<*Xmuc@k;W+8HMN=<%G>SuZX=WQM%n=ju_ z;=_#l`S`fu;JEVH<@aiQn0iHzV-1hMIIaKO=CSIInFoLe`fZ%y0q%^C*Z=9%(#e0C zq4PphFL@Ma8ykOK97PgaJloH|5$2Wah~vCy~2a})bR6h zz-wc-+B`lvuAc+7GJ0g=W9d7WmA>%4L5Yu|saJT6rh1THFCMMiY#uiqzkU1j7qm?^ zqX*jrKHt3ibEWN9ZKryeddZ`CwBZq$PbcoSd3^bjYulf{wBzGw{ag(7lgp?3XOu2J z{n|TayfyU-k9mfN;fHH|M|7RdOV?+G)=waQJijg7=FEYRJZ>_M;^oH zmA~Hh`Sv;+@ev)<$%FpBTimGJ_b=ZPJy@^sU|u);c%AE9-O1*$^sZW)2jU|-QF*`z zj@?D#BRXDrWaHz-fBr&wpPLFAAEsXND9$qRVfgWVyUwiv zo5xKzeM;gZ9`58pKMyv4T)OK!+i1I(ddZ_XVt5#Syv}v5_S-xbee9+7eTR1b$h8aO z3v*kk-pM7UoV<^$)jn0r!MI=?w|ExHxF=W z=I$pemrfq6mpqCyOuHC9ZU*TqLnpd)&9=Iyy(#eDMl1KAo(=LHL>hH069J1FL z+HW)Qf%>z0BzD?wO})ay%!9};eb?@vLsM-YJ3q8s`fcRsw^&yub`qyeQ!jZGIX^Z0 z+zirJCO&BMIR3I%uRv^|U0O$V_Lr<4iQNJ0x2#uqm~oc;dhuAj*yi!r!>?e*R(BPvpmIUFYZnHjjljmNh<(P(AYTk=SW`xoCTvx6+(&n-KhKGP#)lrjgQ1m<1{zE z6i+evBXC_ApJDSj=Ag}G+^^=w7v)kLOzdQQVZGF&Xx5X|MRn|sT1VJC&?lMKqhnQ% ze0(H!5+AHrcq}k_7+t#8uBFW--FWEzplW75rfq`jbNSTND?CmzJObC*12f$`+Iig* zA0Q+7!_-S2O^z>ypT6_%C_cu`1LJ=}fJJaT|`0)2jd{jO0 zk=RLmH1qchOHEl4jQYq!Nl$ki4Rk+@OZ!B5!hcQ4z%_7@e5l?{)lsNs$6P=iJj&TQ!jZm zIX^Z0RLAb9{vn&k)yckll0S}f=a0lr<0C&mHS4&d;n%%(Esf{cJie3cvj-mdU-gVH ziJgx7`Sq@+@d>-{3vbAjvIf$266?BfO= z?R7(@-}0TLT#`6_cx#Q1+pH%$UWro^7u1FC!F3)R z>c&IImnhR;v`yfxb9`y$`b%-9>9>J-y?%(gK z&GcKSfP?XoK;hBM^DudXy0kC!{1|$l&EujA_4CS>XMUR4Y5vIN)8<^$E{30nLi?ra zJe$W=b2gRnCGw0fiJguwre0|mGtM>x^J#yV`Pid-Jf-==JeLG*ghOMsHkjB+{%Gdr zA4T3@H~iA8-9P8exApkqNapiO&-x{?)BKUYzdpybOW^!t;&oe`G=ijM4!rMcGUH1% zx30|Uk=RLm6ivNsm!i4vL0#zIbspN^<}r2ELK$CLxjd$RYJ-WLjxV|UK~2Mp{Peo+ zDBivck6l+td_$OZe3*LKE=}`0 zb?A%|FL@M?GkO?4-D}s*zPG4IOG`-RLCoPR`)&pj+2`8-BF%vs-gcQmoo%>$g`Tpoj$RF_UY znx;O{!`ufd2IjH;i)#R|Gd`-BJO&RMtF^(zPVdpN#I?LvP{kOpb|Ern%mf9xpR~t<1bbK-O3XiRf9)=&T@EsGU z+dQzRH8@~?8fSRGk8-IECUz1ZMN=<%G`BSIVff)%-_h#QZ#&j6ne`m~Fg~*D7uG90 zUS)U~e!SLoE>>+lV9WTZ-V^q@N7K|RJYF#IVRUiT&+?%Uxp{!g>eb2v*JRrz`BmrZ zre5LkXT!tr!?nJnev+F5!YVhkL z$HXPJ9@vu;91tJX%=a$fN4eAn6FdFe2kY;Srd}su$QipgDNph%g+GgydK+_{9*X%b=}eGOWi!$zGx#HoqA;Qx~W(6*vRk*)#D>>9;iE> z+sQ+@8h)<&S-$4OHV^E>1|EoyXp!o{Yf>(?!Ng8Kzcuv=56;((F1*%tUj0ut zkH?ce*uVove7f?;$46o(@zKoB*Z+`S*EIZ6Z|#RIfdk?rnx{O#AsZiw-3s2ikX~xQ6kO*lD|%dPNVjZYc(?8{*4s9@u*a zJaDu|l}A245q19$zx!EjVQ3BeB!_W2Rp6Xqs__{FHxpRK3FHfqhrNqdot~-`T?ukc`AH~e_5>l|He^T6H^XcHXOTwc%Wk=RN8Xy)@e=j(=F>dpOg z=oFg=_FDiC{Etp?=a0lr`)zK%-sE^2nAZnBZS%k$2H=4BXl43K@@q$YBz7{+v0iBx zuJ;4y>#NVTd0<}v@M!0c%zO+ya4&O_2M@$Yl*y;C$HDkW>{O53eTU-a zX50_FKQ>mgd1S|z$g_Uw7+*}iY?mf^gx+^p*o}vdFOg?`n@FSiG{>XJb)_k93+ee0 zkGgrZ<0IED+4#uvV7;OT?~IH@;cuR<0H$XY3e19;ua=80@szT*LL#2 z-)Z3h2OJZbzi&`2`ugtq!b_EZ-MvrN`>dBd_&R{ze;1AS)b&d$#p$mRS~ zH!uI{`QJ#tWxeFV8p)5pyUx{Dxp^QyhX1{j2mFA`4##d(d1Cm_Z9Tr85a#n-@htH6 zv$CGNXCKo~H|cI0PqgAUJ)g z{nMp;UlIq0-~N)>lciJ=oWz0P06&O>Utag1@iQ(MaiHbu$i)Y7p!QP)BM!8@w;#m8 zfrq}r1RJ;pWb5(j*qaW)R{JY#YYoWy}( zzL=a{{16A*{^;!{o=PRbNgN0c@Pjy5@u4>wKjVTC2U@Ot$lZ0da?)9{GUJ{l+zG8wc%uC(QG_5eJxaZTjTFEAc#U_`^Y* zln)Z8ucSX+@AEHPe|GSS$2qR|m54X`)1HDu{JgZ;VI{4%CU~PieM4}VpZiNQDx9zM zm5Kj8iUf!FdGwxR%gU!N_%%CxL2!s4_G3OT9oOXB@36TiotEqs;vCo8d%c+Fpl90; z&p{^#VzeJIU&Gngekc9zo7`)yM2z+$4)KHbJ7&rY`=_?G5;>s#b{wj$$oc?w@p);p z`%CY6;x%^~`!X@wk2vegE55e0N;2F73zX$7kCQ&yObq zVzeJIU&GnO5B+c8vMcDPM2z+$4)KHboBoCijh|YH9MFE-uAE8#;>yJb?brXtchSB~ zjP@fIC!Y6(_9F+hA2HgGIK&Uzuk|w9kI&uDwjZ9mPY%RrKVrUyvr8|u-yt9WG5wT? z(SF1se$amZ_OFi^KeZA$p#5%r&oAg-T)Ft5{SGTWNBc4{+K*VAc-|M=J~a_W^MalE4hIC6`=jFS3$BT)=K!wA4$24iP_g&dL?^h4X*m1%l1xfOC{oO zpL3z$5I=(#F3;GH3$DF5AvnZOZIfrR_GR*^y*MB^%+K4(8*O(e-&dK}l$ITulz;C= z{?4;YpYijLD$g0Y^P8#Nh+xd8w47et{iIPjS2!S^c*WOeTp^fcIJ@{Do_0uH=l&G1 z-6;8=U1wKD=bWA2y9+VIy(Y3TguN!<0|zl;h&X*E{psR_7+P`S-zO>GJ24!l37@9iq z-=Z6Rx%eQ4)P6)TVo1yB#ohLZA#y+r5hI3()r;E?V~Bf;;F{1F;yxkS7|Pa3ye@g@ zgsCgcJgZb9Mhp?tH_k4t5kqP}E*LSSuSjO_+emFO(nrCz4 zij9*0UB)u!+7ja$_v-14C#|1oU2@W|9=XKyxl)N3@kC7DIJ@*hJgNP-V8oM_t0$Kq z#MAiFsdpI5GBM(bSiQLUASbE4?vILi8ee+nepw&DEmZCBc77J@|IMSQi&MxM4Y~s{&eYucvAau!H6dwW%Z>m5q!qb9ia;+SB4-R%dTiL80sQTwE}o!xFR112aS#{+2hdcD!#7|*P;En_uTs2{y#eT&t{&I%%2zTwgT@F?f;2g zj&Ec9&cme>+W(4!Po;gC80}9?A2_@Hte@@AJp#dfZQ7rE{;kjLzwC`mOzs&k9rVU8 zwK3ZNzFmK0bnHcR)4DEQh{j8tu&%GJJe{I^Ido6ahe|FuK;xzwf z%TuLXI;Hua_Q?M!PV+y@Qy=*0@`L>U)8l7L`xB%6iPekG&of#3GOtDZ6NmXh`;$NB z6DdyfKg-pV>wTs9pZ2=$P4hp?d;38i-z_oYo|w48+5X*DzH=7LvJqzywBgrypR~NK#cd#!xsw~`!X?NftWsUcKcaBV}X0ag8Mqh!j0+on%C!8xbd_J zvo;$q5hE6e@&0+Z+whsJeVNxH7KrHsXSbjAGZwf%FSxI3EO77L^*I*yS@P4j<30BE zCEuyNan;WMSf_ZXyonHaG^OdmMA{j7hnP)PW4k7RJ~8Vg5u zUoUdc%8&OwVCzeb)O&R`VVireYo2#x&O3aHtoyAXn$h*z}f9*{cL~k zO%3jA)BfCRdVOyHFTCa~SueahvtEq9@dK{^@V48ByWO5C{b%8)v@a8*{fX%VXSbjA zwEfNB7J>U(wf_y><^tLEN{auwUi?T2+T#8**NA<6>(6?Td$EI~v;DiR7ZZD~5xCF# z`kV`P&a4-SF&7}l`{&{A!_Q>x%e)r3fS5jTcKccXazXpvf#>^#?R&?V3-pKu?sdOD z$HIk~^&&B1fjF!UpUK*nc`agrILy!b84FdjS2NzjTIGegX{T1PxWvuw2BtkT4n1dex>>!w4%@K*{7A->1UtNO3M|w{7|cw*)JPc zHXHBCgcFCKy_2xq5QFN5qxd*9Ai>E${7zT2(9Q zvtn??+G;g?g00ny?|W9hyG#tNh?SXNE48nch@lm6h#zRB_I1I~O3T%gOLJ-!d-7H$ zt~&0Nun#uhZ)>GBE80-{U=-E|yJ|J>cw4J;pa0rTREZc`5%V>iUG0cIsP;9%h$}5u zPcA=*E48l+MqFumZ$FHyR#_7S5G5^K-&=PR|Zm57nAh(r7! zU#WdvF!Gg_t0!09Lu^b*%wo@X3_Ie=bB_T1u{CPz_T3$?FzctjK4R!btStSyseP?P z4Bd!B{6IIguM385TCSdY>UMBojP=lM;wW3UpKP!$X#5;1fm4)Fus z)V?klx@oz3>ZzOO9z@T0+wH@H%xV1kON=QuMH`A%t&p77p;h|*vi5weG`Zrcy4GS5L0KEOU%dt-6hsncOvYtgYKc zQ}(}^Oo*WyF<-;k#S^(p?Q4Reo0h95mmlb+_I1I~P0M@xp>9>rx?6HS`eQfUl4=-l z^A53fdwTX3vIZxHZp6ycubbM}O2p8OIK&ThQ~SDL=%(fB$)!JaYlX~d19TfG+q&I5 zx|#G-V(3P!Ed9EveXT?c-H1c{KsU9o3x;l5uAX}77Kf~xT)LTlT1aj@#y|YMd`5() zNhbQNy#GmgR#hJ{bR$-le%;i*RwBl;Ux-8e;Mp(#v%{O%j%8vz`-M2f51##^_0|RB z*)LkIo_guu^5}oeqD6BTmlvPjz8~W0@6f;h^S1sYn=hBY*CmGj#LCjIzuMPI#L%BO z#1Hg;+u@hU-zO78f8r27&|mAV3x@t$uAX}7U-jINS{wcAKezQ?b;ryD$b=aB6Z18k zT|6;PseMf_^w)Cr3+Y^U+rsxp}&@^C)fLh{)=w@ zGe2K06GMMu_2T9O{k7h@VCb*q>dD22`nN*jKVbZA-Dc~5)A8Fg-}Vthe`00ne!tLP z?Q4Rezm}^f*ZYP32X1kx9_)!09Nc_9>PpW|*jK33i z+xma`l56)T6JqF3%-3*s@r3?rUlR=dwOl>9{6PQR&mWNa4>9y7RxfTo&|mAV3x@t$ zuAW?csDEts^28O1j2+KUko@D)Ka+o=>umjhc=a14{}V%hVrA)mztCUpYl5M_ma8Y% z`-T2@|NJ$gKQZ(tRxfTo&|mAV3x@t$uAW?csDEolK>l~>pHu@s?Pu^l`<%@GUzL<6 zIw5kr9axSxZp#^z9bm!sO9wH=7V-r`;qbvvoC1-L^~GWa#=7Rw4>@! z7mRk)^4@&dj?pnMs~x33R(G=7ap_$(d0)h6M`C5^)(!1=@fPmMbGyJfI!bex&^3>Dx(vJm>7c$or)ymmjpF>QEPqcGPlu zaqA=Pc;d@w$Bi|wwq9qq;~nMAR+0%Z+L4&A;p}?9XvdBJ<&is6+gjrJ_Oa`{0!PW$R-jD1Nk+EL4U`$0RZ{Yd%ooj=$1iT-%l(_aYYgLYIM>VnaZTHc$F zwBrdcqaBlK;0JlN^#;2gzj5p?(qD+tj>O8+-;U3{B^}F>ze_0*m(RLWaEPCk^~Xye zn)bJb&vs?v&+qq-fYb!NKK;1P2k6TcS7=Aop)MHhsO9wH z)<@d$_<(i{nCG+x>~_5ArcX^J6JoR@F<-;k^?uPG?|V2herl!v`<;#380|Rl(WNqv zr7xEswBwAaZ!+t??aIVxM`HT$@j*LIz2qEI?@0M;yKU3vgZ_BmQwPZVr7ssBw4>^f ztk;ZIXh&lD@bO_g#=|e89ba0e^PGOW9T$DR*L*zDj^EpM2a}&`rT;jj z*v4qbpI!4#Q?8y|x}hCkYE3it+m(sYj>PK49am__@w-^>NO@)bAKE_Aj+c!6T`(WC zqw0`+AKqw%b|enrBkdTHS6%(_;q$Z|3wAs1`@nM&pTuZK;t-x_$0L4qqw!NK)%ve( zW3=N7Up`;_)0eLuk9cj>*l$-RMmrMI2hJ`(XvZZ_aQ-$@{^-Z9Yx_hye*5NIg885w zRfl9RC!-bGkvN2pwBwO4qdz7U!4JMmFnp-}xm%V8G1`$hdv@uDc2v9d<2x#agiv)= zy7wKp=K`Jv^KgNdOTu`+bOSL7% zv^6{avRAJl3u0(S%-3*sy;o?a_BFxKOv}}i%MUbD`(#fu!ws4ds~0yPYSuc+Judn+ z(=l=NVq3Gv9==@G0>sdaID{iKQ~R1=Xr|@r$)}mx*X3Ghrsci;P_ybQ&^MzG*qSZ8 zu`KzF7@83)LwCDEGqtY?hGtr>o_w0AeO<1FW?J6c4>gO9=n-doxxYOEAMNkFj@X)A zIwQUR0w%=Jj93}^HBo_cB4`oKDCHivN*pJ8iu%t4#W8k88C5i3K# zW@=w65koWL5I@jN?dyV}nU<@kUYb=sYuxr2;O?94XLz>z<`K4L`urJid1ar^1I_4D z->F-BXr}i1+*4?#d zlI`b%dOmCLYt|=mHZXINY39jie$6z_;$tS4W-U)X3)O7wOk1Gu`@76(hsKKXeJo;V zMyw3|nyG!QL=4S{L;OH9wXX|?W?HVEdTADW#sI%&y55{P(2lbozp$0e&xoNJu`={) zruMZGF*G9%@dM4&zAhM=X}NmpshMXC@N1@VR{xN#+0~aFE%}TXnh`5Qzh-J*D-lC8 z;t)U3OzrD}p_!Jer(T*x$GPWcZOz|J>Lt#oYpOw-rF7<>ZVz?i_BFxKOv}}iYmNZT)V?klnrV4&Kh!MptT#h7>+f=(_R&3_lKB}i zG$U4q{y0o)0lJBUXlf z&D6eDB8FzfA%38l+Sdg`Gc8w7y)5k0Ze4E@Lo;G!=;jE`)IRyXhVco_ zh(r89Gqq2?uVMT^GvW|G)U4%MZ-#2NX6svL=kwVHz259Gr` zab}nL<4o<>pY>+!>6=&wxHZ!?`n)Ukd*XcFyuMdO8;WL;=kKchn(5kUV(H}4EcVQ4 zLpAHa$ky!IXO5A6NQ^im*68#1O|`F;h%p8bhxox5p!Rja7z4CiJ-Nnl83R1?vm@s( zJjK1*)HrLMW@~oVQCrD+lNg#2D?`6#YF{f6Lo?zKKhR9=>w=+~maC_pnt8?mzh(>N z-uA?DTeH%!JInlx7@83)L%(KfpL`x*d_ptg5I@jN?UT;~j2~!59O8#@R`twj{hH}L z$KuCr&GuRTYWX~X7@84>aK!vf?Q4RenU&M!ukPxcg)9ddH@kh1`Mo*fxWd7(v z?T?VYavi~O$kFaLY&El;jW z|NhJM$L*Tm2Ttv#nPwTH4Ix*x|2A{P{h5+qGtFE5=S(imTAq6rp_;9^cyei0^~~}8 znrYu0y3p2amqtnE48-W0#O$v)yYdj`5o%u(jC`i$>dEB?`AqHWf-zs!^4@+pUyVHT z)wX7P?cqvja?hgGrEgB(>h-ezCWdCj%FwTw+Sf|N(2O|54>VKzx?pIgdX35I4`uAW?epqbj&1w%6}@9l@0RXxwY z3)QST$JXq~V@?&#h@ly=GW2Vv_O%i*G$Ri21I^UFE*P3=xq5PGFPeG2zY?t31-527 z9|D&R?0cV{IK#dEJ)P-H$AEcryJ@EFMua#c&YoTEx<0NYF+Xdqvt~L!TYc%|(kwpB zJ*V}@nQAugBa=+Cxi4R{;WKT`-uZ`_(hrHzH;F_0rrOs^#25pJL;PS2Q2V-Ii~(A% zo?N+tV?Z>=ty%iFOs?sf=Ck2X*_z$6^;YsdDPm|wtPK6*rrOs^#L$d5#1Ax6`?_Fg zrse9XmuA&jFJH4YpRqMt^TLDjIUF%GBUXlf&D6eDB8FzfA%38l+Sdg`Gc8w7JvH;J z(fx6z_h8q2*w*X~oBeSrSr9`rV!np6izCKOwXX?=W?HVETz;UL+Sdg`GcE7!NBZWX zb&WF}16Kc&t=Z#8FO+;n49$p@puoKWl6D+!IIKP8P(_jF_+C?Ba+xQ~TtZyT&IpBM$Kc&D6dw7@BFhdUElhX4O;I zSu^=N!)TtZ*(0^L$up;jp&79<^lPT}wGuHjBM$Kc&D6dw7@BFhdg`TFG;f_XllvO= zPuiMY^{v|^pAkbdVrA&pOzo4;$c;~EMjYY?nyG#A8M*NT&4@$%FwR=DU%qBzpRhG6 zTzjJAGh%2)9KsQOQ|)Vlp_!JeCs%wxGqtY^hGtsc+mC4GSqG%wm36N;;RAn{)0|~% z7Ju<=GCw1RX2iKGh$`v*G%ne zC1Pks9O4I>seN5AG}ChR)JwDK{Fkp;e3`A;HP0;)&4{5Hu`={)ruNC-w=+~miP7}nw|XeHEUjO$Jx8@+*iIUOAO73m7zb*)V@|ChGxVeexRA!*9Ai} zEmu!2?WtMSb6biR+EC5nWwvJg3?OB)VSOG@^^BXiPwUQRdS7GU>~5NAyAdJIh_h!`yCR>d z-TLv}vi9czJ?_)GHOt&{Jb7|y7J0@^f1K%e|A(%cT$)uqYbU>E8fVefwr2BwaD?Pp(`co_c8(o#LLK`8CUo0iU)t+jPp)vPLI{ zX2i&uTP2p&49wRXhy6I{c)!D zwGuHjBM$Kc&D6dw7@BFhdU9z`&03y0Ei?<6YphvdYu5U}-m(rLhGxXd(65==*Gk0D zj5x#(G*kP!U}&c0>ZzAz)gJlGt(oSt=6SYedo8KR8l4!L5i3K#W@=w65koWL5I@jN z?dyV}nU<@ko|?S^aaO#>)@+yWe^}y-7@83)L%(KfpL{pe_=IM}A%38l+9%%)HGZHO zafl!G&B(J32<@9=HCwX{dA%8VKHtQgcJF}ipRHclO|xvvr5JH$m-^#O?bo06X5{(4 zhF>$yXYuId(yZ#~n|{r7t}*=4$)#EBSv!Sj*52>;wW=BRQUB;OdyH$JQk0aJ^fyJW zacb(N^^E;vf@?3f1gjT!A0Iz|lk&grUNhfQ+D>q3*$#rk{Opwc-4u^0g1>#vF@pIj z&aU@0c;TrTd;I-XdW@`CE|_IFyZm5G+97#;!m}UV(A-&bvVZi5YtUxZzgz#?>{ri! z(99#YD-)y5h(k0QKY#sfv!OfPe_QBmvsbPEZT8skxn{n+U6~kdM*RQhHXHu;@HQ(? z>+4&cyxHL!o>_=ze&K)euW#Zr>F?F+({ol2;PSL1M`w>NnR(b~U*8^6rtF!2J>d6^ zVblJRF)sc#z26h>_s2G$%u8zp53i)=$RmpqNWc9_g?N9(t$(*x{^R?P>#*NE@$s!I z&l|)UM`S)n#B;13e(1zO{73ou7o0!smaCVo6dtBt@@Sg(OMbYF2lL4N7g?w>=?Jk);f8ZFQs$G>$-n+NKS&Al-4fE~CTdGPrM?sotF z`%#agsh2#O=6CYLb-p9|*mE860S$J4^t?f|3640|F4P0-sl-m=gY}X}@p;oOhEM9v z{d4#{o5$=UpSle^@IN-6-9iuL0DrZ?#O__9hpCr5n&x-%!xg?`>|&e8-PQp;+B`<-?Covh?b>1$j`?Cul1W} zG0-j>AJ8B=Da`}qLA!i>uwLQ8_%OQYb=}dtCEfJUc8N23z(c1VnRdy?$6}+0(WQIs zS{i-K%>x>=ay)Ql*Z45?iXI;^JOblmqDx*!U5v}(cskd@@2UkXI{R&8>J=Vm8Xkrp zuJ9ezkK1_+_NW*C2h2R3yp&6hhpCr5V$*NQ4?cZItIPNTd(?}5JI_3eggo$Cw{~H@ zM=WuAhHT8-fA2RU~*l*)5^AFUI{(=8Z$9S91AF-*IJfelBzXay>u@AfTKztNe zXdX*`Z6Dp*h4l)Lc}5SzkJq}+)eE}u(0<#>^_R{#&GcJSFL}hwA4V5f{Vb0z>Bd9b zrE0zt)T`gJUg2?uX&1wf^|{W~OWi!sE>-iHM9+R}>Lrh8)U=Dy#Z^Dc2R`EFfx4@i ze9CLO#)qkwJmS*~55tewy3WIw+dLNaT{)HGNy~grkKZ*m;H@^8*xf1fQ&TT_L}p$} zU0n6EeBf-G2VP^pjWRq?u3U2CZEn6ERgE5oAFlNs6KB~xzS4KXKH!1)h|Kq}s1JOx zT@t&Ss0Zt%9VoThNA+Br$BXZK_D*1spP?Wg~vxte=)lF zIN-J6b8H?cN4@ypGW~_TluKv3#HL>1Vdkae2cN!UV42P1+~>b`6Y52~#3p}`2Yj(z z61#mw4^yx3ILpL`;fL#dN8E)6>{)NitoQM|Y5|L!9;ROMh?W{2f%En1iqsySaXq=9v@Hjj5OMX5Mc&++9 zo5u+c49mK*mC2{5KdVP#C+o_{)Jq;Qc^H1ZcnsWU^Z4$LSG--?%isz?(sY@>&tyMOUr(V~;5_+`fr;9sz zD3{t`VmByyn0lpMOn;#+y?Bh>Zu7u3;DIAH?SkJ`3%t_VUt&|Q@G$F4>VhkLNBnPY z9;myO;Q>F&CC4MbzCG2ni{acE3GS_24y(kHk*eCCbl}&A38-yw-J&zi9Kg|H*SDK3e9v65y1Lj|0zp zZ~3b0=PQrgcpDpi$j?{@paSA`+viP2ZDyz2LO7w_wc|E!?^-En`^|QPh+dMuwuAc){bN!ZjAg?EO8Xu-! z>JjsPq2b5+T<7S!HjkT*-@g6%3)-f2cK7%IkHk(se__4C!^}IVi>rQ?NB?f~`0^#! zwm*MCd_-p{4_uSgBeB!ZU-I)YlfTH1^|{Wi8*Cmwy!wq2A91E#=p*G)8%*q0NIuQy z^;+sT4xEp*R@yvTN4!h&NAy9}1NBpnH*fT}r3VhUTI1BzEA3*&y(loR58Pz)c+~}8 zZLhQ2(~mJG->% zpm#`riA}wt$2`+6hM%i`mbbd(kJ;b9x4ph?=MP7GEPc-brMJIfd@AE3HuVY*Gro}@ z>vNqazHPV5v|sHe@lnmS3-x&Y^3zHe-h02ci>a49BD0<(KUe)MpSaQHaoFC!Z}*pW z{>ZcoIpEr7>yMV^{ntv#AFP)=V)mE7aewS?o5v1oc3#QxC0eTe1@)85?wdVaI-_;i zptMV_zeMJ9Wb(tczGL`io5!`w$J%j<_=qy=DEa}HtwyJpF1_GBX&2TjddyAzQWsw9 zIuGA#^MEb%z_BKCAD3FBuXN6nV^c4AL?@?y$uGUe{d4TgZXVz=oZ$g4%B7PB>m`ra z@E|`ogY=b&F5?ScVSLncJn&k|rIQEiC6CD5pCdn9=R4v%Z9NXz>kR3)QDz>5`m=f@ zc9O?fukbMQBJ%Tbz-t4y**tcBXu0I|R;Irw2V7YjOzbpHbMvv7pEm^Fw;cPb&ExpX zUcG|t5}(_>--1VCr}IKnFZGD8PW?uPADH`&>OD4(#~!|1#{FtW5BgzzBz7|HvtIIu z{yFtaU3jhQJltj6UwC6#;-lqRS0;8EAEsXNh_5g_j4rPFSsry6UoM^Tx%N1Rwuv)( zYhKsS#i|(|s6VSm z(q}Y2Ough0n{@R0S`!E1;O{EwDtzf}&f$mLU0ukfHA=8P+R$MDzPJP;qP zj2`f#Tyi{edPLWl_%Qr@9PnCvy_*N(BhJjn=qu&Y$%FNZ9{*%`1nN=!j?H87;qTY{ zkz4O)^+@a_K4McZc|;#e{gNNwgX=upWgWHYEnCXEvYJ`XDF=9~4JLLvFEsTEkA~61 z@N?DA@`1Z-J%+}Lx~^QJaf)k{OKmW*lXYce>Lri(3q}vakM+6EW8*fDAHT4b>me|56&9{MbfYOddA^+@bAK1{vB+zjrpFQS}*b^U#osRpaUh;^T*9|{6gY=cL|7-IoC41L_M|)kF=`Xk(jBP;kCrDs5;OgLE{6Kv0H3C z9^K<9%^#Wj{NRx7FNvMz4^yx3xH9!ie#*Z)if*-ed~rlSuZ%pOdn9&}KO$2vdBoQm zJq*9@wQK3{w`?BQWX6{`qenhI5<7_x)+;T5})j}O#9QUP=>k>O1 zUrfE^5sjrh$gg|tS~}5X{W5je6&fG8cFD#^VyE$8>LriZv`1|CX^q{{K$rUtkKL`~ zOEq)ffgVyWwZX(r;)C^)M|81i7sHR&y3X-W?D&A@9A6^O`ZlqX_+Y){5u5cB`MK(6 z`M^Wncs|anD`_=y{X-bw*RG|l zC)_;R^N$=4T*>%Ipy}}8ZXk~ch^G9N*@nPyEkN6WQ5AxG@ z-W^3hu=T)x@tl9enSQH%7QSlO=U&GbQ!jZ$%wvXM_u93z^&d75?7z$Ko#Vc8 zsbTMU%^$fu7PDOpKdrGlns~tGfql)v1MyLY@ks-)4MN&+p`+@2Q4;!*zVg<@Jc;t>LHF zbw_a*J+P-WhId?a=nAEsXF5t(%+b?IKamPU`-?UG%;L>WDJ72_kjeqp`B z<8!HB>cVSX=jxAb9G4mJd(3d4NlQh6k?6woCG>=2KIz z@TeOeh99o=9b=EUc_4rEe<$$!{H9*=h{?n7^Krmyt*(0LxL^I4#%aFaCUQu>Wxc|K z@nQJ%;xYWNTMx8LE0f2xP2e>*?&sno=KaDz9%KLM=7DyJbNxlRNgII zkBKh*7Vj2XwDbDMwOvq-BjwV`gY}X}Z0_$J+Rj)`)&0j$|D~iiJiuW zsh2z=-v2PVxEZ9cL_f88VE<6?!2kH7P9Ey7hJ8i& z&T-fIy5^7Q($042>@S)8k-Ps9bNym;an;ZA>d$OF@EYSIy10`Eue0)psaN#id_C~~ zN6VAP5FgbGlm|F;>Y?LHZ0aSC=(6R4+&|;{ zY#wh&_KO1#Kpw68Z64S!jPX&utdobnpBnZ#ljo4IUTGK3PXpI;W8b%VU=J?FM=O(0!66$T ziQNkMzEpmGT1@?tpYrdH27YAoz`j-zADR9_4~&n*PU0gn^-_;$Ps1Z{ej0b-fxV&_ zAJtr5&+3ubX?&P^$s^u1rHF_96yw-It{?66|d+$hmWIlgE{aHN{JLO^OC68#I)GztD>SuZXGd2(GXT$i2 zGI?D&;OZLo;gNpJddVYZe=+=6pX*%T*lri>5ySY1J@eDVPVbN9`b$(c?P7Fs)z9+g zA8b9a?+W81&a?}!VSFTZnmKBih~Y zF#K4b>)d=Hwdd=Y``}=Fv@-3IzQVCCx4t#?3Xf@qhvCO-UFZHSZ9T9zgv3W?zMhYd z#7^QPGWC*2%>H8dx$0;6>VBIC_FG_lR4;VTKN36bx4Hfj?UL$2eyq=R9_li_U=IVv zN1PdN;X`9J`F_xpr)8XDz0@Omw`rHa{xa_+J3g>40OKRd%*Xg%QZ6;@XQ1t3>LrhO zFQbRih1a^y%~#p&f@keZeB{RctR9J-@L;{d!{kxw;;NtJ#Z7D;cz!+OBg)Lzl>@G> z;ko;gPh(Rrc|`k}b}_oJKG%8ZMY~<_OnAmeE7vZ{rG{tO>o{lXC6Ac>HgJ5YKWp>A zbJiIjtz4Yq{V+ZfJJloKZzB^QhM!9ZmaqPu%_BR$#Gd@oF}|33MUTPMFLhykuJh`r zy7AEQCC<#pv`yfxGoQw$Uh;_cHarYJSN$xX_g`)v?fV~|`3J~w{la?5BR2h&{8*ps zT>O)p2iCVy?(-M;VSFS|c(7jK@vhV_`QbX>QM~{69rNT(jWIajn8-Xo$lETaUg5!h z8<@w|{K(A%eWspyo)UN|mt1`0`Ymj6#D-t`oBL<+aW@ad$52KOddas-D3_Bh!KQmWNFMo6FT*GIP;2&K3al!O~bMox%u6)MN|5SPNOZmPUf(wIR65Qd( z@xG?ajx+VH7W{*2e+*Hyr}zigZeCH{)RXIdJvRROjD11y zC*Qo2VD;i|FXW42V#w$B;u^&BqR}(v?lF6G;oLJ$*wH-adhzIzSxe@RE}Z$nGiNQC zyLe#&)93Lcp7>neY&_w)yzqxZa1u{~)0h4~_Pzv8j;dOJAP^$ExG>6MgNQ)^!)mig zFSrC*3<%Fr5CSCHY)K68Xi?+96KxkX2U`(iuIgKTCy9T;`~T~gOnr0f+;hLP+@-2}>Lx#Zd4PN}`I2Jfla-qypC07X zn{M4m_Pm;>82M!7jvi0^kWYWxVodB>q!{^R<&K_$e6n`Olmqg~${jr(KFFujUicm1 z^SENl7noq>YCt}l5JU$(}*ZJ};_C+;fiCB?`mD|hsG>|#FgecGt8BKgGkWV`u<_hgfS z;y9la^J0AViTW$YA;WJM$^2y2_J)ZFZL{wyg*K3Tb=$77e~Q^6jA z{pP!caWj11Ir0hXuC*V0{~dVWIT;YgB{_n2e>vLzlKfAXO ze8hL*yFS9Z@Sy_-F?>XvUP*uYIKfAy7d|X{Mij$GR&JVndf+2#w^S#FkBCi6e;@JP z{-_h`Bfh8K^^w~q@&A@Q;=ThD@e%hB==M?a+(X=x;?zfOd3xCw|Fo}B9(^svXvlRgK?)6i*c_F)X4UuH_x4W%8m!Pv;4pS>cKwlow2&x$2~kIY9IIHSl#X8-XIgT zk9&P|+vm<(DNgg#Ek{fJ@{{s5FQ)m)@>EBW9^~2fu>kiRfvh(_d(DiqPMpnL<$faF zwnAg~@m%)B#8%m-0jwruYb;M&xo64hZY%drSs7coFUo3fYr*}sj9!RbyN~C3KH+{lVAXRzL0{}+EBDe_?QNAkN+xXUY@0i|U(af9Yr(y5GN1V3 z>aK}oPaxM;>DjP)OJh#stGwuuF~HxcS&b9lzw6K5ZK z^!)eE&fKMNEyVqXTpQiCiJ!}U3LCla5%^5hM($t8!>Do<~5v);%M@vIx*r%9HNJD6jeXX# z+_%fM7yEXR3o+~^<~5v)>^1pPof!5Khv=cbO?mG<>a3K#SLgdF_6H*uV%ST}Yd9C# zYx1Q!G3+G{(L;O7^8P{8nXtXvf1tuX%YDe)_+lS2av_Gj#Jq-ck-a8gsuROr;t)Nw zw)fw!zcbffmj^NIC3eq6_L|(vea3+GE%#l6POrUPtD*rNqqvva>TV@u9HNJP zlY7yjPGPS0nrocC$-U-Wd$HFXxe&u%VqU|!$X=5#)rny*aflw;%RTQf`D`PMB9CYo)o`d8<410-r4d)_zO}YcKYMgkBuPu$MT!lKw2R*W^oeV%SR@qKEc!Z%Wjeu)R)vx%Z@NFZP}! z7h>2;%xgFo*=zEpIx*}e4$(t*WF|XlVWUtA`e*+{sVJ~rr9@^WHJxWn$!uG~>XfO95b>oXYNXdm5_7d|N z&PDc`e5poXYRmp`I_7d|N&PDc`e5pwFZD&)g%{wb$iA410;)bCJC! zw{p)~OR*yERSTWj_#WCg^`v7mAL_Bca_`yI-9GMtJ5l?%r|s%)ANSUssD0ck*R>CO z5{KwPp5Z&r+_M*T{Q24I;moWPnXBB7*R>V<@sbBI zY$fJ3oQrHV`BI%2wi1Wvp{?8(7ujAzx#zEIEB5>)4`SF#%xgFo*=q9f-@c1Z z*h(CthqiL>VbqzJtpjQ+_Z4<+#lFJiK@3}oc@5_xTTQ-HCx)%WA$n*l_cunJiP>s7 z%)N_UTd{XBc@V=^VqU|!$X1gt)rny%aflw;$~}}(r>Ctu`=6#lNAhgqj{I5>`y-PF zF>EE~HJpoVHThDV7`76J=%KCLrx|rt*w)+gwqg%v@*sw-#Jq-ck*y|QsuROj;t)M* zYr%8HWS!uT>nz5V`#ifjjD4QTgBZ3F^BT@YwwipYP7GU#L-f$rvg~V&Iunem{tXfL zl6Gz7-~6UrR*&zub5HADw$3>E#FKXNc2^slxLi!1ltUfpfw{@*KOEBE$xZN=W+(20W>IZT{h zNq-jQu*sL|#2A~1L-cTL>J)r_x8Iyuvzy-jI-u)e?jP>jiv7dMgBZ3F^BT@YwwipY zP7GU#L-f#A?qiNR+5TE=tK~5FD0gke9_8dg3|onL4d)_TO}tXJL?%Ild(8+@swi5Fi&PBGGe5pHt;D>BbCInkU#b(s zR^kvnw3YkHqs|0v)v<|t#=Ev+&v^16hONZBhI5gvCSR%(!&c%DJ+!siZyXMYEBB>$ zZNz^OCMg{#f*w(K^NL(tIe+?H{@;o_*wS19>mCDV$03q95(s*`>3K5V-s$Ma^brR!m0*h(D25r5li@})X4Y$XoSLtC4xz}AtQ^KpG>$6xFGN(@_x zLpZ`#lP}eYVJmTn9<{aLdzry;{YKu_D`wi?cM!u?;t-Co)#OWcV%SO?qKCGY3%+w3 zYU|Lgd0Rj4tcQtVD{%-%*lO~nIx%b|4$(tfqrP$V&#=&e{@Uqb>ppkR(e*GfY$Xoi z2wP3QR40b5#36dr)`IT}q;c(U>v)gn!~48;iS}3G#8#)AJd4(dtUC}tTO&?voq!(N z+U%Rd{cRmwn$O|ociDQF7`77APMnKuHTn4S9MK6|i9__Ltp(pH4vlN{)4Z*#<9fJ! za`75*mw@L@gR;M{d!OU_n;3B=cF#p|HMx~v4>woXR$KcI$sVMuy{!e$o3KXo$JN$| z9oe6Fb+_8Q*7%TyML8EVci_AzGFL` zUx{HWaR^7)YVz^(CeaC7i9__z*0Sumg*w@Lx?)@HUVZ$>`MBamfRvU*0f0eiOJZC*j3|omqIKozwkMHS3Cu}7S(WAD?UcWwD z{i`OYzxIgh9nSM%V%SO?!V$Kbe5pBMcd9FBU#^;GBiFfnW;4&ew} zO}FWdcM0^|ekS%=1-ANEaILHkC2`8=`jn=2N-PS5zQ ze($Sn%3ikU8~*$({=V$cgR6fm@qM07!8*bEVlP{5J{kGZ^TXDtV2lm5b-c%Xf;`C{ z&*d14xHdoLf96nZtIeGw59H(ennhK;Un7RC#I7HT<`a`I)rqlAAP&)^>jc^J8FhO0 z*PVCt{hONXQ9I;L?`BI%2wi1Wvp{cp^>I7AO^?G)UvB{>`r*XHtkTn{#rmKlkczhY_Ohw-bf5vi9{O*!( zhJ}vg*=Uc?;G!#Dt@A4}Y$Xoi2wP3QR40b5#36cUYgF)TE!5Wb-}5;ECc;RstzzEmfMt;8XE z)YgJEVyLZC|C*2M=bxCTwi3fu;t-Co)#T&v--%AxN*tnxwl)jC8=TteADo~A{dLM? zd0YSIj#uiOLkwGqLpZ`#lP}eYVJmTn9@<(iSR;no+FCQ8!*5!;r+ycL7`76JaD=TU zU#b(sR^kvnv^6St-V|!<;=kqN`su5#(fO4awi1VMgsmoDsuROj;t)M*>#E4%9=5KQ z-@hw(wuXCke{Z$>oOX|K*!4Ft;!5nE|8G5S>J-Ga*jBq&pFTO?UtL>>pL1IaKF=w( z)xNv5_@DXyO8eaNb8hPj=di_f%DVZuHokYT&acFuE}62-P!4liCipTm_=d%qYlY$Xoi2wP3QR40b5#36dr)`ELF*y^8O zZETu4DQ|1%-Mi?zi5RvLhj4_gCLh0RS9HQw;t)M*Yr%8GVq2a5`gA^rcRr=AYky+c zN*uxwwwipYP7GU#L-f$rPM;hOiEHaOd0Ss|{|7X##ITh(gd=P<`S?2oq7$|fhv-pT z`;1K?wvP334*Wbj5E3tbnT1%MR%76YE6+D+Hw$=J; z<9E*wTbl*X%0v9%O`7(eyCC-vFdT5;0dIs3Lz%RNJJdDVV1 z6zA|7Ys7kH-upo1-tQhz*=3jiO60aYa1$rz=P9l(_=sZDl6;QaQ_tubpLFC$6xUAs ziQ*7FJ0ACpBX51`xbIZ!Zhc~(T^K7jkA=qX@qhnw`s|};xx3Ay4t(AI!Tto`6zMejTysqs#FfbM?9zXxFWDJ=Z=Xm^YpmR?8`_MJ%nQil2 z+xL*U$ILzDs2QiuIkv&ViJjv&`p08NrcSg=I&L@Sr0eL+<=|i6CL;p+?Yy`a^-hT! zgni{|+D@p#<`#d`@A{+8+VTHeKl|azI-;N>YmrIu?+^ashzkDW()d#k?|a0WA6`Zt zY*%^go$??*QRh8%Cg*wVdH(NiN4rVfiUwYI0?f$U0@{o4PBa+|AkM(@# z&I|H9POj~K8`?!oBUy9fcO3M`?x$T<+q1LV67pcX%0uE(7Cvch|If~rc^>cDc3Q#% zZAUV{k_R+_%NI_5yngzOUDY3vw5vRJkhlc$nEKo9e1Z>lc<93w!~{pj;o;^7*443` z=0_y$l1F(r;SrdxQ#a`DH{fy1A-}!_Jn+9MpS{B$j7bdRW4IE_?ZLRPUGj)tB|L;* zTF3vh^_RRq?%ME!w}V5H9}W*_F)qWESkB@i?UG0N4XIx8^D{`VOqJ(nltbG`eCpx7 z!2>=h%X6!2e!w5G-0kWQX_q`A#zpv1zwg}HDxas21rHppyVLm*znd=x26j4OkJ?GM zTyOnF+9i+jYsDYJFHu(Tqq$z5$IOk^UV?VUY$~-uWTzw`FNpdAweD2tQx@EN^bui%04Y_@FG`BOwpgyYaZIYdh^% z)%+++yUJrv;UWB3&v))@`5B~F8c+7(Vg0s~;X!>nO!|2BwcE|zhxx&F z$s-aT%d;0BRbN^IX=jT%@8kg+2UQgpfzjz7Ag~1#A zJP@OX{H-tjgKF?g<)z0~_jvJ#mXb$V+GSkIW#JJxu8-W-i-+~wj+3WopRu{({2Occ zjhv$WmhCE!Z6!~IpP21Ejr_D152xRL&hnHTpzQVMU0&V%E;1N;Bxu) zjoO#CKHKJfX_q`A`a{lM0&=18c*>h{@WH%+&+fy#jAZ=4@AO3~J6wt7G*8*C@{qhJ z3qR`joyX*JJYILlF|^(MLh29lGA_yAsSUHgMA`nbM`|DWQNQoJxW_zq)xhDqqTM7v z&h6#_Jt>#rN-U@2SXtVoKg!z)kHG%Yej*>2rw{ziQraJ#Z#>KwU^85a<*dI*yW|mZ zei44CQFKi2!2|N(f&ZOLj0eh%OC~PTE_swIseSYd>J}Z1wes`rhn~9bCXI{Z59(oD zV!2(JA8eOABE}_fepx(_=YjSZm-2o^w4c$!_Gf=4W;;~3jj9`8(f&@XJycb+2O zljC*7<<~EnvJCz}+mUCU1CLnF)??DH@_3i{L;OPhzVrB!?tY8s$vD_=BPUPYxcqHd zRNv#>JLz+&NZM5%Zx$ZHFV*V*IdqGk2e?F%ALKgrsexMe(y!}BnQ}$~=#cPuu(Jh(i(=@kg(I8_Vs+e#>^1$6Hc=kY8UO z-^}xP^q3duJQsP^r|?HCr}JDS?JAEqNnFG)eR(X)^Z54M$w^$2etVIPW7L(#X1Ef| zX-jt+*H3{qYo$#wyQjj5FWzM z*FMYJcjtM`T<3zlm>(U_xPHmlZME}8Yt~<+UGj(|Z^@7C`Of7V^E~dn{irv92l{P! zQFmPEkN=)_VC|HHmf84`=`ZCOlBdGY*FMWzm*jaI`J3M-JP?brXI$U+KgX(fPTtk- zU!-02$8?E{@MC+v^Y~~!PZy5b=RlDsKbC*=qUy-9{WU)#X;*n1DgF?CzV=x@bYq^! zP4C(?`TPa3Y0BT$GEZ6W<9ofOTG{YZb{{3}l1G{IobY2k-+BCoJdZ0sc~SEDOERu! z;==qm^6#IlUiIVUmZzC{u6&5ZMfmyJXZiT8c^=>Y+!mT2k>d~60hb%M+pc=mdw;C{ zV7uxMu0I0jxyF6@JnbC3hmIc|hX>lHKfbl&VC~lTZmIcE&a6MmM@xQ)Uuc8x++3RH zvG$o?N$#_eA7#0J$>ztvC5P3PPyML*L)uk;NM4YiuYHzJ`B9$7lTWu39?AHTd0t3= ze1A@}e&}9*HXfP&Ql2gT5PodWcb@u#Jdb1U|8{bJ3m-I{=Rwo~E|+fgQ1!%%Ur^zE z%XaCHXqNB@>@P#Nr{8&D8~8rgzJJ=@@*@)$`s4gVTGex3w43G!+a-_kOyMDZp?=?a z=>9y9gLZi`=`TrsIP)zzK-ZEdXID=@cI$oAAJVS!;QCbj;%lGfQ+}4`fxYW>e(|h7 z?i+be^@N-6G9H=rX(aPL{lfNq=fNN4d0r+tl^X}>K?yYxpS?Uw`Zv#0+d&jb69GcL`{`oruRuEcVdrG50&dxL`q;?i;MU!cMDM=Yo7F}6#8l#fa6qhCAN zT!t&LoUK2kUFGp^;Ssn#ZM>j+{Q(}>Q=9n_734=OXZa!Rl1Fr4%7cCZ|Dt31dU+n$ z@0$71@r>)}FR`4Cr)*bwh=0n$uZRP#wR`ZuUewHw@)vvGH-JYhXL*{5OC<9R`Ss(` zT0id(>=VuWXl8goAM+!Y(|Momsy~-Q#+ceO@T*F!JliV|v@qBN;y&9?)f6GCZ>DQ@LLtzkWR0 z1w1f*M4ov+>yL8wK1!Y=M}h0pc8_r#dj>N<%JRGroZt)BAF-U~M>+F+z0CD#;C z+4ECrmpsac3XjnB=|=hS1N-2DM>4-;)~Cj0xDw0h_`!CShdhspgdge^9jzDUd0>xP z@W9b=@-&+tv7F^c_BkEbr{b4XoBwBPvpf&%9}6D%U-mqgLVt{(b0Z+o(JBy4<1Q=Wbzcc(p(*` z#B!P+Y?nOB|B>b?{ZhmM*T#B$FBm=t4;-C>@gtVA{E&8)2R{cAzogpyKN~N~`vcP8 zf&b;q^L1QHkM423EbS@}`8S zNb=*$-8^6q^P`JLb{soM#<9T92^)iX9@v9Z^W$pcksUu`Im-`eSL4F2j~X*{4KjS0|jC6=@C zBjb-qp3jwqA8HgGQ#Z-;z`l~;f&6GXaiJdOM=WRKhqOz7l;0-fN1#6%TjY6Q&qvJ< z=Q)2iKVmt{4{4V?BJv1)ZrOTio(J}0WPX$j@*|eB`6bJPpEpQ3ed#+--Lwafc&|j} zNApsDe#HALT7G0dZ{WBd$fL1go(J|ZWPX&d>E;1@m>;p6Jjf3=cqjR$HNm*Gk*r{f3PC6C7O!bAA=<iRCmu*e-drPZu7-uOE+ADen*W{v}#$aiOl1OZWW?+f^Ryx8fJp^_{1`B+mn~ z%#RW0^9J(;Br@Y^w%^J;M}DYLbd2>_k3pXKF_QVb!MJ32NW1Ef4@+D^`)!Z=O7g(^ zbo@^H`v#QbU|eFT<9b`#C6C6*!bA8a6$^eW?$KZ1bH=6V+`pjxZhzQ)CEF#Bw)9u> zE1-~EY8T`OxRfuqJT(renu&|Ft32MH`h)zE#tMEcE|_nV`-W?c2hdnJcJ+W6&;IvJcq&FNSYr7<9aM-@gs8Xv(dimk66y~L)s;e2JeF9{Z{z-{J`?^R(>AI_~DGFsAF8Zd9Ype#|I@Yp?UfSKaV6oob@`nr(C*uG^Abf zXtTcr_S?a!c^=sN1$m02>5Qjn-}OfAWP*1AC@`1M;Ke@BjzbAF-US3)|AJ@>n4L5Wk>q(b3s1 z&jb6JfCus;a^gZg%#T=3Kfi5DyX4W}`c(L_uJ1hZ(mW6BH3A+uIu4I)e#CN`A8c27 z$oiQ4Qf>a9QIGtz__Rjzh&9ic63bbhX6M^s@rUq3-J)Y?n>-Kf*8m<#eq?w+ zSIT9$63ba!q+RvLal#{Tz8&8@&jWiYfCrAq;gQXcSk8DzyX4X4d@KA?ZT_D_J=Uk# z=KwtLzv(=Ov6w*La3z-0e#>^12mK*u)F?X2)AIhn9t7Zk{D=zHAF-T1A8Tj)(U9l3 z_C5XdaT#sbMU~?$;8ECgLROt zPuZ^WI6?AM_@&zXKbw2y{Q+t4!2f1u9K*Hr$gIbtUGiwlb5rtTE#G;l$Gi_&a7gka z6PIp(IL{rVUFAW4$l2FE%gcNE{ek=_f63-s)?t3czuWVVj6WJDrg5QPSl4$R@3CIT zyW*K2O@{~Ccl{B|+4v#tsz3M~CeR<_J^X?9xHCVZ3=gwsxDv}*zm;~$qrr8d@Uwco zQS;ULxZoY@%#V)40~%a^#Bw&zW!8mle%>Jbdbe(+&7JZ*@cwh~NX8H6c@S$cKVmtJ z3)`hX+MMTvAM5(g&E4}n@NRPEN96Fx=0_}NJhJoLe@dPPe%>(hnmiA@H=Oy=$?!0H zhAXk0ji=JC`hz?I=ehA7^9$bTt@+`MW8mQCM=WRaT!u&EOc_t5-1zrK%`Ni&!27nD zA7$rx5H&DAVmb4Nw5$G*`=@pwkMSPsWOsgPIy|!Z;m$8@X;*p3b9MTKHu%n+9rONx zEb}AE@Gw2y^GjRWC65L_2MWBe9C@9eM>4gwN1Nk%;O7mU9_v&3 zBN;z#?B;=M%nz3b+f^RS)4|d~hX=HH z<09{Mr9aqWTln#J-+8ddb4$p=AIOivfX_V|F=4*X75mX8e|!JPJRM>5MG5kKM(J&2#vZjBi6BM#GZnj_y*jQI7`(=UD>O~*g9 zy+ZMm_XoT2!~272i{dzbig__U7sb!y7gdQ7KVnBsVf^;H`Zn3iWVA|*_z{QbLHwL{ zYs82jahRUd9Ql@F#IL8GKJnxGnbGzN#ZTV5?8Xo8U8XIH+#tO#aP7qVu*rcKb`tX%&P8^be5*zbJBdT|&`!RW8+Ee%F~m;3Z`-vK z@7pE^V%SN{Yd9C#Y4WWaG3+D`(L+0?97+Lp%9ianzY;Jd^c}ye}MfVtqR4cb{7ZUD+cp*FNiB^V#{Y7yq_0=ay-*Z&kHQ ze8=f$DGt$7IqQPz>zA&j^CR&Z|9D}II7H90lXt8gsd{EAp1I^q#UXkY)z7Y(+#9R88NTnTokiU z+%&K>m918<{rCn6M$Eo<V zTV8&9Dm$te@w0N%?%9y6=Et=#KoWo+d>Hmkj@^8T_Fwe|Wq8Ms#tY{fn~Yd!Lop3j=%@(Dd_ zdS-(QE8G9!o?XE$KB~lPK5?w#^iulMH=jH^`P0=Gl;h+^J?=h`)6Wfb-@Q~Z`yB^_Npo$pXH-x z#A6r_n{V-%088DoPY?QMDHi0OMyS)Xj~|ksZxMg)QS|SK{};b ziyPwqjg2-Gzm6(K{H@$H`ShH3#EWZJe|pVIDp{>vx!pPmUKscPJ-1v>${jrg{r}1T z{I%FMulBWnE=n-sKXdVWXs;^r@mbaJ=N?{Ycp~xVo?PAWch^%XPRD<@JT=j0AIASD zPdJT!tr8>t#OkCN`1BzD51;)Z(KD(T@wal*)CYtXSr$e*~RrN_fA6%Z~S|$XJdKjz+Aw6(YkZN zI1sJKT43GHPhKkZtHj6!V(P}(XDf1HV9I=53lJk0h(q)s7dC(SJ;JA|7`b5Orpd<# zxiIjiF9@G`F^4B^M6EOztUi%X&#D{?lJ&m4PZsU2a4yK+bpMWA*!k|`3*^BHpCk>StomCBAPMlM*nY4YhoE84_6PiThRly}#n#zbx8zLlU1) z4)+ON?QN9(DZ*_$X)gOC_aI%}ZEVUrpTlh&(9Z(6zo}~@_BUmg5+hfMc@1Y@KSF;r z`IchzM=Li?K0S!Vv{;P$tfEG?KX&D6!F@5-1KdZ|wHN!SLN5+t*h`#VNq_oy!d{ba zDTcjPZkl|0XfOA3MU9oQm;1H4_F}(QG9iY&#Jq;Hk0XOEK)Va?|9~qxQ;vd8jdAd*yx(-@)Sk zv#>Y)e(_62p)-3-`rS*a*)sg@aT7GMs&SM$hxXY`=h#hf=uSU;UO=Bl}Sipuj99@6AsB=nJ?9%TGB(b~Q{vKs@ z?RS?f;`f1AH*0liFplY-<+)8t1L zW6ZR2)8x~GG1KIiD@N?BJVei=n#pgh7-OcD_t%3l)8t!$%GhjB<3}oef~!rO@34{;%Mcj$)^W#H2LL<5l1Tz(KD%L@*68g z9Id>+9>me)TZ$1!EAOv|ag0LyV6QmF&9FZXeKX%5u{SxH5F?JnyoR&S|A?c>k19qS zt=u&E^dOEVzg#imXyqY#Ce=)SW5tN0mG{?!IGTJ*G2&?D{q<-ZWp69gm~elL>u4O8 z<>QFG-N}R)aU|w7oPGXB98G>yG2&?Drpc!VaWwhmiV;UE579HJX7U>=MjWlYzaGTV z1a3!=t^<0#|mcwBcpO7~F4xQe%9to`6WmfiOVKerzkAdbr`#Icwj zKL6vL8t>V&DfzHS@tIG)yk_O9#+wfd)_qgW+HEPm^G`~igB6Tp@LT)tyx(T7bAf!0I50pAzY&Mkg99{R0QuzsG1-^w@U{dVW=M~S@y1H|wfv9a{W7k<0qto!BuWszd|&B{$v zk>9M{R*e{bBQ`C5KJ;5z-mQl^6V5lMpSCW^`|Zfz{9gS=48IYF@Pyx9*qkAstt?Uu zzgf9yD)O7P+o}=6Z^Wi0z;6Z5O#0@Vvu+$8?VhLBir*QuaMa$JKpdA>h(mZjv%%)o zgWKhiai zM9*mTq@%YVVkEK-a(T6u_`8>>%! zX0q5dsu*#!@-98zIGP_?HDbh(*tGcbig9c%^#A>u_D5eF{d67Ib z92g)*9En4CB94FA_geWr!?0q+(aKFzQ5@et>n}Qw5F?Jnrp50!ybEl{u^U9sX!Wv> zUX)p_3xh-@)B1RmE zLwF*NCO=$#a$)*h3vvAM7q`>zt5BEEUW}^;e0qQB*NYS*j#f@Be!n4(OCGF?p3&;# z_wSux#PLs`*iy>7^myZFcC~86h$FFS@$+FEqgBu!v5sxr*S)TY@7G2<2k)VF5#v6N zIE3fFUVMD@%nwaczY!mE^ENf&5IvZWtlgGk_|3{qlP@3Ww@!iI4w!pd@%n%EK=RoW z{MKBW_uJZMens<>7=9x*mj3v{Z}Tpxh@Mf!@SByJCZE0Vo3-0g48K`TJ`~RR_p44>$G5k+#Ed6-`{~vVyLz4f)is64NH%&f!;s0|t`-ZO5 zcAkHftkYuuJ3Oh$rw9Hw`Ich%-^!`QZx{VvE_lAD`S0`pvkQ`W3i&_fXL) z{BQhQis64Nrxw3m>i>f0v>|I>>)c*`x=kt?c0Cnlciq|3=ktfiO~iRUtS?UO}?cV z`Dx|U;vZ*_XZYK+a>07c^0U`NyjdqQSEv6WZ|ioyxIo83V%SP-4E-EotI4+%!&WOd zO+KH|)@H#o%VJw+>$o#zz@Crr?`__7_WCkk4h#^(R$^mVY^%w~?<5wTu$4GO4{hxf ze6C+?t24$<&fB{D!B5MaGB7|4TZuzBBCaOiQVd(I+%)xzYp38|tk_n|;l>N{w$Aw) z=iz|?V%SP-42$Dx@~s*%Y$XoS!?;HM`s>dAPdT-}u9vsZ(Ix3%1K-UY;E_jb<nz5#wSGRXhrM|nU27A=R^kwjh^xuB6vI|4H%?sY z?JiGw`Z&T?lehN(z*Z}tfUN~NJmcsSPuj`bU2SY?{jry=#(^lcmA~1!*jCD|+*%iF ziKrm1#kLM;T&K66AGUT@*j9^c`*~t(RB+E$99PHI9&5z;Z~G5j4-+GYiJ6%=7v-?Y zw`#=buf!pGbQ~`DTY&xM%$nWw_SXSj4^RD5K8M%3d0qW%kQlZShj7H$Wb!S=u+_>< zlh0?gwRxd`&MCIl;yTvD*6AZt^>bfh*h*{+i{onYtr{_GB@WR;TcZ`W)z-sPHpB%7`76Ja70{9zNHwpTDfWJ7uRyZclG+% zs$ECc(ZjfwFYYro1;n-e^1Q9nAKXCK!^E(a zID{kOYVz^eBsyU$aflw;+UYko1=u>)V{e`A_3G-e9_|#(uUJd?O|@q*jThzP>iU}) zaV2)ozJ7+dn%v5-hs$5^=WwyDHs>_9ety^*$==)WU-mh7sIAktd4Aa1k$u-EZmX@E z8iRRTKYo_|%|0=5nAmc&cpNtQ`0s;6C&nh?5Is6JUG2}|1CN^(zlq3y2MZnetY+~m z^R}KhYb{+56T?>G5RT}tCf`yFTdmwQ`TD)udezF?I(4hOtq<+^Yn@+-VJopQERL(m zw`#<&l{iEXZ7pB93T)jZZ|fB^?eFx7VJmS6N5s|STZ&<;m7Au1ac!=G9ByopxApVR zdYBlt5*x$fxSD*cMhshtL-a7Nor1qhNc*dQhGlJMR)6_4n<-Yy3zpYa@?P2R{muP?8M{r`RQ%;sXj)|=*AKNN8u~o~d#h)LHYxB~6 z{k6ZXjScg*F2Bpx!^E(a*ckdb!d8=yKhF`Ju$4GO4{a@9v+}mKUy-+UbzBcO3vw9i zrdi%WBzmrGEw2h&d#n*Jdh9TL-b4&riD^I1MLBHp@%NWRC&nh?5Ir25qJkVg*;!!7`76JaKt!l@-4-%)yhqi&u6r?^Hu*iTx{zc{l3KXjq^EN zeaCh>zY@b%Vq;hwSCfyQH;GQzN*tnxwwAA7QCsa^eR_{MXV(kX*XK>du$4H3BjRfE zEyb|a%1u+hxRwj<)r;e5%B6lo#i1y~BAvOblCzLpUO?Cf`yFTdmwQ^^0q>VBLhc`farwZujV~r#jDviD4_T zF)WU&$+v36u$4GO591oGu&ox?Rw*A>_xUX`Y$Xoih`5^k%0C~D7O!XyTYsJYlDw_1 zEyS>u*ggAl54M`z%B>Mgv7(W_-zUYF<4>G>?9rzjH+PQZ?O2az5ys2Vb8cVX@00en zZ+efQwi25Miq{Dy z->MN~oj@F-hwFr9!TL|WSLy4pdJoen|3m-Qkl*>9vEIvcT~7>Ki9VL{cbQZY$Y~^#c?(HR*e|85{Kxatx>^qv(UIk zug}|h$YIB;t;DdEID{kOYVs|`u+_>t>Vxu77(&3|omqI3lhl-%<=)t=u&Ai)*yPIc($b$V>CK{_4;<+Fyxb zE3q*wj;qPHYQ(UWI7E-ewV$m4Ys9EWe|_TNgKr}bV%SQ|YdHJjfw-D{OEGMTTTQ;D z7`9ruY3iH91seXbuy@R$^mV z99NTX)res$aflwqwYdsx9p5}}>*t@Cr?wKqR^kwjh^xuR-@g-`u$4GO4{eS5+UlQR z*X*aKg3popUBiB3Q+ZlGu5Vhpr+ycL z7`74{!{Qt^`Bse>wi1Wv(YO}ezlPe{d3oN}PhWM7&acFoaSEnxSF>OA+GkDAzwTYSCd+~&sOogsadd= zD7Mw^)rWfA(-}uY&$X>lK@Jz&YTsQd@A>?QYxzt5`rA~?=Ja9Cc9T3?-AEU&px1Y4l!&c4&jLYYVs|`u+_>< zlds>?)@X%ob;hPw=W}?iX^-mKpBT0h8^hwbntc4X=%N$05{Kxat({e2>rQ!F550OL z&0%8LN*uxwaW(mtV%Tcsrm0_Cn+2b%hvsl|_q?r@QG34_F>EC^hQ)C;`Bse>wi1Wv zVO*m=w)*E+8=FR6lee|=?p<`R#8v4dzi)(X>d|Y31{|7X##ITjv7#8QS z$;aOz5S_4kY+gM1Ndu-PGyvyvgEW=(&z-^Tw68b#Tj-j%&~TB32-W zd+Nd1w0Z3Ea>4gyr_VlmmiuK{)WP%M=y_t>*b3M-*yB4rmYIeMauv+d4@bH^C;cvYYM_YK1ALZ8{JNY5)Dv!%0F5;I|oB!v?w|ntOa|2vD zGJi2oscG-Suc?3T%Fo@V{*ZQ+$7h9y@MAsSIr@s92lAsSe-j5DIG7(_K5MP|&HMgc z^P?f{l1KYo;UWCe+Ww#AZ}@p6`SFe3`SJO^|51DR{p;RNf3RKhXj~vXgrA>5dSztf zuU+HE6yHnMyDL#Nzm{*ZQ+$0fo;_@Q3WG4!3hKYn`o0`0dQS=+L|&>w5xbNjw0 z9^&*{X_q|O>@UKvhy$(-eKpVHsrd)&rE&4}+u1MsRej^%-LB&p+f^RYUmL=&ACJKc z^E}@Bim@b55tp)OT;KjDC)5u5#$Poq?d&*qzT|0Oe;L0h&*R6NteMO&h(*U4PiYfk zv&-<+wZ~6xDvyS=tNxJuBtO>moufTBI(8<4Aji7#IXdi@yRIX@PnWe8M3@`6wBA;y zy!FMG9B~u-OIzAi9v_uF6@H@5dumqmJoex0=n8y+w#Vdezetr_macV1r8;G&Hz*Hj zS9vUyxCHjwp?&i_<}Nrs;eq@ZyrXx&efajvDwn@vx~_xT(ysFOyzmfyUPs7<@|*KK z##{3f9uR3})JfvOnXq+QFgkL`%(ZP8hqYr**AI7D#%;pzzfX?*~ z9#yWe>U4{&KZ{-7>!+2*4^dTZmXg~mhLC6C4z#UJ7q*7cpIo|N8W z^E&$L4NG3JJNw5}XP%=kAiL)LH}3cMd%yg3`h)E%j|+u|@S}d;dF+flk8h2Ac^P=% z|B%B2I>_bf#}BQHHty4Y+mLpZ#}_0n;uq8{I))C(^O!OHhsivbjO)(&gL=Sa=L_N#_RGtZW?*N#wBvrr{sXTe>(9Sm2H2vQ2T9L+EpGGOMZx7*q-kk zt)J)d;G*+)MQ$WfzQ45^ci2X{vz!v5B3+~=WCzkBR6;F2ja5hoS*In z9!bBAx_O`;xNP{5L-(7p{!=#3NxS6Hko9_7_@Q3W(H!jN0Uyj8_^jqfQ|5i}qAyZ? z!SYm*{1A9%u*c;(z(;sXu^m7?5fYyvkq#=yh46pUUZb@JA;&gKR)!-bvL2i zq~A(^ArHu z$wKg2Id#eyH5yYf5^|M8ICS2mq-9qqgRh~;dalXjJd+%J${0fppJ`I~tj-`V<{ zWL!_;;@r;}2UH!d#B#b0YG>}F8uDC>e(A?!DapK#wmZ&!18qvV3|C^giuPM+mps~%FXYF%zVqOh^E@ES z{?WY2^3;3*iSGWwcFCi0w&bbsLye+i>bLwnz$MCzW5%VM2isL1pOm;8_ zJ^Y>4Z!^zN+Z<1YUl9jfYe#t=AD_1O*AbH>E>3?T2h_Xm;2Uc*_87P4LDH`JgZ(A& z`RUNLc^;1*^8%gcBFWQiznxb;rnd3XuicydrJe0BypIYTPp99M=ke{ilasih?Z_EV z;RhV>%jwe&dgI$mlPxZp`>3|$1^J~l{XYj!=;nd@${+7JUGt;t+~2zSap;wUweN3q ztnrX`8JEUqq`wG1KZEqj$h-19-a6;Dy_p|nCoXP&y!0(+1Q>ZOS|OJmfz`@e*P%W?ZIQ4jWs_yjz6di zT)w#8r0QCak0=ket30H?HiQrB`p#oB^E~eT*84O+B2Rvtbjm~3xtrR3b~`(c$$c&P z`Pygs^!Md?j5W?qo}VH|nsWcbag6mQJ^G^B5B4vaKQj0G4c^}dj$@0D$n*HvPA{_j zaGtM&1M_2tmwvi>=+C}v&p$Hvw+&erwFB?>J8#PKczVxs6CUtE*%{aA3vgL~gAJ=w ze!Z>Tzhu{A(tqd|*7cpouSokBuj6x%haY^s=10@<2im7UZoXulH-7a$$~Jyv`fZz^ zdk8=3_npVTk>~NypKeb4f&3_Y=9f!9wE4aVUVWq9XR}@Qm&WI0{1AS=_E|o3bDqc6 zuYFMSqmzjX{qddUQ}(^NyY3|*b)aqyHME`=|W{BS;hA$M?jy>M&NVp zp=V-;x7tT6SrrcHYvj==jmdK3`vy>TL@@pC4F0eqEl&i(c`EBreIi z(D4WAkjo}FZ&SPWq>U9|B`lBtslONmjorkW^^VsmHO>}-~I?pYsBjIxDpQ{J{ zkL4-bC6C6wsb2D9UEg`i&Hi;V>U89LO~_mNBGr{yuS>hiV>gM5_$965|2cT4p9k8G z?(Y5^$hc&9NW0|Gu8Ti}pPxZ`rFpNP2lAugd9IzikCJxDqwxmeA^iIB7#qv;z@FLg z1&+wc5BS3MM=YoPmhF;9dz!>W_@&zXKO4*PJg}cNc;J60!^1QUS7JFGPuVVcG;DMv*@W|#zENA&4?UF}(cgc@H9#gK& z^T7Ve;DP^5CoUEfFdVMLayrkkUGiw`DE<(Bs8Mu8x8`|ZZ)0#s@?$K0{(%_bFfPND zSWfel?UG0P)#4BFOIq9ib4rirbl7(oJkWM@UpEhEVt&MOI=`@8<-zeo{DOK#$KwCa z`vX1)4;;-qjYl>=VmZx^wzR7}b`gJwUs7%UpMyW_#UsrPaOvFJ&BHWxkLzt|mpmG; z6CQ!%$H;g5JdhvFdyEG-xcL$PYR`kDUGiwZR(J?M)Ga!O?)URRel)++%>%laA2C!O zY?nM5GOp7vs8@7M|6ZO4_Mp}L$i&6DdK=@V^`;SV1HQf!2igJ zi*Z2B;Yuv0&x0D$E_t*iPUMFgMaRg+c^=ri6&#Qso&WBRi*XsQ#B%!mAhxSK-kAD> z{E9f>+So059@tkEJd*k4TirZRhxrl9+4v#tl1F17iHrCJ^@@(gx8-?Y&r|Tg5#4V* z(7x-BSkCf8+9i+n>xGB#OSSob4vpq{U_Vmu!2hNb7vlhZ!ySVwD0;Omb3hjb~P^K5g3>9_j~bh=9dhQlpTI% z=9dhQhRpZ$i`DCmIzRIBNbLy5*OjuyLBs_eutk&GQVWjLB=KH z4{4V?8hfWa$j@5rjhc({JU$-p@5uZpf7kLfn;)^9<%hJZJmkKmE&O`7Zl%#Rc^=qr z5j^m}oEgWg=5QsJv-u@^Kld8(hw!s{y;1wec^=pc5gd~I_@?;-8r=Md<#haLNW1h$ zdk5hWct1Dx(>xFCQwSc&k50k(5zE>5A?=b!V^85B{9r@T(OQz{fjtDl14r3e2W9gk zmb3hjc9qAjDG&N3)#m?MzC6zZ`}=_h{+FG&SWKXAxDw0h{L+wimB-HF58;OzMaTG0 zdhm$%-UEjuKOBERk8z24ympD@hqS9aWL_b^A`Z9~jpun_Up(+g^26yb)Rl6H?{Dw2 z`K2N4Di7wV@MB%yxp8TphdaNN1MgoNnLKUFI+6T*?X$d7@O&)Ek8fMQwV0rGW<1TT zPa74<58=o5eCPHzdif*GQRGMS4&wnG#-*DF+httZ(qHKpU;8YddWD}yk{_8oH7?ye z*sk*6_#u8_d%kn0$8!hlk%pMyXlCN#`XiRp@uQs?KiYDCO@661|IhKO^8Ud7W#EDT zoy=YhS&z#$nwRH5f0ZMv7C+{ZE06|$oxir zX>I?{)^&Lv*!K%OlKjZzDKw>AhAXk0%`e$`zbfN;V16{N&-1{ZT;PGD?C?PQu0LWq z&5wq(tNy47kHC3<=!QHG?3V=|_+S2>^;_crhQpOuPUn}lw5vQgzX(6nC_1KpEzbjc zS%E_`eiYS1=5wI7tc%Exb$#c-fnGeE`^vz1pY5tY_<2;|=RoZ> zd+|u~13oD`_xlzTSk)bucINY_hCKHmKc631-uS0KKaz1hvtBnY-8|SX{n2=njAMbH zM~$rG_Xl#N>CAKF-YY-YE_t+HCF`KT=RvJ0c^=qnhy6Bkc%XgPAF-T{>ui@i8rupF z@e6J6ofkilj|=v}0gogqg?RZ zA(pexU!+~~Xvp(@@?(3x^T^NgJg|QZcpyI_XFWz8%#T=3d9YpOF;((Z__40TsA*qIn9rTv`Zds`JMbyZT_Ew59j>>Y4E`Ra=~-$__B?s86FLJ?m>RB zY~c~D-OC>-2joY`8Bd|eay2u4WS5PwkLC;VJg`3ocpyJIPF!eH%4N6` z%jxI0ZE2T08ncCm@MB%yxzi(0v3CXYBPzIWh~+Flq+Rl8A0<45pRawEk8hIq2lkcF z{3sYdVmbT1wzNwgjaiZ(!jJ9w&Vw7}d0@{6=0_*9UU%~&mRq9Vmy&js$2-Iy!q3+} z%cne%?=RSof%(yNp7XN~^COni{AftK%HvS+hwx)v-?_Xz&jWiUFh4rZIEMCJf5dW@ zAJQ&)w2u)U!q3+}%cB?P^98ONZ*xC~cfIn7hHOCIf`B|n6puYH!6H^}<~@Bi2Q$nY>O!^%Wx%@)8}JsS9!ckcnCjV`z#-N zI?n^|n`eHMf7#8$xC~cfIU7HuUF9L;7x}S0-?{ylJP-I>^W!JRBby(woaRR(JFd@^ zJQaSv_E|o*R=&UB8uO#`{cawt2d**J^=U)eReu~TJOan{<}Z5r!`7$KFN_B`xcSk= zL)s;e_7TEE_@Qpm(SD)dA6S2sf6&bXx>7FP{$RW0(b!*j2)`l@xYp@0&*9zU%#U&= zF0MafIm-`emps~>Z^bYDcuf6OJ}!7KIP;_YL-U7mfY#wkET{dJ?J5uEsqpK^qxIW7 z54^J*Jd*p{hl~ew8JGC`LDL?!c`lQu?dfS;$gdxdsgLJ*;CN&ioqQ`#Q_dDTF(1}tgqlo zCN9#h@{s#l@vaEP9DKJCblDjt4%T(P6Z%V&7fW7Q8|qkL+_GnZ_lI7H8)`W7{{Wnh}(MSHzX zahRS19r>Ax7wz?7#UXm89W(66FI2qn73V4r(KB!Pg^nK4refsxMD!rPO@5(b zG`dXV3iCy`?0x0UzTqxmiG1wf4z$#1@Y+Rbmgf0_&w$N8<8 z7vr-pegpCNeR6Gg?&(=o9Ot*<5Iu2zoBXI^G`dXV2Hzfdvq+sgavL4KPb7AZ!4TX}yy$ZyM&NHOx; z%KPil{Fe7Bp~i~jH{WaS<~QDJP6mqO{8r41@!1zYE0W)QUq17Dtw-LnOzv5P zL~{Q!5SPbm@)R4pI1koZEBQ6M{(feI3tjmt>z?_?4=N7Pv#fP#b%%$zZeFf<`00+~ z5Iv)3OsakSFVwTK;_8CU6^H0K?66nWPEbA5YDWmZ=IT9bH{3=2rfJPJ(>ukie-1nB zO|{7*l%G*M{P+}KbM^7H1I}T2e?7==^Up%XrOM|On-+h(mXDra`~0PBcah@jkGx88 zh@KgjURPtRyNxcaflw~H}`8moos#|H2c^S;=OMQ*WcWSp*z1_1}RSSJ6~R$ z-zr}vO7ok)+1aND`F+eGzrH1vT&@`TZRP#-AityC&)7Sa+*mR4+sgavL4NN&d9LW0 zR=ezw*$GB||8cuV_A(7AD$4KCcb*reQZs5xzS~ab7UcJCPhG-!M%DTBAir66U|?a5 zK0$upz1b#tKA!Oi`TdW{XNg^l6eGW_TprlD-v3ZjQGS~r7S@Q7-^A2` zv(GN%_j?X~R`@ScjQqB8)8x~$s`H!s%b?{I$#3o(vpVPZ5ubWk#+Bt&V&pfmdEFmB zJHdvSu1-|Ox)OUfNR1^NB-qvwnKjM~Xx zzeDsKUt52{PbM^ezwvi_?47o_utuLCzyI*B_1<7iQslD>`F+i%+lu@m#mH|fH*G#W zt2)0Ud0z}#o@jnwA6MXhM96RKMf8!!b{xfiM9_zWI4-Xer&rRSK0C3lFuCQ5s|$Xw z*sd4ldR`Mc=x;Vd_4J$`!Mroq!{;MR!;4HKA6WhkHxre6l!Gipgwoj z`?GsABKF(^%8fntfMTn}h&?f{;q3D@V*lW2n=VadmsjUaJ2%0I{o|)!D&?lhr)Ttx zzf?bb{+B3UBS!3rO^croV!!mzzweXEPE(B7Te)fS@mX`71vT0d_Z-E`m))S)w8VTo zpA8}Q=FdnmVsGW9$;XGW=U!c?5fb}GEW~}c5PQ6P68#?gZb1hQV#J;}y^{X)aaxuA z{)4CN!f~iZjMx*0@ImY!J?8E`QrT&>cien&g3o;N^x8!1s+{#%M?O+~$LS9$4&kFQ zlYPxlCnRR*uiT@|?XTFQjO`L5X2iUPb8*c0Yz1R=iqo;0<)+D}2mN)czwsV^pjLV; z#faIKw`#d6^65d$cHE!x(-fn>TDhw6+V$+@1vT1&{z_b3aJgdB;`a~wtNAlhjQ(on zrpafQ-gDiMezyfR;;?porYB>y_nAVH2eL<;n+Mn*jfv*L_tuP`cFP`>$OG(8iO*87 zM_fh zE8n12{mT%!QY^;3R#79{Pu@It?kPJS;NI;H44@wR2lq~O`^WzA-l@=ogBbmTIK7ho^!Xe8 zV~gL;r=BV?`Ui1{9`ujLqle@^wYz`3^Qo`tcurkDKIk8VTW_QH&0B2nkp!cEEFRck z8SC@x(}Vso^znwsPph4@?gtZ${&DVE9|_hodd9wv{0znD9~04o{;_n!--ulcYw!4W z`iuenj5-tPAKcT|?H}0Fmu(ZHe-QH;&b~ZC z|2XKSyq_DW5~F_*hv-57FuCsjVdaK=e9%8kYM{3J4|h-2GUy*MmUV$i`1GKEoO#G~ z;-6`?()LRejQ+9fk}>rgHTm?Qf0+CX#poYaPAz^vqkkO#nTuq7zECmxhn4r&gZ?q= z){`ZFM{3*u^{WX+|JZiDM|92XbWX1p?emWTdv(AeqB2UQf? z)bOPb=HXgyWct2^;NGYRI^T;&9xTyLwEZM_CtqG z9K`4=#Oamvr;j80%HoqDRo=qtn_dj4H=3ENKl3V*Y+&o1cLcFW}d zOfdS&)Hj?i<^A=bubBJ{#po+m-d|7JS15&%QFa z-$uvIj4Qi+<(}x%KHGGrK-PUorU^iqTiB+&tvV&DF8580$sachOg@+)!+nx35^fMKxmd z6=Ks8&{qn+t57(v#Er18j2xQpE9>re9&>zv7=48}gd_UOTOarm^;C(`SBOLOtd4!< zklzkzUzvK@k=j?*pMT7R`ijZVs1c*D5Qq5Ze{)~4e2Z$t=qtn_{?RqqO3o{tcs`kW zQu;Ttm{+hrZQLi8-LT{pyOReo`U)|x;p`jl&{zI>@3z!aB}QK%4$*_Y(!Tj$l7q_? z4?jJnX62^I$7lHekoP5UmQ>Z<&EA46ipZd#Puv<=GHm)prQbt?tt^f#`dry&v{9HL z2=N&e+9c#_KbN4QX-z;)LeehrQ-h?8ECXt66cksU2DDYwHm)HqPa*Q%f7SV)b6-{6 zTeo|d?=v&?o9X)ZtyAaz>wnL^b*tX%d$!xV`=x)n=E%Zwb$8;RALbbMgOgu=uar0Y z!Tn&*{kN0rTBG=eKe zbBs88|J`qta&;5*gE%t#HHr~OR^IFfadhd`tEAr1INIxq&s}F83KY^Aab!AYiV;Uv zZr(zE7)QMo#!>I2Y8$ z#F3Sor;tvFqrK^*OK4dZc*V#JY^(@R*N`*J+8 zcFDTLh$CWS6XwG>YRg&?I45;H>bTEwRvrAsGx@wU?#{)1?Tzm|OZz!7;)pnf7vkun zzkNFWbchj0#3_CdN8dYw-^*H5j5xA#^Au`-#8Gz0c^XGAx#Z;nDZOMs>bAs_$_uk9d!v|t>`VXqHdDYyjt|t#-#3nJ1 zVIPVG#OA{v_-gv;5F<8;Q~V${?_B>Sxpypfe)Zq~nSTz3*!+*1e^U1zC3jnXc1CRe zbk#?sudMDKJHL=)jB$5u_zL@wQVLxcV$SuZk|H=FgCLfhrctJKRaXY z))1T7{j0Hg$9vwX&w#{;O=4phzE;HM0k7BhE*)aTCUJ@%#O8lovW-3uy!EaJ9gg!5 zo5#QSfqXVsS0T+1o0n|+4Y6O{J#M>S=NPeh+YQ@Zr49sy{2(^XevM+prj?twkRQaR z+0S;bdG%P%0kQd_kNhtAx)7T)Pk*7S8UdHv)G$uWi4yyqLMK33GZx_i~?gL917yz~W6kaA-b(jKvC_G=U)Hm$tb z4`S2xQmJ>g`;e2rrLp69ohczz;$t!p-%v+k5L&VT8;(zl~N3+lLTwJ4-o2SNWlw21$V;lG1|NOP7 z9eWR{w&Oh~@HdVq+G+MzhcKTwJ4-o2RC097fg+@vd>?jTB>b?xEFdw3-e5 z|BlsuWbL1(Yi#dZy~fx6_Cq=z6XP0*?aJfrX!e<&i*~ef^VHOi?a25VyT*a5V)o`@ zZB4wN#wv>L-p=P6BT$rAo-JVz;)eOC=b%IUr$4<$G0U(I`FYWo&hf@58@s;rj^B9Y z<|*WdW7qJb;j!!F=?xd2vv%G2b>CfuR{j68m1z}u-l^B>+zrqfT4gtFU0U@&zVf&l ze!8mF3%=UZ{zHs;H?c*0xbL73diEV+^g-ejKj?#IpD9Kkv~u$l>w~MESwHfuG7?vj zXKBP(`^KtnpWFW}N0JFKbR*_5>_a@En`hr4hHk_uexRG#XNsYlm7Av!AM`2cHjLcM zmZRI;HC5ei+iicHgAqeFVq+QC&9m}v(8hR^_X_Bz ztNx*?o3Sti-Rw6*A)ddlbBz8~3HO4LS#|La23k#ga_iD6i_Bd$#zwR%zTcW_b=B6T z)o|Hbopv_HE_^1k^fr5z;k&)vZhxNkH)8ZbV(WqNG2gTA5O4FFLlvj^xp&7CJI9?! zJ~N7M_>(IYr}&w?ag~lknBQ*OpiaQs*P_cOl z>%8%GXLbML8=MoKr+DU`OBJX1nc42$UcH&(nR{+koZ^S~)+}Y-8~R+td-uGQRpayZ ztDmgzVu=x-#3?*+kMQg}#E4Jg6hDa1M||v|^4Z*sV#KGFo2O7bAU>_$Ma76uD{uCL z_%!>~iV>ey-s}hQY4&RrBR;J>#m`yYKmO#i^UpEe+cL$7Pb+WsgZQ+1GsTEcD{uCr z@tHFB3&p3ahT~`NpQ`cs{WI^U?}CUCpTx#8+^-Rzo_&WH@kyNG2l4sMZ~U>wCo$ra zIK>a*)9PJRjQF&2^Aw76#HZP>R*d+x@@7AXPqSa681ZT4DSpoCe)JFjLF1Dc@kwl6 z!tI6lw0bkeh)*jwPa%CYK2zt4bsX~T|se;`3Rn$Y(||;?v5_Q%E<&r`5Zt81ZT4&3+J{X1`i7;?v5T{UAQgevM+prt6pyL$?rjb^{6S0 zPh!L;v9XNDr)S?G#%IgKDSq(T^5^e-t?*e?jJUFL^Aw6t#MR|Y3g!M;Unf)5Yh$|~MPa!^ttLfkI z@7(9Qye`Dm6+0ZM`qNX$58}%7%oHQ8tejrL*QIe4xt}g4u7)qE#?_y-X7&39#E2_m zV;Sa&xbo~f#E2{66hDZo8?I;xpT*9nt~o8A`ysAA_-EggI5AHl{V`s>{eDx|6_%?N zBd)Bx*$?8%?AItpTv>UuAH>!8MeAgqHrM5KA+CPc-%jRc! z_2M7?SmSE^ikC~fsGFc4#MQQ6{IbRsG2)8YyoB{fT$%kE#fU2_H%}pb5LbV`&3|cJ z@wyOKUwZr-mKRs5r;97Wd0c6EGats)a8t%BFRm6&ug29apZp_@D`LbIv9S#6kGS&e zJH&`9;uJrKt4lX>orOik7_Y3{JcZg3arNx=kJtGXG2)8Yyo9d{ab@;v6eF&z+&qQ& zAg+x6oMOb4l{fprcx8HKiV;^<-t32Qm8HzD66Wnwe^iaDh3PlxI6#cJA~u#`{SjB5 zeTNuvMV#UXadprm&KEw5ovWU|cYg0fTphjo?&Nt0;_8HNJXpqo)rt{UR&Iq1;j?R$XgPfuY!ALyE%nPSA1mD5YOy%<;h$anwr zJ{XECR}K7N-ah}rYFz!VL;0Qmu`y!A6|u1l^F&;E_8nrx6>*9m#MNJq-y`}jcK-1V zm*=sBxO&Pb{xn&C#MMuBxLogp#E2{66h4S6vtOeaab@M^DHIQgt25qqrSPBY^12XL z`|k8%x!z_!7_UsvOfllh%A5T#uG*3Hm>c8DyAKZkuo_qQ9L2vUbl<%cjJP5;mhrgq z>^sDWE8-MCh^t#xy++2F#m*PszF)36;_5x0+*abmJcZg3akb4J`(0mHu2zh=vhrp> zh%2*SqZn~z<;{K&SKD0iXyHHC<#i#h&VAr&x!z_!h%3|6eaBVW3voqkUc&9AaTWP| zsxhuyMevia)qiD?D;(dG+`;{K@pJum?s$UEuZR&>#3?*6UU~K%V#F13iXX()YroJI zK8uR~^p{60W{+VL5qm?)FVLSFCo2Yj%Gin z=b|00yx9-h(fDVI(T-N$?1$~xj(i@M(2m+4dskQ6asLDF&~_w7I}#hqcsqLb9b&X2 zaf%clg=&I6}F3t(T-Mbo&)6D&?q+qloaS9)_qw&uaqaCf>JT{k^kE%GTx4!eTNwBNSxva?fB*6Cay1R7Zsx&t=v3? zbVECu{ap9%T_2MBL_7ZTiF=9vWGnh_hrc$|6m z9b#xkoZ<(XnSG`hnpwGdYSJwG{E9WpbClq@X#TU+IJ0#r!KGNE5}FYk!?)+o1T4#7=4pC#Si+X*=LF|o>{qh3h~kLEYdfhv3~8kesDY+ z)91~F_g6LB{_36eJM6^JjMx~)HS_E{#L$d5#Sb(y`%E!3vvTv)q*)f}n{myoZ_Zs& z)of~RLci}w49$p*VO%rMzC#Snh*SJPGqcYWLo+KkPfeN)KO63wan0;`bK&Dv&AxW` zE;>IWhGxXZFs_+r-yw!(#3_EDnb~KGp_!GNrzXwXpIoVC7H9LHsA~51H=Uy688I{? zHimJ{Jo^qYG$T&&1I^4nQw+_l+&ndDmVKc?v+#3(z6o@hyux5G>nEzl^v(9Zt=y*m9&4`U*TryS3-C?<F^p^G*>{Mc8F7jqXlC~A9w6MH z8F7jq&NbTqw&{IyX}+r2=yTkb^}Lxy?g4n-40la?x843?oo0T^6&P_=Ese*SXWt=4 zoDuu`P@h7ancaQxyt!h{Y_2i<%GRY>7I_Yc$C+t1b;H)BSwHg5F|L`-&-(AFYWDg| zxK7mA7%}=LvGtXB-}LM|#ORyEDSps5&EBo&BiwKgAWre4_kf$j_ka`5Uh7tj55GZ! z54>~CK2X){vbEdm^CmGgBTnInXLQfLLk!J`Q~W?Pv(FSmGb=Ywp>a_)i_B?LHCy^> zRkLp$@;#lO5koU#V;GM!&%Q$p&4^R{Kr^#<-%}TE(2O|64>ikf-1InGxUH($m8aW! zp~TRPIE5qP%(L$hLo?zOKhVtVGsV!%%FR<#oDC!Mv(z}7y|${^JG|#jVrWKe4C8U; z*>{Mc8F7jqXlC}AVrXXN=BY`ucI2I7p;>r_#__EE_f^e~{?vMX-Xw-*#KthLnP=Z2 zhGxVmexRAzXNsYjm7AwV%^Le=vu1;@)o6C$wK|?1EjZWAD<{hkNAyje>ufv3xn|4o zlWV5-PBX!|W?D`*;WpIq>^_LIY*5u~@zeIaNes=1jbWH0H1q5`#L$d5#Sb(yd-r_} z;Rel!Q~Xe~VdH(XInJivTh(k!e%@?1e7<>j!sh|Qt?0Y5_;)bzUkR_h|xER{e5U$MBg;KO@Bu3->_26?AdAJ%B@SYVdNeVk2BM3 zSo2+~%YSgH#uPE)jM&;e9%r6?hZu22oZ<&@X7-t4+ykuKJcZ(w_kez+Z=Q7a`j>{E zHchj^`>JvF%9D4|=S^a0Mr;h@ntApeVrWL3;s=_UeWnq= z*%9x$|MlcS49$pn4Es>KVt(e?cZi`Gaf%;kX7-t4XlCW+Da41G4I}q}xMp_Woc?fC zv&+2aO=4(9Yz*U?dG;M*Xhxjk2b!6^dlw|!pc!$BA8OWa&@4QowehU?k*a1lc%N?) zLo?zOj)*hQzC#Snh*SJPGqcYWLo+KkPfc;wzBN48K%9j&v%Wd?(W+(_dY^9+Lo;Gy z7>_g0zC#Snh*SJPGqcYWLo+KkPfeQj8^*J+W)^1?_f|FYKQ|Y%Gy6<2G_!K^)TCLv`R~7)HCw7`cFETsb{$y|Lo;F?!#>0japu`~h@lyA ziXUiZ_L*X6X65E7#D{U#j(krl@0$s6HusIHW+$EcGS!S2nh_hrxMrSxhZvd>r}%+p zW}hjBW>#*Vnl#HI?_g6k8*En{&p0P5m~2_|vti`<8}IrL4Gw2^4_Nh`YMfcC8A6=d zZ-zqc`uq4Sna|H6f0q;2%;sm=4s~&6nhU9TOffXGa`V)r+3?fh_tvSJ4fm+_ z&854)uAjpZLo;Gy7}w0R?+`;X;uJs7%$aj9?n%O(p{{5<&J#@!k zA59j-(2SVJun%#>ebck=5JNNK6hF|+>@&sC%*xGEh!6YbaK(Mo#c7?tk?03 z7@83q!?(MahC8LFnC}!&hGlovo)t2F*GAK zhH=e2`wlTQBTn%H&CEVi49%?EJT+-HjLgp(G}FH`T(x^uvv1BkR@a;&hGxXZFs_+r z-yw!(#3_EDnb~KGp_!GNr$)^ppD8tHrtdYz@2cK6FZv%>C&4`U*TrGc@R)|6R_5s(mxN^07KUBZg+g#xNddo_&WHnh~e?fo5i( zDTZcNZk`%7i@euJ)vW!qYMgES_}}V1fEbz)8^gF}o_&WHnh~e?fo5i(DTZcNZl0Pn zYe(j^$(rq2jk8tfZKHjY7@83q!?Rf{unh~dP#67^X?+`;X;uJs7%yv1G2A$HN$(_@Mvc5H5Rt7-Z!n) z3?a_!H$$Oz{eAsBVEDzAYG&^o`?uG{nQ20_rD&GjzEaKXyZ=iM*t+7Z{iT&^W^p#G zc}AbU_1W4Fi4kYSj6LjQebck=5aS*|oZ<)f0JF~&;~rq;<|)L7^RsrOZx-ME56v{} zo#Vu}t9|o5_kLaH8pP0y*ciq&^Xxms(2O|64>U9TOffXGa`V)v*_Xp{7T3(~0ki*6 z)olCK-`8h!VrWKe4C9)4_8nqqMx5dYnwh;@t5LW?GvX9K8fV|ybj>DyUe)ZB_wTNK zlNg#2r*Oo5)3fgoLo?zOKhVtVGsV!%%FR<#ob?AQ)y(?l)cvdXfXR)Pw}Tj(5gWsJ zoO$*gVrWL3;s=_UeWn;rbjv49$p*VO%rM zzC#Snh*SJPGqcYWLo+KkPmP*2+yhePv=h5k=m zg&Q;@PVvKgK(^vIGtCCSsP@e*`FXP+xo_fm^YDc4pUwWJPBXvd3XC|bmd4}Ev+oci z&WQbes81oz%x=^EmI>cK8%FK{am{RgHos%FZyHBKTa0Fr_h)g{1s-s}B2({#}?$=REe^5A1$}MVBF0?)4EXcYSJ`ZMGR3JN=fgPu_C#@so!i z{%0K=-S3psJ$vg@cYXCy z*Va4yf5x>fEu-DKPaPXuaBV&J7eB~j5_u=xzp?C%#qYzn6nl%B0(GBy(uOlPTyWaS zXJ2rE`!nwH;gsJI`eXSI&wA;&B4_L1(ZAIknBL)J_^e|JQ^e(Y4$|+qs}R>Wy{4!q z)#3E3zcIoGKK}Ks7f)i3ExV(b8*b#zW(y(+jJj%)X7gF57tW_(=RG`kRQ(r?Z+>w@_0q} z@t*((v}s$OW1$B&>haz0-!u8gFZspMZHn=R8pNP~H!c z2Rwnx+b-O%`}TK!R@;U3lE?H(qKD`bW>6fNuF>P*Z&`c{WgUcad|)2y?5jhFj1TZ|c28CwQZIQdJx6#5za}0FHSO}kH-6{I&;xDJzipHU{1segCY{|i>_rch+y)UVK0c>hbgkzUA2d zJ=SlR%Khb8(k`jv$KO?XZ2a@@<>MIIrtghoNpQQ02^-_=NG%K8G7S~`&Huu{dRV{nZH#YQZIQdog)23_~G2xmfcz9ap@yJ_!00x zzirDrygaUh$5F>!btrkTUhbDDcxWhB#!FtJK zNqCT7?kw_S;^Ht5aGAQrc)*Qu8RfxxmB(7qL-<4}~aTARRI zNskhb=`*BVq&!qV%g5hSr$-T|`TaI9A1m`rC#!wmirs| zvA)oL{vBaG5FfppM|r?cZM#Ul^^(W* zl(b9YeSiA0upW84$h$F)ALL)@FH*1ac!ua9y4ZR3X!^}z9(lj@c)-J`9^U;$>LrgQ zxxZ1D`r55@;jh9xph4!%gLqb5e6U{S!FjUqz#Cxke&KrB{(P$HFZF$bd@1_Ng z%uG7F>v+Fil6uKw`UGj0#QXkqM2|c^ zzOfhN&kcx=wmk2X2Yj(zoSi;5uwLbHOTmNuVjOU6cCO0fo)dm}9qS#+{RO|97C3We z(%I>JeIWIcM{iK@AU~WD+m_x~g%!bAAs+}Jkp_9~Cp z{`NyxfkWPJWxkHzxv^cG-4Tpa)~h@|Tks%1o*UXPMDF`ICzH>Tz{9k_kx@McQZIS* z{$2DCU2sNh8?T*@S-T899`IvaMtQJa@)&$xcnH532OOLF-_>?OIoqW#&o6cD;>>iu z&U(qC_f2UR(FHzZ+eA%&dBInEHa`u#FGRaIJDs1hUh)`xUGxxLVjOU6>P=NWKDYl{ zj>Prj|1j{r5ImgS$23l*Uh?S4@8pNNV%yZERUWtPw!e;JnS6eSdkr=}KAfG7W2{$s zd`I*UKE>JLpG$L99^cz*Jb&+ix`(CrQt-rn>+B|}M^EZi9$zeYkYAWVab)R=Di0iC zTn^=ai{DKPSd{vU)T=xsF31mO#J1VL4D&$UebIxwj7y1!)Jq-%8P|KlFUA4KCL(&| z<7w%Bi*wj64qCsJddZ{rZE2Up{xV+^r@P($Jngq_FFsJeuZOd z!`bP5j`fnq;41|W^252YZMJ4!c+-X*^LBv-effSS#}D{od^kI87uKshq#u)Cj027> z)I2wQ{?6B0d@S1gWIsNhcJVX2m%aaN<53GKBq zJMn#g(%F9hOLU&xlX_JTiI0Ks<9VU|e9is#Q`emK6!6IVt;YjC7#~l*^@#3=uHIQc zj~Yn5%7f#n@C((?@}-*j*o%MobBPyt{ko(O*y=tXFvmuYv4C z^|QSFm@1E5-}}?+pa=f9e>BPi=Yh*<7k#taZr^@1@3+0O9$%Ao5nXUzY#Y?@xaOjR z_5RXdG9G?>{B+&gr``MbgEc;SQZMxw$T&)VF%CG^KCr6CrI($WzlX`=s`O$x9Kd8~;F?-#V>w);_d-r|%0GDI$p6`C=whyZwtXFyP{v!N%ZfL(0 znXluV!DsC~F109pmFDZE`^(@qiBr)fR6on7BkvvZ`1ri>z&XZcln3jj9s?Oidx`g# zsfPF3kjS5-~9E;=VHel_6qB_-uMA6d_A0<^;@Zzdh`~A zN8It7v{Vboa;c@D* zJLUHmjO*DCOb?z@aG9BOc6NV}dX;O79(d zUn=5bkT|YSMD8!((hkfE3m*0!hV`l*GA|{+{GuX1CjPn_A28?mk=;4kE`EGCGkYIY zp09td(1ZLU6!Jq$5gz%x@H?YCa1!IgLE|Cysvhz_d5}0?pM7gpkMp1P9F32x)Ng$~ zoSnso)Jq;cd5#zezx<*jKNf24FWX96p;klvx)ZbL~c)QDY#_?n5ji;#J*TdOad`P|I(c|Y)!Y@=m%jau&-01R~ zfd~E%y?$#PaQ4ikv(x)N>m`o?=Rv{`XT-Mon)v9roM9Y4`rbSUevHe^q_flbV749fEl>96F+b3^;_nmC2#;DIemyub9y z;~4vG;{0R&BUL?6KX~B(&>K&!O<+-)ub2AmfX~^9Q2oLfL)z9*Uf2i`fZ0+`Xf5}Sk$9Nv%!`bQmh4qq0Pd?WtKRd4; zjsKr2k8d6FJsm%W9uIi%^>B7Nez0EU@l$CR(WSn2E1mwkDvv8qx6dp4k@3UX>G;u; zddXw3?Qiq=AU~_I9*uvr%Htj0{iW}{rz^*Yv(xxsz2wo`MtBIn`r55@x~5%@{?vNA zzj*h3YZIJ5GwJN?{v!2~$KbcpE~1OoSdZqfs_Jp{f!At$WS$=IU|bxJ1FzNiV7=tg z`$fTny42ThrBi=f<+1o_yT1%eJdDfCq_ea5kb22uFjlk+`B{ziXrgvJwfjrk>o4W_ zaBXApA@!0+?;g=Z_|?~LrQ&&$KdXQ2X(<2v2C_yJcT`Y z&&M^E%2KlqGoOYg152kgNkf4+^p|8OSCgY_zppNJl!3w*}5rJDFa zPI2(SHuUr;$A`1i_~=Q!5nT#z;h*CltLlL~-Hea6_ddBCAI{G1FQxv{`+lJZ z`Gpx2M;2;$Aon%nqy4kd=St(^=BLj+K*tZ(OFepwQ{mUdWA?*UJ&;eD@zM9@WAs&U znL%D_8$YC8Qy-}6fn36jkG{tP9{l)lb{ZdpvL4&ZxGuWX*KVcL zHTV6I=NIpJj%P7GMxI}!Ue)8*MZ1t6&kgM-{<*3LY#ARD-rud67O)r{*9THBdGvVS z7k)S+w#{B0<^e7frSH)jmr)+9mplfX7Ye@^2OL|dX&00;K6;)WD96TjaZtx~)~h^t zzZG5b6C*zsYUX3mobA&0;sf=M>fzn@rC#N+B=M0rFPyKL2Z1(t;D6tHk82!YG1@Kz zsaJXYr|=MdI3u>r*YsQ1vt9b${RQR5rNl$(C6C^Zg@^FNxv_1wW;}&Gc;xr}5)T|J zxQy~(z2q?<58;RNV%yY*s{I8yCcy(+yVNee9?nk3b=IpqepBc{eucO2&)J&s19=<4 z1OJC59_DEV`6l%~H;{Uj2j@Yei=9`GCf;Aw1Gx~v0rSH4y?5o$WoRR8z8=m_KYtmN z=0UyxmiQ2U#o6JX<5`sl@(Y3o>Tdt{C=YmId^kJh!Fp8>d9ETqoEO`cYTCsgKeFE% z57h7L;p{9v%FlB{i4V~w#sSBc8pid^YZv2yGe`T&KiyrCmgq!dv*~bWNNh?+tk1f4juPJk20q zkBuKvFL?~)ck;9I>d`{Y`!VE_0f#(3y!#wH`0?TFG)`Hs@?g6NpZeOZbiSrtke>xS z5FhE^8`Gw^&|HeXQw<^ukw)RIPxpJg@4Z0jHk#W z0v`C^_vT~PCh#|dd?wa!rC#zFa2yj|a7JvKzp~n2kedS>5FeSx1AdIl404Lt`!T7P zJbIju2|t`0+ZJlt1^F((Bfr0R^FsP5xXd6=hmIetmplf%?+ZVk8`@9R=mE{a1KaSw z!sCat)A(S$PYTuMBoUh)`F581~!;Mh{b^X<^ngT5Fa?st3tA@z|*PoA%P!jI>M_EYoK{(?36 zH9qbR$A`1i_+Y)t;|DU1CF(I>!vpKwYkYX~F>4cC)eP3>*M8fRddXuT;|BSKbYS^H z&3z7Q$Ad>6AD$jK$GEuf2d)0T-se~^dGusHNPes@w4bec&c=G|;E~^7y!jY?Fg~1} zwM%)P%=f2>g&+E3KnU$AaCoS(Kt0f zGg$9j$J0S6K6)}Q?WMjy{am%bU@dOONBbAX0~~xkoSoh0%JX%3pG|(ozaGusRONy7 ztr;Jg_uN1aj1On0di11TjnkhO{g%4$+|YjZ(^VemlZ=nHH(xKuhqJTzkb22uKpw&` zR6olnZmi>B_m{Reu3MYn?9p+Z^^!+V-V^qO59sy4x&qp z1CA|3+67$3Q~%yW>Lm}>I}m>SJ+zUdFN-Y z$9k@?$EMh=kFK~ll>gjzJ4C@)WesM;--IjYda$s8|MNDyV%;HUZdkwW6))Id))zj1 z!v$wtaQ24vr~T1|XI!xE{PhkNYea(|*Ln5%VVzg_!=~8rQ(PR%e>U;sIhr_;ucV;Z@l(u$Zr{X@>&N1hmGI-bus%Pm3rh-$9Y4i9==M$gxUMWtSqVR`7fXIS z-0_uf`C%Pd_`^mFe#FI* z;%5^-u0sp!mGI;GvOYhoFG~)@;7813*f;Ux`n0fJ2|uno>+{39v+#$F82pHfBgM}q zeq6T}*30GBawl*-TJXbKv@WN?Mz^bMuDnwzbOj-P*U#S2!Me6`PM5efxuas=U&znm z#`}5pdnj&AK3H*@pM5<0mg3grV-%*up|_9^+cx?1#E@4Q-sU65Y8r_V2Uq|7*LkM;40g z(ItlN#HLnkjM@inFMe8zp}Uovr%)VFcdi+ZGghKI*8=~2*8QmCj`(Py$R1r{=uT{E z#dXIT=Y?&nOAOtKQ~XeOu91#&maBXI3@U4%LwB6-=BhWqS9!x6wp@naV{y@P$GLVo zY|H$P-LT<;Lyq@nQ)6SeuAG;wg}&K%$=cA%<;AtpHy1Cit-fqt?<(GzjPmmD#|19h z(Jx0$@pjB}uSN1!YdhB6(bk@`j%~_y)JHiM_4_|p5^FifNZzg}M_0LWlr>K_7so7; z`!$s#*I(aU9NQ~xYfne6&AvG}wp=}2+ugzXaRupM&)INR{`Sq;uzk5!`{v@swceM@ zi)*)UE?!*weYw22mis6#fBe|AafNHaM|staD_)y&o%d0W@Ho1ybX;jiawz6~Hk5kU zRnNHN8t|KoW42O`>x?7Uhu>@*WxZRpO6^=Aq@#Ba;o9*&N30#smL^796Z07MvH8lQ z+;Ofw58E=wH5<-Zcgh*(zjR$`)}n3vS9SMbcpBYS5{EgrNN(aLZd_yD*9~jTlNm9% z5%U=Kjk?Jiv9MjPZd`ZX=Z1CX;R_ovxDgjeil0r~xNbeHBiu&r7;ICnMelR->n2`l zn{qAtm2l(w^gcJNPft$7;6}`2*f;5x$@=!NU9N6iv)<)3BDZd?aHo!hw^7;jwLesgi-+W6_*?Eb*@?tN}p?;gIe5u;6sizCI) zSiG%vCPP^-AJ)+}ZI};mJ$#=h*26DYxHV7(gC{YMVc*CzGS-d6UK7tttM?16mrpLl z;7QD5DLlE}K5Un*=RvQYWK42BeP2(kr%x`#;7QD5*f;7a>#f6f**v}Z3)kECdBSIT z1J809evk3Q{g>qG z+7oYAN>d9OJK2PKlC|J1fI0*(%Vjjc3i6?Urz;;u3GS`656S)T97aK8n5*J5` zpG`b7$yEUBW%Kmfow*2np2$T&F2vwT%wyO$@{}9`u-z1%%vIp?M6Lq(#YPOC#Kn=~ zXA@86I)HVMXQ;n>dNP-R&l9-}$b}d@iFpkBCZ5cN0NZ8rbmy=iG1r056S)rH7aK8n z5*J5`pG`cOD*@Ka=IQClTnIi-y@>wa7OuhkKgdh z_&B_7ET2o~*L(19^1uD}uXl$({h+?TBF6O+8_RIJ;ChkUp|D+4jO(>>^Ayqz?UFx> z$y$IoV+Gf%?aSN`{`LOyo+Z6rVq7n=v5a3Ya!3@miydNIFL8<=UN3V{;GAV&uM1qp zGjmM%*L&s{Zn}t!wr z|9YSLvxBt%661P_jb;3LkrSh^UF;C!dWlo~@Oqh31Lp*agl~weQja*OLh`u9uj{ zun+OX^&-bdVY{dp*K6hGDddOk%N!v%W7*ex$HjWR%mL!J@8$2BlXe*!BgXX-8_W3h zB9};EyVxPd^%AG};q@{X3C>ye^}2K52j8<`t`S@>{J7_-8{Dq4;W?{P=n7)((sIX{ zs|4qkd7d(T;kjp)Ueb~mbB}B;UPJj?mgVwdE|Sf~i@8je%Zs^5d|t>+0zcS@(T>E$ zk>Y2p9r4|Ye&l{(?O68^y7nAx)3;RLcOq{Ic@TpmF^^&2#F2SVU>lCLI*#kKKQO0> zuOo7rz$Z3ha3n5{6h9j|%9_)#ULHs5v&?tmb6j-!PRN589Eo`h`$mqkUMOsr$I;rF zxlnwL$b|x**oeWAxHwY$Y~sjVDzL8Qcv$$Yh{ogP)p$gn6!IVjM`9kszKP>d)^CLE zN;xuzimxMbsK6&SVsIobjubzeI5Gzdte44gEp=pm6`v#WtB?mVI1=+1_Kh56-8R@R zlcRq3jJa2Qj-%f*-?Zj-w^TZ%sNfuM4cp zI>tGUsgBIqvbi`i=gaarnvTrpvbi|6Wu4kh;mBMsK1bwwflqA2Xlvr)Nb$3&t(hwZ z*5$T-#`?AE`oVY{)B82^!1x@I2ZlU|!I7B9uy5kXJTkCd9!G0y=7{k*B1a5-Vj~7e z;^Ij0vxy^f%)oki9LID{!Td2kN92zo4`Og6<}vJ>I5PhXY?sH;)A4Vr_d?{BflqA2 z;7D8?DSkF_WNsQ**K$m_Uo-EFuOsr#kOwh167v}LjT~hiTi7m>qxKo*r13c-Ck=dJ zBL+v};z;qci6e8`zq49Eo`h`zDU<$h(Jp>`7>A=CbiQ-k0}l z=EA}Cly$_tFig>&6`1E{lp{QjZqRYGr1-5#N9M_a_3}8{*uxw+n~h`Sc_yx-ab$j+ z&Bjsk>nvYK8y}fF$LEOLIYmpl&-VqRt%-RI`=+*L?j6{c+xmpF*Sc@OhG&qRhcmB^ z&k=cb;1e4$I1(2}il2=fBcFk!a%9dOpCfYakOwh167v}LO&poC2evEKk@yLY zpV)}Ok+?Wg{A}XLd_J&VDM#k&@i`(_4|x!SBQcL*-^fvN%D}dsV|avQJTi}u&k=ci z;1e4$I1(2}il0p!hmm(uMLafhWR9N^jxNU!c_7~paL&;y=kX?v%n=0pmFmd+KR!p~ z{~-rra3tn2?3*|;e-La}%8|K&e2(|!{hGOj>Nw&)b9ln@4D$w!a)igx4LXjN6u&j; z$hl zggIJUGiQ>oBXTB@2QfGj^BDF`9GP+{_n&wjxA@zTurbq>sWkVIP!&W>&vpnzONm8l(!Z0lH5)3w;W!~#k9G2 zF_+V_c}Z@jQC|1q-{(eR&Dycc+VNR#b z#!+%H#oM<|M;rT??`d;!%vQ|N+M2nbe2&Nk#dak||0U)z>|^5!zK6hEQn0PwS( zpV)}Ok+?Wg{A}VllpI^IULHrAV>5r2&k^~v$b%RhiFpkBCXVe$pNY4%*RLg)gU=DU zwa9}Q9Eo`>iKDEs58GND6Ykf{yXA94-YxPV21jBZ!@f~R$rAzFWpdQdeVLQX=ZKtK z@QIBW9Epn~#m^>=L&*^U>t%A(XDjCG@;M@37kLnaBQcL*-^4MKJQuKCDM#k=@;M@x z7kpwP21nxJNb$3YBXfbly3CQEVTDFW&L5fQ%jbwZU*tgyj>J5MeG^CK34`rQIWh;B z&k;Gm;1e4$I1(2}il0p!hmxZK)+^=6{9ryuJcfNE$H+XKpR+af8Ri`G zIU?s6c@TpmF^{EiWX>|!u2e_nBl9^T9~pTNgCjAIVc(=ZeW9!EFmI9gKt)}$lzpuxJVW1OSSahc<6b8*Zhm(KDz+Gj7! zf3~?eGJo3gIhu~ljplPiZZ!DBMvS&5E{+sGo7#FPIe1`QZtLQ6w$R8krt===P4hV- zZyI?JgCjAIVc*2DADM^8Ia;4#PBot+a;lLBF*p+QSPI8%#X1^C=3DbQBHtQ$5Q8Hz zkEL+zN9N&i9j&dIi_PbVTx{e)435M+mcnrund26py@cY?`V8~5`5cj_jXa3Kk(kF) zI5N*0Z0q_=${0Q?Ioy1X$l*pF#NbHGW7s$K8Rme4?eaL9j?C}ob3}eO_{2sGj>N^0 z;%5^_=7)pzN;xw3o6qsSyk9eSTpdT;3lC3t|H!;>qZ|v3{GZU#|DCu=N9L7-eObpi zM;m*XGj4NnWX`$ebF{H%T=L0nHja@oJg%d0WUe`Kykzn8jkxCW=E^(eGW;HE>wVpE z=Bk5jxveE9ny$*s=NIOk+gzNOw{AI{n1gO}abga;6i(fzy4-xuFni+<^0h-wcfvy3 zRqrl-pZkkq4`2S)edNdj#ouAcq=}EoH;{gQ^@{4i6v8n&A>Vdp_;DN3GpO%*v?P6^+GwJNES3RU& z}i;9{At)^5Yr@_?ww@c286urT7>;Li7-RI3u=A-%-^AdGx>m z@iFxBNyCqEnVEEUnopYbQjfvz!Xq(0hTpC7K)yWi$m7Fn7x2JVaG9BOcGpl3)~h`B zk~kGziZjDM+rO*wfb|Y?%AA+~)U+5IYn?dr%+}hAuTmaTFM0GHE<8k+FoWVq@0(Q~ z$Qj4@nELE!{!?=K=+f&tYd5~x`mNNfJS6|>K=?KBSh};y1LrV4roTAK1INJS;0rsQ z>yCN9@hIh`?Tt%(2tS+`+h%Y5<;eK4eQs>|llxvCXVgzUw%NJ$^q2h3>$g%b^%(3b zJQDNLuKGb8k3tVS03h4xDy4D-mxbuW)I zd{7U0?wBDD)=M6PRT8JdFH}Fv$3GM1fqvWf@{RMHg3HY0bG~x^N63Tql1FcU(L;3M zxuN~kx59CXb7pU{I5jPBB>$eA=^^!!$AI@a(FJG3wz(gMc_2QfCI22UHsdnNgY_zp z2Z|oTuQ)sWv;U(o55z~7kUu$}k4e4c(R--Ghv*V!P#l^5aUBoqx0yHpfEJ^AczMsI zUgaV4pMmge;?es_9S`fbnS2I=m^BXYI@&Hhsh2zk9LI!T6OXBfR`2^bhvVtcm`rDp29=;#W>(td*3h*^xJvKS;p}L=M=h>^pJX$ zhv-3mI4`!XdRQF~>$mN|b5zkT)^7(=FM0HK5j})oj028M?^DOa`t8trZivMP>m`o? zc?iEI9)m}OdF11%rw4o)mr}b(z2wp3JXy+{c+Bq==8^YX@411#>f(d-Dvw7=yCm`$ z*7RHC+F@J{yh_c8OO-WxRlzZ{5-dJ zp$GX<%g{c1WR(YU=%8N2$57gZJm81z;_URfp(piHkDffYP#2sR+h+H!@<6^E*4viv zwBdKt0v=~3ot@3srC#zFa6TsdVjOU6?!i?aC`Y~c-}iW++_;q5r8FPwaUPWTJlA_j zl?U?RfJ5GIOFWFr%%rnBLgTbN54ykfm(=;#swxlE1s>Si+x}Pne1qRj3mh5MqbK#U zU3zj~p)NQhwhjLvJ`WmhZ#>|~xRiL5@3;F(e-VB$4mdV^VVDQnrM=@Q51dnQDe;hc zsmDN`C#eh0i*55yt@1$L8n(-D*MbMGwJL;#yB06iVs-(|*f(l?UH{2)`yC6Hl)4KyDZC!2k9x7N^!G z@H#W;?2cr=?Mc1LV;>n$6W`O#{$Z5|a<_m(exH+Z9eQ9hE;EzP&hB$kFL?~)xt9Ej zv%^1okE`-PZWi!B-Tg?vb#~e=tXFwZ57~zq6i1dOt2~f<1w62|OXDf^c-qC!>|XZ% zvyDetkKLrdBtGvit*-JwZWZvr|E$CV+_0VazCY<~zyBpVPaa6U)T1Zw=LU&-^rosj zkUIq&@^Rhcf%>V(lW#qu`=P6M*3Y9@ukw)RYx2Xnv2EeBDi7pF0T0B-Fc2T-f9BX@ zKDGNUszP+uF~p@<8qqaLC6GxxbXh zkH6gWi`|`PUT&YymEv?D^B?kyalo;OgR4A{n*=;iciWqf!5$mh=Ey@n-(Bclp*&cx z>LKGfbt%pa|6J%+c_8-)cwo!68EqHF$Ht%k`7t~6kF@wG-{)E~ek9Hd2QRMjKu!?w z!2kB{qddS3+pe#=vAgt^8#F$8QZMxw$mhtt#QWUvgeni@>Hr7C$I#<}`l-iu`@Flm z`sgnx57w(ZgcteY+}Jkvm?{tC&;XA-K1%mF_yU*HF8XHo3GLgD=KXe1(xcawb`f1- z9B^#v8C4$0Z2=zG`d*x(e(Lelb!(qC_Jo5iKBQjN<4M9p_!Zv5KUW=6<$;_N;DP_c z!1(c^y{_s$Z0`wc7pYfyJf+}4UBV2CBXdVqc_0@AIOOAprw7iU9_Re*=I+fueE6ic zi`1(;o+x?eSwt@E^ms%7@M(4?_mplfK7aqc|I4ArwJ3Pz-T$a9IpNqkZaT(>oddZ{r zIEfF@CCs2WGSv(7Kzz&vK1VJ;N3mY=7)**DqDvEx*~eFTAQuALCELm316ufcI6LjP ztd~4`M~WVzOB0Xm^ePYJI{**-&r0(T^EWf;>@-eUFL?|mL=Vx$&Z|fLCscVLrvW(R zT9>s`A1iIEV_IK;F0%R`FmktZUvW_NoQyGeW_P@ zJXQ1%U3hM2Kj>6>AU6ScU>la&#n;2xS$s&n`+;M~|Y z_p~Yxi6|^5`8c{Uvce)<3n%18eYu1L7kq zwTmAg&Q8DA$9lT;6mOgl3>zDeiuZOd<_$bd$kCFIDoR77iSLK2A^1%cDho$FRa4WW%NoS|` z{hrjTdK@b8kvKmcuX(P-n)u+5-(Ldn9T2C^&f-JrRUQ%-SuZHiB%rBR`9@<$>)UNR31N^oyG_2RXw`WE{XkR)l=$t z6tR)Vhu1C^qhL5Xp7x|(RUG3N9rcQZMxw93easpDTwoJh0Y08KD94Ahv-psDm520W>cVRX?FY}Q z>Vfs-!2|#Mr8qS|Gn3BF?k}bBV<7hz>SE{BqlGn99#}IT9P;^x#{(Yx_;7YQez0EE zL)vK|{OW7B()p9BJg_c2c;xZ1UHJau>~#DXl;cCj4f3-Z>(TUcsywjvJ9uCldOXVU z;p{9vO5^E(&ke$_@D~0#cXE{n)^`UF{O^}|n5UUZXQ%fU*2{M3J+|mCgT(Q4_LLeP zZVh*E$j1-wehUtMe7JSrH9lA`c?{&ami&x=J?cHT$^+}PgGYXU@#bUnP;hb2x1YBA z3+pA1o;>H1AI}Z#mv*{)Sy`vzI8n8{*p!F z1BCg$uvdPbJ6y)o#P=WLdxd!*KH7VY_FF$bd>#X-mwF5&PRWng9okQ|!aVZv;~}Fw z;D_G;8V z$)hLlqsXtmb}Q{2Qssel%)uj%j|Z6^Jd5$+?DYP^ddXuT^B?l#xuN~s6RSM1RylZJ z>p#qRl;gwMX?(C=^5`8P{WkGEUAA+T2i7A85BwiKe3S>c72C|Dv(x(v>m`rD6XgDq zI4_)kNRdDvI6I3Esh2!@@;mtz+`~WT52*6+pI?R#80BGHW+q3T zU&=iA{#44t42mOz@hT4-VSFrj@A*v&Sd`|;rT3=;#z!KLY$D79TqaUK_mFzEUHD!* z@%`!alfyg^AH5O}D8YW~ppNUk(tGXRYKc?fhx1}vf44dw_FUP1u*FBY-#QxFZ&|PE z!RMF6_u8u-S;xcf`~4CRyQWfnl%8LD2g>*%y4ZR3Xne;okG$VT#&ze>+C}Q69s}|a ze)Y9m>D=SOJTR^gz56ZCs*4ZSOCG(0L=WM^b3^;_M^$-XZEp74p*N0|8l$@p+~#-sdsgY*;X zg7aeAsvW94aIN5ht=$@J7hex&r|~h6da1{N?UML;L+>edJZ${P_8a9SRe50TYR1R#furr>>*4G)K6+9wc?_N{JcM7UewGhw#xbl<4IcQP zd3qQJoINw?>~vnpddZ{rOyME?a7JvK*uJU<)}RIl#7BEy(*u5t%gm&+(|IB5RUXGm ze26YMH@5ZmsPe!%)8LWEhc{2AAI68Xv(Im(UgdG3=pnlB+|YjE@l_sJOBy_|Ws&%B zb`~E}FL?~uF2b+y7XCS`x$k4WXz;-QVPyW{?4HE_!g|T0w^sBJeqjd1k^X*FJ+LM; zI3PZTk@3UXT}vLUmplfiN_-@?%iR5|Jh1LFc;xZnwM)UAvFhIA{_DpzK3Fe#^qwPn zh%P)gw4Xh&$^&aVg9oNoQx{ht#V)&J;a_AI^wv6Z=Ooi0mlY= zS9xF^X7I@4!+Q@yUmQQ2owmzB>Qx@nPX@w|=Z5wRtExP()-rft>wC|)<@j)R#-lWT z^v)7JgkRw;{Bw5KDi5ru3?BI3_U47<_;7aCZ>3(53HFC4*C3})Go$l zX42W|`>{c3{xRTvU-*TUD2_}&tjYuHB7+CwV_3S+;Vj07v$OG|bl>ltBJGkm|5$o- zl?VDHcwlQs;=|c#e6U_^m(ztu;(dSm;5r^fY=BGO8&9oGz_2twm3qmeC-F&sI3u>L zdQg~0K7M$5z>jem<-vND$BRS{;TPk8V^a?Z^T@}K(l~~57#|MW{8Z{y9#TK~;k?*3 zta+b|wTKxXeUAs~_w{gg79ZtyIib*_C;Va@aBShRRXwoYFnHkqu*3uB6x+ABBv0Vm_sq(;@!r*}TXnW65sNdJa+1Y(g>Qx>x4eY5RPWo+PoVHh2d0>rSaLC6GPY={?TxKSno!;jL zQm^vhyfAS-HaN1%1MB#L2jV01=Ih{rjq%~^RF6SPMoh#>>1{f$A`xQ zwfpe_GWz^dnkV<3C3*-yoEzJQkxuK^r3Z)gK%J{@ zwZF44EpTMCU3yY4c?_n6hw#H0v2FIKFb~A)LgG3`td~4kC;9RB&_3HI%pJ&;ii<<}&(L*_IlVmlE-~~X_S{7EGW(XE z3%#t|JcamBFRr7DGghJ(*T?ns!uq&mpxEi9mR) zvU2kj;zPZ--Y?EriC$dC*VhZ{_>zHQr z^AzGky|^wh&RB_FTyL0qZGY#}H)6eEG9Y&49bz8CKD5^Q?{@tg-@eOpp%-zApN+5E z)3a|WhF;6@L%q0uGU_htb>h0SUcUc}4vCImTo>8b3+p2DT8N<+F^^#%)628(5<@TI z6hF|*>|2VVmzA5R5Fgb`)&;{EE76PVH~V^F{bn*ChF-)xhJA=1;>)w|5<@TI6hF|* z>|2VVmzA5R5FhHr^{8>iO7!A7)4pCL3HThu0B|qD2ai|(?PFzQ}zyknlg90kxFZ>5Uy-GS1!+;QgjaW2(z*TkF) zxV*An$8Okg!6C={v#GH$j4RNIITW@QotP71xjHe&!`7k`b5<-@C+46S)v1_sz!&Eq z%biyh{Z9S3dqRPWXBaIns&GF;_tWAThW0ieDxP6ju5N;U(0>kcX3VL9GlKoD?hdi` zoOSGj%$G5$nQvNEGo)MAw=&irnbT!!(P=23?Jid*=A7ADbYc#j9%n^DT~~T5!9K}6Q@&PKv!N|v z9Lu$e%u}1RVlFISE9AnWO2p8Ln8&aWwIkxnv+oiku833oAg;{5r5JH#<>o2GN8>8e z7n`(VzAj%Yml#?Rr}%+ZX5Uf_t*qQUh4@e_<{!fukv=#w zqh}vvPBC9AtJ%<&(g&F%Z6#VU@0qU^@}5y8VrWIoW7vn<5pm_&cZm^K#3_CdS7zT* zjJUFL^AzI4xXL7t8_rm6T=gY4o3EACY-me~E9RQ3)9U2u4HurXcHQ}*ck|32x3y@+ ze0b?vox6eK6?5EeEm|>WU%FP_{9W?!`C1_lAA27$`XDinVILYFFkX4~U1E$^#3_C- zUYUJMF~%z^H%}oxh>g|GjClodMtQs%>4Oc=*UTH}>xR66WI_zxh{Pd z8*z#s=w|jU#n8>l%~Obv>Lz)raK@(S#yp3;CdANCa&%R3x-H22C zKsU2*DTZ!VZk|GXs9Pra$Z*E8b@Sd&Fps3Ko3Sti-Rw6*A)e69tj4;;(2dyNhx|Y{ zv$O98K{qQm6!HVz@chWUm^fn__uu~>rH*eSv-~lA|HWLFTZ>lA-I=bHeNM}KoLh}n zDgALIu1-6fV;6IV`dT4pD0Ih0j6O(Q94UT=Vjg|av+ok44-%*NK_4{xmSXflD>qLe zJ{-H40~KdP`(WK1!yCJp|2VVo0XfV z5FhHsoUJ%x*}A!N7;ntU>g$G_tYktA-H3S%`w&m)=Gk|Np&N0EALwTGEyd8y%FR=V z4|U6A?P#2_DY`L-tgjn#$dU;$bR*_5>_a@En`hr8hHk_uexRG#w-iG+D>qLeKGcml zadF17b@TcubKd&8A?Gcb5JNX&9>YGw6S{f!U1I1)oZ<(%nSDz!bhC2v6yihOn8O!m zlyy7xyz|zb-#!1_{5?k9_~y=GKV^Ca&%R3x-H22CKsU2@ zf4?Ezpc`?DAL_=O!#HPCbYo6oUpM3wCKqDpM$BW_$8_`TyTs6qIK>ZiGy9fe=w{{S zDa41m^&`)ojq&F7Q|3VSbwdthG9iX;#5{(5h$rIBv+oi^H{ujO(9P^yilLj8o2L*T z>c*VTIAhuI=IO?q&Ax8P*-R$H(2bbKun+NsZk~OY7`hRs__)s_I zpvD_a@En`hr8hHk_uexRG#w-iG+D>qLeKGcml zvvJ0CPvbix_S*3FAI=J57)Lk@2;A%ZiGy9fe=w{{SDa41m zF(*0BShjBN9L5`Sj{CapdBveRz7az=Vq+QC4Y|w3wo44%h*SJPH?wakhHh4Fo|<%H z4t1PS*6sKWuZ+*%){W^qN#;oRb^F|Vzoy?~A%J)xx@o*Ir@OD)ITwG7*E>cG-H454TsP!~7uzl|bR$mj1KrHNr5L(d zxp`{RjXCOZ#2+j64Bd!%4EqpIjBm(gFScD`=ti942fCTP zTcbjJLO0?RKRUiep6eU?sjG;(O+BN?f#vIV%|!=uTpA;WZp0}(q1#h`b`bq^iJ=>D ziXZ4^_ASNG&C1PFs2!bB~Ts&1EFb}IYV7%_AsHkR>tJMr*e z^1j?9hHk_uexRG#w-iG+D>qL~x(y{48qQd@ZuefK_l3D5tGd1BM|)jQCdAN|2VVo0XfVCf$aT0}y8{JKo$m+D~6l)y-HKf^PPkp%72R zn_2ySe8$&uga)@1YZ`)ES+`TBFFg0m(wB&+Q+D{)rPGq+cv`MbQ@yQAr#Z@yhI5iZNbUxp@ltLI27BR#kGy zt9$9a_8jelr&l#Q;P!WMY#t+qX2ixYe61L(Jo_#&G$T&&1I^67r5Ku7xp`{T zY?mTm0?t^TX6tl(??0ic+4J9Y|7*#D7@86D81^BK(9E;%5<@fM6hF|+?A_Xj!VQ`c zr}$x<4R>6rW_C}Ue{@x|#XH{2Ip-KLG$T&o2+chEE-^GCPVocH%)X@i`Lo;Gy7>_g0zDo?vh*SJPGqZ0ghGtf7o*Ff4>YIm! zKgHvCHv8DBW^a7)71xmkF*GCQG3-Me5oex#ml&E6r}%+pX5Uf_&8*x!h4?Ve+D-R> z1kI)ntZKIRw_m3B0AgrHYz*U?dG=jmXhxjk2b!6EOEEOFa`V)rS%2l4t!13eJ*}$Q zsmJcbXTdRIXhv)dq}k@i7V&A3)d z!DP$&yXh>V*_t!XIrq@uaAx;_@js~3%-W3zaYpR#L+y(3%(LqfBhHBZeaH{u%f@Yi3?ui=XRKelt{)uF#`Jk})$vu$w!eC3 z{SE>#G$S^Kam_sYE-^GCPVocH%)X@|2VVnU$NTCe7NCj}m8;pEu*0S)45#R@Ln7Z#qTCGh%2)Yz*U?dG=jm zXhxjk2b!6EOEEOFa`V)rSwAw@$TbVkY4u%D`*~H(Zd|iL-vtpvGh$;H*UYo;5<@fM z6hF|+>|2VVnU$NTCe4Q1gzuXTn&~}Y{Di7zo!gGk@r)Rn5gWs}W}bbQ7@85M_M3=a`7Kvq#96g89%r7tJ~JZD zymE!1K7}|_qp?kY-W*2yW?VCyYxIxZx-`ooeKS?F@uRmc&DxRsW?VCypABm?d;KN0 zHUcsFCb9LEc;EEwyTs_5#3_EzH_hJt{he^bJ%BjHkKO|!nw@a=TDO8(_zfE0(+-|h zjkC+vZm-Xq#L$d5g(KdddG=jmXhxjk2b!6EOEEOFa`O}#7ge*!Js?%Hg*8>pzIDj= zbbdw*&4`U*JkC7(E-^GCPVocH%)X@{Pd8F7jqXlC{;#n8;k%~O+R?d`&Ijnp`seoj@hcX-d6#L$e`7{)d8?7PI!j5x&) zG&B2_VrXXN=BY`uVWVc@85+m4xs$7!9sQ~G`n*XD&4`U*Tr#*Vnl$U*2b#^EQlr^{*Xnq7wBTGbubeDH9B~iexz4sroNKlWKe=XV?=%yfYo_I7 z6K+G^1G30_jiPTh$64>WRm~PZZO@y;(2Up^hB-nr&%R3x&4^R{Kr^#<-`5as(2O|6 z4>jvY-qWUPwzN}`>)8KZ`|iMABY+IxlP8gb3+*=ee^b;VixA)Bt* z^zK#7F8{%)`gs5`;*6NFhkd9|;U3`GcZtzAiBtTbZ<@XP{=aa;J%BjH5AOllL&KV# zboTm}hMzVq&c+X~YWB*LchTofVrWL3!V#Kz_FZCVMx5dYnwfn|F*LJs^Au`B)oh=Y zYPMeAZMPp$)vWW}19W~y49$p*VLZ+}`z|pwBTn%H&CK3?9w6MH8F7jqYSxc@Ru_*m zd*|3Yq^j8w@4CN!9zYDuh*LNs&OG}rF*GAi@dM4wzNHwNS-E*?inI2ERvKq^-<*45 zRkO>y=S^a0Mr;h@apu`~iJ=*BiXUiZ_U>JfaD!&VDSkB09v0Uu{1jm0S+;Xkvm3n6 zH;JJcaSBJonP=Z6hGxVmexRAzw-iG&D>qL~aW;JTN;R{-IscHVW*2&&ZxTZ@Vq+MO zGta(D49$pB{6I6aZz+amR&Jh}G|Tp0sb&^u^9NKl^FKEvhGxXZFs_+rub)LiGp}4- zg{~Evnf<1JzBzorN;R|bY%pHcOfSR((9HW)Vdz@XH`U0!*C2*w#Qr|y2b!7PrhQh| za*h@n|1NA~!AG56WD{GLP7{rPXIQV(^pm$Poq8+OslQvDPFryPuLp<6s^af^LyH&K z_`d3qb(-1eK(r-{Rrnrzf5n>FJYsyutxK~k@>~>;Gt+GDaa)&W!^k^}xMtQj#~)SI z>~Zhu=zAVw^i5*+SL{Rgf6OC1`z|r&5yUBeFpn_%mSW6Tt=v3?`0%-C7@4o;njLmX zXh73B#l)di&DQL)k3N4BLo;Gy7}w0R?-D~Z;uJs7%-1*B+*Sn@J4Kh>c-fGta(D49$pB{6I6aZz+amR&JgeHEZgd37Re3ud3Ner@l-z zBZg+g#xSm#XWu1;X2dCepqbgX6hkvBH&2b4Mc(zN_RUp0R5ja@KZk3#!hI8;j~p5t z&g>r0drF;V)^0?IGh%-qYS-V#XUTl+jQv)snawq(w%xik>oivkL~36KQ@jk3iJ2LZXrV#J+?I-;|DP_`(DL} znU$NTZZT^X=qxs7ePi-ETl%-h^m903#EjS&7RSu9uM#6>#4&ylGqdkijF?%udFmFk z^2W=nGkXSGAD55WMyvg00$C6vX2d*(eTXB*P0zkcjF=I}_(9CfzE?3~X65E7#D~{r zx#07WVx3ukws^;U%$`2*co2GXE%AbS-ar* znf(4MbdRpTGi+><*V)ep-lgxHB1X)JjbU-jJo_p!Vn!U}2Qf4IUd4!+m7AxoF)Ns# zb%~ii*BDwmAG1sEoTvR6F=9q+42xst*;k1XGvXLOh?&{D&&Y)vVn!U}hdL|24%g=3 z7WtU#4&ylGqZQU%Mosf8F7psjscB=?_#7e3s2D4pAEh#AG2rfd7I8Ph!Hd57>*bN zJo_p!Vn!U}2Qf4IUd4!+m7AxoIxBd#9UHU8mid^yf5A@rU0GtpjMx|!>&&yS5+i2B zF@6v;v+q@mm|3}b>K3y`*K4!8&StzdAF~UuzftFB#E2QOF)WUmXI~{o%!p(BAZBLY zs~9n}a`V(JX6=G|^jMuOeMdfK|EE6>DCp1d%rUGod#*8Kt4=Yqyb(cX#Qr{%SM+C| zoqj(II`hgEhWtQhYVIYoCHv|8j2PD@v0W?0V}NI0CC0T$9ODPqrrGx@#u#Ab<|%ZI>-?-BX6f%GLU)_q zGsk!3bvF0a$8@eijF=G{!{V5E_EloUj5x**VrKTeiV-s_H&5MS)?V(I*%;7zXFg_Q z#y_w3=){N_u`w);nP*=mM$Cv~{2*p#@7~oY+z>P37(dimx!^P5Vx3vc%IoK2cHj-` z>a|IXm=VWt#JK6%SBVib;ut@Onc4R$M$D|-JayICa>vZB&86GtW7faO*6koh%!rL) zvCcgEDluY49ODNuGy7h}h?$j}r*1K86wGO%v+(@P<{FdUlaE>Zquc3y05M`lYz&KI z=Gj+?5i{Z#KZu#xyEP((8)8NrK~qs#BV!ZGu%&GGq|Z9BWB_vplk8F36pT$`SK zl^8K2j`4$-nSHNf#LUXgQ#YM8Uk9DFcF4!<-H)8EIwMBRh>c;f&OG}nF=9p>;|DP_ zd-vT?;f9zI$M~VnngyRb#a^4u_vT~vfBAm1Q7{HzPP<*i_ceygonq!^F2&GUzO-0p zp1s~PLT6sN!q7DZovG35aKBkD_-$r!%&b4_+u)5Av*vQg%;p;HH`u!~+OI>*mj2rt zM`s(mi@dZXyq@8t>4#6-?4w&BJM+lHXCFP&o?{H|^u~(O;vX-2jGF)c#)?t-;rJL; zC%WIaLVI8OgmD%?!TVq=hJ^T&rkO@2BoMvasQb|%$$8#^4<884w{uc9`SFt zqO}`8Gls1XzMSx=-2XDn16QZISbKa}zyKb%vv)lbUv*y)O2-whr~ z-ZDJs>%iN-Ke6xOS9c)~)~h_~!Xq*+gQwSi1M_odr~0T%y~<;M;UWCG@n~$B=W)dT&&{Jg+VWW- zd6>U}erNYSjfd1r9<3vVhw!uWI-~m5c^>z#^NB>Ki9Y1_Hd%cj9?nj6%6iG8K3#YS zzs|K=>CpCh9*3U)i(L?pL?0O*I5XAiK)^J8IpE^q_5ddZ`Hgv3MmrRRkI4sDd@F?HQl=Tjfe2h*{GyvSwGM;BMW zv(cq?y-2;}(USVfFU%l4GU?Df4;(?g&__e&LgayC%!`8-k4#?bdq_NlAI>YU&9EgkKQ{94qe@<^g?-_2NNa9rGgfDv!N|M`T_~)lNL@x^4cq=>zqTj)!;MO1O<-!kCu%0oyyYeI+lE^Pu*w;dtP>U7X>8Hg(ENOX_7_>XU_s@WXjU z+t7qgJnXtHf6nxQ`Ykuei+9~hz2wm%58+qD0msVyop{)I+xD&(3(wqkfd+MfeqQz_Hd3+N17oQ8zZ|G8u2rwY($;+@DVRV7=tg z5*~~T&kgNMKg#o%)pN-NULS4I2mYPSi?dsx^JJ-4c^oHsk@8UeEN}cU&jZI$Fa9rI zp2mZ`j7vrzQZISbXGvZndDNfD^SHj}Bip0iL?2!}Tv4(yFV5~Z#)I{eN2?(`gdfih z?OP*x9)JJfuNJWVjY}*Z77KVA=y!G#$b+n(`Yz2wn4QFsVHo*UY? zp3C!qE!$td(0EuZV3CQ3)T=yBle|b=a7NKq`d*j^xU^;5CokiY;UV>sNBwAthwv-n zfMcx}!aR_dhRnyv1LrU=4(fd+>s1~zgop6Mc|}|EH+dc(`AJ>xE8Cw*^Mbw=_51N~ zc6MJW^^!;H49SbcrHBKL)gQ_8cwn=OcR>5`f7!c_G7dO%px@cuPMxw|<#D#~5PmqL zXlp#5=ke2ZHq(BrE!PX;fz7xK^gBE4$68Xa@}NE>F6r6fzvT}7*mG|lyAZKJ-R<)% zFZ7af8R&O*{TdIcS9zQy`Vf9RH?(he=+|LOUADb`%whqH%=IGmDi7*3QXkDH^LZf; zaA{s@{Rhg8ONK{Ar}bkb9>Ndj7H!QA<38-cA-QfPFN_b4F)t3$WBJl1F`}Tra{8{EN26(|I1}j^E{Wv>*SMKWjR*oWSEi zzq8wcI%U1e5eFPA z{UXofdox!{@`7A6ym-(T_2KL+FH$dg)ITmfgdfih?dw0z^LY5#PpUo|GB3>PW4ALu zP`&hqqm>8iRUR_lGcKX}Szi8bp2yVH=j=>bG z$7aX>uCjF6M^zuJm+@#ZFTyX(AU#t0ah}I*hrC5UCoFs8Epvi1*SPTb>J@ujsd-_& zDN6TsGnT6+4{N4x;JfRd69a_qb2vdLi^!^^F03a_oWFB=%egCKZOr+*>cUNs+B|jraW5N@#SRc$71In)ABry zc;tJ@{cSS7eBQ=ewgp`JXTDM$f5*HD9B*4vujWPOgXG6^L;Kckqx-Q{o#DjaZ>jod zoNqi(KjSelZbt3ebNWml*?ICO<+>Gqq54_A_?fgG{u?~@+3rut^#Xmg1Nu1R;3?Je zS0~Qbcu2h(54n%4N6wQcJ(lOO@zTu~a(roc;~cm$9?x8HbM<={@6b;k_3ZsE@Arit z&Mn#&Kal5f)tsS3r_e{+yKdlvN4P8WSd-tP}R5at0crSBUL@M2za_b*bf#^V&ZZXT2)`l@I5zZb zp2y}-%&~smyT3IKICG%i*{M!hukw)lYx2VxMcdG?@;nZ?VD0&O-4@(8I6Iwx)TLhX zXmP$SaVg?}W91Id$3~vKP{;jtVBAMMoSpS!Qm^vh{aoa8hf;^@_V{mBRUd6{oXhIN z*_l41UgaTiU|hPzqrNnsm-DAwos4tHP176avifj#HqJ@C%7f3fBk$*$59fKj<>_M* zohI`_?{gru$+!&kJ3G~BOX?+$`l)i=M(U$|f1bxdyRV*%FUfs_Jl6*1mU0>BcXl?u zNWJ9IlIxZHcy4Il>~NnA4YFTvc;ib}AI{G7k?F_k=Scq{{6h7!yj{?rf=jue|8R%x zIgHfHc+>|ZFOmINx#07tWM1gagTUR_$0#0Bukw)R`-}_DE!x^0o}a=Vv4AF;1^RG@ zEgn)Yd9>tyhH-JWgQ~dzo9ujr~X2e zvr~Q4v-2^2ZW{Ty$Iy53{pr1K-8S&R|IN%ih%tdaoSlvPna@pI?8jo~V@vWpDsC+_ za7f0NYwbP?9DIE^JJUzDACvoqdgSM(jbG<^U_CGJNbVbkM)NQ(PNz@4$@G!EkK+7P z_~E>wt^V6Q55yciu$8Ye9$9@jJJW~MtJlka$@mg^A60(56OU9I;8MP3G>?Q_@+UK2 zmwL&gMIOS>&g+bt1)os>%9<2^}Irkj9jF6 zzFaYyhxr-kcXl?uNWIGA0?CW;v-3Kmp`YY+`qb9XS^x1R;{gx8KAfHPA5t%Q)Q2P< z!mo4fR$A`xT>ILo_IYLV3!`}$mw|p~XZ?rNOCGH+2@m0CHFidg#rb$#?u{>v%ysL> z!`Z1mSg-OpPx2!CI@fNc_3!6-Ot|lO8(+M<7zdm`(C_SYd}&F&4A!8$Zp*1GeZ-@qfd+UW@}Q zGX1I4OCELkH{*gcinhi>VIIl&;`OKSV_Zh_V7Qx>Wi#{TGv{%c2-x6!3g9o-|fj*p__8)brS9ypo$S?I4{##!u&jag^t3JHz z)^Y-W1O3iU$CtX)OCBxhzsN7lAU#rkDX$N#=?xyq{jH}{oMBuB`kkHjAFP)=>eonK zBCnV7*nB*&PB!(?_WJddIk^nO=VP+|Yi~ z_tScK9G^#Fqdtbc&l@Zjuo&H+vR;jc~_#kUb$0wNWJ9I`l|2{e%*L1 z{%M{E*3RU0+wgcG7L3Qq6IZHkFnOx!L+Vu?pBEm&uPcw|^E|K~C3xWfcHq9@ri~7E z>u6q~`e;eL>9 z)XTiIc)bX})LZy(eOxCV)_*kTr13&*n3L37W_-zBFLJ%sgq zIK#MPcu2jBM}2OZ7se&6E&R7JCLE7M9~qq*m(e^}uksia9+Can;veUAinZOSkERz7 zUmtg@yK!}=|9QXmA9bmhJX&86ol0E7kw}kB`bC}x)=L8q{9pcRXMNoJv(+a){P1IX z-@tm6hxDW5*OkW;c^+7U3>*@Dcs$^X`dIw2J!*}c9@X)sl^tKcB6*Rxbmj4Ao(ILYS|sjrpSDb~sY4{YTtN9Tp{K>y+Flt(M8 z58+jped;azxB0jHJQ?dxfd~F?dY?BK2lyN4cXm5-ylrLTQNKv?BK&Yh(bif&9}leQ z1P;(gGoTOfaCY;_gY_~mb(yD=Ul9i!YpjvyfpwC=Bhg2O2hK_3;-2H)IZr>|XT8c} zj$F4A7o1nLE&f?vr&xOkJg~Jhd0}pT@ut118=Wvo^--64$)iOcvG*@e<#}K|AMn8c zaXT`VBHz;fIiCJcuQa4@~OSvTAeg`Bh5=I``qCYxn3gs^>&Bn zL0HQLJg_wi`VVKPJnB-f#zUTCl3(gA{C8;O{PlwMQNRQLH@tW-C#koAerKoGZ7ciy z^s{ok2){6c^hoQk`F;#*j(`L7QTFD^IK#LM^gBD#ht$h>)cJXX@WZ)9TdTu!7_8#~ z9!dY<#e;rQE(86}&iW6jS9!=hy(Rp3ZfM{7eLf!OE5QR>*?azh`u%t~JJUzz{-u7d zwgS%H3 zZN01MBl~%j=#%_H^|QS7$22mM@w~C?_tzd==}w)0)HBy@OU7Boh3AF# ztyTUp>h~{rPKS5mg9G%@_ISVtwfdQXYic)~|Ek_svR>+=e!g5Uk=Jeg&-r-ZJ@ep^ zj4y%t={wflu6oFM@3g!~z2wo7f0G~DP_zxLp6}Q3?so9N*2rA9j0dh4XQw<^ukzr1 zw(v>4h5t4-$n(Ja(7^-$H#0oU(?Gwo)Bd!TxzDc4{Q~*fd7V+Y!*hPTV;mfk@#VHO zZ^#igKOW9b$2r!^c+}-{Z1PLb4*wlmFCP!Qw;MbXecUvf2Rx-*2Kt?y#)I{eN9!KZ zY2wC- zGMWeLRUV5Z9uk-I%<$jAkLQ1v1Z(iXR*(I?ht#V)?v!{)TvBi0zf1QI^8lAgna>+S zdEt9q$%8f4g&+SN+AlsZ%me+$QZF9#kHccXkDb&sc9_#q8%QUg@tZRjnMB>E_=1zS2sLPu9<2 z_l>Wf{E1HppUH}Q`wv!3AJ~Wdj4b+a^?2Wkr4vFRY!z_vgcQCHKESf3MA)c50`!bNDWNKSp>LK73#!MvREl zBkA8z+YqCXo1YUuy;Wkwh&aX%Vr2D}YQ%^Uaf~0vi0}5tIk7R~d;0wt`E?TiU(pfQ z9axqaaSegdF-o2TNqAD6#>g*E4;Sf3p07DWTx;pM>9rQ`C%x8ak83T(>9xjkq3U)a zKJ1&gCIZd~=qNa*qHp3_2czQ!Z=<*W3z|xvEy&spXx;L}i)%8xQR2llCYCo|Tsz{8 z5-+ZWu{`m@9G7ccAYPoy!&i30oG4p{e~0=D%;nAA#(2z$tlUt@5Bi;6XU4TQa7Okz z+I{BC*<0`JkMcdqv$a3Vs!x`uK8pN6pIbTut~mnRpguc|jMI*t4j<4e*Ap4tR(Kq}{WGt{q^3`EA5HNvxk3Z6xL~?2FoH_N5vz+DIJZhi&9~O*p5ZjiY8j)GF6t^4sXw zP5jT+D%XNqo;GrQC%=uKbn82@ZNz9JF^^$ilt;5K)rgTt;ut^7V^iLrk8_qak6b&- zZ=+u~@jp9{T*InU8xNQ<^W|s!`dIO8JZ>hhN3MCbqPLN2Z^gH< zN9VCzPs?v3*3+U!iJ?_u9>c!qdNlh|jTqM>af~12V!Sitx?VUZdp(ZQYL~ez*X{D# zi*>umg&6H6<}vJx+H3Zu8Zp{S9OH-W<@#Yb=e4w#>x232#rk06LX7qj^BDF;?KS&S zjTr4Ej`73xa-A}qv&`+?^7MXQvs{PFZ!gv%BNt+{mzc+}FKVyZmukdlFL8_?ZLhrV z8Rsl>d%gP=u6O3Q*XKcu_7eO1qV}5IYx|5LnZwJvYw#It@2FW(kM&XV4&D{Lja<_$ zzK!;Ln(MT!=xuDo=yp^d4?CLIW<%D1^V^6u;HWcVXqA}9urJc8*_Ud>xHgGn{P5c3 zT68$4P^+EB8t>ZVT62DTvDO^95Tm`sJcfNyd(FO7BSw3PWBh1)Wz89!v&`*v=PyOEqG&mpI0cwpZT0 ziF2~;J!saf=_giCJTAGf>2&?NbEs#o1?aaIYXOoAG1^PaW7rq9*X-T*&%`I%OB~~e z?d6(;IOnyrmun9C?Zujd9&z54Er`^8VM;>sCYDvt59=u_Ky z^;Q&DPdP(zjGs++|Eg!dMDabR{Yi0*pSiVFYZlLv;;HlBtvJRH^}w|*aZXVGT|Vo8 z9=MjJuLrDUNiM|D12KN-TkN@WGz0NvAq2QzF*EYcA*FS_T~O5zg>hib{$MMVpm=v<}vI;al3l* z`>NYa+eQ2gDZceXKT@3W)A6_c`wqWUwR%f6;>T{>L2<@U^3UvVwcpt+z>b7jGyG6p|~|<4K^4pbKKlHh}#TzBG-sU+*Wn}yK!w1 z)`)g6*@!XT5%U=Kp|~yh+|8BMH-5YL8CNBqI)5|8F@EOOw({&Nil3hMS;aAaFiw#} zPtOv?7^ke?&lF}l#>ZD@<_~M*oWeVJbrxVs`wdKB}N{JWBeeG zW?xZ^JX*PV3h_Z6&3=hu_c%w9?yO2+Tv$il^A&>j`4#$nteqv@@VDeDZ~ePH2Workw+`m8k#wz}Wo_TkQ~CVuwe3kdea+Q_ZvMm^?Nf=-R^k|rXsg+m zYQ$(Oaf~0fwOR0ef#SAKPv&#Ub@{7&TMxNl?fK+EjJ6W<81|ujpsi-_-u)v!(N^LZ zKWuB`()8VxI49iibjoXwwzd3RzO5rqUZ`_dVziYwh9lZ)_N5vz+DaVbhiz>aJX0#p zt7*8@VLUnhn^nDDiP2W#7>;PG*_Ud>Xe)7yA8l*FZ!(j%ZX5nI89t!lx~wzr_r2#& zxmxa}dwPh`R^k|rXsg+mYQ$(Oaf~0fwOMd49oyEXtc~fn^({{yqkR)G+DaV55p6a5 zQjHjGC64jKwl)fWkI<#9)0o%x{rT(lpxsy3dlX`{l{khY+G_Tt8Zp{R9OH*=Enm{5 zZwhNze?#4D<=e`4o}^q>jNkV)3g)|$4?p&}ZGxAp^-b-6fw@vLmnaxti`&|xc`g5- zQ(k>_r5GB{mllt&X8+p0Lz=Yp{IG_L+v?@@k$;W0HVdxT=(ctkb57j%U>#qHkym2N zS8-m=zEmSd-$WeahkaACAg_BKKW%y=c)j-Me7ODNd|t=gv8H~Pff#Kij^T)Y*z8L+ zVziYw#t+-tF34+fTRjc`Jm1zyLw))=Ju%u!9K#W9HTzPH7;Pnv@uO{B{=C|J_=S91 zAN|`}I=&L4t;8`L(N?oB)riqn;ut^L)`EFcY+g$pzUO|KTdSEoh|yMJ9>c!qKF91! zHDa`tIK~g#TJF}?@LWRkI`o}`OIbw3Rr<58K)-xK~f| z8g8rhO+!oaZLPR9r^$mDZ6)S0?2FoJ_N5vz+DaVbhiz@hT4^{Zd;i*{t(w=yuk&ri zy4K`DjJ6W<81_YNHG9`LiBGhZIK~g#+8zqeC1TrJ|82glE8={(d{wxum`j8uY|miI zk9TUTAMq3;uldsAyqf*%Fh`X2+u<{-;o`RTsD_*0``2h|qd>#4Z7u!gU!$#USx0Hv z^J?>^RtF8Adyf6ql^7Z(HfEhg|F8S9zD&y!6SSJ3mr-1lr{gOz z+DaV55p6a5QjHjGC64jKwl-vq$Jf%yBSu?^WBjnK zO<7<3wY0UdIG@+cz4>h@W!|)1#Pg=cPxE>8<4ugb68rn2yqeu>pAR<*`eDow!)>*>f8(Klk+v4h zn~K|NbHq}I&#o*VhW^=kZ5CXwv2AUymafI#`oX-_}zOSWD-_#AqvV3`dNwW?!liqpid-e%RJV z!I)Fr*5md5wfs{4daZnDa~)ra(N^LZj%cgdyXQ^f6Ky4q@x!*33;L$ww%WaVd2C+8 z+s|2BpEnVst;8`L(N?oB)riqn;ut?{Yr9~MSlm|Yhg-77udm@tz4nC;ut@ld*=^)dtHO#mMLo}j`1`3nsq#T zoBQ4}}axZxqa7EN`8@x17$`l0I{3ryLpwqJQRCx7x<{$oFjrtHj75F*#x%iWzd~ z+1H4XL*f`euV=6$7(cIf4$EIod_ISh*ZOyTE=7zS5*x$t_=6mp zeWgl_91_R)SrPC3jDM*{j2sfjw2L`x7ko~c^rbO5d@`TIa~^%G{$__5IV6tZh#Z=I zrAmw(636&i5p!t#OEqHTkT|AYn#0%QI{ZOChx;FNjOLISIV6tZh#Z=IrAmw(636&i z5p!t#OEqHTkT|AY%weCUwQpdc)m8)N=O@>o2GhdC^NHQc{O=kSSq4wwGzG5sC@F>*+348v_j4$Zz&B}NX3WBee8hwb%I%^@*z zNF3t_IW+q*HDctDIK~fhX#7ixkwYstPu+6ZSneEppCLY)&*4U^{Y8IEPmCNA8^hup znti28j2sfj_(2Y@ePvheONo&~;ut^3q1lhA5hI7hF@BIk<6lyY99p?~>XyTH!L#w= z>(J&o^|kV2|I-H^uj3&x<~hX1Fq}io>CC=VBgUMLIL43mIahY+b7JP|f6ITD;l*F< zcNcjOqpid|hJ7d>Xsg+mYQ$(Oaf~0fwb}i*ZlOU^f7jPqKcCm154=m?)k2K6631{v zTg|>yBSu?^WBh1ayY)j6d2OtbZ|kLZ&eMK~7;Pnv;fS`Hz5DKw_(WTYWBjnK?bq1W zWd8cIe4pEU)uDR55~I&0j^T)L)9fo%V)VJhF@DhJ{@eSv*FKjReJ*i~AN09qKc+^E zK9@Mg5Bgl=Us8-d*UHUP=o;4?7F?VBO;G6Kgb(C!(o^{yHm`k`&R>a= zv#(T%kwfAbKgi+xp82ixix(+I4z1igg<^&rn*Ep>F>*+3Uc&7{4vl|FF>+|-<|)L7 zIV`^pIb1xF&*7qZU)HgN7&#<1hT*m%hh|@?5+jGiF@BK4L(iHnnz~3aa%kn|DHJp0 z(Co+5h>=5L^Ac_sa%lWZijhMrH%}ox%weP8dz`V?Vg1#74okCE(!P`!IV3iQ;kF`& zW?!iiBZtH>evrdc$M2whDKT&fCpL!R9AfM@d-pp$@rkjYIL43awP60r-!K)88))m$%K3TD zGxxkr=XAtqD{%}*wAJiOHDa`tIL42*wQF9(lREf7TU&q4&(+_*U?;gB@97~%TZv;h zqOE3Msu82D#4&!@)^fpTd9itIb@)E{h1cJx^H*ZDl{khY+G_Tt8Zp{R9OH*=ZNDCQ z{e3>Kqd(7BF+MLT7ktMW&zi#uw%Y41b zq4vMZXF~<^hT^>1Z?>7b z%#ny={9ul>$pw8nM zrZ~or_PGT)On;jdx;E{*ldV7Ib2#_a$8;+|--Tg3!&4RgKaSrWSdux?{96!}PJMe$6 zGiLnr`pkeBIV3iQVZ9=UW?!iiBZtH>evrfA*=x&pS1wYF99p?~D$1eRxAk1)(8{~} zK@N?-dpDxQ3OOV;FX8wwhwaxPhxI?_ufqdxSXcW}V&srGh9h!l_HNBU@rfJ~$M``G zrygUUmk=X|#4&!*mzsTBF>+|-<|(8FnuLeR^k{x+SZ$1ds`cS=rmXN zzrVd=+;27u@{0SC?IOOP*Y5CHt{-n=-& zM??Sgwial(xUDwkls3#?uhv3C|LnFlzOlS{P1}m~AqPMHH=XBV9oKV4MiyBM4Y~4) z{mW2D!wWw59#6v;DXyOKuwwHP9;X)${?W6yYvG!m-|N1Yo8Llw(8q1;%3DqR#qoT2HLc(!v)LUbJ7sflnTMWpoevI3YvS$*uV|8Wz)ecfeu zX*{G}@@V~!@DP5iFSKucF3;o3uU?ht6#8h$T!?vLJOX2 zXL;k2JdfT#-IBy3(Z}thb&B)AWsfi1RQq;g&ASkI7%=cn^F9v|z# z+_$g3EAoP^A@35wzZnaZpZCKnYU_RHE6SrT^=drs79NrM7`{2rY<8ehuu7<~E+?bjQ3m_ATHX|Mt8-W_UVfEWqXamkfU3=rJ4LraF~+mB$Toy+~Ye zPSG~wx;&5fZ&yon3NAw)5BMRMkBqsge_)T}EFMxXd9)S?58+qD0mlZ1@;r|C-8rg{ z#Tgzr2V4%^by{WndoNhPc(7jOahvcEemJja>$^A4WBZ|f?Yi~!f%+Mb>v!3_T6*v( zosYGoUgaV4AMz{WfMfN+JdfQ^-+cnF+r|y1Q*yw0e}3xn%HpqFwu{CiGcRm?TXZV? za8A+Inv>_T}KNuKjiY}O;6ry*0#-Cm50=;@wiubM9vGxekRZ3rYpBg zc;LEietk3#<1)~{=)eBkcu2kEQNK%g2tS-xv^A%U=CQgne)-r9cHni}_H@cvz~8ig zJhA7O-@L}{j0fu_j~2&$;a9`~$IAQUc`R6P-a_hQ@WIaG?Zj0+-9Kx~7j`8N)~h_O zlj}v|(v3&s;5?6~o?0!rUZ9Vq86NP5xcvH{Z7b!4x7&3q^^!;Z>ynqqaewIaJdagI zX6SWW_U7xTpSk(b+Esih3uUkCE9-Zg$@^wGfc~B!T-tNEVo|Tur z@*T^I)Jq=qg_0MEOE(@fPR{c<`i`A8I2x;NHXH>?|)*ukw)R3gnlb6aHJ4-^|bgJh1V)ElWSf{=~Q>pCJq| zFDt_4SkM1?QCIdg+uGXJ_{fQm^t@B-e|?B~(Al+a2P8W6VnupMZ z+9@y2%ygQ$->=^)c@chz(}F*(1HwE|cRMmKte5eq%l$RuQa~X&)I2E6Bk51&ISk{2 zlb9C=H6E;2dC0tj{F2HF{`5`B^Z3Y5>N?J~GxI{f|8RCT&SmCfb&mUy&mG3r@;n~c z?Bb+fPx2BN=kPsTXJ_-E%(&m8J|gF1gY`U*pRTi+j&sexI0qii&c+w1SMzea=rnTN zAKWv~o#*=S-)Q5A^f`W zXrG_waqjqCZbu%Hymc-$yFBJV59m*;srf5aNMfkTp) z%=1%nnN>cbI_~f-^!~Ok^^!-6@sK^Z7j0v2%=7r(%=G>xxo*Atefj~H)5hh#)!d-ZBOSTEx-{G;@9S;mF+h4zys=XqSQ-uvdW|0sL?DSVL2 zBeS2Ve&lD@>HQq*C6B(Rgh%Xi%X*&2$(zhect9TwuRmp5z~yWIb!c_n=iaIJFRYh5 zhM!D%kRQ(t?dyL?#}6LIeQkMSS^JNs$HUjh2}kb#{#y<%t3FsSdGtM=@*qF@5AEx# zruIAz9<^<5v*(rpeVp^~iM#D{!?*N#5bGt6;U7ypBrfzH+SmU#ACKeq8A*5~<4Zsv zU!QW|Zg(slQ68*Uc|0rekhp~EXL)P2d^{fc;*#Y4Ht9dSc<>zLXQz*!yW7=oU!wXL zmU_vfZ>hvX;=*%7`_@bOc(i}HrRt;Y#lzRfEth;|;_Sz_Qhl&q@)&+vcnH5x{VcDK z&Bx;dyUbC2l&>E>58`=$y#CVKOZCrNUZh^-@zc~V`SHBazV(-UJYM+NS_|2KG&~;g zftX;w{^!oDedD-w`#HW0OTFaL$Lm)3;hdta{z9Hd`G>zw;*sbh!vns+<*5C3uI>Gd z>6G=7$FTHcDQ>BVkxtVd?`N{WX zfUy~u(f19kS9v@qI+eJjXNUjRPYv@x|Is>nG!J-6xny`qy~^WJ(P`xMGCV!cW3O#b zvg_8<2kQ6Z;q2_Xm3o!OFNBBi3r8Y7QaUWpWAi7x>(-l(83&v@(C_T@y6uyCmB$j{ zA^f`WXwS^^IOKx0b=+^&9co(Rm&tPhObJPm}BR z?$JDq%Rs-g(|HiciQYKBQjd@w1c%`E}(n zE6?NnDOV@s9In;Iy`y=Uzkz;dr{i3o)T=yN5)a{L=XFLyAIkH1%hSgs{VA^7#y!RZ z9(;W`JJYGuOCG}y3J>AexppfZnv~~p(C(`z;|uiB_Ts^_QZ57i&d$acsaJXMdJ%p+ zH?(i>m*;_NlKp3UzQrS}4`-+P7|vWTeb0+NgkPwBmN!1!iHG-m{eMRD;Cb2ch4nHX zeIvpn@_K1}Aj|{$D1X~{fP=3O_fLI}%X*c^Bccz93(hUthW;zeBk4byqj|s=_2HoL zkb22uSmuS~SHuCw%KdpBC+>T&>Z82Ccx3hA>`Wh0ukw)Vq)+&D<5B-ep2wIw*3|Ex zHZuK~`5WkWcKSSMIMaU&%RHTNvGY2k=1F-TlZN_|``bhx1^tJ!)9=HuUX2HJ8rgqL z`e2^Nqkl{1r%C^j(J5L(eKU)QIB=ZliKZS?U@vwfK^)eoPzmn@#_;s$`N=wx+k3=7LhV|jR z*!Uv#DvxJ`N96ot>BKybbI-BQE6W}a#KMnts}F=31Os*kqUpW-az;&^N^U-iLy$zzy#k+@imol*JNJdct4ZG35Z*DXCzAI{G7 zk;zM+E< znU}r?L?4kl?K>{a1LI3Ga(rRE%0r$b_X$6?JG5W?{xA=WFXe*yhdXTJi_}XV!w*S3 zBrc))Sw6f+p2tQrcNa;YOg8swV+4z!uj{9@z$0DDP4PTSzak6*+()6yEtUjEb>Z4EU)x3~Lh@A(HjjE;wmx5Js|g?;k*CHZx(-AY>nVIIkSr56vL)lnar&rOH_DE(OE z^TN^%c^+8nl-F&;o1db7KOWA``gN(7dFfmImGnIU!jEkT?b|ozd0@Rx@WB7&`|Uo; zIKbOLzq8YEj`fnq@Tw^f#sy~-ZLO>GJh0{@I3&*|^C zdG!57;vsQKy@mfaFYd&{`j2*ohk45Mr&2F@47cTak+|4-ol$dcm`9?I%zWL~hx4fO zb=IpqUXgeRzs|K=>5OZ`JfM$eh6m0}xr~m-u+*zOek<{ixLA#yQS0k@9$0sd`Y3zP zVY2#gcGiDLz2wpNJK-VxI@fNcgO}!cV7)Z(!2it*4?A<9-`VNBko78$SA~c0v-3Km z!LQ_bV9hUZfIiwD4|wqP;p}W)DD{%Zu>70+aBk5ywnKl4b*sQ5=|2kY8=Refe#?55 z$6rOK!l#G>jx{dJ#{+9hfd{sR$0Mr`XJ`75ddZ`24e7@sc?^9y&jaf{fd~F?WbR+U zE!_tCot=JPoAoM>mqj1Kk7tGUL!Zv`z*Mloh0x`^zp!G9`KZManEt@oM-xwdX>kYgop48Gf0mt{#2d^*7^Yt zY~}A8kE}kNo#{jBC6B(pNnV6s>Mi_t#^re)Sg!^=@PB*BXddQipx@b9{~`4%k3S0! z;b-S{Mq@9?^T3)b;E?DelNVnf&dziy^^(W%ZzM0muXF8II%z1+1M7x>2lUbK;=!}1 z4`*k2k$RN}pW_NYo*UZN2lG6zrU!UnYyV($Ui^4CJL4hsl1E>U^dG`6^%nlS^rk!y zthWIk_`m(gXddQipx@cqbu0BMkClXn@U!zeqw*Db9#{(l9H5WpcZ>%-`0;Rd_Iyn0 zC68gz1>=Hqi?;f8c^+7&0z49Zc-JlcP#@0D`j71O!tq7;@!ZgU=+-gkqKQhOVZqojtA@2yl}oAIsd4?dGznr5Cd$`DYi*7 z?K!_`14l;l7|zVs`F>UM!x=?e`?vY`kiZ^1@c-D@&prO0jwg%L;oA* zf!Nf&&w=PAo0man$oIKo4Hdl0wddLIF2ws>d5+>|zuT{v2i<<O+t9KlI?< z{o8GKb|pDHYh=Q!C#j!`;unuJ9OGy83qAWqiUsEKDUR`j=P!NEkna=5Ik>)N%{*!P)rt*-YMZ zed5g7htEEG=JAJ}aPr}^r=NJdgXz1|p>Mt?*w;7S6AXXY6gzz@P7fu2i}bzK8aLBV zMX}Sj;ut?p-)6r^G4yTa<|xDm`aY_37Wq_(p>JaI625MsZ>x8_V(8n-%~Oca+}Z~{ z`>Bed?`81=eVhG7ilJ{S@6HGMwm8gH41HU9cR$d#>7=O``nK}!epKJ`J`bF+Jo@JQ zr+t0n{nKQi*y&p_54wFQe?6}M{^p{E)Kf*V)3@RnKTh9fzeq9kZRO@D#0UDm=^o`% zC5FC<%}cml(6`k)UNQ7-<>o2G2l_VqsfwX*EAQ?H`ZoKE6hq%u-rW!MZE={Z82YyI z?tY+e(@9e?^ljzc{iwd>_u@EXdGyWqn)~|3d(Fu}vD3F=9(4Oq{-E!-&DxrNDvF)H z7038-`ZoJTilJ{SH%B2p(D&y~dk^_kiJ@;|^Ac_s^lkNyR}6hyxp@llfxgXts$%Hd z%Del4zRmt3#n88vclQH*TO8&phQ6)5yC3M=bkbA|eOq~VKh!thtB*65N8fy3zUh09 zg?L{+87O{skAB5G==P!f^{jKHZ(rfLXZ`LIiaj?$KMM!1seI@@@)=P)@b|Xj7(a_X zwMzBY2k2*<;>sBtD~|DV&_VC0UZ;M>SFaI#;|)7i-+mYUo2QjmPVW@6_a_G(^#1DC zZlL|->bvKp_{JNKslIQR<=y>2-xi;X6!-RDuGqYU^EI;Qit0w+V7+q{-!kQT#c_Ua ztuoi{shZ;6{soF-{7~OquL0*|^}WyZBR}TW;whYea~%d>-&lu%927f!E9ODBFVeSX zUm=FRi9I(#KhXE3&uuS!MifKeR&Jg`e4y{uHh$ACspUAu(6^O$_XB<3^B=2;pYheV z&RIFZ(0B8dTQzR02=mx_wAb(6?t_A%?z*JvTu=(Dym(d}5c>c0@7sZRO@E#0UBwI_)~? z7sn}vzOB5wALx5v?>~y4@zpyISu2SZ^u6fczMb^J=EcRQV1BaUyKlcUwVho3&G{d^ z3mE!dwB{ZffBFjffxbU;#YPgJi>izX^!>zs7fU(4g!2V`Pkr}S#eS}0=-bL;{M=f# z{5BOs-&Wq;kLp|AGl?^nRo@jiHRO6k&^O+3`srV8y$I_O!5=naS6(4bkEDM?dUESe z_rG=cDT!C5^2(`qCwQg5UR?Rn#TQ7qc?zwGIN|)k%K2+OC;1*xJn;9iRV(-W1jgTi zRaUR|PMIh5_8R`{eum9W@OoSHsqH-bN_C5wcO*Wq+4->QcmDn6=yn};->m9w6F)5W zgVn3H**L+y{TEhW{`M--e#X7+EH4g2iYsS)Rk7EsV7tb>?aNimZ&UFfo_I)cj34N7 zV`s>9tZ+_Nm$jMGPR*`i2`zF>Dqo9OlZy2cLyN>bhJBG1r*CnI_^DKgp+(|Re#VSR z{+YdJf);zm?KdyA9Z?J|TDf@&@qrc({o$6vr`NF4qLp{|110P%*S<<=y>2ix!6=#n7Uadwv4fDzs?%Z7PNqt-QM*YLRQ1;fz=zo{IX#Bex-+kqR-iNNirhe4xcg52(qw+^cxt z?`6d?exOD3Q>lJ&_JqVIwD{lO**-t+7ijU%8@^fW2dfvawrYZ*#UF0}4~;)Lh2jG( zS{#NHLyJ~UFX8w=ih5nP zSnF`0NS8fNlFvxxsEx)X_YlzKjDMS);8A`8afdFq+HBXmQ`=s{PM210oer^I?vW?~?9G_&o)_e`=xzOb?%Mc&v z((=_*3|(5eF$%?py5t&{IAeKq$+ateU1IG@G9ZR7iFpkB5I>CFi@xyR;-^x1;q>VV zMt}L>(_5*3`YO`pym$17{YYioO}E$u=RlWB$1IY2UU~`hfiCYm?P%4dVy8>R<|f!K z=+f*f)v+(kP+e|2aMUvC^6mS6JZX!&v#b(Bm&6{O;B^aK-l_2!sxm&%W$&u-@qsQa zzfHx^rIj0_P~NG_vaAV%GnPk}T+7tgCDt+}17hfsn8&aW@q;eSzEXMi-UAY!(BUxUt4NoZZ}ma3AE~tO`g(Ft09~%U=##tfeC|VhpvyV`v9svA*Ra#2mD5YOUC^c3 zSE}2d_uj-O`pd1a**f}q!~M;S4bDs20$mbAm&C@#@d@<1(B+WEXQ;~fK$l~eAwJNh z<*TU}y0mg*6pFLzQr42d8Ox(fu5C+Q_B`u8gLP}$l7V7;2J4mcc!(dKaW1;!CiIt4Yf92|nvj?~HeI$MY`I}pNQ+2Z+eeGMReXru) z{&y<&eT8%|vS{<_^m%8BpYhdOu9=eH${D*R zXAS&)M)l3DrAWu9E#zlll~bxOt-GVdXRvzi#b-)<=2Tz$)g4l9u0nqB*^=egeRe9j zPCh$bhP*?Uy{{FlUMPW6t@Tp@Am?gx6b{5BOsk5=ya3C!QAN3M;HGnPeb% zAON ztXZ+sqhcO(`w%bmkMesFSXYRlM`9e{7V-lUiDhiO{rTr$f^iyX%AL@05IP?tc32daWvU z+Ei>5yJ%AlD@3U_`7hfSYm;`+X1bxTB=+|qEu`9{CA67tsW$m9+lTx>n_pe~runJm z`0AH#{$qln&F`N!VS#x_QOFOpIpNuliTz~7(597}w~!xb)9eQoLz`CK-4C>B`E4qO zHm%(A(@mSQUJcGz$^GyD4yI6>&X(G2&B<$X$Af3bcOS zm3Q|8ZCZYtilI#__xyCxW?R@9R z`R0k=xKR9zD8_S3D>p}>*bl7omg>xjZx%n}tHa~In>;&#rY<<>vgqe1=eXUD zxw_A0XC)Y#+W7hPqy0cri~pT{z@TDi%F4U93!1XLG!;WrR_^&J&{VH8Yd?^#Er>HZ zYHI)da~1f{_KTSUs#ZE_xBm6)|CtSI&_!+7E z;S1kR=2Os7Ytv0-j%JREbTs9~L*zbTe3ckFA~rAK@eVq=U~}HrO|CvLvTfoMI{Mtz zFX^0`zQTM?scyE$=3+ls9e&TBleR!d`|rOt<3=~ZJ`OsvyfhU(Gf@2cze+M62 zJ{>g<&g6r#rWE6VY(<*#>??|$rWDhO+lTx>Q)WM+7@D$j za}|=uTdFDk%l08Z$y`bE z<>pF)lev(SfzM|M^N^yiAXv*wI z6hl*1ZjM5+ho&Aq`?GS-J6-DwJbCqVBEsk*Su*t`zd+$f06T1TGce=J__i0=EimJ`NPHA z=zSBhE3Xn8!*J|3IdH0H-&FkKPhM3V;|FbhyEAOu7H;e0na55)@bD9lnx6S~0?tJn z%j@Uc=+{k*HWK^$kWSG?vvc2}lnrep_V*z_Y-73Lxlgw?)?ZBDt%e6`%;W5>R@G;b z#AqXN3^%mV?3;?wMk_Z@-P+iG4S5_|JD38~SH!wGpS)j%{pin9t+jT92z%iP1)4Yk6@V&E9?2UVI{t z#4&z&J+{9Q)#v2e*m9LLPD$TEiR%$>8yw@Fzxs6(yK=pEWf}G%jlJ?FeBQ5Hh3Kr` z-L05q*oXXl^oPfKeU$ZGKfL^}Ub%S+`C+V@1@~bG%$RxdvD2oX*y+1th*j%=H&(3L z1=mjJSRFSLaYn3~2feXk)hKw^T<2J&^JT=UZ%RH^ANfgLuODJugT$uz@U??$(6g@) z;~FH6@q=s7?3;>l4O+Q*D!K;8JF|Ad=k23nRq#9naT_b^^!k0*1Djo}_Zh^98?mu0 zj+KeC#@p@U}HdxQ=?WgN(ruY2Bh#Rr7ERLIJUm-@^ zh-3U9Zf4(9jJR33dFmFoX2Cse*SKwYdO!QL!9DYFd+yC+b-W}-+=z{3aojxn3NhkF z9ODOZGyA4u#LddhQ@6OaUrXHNc>u1{_SAgbjD;b@&HiO5#B)WRW0VW#W2(1NvoRYB zW;EVlu_}1SNat9k-v>XQMx+pX!Kq9?&R=!b}xf>vAAn(;W|%zSg$u? zT!X}R1s3;Ro_&RQCHG8@IL6PbtL;p>n8&a$8Y4XW3NiFa9ODQ2 z{LkArlFzvZ6hog@Zk|GXpiirJL^1Sf<=y>2pJqQ^G4yHW-TgqHWne1Ed5K3zrZKg*Zr_4)h}Ysjo1*bLi9T$18?D zt-QM*=+o>cD~3L;JjTzF)o;H4I@KpJ^hs=9!n%S!t=^_$=+ny0Qz$;vXES#G)2aV- z)li>fZ_MlSdo%5OOT^G8v9S!d7y9(~`wB6BA3^N73Hrfj!dDz| zl<*l*3|(2dc?x~z30*z@jw8j-c*W3_m3Q|8U77u4#rK?cvSRb%uHV4t`g3dNc>ISH zvM$9NM*m1>j{Rgj(FQT%9FlAaf7aw z{O1<(exmV;p(`t=m#`k7E3==h7`n3Z?tY-FPcHeTyo+_H%636lV-{Wz-7e_L;@MOT zU0Hc|KGapY;Js7JqN~ye^15nGPQN1yHj16D6dTQ;9-u4FzCsLL5qoZeexR!xzA;ti zdn1)Kcibew(A6`iJe>bscA$TNu6hUB@?N0vilHkjH%1|SLRV%#Suu2F<=y>2SKs~U zu<#$MvR%;Csn2~-+S}a^bY<~uDu%ACyt^Ojs#)-vMOR&Uvnjy*k! zovsuc&7dBjE6=_{3|$d>Zi0TGt55vux2mhfr(CDHT6^vx(ea0_7F}}oU1^=;6+>56 zZj6d_W%iTxTimir{S|SHALwf5_Z+12 zD`M!1IK~h9E3==h7`n1@^Ayr2bY=X96hl{5-rWy$W$|n(hOVrS)wg$ESBoCFRmT^_PFISJW>62%m1kcehOUS`H$gv1f3 zfvzl`O~ufamD5Xj-bh`Q3)UR#sw-Cw{9xWbX>wjySFHEG`DCKl=}IvVx_zj<(3NLj zA%?DqJvTu=(AC`k*3bLOJ9lE}ikLpI5BY(v z%zmsP3CF46gyohHk!fsLsy=Cg&4Xb_S^*hKv##~_Z{IgQu*Q6R!zPa3SG@x|34EP z;|IDra_1Aoe!ODn%F4}CNDt7J*-ussU0Hc|KhV{bTiz*rhN^5AbTwhOr!@Zb6ygJ2 zSv;GHp(`t=m+fPt3<%wmPhq&)<6FXfg zhMjDo_Ci;leT5jhBBl@QLw=yEcOFANBZ|-Z(@s?@H%}oyXRJBhvma87JT8kL=HP&rQ$|^7!GmeM|U^C`KNw+&qQ&AdhB0q!@X$^6q|+N8{gAj67O-cR$Qy zqhS6Jkw?28|2Ka<9=Fek=25ZBqhg~Oj6d?|*;j~>M`F)S&=2ys^4U*o9*L1h;ut^3 zquCEBMjox)JcaatJR1L|V&u`vyZd1tn+4-_*F3t<``NFyR?A@UgWPvPFtr9u@PT+lTazJbLyOV&sw7a})G~Jf1Lf1K~5G7_~81%yB697 z{gvsd(}zCOj^#C2IxHWv&7ZJ2ATeS_Yz$qW8hCd*V&>Ubh!Hd57(a-a**6s#*V zLVOsra=~Z&#W9<%{d;?6K4ynpu(m$;Bu31LjbU-jJo^eUVn!U}2Qf4Ireeg*%FR=^ zm^JPT&r^$IW@F-vqw_HvdGf+t$>RSb?@Qq1sLJgpEFpq`ipr?*IS+l>Ao7MqZ2_6; z0dZ9Jb$}59CK9$x*qbdhC>jJ5WN}2n#wXZ-Pf-DzEeVJ+xFCwapezvqgChD+q*;W2 zovQDA-|ectw`&sqKO^a1GIi$mSKmGN+*7x1Rrk~-hGxWk4_hBc_{@oqh@lyAkv!1M z;+u-0nUz~6A0PUxdE?S*X2<4|{qvg5`Ces!EQp~QG2g@1#}S%2@ewgJBQBB$npu2P zF*LJs%jDxj&B}|qXts&}TY?PTbW)|O649%?EGIi6ewe&tq z)^tezHhAZ}W<71sX{;q<8#HS#PkH({;@EWJBVuSqY%TR`56vtd&mT$oKr`YZd3bEL zZu0wVr~MB-Y-8_mX6JzFUY#^^S)@4CEMFehOyeV>R5Si&TVI}3GsZ33Gh33iLw>Nd zn)Rs9svljkG%NpbX*DySjeT;((yZBBcFoEIdCiX4agSsS!x2G@W0ROi3bwvu3gej* z9}(l&BrcK%$EL+M6=OWJa?9l7!||-Vz^~ay=FFT`^^Rvfdc8TkPhPWCu3h~`vLJ?L z#C#81A4h2B#7D%?jJQZ1XlC(E#n8;kEt8KAHLC{Zv|-Kc*lZq|*KBC4q~8l9hGxXZ zFszvq9}z<{;v#vVnZ-91Lo+M4Ox-jq2j*vC&3g12eQ5i~1+F(|@157|mYqiRUJx-fBQ}O%&7Am%7@83m$pg(SzNr|R zS-EBErdhR{&-|L{Ibi;QdCj7GH`DQq7@83q!?0#fd_)Y*h>PTbW)|O649%?EGIi6e z^)mRZ6y-G=dSK0kWI+tgi1{A2K8`qVI`I)PG$SsO2bx)Yd=3z9(2Tf99-cQ_*L9g| z_5*NvXW7Fc}=lKXXoCAo99MEk0gJ#B0jQ8I`Lk`?? z9REaKv-4)IqSu?m(2TeUN1QjE_=p&q5f{k=%`CpD7@AqRW%7-S)T|k}b}Fjbv<~-a zAKK_qou3gyGh$;H_L&nO5koWLB6*;h#mDPD2{&j)TqF-QtKQ&0Hj8RDd~n`pm(H-~ zg%U$E;vyX3GbcVGhGxV?@<20-Zz_gnR&JTP`K+~Y*)=Qgkk{-2=X#SEnh_hru+NZ)1SW79v|;CR;R@Eqi=Zk?moo5awJ*cgU2bK)anXhvKl z4>Ys*rebJj<(8?NX4S4f>#o_*A$gx|_3oQ=JR?ptbIQrm#}UUS_l;vC;zYAatmO$&S;(A>6@W^T`=7(UCFhJEJ5 zN5t?MvAgvh!@$rCGDf^`>7lyLKA?(2Av5IdK0hd~BL# zjSuHFyXX&m9!pOTF^)}QJ66KSrV}3#9AM>^$#;zN98eB?$7Pp; z<{aq1+BD5dJLWYzcGorZdXpHM5gWs>W=?!W49$p(Ys*`11ha2F-|zc;`XHI-X49$p(8-Tp4DZ9x>BUzAT+v2+^m`gew%by{-rilx)EgO|=vy(e|jDg7=%>i1XH_X=8p zb7K1UU%rPF*gRtP)txl6(ShidFjgh&U>+x z*|FJsMP9QF>XFU?h;eKZ^LWM9cmBsb!ikTFF^?cFk_Yn$i*G8%eAUV=laCMQtJT20 z{={dSY~&lzbWYK_GOyWAYrJV8Sr9`rV!ns1k0Ug5;v-^cMqDHhG_&}oVrXXNmdVG5 znw5X#|Gc-TW)m0ZH9PH||5!j4#L$eG?_ulX2+f@Mh!~m?7s&(7EWW82npwGJ^6{Z& z)t5oD*`Lj8w#)7Zt7gQ|jMx~4HFM%4VrWKOBo8#R_@-iLX62Ttn`Y&}-{lnb+4$G; zn!S=ghpPrO!@d5Ey~CNE1DfY_(#-me2tFfrx4wQ|9nXJ;(6W;Q=-)>kaes$D)) z^83s*n|3WAPJBcR&4`QSfo2xpR1D3m z+%k33tod?i_CI;emOOu-ehx#*Qx@p#0YR&8( z?6eNo0dJZ5%vNMU49$r79=1M?IBz=f5iv9)E|LeDS$tD5G_!Kc{qpZ#vPIXa#ZLo;Gy7}m^*kBFfeagjXG%;KAhp_!Firf!;5yJ=S7dUNdSdCi`B zcxPF2si%h+nh_hrux3ttL=4S{i{yc37T;71&8*xqb=55J*=6E0zh?S(hGU=3YxZF6 zUHZ%^VrWKe48xi^@ewgJBQBB$npu2PF*LJs%hXk~JJR-Ik16qg_xXZdG}HSU)$ivu zJMa4IbUY)5X2ixYteF!Z5koWLB6*;h#mAqK3pZ#+TqKWmB( z(2TeUN1Owk_=p&q5f{k=%`CpD7@AqRW$Na$@=yGl@$V&kGc@Rqzss3@L0+@wrSH=D z88I{?HiluJIq?xOG$SsO2bx)YQ!zBNa?8|Bv(}xrV+cXhvKl50A}i;Mgqcv!SuPX1#N#=&?x*&4`O|#IfnbN5s&K zxJVvoX7NqM(9Fs$Q#YSgUk0BI&(CXi!BcZpGh%2)Yz)IbbK)anXhvKl4>Ys*`0sLr z8#E&>l7~KP{j%G!>7Q(HJX`Xuyk@_@`RzK_AckhdML5D|PJBcR&4`QSfo2xpR1D3m z+%k3ZS?d?eu37oZdCfkwU~~PhEHN}AHiluJIq?xOG$SsO2bx)YQ!zBNa?8|Bv*wSN zU9;iK@|u1A@+)?TWSa*d&H##6>vbyy?V8#L$emNFHcr z@lD0h%*riOH=k91mbMprWbZkKeP+jItHXEy2Nv179mLR#*cgU==EO(D(2Tf99%yFq zO~ug6$}Lkj&6-^_^UrAQ9MJ0UcR8((Z>rY;#L$e`7=|@-;v-^cMqDHhG_&}4%{$=+ z&4`QS(PJ}k?Uepqj_({`<5{_pKQ=cxYEZAyiJ=*B5so-Eo%o0tnh_Vt1I;YHsTi7B zxn=6+v+}RP$7T_qEx9PK*#^HjL48IH&4`U**k?|BL=4S{i{yc379W2%RJcJi;v#wI zvvQYnKoQNxzn$0Ym3+Nf4%`b$|JK}hY}$BMIxZqKC{KFgPeedfeR#PAuhyY(GY z@R`Le`}JlsFsBV`X5-oX(^fFe0-pzjHM6-!`Ex6lX4NkDH8%BrH#F7k?AY|fZ+JFY zw+`#5$IChQ{QkP+aaDBUbJ^rrx^S3@6@>si*FK$tmxi2V?j2IBaOt|My%& zv-Y34-p4mObo3MZA9c`ZdjA1!YyG+PZ_&1CfxqeVwQcNg{~m1{4*Xqbwr!QI<7Ya? z{notX4@tjaJ;nH6&%_z&@6B`5ZCup$s%*W>=mDcg?YHOthaU3LqYsRstLxN?jixPN2-|51L)uW#6T+ehXtBoEe09u46^e%L4U8hbF$WAiWn=|=Ft z|CU?}kq2{aboYN$`nJF32J&FNnbU*C4<3+b57_ov0+Jr9{b%5ra){X#t+Ywf+wM%SFI{%AvJQ|_{`T6Q+dFi4&j~}gdV&c=p zA2MHKzmUtNr>!#B-1?as`e+24Dij#cp^eyx^ndgjZR4PMj&Z(Y0hWrJ^dZX4yn zddZ_fe+WNG=iN;7w2yn>F}MHR1<(R@m*=MEd|L2{~TT^&jZom0e{Svza=LRyaz5r7ksO7#n-k}9;{b+JT1pdL4QpAsZ+ny2I$MO zeD;UmQ9t!~`u*?S=1Z5_aa)pll}9V(L4M#LdQCjpiASmjG^onG2l8O9(xZzikKOQF z^M}-{JpL&>gdg|wZHJ%r^GJ?cxi+Of)Z^jr>@qm-vtQBUwj}kEM}s_spRazF55M5& zfx4TH9^409*8FYH;4ij&QT1TGrkK6mUerDj@(^eTE57tW_rB&s)6JMnpa(v2T1vx+Y)Jjq5gU@Zneqp`j(O5s_L4MtM^sepa zk@Sl*FGMbKS#{yBKlS$>!^eGO?e#u&4SBF$@+hq*dWbIA zC-j>5y`KlT%%9cyxc%j!O9rld{?+PJ)~h^TCwho3Ar5$N;(k95_+v?i2ljz3dwp|C z^yV91SU^2WQZIQl-X?m8F4!;hYV6SNk97Xy9JkJV4E3{bcB*~1@{wbXQaxBNd6eEF zJcM6}1Kum|--(AEx8>VxUPunu@92RGqo+o9Gd-kUF3!TN{Yuv|k!hFL{*SDE(4!94noX z=Ye`qFaB3$UPxZXC3#j+je4+N(sSRw8ug-Ini(E(k@ia*XUB`w zOCBYTr@{~Wg=RJ#6#S z%=yLDBaYMaTuJIx9vjH;*0XWU(E~Y*OKl*IvvEx7C65Nj58>ylpXH5(c^;3xVcJ6I0S&4$ zp3=YAkN$|`1~{&>Ugg1gq42|gp;xJy=Ye=0A5CXmH!Tp+?$d_Ut31{bpB5ZHhHv%r z0GFm*N0FCt$?%YR$)ogk;Zbm2SpI>ZM{?YXPssyHuwNhw`-SzAM`I1)A^fmk=rw$8 zexAJ6{d?(gTh81+r5?K<|Kipk-DfX7ZdtGLkogb!g*f27;YOav>HS+?gZATpE5if( z;B~{!->>ZSfeF38-pI}iIS&$D(!KmYhi}UBc3OcYIPEtcFVtgh`GCr6_J6nD z4{Augs)x)=$+~^^57q$@JT)w(; zPvyWjpKbSXrC#!A$oN5i*f;bVelpKv-^ZUwctC^ZBkeqp3tSHQ$>Grz7xdcvRO%&< z(yK)e;TPh7_j=FI^Ei8v^EuFC#sl?JkHe07C>mXNAM=OQOCAmRow{`8(aiI>dhd1Z zbHWGOc`)DCKe=6{KJ%=bIG(azcd3^JXi;~Y@;E(cz@jwphF>Bvj zqfOqkj`d6Sz5|aJ(ZyFk%g4^i^Z4cYZ&iP^GUFJz{CMYWqWb&JH6Btg^(ehnj@yFw z9ft0Hy6to1RpN?1J^!6LepDYcJzRer_2$Kum#+M8^M}-{Jl-Wdgdf}O+YT+s^Z4u9 z&n4H{ICh&E9?bK#Pwrb8-u&(b^hZhRC69*82dNA9^KE;-o#%1l4{HgJWc;|_^gs^! z!gsmH~~>@`?g)A2{3u7pYhE_%Gp6@VavL?Rg$^Kl4&@-ys=49G|jJu>I7VKU$f; z+Up0@r&6!-;5=FQVV}@zVj|CD^Shr)^ho9(86L<5U4HTRtt$6Fd7J7{%FL4+tBW4O zFT?@w4PBe+&N$SK7kr9W?jtTpdNkNC1?S1ruJ`i*mm!A-co~<>bwj3K z8XU)jANCEsdav{IfInIXnjXxPa>?+>jANxY$#`0@Us@gX_}C^#+Hw2)_T$CXBaX9i zUFv1Olz9Cj{MZKHcF7HSJ=VYfLOpJq&Uwx_AX{x9jx(Q1z2wpO5Amt+^VQGt@tg8I z_CD*?H^CdpaqG;Jv5#@74a9LeFJ!&sQR4mNg4Zt-H|BXPe&F+Z-Y?&0{X*{aM;xc~ zAl9oqWZWP>?(5rDZ^`qR^Sz4tqv`NK{jMHyocSX&o;IXE$j?_l%V+;E&*RK}FHXi& z^i4V7k2p@xbF7zoG_wJ>od^DeEPV(pqxd7WC=Z zqCAh?w_VN7`x&1am)bxaXXh8GmpmHeQSiE9{>^zFI3_u+S2O2ZSC2SO{lR*bhs+bH z3;2g#6MuZJZGP&WUlM;f*V)Da88hQ)W`5d`&r8WK!~yS>ANBKqKboHFh8z#6mwJ?5 zBmNM6-FWo=!_NcSR0IBq-?sZwQZIQl$fMxr$gQ8`c^t9h9_o+Qqp5$u3H@SyQyYlm z%pX}EypJp8-SimyXP(C@*RHPDmDPYh;yAk>l(~;vlJg67>BeJrPyTor8Y|g#<-Mi{ za=CiMar*rr*2{h=yU_ z;Jqb}=Xqe=EAYUv+Vb3&f*x_4`9ta@kH&jb9^{9*La!yi$@4%R;DJ{&(=Vt z^^!;FeWHi(OSAcZPFvE6N9ql5DLZ-?2jpu%zp!5NXb2DT^D{`_8GF*tBblH6)<1rL z46iF$FL{(A(L?xj<1zl6pGV>k&wYoS9#SuPG`0|b6y#AF&+|C_H0Sfm!2C3hGk-|E zUpnx3_f6`Ls&l?Y23L=*-hGq#L+T}ul8o!rrE~38+B=cwvG_-JeyKWo za4-5Jj#GcIUghyY(L?xgU*C58Pk9~~qxAgZ@W}cjj#Gb>q+ar9NI#LEuYQ(KyRQ=u zJHJ#5o?lq6^7u%q2l=r+-**0sejdsB#hI@ohwG2{S3AE*z2s5aTKYwF@zu}rR^WV_ zT)$*^U_bgJhRTEWl1F1p;UWB3pKsghFh9kb;`B!=aDIv7)E}%@dGLHIy7=m6dG)1y zzhIqi@JQw#p3lYLH{wN)5_xVi=M6_QwuwL>gy+`yAe%K@QYChrTk@(|8TA4?N&Pbt~?K{G0Ee$^=IRe^+z11<9bQz zC69*86HCI+YV3@Lew^olbsNDWIc}YPF^?iw4eLkhd5-le500n8uXF8ITK!#~2i8Ia zhh$uT-1NX+#-)Ze8SV3z%y?S*Z}F+9|C<-z+7qDyBLtaRw^ydHR${%B@=YJ0Yi9}TIO zJW3xDJ%pd_*BO2jq+0MhbwChpI z_6x^#;fMV~ui+_aJ&Zx!SOX3Hf>-&E#v|*GIL`c$8P^-z$~acAU&^=T^}xDf^he7X zPpwbDu!i-}bY94M*)I)#-cWE{U-HvD53JP%9`Hvi(=W!QhBd})UYPm3p&{cJ`GI-p zHSx1N53GL$9_XX$Q`RrclX9tH9WML)Hao8Kcq#aKL;0RO53DIge^i}uEbEUr&is)* zUc?{Ng>CR{$A6yZfpwbHADMB?^3<@tlzksp>ZLzQ^0`N&;PKMqm^+%>(Ts`7A^+!qSr5=q9Wc(<4zwnoN9$5EA{qa{jUW@};Rl|BcHhxIG%7gPl z;pfwV<-LE%^T1j%>W_aI5A0)HYFGnDecH&(3mfuz7Ji7;{V}Om^^oiQ zQo+v~mQ?dRu-*lDfK%By-^RZtuarv->vY)ok-7iT;5@nD_3gxQc^)_>DN)P$o>JBy zah&>t^{O6nU%gcD{zvuXPCT6XX=WUwCg2)ln-^w3Z`fGorv>N9<)i#OlIs`8r{LiF zqm75uOFbI$d#T{(4HNVIJd*iGW*jpv?Rv0Y<*}!X>%s^7g7N5Br5)%@guG@Qictz^nZC_Ho_SBaTxZtd~4W`=va{FU{uvIeti<2c8=a9{AsK zj$7k^d^J4p+{RO>mpmFD7d=E5>=Am+K0MC@&*BD$m!1xho)$>b3>LriTY|%sb`5C0|%&z%)z#mQLdLMhZ z{)m579;}x<8v6?m;fH-guZe^GJm8OJz#sA3%7gWiM`>94MRW;qzf1aT~*=U#^M?^Leg#1~Z=J+VjY53-LTxK)e*k&qr6BzLor3Y2~yB zX8ioVfuH|uyMawN{Zy2^F}>-;N0ILP?BRwBl~?>RIz;pIR7M0}dBy6L@BWDSEz?RX zr8BPVyl3~_-%y!4#`wOO)}1P*&s@cQ1IH+~EU}+f!4njE zda`^LFFLMr!6mG>rZ}2+rs5)b;7j9ER~*gzmf|9L=u18a9{Xf{xx=ha91^do;hnF* zmwdjv>q|V}og9eaOJcr*%F`<*h6?yFq8^|1+tFIT_s1DszoO^+`Rd}%t& zRSaKRxu)^z3}1e7x0i%}O)-3F<=ykZmsW3GF??y|-Sg0w)nBIfzp%&B_>${CxW2^t z4`e_LUlQ{@Y<+%$FD<^N7{0V}%jC-gU;buv3(i zvlhgZ`Ty zhjG0X>@g+&@7^~W9G!Vwc5Sny&wA2(w{D+d?G)s|ix_=IoW7I(^l?I;-88a}^nVl) zqtA$o_@E6%`;2SywD(!^If6)$;L9dC+I_IZYgnu`z86`i!_pp0v-X1I9+?!Pv-h%j9bp z&uv`u2zyM4|92Q0z1OJ8I4NrxwfCRZPqZ}0++Q9%L*@ceM2!9;W^QbK?L`0GvD(~Z zUK58^h|zzL#qgt*YZ`AJ`0__dCo_h4YyWW@F2ZJ9s zzw3Q+FT^5z{RTgty34l*B(l=C;erIikDJ{z&T>uU%L6}Je4k?Y(aJSV(2oay;Uc*Q zIJZI!KN4FO|M_Ei^&{7=L>ngKN3KQL?nn2!E5+&g+AUAj^7UJKzGghm*C|fV*DOzS z;HNJS&ez;GzW1pZezfv9fxLWq;K$ydEZj=MDkq$FR)XQjU#&9Ca;6FJS;vX*Qw%>& zCJ)9(<3Cq1{AlG$$IB=6BlTQ9uERHsX;)?K9kjsf$4*z`GY_4GYk8czxjt#TH%$|w zf8|_~98zzlzop)^cZfpXeBCvpJV#fqn|e%w;my(qk7qg4c)ftJ(&GCR!<$yFX}oz- zZ!!+vOkb%t`I~Kh?OGnkN_dm&+M)%M@@5=QZ*tAr6~UXAf9_mfA6Ist^Q8pCn{S+>hZJ;xAN%&!LqaK6k<-_#9cGw((Z+zw-0?vBKI(Lz zAdbhi7(7+pzmunsvHkV0oKrm6`(55+s%+aB*LzP-jsBYDO4c_Pz*83Arx>2Ha!unM zQ{bteYr9XBPkX3r|`6jLMBC^duajd52a${@oYJ zQxp05ET5ig-R1u-ck=lo=DVH7saZBo^?rQ$_tXo&>Aglesd9DydlC#!oxRWdSze$I z*Ky+e6vI=K$pcTF7_Be$&ZxY3*N4?uoH;Q(s~} zg}>^@d}?mb7j+CL#<`DJJ1_>m;}G*Hi?1nO>8ZC@tlTpB^Ncw0@%Lzj+mvU2so1jk z`QTiSFJCnSWBX2{htAq<|052ZmH9RR_JvlZGxA!wbrZi_^8{$cobEQHmBrO{Uub3J zhJ1ObRrS~YF{_(a&F|#3y1Zwf%~(4zv?Atv*!sA^R~BDW46Ur(GWqk2IPrDG&}uSy zs8wrx>9v~wy}VX0Z2$NIwt^U15%WE4{cW5cS$s_~w6b!`xfR@iNkrYb=HcdRXK2v zp|e(pjdHvyUASUt)taGchKwdC`8t@gQlSnp90;}|67afz+}{53tY_?lvjS5|JB z{CP&4__|_@SCh#DZ}i92RReR;HeYqRE{yl5$0ojz*X^h4o%3F{h#0yN^F3_+?VTQ3 zd`&TQvvSMi&oko0*A+vz$>gDK&7Y+0Ma|j!Gn3J6{Or7L_pY^`&QXb>8?m)EteeHx zB4X%9TrAIs6JJ*h-6oTVy0w=5>4Y4bv8~O~lZRnD1fh_sR6g;%kbb zo0VH8f1VL1zOER$O(qX@s|M!wUHvBaZ<6cBW?naIjUnh}zZvqkcY0(|_^xF7f^Nj_ z)}Lp@iC^~X$7T`T+GhDSrVZb^f@u}_zN31qO{?^Kj__6a2P>9Vts>)pn^yZBgku@s z9~i#&?{j9vTle$hweH_bk2hi*gT%#-o#~Oq*A(OPLo2sTzGD!dA3E_BV%$R|wk-a! z6rc52e4pZI-iC^c<=M3Incr{@7AcrNq&tsps zJKO$Fx9xc8GkmIzd`&U@VC9y{r$5FAC%!@qKM-3MKOc+_7T>2Bez0=O z-R#gOyt*A0PUmx_BAJhqxN`!zFn?+&(&0&lkk- z1F^C6w-x$tAEAO6%erWw}8T{a!FUB6t`{9m9PEtP*!w+?Q{;Rj;N;^zZDSbU#i_`%98laCMlVEk)};Rh@4o(F!g z__|{F!OFYmp&y#}ygYvBeLU}nTW{Qt&wJ=0h98KHrC)#e!QyL*;Rh?XOg{bLhx1=G z#LpHh#P9>LW%2WYA1uC4G5lcVmdVElelY$u#qfibch3VqSbSYE{9xtX^Ux1fS?e5o zWUnu}jt|cHV))6tANGCx3H1Xp{6K6h{rbZX7GF~gKUleC^63vhtn|yf)DOh)1F>cC z^MM~MzE3gyVC9y{#|M5e{x!w$gOzvB13y@NT`~M%<=ykp56wrGVSI4>(0hK~4`(m3 z=Wh|i55&gOuRr`?@ioQpgOyt*pZ@T}&U4!OFYmfgdcst{8r>^6q))hqA0~hCRIF1MWd}nm@RHXy*NJ_1^39eBDC~KM)&B zzy9!p#n%+W4_0oOeEP!=+yCZ`TS;Vv7=9qOEPg)lgT?nLh99ilGWqzx55~Wy7=Ezw z?s?z`i?1t&AFRB49{Qp6K!NeW=ZCl&5jY~zInz)`rHg+_<`71`t^q&EWV}~ zez0=OG^^fejv6iem?Mn#rG+OAFSLm`S`#O#=oW*ez5ZHdEf_&uPcTh zth{?3`k_3r41RFNhp}_=e)#42Z`Jt&G5kPmEdBb!4;Eij3_n=8W%B6{KTLVeTNg@X zg&2Mywk&==@PozoDTW`c+%oz2zz@d1rWk&(^6q)y2aB&Oh99iFdmj3s6}TUz^T{sb zLtlKN8M^yv{rm!}Kg9jM@YD14@6CFM;Rj-4>DM2AIO!KVy8{al6rD!`J(>dXmq+6aAOu_5bVI&++`& zLk#_ijb&K>n`T}md1?_c^d~No2l`ulO)>Pha?9k?4f-4Zx?<>W<=yj8|K_skpPV0| zfA6>R`k(m2n(9vs{fUibSpSb*_K50F4E>3V3V@WlA@=ji7P(%70}=x^ng$)_9ixA>Z3 z=x^oS^T7YczpfbiTY2|9)W5kb`p4B!|JG%B{lB~C2E1PDA%_0M#?tQ-=zskN8|wJ~ z)H~jjVCetlb^0|wbA|N3_?AtXr$P+=ud1c{q2SR2k-rE9e;ju%Ss7`{$mT)*7-kk`PvKr_q?#5tjQDq zO{HMyPs|+H`tm@3;~!riNgJU*agjXKzx;54@yDnCOY`*nJNx#${&PR`l8hVi_W%S# zf8ruMq5oYUD=kc8Yl@-2m0KpCZqVQ2YnA8Md~=c$`hS0iH%R{Od7!`XkN@sT+6DcI zEsMV&RsSOAU!VRDoUHm!Oyu?7{O+gJ|HROrxCl?^Uz>K3`kxs36Bo$?{de2rYs^zw zeAg`rhW;}fcPLNh^7SM1H~#S&ozh0=Ps|+H`tnf!*0Sgy=SLoV*K6q7^f+?=UJvzW zU(i2WhTnZWp})m1AKxMDiwX69(*K-@-DV$s*a4Y8QlL)Lu3xcq8d^%7daqltbZQ;A zbbe}e@YBaOIZ}@?VjOqGc07fTJBzPH#Q5$maj`rjPJCT4zVkepJUD)mXB__Cf4;Wk zuG7u!nTPTiTyjI+TkGF{p^m}C(2TfPTc<}BUsDXttlTpB$Gj0IzOERWO(qXDYX(%o8s-7NVXhv);4g1XEYY{OtBQBO_#EFmBsuga~jJQZ1YE}+> z4&m=X(_$&xoNJu`vw$%;IYiF*GACmS@C?uPcUTlgUHPn%y+p#Q$v{PVJapLQWq1j~eP_t^_bKzt>^J_MfJ{wz<*KGG~SJP`p zVrWKOtgX`{i?1n$W>#*QLO!$jy6y|jth{?3YS!#_4)AN1JnI|B=KP!Un%VQpz~$wA zE*vytPHSn%ulU<4K1U9JTa{vHMqDHhHLC{hr|h)Sv4@871gZule}g} z?6`*>hr~EGiHjba7GH~qF`f|@%QND{*A-(tn@k>#XRSxm@f&qdiT}InE`DUr%vn|M zc-EuWn`8gXYqrX@tLt~Eh@lyAv9?Z+EWV}~npwGJ@{fxnPJCT4G@DEwYE})5XJO6k z*qq&yKW`3=mGtulVrWKeEe)OTrbiZEQw+_l+%knUv-rC13(c&&dmd_5zIUm8rq}4h zzs~#Yp6A!l`57@ZBQ}O%%`Cna5koWLVtGcK__|_fHkmxstobtdY}zw<&CWk-HyzK2 zp&4MzNQ$OS-E8j`OM<$x-T@d^6q)4Su1efOpZv)K@1mKW1BU*V*DShsGab)}p&4M zzNQ$OS-E8j`OM<$x-T@d^6q)4Su=1Q&_y%#*^mSbS-uAR5oiuZM zF2(R!zBKGJi(fuIyG*V(13n9DX7{$oo?O8+`>lU|R#da`=T;?J;&0$dCkt7xr$zI5<@fMA{_C#lf~B*Lo+M4Org1k#n*LTXlCWz^H8&9 z;M%FEX5;tgHG62IM|FNi49$p*Vc2IDUyF#L8F8^ZBTjr>F*KV@9%|MK%+HEyHv9kb znq4}>p0`E}&4`P&b$Vp+HO0`($}LmKXBJ=AeW96^ch5u3sxO1j#vaORc7b!fNes=1 zjbYel7GH~qp&4Z+N4hK3v%&l(TrHQVaeIeNWG49$p(wRL)A z@ioQJ%*riO$Y&N`*L|Uxm3PlW&B}rAxTMEscg^PirGsYgzDdWktpq2UIpt*OZ)>cX z#>bioPBhbUW(oOqq{4mUHD(njnoS}PHLC{hYZTS2cOtLZ;vd=dCNVT4ww8w48h>9y z^TglR5Dd+Ti{%+{;^XgYNFHcLTqKWb_A>Zv{7-q!UeVW^0nIR{-L$~x0n_g5q?y}u zDTdGTrD300{PJ_XS?zMY>DSDz(dWOoVrfbl_}H}gT11R<0CBNABTjr>G0p*#$-{F%D{#HJ%RzGv^j~e7&#EuwkIiFu zT|=)oiJ=*Bv9?Z+EWV}~npwGJ^3PdDocOw8Xf~NV)T|tMrb1Y=IeKrq_x8MI(Vpw; z{EQfy5nD^cKC}4v^8m>S&4`QT8FAv{&jTb6G$SsOhnh75$7WbFyXUy1mDg;u3tzpE zJcywgG2g@1?~mz`#n%)=Gb^`D{yZa2d|fd#n@k>R)(YHn3~OfR&EenYH9OC_-Xw-* z#MaWVW)>gc3zD4BjJQ~y5hp&r7bJP08F7(3)U4X&*z{jH*?2as!*^V6c0S)EhGxV? zIO6`9#n%)=Gb^`DA)i@%UH64%R^B}iH7f@`LxIoyn%S{A@oe5_M?0Ty5<@d$V;J_C z#n&QYXhvKt&xjLWR}9T2lZTpByBwQ-&CF-bJMx;jpBoZGGvZ=xjlbifpOY>>pKmso zS~DBZh91dl=4wI=&4}Ht|Cn0-z8l&X6E7Xx?eD_y$FYZweq#Tl4jP?f$7Az}6-%e` ziA$$b^{*?IPNk!kPN(Mcopj>&#&Lwbvc6YP4t#c*{{5HlkJ4>?AAYKnX3(nr^-374 z@V$a&VD1&x%*L(83oDpr|L6Z+K~c@he_z2g``glLX2<62yYiZCP>*!ZK#XIP*ga}P z^9YNtMZ}n|5*N!e;>6b#W4=0>Je;q#{@g{gO*ZlkXnN0L=*M}@c3R_2di_ld&4`P& zb$Vp+HO0`($}N*$`w=I;t{9q4CJ!|$cRMxBQ zB8Fzf#qx|e@pZ+}Y%+PMS@rQ{_gU$&yk@)Xez0mr49$p(wRL)A@ioQJ%*riO$Y&N` z*L|Uxm3PlW&00?{yJoG9pDDe9=Vwd&n&I=2jlIK}odbsM&il-I%@BNMzZvrTWBI$5 zOy*~Sb3j-#o1c}QUcof`)6)9PG@JPC3Z~hU%dXk*lr)F?JgN8pV?R9(iE(Ta+mRVQ zZ(4jUBE~s@xLBSMC%=YYxN;W?oEN55vtcmE5FXXV@SKD%wrHS~K@#L$emSX-w@ z7GF~g&8*xq`Nzc(C%noTATHERW~1Bz<40uLk!J``5v}@e@u@ozNQ$OS-EBM z=NWP0>x!Y-Wb#n6>QhU5Y}z@Xd{17p-|aR>$1`GRMrLk!J``5v}@e@u@ozNQ$OS-EBM=NWP0 z>x!Y-Wb#n6R$xxsMKk?7!^SW2nmt&1mp*ff7@84VOT(I3d@Uk|X2iwvj5zUi#n5ar zd8k=AFsJRJncml!{cv8h^RB;6$1`GRMqI3|@!v~mp7=9z!O)DjSf2PZa?KNeMlKkd z5f{lr&8mU>8bvi5yECs@-$f%jo)JSc;vyXJ8M(#R6hkvBw@jg9)8gy8FEq3A?s-(R zz_H1{m+%c}$dNn?=$CoTnwP#y=V!#wjMx~4k4=lOMa0mIxLBSMC%noTATHEaFV zf8H#rS??e6nk~BS>v|3#hGxXY+B!Y5_?lv9X62SC%P#;%Dd;GX61h@yJpp2 zuKPkWEAO6%nw10hHHvCBaa>+Aen%r^^2++YMl0}q zOWfP`k7jmXqk3{D&8**u;4@-(>-WcU`guTLP8-(DK0_%VwPI;j4V(kQn%Q^%C+4qU zng!;xVa?2ErBCNIn|wvIk zb`BUnKCjs-{g3H2Ix#dOww8wHXBHnnt5I@7GvZ=-;%7A~pZHmgf}t64kv!C_8klPo z)olJ~UbEe$)#Av-0kFRI|Wm!ePzq*leDV z*KA;st=mBi&4`U**k=}Bi-@5aaj`rjPJCT4G@DEwYF7Tc|Jn(f`R8YL4j4ZquUYHk zo9cA{F*GAC*4F8f#n%)=Gb^`DA)i@%UH64%R^B}iHERXt8ez?BJez%ZUb9V(8q{la zVrWKe48uON_*z5^&4`QT8FAw4ilNzL@~CEAj?E%IYn+(ZY=d8%pgtppX2iwXIz6)Z z_`9K!6Pghh%M*V$RQbf;4HXQ{h>PT*X4RMB*lZn}*X$L2z4?THJj0xJ(*obum_M(R zW^T`=7(UCFhL25)Uw)ng(G1Mb!kRhbS#1T=EbzUcux2*bm^gR^(=0Ha718YM_~5Co z^{lQzu~Pi|st-K7$c`yPad~9F8H(o!Q>MiCd1w6mzJZ_rY`cL?H~mx+H}ADuS1bza zZKSWtu@5R{8MeMW7`vy(?@v3%|My%&v-Y3q{&67oLEC!Q{`Y9x*m09@Tlv}6zeT^z zKfHL`DqHvTjK}>pamF8#w&C-G_+L-+wDkApCFwRUYI{|--evTF(WCa;bN^#z^&GkT z+>QGCH*O2){tK+1)a8aY_W5VRqwzoK)_rd>c*nss8orzxL*|_p*NXNvW=$@CWAw#@ z7dfmJgX_O|@&Iz-)sk@mYsx%u|HuITqkQe({@2!HtIb=eJfvRb@kwc~v>W?`UgJmR zd2IgWKivo(_+OUq;E@M&?RND~wl3ZA(w53Y>Qx@Q3y*@jw2sU3II{A-8^8e_Tb9oN z!2>VqG55jKw>kA&Pj9X~q+aE*o$wHT>E8aI^FNyBvG+Us6CS9$EZ?sq4`c$D_3z!a zviQI2A56duCQ&metg;_vC$z&L2|o`=jIWtr2lU#JJ>v2mRGgY_zp{e_3{ zV}9SZ`gESh0lPeUo%%zr`^dxc)CS_X4^WSi)T=x`k@6rv+pjY!|0U1kM{Av!_%!i{ ze20(y;_4B{sZUE%uksKb$ggwlR@(cIJdcqxAKnsrpzfx8kAyt1XUe5E5Xar9dSv~v zr|2R4uwUp^otw^+7{mI&1FvEETYUU(THu}Jv$UG>kb0>{W47=p=#Oci$@7@8_Nq70 zA7e+R=MM5Bmyg^tUitA`&a>l1>LriT-oiupfqCfF>c9i>s2Bd|J))fl-ebSS(Daac z$)myJR`_AR&};aVydKb;?Qc5c2kNIDX}{F;xMjWMQ93~M5Pl&JcyH*)PCQaQz@?Sp zVfixtkb0HJ9w`s`JK9SuH8z@pYroaj@!)mfjv9< zgY_zpVd)p)V>Nb06VuZ9EZ>C&c+nrj$D2>h8;EG{mxk0!9;F%5F9mrtWnPWUc)2{l zWmulQKwieBod@ex9wWj-_$A2$KbjrJQ-q^laH<|{dY~MyPW@7ndX>kn(l4S*x~Ko= z@H6>w-92ura%~H*p#F9}?6@sSy~<;I;UWC|4AOU+tNQ%`Odb?BeLDg-$%2^R43#Xwj~RbKFY3)T2S4 z7M!oQI`T+;3Qtt!yZ;wyOma7>eMfG+_p03edChRBQsxb@cbhDtj5l$wq z3ph_^z0{*LQ+!%*zTWJ>1NEcc#2*e1l>7RH^(qg!z9v8H8+r|YyL}!6Ph8%!&t~8N z++68}8;IksrXHp2eFyO;`Eg(0w)y2ej~BLod;#k%zmR@rf!|FFWUCFt zaa)l`BYWKLC&x>{`wq>E^E^-s>c#)+D(!k0myA!PUhNm&hbeg6j$M%Fanf_&z8V~o zKflpV)DDtZV%>>GNOzvSnEerdhb&I7sFFEP~X%7)ZSJsO-B z79Gd_H_v0AyNB(%@}DUWo?l!&;yAmm%*+c*9}_)ODiUr-n0-x)Q} z%ky~j4bv7v3uw@k?-K&^MSsL`1FA>%c-c|#c|KJRUpXReGLlI(mQu_4wSW zb3XRi0jF=u?{)Qa!m4w=wLamC6Ce(!bAA6KHqlYwmgr0zO>H3YvUb~`IyWLv;KJdX15Mr`})`0=Pyz( zc{ENG9>UL8Kg);D%=7r=`EON!l;u1}9@r1MZ2H$72Y-3~MaqNqDv#5|AHt9I`L^Y+ z=8u=w&L7b6qblb)@Id|Sn+vab=hmiiz z$8IabgZu6O{zob!U%1f5Q>j;Za6A=$+|Re2xFpZx=@*tHJd*Ll;ei~~W5d-SsziG~ zZ#**NY3Z|~hw$^&&+_Vb^E?jt#qG)UE&8JA@yEdEi-Dz5u)7jCWoXl0ID>aq1FHmOWKXK(X|)T=xW5ub`KY`1UQ{8FArdAn-zxhecn z^;|b>_m)$lQ!aPTw^Fb2m?L@!KVSVUAHFcp;~N|NGwGLP{P3J#R@v*s=#Kg^+Aj^M zmpn>G2@m1N`h44ob*ChMTg&&+FDLf=I`Jv|QFX?3#-MMWTIK5K%g-IIK4rb+(Ku9i z2tV%Y+m4-+_bFo0CwR@4?`+|BdLwxwbKSst$)j|v_(OC__woO1o$u#?y2l(I$YNYF z*Dq2pc{FOGhv?#GkiJtL^Yci?k6N+Mqgb!Ji83_Y6u>FL^W$mVOa_oolz!-VWE5iy!#Bo%fw_jC-YAY6Eec z%?qVo<-y}c_;Fv~w%Or+(46m8%pcD9k@ZI$Xa2|@FUL#22tQx_Ebr}b|KrSkFHXi& z^i5Olk8%FN{czn7$Jux)^-_H!fmc=T zca3eiWP+PIS25ClB;RGvJT-ZM~mdl6sW~ujh(hS6-Fx zmm_xE!~Egs0WDlT;yCq3DRVv7__XLD{Lr@0tNFP+k5#T+U9T&f&Uk7Zkg+xp$LaU_ zSg-0aUw9O~p8Hy!$Iw_QnV%--mrTDHm)bxar`K~OsaJXMK8)xR(gN@GelO4Cp6A!l z@uNJoeH_C+DVN$n9B1Q4_CAbU=aV1y3%!QFk>_!Kyxy6P9}W-H@9Gi9nLngn)r0px zir$Cm&@Z>d>zjcG{&5{ss}00)cHYn2|0vO?1@C{9JKSH7;x)rKel(r&6tx?d z+CUs<{*ZcAkHf^L1@C`UznSkBte*uQ7(ZGW9>yj1=>uz;KcrsrXz+L`=+knC@dKKJ z2VUit|MRFgPW{1pm50oKO2RMA=KtB-<@{33@UTpoaXm9HEb%%@bg}(9qlpgpVGybQ zaCo5H^+)`xjq6e`^(cKR^+%)Nyl^=1`EBBl3=iYdt_SN?9=tDA@H(ou!{_^_pJty| zmY?)3Kpbb|htx|w z8XU(8&Xb4F$oIG`E0^(qhEFDy8YwL0jr)w^#}f3#lPe%^QWh~v~B4XKwrO1%Fe{EUBRG*-{| z%i&o7Pa zbpt>5D0u&)daj=b&M)QFO%Jw({&0Deq+Zp7=edH{4P%$)d0Bs z9wpw_F8H}evqQgNoo@BVnx=;xvrwjn^~LS{lDV&4;(cx5M=gEZu`BX=VC`%0Nc^#u z@jwpa63QR?|DLDTaA8G#_$mIbp z)p^DPwHucV52=@Wl*mK)Vc*beXxPsKvU~sGoWqGIOG`!Rg1?aVzyIk8@HU)*LN zT4{R4?rTfEDPv|wiho1-frR*8kk9cEg>;HP{7X97@>m`o{?`sRc5C^GOkZEJ(vr+taL%oHhbUw;`O|*&3cu`*Q8%W7w+rZj&Gmmfi)(1+%_}gDf{N= zt*2Dh*=dIPL+Vu??3aS~wa53%^T4`|;DP^5r(ehcZTi+*_K4Sv{IdB&>LriT+2Rl3 zhkZh?@!5GESPKyx;E$HW19`}0WV;>WH4`tk^NZB0JT4R-Mf+u^JP)id2p)+)Ue(zj z3+`T{vd8X!G#*l~^0-|5A^gyW(5rew{``V58$9r;W{z9xk&f$jU#cYal1JlQ;UWCe zZ2q6EdphxO#*f#T9>xLrGUK|`t31vU9>UMhAbqF$6F-k+Ug(Uc*u%JFcu2kEQTn3z zL-=*$G4^vmkHjBqn;y)Sa%tzmddZ_PCOm{6_w{YZ56JrzYoMt=93H5jdOWzzw=36P zdx(u6QZIRwIF5-fzWP}{{?R-StSbf{_+Rz-L$C3@@;tDf5O}~JRnPh5wkPh2w!CN9{2}!! zk4xovDR|rt?UUz$HG04UuV&`Br5+<2e7Z7iy7PU0saJViBzg$HG@JkD(9Ap!ta}3< z_+NGAW8{ErZ+idVD!u(*vhhReRUTgv9>Nd%gkIyb@;tDX3^*k74@VE=A(tg;{6ZY?-tg!0Jg`0scqHS8qX%<=%k{VH8kIh@llepHRUTg#9>S0N z`nD5C=6PVv6Y#*R>ACN4=_j@veEk77ew3tM<-z%f@JqA#e-1bDJg|-kc;J8e4R*d| zpCH?<7oRfN`}6_UFPZsAsUdm@KkO5FO`MbGfwein0sbgE;~4Ug%TqH(DudI$Wj>X9 zsYl~7;Zbn@F;UO+z0LtkG@9z!FtJ~^abG|{L(%BKPNie z4{DpAIy_K6^~lXnrC#!A$b69e{0!1}hHvxF#}a?6*4ZDq`Du2Z{3X#t_;uq^zQfNW zIlp9%+jc$dzC%OmRXyZ*r7qogj4klg9?X?;X`i1qGUHf7#&h!HzP|0m!FiwJ znf&MzyqXRV*B=L+y+!4*%MP^pY4$w#`LtiiFU{uvIsCOe4?O1{Jn+Aj;lWJb`Tix9 z1Ap}uJ8q?3?HBSWc%BZRL16;l{V~6ODvuZYeWXJXYk$x%2WBBMi4?N=>Jn(8c^D);ShhFvB z!S{XUSoKFq>Se!_zA8M5j_c>;dEmL>;DP^D&-rEL(k7MTU%jd7!FrX)6*7JZKeQ?I zn)p(l2cE?Z4$1t(Io~1={qgWA3oGwAF$z6!plRUo!VC8#11gpRazF zPu$gsN9qk|(sJ}L4%j<0o@UN-rSruf1@Bu?&SA5Hn@2Xh2CGxHJ(0{=E%F!pDD162g zpZ&J4D7Gx|*trV+QoSelOz63bzgpRK+DVe9uDEaDGQ~ymEM9cEQ}0!ZUpxJ0ii_m= z%kO{d#J3cewtvl_wbwHF+O>Gm`h%utzv8pswy9#v66@pXFWe6+#esZ&FZO|-=Z+pZ zYoh~3=gc~CpY`Nf*GG&Vwf|8Ejn3Kc@T2!XYSs~RVwgS$9e(0-d0ju@xxC1Sm*Ut@ ziqp4}pFV!^Grc2=uPcV1tlSd$^n#zxn6kR8H8iFeezNlJdElq*-!)D0)D^=|R^B}i z{ABfBr5Jv)^6q)yCyQ?>hM%mwdmi}7^z2s*KUukxr;DHX9A@mXG=Adqm0dsK`O0LV zIQElbz8P;rezN#_L<~O>J88V*3jDO?w@$h~jU7`AKUukD^7#^eI()|qBu`y2{AA_b z^T1D5?^TN7CoAus2Y#~nmSXtH%Dd-*pG?nw#qg7rJ9)bKDX^w)k@1Po$#(sO=VX(C z;@D4$`DVNg`N`tz5i$Hk?4$|$>BFOMU696(Rfypy;v#wAr_oQWC3)(K;U_D%Og>-2 zPgd_$is2_K@16&KviO!__{qw<=YgM0&wj=5la)Jpy7@_-CyYHNJ3e8Z2R`o{e!{%# zhlieiGoE)I!{kLAmsg1S9=1N+KX~pvPJI6$_dVd+qZAj(!}B_yyN-Rb?c8D3Ck~0% zu<)MOleIeJdFpOE@jP{MAVxch`5v~Rc3OP@ATioWTqKXSGq9dlk#_QV?QT2qymoRR zMmvf59=4%&T73T?G1^I7B#*XJp8Je_mZm@XJb1UAcpf}C5Tl*Md=J}DJ1xF{kQnVG zE|Q1ss8v6k+e9&5$(q{kZLajc~{ zJ=Rz*RO5}0kB&`&xq}>Q-g7FBJFa!mu9wwM^h)qla&KW-^^!FiRusLs#sqxe8K;8& z;@S}_j9!5`QMcnUnaj%>7to7yc^-rA7IPx^cVgdofw??mdV1^}k2#T*8}jABxYHMh zajgyPk@dy4qoYS{w4HmF@9DvQXdlnK4KmsGajlQ`_POV+ z6sP`l%Tbg6e3tr?@pfKJ{mJq)M<@?`wqYE=HAfKZ^=GG%Yv!S|)T^?dNPAn6vHkV0 zJQmSbu4@8TlWJ=m&$e>SlI6XvTsvi1+RAlNmiM-DeU@cuE7x9Gp4-|NH;`+zT3?QMl(?XQ3J`Go83fK|uyNrAR*Bf%%=+;gAuRKoC#;UA)1U{2(BiAx=+laM{*fwIck(lpc8|p`k?;j*a zKN1(o!+zwNO4w(zZR9#hZX2;q66+^M8;SWIwxKp!eE%RZ+DKd^58KG~ny^oxjcqd^ z^{PCB$8DoqH}SvRtMZK5rD-GAcXHc^^_|!@VziN%?_nG2M~m+tBt}0H7sU)U2c1^ZWp-_qrJp@58F_CExvz{80{r4l85c(`eE2-S=!6>!QA#@eK2w% zMth0*9=4(OT73T?G1^O9BoEu$l;@{opGmiO!xIL0%yJzvx4l@0j9iG(USht7ZK%B# z-#8nEuOImYrr8F zUc_;EM4Y~p{`7fu!56+0@w4}yp4Y@aPwz9m?hO|xE|TZTKi=%bPt(uy@4RST#YOV; zAJ8{w{QDK(dC`F4B6&DgaBV#76U*549UMLP`R?P$SONXH_MNLg*1jVbV(3rI_plA? z&q(N>zEb`9n{9o0QvDeR{nJ;fKYz2WFAwxL{{4!fzm<2-L;blXAoiHF{_#HaKiB+o z^~aijWI_!6iTNJ3zV@d6XXNsszwz%MB!>RPWE*N1_2*iMh+P)_xz?epKh`=V7h>p7 z%=fVUchcYZ_YV?7f8rwTQvCzh`$fked|!ZTJVO7K;{WlSFU zext?r4-#W;MqDHh+sm~ku}{`-yU(3F>xjw`hb7m=U9L5_mZaNWtR+b<#Aq)u-@`W4 zUW<>vzbHA;Ug9Eow7s&XCH7gC_HxZix4l@il3a+} zc+m%(dLzY`Y_Xr>B6%+U$WNX4Wt&f$p8bQw7|)4|@Sz{dfqN3>IcTb59urQ z1Anuvk5B3c#x2|Y*7WHgB!(Y|Sqrv4KJ)|EtVL|xt!>vAUHuTpBTq6u$eOgSAHI3U zMfx`&#P9<#>g3DE6XU}tKJ-=T_jW(*x#V;5H;&BWZ!i3?&ghpUzF#r?VCCKOERTLL zJ^KfV;Rj-Cm0utFp%u7i-qjDz`GRW$yMDmhz~n*ZyAFs2VY7RdTTdVv!(+^z38T%~l_>j#1Wo;n%f%AW?(M&GH zm@g6YJ#2mbi21+8_YV?dzC>Ik58KPNqOni5z1xi*8=n8q>d|}ATnpN5FV=!47h<%R znD1d5YOlrj4-%uj#6|M3yv`i8FboOo5ybtoskK4^Pw9#Iy(;T;J5!TR-Ve%r5%OhgGhi#y}(HlQ|xa8@t z5MQ#zj*5%q+3(gFm2Ds2L-N!VNAu29TqMt;Q_gkjtt-CsqVFg!lBfTGrc>{@;%MHl z6&J~~=#<|&^)69-=S9ybE|RBruu503b=yR^o?)n^Sw37=ld``^wunqZq{aIVd zc-UWg^zq9Q44<$5soQj%WG-JG_6hL?a)8hKwjM6rU+}s4 zq+c<7Zsl!!f+?fA zAB*0;`ObRXO$?tC7s&&kue8T1GC!{=hR?0sGWqzx=T>iBF???2-SfcbR`0lC_}t37 z=Yh|y-X)6Rb1Uzj2R^rYrzwWdt-O04_}uF4RSch7dG|cO9;xoiBIl$*u-aQX|ZuO2UhR?0Mdmi}Q>RqB3KDYAjdEj%acba1O+{(M> zfzPepUd8aam3PkrpPNtm6~pIN-j*lm^J?IJY*(Mh)$n>`?40x&j_^6JuZ@MF*yk1d z&5+MmxW2ZiW&18rUz}+6G3m1;ku!V#+HLmHhaHgl(ka{5I{)9JePc`6zUtV&NBf$! zrR%?Phx-E`+vG^Sek6uJiQy5xLjJV){{P3`+rUY7RrR6;1c(BDy?7lpVqH;b8s+s& z0yGG8x)S1`0YVaRBtTK!Roz{g>aM1$d%7k*ar8Z&BA@{x&gK5l=)GtwULPWGi;qCS zpi!>~yeAF{$cIS$M!Z6K{ks4E+WWuPsgHB|%uIeSldJvvb)Em&XP>>-+H38#*Is*{ zb85nzFAJ*-9`nkfp4l_F1@mRs&smGV&5m^SSp05lwl6;FqpwY4jxc!@E<+>xqUT?z z36odhGUbTZ#e#j8#k{tX-8nX+Uq1I054~M<2$NS~UE?uN9^}>Y=03YVdgyn6nHnlO14 zE>n(pU3_5p_QpBz+LT<+{NyyR&%XM>?-m`x2Yby(0}OY{21XljjO$Bm&+E%kQ$nCF{7SI@st6DF_1Wy%q+qXHc+<~8FFziFD+ z5C6kM()cP&UWLohNM1evLQR;w3YRHIye>W{-VPfz;8N$*EClj{QNYpM?dzk z)L(_kt8f__$*bpwZHi@*SK%_{h}Y3Q!0Y0S@%2LVhZjf1{&2nudkp5#eZ(IgoHMUJ z=A4pugUns!sJ!B42|T~ z^Doqd$*XXga>VOm!G8A8U-L&;mSOSX8SiF#%AH%u9~LIB!ewYAubzLQCQM$1%akKt zhxY)lqZx92%A;>j{Z-g_&7_OYqVZMt^1ao<#_L&>BVI>enLnp1E7#MvP3!O{-sk?X zFnJXgpF9@v>iMyr6U!v8!ez=yyk2ttypCq9NnVKl@F;!@o$WKZ#=+RyWhLTGgP)z2 zYqq_GDOcg_u}H3-@BDqlQT$#q%RD&Fkwv`|K2N5~dCdyKWZSCeOc66Q*qvE>n(d)36}kbX|LEJ>MI4UWYSuc<=g$ zB!5_#yb710k>ji9U#JO_SK%_{h}Tj4ZZz+i?61YVZlxH^>0g|d>lNSgs5HI`lULy~ zG?G`(k9boole`L-DM!2xzAS&tDdsgZS3fh&>*cR~Xo@!plULy~G?G`(zfcn8hFnJX&LnC?h{0lW<@+w@W9PzqXFy<8V>N>pm?rC0kGx1?z@+w@0M)KfxrhB;WSvY&lqlf#RJ)ir&-1qG1j`GT(zE+X#*#CXdK2mP(?{rt~^WdRBQf_xW z&-e5Ho7@g=?|WKqgBPEh+>RdmH@UgA9Od^p8rryn08XwOUW-M$W8Rq zP6iz8q|&{lJbE;)4+~;HVJ~mqLr}qAqs=_}%vmyY8iel6vF<^az9;UtJ+&-M848O= z9`o9aGW7h1YQmJEaG7%M$-Za%0RLV7T&h@xnQ`wq7rqR?<@2>Tk3F?4Oc@H7X?x1h z^B<}SQ-;E2%DKm7STG+hE5oBNxbS88+~0d~dgfM`G88V;_LQOLKU5Q@428>-bC1jL z;&bhXZcFca`m_w^Z@VJJ<%DTVh0E~D@zC=xEDLiy6fRRvm^Wyg;k+SW%22pWIh3L2 z=e!}5Lm3K}DTgw2`WF)BcO#)#=`^pq3vaQ$8^kae0&Jw24t8SPn^m; zdk-0A^7}lB@zL?}Ls@y{h}S`Z4<4S`$TK+}_a1$C7O&2WP~cT(e$1mW@Tz~uVKDna{aCQ2CsvH`EglZPyfz+gV)6`KR>ye@3S&(b07bOdh%a| zX>)~L^NM{R&%dxN{K>c7m~feLzVYgv<;(Z>B0aYx{Gr!;FyS)gJf{7HO#Vx2D)a95 zKO*5WEP0R2%9`=y5A5EAt6fQ#} z$4$?_uq;d&3YRH|GJJ6Rx=7D02~&oi?j_~fGs@8OUs@BU428XveBGoBo&JS{DML^9 zlJe+DGAyvKWo3Bux@j5S{SO~W`v8O~Lt&>dpI6Gz^Dit5Q-;E2%ApLGZ~Uz%hrG8W zOc{E*mz39Ll%eOpv?fd$3VSK}d{Kr@|3bo)p{ILEdGttz!-DnFVj21x;?ZlSW%%_E z`eJ&PUYIfzb_(-(r3^j)!m==BC|sr-%J9)&*NOi0EeTVGp6(^(wHamT`7f;rQ-;D` zNV_zmPCx z=;>Zk9$%8-uwXyN0W!4bFinPyX&HX})FWe?RVu=ip|De!&nso<`A;WI8G5>xl*cP& z_>{djBpC`*hQeM-K0TD7*Lz7#m@*VDQx0Y5`4)YR!#zSGsP}nKVZ!^l!^Pf(bGW2vWDUVmm@JSyVBpC`*hQeM-K0TD7*Lz7#m@*VD zQx0Y5`LVYzwiRV4T&5h!a8MA>IiMe=*!a;K$7KlL<&Ns>o|nc$Vfr+}WoV>Nmnnxn&F5bGeQACuOrJ)$OgZ#vJpZLNVfr+}Wy+yXkH(=r^s{*kwf3Sr7nSl4*WqmeT7{0qy%l%a5$awx+aUhxN!o?8;8 z3_aaT%AyW$5`AmW3%p;WFh=hTHwSqb0Z)-GQ0=+;hFxp4B@-n@tV(XCm9OU zrxA7v^JVx`*FIs{^Pf)mb07UL370A7D<1Lo8m@@QqFDb9hD8p~PUvTf8C<249~oBT82-3*Avq-V#1W6uv3_?XOyAmKbZkkqn>rylRZK zePCIbG8Fbw^68-rz1~X_rVKsZOUk2%GW7fl2~&oiesDQSh6Qu{(lWelT82ON3vWpC zLt)BL*eT3!Gs@8OpH7%E^mH#Nug%yGANR%Q#_{k22~&oiesDRIq1Ss!!jz$>A6yP) z==m2CrVKs(;Bt}-3!XPFBSWqWpPBLO*Z6w;1+v~>z$@2<^TQebOy9BJxR0z0KRDmM z7W3+R8y4Sx-{5sv;L{cJnt2BIUH1)M2j?kQ#sbdVG;VV-*SKTj1J6qSt1#yv!fsLW zZ7$~^o_}Fkm~#-}GUad%()xaV2mY3Xf8~3AJK-|rJahjqGWjp5sZ82j;WFjW<~scg z3Df3!x|ftUmdNG~3uG9cqt5eQeU0kaPfyG6wtx9Z@_mFULt&?|ScaZ|VOf|m6fRQ^ zW!QgJC9N|GQ-;E2%ApKB|0OkH%22pWIh3K(zmPCx=;>b4K{6bjvkZN$_}HtbWq8rT zpG04yQW2&Mg`L7;8G8POWns!txJ)^e;o{~?qE6kCFlFfJUQ%A0QHGxXlA17ODD0)= z^FeMX>Q-+@IB^Alg^IuXErVNF>lwuh={R=f=%22pWIZ1{EK3%a4Gq!Yh zT!xry+_7|re}_hxa}eP&G;$8&`A;YOo||t^xJ)@qFZs~&w_l@p&M4tGzTkm1Pxq4Y zWO(h*d}+<;xg=rA(9;hthyBp=FCZkUfWZK-}UHUj`5sP!jz$>A6yP)==ENbFlFfJ2bV(` zdVYK(JGK>NDD0)=w~u5vD2V42%P?b0e{x!eM|Ntl&G22yh$%ziGBi?#p8s^hl%c14 zNkuaJn>W8Bj)$XVVaib0OUZ9D%Fye*Bw@hg;;O|m z%=E+mG%dsV-~GPS4}~d1VW%*^%_u|9e>!2x(9^x7yf$M$eE#P?m}DqS847zT`SehR zUhgFdQ-+@ICFRjW8G3#^3m4mpG8Fbw^68Nbhv(c6ecpfUzs;HphZs(v<-&;P3=7`9 z#d-f_xsQxFuV?<>S-fUjJYe{5c=lxaFHf%U-!l2n|M`tkfn1Au_1M*!`$#-zRN$8v z^BR0Qdq3+t?;~<8*mGQl*E3MbVaIuW0L~xq=U@GvX-&2Cj81xvJRSb0OK!^Kza!!0 zy?;*FOUbuQ^*g@;&u8h+`vRW5%cSdiemS&pmm=GlAI^Vo$M2|P z@J0V@bSeLX0p9+qA6OEJiw_O!60}nv`{?tQ_|Lo_x#G8KZ}`&he|MrI)|=>fUF0|N z&3lUe?z(Q8jwk+yKYu6b;QwHP4wdzNH{Ef?8{b%ca-t*FD>~->bEKnWyNr%a)6rjk z+&f4E1v|{pK^oK^Pk8w6)^0ia)$b4;syET`nn*{akM|b+-Tj6JQsaMH1OK!_F!X_k8SYnf0WUW=+jN=2iFsk&-G)H zj-LwcF&EQSe%^6#$21))*L~(~Nk1m&@N!NqA>R|!9{aK0)E=*oaw$p2;%`pV@xCwn zKTJ>AHiJiae<2Oh54H#LJt@%<>lGbyKNsnU^f~>r(Xrt)9nX9Dr=G<2pez>u{Wu*g zCD3we3HjbBI#h3>Q%zUEcQe$2&s6CFPp>4@|d(ZFlR zded|;opew?j=o}?4qlU70KGk8y`p1(KhhED<9$VcbGx(XIF;=B@Ect}SijmM$mNvx z+gNX+Lvo4q716+Jhfd6*BeVzGWRTHQr-65l>&O12TwW3NG}6aAivI3;?YKS7>Z#fF zZ}#?JIZn$s9dof>wa47)NJmMz9Q&DkI;`J*%{U!S%LE;<-bBY;Q9mMmMQy=r!~Z%i z7q$WaC=>qu$je<%y)AfUf{s|P=-B`DNJpfPcNG0Cz9pXy(y~9%U!0b4I#jRdnEQVs z9g)5w8hCB+yZLmOeq`*J$_liM)1i7r$Nq0d{fOatZy#`t8@dTvWy^{ZPH4WA5!yE|EUn zn|B<=Sf8$E+k$Q=G}fEwcyDZv*e<-I=8(nTz!%I({S45$RL;dB;N^nAINMZ$~A^ z+qqb8qT|0rIwE~}^(+0*@6Dpa$J=4XKXsY#?g_cXdPT?n8zLQ%KGm0ZJX2uTDVM>w zjQ1C(Wt~-h5$TBZ<<+nBWADvx5BA$Z+3_~kD?0Y09Tk15FYkElgVS_uS6=lL z>E$TOh3DQDEbY`1^1Vxbvg#Ebb8n385$VgTU+HJ!yYO|M?Jul*@bDlP(dD#^%SH8y zj{RQ=bcjBcpLZP1kjpPrZoHiJlBQvli|AlEftFKC$oJ;d9mbnM4|qIM~wf!BsJ?T7c<(V=p3QN5yL zF4}X^cMu(m@p~W2M_GLT|M=xMC%H`M2lGiT$ai^ak65qh*pK#0^r`&3?o&(1_ZI2teylgONAx??F1(}Y zZ#2WM|NfVKQ?g@&Ona~#r{&ZV^0^(G^vB|OEBcCP;I-k5asM-4eet{57UrLRoy$dK z1zJumA>WePV=mU4+CzF;(jPlBBRMlLEVw9Ba_idJ`SL9O;Pk>E68Ka7KH)|Bt^v>BnNh`~~@(j##hg z*nd~lk4Rr${YpRm`_t|5!Uwaav9-Azxa5*`MgQ`!Qa* zUvmC(`q!rQ>!gGA zXWIk$Tt8yHlFQtCqMnwF;~pAKx5op2=$CAs&AvOBpu_n&wS;_Wo~?Ql9dC+uEYj!q z%|^q2nx^CL{_$?3!^ZuIxI?x*kk9Fe^@@)D*dC&9cI}=z`on2DR(|(4ZGLOxOQt<^ zm-GYq()?ESCOTqVY%bELd-IORJ~2(lm4E$ht{)kDI;kJXm-J(QB3`)v&Zr-;UGnNz z`ry;kbUfp-zq9^A{TN06RC=oWXg`q8`%A1>?J*bQ$fD2hn~g^QJWa=?cR%Xg8ec{Q z{s;0cX?#(=iHu$Hpti>EJcV1<>i3h>z{dj+I=08GL-YJ-+)$KlR&D#y_3xx5(%H zcEXO${c5zQkv`I2^mqEB({w!QqpwYNeKE$zX7yX-b3Kjqs=w^VbG1tm4ZJqEU)V3N ze6;1yeZ@m>ms~~@{Uxg($oFLRTh*KBi1R14i^|VC9=iWD9iRBvkNLQtiSw%r=?C(; z9h;n2z9rhz*e<#^?|A5srscBrrsbp`g9*80+XMNMe$2&sQ+tSxlJm;b|6`htm%rer zY@DNPh7;q9^K)tm`Fxz4pkwZ>QBPyK-!~gA{>?NUU-QYEOi!sR!%Tayglu~tpX+I? zH?>DR-!DnW=)X_X@$9P~Y~u_2$SC8llMen#KaektFZ&a8%nf6E#Po1y{_n9F;~d9Q z%7lNzOMU*r`m^nUe6Am{-qarPT=a#~@_!dUHr*b~O*;5LD$oyHPWE(vf{wZ92dG_; zxA1TDnS45^AB&mxU_s;U;dH3p)E?g$_LYjhLLzW+_^0`FP(KC}bg(?>2cWk{tT)l| z6@d=X$NP%@249?}qxa0`B>fm==$O{Pl z-$8UNeqox95C6kMlKmJwZk!IM<Kd=6@9E2g*%!sNO_}`b$YYJu^ctFMWxxSB@U-?J=nz z$mjYI>lGdQkBIFN+oh;2c z&hXd&=rNyh`|&8JgEVCI1NoBuP`!x``5&=eoc`Hp@Q>5&@%k5L)+-N>)8Vw7T0%az z9~1t^+ znHm1aC*J4d%W&T1qPqeur}&H zq$AQN9`cUE8F6jqP2)>}{XjOir?FnqvHz`+j!0i#{YpPHBd*PB8efJJ{Whx~$d>3( zy@`%XA{~)F)t7fXRuHGN@nymfVmZ}&oK z>C3BM>1Y08x;?J=o<}A7F(?>ckT2N})hjyYzAWlTY!}s+cRcpj({x<^+J`3pbd<5{ zDnt5#d~QEty`p3PtD;;=&R@>V@Yiq6j4z9GZpT=^%Le0qW_(e-i4KkXkv{Q|cO1^p z)AwZNFN+1^3-bB+66;NLd_!!H*e-eXEB*8*r{%Jnoxf!KG2WAH59D+Gi1mt&xj28? zFFEcH?w+P&Z2t1UyIe%O^kZ!PGC8mO+SneEKHZyl9L>;E=A}L5-~OvT&hKr(D--jE zSg+b+KaMME7v53yclvYF?ZNySUyi-h>0r9kGERr;O?1S0)m)^H_ZIyvekz|1&Tr3T zo^Mb&k_*n=u20P8_G3R0eY!XAcxEOYKJE`P`oa3g+r#ITsyDSqoKNmY`ts^m`mus| zA!Rej*fFPpcTdPA)+;*Z{WDn-L#l zeQXPhTV}>vrt>e*GC@bIS9I*haYginJM(`Be>ttE^S52$~-B zDYVC2q%WU>aAi0nF2(P;tKSYX^K7Sq_nulpJ|E{|y`p13&TG{!2hp)OW8CLA$w`OB zr80gp%W_&yEg_$;zr=b)$DI7rNZ&zp3}>v1@w?j6kHt(JMrBDqkT21pdPT>6tY7V- zd-INm9y}`!vkiw?KziB$|cZsQ|{2Na6+iZIvpXqo3t?J*bEp+sL1 z4ZL<{Mtku4l1V=@>p)He?>x1HeBYt{4yrfN@s&|OB7MB0=x^|#X}RzliPDc@fqo$0 z?TL<9ujts1=c2EO241`CgMn`4qwV5%{-htHj9ipQ`hk3|9}{%U#re}*OxL}6$D_Y8 zO$WbKm-J(z-@0s0Eg@gh57n#om{a_tBpr9ZZ<-E%Z%z6!%=8zhfn1$hLcU}_RIljR zkN&CX%WDUv-}S50bnu&DNk20FI`45>PAwr{(o@wdI_547<%&Mlmv_AQ4byb+yH`m+ zGUL9=kbWSa%O%z;I`-qa=+nJ<$GO|5>EO4Ul738#``PwDzC?%W6&-VNUaNM=t6%9G z@0zBA-yf2Gj3(&Nebf)+^L{%y-+y>0SM>RPv(eGFPSe3}=tw^n3*rvQ=kc*vui9fj zo~vDE*Y2qsZ=R-u-+@W`F`=itQ~H5?Za*gIn0tIEcP^%TjkD4Iozrx%&7~iUnRPKJKG%;}ujtsX#rBBpl2^ad@g1hI_vKL*{QiUVW0+|V zr-65$T0*|mZ&k18n0rEOk4T^D%R65DmT5Zp4Fl3)`tj89c%jpBY6rHe#Ikrb^ z7r$>dI(o;nT=-7!q#re>gC%6!1NqW8w;$^j9dl}rc${6kr_Q~1nhw5~TKX}(YMc(I z<Bq&H=Nr5&m}#62 z)tl&eN^FlvUo1BFaM#E4=^!m?J{Kwz9lyJCYzxRItwV+aY;qQE8>39CyHA|OW_J3Z% z^HUFa@UrKxCVa(Jdb#yy z`sUbe30IeXF5$6qa`;+U8D{FeE8!2k?)MWOE9au1sgvz<#}9rwQ}5>zt}cBc;jwaZ z==sDQUt7zxbHcZL<0BF-Qx4-DbI7Ro_wpX<^LDr2ddy09tJS~po8r5-d)=MJPP@BR zf6;DZr`6j64DV&9KIy%@S$*Prd08I+5{5n{94^^!o*q!2JbyJ|>XWB?L3!m+pH>Ff z#qXAFCro|v^h`Oq@_Y62!>ad>-@UjkVd|5oXUfTuAN9%Wy(?krlc#6O$))GhnR-8$ zF!kwd=%GG&dsY*sK6!eEo*cfUPkIkC?>HBI()*RO`o#Aui-LrqPYLTHj(K`Oee(R( zgsD%S?gizQLwy?l@Rd&rIkyw0K6!ekoLu?6difK-{x^}Y+Y+Wed3vUtoOY)^dA)Zf zOnvh7OgXvqd^%I_=Mttqoee$ICvVSc!qg{E&(M>@m-I>R)8-xLqEC8Hc2=MGo@`N& zF!U*5UBod@52#O`znU=h$Lk^7*1ZdA)ZfOnvfnFDZZg{dA_@&m~NKIvaYZPu`x@gsD%So}uReebRf;dB@r6 z6Td^E_nlLp=yyHvV<+Fk_niaUpD@ywg>{X`y!LwWSN>Wif3>E2m-k+faG7${Zx$aG z<_o-MlFw(fR*&PG8oA>-`PBQ=vwZS>>Y_oIdlTW>` zJpUfEP467u-u_sTr~>4ZP@ znvW#x*YnGHO#7cR`KvYE)Bf!%5>^_IdGxToF2!}dKc9K;hySeqUh6hb&iZza-i4oS zBfbltW$;g!Z6qA7gx@@x*hbZ_{8TLGbi!;SPxq4Y%3&LMz15m9+ep|;Ik=4maZe!c zDceSRPk*+JvULjoucAl#?!ei!k-i}?-bNPph|LplXrpX;xSS{d(ASioeJ$Y7*OV>^ zmiAhV@5nlr_M(rgZ(OjwN;J5^-Uw>EgkWIwvP+ueE0HQ78JAhxES

    7k6Ga;RtDh79`V2=nIZ=d6*j*=b2v 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%0A7_gK{4XRwp!EU=LTtuB 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 V`;`8w#?4|g*KS;^2A=Nv`wyBwBJBVG diff --git a/examples/models/resources/models/gltf/robot.glb b/examples/models/resources/models/gltf/robot.glb index 73f5bf44ec1fff629b4ed949bf4ba29a5cbfd78a..549011e75ee4ab8cd69b99743df72020d685b7f9 100644 GIT binary patch literal 1602604 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 z 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%0D1Lm(5^wA`BLP&KScWwx!Y5a!G03$N#uFoLyq^) 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�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

    rU*2;-DQMN>*C4G=a^bfvUXUr9okq%>5@ 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�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 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); + } + } +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -4370,17 +4389,7 @@ static Model LoadIQM(const char *fileName) model.bindPose[i].scale.z = ijoint[i].scale[2]; } - // Build bind pose from parent joints - for (int i = 0; i < model.boneCount; i++) - { - 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); - } - } + BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose); RL_FREE(fileData); @@ -4681,6 +4690,33 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat return image; } +static BoneInfo *LoadGLTFBoneInfo(cgltf_skin skin, int *boneCount) +{ + *boneCount = 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]; + strncpy(bones[i].name, node.name, sizeof(bones[i].name)); + + // find parent bone index + unsigned int parentIndex = -1; + for (unsigned int j = 0; j < skin.joints_count; j++) + { + if (skin.joints[j] == node.parent) + { + parentIndex = j; + break; + } + } + + bones[i].parent = parentIndex; + } + + return bones; +} + // Load glTF file into model struct, .gltf and .glb supported static Model LoadGLTF(const char *fileName) { @@ -4695,6 +4731,7 @@ static Model LoadGLTF(const char *fileName) - 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 animation RESTRICTIONS: - Only triangle meshes supported @@ -5039,11 +5076,41 @@ static Model LoadGLTF(const char *fileName) } } -/* - // TODO: Load glTF meshes animation data + // 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 //---------------------------------------------------------------------------------------------------- + + if (data->skins_count == 1) + { + cgltf_skin skin = data->skins[0]; + model.bones = LoadGLTFBoneInfo(skin, &model.boneCount); + model.bindPose = RL_MALLOC(model.boneCount*sizeof(Transform)); + + for (unsigned int i = 0; i < model.boneCount; i++) + { + cgltf_node node = *skin.joints[i]; + model.bindPose[i].translation.x = node.translation[0]; + model.bindPose[i].translation.y = node.translation[1]; + model.bindPose[i].translation.z = node.translation[2]; + + model.bindPose[i].rotation.x = node.rotation[0]; + model.bindPose[i].rotation.y = node.rotation[1]; + model.bindPose[i].rotation.z = node.rotation[2]; + model.bindPose[i].rotation.w = node.rotation[3]; + + model.bindPose[i].scale.x = node.scale[0]; + model.bindPose[i].scale.y = node.scale[1]; + model.bindPose[i].scale.z = node.scale[2]; + } + + BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose); + } + else if (data->skins_count > 1) + { + TRACELOG(LOG_ERROR, "MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i", fileName, data->skins_count); + } + for (unsigned int i = 0, meshIndex = 0; i < data->meshes_count; i++) { for (unsigned int p = 0; p < data->meshes[i].primitives_count; p++) @@ -5065,7 +5132,6 @@ static Model LoadGLTF(const char *fileName) model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); // Load 4 components of unsigned char data type into mesh.boneIds - // TODO: It seems LOAD_ATTRIBUTE() macro does not work as expected in some cases, // for cgltf_attribute_type_joints we have: // - data.meshes[0] (256 vertices) // - 256 values, provided as cgltf_type_vec4 of bytes (4 byte per joint, stride 4) @@ -5092,10 +5158,17 @@ static Model LoadGLTF(const char *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)); + memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float)); + meshIndex++; // Move to next mesh } + } -*/ + // Free all cgltf loaded data cgltf_free(data); } @@ -5106,6 +5179,217 @@ static Model LoadGLTF(const char *fileName) return model; } + +// Get interpolated pose for bone sampler at a specific time. Returns true on success. +static bool GetGLTFPoseAtTime(cgltf_accessor* input, cgltf_accessor *output, float time, void *data) +{ + // 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 < 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; + } + } + + float t = (time - tstart)/(tend - tstart); + 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) + { + 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); + } + else if (output->type == cgltf_type_vec4) + { + 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; + // only v4 is for rotations, so we know it's a quat. + *r = QuaternionSlerp(v1, v2, t); + } + return true; +} + +#define GLTF_ANIMDELAY 17 // that's roughly ~1000 msec / 60 FPS (16.666666* msec) +static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned int *animCount) +{ + // glTF file loading + unsigned int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); + + ModelAnimation *animations = NULL; + // glTF data loading + cgltf_options options = { 0 }; + 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 == 1) + { + cgltf_skin skin = data->skins[0]; + *animCount = data->animations_count; + animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation)); + for (unsigned int i = 0; i < data->animations_count; i++) + { + animations[i].bones = LoadGLTFBoneInfo(skin, &animations[i].boneCount); + + cgltf_animation animData = data->animations[i]; + + struct Channels { + cgltf_animation_channel *translate; + cgltf_animation_channel *rotate; + cgltf_animation_channel *scale; + }; + + 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; + } + + if (animData.channels[j].sampler->interpolation == cgltf_interpolation_type_linear) + { + 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] Only linear interpolation curves are supported 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; + } + + animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY); + animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); + + for (unsigned 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 (unsigned int k = 0; k < animations[i].boneCount; k++) + { + Vector3 translation = {0, 0, 0}; + Quaternion rotation = {0, 0, 0, 1}; + Vector3 scale = {1, 1, 1}; + if (boneChannels[k].translate) + { + if (!GetGLTFPoseAtTime(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 (!GetGLTFPoseAtTime(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 (!GetGLTFPoseAtTime(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, animations[i].frameCount, animDuration); + RL_FREE(boneChannels); + } + } else TRACELOG(LOG_ERROR, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count); + + cgltf_free(data); + } + + return animations; +} #endif #if defined(SUPPORT_FILEFORMAT_VOX) From d3c1a04983ee8fdc5dc3bbad5f77c572dd20c36c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Jan 2023 20:46:33 +0100 Subject: [PATCH 0199/1710] REVIEWED: GLTF animations support #2844 --- examples/models/models_loading_gltf.c | 41 ++++--- examples/models/models_loading_gltf.png | Bin 21286 -> 25896 bytes src/rmodels.c | 135 ++++++++++++++---------- 3 files changed, 97 insertions(+), 79 deletions(-) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index 879715906..c19df3f5b 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -27,46 +27,43 @@ int main(void) // 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.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 mode type - // Loaf gltf model + // Load gltf model Model model = LoadModel("resources/models/gltf/robot.glb"); + + // Load gltf model animations unsigned int animsCount = 0; - ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); - unsigned int animIndex = 0; + unsigned int animCurrentFrame = 0; + ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode + SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- - unsigned int currentFrame = 0; + // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- ModelAnimation anim = modelAnimations[animIndex]; - if (IsKeyPressed(KEY_UP)) - { - animIndex = (animIndex + 1) % animsCount; - } + + if (IsKeyPressed(KEY_UP)) animIndex = (animIndex + 1)%animsCount; + else if (IsKeyPressed(KEY_DOWN)) animIndex = (animIndex + animsCount - 1)%animsCount; - if (IsKeyPressed(KEY_DOWN)) - { - animIndex = (animIndex + animsCount - 1) % animsCount; - } - - currentFrame = (currentFrame + 1) % anim.frameCount; - UpdateModelAnimation(model, anim, currentFrame); + animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; + UpdateModelAnimation(model, anim, animCurrentFrame); + UpdateCamera(&camera); //---------------------------------------------------------------------------------- @@ -74,7 +71,7 @@ int main(void) //---------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(SKYBLUE); + ClearBackground(RAYWHITE); BeginMode3D(camera); @@ -83,7 +80,7 @@ int main(void) EndMode3D(); - DrawText("Use the up/down arrow keys to switch animation.", 10, 10, 20, WHITE); + DrawText("Use the UP/DOWN arrow keys to switch animation", 10, 10, 20, GRAY); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_gltf.png b/examples/models/models_loading_gltf.png index 654444b848342eff35d72b5243671f1a1243cbf3..34748a22e587a86dc297238c939fbbe148fb5153 100644 GIT binary patch literal 25896 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 literal 21286 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 diff --git a/src/rmodels.c b/src/rmodels.c index ed32751c3..786e5ac79 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -142,18 +142,18 @@ 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, unsigned int *animCount); // Load IQM animation data +static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned 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, unsigned int *animCount); // Load GLTF animation data +static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned 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, unsigned int *animCount); // Load M3D animation data +static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount); // Load M3D animation data #endif //---------------------------------------------------------------------------------- @@ -3829,6 +3829,12 @@ RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Ve 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++) @@ -3847,10 +3853,8 @@ static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform } } } +#endif -//---------------------------------------------------------------------------------- -// Module specific Functions Definition -//---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_OBJ) // Load OBJ mesh data // @@ -4690,7 +4694,8 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat return image; } -static BoneInfo *LoadGLTFBoneInfo(cgltf_skin skin, int *boneCount) +// Load bone info from GLTF skin data +static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount) { *boneCount = skin.joints_count; BoneInfo *bones = RL_MALLOC(skin.joints_count*sizeof(BoneInfo)); @@ -4700,8 +4705,9 @@ static BoneInfo *LoadGLTFBoneInfo(cgltf_skin skin, int *boneCount) cgltf_node node = *skin.joints[i]; strncpy(bones[i].name, node.name, sizeof(bones[i].name)); - // find parent bone index + // Find parent bone index unsigned int parentIndex = -1; + for (unsigned int j = 0; j < skin.joints_count; j++) { if (skin.joints[j] == node.parent) @@ -4731,7 +4737,7 @@ static Model LoadGLTF(const char *fileName) - 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 animation + - Supports basic animations RESTRICTIONS: - Only triangle meshes supported @@ -5079,12 +5085,16 @@ static Model LoadGLTF(const char *fileName) // 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 == 1) { cgltf_skin skin = data->skins[0]; - model.bones = LoadGLTFBoneInfo(skin, &model.boneCount); + model.bones = LoadBoneInfoGLTF(skin, &model.boneCount); model.bindPose = RL_MALLOC(model.boneCount*sizeof(Transform)); for (unsigned int i = 0; i < model.boneCount; i++) @@ -5181,19 +5191,19 @@ static Model LoadGLTF(const char *fileName) } // Get interpolated pose for bone sampler at a specific time. Returns true on success. -static bool GetGLTFPoseAtTime(cgltf_accessor* input, cgltf_accessor *output, float time, void *data) +static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, float time, void *data) { - // input and output should have the same count - + // Input and output should have the same count float tstart = 0.0f; float tend = 0.0f; - - int keyframe = 0; // defaults to first pose + int keyframe = 0; // Defaults to first pose + for (int i = 0; i < 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); + + cgltf_bool r2 = cgltf_accessor_read_float(input, i + 1, &tend, 1); if (!r2) return false; if ((tstart <= time) && (time < tend)) @@ -5227,13 +5237,16 @@ static bool GetGLTFPoseAtTime(cgltf_accessor* input, cgltf_accessor *output, flo cgltf_accessor_read_float(output, keyframe+1, tmp, 4); Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]}; Vector4 *r = data; - // only v4 is for rotations, so we know it's a quat. + + // Only v4 is for rotations, so we know it's a quat *r = QuaternionSlerp(v1, v2, t); } + return true; } -#define GLTF_ANIMDELAY 17 // that's roughly ~1000 msec / 60 FPS (16.666666* msec) +#define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) + static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned int *animCount) { // glTF file loading @@ -5241,10 +5254,12 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in unsigned char *fileData = LoadFileData(fileName, &dataSize); ModelAnimation *animations = NULL; + // glTF data loading cgltf_options options = { 0 }; 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); @@ -5262,9 +5277,10 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in cgltf_skin skin = data->skins[0]; *animCount = data->animations_count; animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation)); + for (unsigned int i = 0; i < data->animations_count; i++) { - animations[i].bones = LoadGLTFBoneInfo(skin, &animations[i].boneCount); + animations[i].bones = LoadBoneInfoGLTF(skin, &animations[i].boneCount); cgltf_animation animData = data->animations[i]; @@ -5276,10 +5292,12 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in 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]) @@ -5291,7 +5309,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in if (boneIndex == -1) { - // animation channel for a node not in the armature. + // Animation channel for a node not in the armature continue; } @@ -5313,10 +5331,12 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in { 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] Only linear interpolation curves are supported for GLTF animation.", fileName); + } + else TRACELOG(LOG_WARNING, "MODEL: [%s] Only linear interpolation curves are supported 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); @@ -5333,17 +5353,16 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in { animations[i].framePoses[j] = RL_MALLOC(animations[i].boneCount*sizeof(Transform)); float time = ((float) j*GLTF_ANIMDELAY)/1000.0f; + for (unsigned int k = 0; k < animations[i].boneCount; k++) { Vector3 translation = {0, 0, 0}; Quaternion rotation = {0, 0, 0, 1}; Vector3 scale = {1, 1, 1}; + if (boneChannels[k].translate) { - if (!GetGLTFPoseAtTime(boneChannels[k].translate->sampler->input, - boneChannels[k].translate->sampler->output, - time, - &translation)) + if (!GetPoseAtTimeGLTF(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); } @@ -5351,10 +5370,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in if (boneChannels[k].rotate) { - if (!GetGLTFPoseAtTime(boneChannels[k].rotate->sampler->input, - boneChannels[k].rotate->sampler->output, - time, - &rotation)) + if (!GetPoseAtTimeGLTF(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); } @@ -5362,10 +5378,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in if (boneChannels[k].scale) { - if (!GetGLTFPoseAtTime(boneChannels[k].scale->sampler->input, - boneChannels[k].scale->sampler->output, - time, - &scale)) + if (!GetPoseAtTimeGLTF(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); } @@ -5374,7 +5387,8 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in animations[i].framePoses[j][k] = (Transform){ .translation = translation, .rotation = rotation, - .scale = scale}; + .scale = scale + }; } BuildPoseFromParentJoints(animations[i].bones, animations[i].boneCount, animations[i].framePoses[j]); @@ -5383,7 +5397,8 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s (%d frames, %fs)", fileName, animData.name, animations[i].frameCount, animDuration); RL_FREE(boneChannels); } - } else TRACELOG(LOG_ERROR, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count); + } + else TRACELOG(LOG_ERROR, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count); cgltf_free(data); } @@ -5576,12 +5591,14 @@ static Model LoadM3D(const char *fileName) 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)); + // without material, we rely on vertex colors if (mi == M3D_UNDEF && model.meshes[k].colors == NULL) { model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); for (j = 0; j < model.meshes[k].vertexCount*4; j += 4) memcpy(&model.meshes[k].colors[j], &WHITE, 4); } + if (m3d->numbone && m3d->numskin) { model.meshes[k].boneIds = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); @@ -5589,6 +5606,7 @@ static Model LoadM3D(const char *fileName) 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; } @@ -5617,25 +5635,25 @@ static Model LoadM3D(const char *fileName) 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.0 - 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.0 - 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.0 - m3d->tmap[m3d->face[i].texcoord[2]].v; + model.meshes[k].texcoords[l*6 + 0] = m3d->tmap[m3d->face[i].texcoord[0]].u; + model.meshes[k].texcoords[l*6 + 1] = 1.0 - 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.0 - 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.0 - 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; + 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) @@ -5658,8 +5676,8 @@ static Model LoadM3D(const char *fileName) { // 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; + model.meshes[k].boneIds[l*12 + n*4] = m3d->numbone; + model.meshes[k].boneWeights[l*12 + n*4] = 1.0f; } } } @@ -5754,6 +5772,7 @@ static Model LoadM3D(const char *fileName) 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 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; @@ -5799,15 +5818,16 @@ static Model LoadM3D(const char *fileName) return model; } +#define M3D_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) + // Load M3D animation data -#define M3D_ANIMDELAY 17 // that's roughly ~1000 msec / 60 FPS (16.666666* msec) static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount) { m3d_t *m3d = NULL; unsigned int bytesRead = 0; unsigned char *fileData = LoadFileData(fileName, &bytesRead); ModelAnimation *animations = NULL; - int i, j; + int i = 0, j = 0; *animCount = 0; @@ -5824,7 +5844,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int 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? + // No animation or bone+skin? if (!m3d->numaction || !m3d->numbone || !m3d->numskin) { m3d_free(m3d); @@ -5860,7 +5880,8 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int { animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); - m3db_t *pose = m3d_pose(m3d, a, i * M3D_ANIMDELAY); + m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY); + if (pose != NULL) { for (j = 0; j < m3d->numbone; j++) From b85490980339cab5cbdde4d674624deaabff783c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Jan 2023 20:48:02 +0100 Subject: [PATCH 0200/1710] Update models_loading_gltf.c --- examples/models/models_loading_gltf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index c19df3f5b..fcd469493 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -1,6 +1,13 @@ /******************************************************************************************* * -* raylib [models] example - loading gltf +* raylib [models] example - loading gltf with animations +* +* 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) * * Example originally created with raylib 3.7, last time updated with raylib 4.2 * From 39f9045703268f0b844916b07643d3801d2212cf Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 2 Jan 2023 20:59:25 +0100 Subject: [PATCH 0201/1710] Update models_loading_gltf.c --- examples/models/models_loading_gltf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index fcd469493..92b517a1c 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -56,21 +56,21 @@ int main(void) 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 //---------------------------------------------------------------------------------- - ModelAnimation anim = modelAnimations[animIndex]; - + // Select current animation if (IsKeyPressed(KEY_UP)) animIndex = (animIndex + 1)%animsCount; else if (IsKeyPressed(KEY_DOWN)) animIndex = (animIndex + animsCount - 1)%animsCount; - + + // Update model animation + ModelAnimation anim = modelAnimations[animIndex]; animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; UpdateModelAnimation(model, anim, animCurrentFrame); + // Update camera UpdateCamera(&camera); //---------------------------------------------------------------------------------- @@ -82,8 +82,8 @@ int main(void) BeginMode3D(camera); - DrawModel(model, position, 1.0f, WHITE); - DrawGrid(10, 1.0f); // Draw a grid + DrawModel(model, position, 1.0f, WHITE); // Draw animated model + DrawGrid(10, 1.0f); EndMode3D(); From 73234d2a28166ca65cd10c01b0233a667e5290c1 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 3 Jan 2023 17:44:06 +0100 Subject: [PATCH 0202/1710] Avoid trying to setup uniform for invalid locations --- src/rcore.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 0e9aa76ec..d17632942 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2525,25 +2525,34 @@ void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformT // Set shader uniform value vector void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count) { - rlEnableShader(shader.id); - rlSetUniform(locIndex, value, uniformType, count); - //rlDisableShader(); // Avoid reseting current shader program, in case other uniforms are set + if (locIndex > -1) + { + rlEnableShader(shader.id); + rlSetUniform(locIndex, value, uniformType, count); + //rlDisableShader(); // Avoid reseting current shader program, in case other uniforms are set + } } // Set shader uniform value (matrix 4x4) void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat) { - rlEnableShader(shader.id); - rlSetUniformMatrix(locIndex, mat); - //rlDisableShader(); + if (locIndex > -1) + { + rlEnableShader(shader.id); + rlSetUniformMatrix(locIndex, mat); + //rlDisableShader(); + } } // Set shader uniform value for texture void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture) { - rlEnableShader(shader.id); - rlSetUniformSampler(locIndex, texture.id); - //rlDisableShader(); + if (locIndex > -1) + { + rlEnableShader(shader.id); + rlSetUniformSampler(locIndex, texture.id); + //rlDisableShader(); + } } // Get a ray trace from mouse position From 89755e52bf066e66dcfac2f2f52f6806ff02a693 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Jan 2023 17:35:51 +0100 Subject: [PATCH 0203/1710] REVIEWED: `rLoadTextureDepth()` fixed issue --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 072925551..a7226a818 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2993,7 +2993,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) #if defined(GRAPHICS_API_OPENGL_ES2) // WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT) // while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities - if (!RLGL.ExtSupported.texDepthWebGL) + if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer) { if (RLGL.ExtSupported.maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; else if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; From ce8000ee7e3c1e07bd747e71872200311038d090 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 4 Jan 2023 20:13:44 +0100 Subject: [PATCH 0204/1710] REVIEWED: `GetClipboardText()` on `PLATFORM_WEB` --- src/rcore.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index d17632942..da9436573 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1945,7 +1945,21 @@ const char *GetClipboardText(void) return glfwGetClipboardString(CORE.Window.handle); #endif #if defined(PLATFORM_WEB) - return emscripten_run_script_string("navigator.clipboard.readText()"); + // 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; #endif return NULL; } From bba6ae562238299f07b7f83f1278fc279eb21fd5 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Jan 2023 12:16:42 +0100 Subject: [PATCH 0205/1710] RENAME: type to projection #2851 --- src/rcamera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcamera.h b/src/rcamera.h index 92c3c49a9..1b77e19a9 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -73,7 +73,7 @@ Vector3 target; // Camera target it looks-at Vector3 up; // Camera up vector (rotation over its axis) float fovy; // Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic - int type; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC + int projection; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D From 6dd1d2d9316c2d665c02d45bd0677150352ed9d3 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Jan 2023 12:20:45 +0100 Subject: [PATCH 0206/1710] ADDED: Required define on Linux #2729 --- src/rcore.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index da9436573..2d68606d8 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -155,6 +155,9 @@ #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. #endif +#if defined(__linux__) && !defined(_GNU_SOURCE) + #define _GNU_SOURCE +#endif // Platform specific defines to handle GetApplicationDirectory() #if defined (PLATFORM_DESKTOP) From f549f67be9e1cc1256d35c86637ac807ec2e6823 Mon Sep 17 00:00:00 2001 From: Go Watanabe <1570475+wtnbgo@users.noreply.github.com> Date: Tue, 10 Jan 2023 20:34:06 +0900 Subject: [PATCH 0207/1710] OpenGLES 2.0 support on PLATFORM_DESKTOP (#2840) * OpenGLES 2.0 support on PLATFORM_DESKTOP * exmples raylib_opengl_interop desktop GLES2 support * rename gles2.h -> glad_gles2.h --- cmake/LibraryConfigurations.cmake | 2 +- examples/others/external/include/glad_gles2.h | 4774 +++++++++++++++++ examples/others/raylib_opengl_interop.c | 30 +- src/external/glad_gles2.h | 4774 +++++++++++++++++ src/rlgl.h | 12 + 5 files changed, 9581 insertions(+), 11 deletions(-) create mode 100644 examples/others/external/include/glad_gles2.h create mode 100644 src/external/glad_gles2.h diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index a16bde69d..ffb1a047d 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -93,7 +93,7 @@ elseif ("${PLATFORM}" MATCHES "DRM") endif () -if (${OPENGL_VERSION}) +if (NOT ${OPENGL_VERSION}) set(${SUGGESTED_GRAPHICS} "${GRAPHICS}") if (${OPENGL_VERSION} MATCHES "4.3") set(GRAPHICS "GRAPHICS_API_OPENGL_43") diff --git a/examples/others/external/include/glad_gles2.h b/examples/others/external/include/glad_gles2.h new file mode 100644 index 000000000..6c753d0a4 --- /dev/null +++ b/examples/others/external/include/glad_gles2.h @@ -0,0 +1,4774 @@ +/** + * Loader generated by glad 2.0.2 on Wed Dec 28 13:28:51 2022 + * + * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 + * + * Generator: C/C++ + * Specification: gl + * Extensions: 170 + * + * APIs: + * - gles2=2.0 + * + * Options: + * - ALIAS = False + * - DEBUG = False + * - HEADER_ONLY = True + * - LOADER = False + * - MX = False + * - ON_DEMAND = False + * + * Commandline: + * --api='gles2=2.0' --extensions='GL_EXT_EGL_image_array,GL_EXT_EGL_image_storage,GL_EXT_EGL_image_storage_compression,GL_EXT_YUV_target,GL_EXT_base_instance,GL_EXT_blend_func_extended,GL_EXT_blend_minmax,GL_EXT_buffer_storage,GL_EXT_clear_texture,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_conservative_depth,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_depth_clamp,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers,GL_EXT_draw_buffers_indexed,GL_EXT_draw_elements_base_vertex,GL_EXT_draw_instanced,GL_EXT_draw_transform_feedback,GL_EXT_external_buffer,GL_EXT_float_blend,GL_EXT_fragment_shading_rate,GL_EXT_geometry_point_size,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_instanced_arrays,GL_EXT_map_buffer_range,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_memory_object_win32,GL_EXT_multi_draw_arrays,GL_EXT_multi_draw_indirect,GL_EXT_multisampled_compatibility,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_multiview_draw_buffers,GL_EXT_multiview_tessellation_geometry_shader,GL_EXT_multiview_texture_multisample,GL_EXT_multiview_timer_query,GL_EXT_occlusion_query_boolean,GL_EXT_polygon_offset_clamp,GL_EXT_post_depth_coverage,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_pvrtc_sRGB,GL_EXT_raster_multisample,GL_EXT_read_format_bgra,GL_EXT_render_snorm,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_semaphore,GL_EXT_semaphore_fd,GL_EXT_semaphore_win32,GL_EXT_separate_depth_stencil,GL_EXT_separate_shader_objects,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_framebuffer_fetch_non_coherent,GL_EXT_shader_group_vote,GL_EXT_shader_implicit_conversions,GL_EXT_shader_integer_mix,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_shader_pixel_local_storage,GL_EXT_shader_pixel_local_storage2,GL_EXT_shader_samples_identical,GL_EXT_shader_texture_lod,GL_EXT_shadow_samplers,GL_EXT_sparse_texture,GL_EXT_sparse_texture2,GL_EXT_tessellation_point_size,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_compression_astc_decode_mode,GL_EXT_texture_compression_bptc,GL_EXT_texture_compression_dxt1,GL_EXT_texture_compression_rgtc,GL_EXT_texture_compression_s3tc,GL_EXT_texture_compression_s3tc_srgb,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_filter_minmax,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_mirror_clamp_to_edge,GL_EXT_texture_norm16,GL_EXT_texture_query_lod,GL_EXT_texture_rg,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_RG8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_shadow_lod,GL_EXT_texture_storage,GL_EXT_texture_storage_compression,GL_EXT_texture_type_2_10_10_10_REV,GL_EXT_texture_view,GL_EXT_unpack_subimage,GL_EXT_win32_keyed_mutex,GL_EXT_window_rectangles,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_context_flush_control,GL_KHR_debug,GL_KHR_no_error,GL_KHR_parallel_shader_compile,GL_KHR_robust_buffer_access_behavior,GL_KHR_robustness,GL_KHR_shader_subgroup,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_KHR_texture_compression_astc_sliced_3d,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_compressed_ETC1_RGB8_sub_texture,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_copy_image,GL_OES_depth24,GL_OES_depth32,GL_OES_depth_texture,GL_OES_draw_buffers_indexed,GL_OES_draw_elements_base_vertex,GL_OES_element_index_uint,GL_OES_fbo_render_mipmap,GL_OES_fragment_precision_high,GL_OES_geometry_point_size,GL_OES_geometry_shader,GL_OES_get_program_binary,GL_OES_gpu_shader5,GL_OES_mapbuffer,GL_OES_packed_depth_stencil,GL_OES_primitive_bounding_box,GL_OES_required_internalformat,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_io_blocks,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil1,GL_OES_stencil4,GL_OES_surfaceless_context,GL_OES_tessellation_point_size,GL_OES_tessellation_shader,GL_OES_texture_3D,GL_OES_texture_border_clamp,GL_OES_texture_buffer,GL_OES_texture_compression_astc,GL_OES_texture_cube_map_array,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OES_vertex_type_10_10_10_2,GL_OES_viewport_array' c --header-only + * + * Online: + * http://glad.sh/#api=gles2%3D2.0&generator=c&options=HEADER_ONLY + * + */ + +#ifndef GLAD_GLES2_H_ +#define GLAD_GLES2_H_ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#ifdef __gl2_h_ + #error OpenGL ES 2 header already included (API: gles2), remove previous include! +#endif +#define __gl2_h_ 1 +#ifdef __gles2_gl2_h_ + #error OpenGL ES 2 header already included (API: gles2), remove previous include! +#endif +#define __gles2_gl2_h_ 1 +#ifdef __gl3_h_ + #error OpenGL ES 3 header already included (API: gles2), remove previous include! +#endif +#define __gl3_h_ 1 +#ifdef __gles2_gl3_h_ + #error OpenGL ES 3 header already included (API: gles2), remove previous include! +#endif +#define __gles2_gl3_h_ 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#define GLAD_GLES2 +#define GLAD_OPTION_GLES2_HEADER_ONLY + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GLAD_PLATFORM_H_ +#define GLAD_PLATFORM_H_ + +#ifndef GLAD_PLATFORM_WIN32 + #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) + #define GLAD_PLATFORM_WIN32 1 + #else + #define GLAD_PLATFORM_WIN32 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_APPLE + #ifdef __APPLE__ + #define GLAD_PLATFORM_APPLE 1 + #else + #define GLAD_PLATFORM_APPLE 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_EMSCRIPTEN + #ifdef __EMSCRIPTEN__ + #define GLAD_PLATFORM_EMSCRIPTEN 1 + #else + #define GLAD_PLATFORM_EMSCRIPTEN 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_UWP + #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) + #ifdef __has_include + #if __has_include() + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #endif + + #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #define GLAD_PLATFORM_UWP 1 + #endif + #endif + + #ifndef GLAD_PLATFORM_UWP + #define GLAD_PLATFORM_UWP 0 + #endif +#endif + +#ifdef __GNUC__ + #define GLAD_GNUC_EXTENSION __extension__ +#else + #define GLAD_GNUC_EXTENSION +#endif + +#define GLAD_UNUSED(x) (void)(x) + +#ifndef GLAD_API_CALL + #if defined(GLAD_API_CALL_EXPORT) + #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) + #if defined(GLAD_API_CALL_EXPORT_BUILD) + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllexport)) extern + #else + #define GLAD_API_CALL __declspec(dllexport) extern + #endif + #else + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllimport)) extern + #else + #define GLAD_API_CALL __declspec(dllimport) extern + #endif + #endif + #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) + #define GLAD_API_CALL __attribute__ ((visibility ("default"))) extern + #else + #define GLAD_API_CALL extern + #endif + #else + #define GLAD_API_CALL extern + #endif +#endif + +#ifdef APIENTRY + #define GLAD_API_PTR APIENTRY +#elif GLAD_PLATFORM_WIN32 + #define GLAD_API_PTR __stdcall +#else + #define GLAD_API_PTR +#endif + +#ifndef GLAPI +#define GLAPI GLAD_API_CALL +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY GLAD_API_PTR +#endif + +#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) +#define GLAD_VERSION_MAJOR(version) (version / 10000) +#define GLAD_VERSION_MINOR(version) (version % 10000) + +#define GLAD_GENERATOR_VERSION "2.0.2" + +typedef void (*GLADapiproc)(void); + +typedef GLADapiproc (*GLADloadfunc)(const char *name); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); + +typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); +typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); + +#endif /* GLAD_PLATFORM_H_ */ + +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_ACTIVE_PROGRAM_EXT 0x8259 +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALL_SHADER_BITS_EXT 0xFFFFFFFF +#define GL_ALPHA 0x1906 +#define GL_ALPHA16F_EXT 0x881C +#define GL_ALPHA32F_EXT 0x8816 +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA8_OES 0x803C +#define GL_ALPHA_BITS 0x0D55 +#define GL_ALWAYS 0x0207 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 0x8D6A +#define GL_ANY_SAMPLES_PASSED_EXT 0x8C2F +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_BACK 0x0405 +#define GL_BGRA8_EXT 0x93A1 +#define GL_BGRA_EXT 0x80E1 +#define GL_BLEND 0x0BE2 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLUE_BITS 0x0D54 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_BUFFER_ACCESS_OES 0x88BB +#define GL_BUFFER_IMMUTABLE_STORAGE_EXT 0x821F +#define GL_BUFFER_KHR 0x82E0 +#define GL_BUFFER_MAPPED_OES 0x88BC +#define GL_BUFFER_MAP_POINTER_OES 0x88BD +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_STORAGE_FLAGS_EXT 0x8220 +#define GL_BUFFER_USAGE 0x8765 +#define GL_BYTE 0x1400 +#define GL_CCW 0x0901 +#define GL_CLAMP_TO_BORDER_EXT 0x812D +#define GL_CLAMP_TO_BORDER_OES 0x812D +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT 0x00004000 +#define GL_CLIENT_STORAGE_BIT_EXT 0x0200 +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_CLIP_DEPTH_MODE_EXT 0x935D +#define GL_CLIP_DISTANCE0_EXT 0x3000 +#define GL_CLIP_DISTANCE1_EXT 0x3001 +#define GL_CLIP_DISTANCE2_EXT 0x3002 +#define GL_CLIP_DISTANCE3_EXT 0x3003 +#define GL_CLIP_DISTANCE4_EXT 0x3004 +#define GL_CLIP_DISTANCE5_EXT 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE6_EXT 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_CLIP_DISTANCE7_EXT 0x3007 +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_ORIGIN_EXT 0x935C +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 +#define GL_COLORBURN_KHR 0x929A +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT_EXT 0x90F0 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_EXT 0x1800 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_COMPARE_REF_TO_TEXTURE_EXT 0x884E +#define GL_COMPILE_STATUS 0x8B81 +#define GL_COMPLETION_STATUS_KHR 0x91B1 +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES 0x93C0 +#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES 0x93C1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES 0x93C2 +#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES 0x93C3 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES 0x93C4 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES 0x93C5 +#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES 0x93C6 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES 0x93C7 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES 0x93C8 +#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES 0x93C9 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_EXT 0x8E8C +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT 0x8E8F +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES 0x93E0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES 0x93E1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES 0x93E2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES 0x93E3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES 0x93E4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES 0x93E5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES 0x93E6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES 0x93E7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES 0x93E8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES 0x93E9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT 0x8E8D +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT 0x8A56 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT 0x8A57 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1 +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#define GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54 +#define GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT 0x8A55 +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 +#define GL_CONTEXT_FLAG_PROTECTED_CONTENT_BIT_EXT 0x00000010 +#define GL_CONTEXT_LOST_KHR 0x0507 +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x82FC +#define GL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x82FB +#define GL_CONTEXT_ROBUST_ACCESS_EXT 0x90F3 +#define GL_CONTEXT_ROBUST_ACCESS_KHR 0x90F3 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_CURRENT_QUERY_EXT 0x8865 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_CW 0x0900 +#define GL_D3D12_FENCE_VALUE_EXT 0x9595 +#define GL_DARKEN_KHR 0x9297 +#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245 +#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D +#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243 +#define GL_DEBUG_OUTPUT_KHR 0x92E0 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 +#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146 +#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148 +#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147 +#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B +#define GL_DEBUG_SOURCE_API_KHR 0x8246 +#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A +#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B +#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247 +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D +#define GL_DEBUG_TYPE_ERROR_KHR 0x824C +#define GL_DEBUG_TYPE_MARKER_KHR 0x8268 +#define GL_DEBUG_TYPE_OTHER_KHR 0x8251 +#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250 +#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A +#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F +#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269 +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E +#define GL_DECODE_EXT 0x8A49 +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_DEDICATED_MEMORY_OBJECT_EXT 0x9581 +#define GL_DELETE_STATUS 0x8B80 +#define GL_DEPTH24_STENCIL8_OES 0x88F0 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_DEPTH_BITS 0x0D56 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_DEPTH_CLAMP_EXT 0x864F +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT16_OES 0x81A5 +#define GL_DEPTH_COMPONENT24_OES 0x81A6 +#define GL_DEPTH_COMPONENT32_OES 0x81A7 +#define GL_DEPTH_EXT 0x1801 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_STENCIL_OES 0x84F9 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEVICE_LUID_EXT 0x9599 +#define GL_DEVICE_NODE_MASK_EXT 0x959A +#define GL_DEVICE_UUID_EXT 0x9597 +#define GL_DIFFERENCE_KHR 0x929E +#define GL_DITHER 0x0BD0 +#define GL_DONT_CARE 0x1100 +#define GL_DRAW_BUFFER0_EXT 0x8825 +#define GL_DRAW_BUFFER10_EXT 0x882F +#define GL_DRAW_BUFFER11_EXT 0x8830 +#define GL_DRAW_BUFFER12_EXT 0x8831 +#define GL_DRAW_BUFFER13_EXT 0x8832 +#define GL_DRAW_BUFFER14_EXT 0x8833 +#define GL_DRAW_BUFFER15_EXT 0x8834 +#define GL_DRAW_BUFFER1_EXT 0x8826 +#define GL_DRAW_BUFFER2_EXT 0x8827 +#define GL_DRAW_BUFFER3_EXT 0x8828 +#define GL_DRAW_BUFFER4_EXT 0x8829 +#define GL_DRAW_BUFFER5_EXT 0x882A +#define GL_DRAW_BUFFER6_EXT 0x882B +#define GL_DRAW_BUFFER7_EXT 0x882C +#define GL_DRAW_BUFFER8_EXT 0x882D +#define GL_DRAW_BUFFER9_EXT 0x882E +#define GL_DRAW_BUFFER_EXT 0x0C01 +#define GL_DRIVER_UUID_EXT 0x9598 +#define GL_DST_ALPHA 0x0304 +#define GL_DST_COLOR 0x0306 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_STORAGE_BIT_EXT 0x0100 +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_EQUAL 0x0202 +#define GL_ETC1_RGB8_OES 0x8D64 +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_EXCLUSIVE_EXT 0x8F11 +#define GL_EXTENSIONS 0x1F03 +#define GL_FALSE 0 +#define GL_FASTEST 0x1101 +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_FIRST_VERTEX_CONVENTION_OES 0x8E4D +#define GL_FIXED 0x140C +#define GL_FLOAT 0x1406 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FRACTIONAL_EVEN_EXT 0x8E7C +#define GL_FRACTIONAL_EVEN_OES 0x8E7C +#define GL_FRACTIONAL_ODD_EXT 0x8E7B +#define GL_FRACTIONAL_ODD_OES 0x8E7B +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES 0x8E5D +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FRAGMENT_SHADER_BIT_EXT 0x00000002 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES 0x8B8B +#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 +#define GL_FRAGMENT_SHADING_RATE_ATTACHMENT_WITH_DEFAULT_FRAMEBUFFER_SUPPORTED_EXT 0x96DF +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT 0x96D2 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT 0x96D5 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT 0x96D4 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT 0x96D6 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT 0x96D3 +#define GL_FRAGMENT_SHADING_RATE_NON_TRIVIAL_COMBINERS_SUPPORTED_EXT 0x8F6F +#define GL_FRAGMENT_SHADING_RATE_WITH_SAMPLE_MASK_SUPPORTED_EXT 0x96DE +#define GL_FRAGMENT_SHADING_RATE_WITH_SHADER_DEPTH_STENCIL_WRITES_SUPPORTED_EXT 0x96DD +#define GL_FRAMEBUFFER 0x8D40 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_OES 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES 0x8CD4 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT 0x8D6C +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS_OES 0x9312 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_INSUFFICIENT_SHADER_COMBINED_LOCAL_STORAGE_EXT 0x9652 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_OES 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_UNDEFINED_OES 0x8219 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_FRONT 0x0404 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_FRONT_FACE 0x0B46 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_GEOMETRY_LINKED_INPUT_TYPE_EXT 0x8917 +#define GL_GEOMETRY_LINKED_INPUT_TYPE_OES 0x8917 +#define GL_GEOMETRY_LINKED_OUTPUT_TYPE_EXT 0x8918 +#define GL_GEOMETRY_LINKED_OUTPUT_TYPE_OES 0x8918 +#define GL_GEOMETRY_LINKED_VERTICES_OUT_EXT 0x8916 +#define GL_GEOMETRY_LINKED_VERTICES_OUT_OES 0x8916 +#define GL_GEOMETRY_SHADER_BIT_EXT 0x00000004 +#define GL_GEOMETRY_SHADER_BIT_OES 0x00000004 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_SHADER_INVOCATIONS_EXT 0x887F +#define GL_GEOMETRY_SHADER_INVOCATIONS_OES 0x887F +#define GL_GEOMETRY_SHADER_OES 0x8DD9 +#define GL_GEQUAL 0x0206 +#define GL_GPU_DISJOINT_EXT 0x8FBB +#define GL_GREATER 0x0204 +#define GL_GREEN_BITS 0x0D53 +#define GL_GUILTY_CONTEXT_RESET_EXT 0x8253 +#define GL_GUILTY_CONTEXT_RESET_KHR 0x8253 +#define GL_HALF_FLOAT_OES 0x8D61 +#define GL_HANDLE_TYPE_D3D11_IMAGE_EXT 0x958B +#define GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT 0x958C +#define GL_HANDLE_TYPE_D3D12_FENCE_EXT 0x9594 +#define GL_HANDLE_TYPE_D3D12_RESOURCE_EXT 0x958A +#define GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT 0x9589 +#define GL_HANDLE_TYPE_OPAQUE_FD_EXT 0x9586 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT 0x9588 +#define GL_HARDLIGHT_KHR 0x929B +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_HIGH_INT 0x8DF5 +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_LUMINOSITY_KHR 0x92B0 +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_BUFFER_OES 0x9051 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_CUBE_MAP_ARRAY_OES 0x9054 +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_INCLUSIVE_EXT 0x8F10 +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_INNOCENT_CONTEXT_RESET_EXT 0x8254 +#define GL_INNOCENT_CONTEXT_RESET_KHR 0x8254 +#define GL_INT 0x1404 +#define GL_INT_10_10_10_2_OES 0x8DF7 +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_BUFFER_OES 0x905C +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_OES 0x905F +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910C +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_INT_SAMPLER_BUFFER_OES 0x8DD0 +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900E +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_OES 0x900E +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVERT 0x150A +#define GL_ISOLINES_EXT 0x8E7A +#define GL_ISOLINES_OES 0x8E7A +#define GL_IS_PER_PATCH_EXT 0x92E7 +#define GL_IS_PER_PATCH_OES 0x92E7 +#define GL_KEEP 0x1E00 +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_LAST_VERTEX_CONVENTION_OES 0x8E4E +#define GL_LAYER_PROVOKING_VERTEX_EXT 0x825E +#define GL_LAYER_PROVOKING_VERTEX_OES 0x825E +#define GL_LAYOUT_COLOR_ATTACHMENT_EXT 0x958E +#define GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT 0x9531 +#define GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT 0x9530 +#define GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT 0x958F +#define GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT 0x9590 +#define GL_LAYOUT_GENERAL_EXT 0x958D +#define GL_LAYOUT_SHADER_READ_ONLY_EXT 0x9591 +#define GL_LAYOUT_TRANSFER_DST_EXT 0x9593 +#define GL_LAYOUT_TRANSFER_SRC_EXT 0x9592 +#define GL_LEQUAL 0x0203 +#define GL_LESS 0x0201 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_LINEAR 0x2601 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINEAR_TILING_EXT 0x9585 +#define GL_LINES 0x0001 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINES_ADJACENCY_OES 0x000A +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_LINE_STRIP_ADJACENCY_OES 0x000B +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINK_STATUS 0x8B82 +#define GL_LOCATION_INDEX_EXT 0x930F +#define GL_LOSE_CONTEXT_ON_RESET_EXT 0x8252 +#define GL_LOSE_CONTEXT_ON_RESET_KHR 0x8252 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_LOWER_LEFT_EXT 0x8CA1 +#define GL_LOW_FLOAT 0x8DF0 +#define GL_LOW_INT 0x8DF3 +#define GL_LUID_SIZE_EXT 8 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE16F_EXT 0x881E +#define GL_LUMINANCE32F_EXT 0x8818 +#define GL_LUMINANCE4_ALPHA4_OES 0x8043 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE8_ALPHA8_OES 0x8045 +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE8_OES 0x8040 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_LUMINANCE_ALPHA16F_EXT 0x881F +#define GL_LUMINANCE_ALPHA32F_EXT 0x8819 +#define GL_MAP_COHERENT_BIT_EXT 0x0080 +#define GL_MAP_FLUSH_EXPLICIT_BIT_EXT 0x0010 +#define GL_MAP_INVALIDATE_BUFFER_BIT_EXT 0x0008 +#define GL_MAP_INVALIDATE_RANGE_BIT_EXT 0x0004 +#define GL_MAP_PERSISTENT_BIT_EXT 0x0040 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_READ_BIT_EXT 0x0001 +#define GL_MAP_UNSYNCHRONIZED_BIT_EXT 0x0020 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_WRITE_BIT_EXT 0x0002 +#define GL_MAX 0x8008 +#define GL_MAX_3D_TEXTURE_SIZE_OES 0x8073 +#define GL_MAX_CLIP_DISTANCES_EXT 0x0D32 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT 0x82FA +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8A32 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_OES 0x8A32 +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT 0x8E1E +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_OES 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT 0x8E1F +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_OES 0x8E1F +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_CULL_DISTANCES_EXT 0x82F9 +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C +#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143 +#define GL_MAX_DRAW_BUFFERS_EXT 0x8824 +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT 0x88FC +#define GL_MAX_EXT 0x8008 +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_OES 0x8E5C +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_LAYERS_EXT 0x96DC +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_ASPECT_RATIO_EXT 0x96DB +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 0x96DA +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 0x96D8 +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_MAX_FRAMEBUFFER_LAYERS_EXT 0x9317 +#define GL_MAX_FRAMEBUFFER_LAYERS_OES 0x9317 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT 0x92D5 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS_OES 0x92D5 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT 0x92CF +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_OES 0x92CF +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT 0x90CD +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS_OES 0x90CD +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT 0x9123 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS_OES 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT 0x9124 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_OES 0x9124 +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_OES 0x8DE0 +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT 0x8E5A +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS_OES 0x8E5A +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT 0x90D7 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_OES 0x90D7 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_OES 0x8C29 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_OES 0x8DE1 +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT 0x8A2C +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS_OES 0x8A2C +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_OES 0x8DDF +#define GL_MAX_LABEL_LENGTH_KHR 0x82E8 +#define GL_MAX_MULTIVIEW_BUFFERS_EXT 0x90F2 +#define GL_MAX_PATCH_VERTICES_EXT 0x8E7D +#define GL_MAX_PATCH_VERTICES_OES 0x8E7D +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_MAX_SAMPLES_EXT 0x8D57 +#define GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_FAST_SIZE_EXT 0x9650 +#define GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_SIZE_EXT 0x9651 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_FAST_SIZE_EXT 0x8F63 +#define GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT 0x8F67 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_EXT 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_EXT 0x919A +#define GL_MAX_SPARSE_TEXTURE_SIZE_EXT 0x9198 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT 0x92D3 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_OES 0x92D3 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT 0x92CD +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_OES 0x92CD +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_EXT 0x90CB +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_OES 0x90CB +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT 0x886C +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_OES 0x886C +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT 0x8E83 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_OES 0x8E83 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT 0x90D8 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_OES 0x90D8 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_EXT 0x8E81 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_OES 0x8E81 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_EXT 0x8E85 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_OES 0x8E85 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_EXT 0x8E89 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_OES 0x8E89 +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_EXT 0x8E7F +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_OES 0x8E7F +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT 0x92D4 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_OES 0x92D4 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT 0x92CE +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_OES 0x92CE +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_EXT 0x90CC +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_OES 0x90CC +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT 0x886D +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_OES 0x886D +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT 0x8E86 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_OES 0x8E86 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT 0x90D9 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_OES 0x90D9 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_EXT 0x8E82 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_OES 0x8E82 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_EXT 0x8E8A +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_OES 0x8E8A +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT 0x8E80 +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_OES 0x8E80 +#define GL_MAX_TESS_GEN_LEVEL_EXT 0x8E7E +#define GL_MAX_TESS_GEN_LEVEL_OES 0x8E7E +#define GL_MAX_TESS_PATCH_COMPONENTS_EXT 0x8E84 +#define GL_MAX_TESS_PATCH_COMPONENTS_OES 0x8E84 +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_MAX_TEXTURE_BUFFER_SIZE_OES 0x8C2B +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VIEWPORTS_OES 0x825B +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_WINDOW_RECTANGLES_EXT 0x8F14 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_MIN 0x8007 +#define GL_MIN_EXT 0x8007 +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES 0x8E5B +#define GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 0x96D9 +#define GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 0x96D7 +#define GL_MIN_SAMPLE_SHADING_VALUE_OES 0x8C37 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_MULTIVIEW_EXT 0x90F1 +#define GL_NEAREST 0x2600 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_NEGATIVE_ONE_TO_ONE_EXT 0x935E +#define GL_NEVER 0x0200 +#define GL_NICEST 0x1102 +#define GL_NONE 0 +#define GL_NOTEQUAL 0x0205 +#define GL_NO_ERROR 0 +#define GL_NO_RESET_NOTIFICATION_EXT 0x8261 +#define GL_NO_RESET_NOTIFICATION_KHR 0x8261 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_NUM_DEVICE_UUIDS_EXT 0x9596 +#define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_NUM_SPARSE_LEVELS_EXT 0x91AA +#define GL_NUM_SURFACE_COMPRESSION_FIXED_RATES_EXT 0x8F6E +#define GL_NUM_TILING_TYPES_EXT 0x9582 +#define GL_NUM_VIRTUAL_PAGE_SIZES_EXT 0x91A8 +#define GL_NUM_WINDOW_RECTANGLES_EXT 0x8F15 +#define GL_ONE 1 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_ONE_MINUS_SRC1_ALPHA_EXT 0x88FB +#define GL_ONE_MINUS_SRC1_COLOR_EXT 0x88FA +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_OPTIMAL_TILING_EXT 0x9584 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_OVERLAY_KHR 0x9296 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PATCHES_EXT 0x000E +#define GL_PATCHES_OES 0x000E +#define GL_PATCH_VERTICES_EXT 0x8E72 +#define GL_PATCH_VERTICES_OES 0x8E72 +#define GL_POINTS 0x0000 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_PRIMITIVES_GENERATED_OES 0x8C87 +#define GL_PRIMITIVE_BOUNDING_BOX_EXT 0x92BE +#define GL_PRIMITIVE_BOUNDING_BOX_OES 0x92BE +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED_OES 0x8221 +#define GL_PROGRAM_BINARY_FORMATS_OES 0x87FF +#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741 +#define GL_PROGRAM_KHR 0x82E2 +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_PROGRAM_PIPELINE_BINDING_EXT 0x825A +#define GL_PROGRAM_PIPELINE_KHR 0x82E4 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_SEPARABLE_EXT 0x8258 +#define GL_PROTECTED_MEMORY_OBJECT_EXT 0x959B +#define GL_QUADS_EXT 0x0007 +#define GL_QUADS_OES 0x0007 +#define GL_QUERY_COUNTER_BITS_EXT 0x8864 +#define GL_QUERY_KHR 0x82E3 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_QUERY_RESULT_AVAILABLE_EXT 0x8867 +#define GL_QUERY_RESULT_EXT 0x8866 +#define GL_R16F_EXT 0x822D +#define GL_R16_EXT 0x822A +#define GL_R16_SNORM_EXT 0x8F98 +#define GL_R32F_EXT 0x822E +#define GL_R8_EXT 0x8229 +#define GL_R8_SNORM 0x8F94 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_READ_BUFFER_EXT 0x0C02 +#define GL_RED_BITS 0x0D52 +#define GL_RED_EXT 0x1903 +#define GL_REFERENCED_BY_GEOMETRY_SHADER_EXT 0x9309 +#define GL_REFERENCED_BY_GEOMETRY_SHADER_OES 0x9309 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT 0x9307 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER_OES 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT 0x9308 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER_OES 0x9308 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERER 0x1F01 +#define GL_REPEAT 0x2901 +#define GL_REPLACE 0x1E01 +#define GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68 +#define GL_RESET_NOTIFICATION_STRATEGY_EXT 0x8256 +#define GL_RESET_NOTIFICATION_STRATEGY_KHR 0x8256 +#define GL_RG16F_EXT 0x822F +#define GL_RG16_EXT 0x822C +#define GL_RG16_SNORM_EXT 0x8F99 +#define GL_RG32F_EXT 0x8230 +#define GL_RG8_EXT 0x822B +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB 0x1907 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB16F_EXT 0x881B +#define GL_RGB16_EXT 0x8054 +#define GL_RGB16_SNORM_EXT 0x8F9A +#define GL_RGB32F_EXT 0x8815 +#define GL_RGB565 0x8D62 +#define GL_RGB565_OES 0x8D62 +#define GL_RGB5_A1 0x8057 +#define GL_RGB5_A1_OES 0x8057 +#define GL_RGB8_OES 0x8051 +#define GL_RGBA 0x1908 +#define GL_RGBA16F_EXT 0x881A +#define GL_RGBA16_EXT 0x805B +#define GL_RGBA16_SNORM_EXT 0x8F9B +#define GL_RGBA32F_EXT 0x8814 +#define GL_RGBA4 0x8056 +#define GL_RGBA4_OES 0x8056 +#define GL_RGBA8_OES 0x8058 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_RG_EXT 0x8227 +#define GL_SAMPLER 0x82E6 +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910B +#define GL_SAMPLER_2D_SHADOW_EXT 0x8B62 +#define GL_SAMPLER_3D_OES 0x8B5F +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_BUFFER_OES 0x8DC2 +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_OES 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_EXT 0x900D +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_OES 0x900D +#define GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT 0x8BE7 +#define GL_SAMPLER_EXTERNAL_OES 0x8D66 +#define GL_SAMPLER_KHR 0x82E6 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_SHADING_OES 0x8C36 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SCREEN_KHR 0x9295 +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_KHR 0x82E1 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_SHADER_PIXEL_LOCAL_STORAGE_EXT 0x8F64 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_TYPE 0x8B4F +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_SHADING_RATE_1X1_PIXELS_EXT 0x96A6 +#define GL_SHADING_RATE_1X1_PIXELS_QCOM 0x96A6 +#define GL_SHADING_RATE_1X2_PIXELS_EXT 0x96A7 +#define GL_SHADING_RATE_1X2_PIXELS_QCOM 0x96A7 +#define GL_SHADING_RATE_1X4_PIXELS_EXT 0x96AA +#define GL_SHADING_RATE_1X4_PIXELS_QCOM 0x96AA +#define GL_SHADING_RATE_2X1_PIXELS_EXT 0x96A8 +#define GL_SHADING_RATE_2X1_PIXELS_QCOM 0x96A8 +#define GL_SHADING_RATE_2X2_PIXELS_EXT 0x96A9 +#define GL_SHADING_RATE_2X2_PIXELS_QCOM 0x96A9 +#define GL_SHADING_RATE_2X4_PIXELS_EXT 0x96AD +#define GL_SHADING_RATE_2X4_PIXELS_QCOM 0x96AD +#define GL_SHADING_RATE_4X1_PIXELS_EXT 0x96AB +#define GL_SHADING_RATE_4X1_PIXELS_QCOM 0x96AB +#define GL_SHADING_RATE_4X2_PIXELS_EXT 0x96AC +#define GL_SHADING_RATE_4X2_PIXELS_QCOM 0x96AC +#define GL_SHADING_RATE_4X4_PIXELS_EXT 0x96AE +#define GL_SHADING_RATE_4X4_PIXELS_QCOM 0x96AE +#define GL_SHADING_RATE_ATTACHMENT_EXT 0x96D1 +#define GL_SHADING_RATE_EXT 0x96D0 +#define GL_SHORT 0x1402 +#define GL_SKIP_DECODE_EXT 0x8A4A +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_EXT 0x91A9 +#define GL_SR8_EXT 0x8FBD +#define GL_SRC1_ALPHA_EXT 0x8589 +#define GL_SRC1_COLOR_EXT 0x88F9 +#define GL_SRC_ALPHA 0x0302 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_SRC_ALPHA_SATURATE_EXT 0x0308 +#define GL_SRC_COLOR 0x0300 +#define GL_SRG8_EXT 0x8FBE +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB_EXT 0x8C40 +#define GL_STACK_OVERFLOW_KHR 0x0503 +#define GL_STACK_UNDERFLOW_KHR 0x0504 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_STENCIL_BITS 0x0D57 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_EXT 0x1802 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_INDEX1_OES 0x8D46 +#define GL_STENCIL_INDEX4_OES 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX8_OES 0x8D48 +#define GL_STENCIL_INDEX_OES 0x1901 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STREAM_DRAW 0x88E0 +#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 +#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 +#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 +#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 +#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 +#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 +#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 +#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 +#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 +#define GL_SUBGROUP_SIZE_KHR 0x9532 +#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 +#define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_SURFACE_COMPRESSION_EXT 0x96C0 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_10BPC_EXT 0x96CD +#define GL_SURFACE_COMPRESSION_FIXED_RATE_11BPC_EXT 0x96CE +#define GL_SURFACE_COMPRESSION_FIXED_RATE_12BPC_EXT 0x96CF +#define GL_SURFACE_COMPRESSION_FIXED_RATE_1BPC_EXT 0x96C4 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_2BPC_EXT 0x96C5 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_3BPC_EXT 0x96C6 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_4BPC_EXT 0x96C7 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_5BPC_EXT 0x96C8 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_6BPC_EXT 0x96C9 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_7BPC_EXT 0x96CA +#define GL_SURFACE_COMPRESSION_FIXED_RATE_8BPC_EXT 0x96CB +#define GL_SURFACE_COMPRESSION_FIXED_RATE_9BPC_EXT 0x96CC +#define GL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT 0x96C2 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT 0x96C1 +#define GL_TESS_CONTROL_OUTPUT_VERTICES_EXT 0x8E75 +#define GL_TESS_CONTROL_OUTPUT_VERTICES_OES 0x8E75 +#define GL_TESS_CONTROL_SHADER_BIT_EXT 0x00000008 +#define GL_TESS_CONTROL_SHADER_BIT_OES 0x00000008 +#define GL_TESS_CONTROL_SHADER_EXT 0x8E88 +#define GL_TESS_CONTROL_SHADER_OES 0x8E88 +#define GL_TESS_EVALUATION_SHADER_BIT_EXT 0x00000010 +#define GL_TESS_EVALUATION_SHADER_BIT_OES 0x00000010 +#define GL_TESS_EVALUATION_SHADER_EXT 0x8E87 +#define GL_TESS_EVALUATION_SHADER_OES 0x8E87 +#define GL_TESS_GEN_MODE_EXT 0x8E76 +#define GL_TESS_GEN_MODE_OES 0x8E76 +#define GL_TESS_GEN_POINT_MODE_EXT 0x8E79 +#define GL_TESS_GEN_POINT_MODE_OES 0x8E79 +#define GL_TESS_GEN_SPACING_EXT 0x8E77 +#define GL_TESS_GEN_SPACING_OES 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER_EXT 0x8E78 +#define GL_TESS_GEN_VERTEX_ORDER_OES 0x8E78 +#define GL_TEXTURE 0x1702 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES 0x9102 +#define GL_TEXTURE_3D 0x806F +#define GL_TEXTURE_3D_OES 0x806F +#define GL_TEXTURE_ASTC_DECODE_PRECISION_EXT 0x8F69 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY_OES 0x9105 +#define GL_TEXTURE_BINDING_3D_OES 0x806A +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BINDING_BUFFER_OES 0x8C2C +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_EXT 0x900A +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_OES 0x900A +#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67 +#define GL_TEXTURE_BORDER_COLOR_EXT 0x1004 +#define GL_TEXTURE_BORDER_COLOR_OES 0x1004 +#define GL_TEXTURE_BUFFER_BINDING_EXT 0x8C2A +#define GL_TEXTURE_BUFFER_BINDING_OES 0x8C2A +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_OES 0x8C2D +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_TEXTURE_BUFFER_OES 0x8C2A +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_EXT 0x919F +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_OES 0x919F +#define GL_TEXTURE_BUFFER_OFFSET_EXT 0x919D +#define GL_TEXTURE_BUFFER_OFFSET_OES 0x919D +#define GL_TEXTURE_BUFFER_SIZE_EXT 0x919E +#define GL_TEXTURE_BUFFER_SIZE_OES 0x919E +#define GL_TEXTURE_COMPARE_FUNC_EXT 0x884D +#define GL_TEXTURE_COMPARE_MODE_EXT 0x884C +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_ARRAY_EXT 0x9009 +#define GL_TEXTURE_CUBE_MAP_ARRAY_OES 0x9009 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#define GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT 0x8FBF +#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_PROTECTED_EXT 0x8BFA +#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 +#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366 +#define GL_TEXTURE_SPARSE_EXT 0x91A6 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_TEXTURE_TILING_EXT 0x9580 +#define GL_TEXTURE_VIEW_MIN_LAYER_EXT 0x82DD +#define GL_TEXTURE_VIEW_MIN_LAYER_OES 0x82DD +#define GL_TEXTURE_VIEW_MIN_LEVEL_EXT 0x82DB +#define GL_TEXTURE_VIEW_MIN_LEVEL_OES 0x82DB +#define GL_TEXTURE_VIEW_NUM_LAYERS_EXT 0x82DE +#define GL_TEXTURE_VIEW_NUM_LAYERS_OES 0x82DE +#define GL_TEXTURE_VIEW_NUM_LEVELS_EXT 0x82DC +#define GL_TEXTURE_VIEW_NUM_LEVELS_OES 0x82DC +#define GL_TEXTURE_WRAP_R_OES 0x8072 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TILING_TYPES_EXT 0x9583 +#define GL_TIMESTAMP_EXT 0x8E28 +#define GL_TIME_ELAPSED_EXT 0x88BF +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLES_ADJACENCY_OES 0x000C +#define GL_TRIANGLE_FAN 0x0006 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_TRIANGLE_STRIP_ADJACENCY_OES 0x000D +#define GL_TRUE 1 +#define GL_UNDEFINED_VERTEX_EXT 0x8260 +#define GL_UNDEFINED_VERTEX_OES 0x8260 +#define GL_UNKNOWN_CONTEXT_RESET_EXT 0x8255 +#define GL_UNKNOWN_CONTEXT_RESET_KHR 0x8255 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNPACK_ROW_LENGTH_EXT 0x0CF2 +#define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4 +#define GL_UNPACK_SKIP_ROWS_EXT 0x0CF3 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_INT_10_10_10_2_OES 0x8DF6 +#define GL_UNSIGNED_INT_24_8_OES 0x84FA +#define GL_UNSIGNED_INT_2_10_10_10_REV_EXT 0x8368 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_OES 0x9067 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_OES 0x906A +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910D +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_OES 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900F +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_OES 0x900F +#define GL_UNSIGNED_NORMALIZED_EXT 0x8C17 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_UPPER_LEFT_EXT 0x8CA2 +#define GL_UUID_SIZE_EXT 16 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_VENDOR 0x1F00 +#define GL_VERSION 0x1F02 +#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5 +#define GL_VERTEX_ARRAY_KHR 0x8074 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_EXT 0x88FE +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_VERTEX_SHADER_BIT_EXT 0x00000001 +#define GL_VIEWPORT 0x0BA2 +#define GL_VIEWPORT_BOUNDS_RANGE_OES 0x825D +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX_OES 0x825F +#define GL_VIEWPORT_SUBPIXEL_BITS_OES 0x825C +#define GL_VIRTUAL_PAGE_SIZE_INDEX_EXT 0x91A7 +#define GL_VIRTUAL_PAGE_SIZE_X_EXT 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_EXT 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_EXT 0x9197 +#define GL_WEIGHTED_AVERAGE_ARB 0x9367 +#define GL_WEIGHTED_AVERAGE_EXT 0x9367 +#define GL_WINDOW_RECTANGLE_EXT 0x8F12 +#define GL_WINDOW_RECTANGLE_MODE_EXT 0x8F13 +#define GL_WRITE_ONLY_OES 0x88B9 +#define GL_ZERO 0 +#define GL_ZERO_TO_ONE 0x935F +#define GL_ZERO_TO_ONE_EXT 0x935F + + +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are 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 Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_GLAD_API_PTR + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_GLAD_API_PTR funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_GLAD_API_PTR + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_GLAD_API_PTR __stdcall +#else +# define KHRONOS_GLAD_API_PTR +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef khronos_int8_t GLbyte; +typedef khronos_uint8_t GLubyte; +typedef khronos_int16_t GLshort; +typedef khronos_uint16_t GLushort; +typedef int GLint; +typedef unsigned int GLuint; +typedef khronos_int32_t GLclampx; +typedef int GLsizei; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef double GLdouble; +typedef double GLclampd; +typedef void *GLeglClientBufferEXT; +typedef void *GLeglImageOES; +typedef char GLchar; +typedef char GLcharARB; +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef khronos_uint16_t GLhalf; +typedef khronos_uint16_t GLhalfARB; +typedef khronos_int32_t GLfixed; +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptr; +#else +typedef khronos_intptr_t GLintptr; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptrARB; +#else +typedef khronos_intptr_t GLintptrARB; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptr; +#else +typedef khronos_ssize_t GLsizeiptr; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptrARB; +#else +typedef khronos_ssize_t GLsizeiptrARB; +#endif +typedef khronos_int64_t GLint64; +typedef khronos_int64_t GLint64EXT; +typedef khronos_uint64_t GLuint64; +typedef khronos_uint64_t GLuint64EXT; +typedef struct __GLsync *GLsync; +struct _cl_context; +struct _cl_event; +typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +typedef unsigned short GLhalfNV; +typedef GLintptr GLvdpauSurfaceNV; +typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); + + +#define GL_ES_VERSION_2_0 1 +GLAD_API_CALL int GLAD_GL_ES_VERSION_2_0; +#define GL_EXT_EGL_image_array 1 +GLAD_API_CALL int GLAD_GL_EXT_EGL_image_array; +#define GL_EXT_EGL_image_storage 1 +GLAD_API_CALL int GLAD_GL_EXT_EGL_image_storage; +#define GL_EXT_EGL_image_storage_compression 1 +GLAD_API_CALL int GLAD_GL_EXT_EGL_image_storage_compression; +#define GL_EXT_YUV_target 1 +GLAD_API_CALL int GLAD_GL_EXT_YUV_target; +#define GL_EXT_base_instance 1 +GLAD_API_CALL int GLAD_GL_EXT_base_instance; +#define GL_EXT_blend_func_extended 1 +GLAD_API_CALL int GLAD_GL_EXT_blend_func_extended; +#define GL_EXT_blend_minmax 1 +GLAD_API_CALL int GLAD_GL_EXT_blend_minmax; +#define GL_EXT_buffer_storage 1 +GLAD_API_CALL int GLAD_GL_EXT_buffer_storage; +#define GL_EXT_clear_texture 1 +GLAD_API_CALL int GLAD_GL_EXT_clear_texture; +#define GL_EXT_clip_control 1 +GLAD_API_CALL int GLAD_GL_EXT_clip_control; +#define GL_EXT_clip_cull_distance 1 +GLAD_API_CALL int GLAD_GL_EXT_clip_cull_distance; +#define GL_EXT_color_buffer_float 1 +GLAD_API_CALL int GLAD_GL_EXT_color_buffer_float; +#define GL_EXT_color_buffer_half_float 1 +GLAD_API_CALL int GLAD_GL_EXT_color_buffer_half_float; +#define GL_EXT_conservative_depth 1 +GLAD_API_CALL int GLAD_GL_EXT_conservative_depth; +#define GL_EXT_copy_image 1 +GLAD_API_CALL int GLAD_GL_EXT_copy_image; +#define GL_EXT_debug_label 1 +GLAD_API_CALL int GLAD_GL_EXT_debug_label; +#define GL_EXT_debug_marker 1 +GLAD_API_CALL int GLAD_GL_EXT_debug_marker; +#define GL_EXT_depth_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_depth_clamp; +#define GL_EXT_discard_framebuffer 1 +GLAD_API_CALL int GLAD_GL_EXT_discard_framebuffer; +#define GL_EXT_disjoint_timer_query 1 +GLAD_API_CALL int GLAD_GL_EXT_disjoint_timer_query; +#define GL_EXT_draw_buffers 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_buffers; +#define GL_EXT_draw_buffers_indexed 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_buffers_indexed; +#define GL_EXT_draw_elements_base_vertex 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_elements_base_vertex; +#define GL_EXT_draw_instanced 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_instanced; +#define GL_EXT_draw_transform_feedback 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_transform_feedback; +#define GL_EXT_external_buffer 1 +GLAD_API_CALL int GLAD_GL_EXT_external_buffer; +#define GL_EXT_float_blend 1 +GLAD_API_CALL int GLAD_GL_EXT_float_blend; +#define GL_EXT_fragment_shading_rate 1 +GLAD_API_CALL int GLAD_GL_EXT_fragment_shading_rate; +#define GL_EXT_geometry_point_size 1 +GLAD_API_CALL int GLAD_GL_EXT_geometry_point_size; +#define GL_EXT_geometry_shader 1 +GLAD_API_CALL int GLAD_GL_EXT_geometry_shader; +#define GL_EXT_gpu_shader5 1 +GLAD_API_CALL int GLAD_GL_EXT_gpu_shader5; +#define GL_EXT_instanced_arrays 1 +GLAD_API_CALL int GLAD_GL_EXT_instanced_arrays; +#define GL_EXT_map_buffer_range 1 +GLAD_API_CALL int GLAD_GL_EXT_map_buffer_range; +#define GL_EXT_memory_object 1 +GLAD_API_CALL int GLAD_GL_EXT_memory_object; +#define GL_EXT_memory_object_fd 1 +GLAD_API_CALL int GLAD_GL_EXT_memory_object_fd; +#define GL_EXT_memory_object_win32 1 +GLAD_API_CALL int GLAD_GL_EXT_memory_object_win32; +#define GL_EXT_multi_draw_arrays 1 +GLAD_API_CALL int GLAD_GL_EXT_multi_draw_arrays; +#define GL_EXT_multi_draw_indirect 1 +GLAD_API_CALL int GLAD_GL_EXT_multi_draw_indirect; +#define GL_EXT_multisampled_compatibility 1 +GLAD_API_CALL int GLAD_GL_EXT_multisampled_compatibility; +#define GL_EXT_multisampled_render_to_texture 1 +GLAD_API_CALL int GLAD_GL_EXT_multisampled_render_to_texture; +#define GL_EXT_multisampled_render_to_texture2 1 +GLAD_API_CALL int GLAD_GL_EXT_multisampled_render_to_texture2; +#define GL_EXT_multiview_draw_buffers 1 +GLAD_API_CALL int GLAD_GL_EXT_multiview_draw_buffers; +#define GL_EXT_multiview_tessellation_geometry_shader 1 +GLAD_API_CALL int GLAD_GL_EXT_multiview_tessellation_geometry_shader; +#define GL_EXT_multiview_texture_multisample 1 +GLAD_API_CALL int GLAD_GL_EXT_multiview_texture_multisample; +#define GL_EXT_multiview_timer_query 1 +GLAD_API_CALL int GLAD_GL_EXT_multiview_timer_query; +#define GL_EXT_occlusion_query_boolean 1 +GLAD_API_CALL int GLAD_GL_EXT_occlusion_query_boolean; +#define GL_EXT_polygon_offset_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_polygon_offset_clamp; +#define GL_EXT_post_depth_coverage 1 +GLAD_API_CALL int GLAD_GL_EXT_post_depth_coverage; +#define GL_EXT_primitive_bounding_box 1 +GLAD_API_CALL int GLAD_GL_EXT_primitive_bounding_box; +#define GL_EXT_protected_textures 1 +GLAD_API_CALL int GLAD_GL_EXT_protected_textures; +#define GL_EXT_pvrtc_sRGB 1 +GLAD_API_CALL int GLAD_GL_EXT_pvrtc_sRGB; +#define GL_EXT_raster_multisample 1 +GLAD_API_CALL int GLAD_GL_EXT_raster_multisample; +#define GL_EXT_read_format_bgra 1 +GLAD_API_CALL int GLAD_GL_EXT_read_format_bgra; +#define GL_EXT_render_snorm 1 +GLAD_API_CALL int GLAD_GL_EXT_render_snorm; +#define GL_EXT_robustness 1 +GLAD_API_CALL int GLAD_GL_EXT_robustness; +#define GL_EXT_sRGB 1 +GLAD_API_CALL int GLAD_GL_EXT_sRGB; +#define GL_EXT_sRGB_write_control 1 +GLAD_API_CALL int GLAD_GL_EXT_sRGB_write_control; +#define GL_EXT_semaphore 1 +GLAD_API_CALL int GLAD_GL_EXT_semaphore; +#define GL_EXT_semaphore_fd 1 +GLAD_API_CALL int GLAD_GL_EXT_semaphore_fd; +#define GL_EXT_semaphore_win32 1 +GLAD_API_CALL int GLAD_GL_EXT_semaphore_win32; +#define GL_EXT_separate_depth_stencil 1 +GLAD_API_CALL int GLAD_GL_EXT_separate_depth_stencil; +#define GL_EXT_separate_shader_objects 1 +GLAD_API_CALL int GLAD_GL_EXT_separate_shader_objects; +#define GL_EXT_shader_framebuffer_fetch 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_framebuffer_fetch; +#define GL_EXT_shader_framebuffer_fetch_non_coherent 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_framebuffer_fetch_non_coherent; +#define GL_EXT_shader_group_vote 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_group_vote; +#define GL_EXT_shader_implicit_conversions 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_implicit_conversions; +#define GL_EXT_shader_integer_mix 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_integer_mix; +#define GL_EXT_shader_io_blocks 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_io_blocks; +#define GL_EXT_shader_non_constant_global_initializers 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_non_constant_global_initializers; +#define GL_EXT_shader_pixel_local_storage 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_pixel_local_storage; +#define GL_EXT_shader_pixel_local_storage2 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_pixel_local_storage2; +#define GL_EXT_shader_samples_identical 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_samples_identical; +#define GL_EXT_shader_texture_lod 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_texture_lod; +#define GL_EXT_shadow_samplers 1 +GLAD_API_CALL int GLAD_GL_EXT_shadow_samplers; +#define GL_EXT_sparse_texture 1 +GLAD_API_CALL int GLAD_GL_EXT_sparse_texture; +#define GL_EXT_sparse_texture2 1 +GLAD_API_CALL int GLAD_GL_EXT_sparse_texture2; +#define GL_EXT_tessellation_point_size 1 +GLAD_API_CALL int GLAD_GL_EXT_tessellation_point_size; +#define GL_EXT_tessellation_shader 1 +GLAD_API_CALL int GLAD_GL_EXT_tessellation_shader; +#define GL_EXT_texture_border_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_border_clamp; +#define GL_EXT_texture_buffer 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_buffer; +#define GL_EXT_texture_compression_astc_decode_mode 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_astc_decode_mode; +#define GL_EXT_texture_compression_bptc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_bptc; +#define GL_EXT_texture_compression_dxt1 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_dxt1; +#define GL_EXT_texture_compression_rgtc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_rgtc; +#define GL_EXT_texture_compression_s3tc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc; +#define GL_EXT_texture_compression_s3tc_srgb 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc_srgb; +#define GL_EXT_texture_cube_map_array 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_cube_map_array; +#define GL_EXT_texture_filter_anisotropic 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_filter_anisotropic; +#define GL_EXT_texture_filter_minmax 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_filter_minmax; +#define GL_EXT_texture_format_BGRA8888 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_format_BGRA8888; +#define GL_EXT_texture_format_sRGB_override 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_format_sRGB_override; +#define GL_EXT_texture_mirror_clamp_to_edge 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_mirror_clamp_to_edge; +#define GL_EXT_texture_norm16 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_norm16; +#define GL_EXT_texture_query_lod 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_query_lod; +#define GL_EXT_texture_rg 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_rg; +#define GL_EXT_texture_sRGB_R8 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_sRGB_R8; +#define GL_EXT_texture_sRGB_RG8 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_sRGB_RG8; +#define GL_EXT_texture_sRGB_decode 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_sRGB_decode; +#define GL_EXT_texture_shadow_lod 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_shadow_lod; +#define GL_EXT_texture_storage 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_storage; +#define GL_EXT_texture_storage_compression 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_storage_compression; +#define GL_EXT_texture_type_2_10_10_10_REV 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_type_2_10_10_10_REV; +#define GL_EXT_texture_view 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_view; +#define GL_EXT_unpack_subimage 1 +GLAD_API_CALL int GLAD_GL_EXT_unpack_subimage; +#define GL_EXT_win32_keyed_mutex 1 +GLAD_API_CALL int GLAD_GL_EXT_win32_keyed_mutex; +#define GL_EXT_window_rectangles 1 +GLAD_API_CALL int GLAD_GL_EXT_window_rectangles; +#define GL_KHR_blend_equation_advanced 1 +GLAD_API_CALL int GLAD_GL_KHR_blend_equation_advanced; +#define GL_KHR_blend_equation_advanced_coherent 1 +GLAD_API_CALL int GLAD_GL_KHR_blend_equation_advanced_coherent; +#define GL_KHR_context_flush_control 1 +GLAD_API_CALL int GLAD_GL_KHR_context_flush_control; +#define GL_KHR_debug 1 +GLAD_API_CALL int GLAD_GL_KHR_debug; +#define GL_KHR_no_error 1 +GLAD_API_CALL int GLAD_GL_KHR_no_error; +#define GL_KHR_parallel_shader_compile 1 +GLAD_API_CALL int GLAD_GL_KHR_parallel_shader_compile; +#define GL_KHR_robust_buffer_access_behavior 1 +GLAD_API_CALL int GLAD_GL_KHR_robust_buffer_access_behavior; +#define GL_KHR_robustness 1 +GLAD_API_CALL int GLAD_GL_KHR_robustness; +#define GL_KHR_shader_subgroup 1 +GLAD_API_CALL int GLAD_GL_KHR_shader_subgroup; +#define GL_KHR_texture_compression_astc_hdr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_hdr; +#define GL_KHR_texture_compression_astc_ldr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_ldr; +#define GL_KHR_texture_compression_astc_sliced_3d 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_sliced_3d; +#define GL_OES_EGL_image 1 +GLAD_API_CALL int GLAD_GL_OES_EGL_image; +#define GL_OES_EGL_image_external 1 +GLAD_API_CALL int GLAD_GL_OES_EGL_image_external; +#define GL_OES_EGL_image_external_essl3 1 +GLAD_API_CALL int GLAD_GL_OES_EGL_image_external_essl3; +#define GL_OES_compressed_ETC1_RGB8_sub_texture 1 +GLAD_API_CALL int GLAD_GL_OES_compressed_ETC1_RGB8_sub_texture; +#define GL_OES_compressed_ETC1_RGB8_texture 1 +GLAD_API_CALL int GLAD_GL_OES_compressed_ETC1_RGB8_texture; +#define GL_OES_compressed_paletted_texture 1 +GLAD_API_CALL int GLAD_GL_OES_compressed_paletted_texture; +#define GL_OES_copy_image 1 +GLAD_API_CALL int GLAD_GL_OES_copy_image; +#define GL_OES_depth24 1 +GLAD_API_CALL int GLAD_GL_OES_depth24; +#define GL_OES_depth32 1 +GLAD_API_CALL int GLAD_GL_OES_depth32; +#define GL_OES_depth_texture 1 +GLAD_API_CALL int GLAD_GL_OES_depth_texture; +#define GL_OES_draw_buffers_indexed 1 +GLAD_API_CALL int GLAD_GL_OES_draw_buffers_indexed; +#define GL_OES_draw_elements_base_vertex 1 +GLAD_API_CALL int GLAD_GL_OES_draw_elements_base_vertex; +#define GL_OES_element_index_uint 1 +GLAD_API_CALL int GLAD_GL_OES_element_index_uint; +#define GL_OES_fbo_render_mipmap 1 +GLAD_API_CALL int GLAD_GL_OES_fbo_render_mipmap; +#define GL_OES_fragment_precision_high 1 +GLAD_API_CALL int GLAD_GL_OES_fragment_precision_high; +#define GL_OES_geometry_point_size 1 +GLAD_API_CALL int GLAD_GL_OES_geometry_point_size; +#define GL_OES_geometry_shader 1 +GLAD_API_CALL int GLAD_GL_OES_geometry_shader; +#define GL_OES_get_program_binary 1 +GLAD_API_CALL int GLAD_GL_OES_get_program_binary; +#define GL_OES_gpu_shader5 1 +GLAD_API_CALL int GLAD_GL_OES_gpu_shader5; +#define GL_OES_mapbuffer 1 +GLAD_API_CALL int GLAD_GL_OES_mapbuffer; +#define GL_OES_packed_depth_stencil 1 +GLAD_API_CALL int GLAD_GL_OES_packed_depth_stencil; +#define GL_OES_primitive_bounding_box 1 +GLAD_API_CALL int GLAD_GL_OES_primitive_bounding_box; +#define GL_OES_required_internalformat 1 +GLAD_API_CALL int GLAD_GL_OES_required_internalformat; +#define GL_OES_rgb8_rgba8 1 +GLAD_API_CALL int GLAD_GL_OES_rgb8_rgba8; +#define GL_OES_sample_shading 1 +GLAD_API_CALL int GLAD_GL_OES_sample_shading; +#define GL_OES_sample_variables 1 +GLAD_API_CALL int GLAD_GL_OES_sample_variables; +#define GL_OES_shader_image_atomic 1 +GLAD_API_CALL int GLAD_GL_OES_shader_image_atomic; +#define GL_OES_shader_io_blocks 1 +GLAD_API_CALL int GLAD_GL_OES_shader_io_blocks; +#define GL_OES_shader_multisample_interpolation 1 +GLAD_API_CALL int GLAD_GL_OES_shader_multisample_interpolation; +#define GL_OES_standard_derivatives 1 +GLAD_API_CALL int GLAD_GL_OES_standard_derivatives; +#define GL_OES_stencil1 1 +GLAD_API_CALL int GLAD_GL_OES_stencil1; +#define GL_OES_stencil4 1 +GLAD_API_CALL int GLAD_GL_OES_stencil4; +#define GL_OES_surfaceless_context 1 +GLAD_API_CALL int GLAD_GL_OES_surfaceless_context; +#define GL_OES_tessellation_point_size 1 +GLAD_API_CALL int GLAD_GL_OES_tessellation_point_size; +#define GL_OES_tessellation_shader 1 +GLAD_API_CALL int GLAD_GL_OES_tessellation_shader; +#define GL_OES_texture_3D 1 +GLAD_API_CALL int GLAD_GL_OES_texture_3D; +#define GL_OES_texture_border_clamp 1 +GLAD_API_CALL int GLAD_GL_OES_texture_border_clamp; +#define GL_OES_texture_buffer 1 +GLAD_API_CALL int GLAD_GL_OES_texture_buffer; +#define GL_OES_texture_compression_astc 1 +GLAD_API_CALL int GLAD_GL_OES_texture_compression_astc; +#define GL_OES_texture_cube_map_array 1 +GLAD_API_CALL int GLAD_GL_OES_texture_cube_map_array; +#define GL_OES_texture_float 1 +GLAD_API_CALL int GLAD_GL_OES_texture_float; +#define GL_OES_texture_float_linear 1 +GLAD_API_CALL int GLAD_GL_OES_texture_float_linear; +#define GL_OES_texture_half_float 1 +GLAD_API_CALL int GLAD_GL_OES_texture_half_float; +#define GL_OES_texture_half_float_linear 1 +GLAD_API_CALL int GLAD_GL_OES_texture_half_float_linear; +#define GL_OES_texture_npot 1 +GLAD_API_CALL int GLAD_GL_OES_texture_npot; +#define GL_OES_texture_stencil8 1 +GLAD_API_CALL int GLAD_GL_OES_texture_stencil8; +#define GL_OES_texture_storage_multisample_2d_array 1 +GLAD_API_CALL int GLAD_GL_OES_texture_storage_multisample_2d_array; +#define GL_OES_texture_view 1 +GLAD_API_CALL int GLAD_GL_OES_texture_view; +#define GL_OES_vertex_array_object 1 +GLAD_API_CALL int GLAD_GL_OES_vertex_array_object; +#define GL_OES_vertex_half_float 1 +GLAD_API_CALL int GLAD_GL_OES_vertex_half_float; +#define GL_OES_vertex_type_10_10_10_2 1 +GLAD_API_CALL int GLAD_GL_OES_vertex_type_10_10_10_2; +#define GL_OES_viewport_array 1 +GLAD_API_CALL int GLAD_GL_OES_viewport_array; + + +typedef GLboolean (GLAD_API_PTR *PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC)(GLuint memory, GLuint64 key, GLuint timeout); +typedef void (GLAD_API_PTR *PFNGLACTIVESHADERPROGRAMEXTPROC)(GLuint pipeline, GLuint program); +typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); +typedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLBEGINQUERYEXTPROC)(GLenum target, GLuint id); +typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONEXTPROC)(GLuint program, GLuint color, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONINDEXEDEXTPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +typedef void (GLAD_API_PTR *PFNGLBINDPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +typedef void (GLAD_API_PTR *PFNGLBINDVERTEXARRAYOESPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLBLENDBARRIERKHRPROC)(void); +typedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIEXTPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIOESPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONIEXTPROC)(GLuint buf, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONIOESPROC)(GLuint buf, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIEXTPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIOESPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIEXTPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIOESPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); +typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEEXTPROC)(GLenum target, GLsizeiptr size, const void * data, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEEXTERNALEXTPROC)(GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEMEMEXTPROC)(GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); +typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHFPROC)(GLfloat d); +typedef void (GLAD_API_PTR *PFNGLCLEARPIXELLOCALSTORAGEUIEXTPROC)(GLsizei offset, GLsizei n, const GLuint * values); +typedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); +typedef void (GLAD_API_PTR *PFNGLCLEARTEXIMAGEEXTPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLEARTEXSUBIMAGEEXTPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLIPCONTROLEXTPROC)(GLenum origin, GLenum depth); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKIEXTPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKIOESPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DOESPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOPYIMAGESUBDATAEXTPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (GLAD_API_PTR *PFNGLCOPYIMAGESUBDATAOESPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DOESPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCREATEMEMORYOBJECTSEXTPROC)(GLsizei n, GLuint * memoryObjects); +typedef GLuint (GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROGRAMVEXTPROC)(GLenum type, GLsizei count, const GLchar *const* strings); +typedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void * userParam); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); +typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLDELETEMEMORYOBJECTSEXTPROC)(GLsizei n, const GLuint * memoryObjects); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPIPELINESEXTPROC)(GLsizei n, const GLuint * pipelines); +typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESEXTPROC)(GLsizei n, const GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLDELETESEMAPHORESEXTPROC)(GLsizei n, const GLuint * semaphores); +typedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSOESPROC)(GLsizei n, const GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); +typedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEARRAYFVOESPROC)(GLuint first, GLsizei count, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEINDEXEDFOESPROC)(GLuint index, GLfloat n, GLfloat f); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f); +typedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEIEXTPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEIOESPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISCARDFRAMEBUFFEREXTPROC)(GLenum target, GLsizei numAttachments, const GLenum * attachments); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDEXTPROC)(GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSEXTPROC)(GLsizei n, const GLenum * bufs); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSINDEXEDEXTPROC)(GLint n, const GLenum * location, const GLint * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXOESPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKEXTPROC)(GLenum mode, GLuint id); +typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDEXTPROC)(GLenum mode, GLuint id, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC)(GLenum target, GLeglImageOES image); +typedef void (GLAD_API_PTR *PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC)(GLenum target, GLeglImageOES image, const GLint * attrib_list); +typedef void (GLAD_API_PTR *PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum target, GLeglImageOES image); +typedef void (GLAD_API_PTR *PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC)(GLuint texture, GLeglImageOES image, const GLint * attrib_list); +typedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEIEXTPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEIOESPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLENDQUERYEXTPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLFINISHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEEXTPROC)(GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC)(GLuint target, GLsizei size); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERSHADINGRATEEXTPROC)(GLenum target, GLenum attachment, GLuint texture, GLint baseLayer, GLsizei numLayers, GLsizei texelWidth, GLsizei texelHeight); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DOESPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREEXTPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREOESPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLGENPROGRAMPIPELINESEXTPROC)(GLsizei n, GLuint * pipelines); +typedef void (GLAD_API_PTR *PFNGLGENQUERIESEXTPROC)(GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLGENSEMAPHORESEXTPROC)(GLsizei n, GLuint * semaphores); +typedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLGENVERTEXARRAYSOESPROC)(GLsizei n, GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); +typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVOESPROC)(GLenum target, GLenum pname, void ** params); +typedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); +typedef GLenum (GLAD_API_PTR *PFNGLGETERRORPROC)(void); +typedef void (GLAD_API_PTR *PFNGLGETFLOATI_VOESPROC)(GLenum target, GLuint index, GLfloat * data); +typedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); +typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATAINDEXEXTPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETFRAGMENTSHADINGRATESEXTPROC)(GLsizei samples, GLsizei maxCount, GLsizei * count, GLenum * shadingRates); +typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef GLsizei (GLAD_API_PTR *PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC)(GLuint target); +typedef GLenum (GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSEXTPROC)(void); +typedef GLenum (GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void); +typedef void (GLAD_API_PTR *PFNGLGETINTEGER64VEXTPROC)(GLenum pname, GLint64 * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERI_VEXTPROC)(GLenum target, GLuint index, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC)(GLuint memoryObject, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELEXTPROC)(GLenum type, GLuint object, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTPTRLABELKHRPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETPOINTERVKHRPROC)(GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMBINARYOESPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLenum * binaryFormat, void * binary); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC)(GLuint pipeline, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEIVEXTPROC)(GLuint pipeline, GLenum pname, GLint * params); +typedef GLint (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC)(GLuint program, GLenum programInterface, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VEXTPROC)(GLuint id, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVEXTPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUI64VEXTPROC)(GLuint id, GLenum pname, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVEXTPROC)(GLuint id, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVEXTPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVOESPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVEXTPROC)(GLuint sampler, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVOESPROC)(GLuint sampler, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC)(GLuint semaphore, GLenum pname, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision); +typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); +typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVOESPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVEXTPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVOESPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETUNSIGNEDBYTEI_VEXTPROC)(GLenum target, GLuint index, GLubyte * data); +typedef void (GLAD_API_PTR *PFNGLGETUNSIGNEDBYTEVEXTPROC)(GLenum pname, GLubyte * data); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVEXTPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVEXTPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUIVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLIMPORTMEMORYFDEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, GLint fd); +typedef void (GLAD_API_PTR *PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, void * handle); +typedef void (GLAD_API_PTR *PFNGLIMPORTMEMORYWIN32NAMEEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, const void * name); +typedef void (GLAD_API_PTR *PFNGLIMPORTSEMAPHOREFDEXTPROC)(GLuint semaphore, GLenum handleType, GLint fd); +typedef void (GLAD_API_PTR *PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC)(GLuint semaphore, GLenum handleType, void * handle); +typedef void (GLAD_API_PTR *PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC)(GLuint semaphore, GLenum handleType, const void * name); +typedef void (GLAD_API_PTR *PFNGLINSERTEVENTMARKEREXTPROC)(GLsizei length, const GLchar * marker); +typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIEXTPROC)(GLenum target, GLuint index); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIOESPROC)(GLenum target, GLuint index); +typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISMEMORYOBJECTEXTPROC)(GLuint memoryObject); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYEXTPROC)(GLuint id); +typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISSEMAPHOREEXTPROC)(GLuint semaphore); +typedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); +typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); +typedef GLboolean (GLAD_API_PTR *PFNGLISVERTEXARRAYOESPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLLABELOBJECTEXTPROC)(GLenum type, GLuint object, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); +typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFEROESPROC)(GLenum target, GLenum access); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERRANGEEXTPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (GLAD_API_PTR *PFNGLMAXSHADERCOMPILERTHREADSKHRPROC)(GLuint count); +typedef void (GLAD_API_PTR *PFNGLMEMORYOBJECTPARAMETERIVEXTPROC)(GLuint memoryObject, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLMINSAMPLESHADINGOESPROC)(GLfloat value); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSEXTPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSINDIRECTEXTPROC)(GLenum mode, const void * indirect, GLsizei drawcount, GLsizei stride); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount, const GLint * basevertex); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSEXTPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSINDIRECTEXTPROC)(GLenum mode, GLenum type, const void * indirect, GLsizei drawcount, GLsizei stride); +typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC)(GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLOBJECTPTRLABELKHRPROC)(const void * ptr, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLPATCHPARAMETERIEXTPROC)(GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPATCHPARAMETERIOESPROC)(GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETCLAMPEXTPROC)(GLfloat factor, GLfloat units, GLfloat clamp); +typedef void (GLAD_API_PTR *PFNGLPOPDEBUGGROUPKHRPROC)(void); +typedef void (GLAD_API_PTR *PFNGLPOPGROUPMARKEREXTPROC)(void); +typedef void (GLAD_API_PTR *PFNGLPRIMITIVEBOUNDINGBOXEXTPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +typedef void (GLAD_API_PTR *PFNGLPRIMITIVEBOUNDINGBOXOESPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +typedef void (GLAD_API_PTR *PFNGLPROGRAMBINARYOESPROC)(GLuint program, GLenum binaryFormat, const void * binary, GLint length); +typedef void (GLAD_API_PTR *PFNGLPROGRAMPARAMETERIEXTPROC)(GLuint program, GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FEXTPROC)(GLuint program, GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IEXTPROC)(GLuint program, GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIEXTPROC)(GLuint program, GLint location, GLuint v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message); +typedef void (GLAD_API_PTR *PFNGLPUSHGROUPMARKEREXTPROC)(GLsizei length, const GLchar * marker); +typedef void (GLAD_API_PTR *PFNGLQUERYCOUNTEREXTPROC)(GLuint id, GLenum target); +typedef void (GLAD_API_PTR *PFNGLRASTERSAMPLESEXTPROC)(GLuint samples, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLREADBUFFERINDEXEDEXTPROC)(GLenum src, GLint index); +typedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLREADNPIXELSEXTPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); +typedef void (GLAD_API_PTR *PFNGLREADNPIXELSKHRPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); +typedef GLboolean (GLAD_API_PTR *PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC)(GLuint memory, GLuint64 key); +typedef void (GLAD_API_PTR *PFNGLRELEASESHADERCOMPILERPROC)(void); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVEXTPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVOESPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVEXTPROC)(GLuint sampler, GLenum pname, const GLuint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVOESPROC)(GLuint sampler, GLenum pname, const GLuint * param); +typedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSCISSORARRAYVOESPROC)(GLuint first, GLsizei count, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLSCISSORINDEXEDOESPROC)(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSCISSORINDEXEDVOESPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLSEMAPHOREPARAMETERUI64VEXTPROC)(GLuint semaphore, GLenum pname, const GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint * shaders, GLenum binaryFormat, const void * binary, GLsizei length); +typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); +typedef void (GLAD_API_PTR *PFNGLSHADINGRATECOMBINEROPSEXTPROC)(GLenum combinerOp0, GLenum combinerOp1); +typedef void (GLAD_API_PTR *PFNGLSHADINGRATEEXTPROC)(GLenum rate); +typedef void (GLAD_API_PTR *PFNGLSIGNALSEMAPHOREEXTPROC)(GLuint semaphore, GLuint numBufferBarriers, const GLuint * buffers, GLuint numTextureBarriers, const GLuint * textures, const GLenum * dstLayouts); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFEREXTPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFEROESPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFERRANGEEXTPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFERRANGEOESPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DOESPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXPAGECOMMITMENTEXTPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVEXTPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVOESPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVEXTPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVOESPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE1DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE2DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEATTRIBS2DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, const GLint * attrib_list); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEATTRIBS3DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, const GLint * attrib_list); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEMEM2DEXTPROC)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEMEM3DEXTPROC)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE3DOESPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE1DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE2DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE3DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGEMEM2DEXTPROC)(GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC)(GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGEMEM3DEXTPROC)(GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC)(GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXTUREVIEWEXTPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (GLAD_API_PTR *PFNGLTEXTUREVIEWOESPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFEROESPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMSTAGESEXTPROC)(GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISOREXTPROC)(GLuint index, GLuint divisor); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTARRAYVOESPROC)(GLuint first, GLsizei count, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFOESPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFVOESPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLWAITSEMAPHOREEXTPROC)(GLuint semaphore, GLuint numBufferBarriers, const GLuint * buffers, GLuint numTextureBarriers, const GLuint * textures, const GLenum * srcLayouts); +typedef void (GLAD_API_PTR *PFNGLWINDOWRECTANGLESEXTPROC)(GLenum mode, GLsizei count, const GLint * box); + +GLAD_API_CALL PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC glad_glAcquireKeyedMutexWin32EXT; +#define glAcquireKeyedMutexWin32EXT glad_glAcquireKeyedMutexWin32EXT +GLAD_API_CALL PFNGLACTIVESHADERPROGRAMEXTPROC glad_glActiveShaderProgramEXT; +#define glActiveShaderProgramEXT glad_glActiveShaderProgramEXT +GLAD_API_CALL PFNGLACTIVETEXTUREPROC glad_glActiveTexture; +#define glActiveTexture glad_glActiveTexture +GLAD_API_CALL PFNGLATTACHSHADERPROC glad_glAttachShader; +#define glAttachShader glad_glAttachShader +GLAD_API_CALL PFNGLBEGINQUERYEXTPROC glad_glBeginQueryEXT; +#define glBeginQueryEXT glad_glBeginQueryEXT +GLAD_API_CALL PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; +#define glBindAttribLocation glad_glBindAttribLocation +GLAD_API_CALL PFNGLBINDBUFFERPROC glad_glBindBuffer; +#define glBindBuffer glad_glBindBuffer +GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONEXTPROC glad_glBindFragDataLocationEXT; +#define glBindFragDataLocationEXT glad_glBindFragDataLocationEXT +GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONINDEXEDEXTPROC glad_glBindFragDataLocationIndexedEXT; +#define glBindFragDataLocationIndexedEXT glad_glBindFragDataLocationIndexedEXT +GLAD_API_CALL PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; +#define glBindFramebuffer glad_glBindFramebuffer +GLAD_API_CALL PFNGLBINDPROGRAMPIPELINEEXTPROC glad_glBindProgramPipelineEXT; +#define glBindProgramPipelineEXT glad_glBindProgramPipelineEXT +GLAD_API_CALL PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; +#define glBindRenderbuffer glad_glBindRenderbuffer +GLAD_API_CALL PFNGLBINDTEXTUREPROC glad_glBindTexture; +#define glBindTexture glad_glBindTexture +GLAD_API_CALL PFNGLBINDVERTEXARRAYOESPROC glad_glBindVertexArrayOES; +#define glBindVertexArrayOES glad_glBindVertexArrayOES +GLAD_API_CALL PFNGLBLENDBARRIERKHRPROC glad_glBlendBarrierKHR; +#define glBlendBarrierKHR glad_glBlendBarrierKHR +GLAD_API_CALL PFNGLBLENDCOLORPROC glad_glBlendColor; +#define glBlendColor glad_glBlendColor +GLAD_API_CALL PFNGLBLENDEQUATIONPROC glad_glBlendEquation; +#define glBlendEquation glad_glBlendEquation +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; +#define glBlendEquationSeparate glad_glBlendEquationSeparate +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEIEXTPROC glad_glBlendEquationSeparateiEXT; +#define glBlendEquationSeparateiEXT glad_glBlendEquationSeparateiEXT +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEIOESPROC glad_glBlendEquationSeparateiOES; +#define glBlendEquationSeparateiOES glad_glBlendEquationSeparateiOES +GLAD_API_CALL PFNGLBLENDEQUATIONIEXTPROC glad_glBlendEquationiEXT; +#define glBlendEquationiEXT glad_glBlendEquationiEXT +GLAD_API_CALL PFNGLBLENDEQUATIONIOESPROC glad_glBlendEquationiOES; +#define glBlendEquationiOES glad_glBlendEquationiOES +GLAD_API_CALL PFNGLBLENDFUNCPROC glad_glBlendFunc; +#define glBlendFunc glad_glBlendFunc +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; +#define glBlendFuncSeparate glad_glBlendFuncSeparate +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEIEXTPROC glad_glBlendFuncSeparateiEXT; +#define glBlendFuncSeparateiEXT glad_glBlendFuncSeparateiEXT +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEIOESPROC glad_glBlendFuncSeparateiOES; +#define glBlendFuncSeparateiOES glad_glBlendFuncSeparateiOES +GLAD_API_CALL PFNGLBLENDFUNCIEXTPROC glad_glBlendFunciEXT; +#define glBlendFunciEXT glad_glBlendFunciEXT +GLAD_API_CALL PFNGLBLENDFUNCIOESPROC glad_glBlendFunciOES; +#define glBlendFunciOES glad_glBlendFunciOES +GLAD_API_CALL PFNGLBUFFERDATAPROC glad_glBufferData; +#define glBufferData glad_glBufferData +GLAD_API_CALL PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT; +#define glBufferStorageEXT glad_glBufferStorageEXT +GLAD_API_CALL PFNGLBUFFERSTORAGEEXTERNALEXTPROC glad_glBufferStorageExternalEXT; +#define glBufferStorageExternalEXT glad_glBufferStorageExternalEXT +GLAD_API_CALL PFNGLBUFFERSTORAGEMEMEXTPROC glad_glBufferStorageMemEXT; +#define glBufferStorageMemEXT glad_glBufferStorageMemEXT +GLAD_API_CALL PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; +#define glBufferSubData glad_glBufferSubData +GLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; +#define glCheckFramebufferStatus glad_glCheckFramebufferStatus +GLAD_API_CALL PFNGLCLEARPROC glad_glClear; +#define glClear glad_glClear +GLAD_API_CALL PFNGLCLEARCOLORPROC glad_glClearColor; +#define glClearColor glad_glClearColor +GLAD_API_CALL PFNGLCLEARDEPTHFPROC glad_glClearDepthf; +#define glClearDepthf glad_glClearDepthf +GLAD_API_CALL PFNGLCLEARPIXELLOCALSTORAGEUIEXTPROC glad_glClearPixelLocalStorageuiEXT; +#define glClearPixelLocalStorageuiEXT glad_glClearPixelLocalStorageuiEXT +GLAD_API_CALL PFNGLCLEARSTENCILPROC glad_glClearStencil; +#define glClearStencil glad_glClearStencil +GLAD_API_CALL PFNGLCLEARTEXIMAGEEXTPROC glad_glClearTexImageEXT; +#define glClearTexImageEXT glad_glClearTexImageEXT +GLAD_API_CALL PFNGLCLEARTEXSUBIMAGEEXTPROC glad_glClearTexSubImageEXT; +#define glClearTexSubImageEXT glad_glClearTexSubImageEXT +GLAD_API_CALL PFNGLCLIPCONTROLEXTPROC glad_glClipControlEXT; +#define glClipControlEXT glad_glClipControlEXT +GLAD_API_CALL PFNGLCOLORMASKPROC glad_glColorMask; +#define glColorMask glad_glColorMask +GLAD_API_CALL PFNGLCOLORMASKIEXTPROC glad_glColorMaskiEXT; +#define glColorMaskiEXT glad_glColorMaskiEXT +GLAD_API_CALL PFNGLCOLORMASKIOESPROC glad_glColorMaskiOES; +#define glColorMaskiOES glad_glColorMaskiOES +GLAD_API_CALL PFNGLCOMPILESHADERPROC glad_glCompileShader; +#define glCompileShader glad_glCompileShader +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; +#define glCompressedTexImage2D glad_glCompressedTexImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DOESPROC glad_glCompressedTexImage3DOES; +#define glCompressedTexImage3DOES glad_glCompressedTexImage3DOES +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; +#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC glad_glCompressedTexSubImage3DOES; +#define glCompressedTexSubImage3DOES glad_glCompressedTexSubImage3DOES +GLAD_API_CALL PFNGLCOPYIMAGESUBDATAEXTPROC glad_glCopyImageSubDataEXT; +#define glCopyImageSubDataEXT glad_glCopyImageSubDataEXT +GLAD_API_CALL PFNGLCOPYIMAGESUBDATAOESPROC glad_glCopyImageSubDataOES; +#define glCopyImageSubDataOES glad_glCopyImageSubDataOES +GLAD_API_CALL PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; +#define glCopyTexImage2D glad_glCopyTexImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; +#define glCopyTexSubImage2D glad_glCopyTexSubImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE3DOESPROC glad_glCopyTexSubImage3DOES; +#define glCopyTexSubImage3DOES glad_glCopyTexSubImage3DOES +GLAD_API_CALL PFNGLCREATEMEMORYOBJECTSEXTPROC glad_glCreateMemoryObjectsEXT; +#define glCreateMemoryObjectsEXT glad_glCreateMemoryObjectsEXT +GLAD_API_CALL PFNGLCREATEPROGRAMPROC glad_glCreateProgram; +#define glCreateProgram glad_glCreateProgram +GLAD_API_CALL PFNGLCREATESHADERPROC glad_glCreateShader; +#define glCreateShader glad_glCreateShader +GLAD_API_CALL PFNGLCREATESHADERPROGRAMVEXTPROC glad_glCreateShaderProgramvEXT; +#define glCreateShaderProgramvEXT glad_glCreateShaderProgramvEXT +GLAD_API_CALL PFNGLCULLFACEPROC glad_glCullFace; +#define glCullFace glad_glCullFace +GLAD_API_CALL PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR; +#define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR +GLAD_API_CALL PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR; +#define glDebugMessageControlKHR glad_glDebugMessageControlKHR +GLAD_API_CALL PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR; +#define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR +GLAD_API_CALL PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; +#define glDeleteBuffers glad_glDeleteBuffers +GLAD_API_CALL PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; +#define glDeleteFramebuffers glad_glDeleteFramebuffers +GLAD_API_CALL PFNGLDELETEMEMORYOBJECTSEXTPROC glad_glDeleteMemoryObjectsEXT; +#define glDeleteMemoryObjectsEXT glad_glDeleteMemoryObjectsEXT +GLAD_API_CALL PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; +#define glDeleteProgram glad_glDeleteProgram +GLAD_API_CALL PFNGLDELETEPROGRAMPIPELINESEXTPROC glad_glDeleteProgramPipelinesEXT; +#define glDeleteProgramPipelinesEXT glad_glDeleteProgramPipelinesEXT +GLAD_API_CALL PFNGLDELETEQUERIESEXTPROC glad_glDeleteQueriesEXT; +#define glDeleteQueriesEXT glad_glDeleteQueriesEXT +GLAD_API_CALL PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; +#define glDeleteRenderbuffers glad_glDeleteRenderbuffers +GLAD_API_CALL PFNGLDELETESEMAPHORESEXTPROC glad_glDeleteSemaphoresEXT; +#define glDeleteSemaphoresEXT glad_glDeleteSemaphoresEXT +GLAD_API_CALL PFNGLDELETESHADERPROC glad_glDeleteShader; +#define glDeleteShader glad_glDeleteShader +GLAD_API_CALL PFNGLDELETETEXTURESPROC glad_glDeleteTextures; +#define glDeleteTextures glad_glDeleteTextures +GLAD_API_CALL PFNGLDELETEVERTEXARRAYSOESPROC glad_glDeleteVertexArraysOES; +#define glDeleteVertexArraysOES glad_glDeleteVertexArraysOES +GLAD_API_CALL PFNGLDEPTHFUNCPROC glad_glDepthFunc; +#define glDepthFunc glad_glDepthFunc +GLAD_API_CALL PFNGLDEPTHMASKPROC glad_glDepthMask; +#define glDepthMask glad_glDepthMask +GLAD_API_CALL PFNGLDEPTHRANGEARRAYFVOESPROC glad_glDepthRangeArrayfvOES; +#define glDepthRangeArrayfvOES glad_glDepthRangeArrayfvOES +GLAD_API_CALL PFNGLDEPTHRANGEINDEXEDFOESPROC glad_glDepthRangeIndexedfOES; +#define glDepthRangeIndexedfOES glad_glDepthRangeIndexedfOES +GLAD_API_CALL PFNGLDEPTHRANGEFPROC glad_glDepthRangef; +#define glDepthRangef glad_glDepthRangef +GLAD_API_CALL PFNGLDETACHSHADERPROC glad_glDetachShader; +#define glDetachShader glad_glDetachShader +GLAD_API_CALL PFNGLDISABLEPROC glad_glDisable; +#define glDisable glad_glDisable +GLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; +#define glDisableVertexAttribArray glad_glDisableVertexAttribArray +GLAD_API_CALL PFNGLDISABLEIEXTPROC glad_glDisableiEXT; +#define glDisableiEXT glad_glDisableiEXT +GLAD_API_CALL PFNGLDISABLEIOESPROC glad_glDisableiOES; +#define glDisableiOES glad_glDisableiOES +GLAD_API_CALL PFNGLDISCARDFRAMEBUFFEREXTPROC glad_glDiscardFramebufferEXT; +#define glDiscardFramebufferEXT glad_glDiscardFramebufferEXT +GLAD_API_CALL PFNGLDRAWARRAYSPROC glad_glDrawArrays; +#define glDrawArrays glad_glDrawArrays +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC glad_glDrawArraysInstancedBaseInstanceEXT; +#define glDrawArraysInstancedBaseInstanceEXT glad_glDrawArraysInstancedBaseInstanceEXT +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT; +#define glDrawArraysInstancedEXT glad_glDrawArraysInstancedEXT +GLAD_API_CALL PFNGLDRAWBUFFERSEXTPROC glad_glDrawBuffersEXT; +#define glDrawBuffersEXT glad_glDrawBuffersEXT +GLAD_API_CALL PFNGLDRAWBUFFERSINDEXEDEXTPROC glad_glDrawBuffersIndexedEXT; +#define glDrawBuffersIndexedEXT glad_glDrawBuffersIndexedEXT +GLAD_API_CALL PFNGLDRAWELEMENTSPROC glad_glDrawElements; +#define glDrawElements glad_glDrawElements +GLAD_API_CALL PFNGLDRAWELEMENTSBASEVERTEXEXTPROC glad_glDrawElementsBaseVertexEXT; +#define glDrawElementsBaseVertexEXT glad_glDrawElementsBaseVertexEXT +GLAD_API_CALL PFNGLDRAWELEMENTSBASEVERTEXOESPROC glad_glDrawElementsBaseVertexOES; +#define glDrawElementsBaseVertexOES glad_glDrawElementsBaseVertexOES +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC glad_glDrawElementsInstancedBaseInstanceEXT; +#define glDrawElementsInstancedBaseInstanceEXT glad_glDrawElementsInstancedBaseInstanceEXT +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC glad_glDrawElementsInstancedBaseVertexBaseInstanceEXT; +#define glDrawElementsInstancedBaseVertexBaseInstanceEXT glad_glDrawElementsInstancedBaseVertexBaseInstanceEXT +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC glad_glDrawElementsInstancedBaseVertexEXT; +#define glDrawElementsInstancedBaseVertexEXT glad_glDrawElementsInstancedBaseVertexEXT +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC glad_glDrawElementsInstancedBaseVertexOES; +#define glDrawElementsInstancedBaseVertexOES glad_glDrawElementsInstancedBaseVertexOES +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT; +#define glDrawElementsInstancedEXT glad_glDrawElementsInstancedEXT +GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC glad_glDrawRangeElementsBaseVertexEXT; +#define glDrawRangeElementsBaseVertexEXT glad_glDrawRangeElementsBaseVertexEXT +GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC glad_glDrawRangeElementsBaseVertexOES; +#define glDrawRangeElementsBaseVertexOES glad_glDrawRangeElementsBaseVertexOES +GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKEXTPROC glad_glDrawTransformFeedbackEXT; +#define glDrawTransformFeedbackEXT glad_glDrawTransformFeedbackEXT +GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDEXTPROC glad_glDrawTransformFeedbackInstancedEXT; +#define glDrawTransformFeedbackInstancedEXT glad_glDrawTransformFeedbackInstancedEXT +GLAD_API_CALL PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glad_glEGLImageTargetRenderbufferStorageOES; +#define glEGLImageTargetRenderbufferStorageOES glad_glEGLImageTargetRenderbufferStorageOES +GLAD_API_CALL PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glad_glEGLImageTargetTexStorageEXT; +#define glEGLImageTargetTexStorageEXT glad_glEGLImageTargetTexStorageEXT +GLAD_API_CALL PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glad_glEGLImageTargetTexture2DOES; +#define glEGLImageTargetTexture2DOES glad_glEGLImageTargetTexture2DOES +GLAD_API_CALL PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC glad_glEGLImageTargetTextureStorageEXT; +#define glEGLImageTargetTextureStorageEXT glad_glEGLImageTargetTextureStorageEXT +GLAD_API_CALL PFNGLENABLEPROC glad_glEnable; +#define glEnable glad_glEnable +GLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; +#define glEnableVertexAttribArray glad_glEnableVertexAttribArray +GLAD_API_CALL PFNGLENABLEIEXTPROC glad_glEnableiEXT; +#define glEnableiEXT glad_glEnableiEXT +GLAD_API_CALL PFNGLENABLEIOESPROC glad_glEnableiOES; +#define glEnableiOES glad_glEnableiOES +GLAD_API_CALL PFNGLENDQUERYEXTPROC glad_glEndQueryEXT; +#define glEndQueryEXT glad_glEndQueryEXT +GLAD_API_CALL PFNGLFINISHPROC glad_glFinish; +#define glFinish glad_glFinish +GLAD_API_CALL PFNGLFLUSHPROC glad_glFlush; +#define glFlush glad_glFlush +GLAD_API_CALL PFNGLFLUSHMAPPEDBUFFERRANGEEXTPROC glad_glFlushMappedBufferRangeEXT; +#define glFlushMappedBufferRangeEXT glad_glFlushMappedBufferRangeEXT +GLAD_API_CALL PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC glad_glFramebufferFetchBarrierEXT; +#define glFramebufferFetchBarrierEXT glad_glFramebufferFetchBarrierEXT +GLAD_API_CALL PFNGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC glad_glFramebufferPixelLocalStorageSizeEXT; +#define glFramebufferPixelLocalStorageSizeEXT glad_glFramebufferPixelLocalStorageSizeEXT +GLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; +#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer +GLAD_API_CALL PFNGLFRAMEBUFFERSHADINGRATEEXTPROC glad_glFramebufferShadingRateEXT; +#define glFramebufferShadingRateEXT glad_glFramebufferShadingRateEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; +#define glFramebufferTexture2D glad_glFramebufferTexture2D +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glad_glFramebufferTexture2DMultisampleEXT; +#define glFramebufferTexture2DMultisampleEXT glad_glFramebufferTexture2DMultisampleEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE3DOESPROC glad_glFramebufferTexture3DOES; +#define glFramebufferTexture3DOES glad_glFramebufferTexture3DOES +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREEXTPROC glad_glFramebufferTextureEXT; +#define glFramebufferTextureEXT glad_glFramebufferTextureEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREOESPROC glad_glFramebufferTextureOES; +#define glFramebufferTextureOES glad_glFramebufferTextureOES +GLAD_API_CALL PFNGLFRONTFACEPROC glad_glFrontFace; +#define glFrontFace glad_glFrontFace +GLAD_API_CALL PFNGLGENBUFFERSPROC glad_glGenBuffers; +#define glGenBuffers glad_glGenBuffers +GLAD_API_CALL PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; +#define glGenFramebuffers glad_glGenFramebuffers +GLAD_API_CALL PFNGLGENPROGRAMPIPELINESEXTPROC glad_glGenProgramPipelinesEXT; +#define glGenProgramPipelinesEXT glad_glGenProgramPipelinesEXT +GLAD_API_CALL PFNGLGENQUERIESEXTPROC glad_glGenQueriesEXT; +#define glGenQueriesEXT glad_glGenQueriesEXT +GLAD_API_CALL PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; +#define glGenRenderbuffers glad_glGenRenderbuffers +GLAD_API_CALL PFNGLGENSEMAPHORESEXTPROC glad_glGenSemaphoresEXT; +#define glGenSemaphoresEXT glad_glGenSemaphoresEXT +GLAD_API_CALL PFNGLGENTEXTURESPROC glad_glGenTextures; +#define glGenTextures glad_glGenTextures +GLAD_API_CALL PFNGLGENVERTEXARRAYSOESPROC glad_glGenVertexArraysOES; +#define glGenVertexArraysOES glad_glGenVertexArraysOES +GLAD_API_CALL PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; +#define glGenerateMipmap glad_glGenerateMipmap +GLAD_API_CALL PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; +#define glGetActiveAttrib glad_glGetActiveAttrib +GLAD_API_CALL PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; +#define glGetActiveUniform glad_glGetActiveUniform +GLAD_API_CALL PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; +#define glGetAttachedShaders glad_glGetAttachedShaders +GLAD_API_CALL PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; +#define glGetAttribLocation glad_glGetAttribLocation +GLAD_API_CALL PFNGLGETBOOLEANVPROC glad_glGetBooleanv; +#define glGetBooleanv glad_glGetBooleanv +GLAD_API_CALL PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; +#define glGetBufferParameteriv glad_glGetBufferParameteriv +GLAD_API_CALL PFNGLGETBUFFERPOINTERVOESPROC glad_glGetBufferPointervOES; +#define glGetBufferPointervOES glad_glGetBufferPointervOES +GLAD_API_CALL PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR; +#define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR +GLAD_API_CALL PFNGLGETERRORPROC glad_glGetError; +#define glGetError glad_glGetError +GLAD_API_CALL PFNGLGETFLOATI_VOESPROC glad_glGetFloati_vOES; +#define glGetFloati_vOES glad_glGetFloati_vOES +GLAD_API_CALL PFNGLGETFLOATVPROC glad_glGetFloatv; +#define glGetFloatv glad_glGetFloatv +GLAD_API_CALL PFNGLGETFRAGDATAINDEXEXTPROC glad_glGetFragDataIndexEXT; +#define glGetFragDataIndexEXT glad_glGetFragDataIndexEXT +GLAD_API_CALL PFNGLGETFRAGMENTSHADINGRATESEXTPROC glad_glGetFragmentShadingRatesEXT; +#define glGetFragmentShadingRatesEXT glad_glGetFragmentShadingRatesEXT +GLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; +#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv +GLAD_API_CALL PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC glad_glGetFramebufferPixelLocalStorageSizeEXT; +#define glGetFramebufferPixelLocalStorageSizeEXT glad_glGetFramebufferPixelLocalStorageSizeEXT +GLAD_API_CALL PFNGLGETGRAPHICSRESETSTATUSEXTPROC glad_glGetGraphicsResetStatusEXT; +#define glGetGraphicsResetStatusEXT glad_glGetGraphicsResetStatusEXT +GLAD_API_CALL PFNGLGETGRAPHICSRESETSTATUSKHRPROC glad_glGetGraphicsResetStatusKHR; +#define glGetGraphicsResetStatusKHR glad_glGetGraphicsResetStatusKHR +GLAD_API_CALL PFNGLGETINTEGER64VEXTPROC glad_glGetInteger64vEXT; +#define glGetInteger64vEXT glad_glGetInteger64vEXT +GLAD_API_CALL PFNGLGETINTEGERI_VEXTPROC glad_glGetIntegeri_vEXT; +#define glGetIntegeri_vEXT glad_glGetIntegeri_vEXT +GLAD_API_CALL PFNGLGETINTEGERVPROC glad_glGetIntegerv; +#define glGetIntegerv glad_glGetIntegerv +GLAD_API_CALL PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC glad_glGetMemoryObjectParameterivEXT; +#define glGetMemoryObjectParameterivEXT glad_glGetMemoryObjectParameterivEXT +GLAD_API_CALL PFNGLGETOBJECTLABELEXTPROC glad_glGetObjectLabelEXT; +#define glGetObjectLabelEXT glad_glGetObjectLabelEXT +GLAD_API_CALL PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR; +#define glGetObjectLabelKHR glad_glGetObjectLabelKHR +GLAD_API_CALL PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR; +#define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR +GLAD_API_CALL PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR; +#define glGetPointervKHR glad_glGetPointervKHR +GLAD_API_CALL PFNGLGETPROGRAMBINARYOESPROC glad_glGetProgramBinaryOES; +#define glGetProgramBinaryOES glad_glGetProgramBinaryOES +GLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; +#define glGetProgramInfoLog glad_glGetProgramInfoLog +GLAD_API_CALL PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC glad_glGetProgramPipelineInfoLogEXT; +#define glGetProgramPipelineInfoLogEXT glad_glGetProgramPipelineInfoLogEXT +GLAD_API_CALL PFNGLGETPROGRAMPIPELINEIVEXTPROC glad_glGetProgramPipelineivEXT; +#define glGetProgramPipelineivEXT glad_glGetProgramPipelineivEXT +GLAD_API_CALL PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC glad_glGetProgramResourceLocationIndexEXT; +#define glGetProgramResourceLocationIndexEXT glad_glGetProgramResourceLocationIndexEXT +GLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; +#define glGetProgramiv glad_glGetProgramiv +GLAD_API_CALL PFNGLGETQUERYOBJECTI64VEXTPROC glad_glGetQueryObjecti64vEXT; +#define glGetQueryObjecti64vEXT glad_glGetQueryObjecti64vEXT +GLAD_API_CALL PFNGLGETQUERYOBJECTIVEXTPROC glad_glGetQueryObjectivEXT; +#define glGetQueryObjectivEXT glad_glGetQueryObjectivEXT +GLAD_API_CALL PFNGLGETQUERYOBJECTUI64VEXTPROC glad_glGetQueryObjectui64vEXT; +#define glGetQueryObjectui64vEXT glad_glGetQueryObjectui64vEXT +GLAD_API_CALL PFNGLGETQUERYOBJECTUIVEXTPROC glad_glGetQueryObjectuivEXT; +#define glGetQueryObjectuivEXT glad_glGetQueryObjectuivEXT +GLAD_API_CALL PFNGLGETQUERYIVEXTPROC glad_glGetQueryivEXT; +#define glGetQueryivEXT glad_glGetQueryivEXT +GLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; +#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIIVEXTPROC glad_glGetSamplerParameterIivEXT; +#define glGetSamplerParameterIivEXT glad_glGetSamplerParameterIivEXT +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIIVOESPROC glad_glGetSamplerParameterIivOES; +#define glGetSamplerParameterIivOES glad_glGetSamplerParameterIivOES +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIUIVEXTPROC glad_glGetSamplerParameterIuivEXT; +#define glGetSamplerParameterIuivEXT glad_glGetSamplerParameterIuivEXT +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIUIVOESPROC glad_glGetSamplerParameterIuivOES; +#define glGetSamplerParameterIuivOES glad_glGetSamplerParameterIuivOES +GLAD_API_CALL PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC glad_glGetSemaphoreParameterui64vEXT; +#define glGetSemaphoreParameterui64vEXT glad_glGetSemaphoreParameterui64vEXT +GLAD_API_CALL PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; +#define glGetShaderInfoLog glad_glGetShaderInfoLog +GLAD_API_CALL PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat; +#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat +GLAD_API_CALL PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; +#define glGetShaderSource glad_glGetShaderSource +GLAD_API_CALL PFNGLGETSHADERIVPROC glad_glGetShaderiv; +#define glGetShaderiv glad_glGetShaderiv +GLAD_API_CALL PFNGLGETSTRINGPROC glad_glGetString; +#define glGetString glad_glGetString +GLAD_API_CALL PFNGLGETTEXPARAMETERIIVEXTPROC glad_glGetTexParameterIivEXT; +#define glGetTexParameterIivEXT glad_glGetTexParameterIivEXT +GLAD_API_CALL PFNGLGETTEXPARAMETERIIVOESPROC glad_glGetTexParameterIivOES; +#define glGetTexParameterIivOES glad_glGetTexParameterIivOES +GLAD_API_CALL PFNGLGETTEXPARAMETERIUIVEXTPROC glad_glGetTexParameterIuivEXT; +#define glGetTexParameterIuivEXT glad_glGetTexParameterIuivEXT +GLAD_API_CALL PFNGLGETTEXPARAMETERIUIVOESPROC glad_glGetTexParameterIuivOES; +#define glGetTexParameterIuivOES glad_glGetTexParameterIuivOES +GLAD_API_CALL PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; +#define glGetTexParameterfv glad_glGetTexParameterfv +GLAD_API_CALL PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; +#define glGetTexParameteriv glad_glGetTexParameteriv +GLAD_API_CALL PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; +#define glGetUniformLocation glad_glGetUniformLocation +GLAD_API_CALL PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; +#define glGetUniformfv glad_glGetUniformfv +GLAD_API_CALL PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; +#define glGetUniformiv glad_glGetUniformiv +GLAD_API_CALL PFNGLGETUNSIGNEDBYTEI_VEXTPROC glad_glGetUnsignedBytei_vEXT; +#define glGetUnsignedBytei_vEXT glad_glGetUnsignedBytei_vEXT +GLAD_API_CALL PFNGLGETUNSIGNEDBYTEVEXTPROC glad_glGetUnsignedBytevEXT; +#define glGetUnsignedBytevEXT glad_glGetUnsignedBytevEXT +GLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; +#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv +GLAD_API_CALL PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; +#define glGetVertexAttribfv glad_glGetVertexAttribfv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; +#define glGetVertexAttribiv glad_glGetVertexAttribiv +GLAD_API_CALL PFNGLGETNUNIFORMFVEXTPROC glad_glGetnUniformfvEXT; +#define glGetnUniformfvEXT glad_glGetnUniformfvEXT +GLAD_API_CALL PFNGLGETNUNIFORMFVKHRPROC glad_glGetnUniformfvKHR; +#define glGetnUniformfvKHR glad_glGetnUniformfvKHR +GLAD_API_CALL PFNGLGETNUNIFORMIVEXTPROC glad_glGetnUniformivEXT; +#define glGetnUniformivEXT glad_glGetnUniformivEXT +GLAD_API_CALL PFNGLGETNUNIFORMIVKHRPROC glad_glGetnUniformivKHR; +#define glGetnUniformivKHR glad_glGetnUniformivKHR +GLAD_API_CALL PFNGLGETNUNIFORMUIVKHRPROC glad_glGetnUniformuivKHR; +#define glGetnUniformuivKHR glad_glGetnUniformuivKHR +GLAD_API_CALL PFNGLHINTPROC glad_glHint; +#define glHint glad_glHint +GLAD_API_CALL PFNGLIMPORTMEMORYFDEXTPROC glad_glImportMemoryFdEXT; +#define glImportMemoryFdEXT glad_glImportMemoryFdEXT +GLAD_API_CALL PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC glad_glImportMemoryWin32HandleEXT; +#define glImportMemoryWin32HandleEXT glad_glImportMemoryWin32HandleEXT +GLAD_API_CALL PFNGLIMPORTMEMORYWIN32NAMEEXTPROC glad_glImportMemoryWin32NameEXT; +#define glImportMemoryWin32NameEXT glad_glImportMemoryWin32NameEXT +GLAD_API_CALL PFNGLIMPORTSEMAPHOREFDEXTPROC glad_glImportSemaphoreFdEXT; +#define glImportSemaphoreFdEXT glad_glImportSemaphoreFdEXT +GLAD_API_CALL PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC glad_glImportSemaphoreWin32HandleEXT; +#define glImportSemaphoreWin32HandleEXT glad_glImportSemaphoreWin32HandleEXT +GLAD_API_CALL PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC glad_glImportSemaphoreWin32NameEXT; +#define glImportSemaphoreWin32NameEXT glad_glImportSemaphoreWin32NameEXT +GLAD_API_CALL PFNGLINSERTEVENTMARKEREXTPROC glad_glInsertEventMarkerEXT; +#define glInsertEventMarkerEXT glad_glInsertEventMarkerEXT +GLAD_API_CALL PFNGLISBUFFERPROC glad_glIsBuffer; +#define glIsBuffer glad_glIsBuffer +GLAD_API_CALL PFNGLISENABLEDPROC glad_glIsEnabled; +#define glIsEnabled glad_glIsEnabled +GLAD_API_CALL PFNGLISENABLEDIEXTPROC glad_glIsEnablediEXT; +#define glIsEnablediEXT glad_glIsEnablediEXT +GLAD_API_CALL PFNGLISENABLEDIOESPROC glad_glIsEnablediOES; +#define glIsEnablediOES glad_glIsEnablediOES +GLAD_API_CALL PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; +#define glIsFramebuffer glad_glIsFramebuffer +GLAD_API_CALL PFNGLISMEMORYOBJECTEXTPROC glad_glIsMemoryObjectEXT; +#define glIsMemoryObjectEXT glad_glIsMemoryObjectEXT +GLAD_API_CALL PFNGLISPROGRAMPROC glad_glIsProgram; +#define glIsProgram glad_glIsProgram +GLAD_API_CALL PFNGLISPROGRAMPIPELINEEXTPROC glad_glIsProgramPipelineEXT; +#define glIsProgramPipelineEXT glad_glIsProgramPipelineEXT +GLAD_API_CALL PFNGLISQUERYEXTPROC glad_glIsQueryEXT; +#define glIsQueryEXT glad_glIsQueryEXT +GLAD_API_CALL PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; +#define glIsRenderbuffer glad_glIsRenderbuffer +GLAD_API_CALL PFNGLISSEMAPHOREEXTPROC glad_glIsSemaphoreEXT; +#define glIsSemaphoreEXT glad_glIsSemaphoreEXT +GLAD_API_CALL PFNGLISSHADERPROC glad_glIsShader; +#define glIsShader glad_glIsShader +GLAD_API_CALL PFNGLISTEXTUREPROC glad_glIsTexture; +#define glIsTexture glad_glIsTexture +GLAD_API_CALL PFNGLISVERTEXARRAYOESPROC glad_glIsVertexArrayOES; +#define glIsVertexArrayOES glad_glIsVertexArrayOES +GLAD_API_CALL PFNGLLABELOBJECTEXTPROC glad_glLabelObjectEXT; +#define glLabelObjectEXT glad_glLabelObjectEXT +GLAD_API_CALL PFNGLLINEWIDTHPROC glad_glLineWidth; +#define glLineWidth glad_glLineWidth +GLAD_API_CALL PFNGLLINKPROGRAMPROC glad_glLinkProgram; +#define glLinkProgram glad_glLinkProgram +GLAD_API_CALL PFNGLMAPBUFFEROESPROC glad_glMapBufferOES; +#define glMapBufferOES glad_glMapBufferOES +GLAD_API_CALL PFNGLMAPBUFFERRANGEEXTPROC glad_glMapBufferRangeEXT; +#define glMapBufferRangeEXT glad_glMapBufferRangeEXT +GLAD_API_CALL PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glad_glMaxShaderCompilerThreadsKHR; +#define glMaxShaderCompilerThreadsKHR glad_glMaxShaderCompilerThreadsKHR +GLAD_API_CALL PFNGLMEMORYOBJECTPARAMETERIVEXTPROC glad_glMemoryObjectParameterivEXT; +#define glMemoryObjectParameterivEXT glad_glMemoryObjectParameterivEXT +GLAD_API_CALL PFNGLMINSAMPLESHADINGOESPROC glad_glMinSampleShadingOES; +#define glMinSampleShadingOES glad_glMinSampleShadingOES +GLAD_API_CALL PFNGLMULTIDRAWARRAYSEXTPROC glad_glMultiDrawArraysEXT; +#define glMultiDrawArraysEXT glad_glMultiDrawArraysEXT +GLAD_API_CALL PFNGLMULTIDRAWARRAYSINDIRECTEXTPROC glad_glMultiDrawArraysIndirectEXT; +#define glMultiDrawArraysIndirectEXT glad_glMultiDrawArraysIndirectEXT +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC glad_glMultiDrawElementsBaseVertexEXT; +#define glMultiDrawElementsBaseVertexEXT glad_glMultiDrawElementsBaseVertexEXT +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSEXTPROC glad_glMultiDrawElementsEXT; +#define glMultiDrawElementsEXT glad_glMultiDrawElementsEXT +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSINDIRECTEXTPROC glad_glMultiDrawElementsIndirectEXT; +#define glMultiDrawElementsIndirectEXT glad_glMultiDrawElementsIndirectEXT +GLAD_API_CALL PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC glad_glNamedBufferStorageExternalEXT; +#define glNamedBufferStorageExternalEXT glad_glNamedBufferStorageExternalEXT +GLAD_API_CALL PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC glad_glNamedBufferStorageMemEXT; +#define glNamedBufferStorageMemEXT glad_glNamedBufferStorageMemEXT +GLAD_API_CALL PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR; +#define glObjectLabelKHR glad_glObjectLabelKHR +GLAD_API_CALL PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR; +#define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR +GLAD_API_CALL PFNGLPATCHPARAMETERIEXTPROC glad_glPatchParameteriEXT; +#define glPatchParameteriEXT glad_glPatchParameteriEXT +GLAD_API_CALL PFNGLPATCHPARAMETERIOESPROC glad_glPatchParameteriOES; +#define glPatchParameteriOES glad_glPatchParameteriOES +GLAD_API_CALL PFNGLPIXELSTOREIPROC glad_glPixelStorei; +#define glPixelStorei glad_glPixelStorei +GLAD_API_CALL PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; +#define glPolygonOffset glad_glPolygonOffset +GLAD_API_CALL PFNGLPOLYGONOFFSETCLAMPEXTPROC glad_glPolygonOffsetClampEXT; +#define glPolygonOffsetClampEXT glad_glPolygonOffsetClampEXT +GLAD_API_CALL PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR; +#define glPopDebugGroupKHR glad_glPopDebugGroupKHR +GLAD_API_CALL PFNGLPOPGROUPMARKEREXTPROC glad_glPopGroupMarkerEXT; +#define glPopGroupMarkerEXT glad_glPopGroupMarkerEXT +GLAD_API_CALL PFNGLPRIMITIVEBOUNDINGBOXEXTPROC glad_glPrimitiveBoundingBoxEXT; +#define glPrimitiveBoundingBoxEXT glad_glPrimitiveBoundingBoxEXT +GLAD_API_CALL PFNGLPRIMITIVEBOUNDINGBOXOESPROC glad_glPrimitiveBoundingBoxOES; +#define glPrimitiveBoundingBoxOES glad_glPrimitiveBoundingBoxOES +GLAD_API_CALL PFNGLPROGRAMBINARYOESPROC glad_glProgramBinaryOES; +#define glProgramBinaryOES glad_glProgramBinaryOES +GLAD_API_CALL PFNGLPROGRAMPARAMETERIEXTPROC glad_glProgramParameteriEXT; +#define glProgramParameteriEXT glad_glProgramParameteriEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1FEXTPROC glad_glProgramUniform1fEXT; +#define glProgramUniform1fEXT glad_glProgramUniform1fEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1FVEXTPROC glad_glProgramUniform1fvEXT; +#define glProgramUniform1fvEXT glad_glProgramUniform1fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1IEXTPROC glad_glProgramUniform1iEXT; +#define glProgramUniform1iEXT glad_glProgramUniform1iEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1IVEXTPROC glad_glProgramUniform1ivEXT; +#define glProgramUniform1ivEXT glad_glProgramUniform1ivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1UIEXTPROC glad_glProgramUniform1uiEXT; +#define glProgramUniform1uiEXT glad_glProgramUniform1uiEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1UIVEXTPROC glad_glProgramUniform1uivEXT; +#define glProgramUniform1uivEXT glad_glProgramUniform1uivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2FEXTPROC glad_glProgramUniform2fEXT; +#define glProgramUniform2fEXT glad_glProgramUniform2fEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2FVEXTPROC glad_glProgramUniform2fvEXT; +#define glProgramUniform2fvEXT glad_glProgramUniform2fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2IEXTPROC glad_glProgramUniform2iEXT; +#define glProgramUniform2iEXT glad_glProgramUniform2iEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2IVEXTPROC glad_glProgramUniform2ivEXT; +#define glProgramUniform2ivEXT glad_glProgramUniform2ivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2UIEXTPROC glad_glProgramUniform2uiEXT; +#define glProgramUniform2uiEXT glad_glProgramUniform2uiEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2UIVEXTPROC glad_glProgramUniform2uivEXT; +#define glProgramUniform2uivEXT glad_glProgramUniform2uivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3FEXTPROC glad_glProgramUniform3fEXT; +#define glProgramUniform3fEXT glad_glProgramUniform3fEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3FVEXTPROC glad_glProgramUniform3fvEXT; +#define glProgramUniform3fvEXT glad_glProgramUniform3fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3IEXTPROC glad_glProgramUniform3iEXT; +#define glProgramUniform3iEXT glad_glProgramUniform3iEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3IVEXTPROC glad_glProgramUniform3ivEXT; +#define glProgramUniform3ivEXT glad_glProgramUniform3ivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3UIEXTPROC glad_glProgramUniform3uiEXT; +#define glProgramUniform3uiEXT glad_glProgramUniform3uiEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3UIVEXTPROC glad_glProgramUniform3uivEXT; +#define glProgramUniform3uivEXT glad_glProgramUniform3uivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4FEXTPROC glad_glProgramUniform4fEXT; +#define glProgramUniform4fEXT glad_glProgramUniform4fEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4FVEXTPROC glad_glProgramUniform4fvEXT; +#define glProgramUniform4fvEXT glad_glProgramUniform4fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4IEXTPROC glad_glProgramUniform4iEXT; +#define glProgramUniform4iEXT glad_glProgramUniform4iEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4IVEXTPROC glad_glProgramUniform4ivEXT; +#define glProgramUniform4ivEXT glad_glProgramUniform4ivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4UIEXTPROC glad_glProgramUniform4uiEXT; +#define glProgramUniform4uiEXT glad_glProgramUniform4uiEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4UIVEXTPROC glad_glProgramUniform4uivEXT; +#define glProgramUniform4uivEXT glad_glProgramUniform4uivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glad_glProgramUniformMatrix2fvEXT; +#define glProgramUniformMatrix2fvEXT glad_glProgramUniformMatrix2fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glad_glProgramUniformMatrix2x3fvEXT; +#define glProgramUniformMatrix2x3fvEXT glad_glProgramUniformMatrix2x3fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glad_glProgramUniformMatrix2x4fvEXT; +#define glProgramUniformMatrix2x4fvEXT glad_glProgramUniformMatrix2x4fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glad_glProgramUniformMatrix3fvEXT; +#define glProgramUniformMatrix3fvEXT glad_glProgramUniformMatrix3fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glad_glProgramUniformMatrix3x2fvEXT; +#define glProgramUniformMatrix3x2fvEXT glad_glProgramUniformMatrix3x2fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glad_glProgramUniformMatrix3x4fvEXT; +#define glProgramUniformMatrix3x4fvEXT glad_glProgramUniformMatrix3x4fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glad_glProgramUniformMatrix4fvEXT; +#define glProgramUniformMatrix4fvEXT glad_glProgramUniformMatrix4fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glad_glProgramUniformMatrix4x2fvEXT; +#define glProgramUniformMatrix4x2fvEXT glad_glProgramUniformMatrix4x2fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glad_glProgramUniformMatrix4x3fvEXT; +#define glProgramUniformMatrix4x3fvEXT glad_glProgramUniformMatrix4x3fvEXT +GLAD_API_CALL PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR; +#define glPushDebugGroupKHR glad_glPushDebugGroupKHR +GLAD_API_CALL PFNGLPUSHGROUPMARKEREXTPROC glad_glPushGroupMarkerEXT; +#define glPushGroupMarkerEXT glad_glPushGroupMarkerEXT +GLAD_API_CALL PFNGLQUERYCOUNTEREXTPROC glad_glQueryCounterEXT; +#define glQueryCounterEXT glad_glQueryCounterEXT +GLAD_API_CALL PFNGLRASTERSAMPLESEXTPROC glad_glRasterSamplesEXT; +#define glRasterSamplesEXT glad_glRasterSamplesEXT +GLAD_API_CALL PFNGLREADBUFFERINDEXEDEXTPROC glad_glReadBufferIndexedEXT; +#define glReadBufferIndexedEXT glad_glReadBufferIndexedEXT +GLAD_API_CALL PFNGLREADPIXELSPROC glad_glReadPixels; +#define glReadPixels glad_glReadPixels +GLAD_API_CALL PFNGLREADNPIXELSEXTPROC glad_glReadnPixelsEXT; +#define glReadnPixelsEXT glad_glReadnPixelsEXT +GLAD_API_CALL PFNGLREADNPIXELSKHRPROC glad_glReadnPixelsKHR; +#define glReadnPixelsKHR glad_glReadnPixelsKHR +GLAD_API_CALL PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC glad_glReleaseKeyedMutexWin32EXT; +#define glReleaseKeyedMutexWin32EXT glad_glReleaseKeyedMutexWin32EXT +GLAD_API_CALL PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler; +#define glReleaseShaderCompiler glad_glReleaseShaderCompiler +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; +#define glRenderbufferStorage glad_glRenderbufferStorage +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT; +#define glRenderbufferStorageMultisampleEXT glad_glRenderbufferStorageMultisampleEXT +GLAD_API_CALL PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; +#define glSampleCoverage glad_glSampleCoverage +GLAD_API_CALL PFNGLSAMPLERPARAMETERIIVEXTPROC glad_glSamplerParameterIivEXT; +#define glSamplerParameterIivEXT glad_glSamplerParameterIivEXT +GLAD_API_CALL PFNGLSAMPLERPARAMETERIIVOESPROC glad_glSamplerParameterIivOES; +#define glSamplerParameterIivOES glad_glSamplerParameterIivOES +GLAD_API_CALL PFNGLSAMPLERPARAMETERIUIVEXTPROC glad_glSamplerParameterIuivEXT; +#define glSamplerParameterIuivEXT glad_glSamplerParameterIuivEXT +GLAD_API_CALL PFNGLSAMPLERPARAMETERIUIVOESPROC glad_glSamplerParameterIuivOES; +#define glSamplerParameterIuivOES glad_glSamplerParameterIuivOES +GLAD_API_CALL PFNGLSCISSORPROC glad_glScissor; +#define glScissor glad_glScissor +GLAD_API_CALL PFNGLSCISSORARRAYVOESPROC glad_glScissorArrayvOES; +#define glScissorArrayvOES glad_glScissorArrayvOES +GLAD_API_CALL PFNGLSCISSORINDEXEDOESPROC glad_glScissorIndexedOES; +#define glScissorIndexedOES glad_glScissorIndexedOES +GLAD_API_CALL PFNGLSCISSORINDEXEDVOESPROC glad_glScissorIndexedvOES; +#define glScissorIndexedvOES glad_glScissorIndexedvOES +GLAD_API_CALL PFNGLSEMAPHOREPARAMETERUI64VEXTPROC glad_glSemaphoreParameterui64vEXT; +#define glSemaphoreParameterui64vEXT glad_glSemaphoreParameterui64vEXT +GLAD_API_CALL PFNGLSHADERBINARYPROC glad_glShaderBinary; +#define glShaderBinary glad_glShaderBinary +GLAD_API_CALL PFNGLSHADERSOURCEPROC glad_glShaderSource; +#define glShaderSource glad_glShaderSource +GLAD_API_CALL PFNGLSHADINGRATECOMBINEROPSEXTPROC glad_glShadingRateCombinerOpsEXT; +#define glShadingRateCombinerOpsEXT glad_glShadingRateCombinerOpsEXT +GLAD_API_CALL PFNGLSHADINGRATEEXTPROC glad_glShadingRateEXT; +#define glShadingRateEXT glad_glShadingRateEXT +GLAD_API_CALL PFNGLSIGNALSEMAPHOREEXTPROC glad_glSignalSemaphoreEXT; +#define glSignalSemaphoreEXT glad_glSignalSemaphoreEXT +GLAD_API_CALL PFNGLSTENCILFUNCPROC glad_glStencilFunc; +#define glStencilFunc glad_glStencilFunc +GLAD_API_CALL PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; +#define glStencilFuncSeparate glad_glStencilFuncSeparate +GLAD_API_CALL PFNGLSTENCILMASKPROC glad_glStencilMask; +#define glStencilMask glad_glStencilMask +GLAD_API_CALL PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; +#define glStencilMaskSeparate glad_glStencilMaskSeparate +GLAD_API_CALL PFNGLSTENCILOPPROC glad_glStencilOp; +#define glStencilOp glad_glStencilOp +GLAD_API_CALL PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; +#define glStencilOpSeparate glad_glStencilOpSeparate +GLAD_API_CALL PFNGLTEXBUFFEREXTPROC glad_glTexBufferEXT; +#define glTexBufferEXT glad_glTexBufferEXT +GLAD_API_CALL PFNGLTEXBUFFEROESPROC glad_glTexBufferOES; +#define glTexBufferOES glad_glTexBufferOES +GLAD_API_CALL PFNGLTEXBUFFERRANGEEXTPROC glad_glTexBufferRangeEXT; +#define glTexBufferRangeEXT glad_glTexBufferRangeEXT +GLAD_API_CALL PFNGLTEXBUFFERRANGEOESPROC glad_glTexBufferRangeOES; +#define glTexBufferRangeOES glad_glTexBufferRangeOES +GLAD_API_CALL PFNGLTEXIMAGE2DPROC glad_glTexImage2D; +#define glTexImage2D glad_glTexImage2D +GLAD_API_CALL PFNGLTEXIMAGE3DOESPROC glad_glTexImage3DOES; +#define glTexImage3DOES glad_glTexImage3DOES +GLAD_API_CALL PFNGLTEXPAGECOMMITMENTEXTPROC glad_glTexPageCommitmentEXT; +#define glTexPageCommitmentEXT glad_glTexPageCommitmentEXT +GLAD_API_CALL PFNGLTEXPARAMETERIIVEXTPROC glad_glTexParameterIivEXT; +#define glTexParameterIivEXT glad_glTexParameterIivEXT +GLAD_API_CALL PFNGLTEXPARAMETERIIVOESPROC glad_glTexParameterIivOES; +#define glTexParameterIivOES glad_glTexParameterIivOES +GLAD_API_CALL PFNGLTEXPARAMETERIUIVEXTPROC glad_glTexParameterIuivEXT; +#define glTexParameterIuivEXT glad_glTexParameterIuivEXT +GLAD_API_CALL PFNGLTEXPARAMETERIUIVOESPROC glad_glTexParameterIuivOES; +#define glTexParameterIuivOES glad_glTexParameterIuivOES +GLAD_API_CALL PFNGLTEXPARAMETERFPROC glad_glTexParameterf; +#define glTexParameterf glad_glTexParameterf +GLAD_API_CALL PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; +#define glTexParameterfv glad_glTexParameterfv +GLAD_API_CALL PFNGLTEXPARAMETERIPROC glad_glTexParameteri; +#define glTexParameteri glad_glTexParameteri +GLAD_API_CALL PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; +#define glTexParameteriv glad_glTexParameteriv +GLAD_API_CALL PFNGLTEXSTORAGE1DEXTPROC glad_glTexStorage1DEXT; +#define glTexStorage1DEXT glad_glTexStorage1DEXT +GLAD_API_CALL PFNGLTEXSTORAGE2DEXTPROC glad_glTexStorage2DEXT; +#define glTexStorage2DEXT glad_glTexStorage2DEXT +GLAD_API_CALL PFNGLTEXSTORAGE3DEXTPROC glad_glTexStorage3DEXT; +#define glTexStorage3DEXT glad_glTexStorage3DEXT +GLAD_API_CALL PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC glad_glTexStorage3DMultisampleOES; +#define glTexStorage3DMultisampleOES glad_glTexStorage3DMultisampleOES +GLAD_API_CALL PFNGLTEXSTORAGEATTRIBS2DEXTPROC glad_glTexStorageAttribs2DEXT; +#define glTexStorageAttribs2DEXT glad_glTexStorageAttribs2DEXT +GLAD_API_CALL PFNGLTEXSTORAGEATTRIBS3DEXTPROC glad_glTexStorageAttribs3DEXT; +#define glTexStorageAttribs3DEXT glad_glTexStorageAttribs3DEXT +GLAD_API_CALL PFNGLTEXSTORAGEMEM2DEXTPROC glad_glTexStorageMem2DEXT; +#define glTexStorageMem2DEXT glad_glTexStorageMem2DEXT +GLAD_API_CALL PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC glad_glTexStorageMem2DMultisampleEXT; +#define glTexStorageMem2DMultisampleEXT glad_glTexStorageMem2DMultisampleEXT +GLAD_API_CALL PFNGLTEXSTORAGEMEM3DEXTPROC glad_glTexStorageMem3DEXT; +#define glTexStorageMem3DEXT glad_glTexStorageMem3DEXT +GLAD_API_CALL PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC glad_glTexStorageMem3DMultisampleEXT; +#define glTexStorageMem3DMultisampleEXT glad_glTexStorageMem3DMultisampleEXT +GLAD_API_CALL PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; +#define glTexSubImage2D glad_glTexSubImage2D +GLAD_API_CALL PFNGLTEXSUBIMAGE3DOESPROC glad_glTexSubImage3DOES; +#define glTexSubImage3DOES glad_glTexSubImage3DOES +GLAD_API_CALL PFNGLTEXTURESTORAGE1DEXTPROC glad_glTextureStorage1DEXT; +#define glTextureStorage1DEXT glad_glTextureStorage1DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGE2DEXTPROC glad_glTextureStorage2DEXT; +#define glTextureStorage2DEXT glad_glTextureStorage2DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGE3DEXTPROC glad_glTextureStorage3DEXT; +#define glTextureStorage3DEXT glad_glTextureStorage3DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGEMEM2DEXTPROC glad_glTextureStorageMem2DEXT; +#define glTextureStorageMem2DEXT glad_glTextureStorageMem2DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC glad_glTextureStorageMem2DMultisampleEXT; +#define glTextureStorageMem2DMultisampleEXT glad_glTextureStorageMem2DMultisampleEXT +GLAD_API_CALL PFNGLTEXTURESTORAGEMEM3DEXTPROC glad_glTextureStorageMem3DEXT; +#define glTextureStorageMem3DEXT glad_glTextureStorageMem3DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC glad_glTextureStorageMem3DMultisampleEXT; +#define glTextureStorageMem3DMultisampleEXT glad_glTextureStorageMem3DMultisampleEXT +GLAD_API_CALL PFNGLTEXTUREVIEWEXTPROC glad_glTextureViewEXT; +#define glTextureViewEXT glad_glTextureViewEXT +GLAD_API_CALL PFNGLTEXTUREVIEWOESPROC glad_glTextureViewOES; +#define glTextureViewOES glad_glTextureViewOES +GLAD_API_CALL PFNGLUNIFORM1FPROC glad_glUniform1f; +#define glUniform1f glad_glUniform1f +GLAD_API_CALL PFNGLUNIFORM1FVPROC glad_glUniform1fv; +#define glUniform1fv glad_glUniform1fv +GLAD_API_CALL PFNGLUNIFORM1IPROC glad_glUniform1i; +#define glUniform1i glad_glUniform1i +GLAD_API_CALL PFNGLUNIFORM1IVPROC glad_glUniform1iv; +#define glUniform1iv glad_glUniform1iv +GLAD_API_CALL PFNGLUNIFORM2FPROC glad_glUniform2f; +#define glUniform2f glad_glUniform2f +GLAD_API_CALL PFNGLUNIFORM2FVPROC glad_glUniform2fv; +#define glUniform2fv glad_glUniform2fv +GLAD_API_CALL PFNGLUNIFORM2IPROC glad_glUniform2i; +#define glUniform2i glad_glUniform2i +GLAD_API_CALL PFNGLUNIFORM2IVPROC glad_glUniform2iv; +#define glUniform2iv glad_glUniform2iv +GLAD_API_CALL PFNGLUNIFORM3FPROC glad_glUniform3f; +#define glUniform3f glad_glUniform3f +GLAD_API_CALL PFNGLUNIFORM3FVPROC glad_glUniform3fv; +#define glUniform3fv glad_glUniform3fv +GLAD_API_CALL PFNGLUNIFORM3IPROC glad_glUniform3i; +#define glUniform3i glad_glUniform3i +GLAD_API_CALL PFNGLUNIFORM3IVPROC glad_glUniform3iv; +#define glUniform3iv glad_glUniform3iv +GLAD_API_CALL PFNGLUNIFORM4FPROC glad_glUniform4f; +#define glUniform4f glad_glUniform4f +GLAD_API_CALL PFNGLUNIFORM4FVPROC glad_glUniform4fv; +#define glUniform4fv glad_glUniform4fv +GLAD_API_CALL PFNGLUNIFORM4IPROC glad_glUniform4i; +#define glUniform4i glad_glUniform4i +GLAD_API_CALL PFNGLUNIFORM4IVPROC glad_glUniform4iv; +#define glUniform4iv glad_glUniform4iv +GLAD_API_CALL PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; +#define glUniformMatrix2fv glad_glUniformMatrix2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; +#define glUniformMatrix3fv glad_glUniformMatrix3fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; +#define glUniformMatrix4fv glad_glUniformMatrix4fv +GLAD_API_CALL PFNGLUNMAPBUFFEROESPROC glad_glUnmapBufferOES; +#define glUnmapBufferOES glad_glUnmapBufferOES +GLAD_API_CALL PFNGLUSEPROGRAMPROC glad_glUseProgram; +#define glUseProgram glad_glUseProgram +GLAD_API_CALL PFNGLUSEPROGRAMSTAGESEXTPROC glad_glUseProgramStagesEXT; +#define glUseProgramStagesEXT glad_glUseProgramStagesEXT +GLAD_API_CALL PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; +#define glValidateProgram glad_glValidateProgram +GLAD_API_CALL PFNGLVALIDATEPROGRAMPIPELINEEXTPROC glad_glValidateProgramPipelineEXT; +#define glValidateProgramPipelineEXT glad_glValidateProgramPipelineEXT +GLAD_API_CALL PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; +#define glVertexAttrib1f glad_glVertexAttrib1f +GLAD_API_CALL PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; +#define glVertexAttrib1fv glad_glVertexAttrib1fv +GLAD_API_CALL PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; +#define glVertexAttrib2f glad_glVertexAttrib2f +GLAD_API_CALL PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; +#define glVertexAttrib2fv glad_glVertexAttrib2fv +GLAD_API_CALL PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; +#define glVertexAttrib3f glad_glVertexAttrib3f +GLAD_API_CALL PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; +#define glVertexAttrib3fv glad_glVertexAttrib3fv +GLAD_API_CALL PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; +#define glVertexAttrib4f glad_glVertexAttrib4f +GLAD_API_CALL PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; +#define glVertexAttrib4fv glad_glVertexAttrib4fv +GLAD_API_CALL PFNGLVERTEXATTRIBDIVISOREXTPROC glad_glVertexAttribDivisorEXT; +#define glVertexAttribDivisorEXT glad_glVertexAttribDivisorEXT +GLAD_API_CALL PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; +#define glVertexAttribPointer glad_glVertexAttribPointer +GLAD_API_CALL PFNGLVIEWPORTPROC glad_glViewport; +#define glViewport glad_glViewport +GLAD_API_CALL PFNGLVIEWPORTARRAYVOESPROC glad_glViewportArrayvOES; +#define glViewportArrayvOES glad_glViewportArrayvOES +GLAD_API_CALL PFNGLVIEWPORTINDEXEDFOESPROC glad_glViewportIndexedfOES; +#define glViewportIndexedfOES glad_glViewportIndexedfOES +GLAD_API_CALL PFNGLVIEWPORTINDEXEDFVOESPROC glad_glViewportIndexedfvOES; +#define glViewportIndexedfvOES glad_glViewportIndexedfvOES +GLAD_API_CALL PFNGLWAITSEMAPHOREEXTPROC glad_glWaitSemaphoreEXT; +#define glWaitSemaphoreEXT glad_glWaitSemaphoreEXT +GLAD_API_CALL PFNGLWINDOWRECTANGLESEXTPROC glad_glWindowRectanglesEXT; +#define glWindowRectanglesEXT glad_glWindowRectanglesEXT + + + + + +GLAD_API_CALL int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr); +GLAD_API_CALL int gladLoadGLES2( GLADloadfunc load); + + + +#ifdef __cplusplus +} +#endif +#endif + +/* Source */ +#ifdef GLAD_GLES2_IMPLEMENTATION +/** + * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 + */ +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_GL_ES_VERSION_2_0 = 0; +int GLAD_GL_EXT_EGL_image_array = 0; +int GLAD_GL_EXT_EGL_image_storage = 0; +int GLAD_GL_EXT_EGL_image_storage_compression = 0; +int GLAD_GL_EXT_YUV_target = 0; +int GLAD_GL_EXT_base_instance = 0; +int GLAD_GL_EXT_blend_func_extended = 0; +int GLAD_GL_EXT_blend_minmax = 0; +int GLAD_GL_EXT_buffer_storage = 0; +int GLAD_GL_EXT_clear_texture = 0; +int GLAD_GL_EXT_clip_control = 0; +int GLAD_GL_EXT_clip_cull_distance = 0; +int GLAD_GL_EXT_color_buffer_float = 0; +int GLAD_GL_EXT_color_buffer_half_float = 0; +int GLAD_GL_EXT_conservative_depth = 0; +int GLAD_GL_EXT_copy_image = 0; +int GLAD_GL_EXT_debug_label = 0; +int GLAD_GL_EXT_debug_marker = 0; +int GLAD_GL_EXT_depth_clamp = 0; +int GLAD_GL_EXT_discard_framebuffer = 0; +int GLAD_GL_EXT_disjoint_timer_query = 0; +int GLAD_GL_EXT_draw_buffers = 0; +int GLAD_GL_EXT_draw_buffers_indexed = 0; +int GLAD_GL_EXT_draw_elements_base_vertex = 0; +int GLAD_GL_EXT_draw_instanced = 0; +int GLAD_GL_EXT_draw_transform_feedback = 0; +int GLAD_GL_EXT_external_buffer = 0; +int GLAD_GL_EXT_float_blend = 0; +int GLAD_GL_EXT_fragment_shading_rate = 0; +int GLAD_GL_EXT_geometry_point_size = 0; +int GLAD_GL_EXT_geometry_shader = 0; +int GLAD_GL_EXT_gpu_shader5 = 0; +int GLAD_GL_EXT_instanced_arrays = 0; +int GLAD_GL_EXT_map_buffer_range = 0; +int GLAD_GL_EXT_memory_object = 0; +int GLAD_GL_EXT_memory_object_fd = 0; +int GLAD_GL_EXT_memory_object_win32 = 0; +int GLAD_GL_EXT_multi_draw_arrays = 0; +int GLAD_GL_EXT_multi_draw_indirect = 0; +int GLAD_GL_EXT_multisampled_compatibility = 0; +int GLAD_GL_EXT_multisampled_render_to_texture = 0; +int GLAD_GL_EXT_multisampled_render_to_texture2 = 0; +int GLAD_GL_EXT_multiview_draw_buffers = 0; +int GLAD_GL_EXT_multiview_tessellation_geometry_shader = 0; +int GLAD_GL_EXT_multiview_texture_multisample = 0; +int GLAD_GL_EXT_multiview_timer_query = 0; +int GLAD_GL_EXT_occlusion_query_boolean = 0; +int GLAD_GL_EXT_polygon_offset_clamp = 0; +int GLAD_GL_EXT_post_depth_coverage = 0; +int GLAD_GL_EXT_primitive_bounding_box = 0; +int GLAD_GL_EXT_protected_textures = 0; +int GLAD_GL_EXT_pvrtc_sRGB = 0; +int GLAD_GL_EXT_raster_multisample = 0; +int GLAD_GL_EXT_read_format_bgra = 0; +int GLAD_GL_EXT_render_snorm = 0; +int GLAD_GL_EXT_robustness = 0; +int GLAD_GL_EXT_sRGB = 0; +int GLAD_GL_EXT_sRGB_write_control = 0; +int GLAD_GL_EXT_semaphore = 0; +int GLAD_GL_EXT_semaphore_fd = 0; +int GLAD_GL_EXT_semaphore_win32 = 0; +int GLAD_GL_EXT_separate_depth_stencil = 0; +int GLAD_GL_EXT_separate_shader_objects = 0; +int GLAD_GL_EXT_shader_framebuffer_fetch = 0; +int GLAD_GL_EXT_shader_framebuffer_fetch_non_coherent = 0; +int GLAD_GL_EXT_shader_group_vote = 0; +int GLAD_GL_EXT_shader_implicit_conversions = 0; +int GLAD_GL_EXT_shader_integer_mix = 0; +int GLAD_GL_EXT_shader_io_blocks = 0; +int GLAD_GL_EXT_shader_non_constant_global_initializers = 0; +int GLAD_GL_EXT_shader_pixel_local_storage = 0; +int GLAD_GL_EXT_shader_pixel_local_storage2 = 0; +int GLAD_GL_EXT_shader_samples_identical = 0; +int GLAD_GL_EXT_shader_texture_lod = 0; +int GLAD_GL_EXT_shadow_samplers = 0; +int GLAD_GL_EXT_sparse_texture = 0; +int GLAD_GL_EXT_sparse_texture2 = 0; +int GLAD_GL_EXT_tessellation_point_size = 0; +int GLAD_GL_EXT_tessellation_shader = 0; +int GLAD_GL_EXT_texture_border_clamp = 0; +int GLAD_GL_EXT_texture_buffer = 0; +int GLAD_GL_EXT_texture_compression_astc_decode_mode = 0; +int GLAD_GL_EXT_texture_compression_bptc = 0; +int GLAD_GL_EXT_texture_compression_dxt1 = 0; +int GLAD_GL_EXT_texture_compression_rgtc = 0; +int GLAD_GL_EXT_texture_compression_s3tc = 0; +int GLAD_GL_EXT_texture_compression_s3tc_srgb = 0; +int GLAD_GL_EXT_texture_cube_map_array = 0; +int GLAD_GL_EXT_texture_filter_anisotropic = 0; +int GLAD_GL_EXT_texture_filter_minmax = 0; +int GLAD_GL_EXT_texture_format_BGRA8888 = 0; +int GLAD_GL_EXT_texture_format_sRGB_override = 0; +int GLAD_GL_EXT_texture_mirror_clamp_to_edge = 0; +int GLAD_GL_EXT_texture_norm16 = 0; +int GLAD_GL_EXT_texture_query_lod = 0; +int GLAD_GL_EXT_texture_rg = 0; +int GLAD_GL_EXT_texture_sRGB_R8 = 0; +int GLAD_GL_EXT_texture_sRGB_RG8 = 0; +int GLAD_GL_EXT_texture_sRGB_decode = 0; +int GLAD_GL_EXT_texture_shadow_lod = 0; +int GLAD_GL_EXT_texture_storage = 0; +int GLAD_GL_EXT_texture_storage_compression = 0; +int GLAD_GL_EXT_texture_type_2_10_10_10_REV = 0; +int GLAD_GL_EXT_texture_view = 0; +int GLAD_GL_EXT_unpack_subimage = 0; +int GLAD_GL_EXT_win32_keyed_mutex = 0; +int GLAD_GL_EXT_window_rectangles = 0; +int GLAD_GL_KHR_blend_equation_advanced = 0; +int GLAD_GL_KHR_blend_equation_advanced_coherent = 0; +int GLAD_GL_KHR_context_flush_control = 0; +int GLAD_GL_KHR_debug = 0; +int GLAD_GL_KHR_no_error = 0; +int GLAD_GL_KHR_parallel_shader_compile = 0; +int GLAD_GL_KHR_robust_buffer_access_behavior = 0; +int GLAD_GL_KHR_robustness = 0; +int GLAD_GL_KHR_shader_subgroup = 0; +int GLAD_GL_KHR_texture_compression_astc_hdr = 0; +int GLAD_GL_KHR_texture_compression_astc_ldr = 0; +int GLAD_GL_KHR_texture_compression_astc_sliced_3d = 0; +int GLAD_GL_OES_EGL_image = 0; +int GLAD_GL_OES_EGL_image_external = 0; +int GLAD_GL_OES_EGL_image_external_essl3 = 0; +int GLAD_GL_OES_compressed_ETC1_RGB8_sub_texture = 0; +int GLAD_GL_OES_compressed_ETC1_RGB8_texture = 0; +int GLAD_GL_OES_compressed_paletted_texture = 0; +int GLAD_GL_OES_copy_image = 0; +int GLAD_GL_OES_depth24 = 0; +int GLAD_GL_OES_depth32 = 0; +int GLAD_GL_OES_depth_texture = 0; +int GLAD_GL_OES_draw_buffers_indexed = 0; +int GLAD_GL_OES_draw_elements_base_vertex = 0; +int GLAD_GL_OES_element_index_uint = 0; +int GLAD_GL_OES_fbo_render_mipmap = 0; +int GLAD_GL_OES_fragment_precision_high = 0; +int GLAD_GL_OES_geometry_point_size = 0; +int GLAD_GL_OES_geometry_shader = 0; +int GLAD_GL_OES_get_program_binary = 0; +int GLAD_GL_OES_gpu_shader5 = 0; +int GLAD_GL_OES_mapbuffer = 0; +int GLAD_GL_OES_packed_depth_stencil = 0; +int GLAD_GL_OES_primitive_bounding_box = 0; +int GLAD_GL_OES_required_internalformat = 0; +int GLAD_GL_OES_rgb8_rgba8 = 0; +int GLAD_GL_OES_sample_shading = 0; +int GLAD_GL_OES_sample_variables = 0; +int GLAD_GL_OES_shader_image_atomic = 0; +int GLAD_GL_OES_shader_io_blocks = 0; +int GLAD_GL_OES_shader_multisample_interpolation = 0; +int GLAD_GL_OES_standard_derivatives = 0; +int GLAD_GL_OES_stencil1 = 0; +int GLAD_GL_OES_stencil4 = 0; +int GLAD_GL_OES_surfaceless_context = 0; +int GLAD_GL_OES_tessellation_point_size = 0; +int GLAD_GL_OES_tessellation_shader = 0; +int GLAD_GL_OES_texture_3D = 0; +int GLAD_GL_OES_texture_border_clamp = 0; +int GLAD_GL_OES_texture_buffer = 0; +int GLAD_GL_OES_texture_compression_astc = 0; +int GLAD_GL_OES_texture_cube_map_array = 0; +int GLAD_GL_OES_texture_float = 0; +int GLAD_GL_OES_texture_float_linear = 0; +int GLAD_GL_OES_texture_half_float = 0; +int GLAD_GL_OES_texture_half_float_linear = 0; +int GLAD_GL_OES_texture_npot = 0; +int GLAD_GL_OES_texture_stencil8 = 0; +int GLAD_GL_OES_texture_storage_multisample_2d_array = 0; +int GLAD_GL_OES_texture_view = 0; +int GLAD_GL_OES_vertex_array_object = 0; +int GLAD_GL_OES_vertex_half_float = 0; +int GLAD_GL_OES_vertex_type_10_10_10_2 = 0; +int GLAD_GL_OES_viewport_array = 0; + + + +PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC glad_glAcquireKeyedMutexWin32EXT = NULL; +PFNGLACTIVESHADERPROGRAMEXTPROC glad_glActiveShaderProgramEXT = NULL; +PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; +PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; +PFNGLBEGINQUERYEXTPROC glad_glBeginQueryEXT = NULL; +PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; +PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; +PFNGLBINDFRAGDATALOCATIONEXTPROC glad_glBindFragDataLocationEXT = NULL; +PFNGLBINDFRAGDATALOCATIONINDEXEDEXTPROC glad_glBindFragDataLocationIndexedEXT = NULL; +PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; +PFNGLBINDPROGRAMPIPELINEEXTPROC glad_glBindProgramPipelineEXT = NULL; +PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; +PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; +PFNGLBINDVERTEXARRAYOESPROC glad_glBindVertexArrayOES = NULL; +PFNGLBLENDBARRIERKHRPROC glad_glBlendBarrierKHR = NULL; +PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; +PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; +PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; +PFNGLBLENDEQUATIONSEPARATEIEXTPROC glad_glBlendEquationSeparateiEXT = NULL; +PFNGLBLENDEQUATIONSEPARATEIOESPROC glad_glBlendEquationSeparateiOES = NULL; +PFNGLBLENDEQUATIONIEXTPROC glad_glBlendEquationiEXT = NULL; +PFNGLBLENDEQUATIONIOESPROC glad_glBlendEquationiOES = NULL; +PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; +PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; +PFNGLBLENDFUNCSEPARATEIEXTPROC glad_glBlendFuncSeparateiEXT = NULL; +PFNGLBLENDFUNCSEPARATEIOESPROC glad_glBlendFuncSeparateiOES = NULL; +PFNGLBLENDFUNCIEXTPROC glad_glBlendFunciEXT = NULL; +PFNGLBLENDFUNCIOESPROC glad_glBlendFunciOES = NULL; +PFNGLBUFFERDATAPROC glad_glBufferData = NULL; +PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT = NULL; +PFNGLBUFFERSTORAGEEXTERNALEXTPROC glad_glBufferStorageExternalEXT = NULL; +PFNGLBUFFERSTORAGEMEMEXTPROC glad_glBufferStorageMemEXT = NULL; +PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; +PFNGLCLEARPROC glad_glClear = NULL; +PFNGLCLEARCOLORPROC glad_glClearColor = NULL; +PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL; +PFNGLCLEARPIXELLOCALSTORAGEUIEXTPROC glad_glClearPixelLocalStorageuiEXT = NULL; +PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; +PFNGLCLEARTEXIMAGEEXTPROC glad_glClearTexImageEXT = NULL; +PFNGLCLEARTEXSUBIMAGEEXTPROC glad_glClearTexSubImageEXT = NULL; +PFNGLCLIPCONTROLEXTPROC glad_glClipControlEXT = NULL; +PFNGLCOLORMASKPROC glad_glColorMask = NULL; +PFNGLCOLORMASKIEXTPROC glad_glColorMaskiEXT = NULL; +PFNGLCOLORMASKIOESPROC glad_glColorMaskiOES = NULL; +PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; +PFNGLCOMPRESSEDTEXIMAGE3DOESPROC glad_glCompressedTexImage3DOES = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC glad_glCompressedTexSubImage3DOES = NULL; +PFNGLCOPYIMAGESUBDATAEXTPROC glad_glCopyImageSubDataEXT = NULL; +PFNGLCOPYIMAGESUBDATAOESPROC glad_glCopyImageSubDataOES = NULL; +PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE3DOESPROC glad_glCopyTexSubImage3DOES = NULL; +PFNGLCREATEMEMORYOBJECTSEXTPROC glad_glCreateMemoryObjectsEXT = NULL; +PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; +PFNGLCREATESHADERPROC glad_glCreateShader = NULL; +PFNGLCREATESHADERPROGRAMVEXTPROC glad_glCreateShaderProgramvEXT = NULL; +PFNGLCULLFACEPROC glad_glCullFace = NULL; +PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR = NULL; +PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR = NULL; +PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR = NULL; +PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; +PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; +PFNGLDELETEMEMORYOBJECTSEXTPROC glad_glDeleteMemoryObjectsEXT = NULL; +PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; +PFNGLDELETEPROGRAMPIPELINESEXTPROC glad_glDeleteProgramPipelinesEXT = NULL; +PFNGLDELETEQUERIESEXTPROC glad_glDeleteQueriesEXT = NULL; +PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; +PFNGLDELETESEMAPHORESEXTPROC glad_glDeleteSemaphoresEXT = NULL; +PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; +PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; +PFNGLDELETEVERTEXARRAYSOESPROC glad_glDeleteVertexArraysOES = NULL; +PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; +PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; +PFNGLDEPTHRANGEARRAYFVOESPROC glad_glDepthRangeArrayfvOES = NULL; +PFNGLDEPTHRANGEINDEXEDFOESPROC glad_glDepthRangeIndexedfOES = NULL; +PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL; +PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; +PFNGLDISABLEPROC glad_glDisable = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; +PFNGLDISABLEIEXTPROC glad_glDisableiEXT = NULL; +PFNGLDISABLEIOESPROC glad_glDisableiOES = NULL; +PFNGLDISCARDFRAMEBUFFEREXTPROC glad_glDiscardFramebufferEXT = NULL; +PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; +PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC glad_glDrawArraysInstancedBaseInstanceEXT = NULL; +PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT = NULL; +PFNGLDRAWBUFFERSEXTPROC glad_glDrawBuffersEXT = NULL; +PFNGLDRAWBUFFERSINDEXEDEXTPROC glad_glDrawBuffersIndexedEXT = NULL; +PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; +PFNGLDRAWELEMENTSBASEVERTEXEXTPROC glad_glDrawElementsBaseVertexEXT = NULL; +PFNGLDRAWELEMENTSBASEVERTEXOESPROC glad_glDrawElementsBaseVertexOES = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC glad_glDrawElementsInstancedBaseInstanceEXT = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC glad_glDrawElementsInstancedBaseVertexBaseInstanceEXT = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC glad_glDrawElementsInstancedBaseVertexEXT = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC glad_glDrawElementsInstancedBaseVertexOES = NULL; +PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT = NULL; +PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC glad_glDrawRangeElementsBaseVertexEXT = NULL; +PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC glad_glDrawRangeElementsBaseVertexOES = NULL; +PFNGLDRAWTRANSFORMFEEDBACKEXTPROC glad_glDrawTransformFeedbackEXT = NULL; +PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDEXTPROC glad_glDrawTransformFeedbackInstancedEXT = NULL; +PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glad_glEGLImageTargetRenderbufferStorageOES = NULL; +PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glad_glEGLImageTargetTexStorageEXT = NULL; +PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glad_glEGLImageTargetTexture2DOES = NULL; +PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC glad_glEGLImageTargetTextureStorageEXT = NULL; +PFNGLENABLEPROC glad_glEnable = NULL; +PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; +PFNGLENABLEIEXTPROC glad_glEnableiEXT = NULL; +PFNGLENABLEIOESPROC glad_glEnableiOES = NULL; +PFNGLENDQUERYEXTPROC glad_glEndQueryEXT = NULL; +PFNGLFINISHPROC glad_glFinish = NULL; +PFNGLFLUSHPROC glad_glFlush = NULL; +PFNGLFLUSHMAPPEDBUFFERRANGEEXTPROC glad_glFlushMappedBufferRangeEXT = NULL; +PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC glad_glFramebufferFetchBarrierEXT = NULL; +PFNGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC glad_glFramebufferPixelLocalStorageSizeEXT = NULL; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; +PFNGLFRAMEBUFFERSHADINGRATEEXTPROC glad_glFramebufferShadingRateEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; +PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glad_glFramebufferTexture2DMultisampleEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE3DOESPROC glad_glFramebufferTexture3DOES = NULL; +PFNGLFRAMEBUFFERTEXTUREEXTPROC glad_glFramebufferTextureEXT = NULL; +PFNGLFRAMEBUFFERTEXTUREOESPROC glad_glFramebufferTextureOES = NULL; +PFNGLFRONTFACEPROC glad_glFrontFace = NULL; +PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; +PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; +PFNGLGENPROGRAMPIPELINESEXTPROC glad_glGenProgramPipelinesEXT = NULL; +PFNGLGENQUERIESEXTPROC glad_glGenQueriesEXT = NULL; +PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; +PFNGLGENSEMAPHORESEXTPROC glad_glGenSemaphoresEXT = NULL; +PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; +PFNGLGENVERTEXARRAYSOESPROC glad_glGenVertexArraysOES = NULL; +PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; +PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; +PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; +PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; +PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; +PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; +PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; +PFNGLGETBUFFERPOINTERVOESPROC glad_glGetBufferPointervOES = NULL; +PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR = NULL; +PFNGLGETERRORPROC glad_glGetError = NULL; +PFNGLGETFLOATI_VOESPROC glad_glGetFloati_vOES = NULL; +PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; +PFNGLGETFRAGDATAINDEXEXTPROC glad_glGetFragDataIndexEXT = NULL; +PFNGLGETFRAGMENTSHADINGRATESEXTPROC glad_glGetFragmentShadingRatesEXT = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; +PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC glad_glGetFramebufferPixelLocalStorageSizeEXT = NULL; +PFNGLGETGRAPHICSRESETSTATUSEXTPROC glad_glGetGraphicsResetStatusEXT = NULL; +PFNGLGETGRAPHICSRESETSTATUSKHRPROC glad_glGetGraphicsResetStatusKHR = NULL; +PFNGLGETINTEGER64VEXTPROC glad_glGetInteger64vEXT = NULL; +PFNGLGETINTEGERI_VEXTPROC glad_glGetIntegeri_vEXT = NULL; +PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; +PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC glad_glGetMemoryObjectParameterivEXT = NULL; +PFNGLGETOBJECTLABELEXTPROC glad_glGetObjectLabelEXT = NULL; +PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR = NULL; +PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR = NULL; +PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR = NULL; +PFNGLGETPROGRAMBINARYOESPROC glad_glGetProgramBinaryOES = NULL; +PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; +PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC glad_glGetProgramPipelineInfoLogEXT = NULL; +PFNGLGETPROGRAMPIPELINEIVEXTPROC glad_glGetProgramPipelineivEXT = NULL; +PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC glad_glGetProgramResourceLocationIndexEXT = NULL; +PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; +PFNGLGETQUERYOBJECTI64VEXTPROC glad_glGetQueryObjecti64vEXT = NULL; +PFNGLGETQUERYOBJECTIVEXTPROC glad_glGetQueryObjectivEXT = NULL; +PFNGLGETQUERYOBJECTUI64VEXTPROC glad_glGetQueryObjectui64vEXT = NULL; +PFNGLGETQUERYOBJECTUIVEXTPROC glad_glGetQueryObjectuivEXT = NULL; +PFNGLGETQUERYIVEXTPROC glad_glGetQueryivEXT = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; +PFNGLGETSAMPLERPARAMETERIIVEXTPROC glad_glGetSamplerParameterIivEXT = NULL; +PFNGLGETSAMPLERPARAMETERIIVOESPROC glad_glGetSamplerParameterIivOES = NULL; +PFNGLGETSAMPLERPARAMETERIUIVEXTPROC glad_glGetSamplerParameterIuivEXT = NULL; +PFNGLGETSAMPLERPARAMETERIUIVOESPROC glad_glGetSamplerParameterIuivOES = NULL; +PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC glad_glGetSemaphoreParameterui64vEXT = NULL; +PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; +PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL; +PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; +PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; +PFNGLGETSTRINGPROC glad_glGetString = NULL; +PFNGLGETTEXPARAMETERIIVEXTPROC glad_glGetTexParameterIivEXT = NULL; +PFNGLGETTEXPARAMETERIIVOESPROC glad_glGetTexParameterIivOES = NULL; +PFNGLGETTEXPARAMETERIUIVEXTPROC glad_glGetTexParameterIuivEXT = NULL; +PFNGLGETTEXPARAMETERIUIVOESPROC glad_glGetTexParameterIuivOES = NULL; +PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; +PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; +PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; +PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; +PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; +PFNGLGETUNSIGNEDBYTEI_VEXTPROC glad_glGetUnsignedBytei_vEXT = NULL; +PFNGLGETUNSIGNEDBYTEVEXTPROC glad_glGetUnsignedBytevEXT = NULL; +PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; +PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; +PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; +PFNGLGETNUNIFORMFVEXTPROC glad_glGetnUniformfvEXT = NULL; +PFNGLGETNUNIFORMFVKHRPROC glad_glGetnUniformfvKHR = NULL; +PFNGLGETNUNIFORMIVEXTPROC glad_glGetnUniformivEXT = NULL; +PFNGLGETNUNIFORMIVKHRPROC glad_glGetnUniformivKHR = NULL; +PFNGLGETNUNIFORMUIVKHRPROC glad_glGetnUniformuivKHR = NULL; +PFNGLHINTPROC glad_glHint = NULL; +PFNGLIMPORTMEMORYFDEXTPROC glad_glImportMemoryFdEXT = NULL; +PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC glad_glImportMemoryWin32HandleEXT = NULL; +PFNGLIMPORTMEMORYWIN32NAMEEXTPROC glad_glImportMemoryWin32NameEXT = NULL; +PFNGLIMPORTSEMAPHOREFDEXTPROC glad_glImportSemaphoreFdEXT = NULL; +PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC glad_glImportSemaphoreWin32HandleEXT = NULL; +PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC glad_glImportSemaphoreWin32NameEXT = NULL; +PFNGLINSERTEVENTMARKEREXTPROC glad_glInsertEventMarkerEXT = NULL; +PFNGLISBUFFERPROC glad_glIsBuffer = NULL; +PFNGLISENABLEDPROC glad_glIsEnabled = NULL; +PFNGLISENABLEDIEXTPROC glad_glIsEnablediEXT = NULL; +PFNGLISENABLEDIOESPROC glad_glIsEnablediOES = NULL; +PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; +PFNGLISMEMORYOBJECTEXTPROC glad_glIsMemoryObjectEXT = NULL; +PFNGLISPROGRAMPROC glad_glIsProgram = NULL; +PFNGLISPROGRAMPIPELINEEXTPROC glad_glIsProgramPipelineEXT = NULL; +PFNGLISQUERYEXTPROC glad_glIsQueryEXT = NULL; +PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; +PFNGLISSEMAPHOREEXTPROC glad_glIsSemaphoreEXT = NULL; +PFNGLISSHADERPROC glad_glIsShader = NULL; +PFNGLISTEXTUREPROC glad_glIsTexture = NULL; +PFNGLISVERTEXARRAYOESPROC glad_glIsVertexArrayOES = NULL; +PFNGLLABELOBJECTEXTPROC glad_glLabelObjectEXT = NULL; +PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; +PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; +PFNGLMAPBUFFEROESPROC glad_glMapBufferOES = NULL; +PFNGLMAPBUFFERRANGEEXTPROC glad_glMapBufferRangeEXT = NULL; +PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glad_glMaxShaderCompilerThreadsKHR = NULL; +PFNGLMEMORYOBJECTPARAMETERIVEXTPROC glad_glMemoryObjectParameterivEXT = NULL; +PFNGLMINSAMPLESHADINGOESPROC glad_glMinSampleShadingOES = NULL; +PFNGLMULTIDRAWARRAYSEXTPROC glad_glMultiDrawArraysEXT = NULL; +PFNGLMULTIDRAWARRAYSINDIRECTEXTPROC glad_glMultiDrawArraysIndirectEXT = NULL; +PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC glad_glMultiDrawElementsBaseVertexEXT = NULL; +PFNGLMULTIDRAWELEMENTSEXTPROC glad_glMultiDrawElementsEXT = NULL; +PFNGLMULTIDRAWELEMENTSINDIRECTEXTPROC glad_glMultiDrawElementsIndirectEXT = NULL; +PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC glad_glNamedBufferStorageExternalEXT = NULL; +PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC glad_glNamedBufferStorageMemEXT = NULL; +PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR = NULL; +PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR = NULL; +PFNGLPATCHPARAMETERIEXTPROC glad_glPatchParameteriEXT = NULL; +PFNGLPATCHPARAMETERIOESPROC glad_glPatchParameteriOES = NULL; +PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; +PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; +PFNGLPOLYGONOFFSETCLAMPEXTPROC glad_glPolygonOffsetClampEXT = NULL; +PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR = NULL; +PFNGLPOPGROUPMARKEREXTPROC glad_glPopGroupMarkerEXT = NULL; +PFNGLPRIMITIVEBOUNDINGBOXEXTPROC glad_glPrimitiveBoundingBoxEXT = NULL; +PFNGLPRIMITIVEBOUNDINGBOXOESPROC glad_glPrimitiveBoundingBoxOES = NULL; +PFNGLPROGRAMBINARYOESPROC glad_glProgramBinaryOES = NULL; +PFNGLPROGRAMPARAMETERIEXTPROC glad_glProgramParameteriEXT = NULL; +PFNGLPROGRAMUNIFORM1FEXTPROC glad_glProgramUniform1fEXT = NULL; +PFNGLPROGRAMUNIFORM1FVEXTPROC glad_glProgramUniform1fvEXT = NULL; +PFNGLPROGRAMUNIFORM1IEXTPROC glad_glProgramUniform1iEXT = NULL; +PFNGLPROGRAMUNIFORM1IVEXTPROC glad_glProgramUniform1ivEXT = NULL; +PFNGLPROGRAMUNIFORM1UIEXTPROC glad_glProgramUniform1uiEXT = NULL; +PFNGLPROGRAMUNIFORM1UIVEXTPROC glad_glProgramUniform1uivEXT = NULL; +PFNGLPROGRAMUNIFORM2FEXTPROC glad_glProgramUniform2fEXT = NULL; +PFNGLPROGRAMUNIFORM2FVEXTPROC glad_glProgramUniform2fvEXT = NULL; +PFNGLPROGRAMUNIFORM2IEXTPROC glad_glProgramUniform2iEXT = NULL; +PFNGLPROGRAMUNIFORM2IVEXTPROC glad_glProgramUniform2ivEXT = NULL; +PFNGLPROGRAMUNIFORM2UIEXTPROC glad_glProgramUniform2uiEXT = NULL; +PFNGLPROGRAMUNIFORM2UIVEXTPROC glad_glProgramUniform2uivEXT = NULL; +PFNGLPROGRAMUNIFORM3FEXTPROC glad_glProgramUniform3fEXT = NULL; +PFNGLPROGRAMUNIFORM3FVEXTPROC glad_glProgramUniform3fvEXT = NULL; +PFNGLPROGRAMUNIFORM3IEXTPROC glad_glProgramUniform3iEXT = NULL; +PFNGLPROGRAMUNIFORM3IVEXTPROC glad_glProgramUniform3ivEXT = NULL; +PFNGLPROGRAMUNIFORM3UIEXTPROC glad_glProgramUniform3uiEXT = NULL; +PFNGLPROGRAMUNIFORM3UIVEXTPROC glad_glProgramUniform3uivEXT = NULL; +PFNGLPROGRAMUNIFORM4FEXTPROC glad_glProgramUniform4fEXT = NULL; +PFNGLPROGRAMUNIFORM4FVEXTPROC glad_glProgramUniform4fvEXT = NULL; +PFNGLPROGRAMUNIFORM4IEXTPROC glad_glProgramUniform4iEXT = NULL; +PFNGLPROGRAMUNIFORM4IVEXTPROC glad_glProgramUniform4ivEXT = NULL; +PFNGLPROGRAMUNIFORM4UIEXTPROC glad_glProgramUniform4uiEXT = NULL; +PFNGLPROGRAMUNIFORM4UIVEXTPROC glad_glProgramUniform4uivEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glad_glProgramUniformMatrix2fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glad_glProgramUniformMatrix2x3fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glad_glProgramUniformMatrix2x4fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glad_glProgramUniformMatrix3fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glad_glProgramUniformMatrix3x2fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glad_glProgramUniformMatrix3x4fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glad_glProgramUniformMatrix4fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glad_glProgramUniformMatrix4x2fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glad_glProgramUniformMatrix4x3fvEXT = NULL; +PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR = NULL; +PFNGLPUSHGROUPMARKEREXTPROC glad_glPushGroupMarkerEXT = NULL; +PFNGLQUERYCOUNTEREXTPROC glad_glQueryCounterEXT = NULL; +PFNGLRASTERSAMPLESEXTPROC glad_glRasterSamplesEXT = NULL; +PFNGLREADBUFFERINDEXEDEXTPROC glad_glReadBufferIndexedEXT = NULL; +PFNGLREADPIXELSPROC glad_glReadPixels = NULL; +PFNGLREADNPIXELSEXTPROC glad_glReadnPixelsEXT = NULL; +PFNGLREADNPIXELSKHRPROC glad_glReadnPixelsKHR = NULL; +PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC glad_glReleaseKeyedMutexWin32EXT = NULL; +PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL; +PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT = NULL; +PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; +PFNGLSAMPLERPARAMETERIIVEXTPROC glad_glSamplerParameterIivEXT = NULL; +PFNGLSAMPLERPARAMETERIIVOESPROC glad_glSamplerParameterIivOES = NULL; +PFNGLSAMPLERPARAMETERIUIVEXTPROC glad_glSamplerParameterIuivEXT = NULL; +PFNGLSAMPLERPARAMETERIUIVOESPROC glad_glSamplerParameterIuivOES = NULL; +PFNGLSCISSORPROC glad_glScissor = NULL; +PFNGLSCISSORARRAYVOESPROC glad_glScissorArrayvOES = NULL; +PFNGLSCISSORINDEXEDOESPROC glad_glScissorIndexedOES = NULL; +PFNGLSCISSORINDEXEDVOESPROC glad_glScissorIndexedvOES = NULL; +PFNGLSEMAPHOREPARAMETERUI64VEXTPROC glad_glSemaphoreParameterui64vEXT = NULL; +PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL; +PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; +PFNGLSHADINGRATECOMBINEROPSEXTPROC glad_glShadingRateCombinerOpsEXT = NULL; +PFNGLSHADINGRATEEXTPROC glad_glShadingRateEXT = NULL; +PFNGLSIGNALSEMAPHOREEXTPROC glad_glSignalSemaphoreEXT = NULL; +PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; +PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; +PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; +PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; +PFNGLSTENCILOPPROC glad_glStencilOp = NULL; +PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; +PFNGLTEXBUFFEREXTPROC glad_glTexBufferEXT = NULL; +PFNGLTEXBUFFEROESPROC glad_glTexBufferOES = NULL; +PFNGLTEXBUFFERRANGEEXTPROC glad_glTexBufferRangeEXT = NULL; +PFNGLTEXBUFFERRANGEOESPROC glad_glTexBufferRangeOES = NULL; +PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; +PFNGLTEXIMAGE3DOESPROC glad_glTexImage3DOES = NULL; +PFNGLTEXPAGECOMMITMENTEXTPROC glad_glTexPageCommitmentEXT = NULL; +PFNGLTEXPARAMETERIIVEXTPROC glad_glTexParameterIivEXT = NULL; +PFNGLTEXPARAMETERIIVOESPROC glad_glTexParameterIivOES = NULL; +PFNGLTEXPARAMETERIUIVEXTPROC glad_glTexParameterIuivEXT = NULL; +PFNGLTEXPARAMETERIUIVOESPROC glad_glTexParameterIuivOES = NULL; +PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; +PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; +PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; +PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; +PFNGLTEXSTORAGE1DEXTPROC glad_glTexStorage1DEXT = NULL; +PFNGLTEXSTORAGE2DEXTPROC glad_glTexStorage2DEXT = NULL; +PFNGLTEXSTORAGE3DEXTPROC glad_glTexStorage3DEXT = NULL; +PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC glad_glTexStorage3DMultisampleOES = NULL; +PFNGLTEXSTORAGEATTRIBS2DEXTPROC glad_glTexStorageAttribs2DEXT = NULL; +PFNGLTEXSTORAGEATTRIBS3DEXTPROC glad_glTexStorageAttribs3DEXT = NULL; +PFNGLTEXSTORAGEMEM2DEXTPROC glad_glTexStorageMem2DEXT = NULL; +PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC glad_glTexStorageMem2DMultisampleEXT = NULL; +PFNGLTEXSTORAGEMEM3DEXTPROC glad_glTexStorageMem3DEXT = NULL; +PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC glad_glTexStorageMem3DMultisampleEXT = NULL; +PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; +PFNGLTEXSUBIMAGE3DOESPROC glad_glTexSubImage3DOES = NULL; +PFNGLTEXTURESTORAGE1DEXTPROC glad_glTextureStorage1DEXT = NULL; +PFNGLTEXTURESTORAGE2DEXTPROC glad_glTextureStorage2DEXT = NULL; +PFNGLTEXTURESTORAGE3DEXTPROC glad_glTextureStorage3DEXT = NULL; +PFNGLTEXTURESTORAGEMEM2DEXTPROC glad_glTextureStorageMem2DEXT = NULL; +PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC glad_glTextureStorageMem2DMultisampleEXT = NULL; +PFNGLTEXTURESTORAGEMEM3DEXTPROC glad_glTextureStorageMem3DEXT = NULL; +PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC glad_glTextureStorageMem3DMultisampleEXT = NULL; +PFNGLTEXTUREVIEWEXTPROC glad_glTextureViewEXT = NULL; +PFNGLTEXTUREVIEWOESPROC glad_glTextureViewOES = NULL; +PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; +PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; +PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; +PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; +PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; +PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; +PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; +PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; +PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; +PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; +PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; +PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; +PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; +PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; +PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; +PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; +PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; +PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; +PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; +PFNGLUNMAPBUFFEROESPROC glad_glUnmapBufferOES = NULL; +PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; +PFNGLUSEPROGRAMSTAGESEXTPROC glad_glUseProgramStagesEXT = NULL; +PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; +PFNGLVALIDATEPROGRAMPIPELINEEXTPROC glad_glValidateProgramPipelineEXT = NULL; +PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; +PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; +PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; +PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; +PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; +PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; +PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; +PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; +PFNGLVERTEXATTRIBDIVISOREXTPROC glad_glVertexAttribDivisorEXT = NULL; +PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; +PFNGLVIEWPORTPROC glad_glViewport = NULL; +PFNGLVIEWPORTARRAYVOESPROC glad_glViewportArrayvOES = NULL; +PFNGLVIEWPORTINDEXEDFOESPROC glad_glViewportIndexedfOES = NULL; +PFNGLVIEWPORTINDEXEDFVOESPROC glad_glViewportIndexedfvOES = NULL; +PFNGLWAITSEMAPHOREEXTPROC glad_glWaitSemaphoreEXT = NULL; +PFNGLWINDOWRECTANGLESEXTPROC glad_glWindowRectanglesEXT = NULL; + + +static void glad_gl_load_GL_ES_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ES_VERSION_2_0) return; + glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); + glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); + glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); + glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); + glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); + glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); + glad_glBindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, "glBindTexture"); + glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); + glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); + glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, "glBlendEquationSeparate"); + glad_glBlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, "glBlendFunc"); + glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, "glBlendFuncSeparate"); + glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); + glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); + glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); + glad_glClear = (PFNGLCLEARPROC) load(userptr, "glClear"); + glad_glClearColor = (PFNGLCLEARCOLORPROC) load(userptr, "glClearColor"); + glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC) load(userptr, "glClearDepthf"); + glad_glClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, "glClearStencil"); + glad_glColorMask = (PFNGLCOLORMASKPROC) load(userptr, "glColorMask"); + glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); + glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); + glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); + glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, "glCopyTexImage2D"); + glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, "glCopyTexSubImage2D"); + glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); + glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); + glad_glCullFace = (PFNGLCULLFACEPROC) load(userptr, "glCullFace"); + glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); + glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); + glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, "glDeleteProgram"); + glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); + glad_glDeleteShader = (PFNGLDELETESHADERPROC) load(userptr, "glDeleteShader"); + glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, "glDeleteTextures"); + glad_glDepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, "glDepthFunc"); + glad_glDepthMask = (PFNGLDEPTHMASKPROC) load(userptr, "glDepthMask"); + glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC) load(userptr, "glDepthRangef"); + glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); + glad_glDisable = (PFNGLDISABLEPROC) load(userptr, "glDisable"); + glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); + glad_glDrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, "glDrawArrays"); + glad_glDrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, "glDrawElements"); + glad_glEnable = (PFNGLENABLEPROC) load(userptr, "glEnable"); + glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); + glad_glFinish = (PFNGLFINISHPROC) load(userptr, "glFinish"); + glad_glFlush = (PFNGLFLUSHPROC) load(userptr, "glFlush"); + glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); + glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); + glad_glFrontFace = (PFNGLFRONTFACEPROC) load(userptr, "glFrontFace"); + glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); + glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); + glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); + glad_glGenTextures = (PFNGLGENTEXTURESPROC) load(userptr, "glGenTextures"); + glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); + glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); + glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); + glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, "glGetAttachedShaders"); + glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); + glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, "glGetBooleanv"); + glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); + glad_glGetError = (PFNGLGETERRORPROC) load(userptr, "glGetError"); + glad_glGetFloatv = (PFNGLGETFLOATVPROC) load(userptr, "glGetFloatv"); + glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); + glad_glGetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, "glGetIntegerv"); + glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, "glGetProgramInfoLog"); + glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, "glGetProgramiv"); + glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); + glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, "glGetShaderInfoLog"); + glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC) load(userptr, "glGetShaderPrecisionFormat"); + glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); + glad_glGetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, "glGetShaderiv"); + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, "glGetTexParameterfv"); + glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, "glGetTexParameteriv"); + glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); + glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); + glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); + glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); + glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); + glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); + glad_glHint = (PFNGLHINTPROC) load(userptr, "glHint"); + glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); + glad_glIsEnabled = (PFNGLISENABLEDPROC) load(userptr, "glIsEnabled"); + glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); + glad_glIsProgram = (PFNGLISPROGRAMPROC) load(userptr, "glIsProgram"); + glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); + glad_glIsShader = (PFNGLISSHADERPROC) load(userptr, "glIsShader"); + glad_glIsTexture = (PFNGLISTEXTUREPROC) load(userptr, "glIsTexture"); + glad_glLineWidth = (PFNGLLINEWIDTHPROC) load(userptr, "glLineWidth"); + glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); + glad_glPixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, "glPixelStorei"); + glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, "glPolygonOffset"); + glad_glReadPixels = (PFNGLREADPIXELSPROC) load(userptr, "glReadPixels"); + glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC) load(userptr, "glReleaseShaderCompiler"); + glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); + glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); + glad_glScissor = (PFNGLSCISSORPROC) load(userptr, "glScissor"); + glad_glShaderBinary = (PFNGLSHADERBINARYPROC) load(userptr, "glShaderBinary"); + glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); + glad_glStencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, "glStencilFunc"); + glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, "glStencilFuncSeparate"); + glad_glStencilMask = (PFNGLSTENCILMASKPROC) load(userptr, "glStencilMask"); + glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, "glStencilMaskSeparate"); + glad_glStencilOp = (PFNGLSTENCILOPPROC) load(userptr, "glStencilOp"); + glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, "glStencilOpSeparate"); + glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, "glTexImage2D"); + glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, "glTexParameterf"); + glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, "glTexParameterfv"); + glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, "glTexParameteri"); + glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, "glTexParameteriv"); + glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, "glTexSubImage2D"); + glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); + glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); + glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); + glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); + glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); + glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); + glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); + glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); + glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); + glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); + glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); + glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); + glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); + glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); + glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); + glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); + glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); + glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); + glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); + glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); + glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); + glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); + glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); + glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); + glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); + glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); + glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); + glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); + glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); + glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); + glad_glViewport = (PFNGLVIEWPORTPROC) load(userptr, "glViewport"); +} +static void glad_gl_load_GL_EXT_EGL_image_storage( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_EGL_image_storage) return; + glad_glEGLImageTargetTexStorageEXT = (PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) load(userptr, "glEGLImageTargetTexStorageEXT"); + glad_glEGLImageTargetTextureStorageEXT = (PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) load(userptr, "glEGLImageTargetTextureStorageEXT"); +} +static void glad_gl_load_GL_EXT_base_instance( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_base_instance) return; + glad_glDrawArraysInstancedBaseInstanceEXT = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC) load(userptr, "glDrawArraysInstancedBaseInstanceEXT"); + glad_glDrawElementsInstancedBaseInstanceEXT = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC) load(userptr, "glDrawElementsInstancedBaseInstanceEXT"); + glad_glDrawElementsInstancedBaseVertexBaseInstanceEXT = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC) load(userptr, "glDrawElementsInstancedBaseVertexBaseInstanceEXT"); +} +static void glad_gl_load_GL_EXT_blend_func_extended( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_blend_func_extended) return; + glad_glBindFragDataLocationEXT = (PFNGLBINDFRAGDATALOCATIONEXTPROC) load(userptr, "glBindFragDataLocationEXT"); + glad_glBindFragDataLocationIndexedEXT = (PFNGLBINDFRAGDATALOCATIONINDEXEDEXTPROC) load(userptr, "glBindFragDataLocationIndexedEXT"); + glad_glGetFragDataIndexEXT = (PFNGLGETFRAGDATAINDEXEXTPROC) load(userptr, "glGetFragDataIndexEXT"); + glad_glGetProgramResourceLocationIndexEXT = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC) load(userptr, "glGetProgramResourceLocationIndexEXT"); +} +static void glad_gl_load_GL_EXT_buffer_storage( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_buffer_storage) return; + glad_glBufferStorageEXT = (PFNGLBUFFERSTORAGEEXTPROC) load(userptr, "glBufferStorageEXT"); +} +static void glad_gl_load_GL_EXT_clear_texture( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_clear_texture) return; + glad_glClearTexImageEXT = (PFNGLCLEARTEXIMAGEEXTPROC) load(userptr, "glClearTexImageEXT"); + glad_glClearTexSubImageEXT = (PFNGLCLEARTEXSUBIMAGEEXTPROC) load(userptr, "glClearTexSubImageEXT"); +} +static void glad_gl_load_GL_EXT_clip_control( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_clip_control) return; + glad_glClipControlEXT = (PFNGLCLIPCONTROLEXTPROC) load(userptr, "glClipControlEXT"); +} +static void glad_gl_load_GL_EXT_copy_image( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_copy_image) return; + glad_glCopyImageSubDataEXT = (PFNGLCOPYIMAGESUBDATAEXTPROC) load(userptr, "glCopyImageSubDataEXT"); +} +static void glad_gl_load_GL_EXT_debug_label( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_debug_label) return; + glad_glGetObjectLabelEXT = (PFNGLGETOBJECTLABELEXTPROC) load(userptr, "glGetObjectLabelEXT"); + glad_glLabelObjectEXT = (PFNGLLABELOBJECTEXTPROC) load(userptr, "glLabelObjectEXT"); +} +static void glad_gl_load_GL_EXT_debug_marker( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_debug_marker) return; + glad_glInsertEventMarkerEXT = (PFNGLINSERTEVENTMARKEREXTPROC) load(userptr, "glInsertEventMarkerEXT"); + glad_glPopGroupMarkerEXT = (PFNGLPOPGROUPMARKEREXTPROC) load(userptr, "glPopGroupMarkerEXT"); + glad_glPushGroupMarkerEXT = (PFNGLPUSHGROUPMARKEREXTPROC) load(userptr, "glPushGroupMarkerEXT"); +} +static void glad_gl_load_GL_EXT_discard_framebuffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_discard_framebuffer) return; + glad_glDiscardFramebufferEXT = (PFNGLDISCARDFRAMEBUFFEREXTPROC) load(userptr, "glDiscardFramebufferEXT"); +} +static void glad_gl_load_GL_EXT_disjoint_timer_query( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_disjoint_timer_query) return; + glad_glBeginQueryEXT = (PFNGLBEGINQUERYEXTPROC) load(userptr, "glBeginQueryEXT"); + glad_glDeleteQueriesEXT = (PFNGLDELETEQUERIESEXTPROC) load(userptr, "glDeleteQueriesEXT"); + glad_glEndQueryEXT = (PFNGLENDQUERYEXTPROC) load(userptr, "glEndQueryEXT"); + glad_glGenQueriesEXT = (PFNGLGENQUERIESEXTPROC) load(userptr, "glGenQueriesEXT"); + glad_glGetInteger64vEXT = (PFNGLGETINTEGER64VEXTPROC) load(userptr, "glGetInteger64vEXT"); + glad_glGetQueryObjecti64vEXT = (PFNGLGETQUERYOBJECTI64VEXTPROC) load(userptr, "glGetQueryObjecti64vEXT"); + glad_glGetQueryObjectivEXT = (PFNGLGETQUERYOBJECTIVEXTPROC) load(userptr, "glGetQueryObjectivEXT"); + glad_glGetQueryObjectui64vEXT = (PFNGLGETQUERYOBJECTUI64VEXTPROC) load(userptr, "glGetQueryObjectui64vEXT"); + glad_glGetQueryObjectuivEXT = (PFNGLGETQUERYOBJECTUIVEXTPROC) load(userptr, "glGetQueryObjectuivEXT"); + glad_glGetQueryivEXT = (PFNGLGETQUERYIVEXTPROC) load(userptr, "glGetQueryivEXT"); + glad_glIsQueryEXT = (PFNGLISQUERYEXTPROC) load(userptr, "glIsQueryEXT"); + glad_glQueryCounterEXT = (PFNGLQUERYCOUNTEREXTPROC) load(userptr, "glQueryCounterEXT"); +} +static void glad_gl_load_GL_EXT_draw_buffers( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_buffers) return; + glad_glDrawBuffersEXT = (PFNGLDRAWBUFFERSEXTPROC) load(userptr, "glDrawBuffersEXT"); +} +static void glad_gl_load_GL_EXT_draw_buffers_indexed( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_buffers_indexed) return; + glad_glBlendEquationSeparateiEXT = (PFNGLBLENDEQUATIONSEPARATEIEXTPROC) load(userptr, "glBlendEquationSeparateiEXT"); + glad_glBlendEquationiEXT = (PFNGLBLENDEQUATIONIEXTPROC) load(userptr, "glBlendEquationiEXT"); + glad_glBlendFuncSeparateiEXT = (PFNGLBLENDFUNCSEPARATEIEXTPROC) load(userptr, "glBlendFuncSeparateiEXT"); + glad_glBlendFunciEXT = (PFNGLBLENDFUNCIEXTPROC) load(userptr, "glBlendFunciEXT"); + glad_glColorMaskiEXT = (PFNGLCOLORMASKIEXTPROC) load(userptr, "glColorMaskiEXT"); + glad_glDisableiEXT = (PFNGLDISABLEIEXTPROC) load(userptr, "glDisableiEXT"); + glad_glEnableiEXT = (PFNGLENABLEIEXTPROC) load(userptr, "glEnableiEXT"); + glad_glIsEnablediEXT = (PFNGLISENABLEDIEXTPROC) load(userptr, "glIsEnablediEXT"); +} +static void glad_gl_load_GL_EXT_draw_elements_base_vertex( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_elements_base_vertex) return; + glad_glDrawElementsBaseVertexEXT = (PFNGLDRAWELEMENTSBASEVERTEXEXTPROC) load(userptr, "glDrawElementsBaseVertexEXT"); + glad_glDrawElementsInstancedBaseVertexEXT = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC) load(userptr, "glDrawElementsInstancedBaseVertexEXT"); + glad_glDrawRangeElementsBaseVertexEXT = (PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC) load(userptr, "glDrawRangeElementsBaseVertexEXT"); + glad_glMultiDrawElementsBaseVertexEXT = (PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC) load(userptr, "glMultiDrawElementsBaseVertexEXT"); +} +static void glad_gl_load_GL_EXT_draw_instanced( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_instanced) return; + glad_glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC) load(userptr, "glDrawArraysInstancedEXT"); + glad_glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC) load(userptr, "glDrawElementsInstancedEXT"); +} +static void glad_gl_load_GL_EXT_draw_transform_feedback( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_transform_feedback) return; + glad_glDrawTransformFeedbackEXT = (PFNGLDRAWTRANSFORMFEEDBACKEXTPROC) load(userptr, "glDrawTransformFeedbackEXT"); + glad_glDrawTransformFeedbackInstancedEXT = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDEXTPROC) load(userptr, "glDrawTransformFeedbackInstancedEXT"); +} +static void glad_gl_load_GL_EXT_external_buffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_external_buffer) return; + glad_glBufferStorageExternalEXT = (PFNGLBUFFERSTORAGEEXTERNALEXTPROC) load(userptr, "glBufferStorageExternalEXT"); + glad_glNamedBufferStorageExternalEXT = (PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC) load(userptr, "glNamedBufferStorageExternalEXT"); +} +static void glad_gl_load_GL_EXT_fragment_shading_rate( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_fragment_shading_rate) return; + glad_glFramebufferShadingRateEXT = (PFNGLFRAMEBUFFERSHADINGRATEEXTPROC) load(userptr, "glFramebufferShadingRateEXT"); + glad_glGetFragmentShadingRatesEXT = (PFNGLGETFRAGMENTSHADINGRATESEXTPROC) load(userptr, "glGetFragmentShadingRatesEXT"); + glad_glShadingRateCombinerOpsEXT = (PFNGLSHADINGRATECOMBINEROPSEXTPROC) load(userptr, "glShadingRateCombinerOpsEXT"); + glad_glShadingRateEXT = (PFNGLSHADINGRATEEXTPROC) load(userptr, "glShadingRateEXT"); +} +static void glad_gl_load_GL_EXT_geometry_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_geometry_shader) return; + glad_glFramebufferTextureEXT = (PFNGLFRAMEBUFFERTEXTUREEXTPROC) load(userptr, "glFramebufferTextureEXT"); +} +static void glad_gl_load_GL_EXT_instanced_arrays( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_instanced_arrays) return; + glad_glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC) load(userptr, "glDrawArraysInstancedEXT"); + glad_glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC) load(userptr, "glDrawElementsInstancedEXT"); + glad_glVertexAttribDivisorEXT = (PFNGLVERTEXATTRIBDIVISOREXTPROC) load(userptr, "glVertexAttribDivisorEXT"); +} +static void glad_gl_load_GL_EXT_map_buffer_range( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_map_buffer_range) return; + glad_glFlushMappedBufferRangeEXT = (PFNGLFLUSHMAPPEDBUFFERRANGEEXTPROC) load(userptr, "glFlushMappedBufferRangeEXT"); + glad_glMapBufferRangeEXT = (PFNGLMAPBUFFERRANGEEXTPROC) load(userptr, "glMapBufferRangeEXT"); +} +static void glad_gl_load_GL_EXT_memory_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_memory_object) return; + glad_glBufferStorageMemEXT = (PFNGLBUFFERSTORAGEMEMEXTPROC) load(userptr, "glBufferStorageMemEXT"); + glad_glCreateMemoryObjectsEXT = (PFNGLCREATEMEMORYOBJECTSEXTPROC) load(userptr, "glCreateMemoryObjectsEXT"); + glad_glDeleteMemoryObjectsEXT = (PFNGLDELETEMEMORYOBJECTSEXTPROC) load(userptr, "glDeleteMemoryObjectsEXT"); + glad_glGetMemoryObjectParameterivEXT = (PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC) load(userptr, "glGetMemoryObjectParameterivEXT"); + glad_glGetUnsignedBytei_vEXT = (PFNGLGETUNSIGNEDBYTEI_VEXTPROC) load(userptr, "glGetUnsignedBytei_vEXT"); + glad_glGetUnsignedBytevEXT = (PFNGLGETUNSIGNEDBYTEVEXTPROC) load(userptr, "glGetUnsignedBytevEXT"); + glad_glIsMemoryObjectEXT = (PFNGLISMEMORYOBJECTEXTPROC) load(userptr, "glIsMemoryObjectEXT"); + glad_glMemoryObjectParameterivEXT = (PFNGLMEMORYOBJECTPARAMETERIVEXTPROC) load(userptr, "glMemoryObjectParameterivEXT"); + glad_glNamedBufferStorageMemEXT = (PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC) load(userptr, "glNamedBufferStorageMemEXT"); + glad_glTexStorageMem2DEXT = (PFNGLTEXSTORAGEMEM2DEXTPROC) load(userptr, "glTexStorageMem2DEXT"); + glad_glTexStorageMem2DMultisampleEXT = (PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC) load(userptr, "glTexStorageMem2DMultisampleEXT"); + glad_glTexStorageMem3DEXT = (PFNGLTEXSTORAGEMEM3DEXTPROC) load(userptr, "glTexStorageMem3DEXT"); + glad_glTexStorageMem3DMultisampleEXT = (PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC) load(userptr, "glTexStorageMem3DMultisampleEXT"); + glad_glTextureStorageMem2DEXT = (PFNGLTEXTURESTORAGEMEM2DEXTPROC) load(userptr, "glTextureStorageMem2DEXT"); + glad_glTextureStorageMem2DMultisampleEXT = (PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC) load(userptr, "glTextureStorageMem2DMultisampleEXT"); + glad_glTextureStorageMem3DEXT = (PFNGLTEXTURESTORAGEMEM3DEXTPROC) load(userptr, "glTextureStorageMem3DEXT"); + glad_glTextureStorageMem3DMultisampleEXT = (PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC) load(userptr, "glTextureStorageMem3DMultisampleEXT"); +} +static void glad_gl_load_GL_EXT_memory_object_fd( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_memory_object_fd) return; + glad_glImportMemoryFdEXT = (PFNGLIMPORTMEMORYFDEXTPROC) load(userptr, "glImportMemoryFdEXT"); +} +static void glad_gl_load_GL_EXT_memory_object_win32( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_memory_object_win32) return; + glad_glImportMemoryWin32HandleEXT = (PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC) load(userptr, "glImportMemoryWin32HandleEXT"); + glad_glImportMemoryWin32NameEXT = (PFNGLIMPORTMEMORYWIN32NAMEEXTPROC) load(userptr, "glImportMemoryWin32NameEXT"); +} +static void glad_gl_load_GL_EXT_multi_draw_arrays( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_multi_draw_arrays) return; + glad_glMultiDrawArraysEXT = (PFNGLMULTIDRAWARRAYSEXTPROC) load(userptr, "glMultiDrawArraysEXT"); + glad_glMultiDrawElementsEXT = (PFNGLMULTIDRAWELEMENTSEXTPROC) load(userptr, "glMultiDrawElementsEXT"); +} +static void glad_gl_load_GL_EXT_multi_draw_indirect( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_multi_draw_indirect) return; + glad_glMultiDrawArraysIndirectEXT = (PFNGLMULTIDRAWARRAYSINDIRECTEXTPROC) load(userptr, "glMultiDrawArraysIndirectEXT"); + glad_glMultiDrawElementsIndirectEXT = (PFNGLMULTIDRAWELEMENTSINDIRECTEXTPROC) load(userptr, "glMultiDrawElementsIndirectEXT"); +} +static void glad_gl_load_GL_EXT_multisampled_render_to_texture( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_multisampled_render_to_texture) return; + glad_glFramebufferTexture2DMultisampleEXT = (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC) load(userptr, "glFramebufferTexture2DMultisampleEXT"); + glad_glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) load(userptr, "glRenderbufferStorageMultisampleEXT"); +} +static void glad_gl_load_GL_EXT_multiview_draw_buffers( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_multiview_draw_buffers) return; + glad_glDrawBuffersIndexedEXT = (PFNGLDRAWBUFFERSINDEXEDEXTPROC) load(userptr, "glDrawBuffersIndexedEXT"); + glad_glGetIntegeri_vEXT = (PFNGLGETINTEGERI_VEXTPROC) load(userptr, "glGetIntegeri_vEXT"); + glad_glReadBufferIndexedEXT = (PFNGLREADBUFFERINDEXEDEXTPROC) load(userptr, "glReadBufferIndexedEXT"); +} +static void glad_gl_load_GL_EXT_occlusion_query_boolean( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_occlusion_query_boolean) return; + glad_glBeginQueryEXT = (PFNGLBEGINQUERYEXTPROC) load(userptr, "glBeginQueryEXT"); + glad_glDeleteQueriesEXT = (PFNGLDELETEQUERIESEXTPROC) load(userptr, "glDeleteQueriesEXT"); + glad_glEndQueryEXT = (PFNGLENDQUERYEXTPROC) load(userptr, "glEndQueryEXT"); + glad_glGenQueriesEXT = (PFNGLGENQUERIESEXTPROC) load(userptr, "glGenQueriesEXT"); + glad_glGetQueryObjectuivEXT = (PFNGLGETQUERYOBJECTUIVEXTPROC) load(userptr, "glGetQueryObjectuivEXT"); + glad_glGetQueryivEXT = (PFNGLGETQUERYIVEXTPROC) load(userptr, "glGetQueryivEXT"); + glad_glIsQueryEXT = (PFNGLISQUERYEXTPROC) load(userptr, "glIsQueryEXT"); +} +static void glad_gl_load_GL_EXT_polygon_offset_clamp( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_polygon_offset_clamp) return; + glad_glPolygonOffsetClampEXT = (PFNGLPOLYGONOFFSETCLAMPEXTPROC) load(userptr, "glPolygonOffsetClampEXT"); +} +static void glad_gl_load_GL_EXT_primitive_bounding_box( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_primitive_bounding_box) return; + glad_glPrimitiveBoundingBoxEXT = (PFNGLPRIMITIVEBOUNDINGBOXEXTPROC) load(userptr, "glPrimitiveBoundingBoxEXT"); +} +static void glad_gl_load_GL_EXT_raster_multisample( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_raster_multisample) return; + glad_glRasterSamplesEXT = (PFNGLRASTERSAMPLESEXTPROC) load(userptr, "glRasterSamplesEXT"); +} +static void glad_gl_load_GL_EXT_robustness( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_robustness) return; + glad_glGetGraphicsResetStatusEXT = (PFNGLGETGRAPHICSRESETSTATUSEXTPROC) load(userptr, "glGetGraphicsResetStatusEXT"); + glad_glGetnUniformfvEXT = (PFNGLGETNUNIFORMFVEXTPROC) load(userptr, "glGetnUniformfvEXT"); + glad_glGetnUniformivEXT = (PFNGLGETNUNIFORMIVEXTPROC) load(userptr, "glGetnUniformivEXT"); + glad_glReadnPixelsEXT = (PFNGLREADNPIXELSEXTPROC) load(userptr, "glReadnPixelsEXT"); +} +static void glad_gl_load_GL_EXT_semaphore( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_semaphore) return; + glad_glDeleteSemaphoresEXT = (PFNGLDELETESEMAPHORESEXTPROC) load(userptr, "glDeleteSemaphoresEXT"); + glad_glGenSemaphoresEXT = (PFNGLGENSEMAPHORESEXTPROC) load(userptr, "glGenSemaphoresEXT"); + glad_glGetSemaphoreParameterui64vEXT = (PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC) load(userptr, "glGetSemaphoreParameterui64vEXT"); + glad_glGetUnsignedBytei_vEXT = (PFNGLGETUNSIGNEDBYTEI_VEXTPROC) load(userptr, "glGetUnsignedBytei_vEXT"); + glad_glGetUnsignedBytevEXT = (PFNGLGETUNSIGNEDBYTEVEXTPROC) load(userptr, "glGetUnsignedBytevEXT"); + glad_glIsSemaphoreEXT = (PFNGLISSEMAPHOREEXTPROC) load(userptr, "glIsSemaphoreEXT"); + glad_glSemaphoreParameterui64vEXT = (PFNGLSEMAPHOREPARAMETERUI64VEXTPROC) load(userptr, "glSemaphoreParameterui64vEXT"); + glad_glSignalSemaphoreEXT = (PFNGLSIGNALSEMAPHOREEXTPROC) load(userptr, "glSignalSemaphoreEXT"); + glad_glWaitSemaphoreEXT = (PFNGLWAITSEMAPHOREEXTPROC) load(userptr, "glWaitSemaphoreEXT"); +} +static void glad_gl_load_GL_EXT_semaphore_fd( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_semaphore_fd) return; + glad_glImportSemaphoreFdEXT = (PFNGLIMPORTSEMAPHOREFDEXTPROC) load(userptr, "glImportSemaphoreFdEXT"); +} +static void glad_gl_load_GL_EXT_semaphore_win32( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_semaphore_win32) return; + glad_glImportSemaphoreWin32HandleEXT = (PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC) load(userptr, "glImportSemaphoreWin32HandleEXT"); + glad_glImportSemaphoreWin32NameEXT = (PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC) load(userptr, "glImportSemaphoreWin32NameEXT"); +} +static void glad_gl_load_GL_EXT_separate_shader_objects( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_separate_shader_objects) return; + glad_glActiveShaderProgramEXT = (PFNGLACTIVESHADERPROGRAMEXTPROC) load(userptr, "glActiveShaderProgramEXT"); + glad_glBindProgramPipelineEXT = (PFNGLBINDPROGRAMPIPELINEEXTPROC) load(userptr, "glBindProgramPipelineEXT"); + glad_glCreateShaderProgramvEXT = (PFNGLCREATESHADERPROGRAMVEXTPROC) load(userptr, "glCreateShaderProgramvEXT"); + glad_glDeleteProgramPipelinesEXT = (PFNGLDELETEPROGRAMPIPELINESEXTPROC) load(userptr, "glDeleteProgramPipelinesEXT"); + glad_glGenProgramPipelinesEXT = (PFNGLGENPROGRAMPIPELINESEXTPROC) load(userptr, "glGenProgramPipelinesEXT"); + glad_glGetProgramPipelineInfoLogEXT = (PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC) load(userptr, "glGetProgramPipelineInfoLogEXT"); + glad_glGetProgramPipelineivEXT = (PFNGLGETPROGRAMPIPELINEIVEXTPROC) load(userptr, "glGetProgramPipelineivEXT"); + glad_glIsProgramPipelineEXT = (PFNGLISPROGRAMPIPELINEEXTPROC) load(userptr, "glIsProgramPipelineEXT"); + glad_glProgramParameteriEXT = (PFNGLPROGRAMPARAMETERIEXTPROC) load(userptr, "glProgramParameteriEXT"); + glad_glProgramUniform1fEXT = (PFNGLPROGRAMUNIFORM1FEXTPROC) load(userptr, "glProgramUniform1fEXT"); + glad_glProgramUniform1fvEXT = (PFNGLPROGRAMUNIFORM1FVEXTPROC) load(userptr, "glProgramUniform1fvEXT"); + glad_glProgramUniform1iEXT = (PFNGLPROGRAMUNIFORM1IEXTPROC) load(userptr, "glProgramUniform1iEXT"); + glad_glProgramUniform1ivEXT = (PFNGLPROGRAMUNIFORM1IVEXTPROC) load(userptr, "glProgramUniform1ivEXT"); + glad_glProgramUniform1uiEXT = (PFNGLPROGRAMUNIFORM1UIEXTPROC) load(userptr, "glProgramUniform1uiEXT"); + glad_glProgramUniform1uivEXT = (PFNGLPROGRAMUNIFORM1UIVEXTPROC) load(userptr, "glProgramUniform1uivEXT"); + glad_glProgramUniform2fEXT = (PFNGLPROGRAMUNIFORM2FEXTPROC) load(userptr, "glProgramUniform2fEXT"); + glad_glProgramUniform2fvEXT = (PFNGLPROGRAMUNIFORM2FVEXTPROC) load(userptr, "glProgramUniform2fvEXT"); + glad_glProgramUniform2iEXT = (PFNGLPROGRAMUNIFORM2IEXTPROC) load(userptr, "glProgramUniform2iEXT"); + glad_glProgramUniform2ivEXT = (PFNGLPROGRAMUNIFORM2IVEXTPROC) load(userptr, "glProgramUniform2ivEXT"); + glad_glProgramUniform2uiEXT = (PFNGLPROGRAMUNIFORM2UIEXTPROC) load(userptr, "glProgramUniform2uiEXT"); + glad_glProgramUniform2uivEXT = (PFNGLPROGRAMUNIFORM2UIVEXTPROC) load(userptr, "glProgramUniform2uivEXT"); + glad_glProgramUniform3fEXT = (PFNGLPROGRAMUNIFORM3FEXTPROC) load(userptr, "glProgramUniform3fEXT"); + glad_glProgramUniform3fvEXT = (PFNGLPROGRAMUNIFORM3FVEXTPROC) load(userptr, "glProgramUniform3fvEXT"); + glad_glProgramUniform3iEXT = (PFNGLPROGRAMUNIFORM3IEXTPROC) load(userptr, "glProgramUniform3iEXT"); + glad_glProgramUniform3ivEXT = (PFNGLPROGRAMUNIFORM3IVEXTPROC) load(userptr, "glProgramUniform3ivEXT"); + glad_glProgramUniform3uiEXT = (PFNGLPROGRAMUNIFORM3UIEXTPROC) load(userptr, "glProgramUniform3uiEXT"); + glad_glProgramUniform3uivEXT = (PFNGLPROGRAMUNIFORM3UIVEXTPROC) load(userptr, "glProgramUniform3uivEXT"); + glad_glProgramUniform4fEXT = (PFNGLPROGRAMUNIFORM4FEXTPROC) load(userptr, "glProgramUniform4fEXT"); + glad_glProgramUniform4fvEXT = (PFNGLPROGRAMUNIFORM4FVEXTPROC) load(userptr, "glProgramUniform4fvEXT"); + glad_glProgramUniform4iEXT = (PFNGLPROGRAMUNIFORM4IEXTPROC) load(userptr, "glProgramUniform4iEXT"); + glad_glProgramUniform4ivEXT = (PFNGLPROGRAMUNIFORM4IVEXTPROC) load(userptr, "glProgramUniform4ivEXT"); + glad_glProgramUniform4uiEXT = (PFNGLPROGRAMUNIFORM4UIEXTPROC) load(userptr, "glProgramUniform4uiEXT"); + glad_glProgramUniform4uivEXT = (PFNGLPROGRAMUNIFORM4UIVEXTPROC) load(userptr, "glProgramUniform4uivEXT"); + glad_glProgramUniformMatrix2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) load(userptr, "glProgramUniformMatrix2fvEXT"); + glad_glProgramUniformMatrix2x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) load(userptr, "glProgramUniformMatrix2x3fvEXT"); + glad_glProgramUniformMatrix2x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) load(userptr, "glProgramUniformMatrix2x4fvEXT"); + glad_glProgramUniformMatrix3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) load(userptr, "glProgramUniformMatrix3fvEXT"); + glad_glProgramUniformMatrix3x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) load(userptr, "glProgramUniformMatrix3x2fvEXT"); + glad_glProgramUniformMatrix3x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) load(userptr, "glProgramUniformMatrix3x4fvEXT"); + glad_glProgramUniformMatrix4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) load(userptr, "glProgramUniformMatrix4fvEXT"); + glad_glProgramUniformMatrix4x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) load(userptr, "glProgramUniformMatrix4x2fvEXT"); + glad_glProgramUniformMatrix4x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) load(userptr, "glProgramUniformMatrix4x3fvEXT"); + glad_glUseProgramStagesEXT = (PFNGLUSEPROGRAMSTAGESEXTPROC) load(userptr, "glUseProgramStagesEXT"); + glad_glValidateProgramPipelineEXT = (PFNGLVALIDATEPROGRAMPIPELINEEXTPROC) load(userptr, "glValidateProgramPipelineEXT"); +} +static void glad_gl_load_GL_EXT_shader_framebuffer_fetch_non_coherent( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_shader_framebuffer_fetch_non_coherent) return; + glad_glFramebufferFetchBarrierEXT = (PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) load(userptr, "glFramebufferFetchBarrierEXT"); +} +static void glad_gl_load_GL_EXT_shader_pixel_local_storage2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_shader_pixel_local_storage2) return; + glad_glClearPixelLocalStorageuiEXT = (PFNGLCLEARPIXELLOCALSTORAGEUIEXTPROC) load(userptr, "glClearPixelLocalStorageuiEXT"); + glad_glFramebufferPixelLocalStorageSizeEXT = (PFNGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC) load(userptr, "glFramebufferPixelLocalStorageSizeEXT"); + glad_glGetFramebufferPixelLocalStorageSizeEXT = (PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC) load(userptr, "glGetFramebufferPixelLocalStorageSizeEXT"); +} +static void glad_gl_load_GL_EXT_sparse_texture( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_sparse_texture) return; + glad_glTexPageCommitmentEXT = (PFNGLTEXPAGECOMMITMENTEXTPROC) load(userptr, "glTexPageCommitmentEXT"); +} +static void glad_gl_load_GL_EXT_tessellation_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_tessellation_shader) return; + glad_glPatchParameteriEXT = (PFNGLPATCHPARAMETERIEXTPROC) load(userptr, "glPatchParameteriEXT"); +} +static void glad_gl_load_GL_EXT_texture_border_clamp( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_border_clamp) return; + glad_glGetSamplerParameterIivEXT = (PFNGLGETSAMPLERPARAMETERIIVEXTPROC) load(userptr, "glGetSamplerParameterIivEXT"); + glad_glGetSamplerParameterIuivEXT = (PFNGLGETSAMPLERPARAMETERIUIVEXTPROC) load(userptr, "glGetSamplerParameterIuivEXT"); + glad_glGetTexParameterIivEXT = (PFNGLGETTEXPARAMETERIIVEXTPROC) load(userptr, "glGetTexParameterIivEXT"); + glad_glGetTexParameterIuivEXT = (PFNGLGETTEXPARAMETERIUIVEXTPROC) load(userptr, "glGetTexParameterIuivEXT"); + glad_glSamplerParameterIivEXT = (PFNGLSAMPLERPARAMETERIIVEXTPROC) load(userptr, "glSamplerParameterIivEXT"); + glad_glSamplerParameterIuivEXT = (PFNGLSAMPLERPARAMETERIUIVEXTPROC) load(userptr, "glSamplerParameterIuivEXT"); + glad_glTexParameterIivEXT = (PFNGLTEXPARAMETERIIVEXTPROC) load(userptr, "glTexParameterIivEXT"); + glad_glTexParameterIuivEXT = (PFNGLTEXPARAMETERIUIVEXTPROC) load(userptr, "glTexParameterIuivEXT"); +} +static void glad_gl_load_GL_EXT_texture_buffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_buffer) return; + glad_glTexBufferEXT = (PFNGLTEXBUFFEREXTPROC) load(userptr, "glTexBufferEXT"); + glad_glTexBufferRangeEXT = (PFNGLTEXBUFFERRANGEEXTPROC) load(userptr, "glTexBufferRangeEXT"); +} +static void glad_gl_load_GL_EXT_texture_storage( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_storage) return; + glad_glTexStorage1DEXT = (PFNGLTEXSTORAGE1DEXTPROC) load(userptr, "glTexStorage1DEXT"); + glad_glTexStorage2DEXT = (PFNGLTEXSTORAGE2DEXTPROC) load(userptr, "glTexStorage2DEXT"); + glad_glTexStorage3DEXT = (PFNGLTEXSTORAGE3DEXTPROC) load(userptr, "glTexStorage3DEXT"); + glad_glTextureStorage1DEXT = (PFNGLTEXTURESTORAGE1DEXTPROC) load(userptr, "glTextureStorage1DEXT"); + glad_glTextureStorage2DEXT = (PFNGLTEXTURESTORAGE2DEXTPROC) load(userptr, "glTextureStorage2DEXT"); + glad_glTextureStorage3DEXT = (PFNGLTEXTURESTORAGE3DEXTPROC) load(userptr, "glTextureStorage3DEXT"); +} +static void glad_gl_load_GL_EXT_texture_storage_compression( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_storage_compression) return; + glad_glTexStorageAttribs2DEXT = (PFNGLTEXSTORAGEATTRIBS2DEXTPROC) load(userptr, "glTexStorageAttribs2DEXT"); + glad_glTexStorageAttribs3DEXT = (PFNGLTEXSTORAGEATTRIBS3DEXTPROC) load(userptr, "glTexStorageAttribs3DEXT"); +} +static void glad_gl_load_GL_EXT_texture_view( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_view) return; + glad_glTextureViewEXT = (PFNGLTEXTUREVIEWEXTPROC) load(userptr, "glTextureViewEXT"); +} +static void glad_gl_load_GL_EXT_win32_keyed_mutex( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_win32_keyed_mutex) return; + glad_glAcquireKeyedMutexWin32EXT = (PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC) load(userptr, "glAcquireKeyedMutexWin32EXT"); + glad_glReleaseKeyedMutexWin32EXT = (PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC) load(userptr, "glReleaseKeyedMutexWin32EXT"); +} +static void glad_gl_load_GL_EXT_window_rectangles( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_window_rectangles) return; + glad_glWindowRectanglesEXT = (PFNGLWINDOWRECTANGLESEXTPROC) load(userptr, "glWindowRectanglesEXT"); +} +static void glad_gl_load_GL_KHR_blend_equation_advanced( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_blend_equation_advanced) return; + glad_glBlendBarrierKHR = (PFNGLBLENDBARRIERKHRPROC) load(userptr, "glBlendBarrierKHR"); +} +static void glad_gl_load_GL_KHR_debug( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_debug) return; + glad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC) load(userptr, "glDebugMessageCallbackKHR"); + glad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC) load(userptr, "glDebugMessageControlKHR"); + glad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC) load(userptr, "glDebugMessageInsertKHR"); + glad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC) load(userptr, "glGetDebugMessageLogKHR"); + glad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC) load(userptr, "glGetObjectLabelKHR"); + glad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC) load(userptr, "glGetObjectPtrLabelKHR"); + glad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC) load(userptr, "glGetPointervKHR"); + glad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC) load(userptr, "glObjectLabelKHR"); + glad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC) load(userptr, "glObjectPtrLabelKHR"); + glad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC) load(userptr, "glPopDebugGroupKHR"); + glad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC) load(userptr, "glPushDebugGroupKHR"); +} +static void glad_gl_load_GL_KHR_parallel_shader_compile( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_parallel_shader_compile) return; + glad_glMaxShaderCompilerThreadsKHR = (PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) load(userptr, "glMaxShaderCompilerThreadsKHR"); +} +static void glad_gl_load_GL_KHR_robustness( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_robustness) return; + glad_glGetGraphicsResetStatusKHR = (PFNGLGETGRAPHICSRESETSTATUSKHRPROC) load(userptr, "glGetGraphicsResetStatusKHR"); + glad_glGetnUniformfvKHR = (PFNGLGETNUNIFORMFVKHRPROC) load(userptr, "glGetnUniformfvKHR"); + glad_glGetnUniformivKHR = (PFNGLGETNUNIFORMIVKHRPROC) load(userptr, "glGetnUniformivKHR"); + glad_glGetnUniformuivKHR = (PFNGLGETNUNIFORMUIVKHRPROC) load(userptr, "glGetnUniformuivKHR"); + glad_glReadnPixelsKHR = (PFNGLREADNPIXELSKHRPROC) load(userptr, "glReadnPixelsKHR"); +} +static void glad_gl_load_GL_OES_EGL_image( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_EGL_image) return; + glad_glEGLImageTargetRenderbufferStorageOES = (PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) load(userptr, "glEGLImageTargetRenderbufferStorageOES"); + glad_glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) load(userptr, "glEGLImageTargetTexture2DOES"); +} +static void glad_gl_load_GL_OES_copy_image( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_copy_image) return; + glad_glCopyImageSubDataOES = (PFNGLCOPYIMAGESUBDATAOESPROC) load(userptr, "glCopyImageSubDataOES"); +} +static void glad_gl_load_GL_OES_draw_buffers_indexed( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_draw_buffers_indexed) return; + glad_glBlendEquationSeparateiOES = (PFNGLBLENDEQUATIONSEPARATEIOESPROC) load(userptr, "glBlendEquationSeparateiOES"); + glad_glBlendEquationiOES = (PFNGLBLENDEQUATIONIOESPROC) load(userptr, "glBlendEquationiOES"); + glad_glBlendFuncSeparateiOES = (PFNGLBLENDFUNCSEPARATEIOESPROC) load(userptr, "glBlendFuncSeparateiOES"); + glad_glBlendFunciOES = (PFNGLBLENDFUNCIOESPROC) load(userptr, "glBlendFunciOES"); + glad_glColorMaskiOES = (PFNGLCOLORMASKIOESPROC) load(userptr, "glColorMaskiOES"); + glad_glDisableiOES = (PFNGLDISABLEIOESPROC) load(userptr, "glDisableiOES"); + glad_glEnableiOES = (PFNGLENABLEIOESPROC) load(userptr, "glEnableiOES"); + glad_glIsEnablediOES = (PFNGLISENABLEDIOESPROC) load(userptr, "glIsEnablediOES"); +} +static void glad_gl_load_GL_OES_draw_elements_base_vertex( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_draw_elements_base_vertex) return; + glad_glDrawElementsBaseVertexOES = (PFNGLDRAWELEMENTSBASEVERTEXOESPROC) load(userptr, "glDrawElementsBaseVertexOES"); + glad_glDrawElementsInstancedBaseVertexOES = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC) load(userptr, "glDrawElementsInstancedBaseVertexOES"); + glad_glDrawRangeElementsBaseVertexOES = (PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC) load(userptr, "glDrawRangeElementsBaseVertexOES"); + glad_glMultiDrawElementsBaseVertexEXT = (PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC) load(userptr, "glMultiDrawElementsBaseVertexEXT"); +} +static void glad_gl_load_GL_OES_geometry_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_geometry_shader) return; + glad_glFramebufferTextureOES = (PFNGLFRAMEBUFFERTEXTUREOESPROC) load(userptr, "glFramebufferTextureOES"); +} +static void glad_gl_load_GL_OES_get_program_binary( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_get_program_binary) return; + glad_glGetProgramBinaryOES = (PFNGLGETPROGRAMBINARYOESPROC) load(userptr, "glGetProgramBinaryOES"); + glad_glProgramBinaryOES = (PFNGLPROGRAMBINARYOESPROC) load(userptr, "glProgramBinaryOES"); +} +static void glad_gl_load_GL_OES_mapbuffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_mapbuffer) return; + glad_glGetBufferPointervOES = (PFNGLGETBUFFERPOINTERVOESPROC) load(userptr, "glGetBufferPointervOES"); + glad_glMapBufferOES = (PFNGLMAPBUFFEROESPROC) load(userptr, "glMapBufferOES"); + glad_glUnmapBufferOES = (PFNGLUNMAPBUFFEROESPROC) load(userptr, "glUnmapBufferOES"); +} +static void glad_gl_load_GL_OES_primitive_bounding_box( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_primitive_bounding_box) return; + glad_glPrimitiveBoundingBoxOES = (PFNGLPRIMITIVEBOUNDINGBOXOESPROC) load(userptr, "glPrimitiveBoundingBoxOES"); +} +static void glad_gl_load_GL_OES_sample_shading( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_sample_shading) return; + glad_glMinSampleShadingOES = (PFNGLMINSAMPLESHADINGOESPROC) load(userptr, "glMinSampleShadingOES"); +} +static void glad_gl_load_GL_OES_tessellation_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_tessellation_shader) return; + glad_glPatchParameteriOES = (PFNGLPATCHPARAMETERIOESPROC) load(userptr, "glPatchParameteriOES"); +} +static void glad_gl_load_GL_OES_texture_3D( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_3D) return; + glad_glCompressedTexImage3DOES = (PFNGLCOMPRESSEDTEXIMAGE3DOESPROC) load(userptr, "glCompressedTexImage3DOES"); + glad_glCompressedTexSubImage3DOES = (PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC) load(userptr, "glCompressedTexSubImage3DOES"); + glad_glCopyTexSubImage3DOES = (PFNGLCOPYTEXSUBIMAGE3DOESPROC) load(userptr, "glCopyTexSubImage3DOES"); + glad_glFramebufferTexture3DOES = (PFNGLFRAMEBUFFERTEXTURE3DOESPROC) load(userptr, "glFramebufferTexture3DOES"); + glad_glTexImage3DOES = (PFNGLTEXIMAGE3DOESPROC) load(userptr, "glTexImage3DOES"); + glad_glTexSubImage3DOES = (PFNGLTEXSUBIMAGE3DOESPROC) load(userptr, "glTexSubImage3DOES"); +} +static void glad_gl_load_GL_OES_texture_border_clamp( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_border_clamp) return; + glad_glGetSamplerParameterIivOES = (PFNGLGETSAMPLERPARAMETERIIVOESPROC) load(userptr, "glGetSamplerParameterIivOES"); + glad_glGetSamplerParameterIuivOES = (PFNGLGETSAMPLERPARAMETERIUIVOESPROC) load(userptr, "glGetSamplerParameterIuivOES"); + glad_glGetTexParameterIivOES = (PFNGLGETTEXPARAMETERIIVOESPROC) load(userptr, "glGetTexParameterIivOES"); + glad_glGetTexParameterIuivOES = (PFNGLGETTEXPARAMETERIUIVOESPROC) load(userptr, "glGetTexParameterIuivOES"); + glad_glSamplerParameterIivOES = (PFNGLSAMPLERPARAMETERIIVOESPROC) load(userptr, "glSamplerParameterIivOES"); + glad_glSamplerParameterIuivOES = (PFNGLSAMPLERPARAMETERIUIVOESPROC) load(userptr, "glSamplerParameterIuivOES"); + glad_glTexParameterIivOES = (PFNGLTEXPARAMETERIIVOESPROC) load(userptr, "glTexParameterIivOES"); + glad_glTexParameterIuivOES = (PFNGLTEXPARAMETERIUIVOESPROC) load(userptr, "glTexParameterIuivOES"); +} +static void glad_gl_load_GL_OES_texture_buffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_buffer) return; + glad_glTexBufferOES = (PFNGLTEXBUFFEROESPROC) load(userptr, "glTexBufferOES"); + glad_glTexBufferRangeOES = (PFNGLTEXBUFFERRANGEOESPROC) load(userptr, "glTexBufferRangeOES"); +} +static void glad_gl_load_GL_OES_texture_storage_multisample_2d_array( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_storage_multisample_2d_array) return; + glad_glTexStorage3DMultisampleOES = (PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC) load(userptr, "glTexStorage3DMultisampleOES"); +} +static void glad_gl_load_GL_OES_texture_view( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_view) return; + glad_glTextureViewOES = (PFNGLTEXTUREVIEWOESPROC) load(userptr, "glTextureViewOES"); +} +static void glad_gl_load_GL_OES_vertex_array_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_vertex_array_object) return; + glad_glBindVertexArrayOES = (PFNGLBINDVERTEXARRAYOESPROC) load(userptr, "glBindVertexArrayOES"); + glad_glDeleteVertexArraysOES = (PFNGLDELETEVERTEXARRAYSOESPROC) load(userptr, "glDeleteVertexArraysOES"); + glad_glGenVertexArraysOES = (PFNGLGENVERTEXARRAYSOESPROC) load(userptr, "glGenVertexArraysOES"); + glad_glIsVertexArrayOES = (PFNGLISVERTEXARRAYOESPROC) load(userptr, "glIsVertexArrayOES"); +} +static void glad_gl_load_GL_OES_viewport_array( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_viewport_array) return; + glad_glDepthRangeArrayfvOES = (PFNGLDEPTHRANGEARRAYFVOESPROC) load(userptr, "glDepthRangeArrayfvOES"); + glad_glDepthRangeIndexedfOES = (PFNGLDEPTHRANGEINDEXEDFOESPROC) load(userptr, "glDepthRangeIndexedfOES"); + glad_glDisableiOES = (PFNGLDISABLEIOESPROC) load(userptr, "glDisableiOES"); + glad_glEnableiOES = (PFNGLENABLEIOESPROC) load(userptr, "glEnableiOES"); + glad_glGetFloati_vOES = (PFNGLGETFLOATI_VOESPROC) load(userptr, "glGetFloati_vOES"); + glad_glIsEnablediOES = (PFNGLISENABLEDIOESPROC) load(userptr, "glIsEnablediOES"); + glad_glScissorArrayvOES = (PFNGLSCISSORARRAYVOESPROC) load(userptr, "glScissorArrayvOES"); + glad_glScissorIndexedOES = (PFNGLSCISSORINDEXEDOESPROC) load(userptr, "glScissorIndexedOES"); + glad_glScissorIndexedvOES = (PFNGLSCISSORINDEXEDVOESPROC) load(userptr, "glScissorIndexedvOES"); + glad_glViewportArrayvOES = (PFNGLVIEWPORTARRAYVOESPROC) load(userptr, "glViewportArrayvOES"); + glad_glViewportIndexedfOES = (PFNGLVIEWPORTINDEXEDFOESPROC) load(userptr, "glViewportIndexedfOES"); + glad_glViewportIndexedfvOES = (PFNGLVIEWPORTINDEXEDFVOESPROC) load(userptr, "glViewportIndexedfvOES"); +} + + + +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define GLAD_GL_IS_SOME_NEW_VERSION 1 +#else +#define GLAD_GL_IS_SOME_NEW_VERSION 0 +#endif + +static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { +#if GLAD_GL_IS_SOME_NEW_VERSION + if(GLAD_VERSION_MAJOR(version) < 3) { +#else + GLAD_UNUSED(version); + GLAD_UNUSED(out_num_exts_i); + GLAD_UNUSED(out_exts_i); +#endif + if (glad_glGetString == NULL) { + return 0; + } + *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); +#if GLAD_GL_IS_SOME_NEW_VERSION + } else { + unsigned int index = 0; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) { + return 0; + } + glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); + if (num_exts_i > 0) { + exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); + } + if (exts_i == NULL) { + return 0; + } + for(index = 0; index < num_exts_i; index++) { + const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index); + size_t len = strlen(gl_str_tmp) + 1; + + char *local_str = (char*) malloc(len * sizeof(char)); + if(local_str != NULL) { + memcpy(local_str, gl_str_tmp, len * sizeof(char)); + } + + exts_i[index] = local_str; + } + + *out_num_exts_i = num_exts_i; + *out_exts_i = exts_i; + } +#endif + return 1; +} +static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { + if (exts_i != NULL) { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + free((void *) (exts_i[index])); + } + free((void *)exts_i); + exts_i = NULL; + } +} +static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { + if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { + const char *extensions; + const char *loc; + const char *terminator; + extensions = exts; + if(extensions == NULL || ext == NULL) { + return 0; + } + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } + } else { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + if(strcmp(e, ext) == 0) { + return 1; + } + } + } + return 0; +} + +static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_gl_find_extensions_gles2( int version) { + const char *exts = NULL; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; + + GLAD_GL_EXT_EGL_image_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_EGL_image_array"); + GLAD_GL_EXT_EGL_image_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_EGL_image_storage"); + GLAD_GL_EXT_EGL_image_storage_compression = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_EGL_image_storage_compression"); + GLAD_GL_EXT_YUV_target = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_YUV_target"); + GLAD_GL_EXT_base_instance = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_base_instance"); + GLAD_GL_EXT_blend_func_extended = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_blend_func_extended"); + GLAD_GL_EXT_blend_minmax = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_blend_minmax"); + GLAD_GL_EXT_buffer_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_buffer_storage"); + GLAD_GL_EXT_clear_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_clear_texture"); + GLAD_GL_EXT_clip_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_clip_control"); + GLAD_GL_EXT_clip_cull_distance = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_clip_cull_distance"); + GLAD_GL_EXT_color_buffer_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_color_buffer_float"); + GLAD_GL_EXT_color_buffer_half_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_color_buffer_half_float"); + GLAD_GL_EXT_conservative_depth = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_conservative_depth"); + GLAD_GL_EXT_copy_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_copy_image"); + GLAD_GL_EXT_debug_label = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_debug_label"); + GLAD_GL_EXT_debug_marker = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_debug_marker"); + GLAD_GL_EXT_depth_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_depth_clamp"); + GLAD_GL_EXT_discard_framebuffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_discard_framebuffer"); + GLAD_GL_EXT_disjoint_timer_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_disjoint_timer_query"); + GLAD_GL_EXT_draw_buffers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_buffers"); + GLAD_GL_EXT_draw_buffers_indexed = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_buffers_indexed"); + GLAD_GL_EXT_draw_elements_base_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_elements_base_vertex"); + GLAD_GL_EXT_draw_instanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_instanced"); + GLAD_GL_EXT_draw_transform_feedback = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_transform_feedback"); + GLAD_GL_EXT_external_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_external_buffer"); + GLAD_GL_EXT_float_blend = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_float_blend"); + GLAD_GL_EXT_fragment_shading_rate = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_fragment_shading_rate"); + GLAD_GL_EXT_geometry_point_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_geometry_point_size"); + GLAD_GL_EXT_geometry_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_geometry_shader"); + GLAD_GL_EXT_gpu_shader5 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_gpu_shader5"); + GLAD_GL_EXT_instanced_arrays = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_instanced_arrays"); + GLAD_GL_EXT_map_buffer_range = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_map_buffer_range"); + GLAD_GL_EXT_memory_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_memory_object"); + GLAD_GL_EXT_memory_object_fd = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_memory_object_fd"); + GLAD_GL_EXT_memory_object_win32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_memory_object_win32"); + GLAD_GL_EXT_multi_draw_arrays = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multi_draw_arrays"); + GLAD_GL_EXT_multi_draw_indirect = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multi_draw_indirect"); + GLAD_GL_EXT_multisampled_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multisampled_compatibility"); + GLAD_GL_EXT_multisampled_render_to_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multisampled_render_to_texture"); + GLAD_GL_EXT_multisampled_render_to_texture2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multisampled_render_to_texture2"); + GLAD_GL_EXT_multiview_draw_buffers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multiview_draw_buffers"); + GLAD_GL_EXT_multiview_tessellation_geometry_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multiview_tessellation_geometry_shader"); + GLAD_GL_EXT_multiview_texture_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multiview_texture_multisample"); + GLAD_GL_EXT_multiview_timer_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multiview_timer_query"); + GLAD_GL_EXT_occlusion_query_boolean = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_occlusion_query_boolean"); + GLAD_GL_EXT_polygon_offset_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_polygon_offset_clamp"); + GLAD_GL_EXT_post_depth_coverage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_post_depth_coverage"); + GLAD_GL_EXT_primitive_bounding_box = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_primitive_bounding_box"); + GLAD_GL_EXT_protected_textures = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_protected_textures"); + GLAD_GL_EXT_pvrtc_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_pvrtc_sRGB"); + GLAD_GL_EXT_raster_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_raster_multisample"); + GLAD_GL_EXT_read_format_bgra = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_read_format_bgra"); + GLAD_GL_EXT_render_snorm = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_render_snorm"); + GLAD_GL_EXT_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_robustness"); + GLAD_GL_EXT_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_sRGB"); + GLAD_GL_EXT_sRGB_write_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_sRGB_write_control"); + GLAD_GL_EXT_semaphore = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_semaphore"); + GLAD_GL_EXT_semaphore_fd = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_semaphore_fd"); + GLAD_GL_EXT_semaphore_win32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_semaphore_win32"); + GLAD_GL_EXT_separate_depth_stencil = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_separate_depth_stencil"); + GLAD_GL_EXT_separate_shader_objects = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_separate_shader_objects"); + GLAD_GL_EXT_shader_framebuffer_fetch = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_framebuffer_fetch"); + GLAD_GL_EXT_shader_framebuffer_fetch_non_coherent = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_framebuffer_fetch_non_coherent"); + GLAD_GL_EXT_shader_group_vote = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_group_vote"); + GLAD_GL_EXT_shader_implicit_conversions = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_implicit_conversions"); + GLAD_GL_EXT_shader_integer_mix = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_integer_mix"); + GLAD_GL_EXT_shader_io_blocks = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_io_blocks"); + GLAD_GL_EXT_shader_non_constant_global_initializers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_non_constant_global_initializers"); + GLAD_GL_EXT_shader_pixel_local_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_pixel_local_storage"); + GLAD_GL_EXT_shader_pixel_local_storage2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_pixel_local_storage2"); + GLAD_GL_EXT_shader_samples_identical = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_samples_identical"); + GLAD_GL_EXT_shader_texture_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_texture_lod"); + GLAD_GL_EXT_shadow_samplers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shadow_samplers"); + GLAD_GL_EXT_sparse_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_sparse_texture"); + GLAD_GL_EXT_sparse_texture2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_sparse_texture2"); + GLAD_GL_EXT_tessellation_point_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_tessellation_point_size"); + GLAD_GL_EXT_tessellation_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_tessellation_shader"); + GLAD_GL_EXT_texture_border_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_border_clamp"); + GLAD_GL_EXT_texture_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_buffer"); + GLAD_GL_EXT_texture_compression_astc_decode_mode = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_astc_decode_mode"); + GLAD_GL_EXT_texture_compression_bptc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_bptc"); + GLAD_GL_EXT_texture_compression_dxt1 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_dxt1"); + GLAD_GL_EXT_texture_compression_rgtc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_rgtc"); + GLAD_GL_EXT_texture_compression_s3tc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc"); + GLAD_GL_EXT_texture_compression_s3tc_srgb = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc_srgb"); + GLAD_GL_EXT_texture_cube_map_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_cube_map_array"); + GLAD_GL_EXT_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_filter_anisotropic"); + GLAD_GL_EXT_texture_filter_minmax = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_filter_minmax"); + GLAD_GL_EXT_texture_format_BGRA8888 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_format_BGRA8888"); + GLAD_GL_EXT_texture_format_sRGB_override = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_format_sRGB_override"); + GLAD_GL_EXT_texture_mirror_clamp_to_edge = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_mirror_clamp_to_edge"); + GLAD_GL_EXT_texture_norm16 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_norm16"); + GLAD_GL_EXT_texture_query_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_query_lod"); + GLAD_GL_EXT_texture_rg = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_rg"); + GLAD_GL_EXT_texture_sRGB_R8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_sRGB_R8"); + GLAD_GL_EXT_texture_sRGB_RG8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_sRGB_RG8"); + GLAD_GL_EXT_texture_sRGB_decode = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_sRGB_decode"); + GLAD_GL_EXT_texture_shadow_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_shadow_lod"); + GLAD_GL_EXT_texture_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_storage"); + GLAD_GL_EXT_texture_storage_compression = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_storage_compression"); + GLAD_GL_EXT_texture_type_2_10_10_10_REV = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_type_2_10_10_10_REV"); + GLAD_GL_EXT_texture_view = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_view"); + GLAD_GL_EXT_unpack_subimage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_unpack_subimage"); + GLAD_GL_EXT_win32_keyed_mutex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_win32_keyed_mutex"); + GLAD_GL_EXT_window_rectangles = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_window_rectangles"); + GLAD_GL_KHR_blend_equation_advanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_blend_equation_advanced"); + GLAD_GL_KHR_blend_equation_advanced_coherent = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_blend_equation_advanced_coherent"); + GLAD_GL_KHR_context_flush_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_context_flush_control"); + GLAD_GL_KHR_debug = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_debug"); + GLAD_GL_KHR_no_error = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_no_error"); + GLAD_GL_KHR_parallel_shader_compile = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_parallel_shader_compile"); + GLAD_GL_KHR_robust_buffer_access_behavior = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_robust_buffer_access_behavior"); + GLAD_GL_KHR_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_robustness"); + GLAD_GL_KHR_shader_subgroup = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_shader_subgroup"); + GLAD_GL_KHR_texture_compression_astc_hdr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_hdr"); + GLAD_GL_KHR_texture_compression_astc_ldr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_ldr"); + GLAD_GL_KHR_texture_compression_astc_sliced_3d = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_sliced_3d"); + GLAD_GL_OES_EGL_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_EGL_image"); + GLAD_GL_OES_EGL_image_external = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_EGL_image_external"); + GLAD_GL_OES_EGL_image_external_essl3 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_EGL_image_external_essl3"); + GLAD_GL_OES_compressed_ETC1_RGB8_sub_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_ETC1_RGB8_sub_texture"); + GLAD_GL_OES_compressed_ETC1_RGB8_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_ETC1_RGB8_texture"); + GLAD_GL_OES_compressed_paletted_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_paletted_texture"); + GLAD_GL_OES_copy_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_copy_image"); + GLAD_GL_OES_depth24 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_depth24"); + GLAD_GL_OES_depth32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_depth32"); + GLAD_GL_OES_depth_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_depth_texture"); + GLAD_GL_OES_draw_buffers_indexed = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_draw_buffers_indexed"); + GLAD_GL_OES_draw_elements_base_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_draw_elements_base_vertex"); + GLAD_GL_OES_element_index_uint = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_element_index_uint"); + GLAD_GL_OES_fbo_render_mipmap = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_fbo_render_mipmap"); + GLAD_GL_OES_fragment_precision_high = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_fragment_precision_high"); + GLAD_GL_OES_geometry_point_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_geometry_point_size"); + GLAD_GL_OES_geometry_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_geometry_shader"); + GLAD_GL_OES_get_program_binary = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_get_program_binary"); + GLAD_GL_OES_gpu_shader5 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_gpu_shader5"); + GLAD_GL_OES_mapbuffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_mapbuffer"); + GLAD_GL_OES_packed_depth_stencil = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_packed_depth_stencil"); + GLAD_GL_OES_primitive_bounding_box = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_primitive_bounding_box"); + GLAD_GL_OES_required_internalformat = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_required_internalformat"); + GLAD_GL_OES_rgb8_rgba8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_rgb8_rgba8"); + GLAD_GL_OES_sample_shading = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_sample_shading"); + GLAD_GL_OES_sample_variables = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_sample_variables"); + GLAD_GL_OES_shader_image_atomic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_shader_image_atomic"); + GLAD_GL_OES_shader_io_blocks = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_shader_io_blocks"); + GLAD_GL_OES_shader_multisample_interpolation = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_shader_multisample_interpolation"); + GLAD_GL_OES_standard_derivatives = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_standard_derivatives"); + GLAD_GL_OES_stencil1 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_stencil1"); + GLAD_GL_OES_stencil4 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_stencil4"); + GLAD_GL_OES_surfaceless_context = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_surfaceless_context"); + GLAD_GL_OES_tessellation_point_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_tessellation_point_size"); + GLAD_GL_OES_tessellation_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_tessellation_shader"); + GLAD_GL_OES_texture_3D = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_3D"); + GLAD_GL_OES_texture_border_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_border_clamp"); + GLAD_GL_OES_texture_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_buffer"); + GLAD_GL_OES_texture_compression_astc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_compression_astc"); + GLAD_GL_OES_texture_cube_map_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_cube_map_array"); + GLAD_GL_OES_texture_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_float"); + GLAD_GL_OES_texture_float_linear = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_float_linear"); + GLAD_GL_OES_texture_half_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_half_float"); + GLAD_GL_OES_texture_half_float_linear = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_half_float_linear"); + GLAD_GL_OES_texture_npot = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_npot"); + GLAD_GL_OES_texture_stencil8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_stencil8"); + GLAD_GL_OES_texture_storage_multisample_2d_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_storage_multisample_2d_array"); + GLAD_GL_OES_texture_view = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_view"); + GLAD_GL_OES_vertex_array_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_vertex_array_object"); + GLAD_GL_OES_vertex_half_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_vertex_half_float"); + GLAD_GL_OES_vertex_type_10_10_10_2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_vertex_type_10_10_10_2"); + GLAD_GL_OES_viewport_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_viewport_array"); + + glad_gl_free_extensions(exts_i, num_exts_i); + + return 1; +} + +static int glad_gl_find_core_gles2(void) { + int i; + const char* version; + const char* prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + "OpenGL SC ", + NULL + }; + int major = 0; + int minor = 0; + version = (const char*) glad_glGetString(GL_VERSION); + if (!version) return 0; + for (i = 0; prefixes[i]; i++) { + const size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } + + GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); + + GLAD_GL_ES_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr) { + int version; + + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + if(glad_glGetString == NULL) return 0; + if(glad_glGetString(GL_VERSION) == NULL) return 0; + version = glad_gl_find_core_gles2(); + + glad_gl_load_GL_ES_VERSION_2_0(load, userptr); + + if (!glad_gl_find_extensions_gles2(version)) return 0; + glad_gl_load_GL_EXT_EGL_image_storage(load, userptr); + glad_gl_load_GL_EXT_base_instance(load, userptr); + glad_gl_load_GL_EXT_blend_func_extended(load, userptr); + glad_gl_load_GL_EXT_buffer_storage(load, userptr); + glad_gl_load_GL_EXT_clear_texture(load, userptr); + glad_gl_load_GL_EXT_clip_control(load, userptr); + glad_gl_load_GL_EXT_copy_image(load, userptr); + glad_gl_load_GL_EXT_debug_label(load, userptr); + glad_gl_load_GL_EXT_debug_marker(load, userptr); + glad_gl_load_GL_EXT_discard_framebuffer(load, userptr); + glad_gl_load_GL_EXT_disjoint_timer_query(load, userptr); + glad_gl_load_GL_EXT_draw_buffers(load, userptr); + glad_gl_load_GL_EXT_draw_buffers_indexed(load, userptr); + glad_gl_load_GL_EXT_draw_elements_base_vertex(load, userptr); + glad_gl_load_GL_EXT_draw_instanced(load, userptr); + glad_gl_load_GL_EXT_draw_transform_feedback(load, userptr); + glad_gl_load_GL_EXT_external_buffer(load, userptr); + glad_gl_load_GL_EXT_fragment_shading_rate(load, userptr); + glad_gl_load_GL_EXT_geometry_shader(load, userptr); + glad_gl_load_GL_EXT_instanced_arrays(load, userptr); + glad_gl_load_GL_EXT_map_buffer_range(load, userptr); + glad_gl_load_GL_EXT_memory_object(load, userptr); + glad_gl_load_GL_EXT_memory_object_fd(load, userptr); + glad_gl_load_GL_EXT_memory_object_win32(load, userptr); + glad_gl_load_GL_EXT_multi_draw_arrays(load, userptr); + glad_gl_load_GL_EXT_multi_draw_indirect(load, userptr); + glad_gl_load_GL_EXT_multisampled_render_to_texture(load, userptr); + glad_gl_load_GL_EXT_multiview_draw_buffers(load, userptr); + glad_gl_load_GL_EXT_occlusion_query_boolean(load, userptr); + glad_gl_load_GL_EXT_polygon_offset_clamp(load, userptr); + glad_gl_load_GL_EXT_primitive_bounding_box(load, userptr); + glad_gl_load_GL_EXT_raster_multisample(load, userptr); + glad_gl_load_GL_EXT_robustness(load, userptr); + glad_gl_load_GL_EXT_semaphore(load, userptr); + glad_gl_load_GL_EXT_semaphore_fd(load, userptr); + glad_gl_load_GL_EXT_semaphore_win32(load, userptr); + glad_gl_load_GL_EXT_separate_shader_objects(load, userptr); + glad_gl_load_GL_EXT_shader_framebuffer_fetch_non_coherent(load, userptr); + glad_gl_load_GL_EXT_shader_pixel_local_storage2(load, userptr); + glad_gl_load_GL_EXT_sparse_texture(load, userptr); + glad_gl_load_GL_EXT_tessellation_shader(load, userptr); + glad_gl_load_GL_EXT_texture_border_clamp(load, userptr); + glad_gl_load_GL_EXT_texture_buffer(load, userptr); + glad_gl_load_GL_EXT_texture_storage(load, userptr); + glad_gl_load_GL_EXT_texture_storage_compression(load, userptr); + glad_gl_load_GL_EXT_texture_view(load, userptr); + glad_gl_load_GL_EXT_win32_keyed_mutex(load, userptr); + glad_gl_load_GL_EXT_window_rectangles(load, userptr); + glad_gl_load_GL_KHR_blend_equation_advanced(load, userptr); + glad_gl_load_GL_KHR_debug(load, userptr); + glad_gl_load_GL_KHR_parallel_shader_compile(load, userptr); + glad_gl_load_GL_KHR_robustness(load, userptr); + glad_gl_load_GL_OES_EGL_image(load, userptr); + glad_gl_load_GL_OES_copy_image(load, userptr); + glad_gl_load_GL_OES_draw_buffers_indexed(load, userptr); + glad_gl_load_GL_OES_draw_elements_base_vertex(load, userptr); + glad_gl_load_GL_OES_geometry_shader(load, userptr); + glad_gl_load_GL_OES_get_program_binary(load, userptr); + glad_gl_load_GL_OES_mapbuffer(load, userptr); + glad_gl_load_GL_OES_primitive_bounding_box(load, userptr); + glad_gl_load_GL_OES_sample_shading(load, userptr); + glad_gl_load_GL_OES_tessellation_shader(load, userptr); + glad_gl_load_GL_OES_texture_3D(load, userptr); + glad_gl_load_GL_OES_texture_border_clamp(load, userptr); + glad_gl_load_GL_OES_texture_buffer(load, userptr); + glad_gl_load_GL_OES_texture_storage_multisample_2d_array(load, userptr); + glad_gl_load_GL_OES_texture_view(load, userptr); + glad_gl_load_GL_OES_vertex_array_object(load, userptr); + glad_gl_load_GL_OES_viewport_array(load, userptr); + + + + return version; +} + + +int gladLoadGLES2( GLADloadfunc load) { + return gladLoadGLES2UserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* GLAD_GLES2_IMPLEMENTATION */ + diff --git a/examples/others/raylib_opengl_interop.c b/examples/others/raylib_opengl_interop.c index bda301a7e..bcdbbbdd5 100644 --- a/examples/others/raylib_opengl_interop.c +++ b/examples/others/raylib_opengl_interop.c @@ -26,21 +26,29 @@ #include "raylib.h" -#include "rlgl.h" // Required for: rlDrawRenderBatchActive(), rlGetMatrixModelview(), rlGetMatrixProjection() -#if defined(__APPLE__) - #include // OpenGL 3 library for OSX - #include // OpenGL 3 extensions library for OSX -#else - #include "glad.h" // Required for: OpenGL functionality -#endif -#include "raymath.h" // Required for: MatrixMultiply(), MatrixToFloat() - #if defined(PLATFORM_DESKTOP) - #define GLSL_VERSION 330 + #if defined(GRAPHICS_API_OPENGL_ES2) + #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__) + #include // OpenGL 3 library for OSX + #include // OpenGL 3 extensions library for OSX + #else + #include "glad.h" // Required for: OpenGL functionality + #endif + #define GLSL_VERSION 330 + #endif #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif +#include "rlgl.h" // Required for: rlDrawRenderBatchActive(), rlGetMatrixModelview(), rlGetMatrixProjection() +#include "raymath.h" // Required for: MatrixMultiply(), MatrixToFloat() + #define MAX_PARTICLES 1000 // Particle type @@ -97,7 +105,9 @@ int main(void) glBindVertexArray(0); // Allows the vertex shader to set the point size of each particle individually + #ifndef GRAPHICS_API_OPENGL_ES2 glEnable(GL_PROGRAM_POINT_SIZE); + #endif SetTargetFPS(60); //-------------------------------------------------------------------------------------- diff --git a/src/external/glad_gles2.h b/src/external/glad_gles2.h new file mode 100644 index 000000000..6c753d0a4 --- /dev/null +++ b/src/external/glad_gles2.h @@ -0,0 +1,4774 @@ +/** + * Loader generated by glad 2.0.2 on Wed Dec 28 13:28:51 2022 + * + * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 + * + * Generator: C/C++ + * Specification: gl + * Extensions: 170 + * + * APIs: + * - gles2=2.0 + * + * Options: + * - ALIAS = False + * - DEBUG = False + * - HEADER_ONLY = True + * - LOADER = False + * - MX = False + * - ON_DEMAND = False + * + * Commandline: + * --api='gles2=2.0' --extensions='GL_EXT_EGL_image_array,GL_EXT_EGL_image_storage,GL_EXT_EGL_image_storage_compression,GL_EXT_YUV_target,GL_EXT_base_instance,GL_EXT_blend_func_extended,GL_EXT_blend_minmax,GL_EXT_buffer_storage,GL_EXT_clear_texture,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_conservative_depth,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_depth_clamp,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers,GL_EXT_draw_buffers_indexed,GL_EXT_draw_elements_base_vertex,GL_EXT_draw_instanced,GL_EXT_draw_transform_feedback,GL_EXT_external_buffer,GL_EXT_float_blend,GL_EXT_fragment_shading_rate,GL_EXT_geometry_point_size,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_instanced_arrays,GL_EXT_map_buffer_range,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_memory_object_win32,GL_EXT_multi_draw_arrays,GL_EXT_multi_draw_indirect,GL_EXT_multisampled_compatibility,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_multiview_draw_buffers,GL_EXT_multiview_tessellation_geometry_shader,GL_EXT_multiview_texture_multisample,GL_EXT_multiview_timer_query,GL_EXT_occlusion_query_boolean,GL_EXT_polygon_offset_clamp,GL_EXT_post_depth_coverage,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_pvrtc_sRGB,GL_EXT_raster_multisample,GL_EXT_read_format_bgra,GL_EXT_render_snorm,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_semaphore,GL_EXT_semaphore_fd,GL_EXT_semaphore_win32,GL_EXT_separate_depth_stencil,GL_EXT_separate_shader_objects,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_framebuffer_fetch_non_coherent,GL_EXT_shader_group_vote,GL_EXT_shader_implicit_conversions,GL_EXT_shader_integer_mix,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_shader_pixel_local_storage,GL_EXT_shader_pixel_local_storage2,GL_EXT_shader_samples_identical,GL_EXT_shader_texture_lod,GL_EXT_shadow_samplers,GL_EXT_sparse_texture,GL_EXT_sparse_texture2,GL_EXT_tessellation_point_size,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_compression_astc_decode_mode,GL_EXT_texture_compression_bptc,GL_EXT_texture_compression_dxt1,GL_EXT_texture_compression_rgtc,GL_EXT_texture_compression_s3tc,GL_EXT_texture_compression_s3tc_srgb,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_filter_minmax,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_mirror_clamp_to_edge,GL_EXT_texture_norm16,GL_EXT_texture_query_lod,GL_EXT_texture_rg,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_RG8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_shadow_lod,GL_EXT_texture_storage,GL_EXT_texture_storage_compression,GL_EXT_texture_type_2_10_10_10_REV,GL_EXT_texture_view,GL_EXT_unpack_subimage,GL_EXT_win32_keyed_mutex,GL_EXT_window_rectangles,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_context_flush_control,GL_KHR_debug,GL_KHR_no_error,GL_KHR_parallel_shader_compile,GL_KHR_robust_buffer_access_behavior,GL_KHR_robustness,GL_KHR_shader_subgroup,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_KHR_texture_compression_astc_sliced_3d,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_compressed_ETC1_RGB8_sub_texture,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_copy_image,GL_OES_depth24,GL_OES_depth32,GL_OES_depth_texture,GL_OES_draw_buffers_indexed,GL_OES_draw_elements_base_vertex,GL_OES_element_index_uint,GL_OES_fbo_render_mipmap,GL_OES_fragment_precision_high,GL_OES_geometry_point_size,GL_OES_geometry_shader,GL_OES_get_program_binary,GL_OES_gpu_shader5,GL_OES_mapbuffer,GL_OES_packed_depth_stencil,GL_OES_primitive_bounding_box,GL_OES_required_internalformat,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_io_blocks,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil1,GL_OES_stencil4,GL_OES_surfaceless_context,GL_OES_tessellation_point_size,GL_OES_tessellation_shader,GL_OES_texture_3D,GL_OES_texture_border_clamp,GL_OES_texture_buffer,GL_OES_texture_compression_astc,GL_OES_texture_cube_map_array,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OES_vertex_type_10_10_10_2,GL_OES_viewport_array' c --header-only + * + * Online: + * http://glad.sh/#api=gles2%3D2.0&generator=c&options=HEADER_ONLY + * + */ + +#ifndef GLAD_GLES2_H_ +#define GLAD_GLES2_H_ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#ifdef __gl2_h_ + #error OpenGL ES 2 header already included (API: gles2), remove previous include! +#endif +#define __gl2_h_ 1 +#ifdef __gles2_gl2_h_ + #error OpenGL ES 2 header already included (API: gles2), remove previous include! +#endif +#define __gles2_gl2_h_ 1 +#ifdef __gl3_h_ + #error OpenGL ES 3 header already included (API: gles2), remove previous include! +#endif +#define __gl3_h_ 1 +#ifdef __gles2_gl3_h_ + #error OpenGL ES 3 header already included (API: gles2), remove previous include! +#endif +#define __gles2_gl3_h_ 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#define GLAD_GLES2 +#define GLAD_OPTION_GLES2_HEADER_ONLY + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GLAD_PLATFORM_H_ +#define GLAD_PLATFORM_H_ + +#ifndef GLAD_PLATFORM_WIN32 + #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) + #define GLAD_PLATFORM_WIN32 1 + #else + #define GLAD_PLATFORM_WIN32 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_APPLE + #ifdef __APPLE__ + #define GLAD_PLATFORM_APPLE 1 + #else + #define GLAD_PLATFORM_APPLE 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_EMSCRIPTEN + #ifdef __EMSCRIPTEN__ + #define GLAD_PLATFORM_EMSCRIPTEN 1 + #else + #define GLAD_PLATFORM_EMSCRIPTEN 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_UWP + #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) + #ifdef __has_include + #if __has_include() + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #endif + + #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #define GLAD_PLATFORM_UWP 1 + #endif + #endif + + #ifndef GLAD_PLATFORM_UWP + #define GLAD_PLATFORM_UWP 0 + #endif +#endif + +#ifdef __GNUC__ + #define GLAD_GNUC_EXTENSION __extension__ +#else + #define GLAD_GNUC_EXTENSION +#endif + +#define GLAD_UNUSED(x) (void)(x) + +#ifndef GLAD_API_CALL + #if defined(GLAD_API_CALL_EXPORT) + #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) + #if defined(GLAD_API_CALL_EXPORT_BUILD) + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllexport)) extern + #else + #define GLAD_API_CALL __declspec(dllexport) extern + #endif + #else + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllimport)) extern + #else + #define GLAD_API_CALL __declspec(dllimport) extern + #endif + #endif + #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) + #define GLAD_API_CALL __attribute__ ((visibility ("default"))) extern + #else + #define GLAD_API_CALL extern + #endif + #else + #define GLAD_API_CALL extern + #endif +#endif + +#ifdef APIENTRY + #define GLAD_API_PTR APIENTRY +#elif GLAD_PLATFORM_WIN32 + #define GLAD_API_PTR __stdcall +#else + #define GLAD_API_PTR +#endif + +#ifndef GLAPI +#define GLAPI GLAD_API_CALL +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY GLAD_API_PTR +#endif + +#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) +#define GLAD_VERSION_MAJOR(version) (version / 10000) +#define GLAD_VERSION_MINOR(version) (version % 10000) + +#define GLAD_GENERATOR_VERSION "2.0.2" + +typedef void (*GLADapiproc)(void); + +typedef GLADapiproc (*GLADloadfunc)(const char *name); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); + +typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); +typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); + +#endif /* GLAD_PLATFORM_H_ */ + +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_ACTIVE_PROGRAM_EXT 0x8259 +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALL_SHADER_BITS_EXT 0xFFFFFFFF +#define GL_ALPHA 0x1906 +#define GL_ALPHA16F_EXT 0x881C +#define GL_ALPHA32F_EXT 0x8816 +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA8_OES 0x803C +#define GL_ALPHA_BITS 0x0D55 +#define GL_ALWAYS 0x0207 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 0x8D6A +#define GL_ANY_SAMPLES_PASSED_EXT 0x8C2F +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_BACK 0x0405 +#define GL_BGRA8_EXT 0x93A1 +#define GL_BGRA_EXT 0x80E1 +#define GL_BLEND 0x0BE2 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLUE_BITS 0x0D54 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_BUFFER_ACCESS_OES 0x88BB +#define GL_BUFFER_IMMUTABLE_STORAGE_EXT 0x821F +#define GL_BUFFER_KHR 0x82E0 +#define GL_BUFFER_MAPPED_OES 0x88BC +#define GL_BUFFER_MAP_POINTER_OES 0x88BD +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_STORAGE_FLAGS_EXT 0x8220 +#define GL_BUFFER_USAGE 0x8765 +#define GL_BYTE 0x1400 +#define GL_CCW 0x0901 +#define GL_CLAMP_TO_BORDER_EXT 0x812D +#define GL_CLAMP_TO_BORDER_OES 0x812D +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT 0x00004000 +#define GL_CLIENT_STORAGE_BIT_EXT 0x0200 +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_CLIP_DEPTH_MODE_EXT 0x935D +#define GL_CLIP_DISTANCE0_EXT 0x3000 +#define GL_CLIP_DISTANCE1_EXT 0x3001 +#define GL_CLIP_DISTANCE2_EXT 0x3002 +#define GL_CLIP_DISTANCE3_EXT 0x3003 +#define GL_CLIP_DISTANCE4_EXT 0x3004 +#define GL_CLIP_DISTANCE5_EXT 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE6_EXT 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_CLIP_DISTANCE7_EXT 0x3007 +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_ORIGIN_EXT 0x935C +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 +#define GL_COLORBURN_KHR 0x929A +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT_EXT 0x90F0 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_EXT 0x1800 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_COMPARE_REF_TO_TEXTURE_EXT 0x884E +#define GL_COMPILE_STATUS 0x8B81 +#define GL_COMPLETION_STATUS_KHR 0x91B1 +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES 0x93C0 +#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES 0x93C1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES 0x93C2 +#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES 0x93C3 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES 0x93C4 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES 0x93C5 +#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES 0x93C6 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES 0x93C7 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES 0x93C8 +#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES 0x93C9 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_EXT 0x8E8C +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT 0x8E8F +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES 0x93E0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES 0x93E1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES 0x93E2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES 0x93E3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES 0x93E4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES 0x93E5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES 0x93E6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES 0x93E7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES 0x93E8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES 0x93E9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT 0x8E8D +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT 0x8A56 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT 0x8A57 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1 +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#define GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54 +#define GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT 0x8A55 +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 +#define GL_CONTEXT_FLAG_PROTECTED_CONTENT_BIT_EXT 0x00000010 +#define GL_CONTEXT_LOST_KHR 0x0507 +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x82FC +#define GL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x82FB +#define GL_CONTEXT_ROBUST_ACCESS_EXT 0x90F3 +#define GL_CONTEXT_ROBUST_ACCESS_KHR 0x90F3 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_CURRENT_QUERY_EXT 0x8865 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_CW 0x0900 +#define GL_D3D12_FENCE_VALUE_EXT 0x9595 +#define GL_DARKEN_KHR 0x9297 +#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245 +#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D +#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243 +#define GL_DEBUG_OUTPUT_KHR 0x92E0 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 +#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146 +#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148 +#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147 +#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B +#define GL_DEBUG_SOURCE_API_KHR 0x8246 +#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A +#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B +#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247 +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D +#define GL_DEBUG_TYPE_ERROR_KHR 0x824C +#define GL_DEBUG_TYPE_MARKER_KHR 0x8268 +#define GL_DEBUG_TYPE_OTHER_KHR 0x8251 +#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250 +#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A +#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F +#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269 +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E +#define GL_DECODE_EXT 0x8A49 +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_DEDICATED_MEMORY_OBJECT_EXT 0x9581 +#define GL_DELETE_STATUS 0x8B80 +#define GL_DEPTH24_STENCIL8_OES 0x88F0 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_DEPTH_BITS 0x0D56 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_DEPTH_CLAMP_EXT 0x864F +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT16_OES 0x81A5 +#define GL_DEPTH_COMPONENT24_OES 0x81A6 +#define GL_DEPTH_COMPONENT32_OES 0x81A7 +#define GL_DEPTH_EXT 0x1801 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_STENCIL_OES 0x84F9 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEVICE_LUID_EXT 0x9599 +#define GL_DEVICE_NODE_MASK_EXT 0x959A +#define GL_DEVICE_UUID_EXT 0x9597 +#define GL_DIFFERENCE_KHR 0x929E +#define GL_DITHER 0x0BD0 +#define GL_DONT_CARE 0x1100 +#define GL_DRAW_BUFFER0_EXT 0x8825 +#define GL_DRAW_BUFFER10_EXT 0x882F +#define GL_DRAW_BUFFER11_EXT 0x8830 +#define GL_DRAW_BUFFER12_EXT 0x8831 +#define GL_DRAW_BUFFER13_EXT 0x8832 +#define GL_DRAW_BUFFER14_EXT 0x8833 +#define GL_DRAW_BUFFER15_EXT 0x8834 +#define GL_DRAW_BUFFER1_EXT 0x8826 +#define GL_DRAW_BUFFER2_EXT 0x8827 +#define GL_DRAW_BUFFER3_EXT 0x8828 +#define GL_DRAW_BUFFER4_EXT 0x8829 +#define GL_DRAW_BUFFER5_EXT 0x882A +#define GL_DRAW_BUFFER6_EXT 0x882B +#define GL_DRAW_BUFFER7_EXT 0x882C +#define GL_DRAW_BUFFER8_EXT 0x882D +#define GL_DRAW_BUFFER9_EXT 0x882E +#define GL_DRAW_BUFFER_EXT 0x0C01 +#define GL_DRIVER_UUID_EXT 0x9598 +#define GL_DST_ALPHA 0x0304 +#define GL_DST_COLOR 0x0306 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_STORAGE_BIT_EXT 0x0100 +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_EQUAL 0x0202 +#define GL_ETC1_RGB8_OES 0x8D64 +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_EXCLUSIVE_EXT 0x8F11 +#define GL_EXTENSIONS 0x1F03 +#define GL_FALSE 0 +#define GL_FASTEST 0x1101 +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_FIRST_VERTEX_CONVENTION_OES 0x8E4D +#define GL_FIXED 0x140C +#define GL_FLOAT 0x1406 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FRACTIONAL_EVEN_EXT 0x8E7C +#define GL_FRACTIONAL_EVEN_OES 0x8E7C +#define GL_FRACTIONAL_ODD_EXT 0x8E7B +#define GL_FRACTIONAL_ODD_OES 0x8E7B +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES 0x8E5D +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FRAGMENT_SHADER_BIT_EXT 0x00000002 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES 0x8B8B +#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 +#define GL_FRAGMENT_SHADING_RATE_ATTACHMENT_WITH_DEFAULT_FRAMEBUFFER_SUPPORTED_EXT 0x96DF +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT 0x96D2 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT 0x96D5 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT 0x96D4 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT 0x96D6 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT 0x96D3 +#define GL_FRAGMENT_SHADING_RATE_NON_TRIVIAL_COMBINERS_SUPPORTED_EXT 0x8F6F +#define GL_FRAGMENT_SHADING_RATE_WITH_SAMPLE_MASK_SUPPORTED_EXT 0x96DE +#define GL_FRAGMENT_SHADING_RATE_WITH_SHADER_DEPTH_STENCIL_WRITES_SUPPORTED_EXT 0x96DD +#define GL_FRAMEBUFFER 0x8D40 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_OES 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES 0x8CD4 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT 0x8D6C +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS_OES 0x9312 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_INSUFFICIENT_SHADER_COMBINED_LOCAL_STORAGE_EXT 0x9652 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_OES 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_UNDEFINED_OES 0x8219 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_FRONT 0x0404 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_FRONT_FACE 0x0B46 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_GEOMETRY_LINKED_INPUT_TYPE_EXT 0x8917 +#define GL_GEOMETRY_LINKED_INPUT_TYPE_OES 0x8917 +#define GL_GEOMETRY_LINKED_OUTPUT_TYPE_EXT 0x8918 +#define GL_GEOMETRY_LINKED_OUTPUT_TYPE_OES 0x8918 +#define GL_GEOMETRY_LINKED_VERTICES_OUT_EXT 0x8916 +#define GL_GEOMETRY_LINKED_VERTICES_OUT_OES 0x8916 +#define GL_GEOMETRY_SHADER_BIT_EXT 0x00000004 +#define GL_GEOMETRY_SHADER_BIT_OES 0x00000004 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_SHADER_INVOCATIONS_EXT 0x887F +#define GL_GEOMETRY_SHADER_INVOCATIONS_OES 0x887F +#define GL_GEOMETRY_SHADER_OES 0x8DD9 +#define GL_GEQUAL 0x0206 +#define GL_GPU_DISJOINT_EXT 0x8FBB +#define GL_GREATER 0x0204 +#define GL_GREEN_BITS 0x0D53 +#define GL_GUILTY_CONTEXT_RESET_EXT 0x8253 +#define GL_GUILTY_CONTEXT_RESET_KHR 0x8253 +#define GL_HALF_FLOAT_OES 0x8D61 +#define GL_HANDLE_TYPE_D3D11_IMAGE_EXT 0x958B +#define GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT 0x958C +#define GL_HANDLE_TYPE_D3D12_FENCE_EXT 0x9594 +#define GL_HANDLE_TYPE_D3D12_RESOURCE_EXT 0x958A +#define GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT 0x9589 +#define GL_HANDLE_TYPE_OPAQUE_FD_EXT 0x9586 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT 0x9588 +#define GL_HARDLIGHT_KHR 0x929B +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_HIGH_INT 0x8DF5 +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_LUMINOSITY_KHR 0x92B0 +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_BUFFER_OES 0x9051 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_CUBE_MAP_ARRAY_OES 0x9054 +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_INCLUSIVE_EXT 0x8F10 +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_INNOCENT_CONTEXT_RESET_EXT 0x8254 +#define GL_INNOCENT_CONTEXT_RESET_KHR 0x8254 +#define GL_INT 0x1404 +#define GL_INT_10_10_10_2_OES 0x8DF7 +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_BUFFER_OES 0x905C +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_OES 0x905F +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910C +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_INT_SAMPLER_BUFFER_OES 0x8DD0 +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900E +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_OES 0x900E +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVERT 0x150A +#define GL_ISOLINES_EXT 0x8E7A +#define GL_ISOLINES_OES 0x8E7A +#define GL_IS_PER_PATCH_EXT 0x92E7 +#define GL_IS_PER_PATCH_OES 0x92E7 +#define GL_KEEP 0x1E00 +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_LAST_VERTEX_CONVENTION_OES 0x8E4E +#define GL_LAYER_PROVOKING_VERTEX_EXT 0x825E +#define GL_LAYER_PROVOKING_VERTEX_OES 0x825E +#define GL_LAYOUT_COLOR_ATTACHMENT_EXT 0x958E +#define GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT 0x9531 +#define GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT 0x9530 +#define GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT 0x958F +#define GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT 0x9590 +#define GL_LAYOUT_GENERAL_EXT 0x958D +#define GL_LAYOUT_SHADER_READ_ONLY_EXT 0x9591 +#define GL_LAYOUT_TRANSFER_DST_EXT 0x9593 +#define GL_LAYOUT_TRANSFER_SRC_EXT 0x9592 +#define GL_LEQUAL 0x0203 +#define GL_LESS 0x0201 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_LINEAR 0x2601 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINEAR_TILING_EXT 0x9585 +#define GL_LINES 0x0001 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINES_ADJACENCY_OES 0x000A +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_LINE_STRIP_ADJACENCY_OES 0x000B +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINK_STATUS 0x8B82 +#define GL_LOCATION_INDEX_EXT 0x930F +#define GL_LOSE_CONTEXT_ON_RESET_EXT 0x8252 +#define GL_LOSE_CONTEXT_ON_RESET_KHR 0x8252 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_LOWER_LEFT_EXT 0x8CA1 +#define GL_LOW_FLOAT 0x8DF0 +#define GL_LOW_INT 0x8DF3 +#define GL_LUID_SIZE_EXT 8 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE16F_EXT 0x881E +#define GL_LUMINANCE32F_EXT 0x8818 +#define GL_LUMINANCE4_ALPHA4_OES 0x8043 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE8_ALPHA8_OES 0x8045 +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE8_OES 0x8040 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_LUMINANCE_ALPHA16F_EXT 0x881F +#define GL_LUMINANCE_ALPHA32F_EXT 0x8819 +#define GL_MAP_COHERENT_BIT_EXT 0x0080 +#define GL_MAP_FLUSH_EXPLICIT_BIT_EXT 0x0010 +#define GL_MAP_INVALIDATE_BUFFER_BIT_EXT 0x0008 +#define GL_MAP_INVALIDATE_RANGE_BIT_EXT 0x0004 +#define GL_MAP_PERSISTENT_BIT_EXT 0x0040 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_READ_BIT_EXT 0x0001 +#define GL_MAP_UNSYNCHRONIZED_BIT_EXT 0x0020 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_WRITE_BIT_EXT 0x0002 +#define GL_MAX 0x8008 +#define GL_MAX_3D_TEXTURE_SIZE_OES 0x8073 +#define GL_MAX_CLIP_DISTANCES_EXT 0x0D32 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT 0x82FA +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8A32 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_OES 0x8A32 +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT 0x8E1E +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_OES 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT 0x8E1F +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_OES 0x8E1F +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_CULL_DISTANCES_EXT 0x82F9 +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C +#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143 +#define GL_MAX_DRAW_BUFFERS_EXT 0x8824 +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT 0x88FC +#define GL_MAX_EXT 0x8008 +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_OES 0x8E5C +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_LAYERS_EXT 0x96DC +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_ASPECT_RATIO_EXT 0x96DB +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 0x96DA +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 0x96D8 +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_MAX_FRAMEBUFFER_LAYERS_EXT 0x9317 +#define GL_MAX_FRAMEBUFFER_LAYERS_OES 0x9317 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT 0x92D5 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS_OES 0x92D5 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT 0x92CF +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_OES 0x92CF +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT 0x90CD +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS_OES 0x90CD +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT 0x9123 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS_OES 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT 0x9124 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_OES 0x9124 +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_OES 0x8DE0 +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT 0x8E5A +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS_OES 0x8E5A +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT 0x90D7 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_OES 0x90D7 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_OES 0x8C29 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_OES 0x8DE1 +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT 0x8A2C +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS_OES 0x8A2C +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_OES 0x8DDF +#define GL_MAX_LABEL_LENGTH_KHR 0x82E8 +#define GL_MAX_MULTIVIEW_BUFFERS_EXT 0x90F2 +#define GL_MAX_PATCH_VERTICES_EXT 0x8E7D +#define GL_MAX_PATCH_VERTICES_OES 0x8E7D +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_MAX_SAMPLES_EXT 0x8D57 +#define GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_FAST_SIZE_EXT 0x9650 +#define GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_SIZE_EXT 0x9651 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_FAST_SIZE_EXT 0x8F63 +#define GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT 0x8F67 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_EXT 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_EXT 0x919A +#define GL_MAX_SPARSE_TEXTURE_SIZE_EXT 0x9198 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT 0x92D3 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_OES 0x92D3 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT 0x92CD +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_OES 0x92CD +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_EXT 0x90CB +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_OES 0x90CB +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT 0x886C +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_OES 0x886C +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT 0x8E83 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_OES 0x8E83 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT 0x90D8 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_OES 0x90D8 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_EXT 0x8E81 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_OES 0x8E81 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_EXT 0x8E85 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_OES 0x8E85 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_EXT 0x8E89 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_OES 0x8E89 +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_EXT 0x8E7F +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_OES 0x8E7F +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT 0x92D4 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_OES 0x92D4 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT 0x92CE +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_OES 0x92CE +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_EXT 0x90CC +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_OES 0x90CC +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT 0x886D +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_OES 0x886D +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT 0x8E86 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_OES 0x8E86 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT 0x90D9 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_OES 0x90D9 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_EXT 0x8E82 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_OES 0x8E82 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_EXT 0x8E8A +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_OES 0x8E8A +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT 0x8E80 +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_OES 0x8E80 +#define GL_MAX_TESS_GEN_LEVEL_EXT 0x8E7E +#define GL_MAX_TESS_GEN_LEVEL_OES 0x8E7E +#define GL_MAX_TESS_PATCH_COMPONENTS_EXT 0x8E84 +#define GL_MAX_TESS_PATCH_COMPONENTS_OES 0x8E84 +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_MAX_TEXTURE_BUFFER_SIZE_OES 0x8C2B +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VIEWPORTS_OES 0x825B +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_WINDOW_RECTANGLES_EXT 0x8F14 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_MIN 0x8007 +#define GL_MIN_EXT 0x8007 +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES 0x8E5B +#define GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 0x96D9 +#define GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 0x96D7 +#define GL_MIN_SAMPLE_SHADING_VALUE_OES 0x8C37 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_MULTIVIEW_EXT 0x90F1 +#define GL_NEAREST 0x2600 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_NEGATIVE_ONE_TO_ONE_EXT 0x935E +#define GL_NEVER 0x0200 +#define GL_NICEST 0x1102 +#define GL_NONE 0 +#define GL_NOTEQUAL 0x0205 +#define GL_NO_ERROR 0 +#define GL_NO_RESET_NOTIFICATION_EXT 0x8261 +#define GL_NO_RESET_NOTIFICATION_KHR 0x8261 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_NUM_DEVICE_UUIDS_EXT 0x9596 +#define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_NUM_SPARSE_LEVELS_EXT 0x91AA +#define GL_NUM_SURFACE_COMPRESSION_FIXED_RATES_EXT 0x8F6E +#define GL_NUM_TILING_TYPES_EXT 0x9582 +#define GL_NUM_VIRTUAL_PAGE_SIZES_EXT 0x91A8 +#define GL_NUM_WINDOW_RECTANGLES_EXT 0x8F15 +#define GL_ONE 1 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_ONE_MINUS_SRC1_ALPHA_EXT 0x88FB +#define GL_ONE_MINUS_SRC1_COLOR_EXT 0x88FA +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_OPTIMAL_TILING_EXT 0x9584 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_OVERLAY_KHR 0x9296 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PATCHES_EXT 0x000E +#define GL_PATCHES_OES 0x000E +#define GL_PATCH_VERTICES_EXT 0x8E72 +#define GL_PATCH_VERTICES_OES 0x8E72 +#define GL_POINTS 0x0000 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_PRIMITIVES_GENERATED_OES 0x8C87 +#define GL_PRIMITIVE_BOUNDING_BOX_EXT 0x92BE +#define GL_PRIMITIVE_BOUNDING_BOX_OES 0x92BE +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED_OES 0x8221 +#define GL_PROGRAM_BINARY_FORMATS_OES 0x87FF +#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741 +#define GL_PROGRAM_KHR 0x82E2 +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_PROGRAM_PIPELINE_BINDING_EXT 0x825A +#define GL_PROGRAM_PIPELINE_KHR 0x82E4 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_SEPARABLE_EXT 0x8258 +#define GL_PROTECTED_MEMORY_OBJECT_EXT 0x959B +#define GL_QUADS_EXT 0x0007 +#define GL_QUADS_OES 0x0007 +#define GL_QUERY_COUNTER_BITS_EXT 0x8864 +#define GL_QUERY_KHR 0x82E3 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_QUERY_RESULT_AVAILABLE_EXT 0x8867 +#define GL_QUERY_RESULT_EXT 0x8866 +#define GL_R16F_EXT 0x822D +#define GL_R16_EXT 0x822A +#define GL_R16_SNORM_EXT 0x8F98 +#define GL_R32F_EXT 0x822E +#define GL_R8_EXT 0x8229 +#define GL_R8_SNORM 0x8F94 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_READ_BUFFER_EXT 0x0C02 +#define GL_RED_BITS 0x0D52 +#define GL_RED_EXT 0x1903 +#define GL_REFERENCED_BY_GEOMETRY_SHADER_EXT 0x9309 +#define GL_REFERENCED_BY_GEOMETRY_SHADER_OES 0x9309 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT 0x9307 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER_OES 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT 0x9308 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER_OES 0x9308 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERER 0x1F01 +#define GL_REPEAT 0x2901 +#define GL_REPLACE 0x1E01 +#define GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68 +#define GL_RESET_NOTIFICATION_STRATEGY_EXT 0x8256 +#define GL_RESET_NOTIFICATION_STRATEGY_KHR 0x8256 +#define GL_RG16F_EXT 0x822F +#define GL_RG16_EXT 0x822C +#define GL_RG16_SNORM_EXT 0x8F99 +#define GL_RG32F_EXT 0x8230 +#define GL_RG8_EXT 0x822B +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB 0x1907 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB16F_EXT 0x881B +#define GL_RGB16_EXT 0x8054 +#define GL_RGB16_SNORM_EXT 0x8F9A +#define GL_RGB32F_EXT 0x8815 +#define GL_RGB565 0x8D62 +#define GL_RGB565_OES 0x8D62 +#define GL_RGB5_A1 0x8057 +#define GL_RGB5_A1_OES 0x8057 +#define GL_RGB8_OES 0x8051 +#define GL_RGBA 0x1908 +#define GL_RGBA16F_EXT 0x881A +#define GL_RGBA16_EXT 0x805B +#define GL_RGBA16_SNORM_EXT 0x8F9B +#define GL_RGBA32F_EXT 0x8814 +#define GL_RGBA4 0x8056 +#define GL_RGBA4_OES 0x8056 +#define GL_RGBA8_OES 0x8058 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_RG_EXT 0x8227 +#define GL_SAMPLER 0x82E6 +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910B +#define GL_SAMPLER_2D_SHADOW_EXT 0x8B62 +#define GL_SAMPLER_3D_OES 0x8B5F +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_BUFFER_OES 0x8DC2 +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_OES 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_EXT 0x900D +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_OES 0x900D +#define GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT 0x8BE7 +#define GL_SAMPLER_EXTERNAL_OES 0x8D66 +#define GL_SAMPLER_KHR 0x82E6 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_SHADING_OES 0x8C36 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SCREEN_KHR 0x9295 +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_KHR 0x82E1 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_SHADER_PIXEL_LOCAL_STORAGE_EXT 0x8F64 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_TYPE 0x8B4F +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_SHADING_RATE_1X1_PIXELS_EXT 0x96A6 +#define GL_SHADING_RATE_1X1_PIXELS_QCOM 0x96A6 +#define GL_SHADING_RATE_1X2_PIXELS_EXT 0x96A7 +#define GL_SHADING_RATE_1X2_PIXELS_QCOM 0x96A7 +#define GL_SHADING_RATE_1X4_PIXELS_EXT 0x96AA +#define GL_SHADING_RATE_1X4_PIXELS_QCOM 0x96AA +#define GL_SHADING_RATE_2X1_PIXELS_EXT 0x96A8 +#define GL_SHADING_RATE_2X1_PIXELS_QCOM 0x96A8 +#define GL_SHADING_RATE_2X2_PIXELS_EXT 0x96A9 +#define GL_SHADING_RATE_2X2_PIXELS_QCOM 0x96A9 +#define GL_SHADING_RATE_2X4_PIXELS_EXT 0x96AD +#define GL_SHADING_RATE_2X4_PIXELS_QCOM 0x96AD +#define GL_SHADING_RATE_4X1_PIXELS_EXT 0x96AB +#define GL_SHADING_RATE_4X1_PIXELS_QCOM 0x96AB +#define GL_SHADING_RATE_4X2_PIXELS_EXT 0x96AC +#define GL_SHADING_RATE_4X2_PIXELS_QCOM 0x96AC +#define GL_SHADING_RATE_4X4_PIXELS_EXT 0x96AE +#define GL_SHADING_RATE_4X4_PIXELS_QCOM 0x96AE +#define GL_SHADING_RATE_ATTACHMENT_EXT 0x96D1 +#define GL_SHADING_RATE_EXT 0x96D0 +#define GL_SHORT 0x1402 +#define GL_SKIP_DECODE_EXT 0x8A4A +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_EXT 0x91A9 +#define GL_SR8_EXT 0x8FBD +#define GL_SRC1_ALPHA_EXT 0x8589 +#define GL_SRC1_COLOR_EXT 0x88F9 +#define GL_SRC_ALPHA 0x0302 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_SRC_ALPHA_SATURATE_EXT 0x0308 +#define GL_SRC_COLOR 0x0300 +#define GL_SRG8_EXT 0x8FBE +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB_EXT 0x8C40 +#define GL_STACK_OVERFLOW_KHR 0x0503 +#define GL_STACK_UNDERFLOW_KHR 0x0504 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_STENCIL_BITS 0x0D57 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_EXT 0x1802 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_INDEX1_OES 0x8D46 +#define GL_STENCIL_INDEX4_OES 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX8_OES 0x8D48 +#define GL_STENCIL_INDEX_OES 0x1901 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STREAM_DRAW 0x88E0 +#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 +#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 +#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 +#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 +#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 +#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 +#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 +#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 +#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 +#define GL_SUBGROUP_SIZE_KHR 0x9532 +#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 +#define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_SURFACE_COMPRESSION_EXT 0x96C0 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_10BPC_EXT 0x96CD +#define GL_SURFACE_COMPRESSION_FIXED_RATE_11BPC_EXT 0x96CE +#define GL_SURFACE_COMPRESSION_FIXED_RATE_12BPC_EXT 0x96CF +#define GL_SURFACE_COMPRESSION_FIXED_RATE_1BPC_EXT 0x96C4 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_2BPC_EXT 0x96C5 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_3BPC_EXT 0x96C6 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_4BPC_EXT 0x96C7 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_5BPC_EXT 0x96C8 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_6BPC_EXT 0x96C9 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_7BPC_EXT 0x96CA +#define GL_SURFACE_COMPRESSION_FIXED_RATE_8BPC_EXT 0x96CB +#define GL_SURFACE_COMPRESSION_FIXED_RATE_9BPC_EXT 0x96CC +#define GL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT 0x96C2 +#define GL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT 0x96C1 +#define GL_TESS_CONTROL_OUTPUT_VERTICES_EXT 0x8E75 +#define GL_TESS_CONTROL_OUTPUT_VERTICES_OES 0x8E75 +#define GL_TESS_CONTROL_SHADER_BIT_EXT 0x00000008 +#define GL_TESS_CONTROL_SHADER_BIT_OES 0x00000008 +#define GL_TESS_CONTROL_SHADER_EXT 0x8E88 +#define GL_TESS_CONTROL_SHADER_OES 0x8E88 +#define GL_TESS_EVALUATION_SHADER_BIT_EXT 0x00000010 +#define GL_TESS_EVALUATION_SHADER_BIT_OES 0x00000010 +#define GL_TESS_EVALUATION_SHADER_EXT 0x8E87 +#define GL_TESS_EVALUATION_SHADER_OES 0x8E87 +#define GL_TESS_GEN_MODE_EXT 0x8E76 +#define GL_TESS_GEN_MODE_OES 0x8E76 +#define GL_TESS_GEN_POINT_MODE_EXT 0x8E79 +#define GL_TESS_GEN_POINT_MODE_OES 0x8E79 +#define GL_TESS_GEN_SPACING_EXT 0x8E77 +#define GL_TESS_GEN_SPACING_OES 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER_EXT 0x8E78 +#define GL_TESS_GEN_VERTEX_ORDER_OES 0x8E78 +#define GL_TEXTURE 0x1702 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES 0x9102 +#define GL_TEXTURE_3D 0x806F +#define GL_TEXTURE_3D_OES 0x806F +#define GL_TEXTURE_ASTC_DECODE_PRECISION_EXT 0x8F69 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY_OES 0x9105 +#define GL_TEXTURE_BINDING_3D_OES 0x806A +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BINDING_BUFFER_OES 0x8C2C +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_EXT 0x900A +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_OES 0x900A +#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67 +#define GL_TEXTURE_BORDER_COLOR_EXT 0x1004 +#define GL_TEXTURE_BORDER_COLOR_OES 0x1004 +#define GL_TEXTURE_BUFFER_BINDING_EXT 0x8C2A +#define GL_TEXTURE_BUFFER_BINDING_OES 0x8C2A +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_OES 0x8C2D +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_TEXTURE_BUFFER_OES 0x8C2A +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_EXT 0x919F +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_OES 0x919F +#define GL_TEXTURE_BUFFER_OFFSET_EXT 0x919D +#define GL_TEXTURE_BUFFER_OFFSET_OES 0x919D +#define GL_TEXTURE_BUFFER_SIZE_EXT 0x919E +#define GL_TEXTURE_BUFFER_SIZE_OES 0x919E +#define GL_TEXTURE_COMPARE_FUNC_EXT 0x884D +#define GL_TEXTURE_COMPARE_MODE_EXT 0x884C +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_ARRAY_EXT 0x9009 +#define GL_TEXTURE_CUBE_MAP_ARRAY_OES 0x9009 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#define GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT 0x8FBF +#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_PROTECTED_EXT 0x8BFA +#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 +#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366 +#define GL_TEXTURE_SPARSE_EXT 0x91A6 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_TEXTURE_TILING_EXT 0x9580 +#define GL_TEXTURE_VIEW_MIN_LAYER_EXT 0x82DD +#define GL_TEXTURE_VIEW_MIN_LAYER_OES 0x82DD +#define GL_TEXTURE_VIEW_MIN_LEVEL_EXT 0x82DB +#define GL_TEXTURE_VIEW_MIN_LEVEL_OES 0x82DB +#define GL_TEXTURE_VIEW_NUM_LAYERS_EXT 0x82DE +#define GL_TEXTURE_VIEW_NUM_LAYERS_OES 0x82DE +#define GL_TEXTURE_VIEW_NUM_LEVELS_EXT 0x82DC +#define GL_TEXTURE_VIEW_NUM_LEVELS_OES 0x82DC +#define GL_TEXTURE_WRAP_R_OES 0x8072 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TILING_TYPES_EXT 0x9583 +#define GL_TIMESTAMP_EXT 0x8E28 +#define GL_TIME_ELAPSED_EXT 0x88BF +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLES_ADJACENCY_OES 0x000C +#define GL_TRIANGLE_FAN 0x0006 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_TRIANGLE_STRIP_ADJACENCY_OES 0x000D +#define GL_TRUE 1 +#define GL_UNDEFINED_VERTEX_EXT 0x8260 +#define GL_UNDEFINED_VERTEX_OES 0x8260 +#define GL_UNKNOWN_CONTEXT_RESET_EXT 0x8255 +#define GL_UNKNOWN_CONTEXT_RESET_KHR 0x8255 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNPACK_ROW_LENGTH_EXT 0x0CF2 +#define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4 +#define GL_UNPACK_SKIP_ROWS_EXT 0x0CF3 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_INT_10_10_10_2_OES 0x8DF6 +#define GL_UNSIGNED_INT_24_8_OES 0x84FA +#define GL_UNSIGNED_INT_2_10_10_10_REV_EXT 0x8368 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_OES 0x9067 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_OES 0x906A +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910D +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_OES 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900F +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_OES 0x900F +#define GL_UNSIGNED_NORMALIZED_EXT 0x8C17 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_UPPER_LEFT_EXT 0x8CA2 +#define GL_UUID_SIZE_EXT 16 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_VENDOR 0x1F00 +#define GL_VERSION 0x1F02 +#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5 +#define GL_VERTEX_ARRAY_KHR 0x8074 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_EXT 0x88FE +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_VERTEX_SHADER_BIT_EXT 0x00000001 +#define GL_VIEWPORT 0x0BA2 +#define GL_VIEWPORT_BOUNDS_RANGE_OES 0x825D +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX_OES 0x825F +#define GL_VIEWPORT_SUBPIXEL_BITS_OES 0x825C +#define GL_VIRTUAL_PAGE_SIZE_INDEX_EXT 0x91A7 +#define GL_VIRTUAL_PAGE_SIZE_X_EXT 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_EXT 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_EXT 0x9197 +#define GL_WEIGHTED_AVERAGE_ARB 0x9367 +#define GL_WEIGHTED_AVERAGE_EXT 0x9367 +#define GL_WINDOW_RECTANGLE_EXT 0x8F12 +#define GL_WINDOW_RECTANGLE_MODE_EXT 0x8F13 +#define GL_WRITE_ONLY_OES 0x88B9 +#define GL_ZERO 0 +#define GL_ZERO_TO_ONE 0x935F +#define GL_ZERO_TO_ONE_EXT 0x935F + + +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are 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 Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_GLAD_API_PTR + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_GLAD_API_PTR funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_GLAD_API_PTR + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_GLAD_API_PTR __stdcall +#else +# define KHRONOS_GLAD_API_PTR +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef khronos_int8_t GLbyte; +typedef khronos_uint8_t GLubyte; +typedef khronos_int16_t GLshort; +typedef khronos_uint16_t GLushort; +typedef int GLint; +typedef unsigned int GLuint; +typedef khronos_int32_t GLclampx; +typedef int GLsizei; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef double GLdouble; +typedef double GLclampd; +typedef void *GLeglClientBufferEXT; +typedef void *GLeglImageOES; +typedef char GLchar; +typedef char GLcharARB; +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef khronos_uint16_t GLhalf; +typedef khronos_uint16_t GLhalfARB; +typedef khronos_int32_t GLfixed; +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptr; +#else +typedef khronos_intptr_t GLintptr; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptrARB; +#else +typedef khronos_intptr_t GLintptrARB; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptr; +#else +typedef khronos_ssize_t GLsizeiptr; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptrARB; +#else +typedef khronos_ssize_t GLsizeiptrARB; +#endif +typedef khronos_int64_t GLint64; +typedef khronos_int64_t GLint64EXT; +typedef khronos_uint64_t GLuint64; +typedef khronos_uint64_t GLuint64EXT; +typedef struct __GLsync *GLsync; +struct _cl_context; +struct _cl_event; +typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +typedef unsigned short GLhalfNV; +typedef GLintptr GLvdpauSurfaceNV; +typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); + + +#define GL_ES_VERSION_2_0 1 +GLAD_API_CALL int GLAD_GL_ES_VERSION_2_0; +#define GL_EXT_EGL_image_array 1 +GLAD_API_CALL int GLAD_GL_EXT_EGL_image_array; +#define GL_EXT_EGL_image_storage 1 +GLAD_API_CALL int GLAD_GL_EXT_EGL_image_storage; +#define GL_EXT_EGL_image_storage_compression 1 +GLAD_API_CALL int GLAD_GL_EXT_EGL_image_storage_compression; +#define GL_EXT_YUV_target 1 +GLAD_API_CALL int GLAD_GL_EXT_YUV_target; +#define GL_EXT_base_instance 1 +GLAD_API_CALL int GLAD_GL_EXT_base_instance; +#define GL_EXT_blend_func_extended 1 +GLAD_API_CALL int GLAD_GL_EXT_blend_func_extended; +#define GL_EXT_blend_minmax 1 +GLAD_API_CALL int GLAD_GL_EXT_blend_minmax; +#define GL_EXT_buffer_storage 1 +GLAD_API_CALL int GLAD_GL_EXT_buffer_storage; +#define GL_EXT_clear_texture 1 +GLAD_API_CALL int GLAD_GL_EXT_clear_texture; +#define GL_EXT_clip_control 1 +GLAD_API_CALL int GLAD_GL_EXT_clip_control; +#define GL_EXT_clip_cull_distance 1 +GLAD_API_CALL int GLAD_GL_EXT_clip_cull_distance; +#define GL_EXT_color_buffer_float 1 +GLAD_API_CALL int GLAD_GL_EXT_color_buffer_float; +#define GL_EXT_color_buffer_half_float 1 +GLAD_API_CALL int GLAD_GL_EXT_color_buffer_half_float; +#define GL_EXT_conservative_depth 1 +GLAD_API_CALL int GLAD_GL_EXT_conservative_depth; +#define GL_EXT_copy_image 1 +GLAD_API_CALL int GLAD_GL_EXT_copy_image; +#define GL_EXT_debug_label 1 +GLAD_API_CALL int GLAD_GL_EXT_debug_label; +#define GL_EXT_debug_marker 1 +GLAD_API_CALL int GLAD_GL_EXT_debug_marker; +#define GL_EXT_depth_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_depth_clamp; +#define GL_EXT_discard_framebuffer 1 +GLAD_API_CALL int GLAD_GL_EXT_discard_framebuffer; +#define GL_EXT_disjoint_timer_query 1 +GLAD_API_CALL int GLAD_GL_EXT_disjoint_timer_query; +#define GL_EXT_draw_buffers 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_buffers; +#define GL_EXT_draw_buffers_indexed 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_buffers_indexed; +#define GL_EXT_draw_elements_base_vertex 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_elements_base_vertex; +#define GL_EXT_draw_instanced 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_instanced; +#define GL_EXT_draw_transform_feedback 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_transform_feedback; +#define GL_EXT_external_buffer 1 +GLAD_API_CALL int GLAD_GL_EXT_external_buffer; +#define GL_EXT_float_blend 1 +GLAD_API_CALL int GLAD_GL_EXT_float_blend; +#define GL_EXT_fragment_shading_rate 1 +GLAD_API_CALL int GLAD_GL_EXT_fragment_shading_rate; +#define GL_EXT_geometry_point_size 1 +GLAD_API_CALL int GLAD_GL_EXT_geometry_point_size; +#define GL_EXT_geometry_shader 1 +GLAD_API_CALL int GLAD_GL_EXT_geometry_shader; +#define GL_EXT_gpu_shader5 1 +GLAD_API_CALL int GLAD_GL_EXT_gpu_shader5; +#define GL_EXT_instanced_arrays 1 +GLAD_API_CALL int GLAD_GL_EXT_instanced_arrays; +#define GL_EXT_map_buffer_range 1 +GLAD_API_CALL int GLAD_GL_EXT_map_buffer_range; +#define GL_EXT_memory_object 1 +GLAD_API_CALL int GLAD_GL_EXT_memory_object; +#define GL_EXT_memory_object_fd 1 +GLAD_API_CALL int GLAD_GL_EXT_memory_object_fd; +#define GL_EXT_memory_object_win32 1 +GLAD_API_CALL int GLAD_GL_EXT_memory_object_win32; +#define GL_EXT_multi_draw_arrays 1 +GLAD_API_CALL int GLAD_GL_EXT_multi_draw_arrays; +#define GL_EXT_multi_draw_indirect 1 +GLAD_API_CALL int GLAD_GL_EXT_multi_draw_indirect; +#define GL_EXT_multisampled_compatibility 1 +GLAD_API_CALL int GLAD_GL_EXT_multisampled_compatibility; +#define GL_EXT_multisampled_render_to_texture 1 +GLAD_API_CALL int GLAD_GL_EXT_multisampled_render_to_texture; +#define GL_EXT_multisampled_render_to_texture2 1 +GLAD_API_CALL int GLAD_GL_EXT_multisampled_render_to_texture2; +#define GL_EXT_multiview_draw_buffers 1 +GLAD_API_CALL int GLAD_GL_EXT_multiview_draw_buffers; +#define GL_EXT_multiview_tessellation_geometry_shader 1 +GLAD_API_CALL int GLAD_GL_EXT_multiview_tessellation_geometry_shader; +#define GL_EXT_multiview_texture_multisample 1 +GLAD_API_CALL int GLAD_GL_EXT_multiview_texture_multisample; +#define GL_EXT_multiview_timer_query 1 +GLAD_API_CALL int GLAD_GL_EXT_multiview_timer_query; +#define GL_EXT_occlusion_query_boolean 1 +GLAD_API_CALL int GLAD_GL_EXT_occlusion_query_boolean; +#define GL_EXT_polygon_offset_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_polygon_offset_clamp; +#define GL_EXT_post_depth_coverage 1 +GLAD_API_CALL int GLAD_GL_EXT_post_depth_coverage; +#define GL_EXT_primitive_bounding_box 1 +GLAD_API_CALL int GLAD_GL_EXT_primitive_bounding_box; +#define GL_EXT_protected_textures 1 +GLAD_API_CALL int GLAD_GL_EXT_protected_textures; +#define GL_EXT_pvrtc_sRGB 1 +GLAD_API_CALL int GLAD_GL_EXT_pvrtc_sRGB; +#define GL_EXT_raster_multisample 1 +GLAD_API_CALL int GLAD_GL_EXT_raster_multisample; +#define GL_EXT_read_format_bgra 1 +GLAD_API_CALL int GLAD_GL_EXT_read_format_bgra; +#define GL_EXT_render_snorm 1 +GLAD_API_CALL int GLAD_GL_EXT_render_snorm; +#define GL_EXT_robustness 1 +GLAD_API_CALL int GLAD_GL_EXT_robustness; +#define GL_EXT_sRGB 1 +GLAD_API_CALL int GLAD_GL_EXT_sRGB; +#define GL_EXT_sRGB_write_control 1 +GLAD_API_CALL int GLAD_GL_EXT_sRGB_write_control; +#define GL_EXT_semaphore 1 +GLAD_API_CALL int GLAD_GL_EXT_semaphore; +#define GL_EXT_semaphore_fd 1 +GLAD_API_CALL int GLAD_GL_EXT_semaphore_fd; +#define GL_EXT_semaphore_win32 1 +GLAD_API_CALL int GLAD_GL_EXT_semaphore_win32; +#define GL_EXT_separate_depth_stencil 1 +GLAD_API_CALL int GLAD_GL_EXT_separate_depth_stencil; +#define GL_EXT_separate_shader_objects 1 +GLAD_API_CALL int GLAD_GL_EXT_separate_shader_objects; +#define GL_EXT_shader_framebuffer_fetch 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_framebuffer_fetch; +#define GL_EXT_shader_framebuffer_fetch_non_coherent 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_framebuffer_fetch_non_coherent; +#define GL_EXT_shader_group_vote 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_group_vote; +#define GL_EXT_shader_implicit_conversions 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_implicit_conversions; +#define GL_EXT_shader_integer_mix 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_integer_mix; +#define GL_EXT_shader_io_blocks 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_io_blocks; +#define GL_EXT_shader_non_constant_global_initializers 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_non_constant_global_initializers; +#define GL_EXT_shader_pixel_local_storage 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_pixel_local_storage; +#define GL_EXT_shader_pixel_local_storage2 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_pixel_local_storage2; +#define GL_EXT_shader_samples_identical 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_samples_identical; +#define GL_EXT_shader_texture_lod 1 +GLAD_API_CALL int GLAD_GL_EXT_shader_texture_lod; +#define GL_EXT_shadow_samplers 1 +GLAD_API_CALL int GLAD_GL_EXT_shadow_samplers; +#define GL_EXT_sparse_texture 1 +GLAD_API_CALL int GLAD_GL_EXT_sparse_texture; +#define GL_EXT_sparse_texture2 1 +GLAD_API_CALL int GLAD_GL_EXT_sparse_texture2; +#define GL_EXT_tessellation_point_size 1 +GLAD_API_CALL int GLAD_GL_EXT_tessellation_point_size; +#define GL_EXT_tessellation_shader 1 +GLAD_API_CALL int GLAD_GL_EXT_tessellation_shader; +#define GL_EXT_texture_border_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_border_clamp; +#define GL_EXT_texture_buffer 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_buffer; +#define GL_EXT_texture_compression_astc_decode_mode 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_astc_decode_mode; +#define GL_EXT_texture_compression_bptc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_bptc; +#define GL_EXT_texture_compression_dxt1 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_dxt1; +#define GL_EXT_texture_compression_rgtc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_rgtc; +#define GL_EXT_texture_compression_s3tc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc; +#define GL_EXT_texture_compression_s3tc_srgb 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc_srgb; +#define GL_EXT_texture_cube_map_array 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_cube_map_array; +#define GL_EXT_texture_filter_anisotropic 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_filter_anisotropic; +#define GL_EXT_texture_filter_minmax 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_filter_minmax; +#define GL_EXT_texture_format_BGRA8888 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_format_BGRA8888; +#define GL_EXT_texture_format_sRGB_override 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_format_sRGB_override; +#define GL_EXT_texture_mirror_clamp_to_edge 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_mirror_clamp_to_edge; +#define GL_EXT_texture_norm16 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_norm16; +#define GL_EXT_texture_query_lod 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_query_lod; +#define GL_EXT_texture_rg 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_rg; +#define GL_EXT_texture_sRGB_R8 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_sRGB_R8; +#define GL_EXT_texture_sRGB_RG8 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_sRGB_RG8; +#define GL_EXT_texture_sRGB_decode 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_sRGB_decode; +#define GL_EXT_texture_shadow_lod 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_shadow_lod; +#define GL_EXT_texture_storage 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_storage; +#define GL_EXT_texture_storage_compression 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_storage_compression; +#define GL_EXT_texture_type_2_10_10_10_REV 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_type_2_10_10_10_REV; +#define GL_EXT_texture_view 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_view; +#define GL_EXT_unpack_subimage 1 +GLAD_API_CALL int GLAD_GL_EXT_unpack_subimage; +#define GL_EXT_win32_keyed_mutex 1 +GLAD_API_CALL int GLAD_GL_EXT_win32_keyed_mutex; +#define GL_EXT_window_rectangles 1 +GLAD_API_CALL int GLAD_GL_EXT_window_rectangles; +#define GL_KHR_blend_equation_advanced 1 +GLAD_API_CALL int GLAD_GL_KHR_blend_equation_advanced; +#define GL_KHR_blend_equation_advanced_coherent 1 +GLAD_API_CALL int GLAD_GL_KHR_blend_equation_advanced_coherent; +#define GL_KHR_context_flush_control 1 +GLAD_API_CALL int GLAD_GL_KHR_context_flush_control; +#define GL_KHR_debug 1 +GLAD_API_CALL int GLAD_GL_KHR_debug; +#define GL_KHR_no_error 1 +GLAD_API_CALL int GLAD_GL_KHR_no_error; +#define GL_KHR_parallel_shader_compile 1 +GLAD_API_CALL int GLAD_GL_KHR_parallel_shader_compile; +#define GL_KHR_robust_buffer_access_behavior 1 +GLAD_API_CALL int GLAD_GL_KHR_robust_buffer_access_behavior; +#define GL_KHR_robustness 1 +GLAD_API_CALL int GLAD_GL_KHR_robustness; +#define GL_KHR_shader_subgroup 1 +GLAD_API_CALL int GLAD_GL_KHR_shader_subgroup; +#define GL_KHR_texture_compression_astc_hdr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_hdr; +#define GL_KHR_texture_compression_astc_ldr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_ldr; +#define GL_KHR_texture_compression_astc_sliced_3d 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_sliced_3d; +#define GL_OES_EGL_image 1 +GLAD_API_CALL int GLAD_GL_OES_EGL_image; +#define GL_OES_EGL_image_external 1 +GLAD_API_CALL int GLAD_GL_OES_EGL_image_external; +#define GL_OES_EGL_image_external_essl3 1 +GLAD_API_CALL int GLAD_GL_OES_EGL_image_external_essl3; +#define GL_OES_compressed_ETC1_RGB8_sub_texture 1 +GLAD_API_CALL int GLAD_GL_OES_compressed_ETC1_RGB8_sub_texture; +#define GL_OES_compressed_ETC1_RGB8_texture 1 +GLAD_API_CALL int GLAD_GL_OES_compressed_ETC1_RGB8_texture; +#define GL_OES_compressed_paletted_texture 1 +GLAD_API_CALL int GLAD_GL_OES_compressed_paletted_texture; +#define GL_OES_copy_image 1 +GLAD_API_CALL int GLAD_GL_OES_copy_image; +#define GL_OES_depth24 1 +GLAD_API_CALL int GLAD_GL_OES_depth24; +#define GL_OES_depth32 1 +GLAD_API_CALL int GLAD_GL_OES_depth32; +#define GL_OES_depth_texture 1 +GLAD_API_CALL int GLAD_GL_OES_depth_texture; +#define GL_OES_draw_buffers_indexed 1 +GLAD_API_CALL int GLAD_GL_OES_draw_buffers_indexed; +#define GL_OES_draw_elements_base_vertex 1 +GLAD_API_CALL int GLAD_GL_OES_draw_elements_base_vertex; +#define GL_OES_element_index_uint 1 +GLAD_API_CALL int GLAD_GL_OES_element_index_uint; +#define GL_OES_fbo_render_mipmap 1 +GLAD_API_CALL int GLAD_GL_OES_fbo_render_mipmap; +#define GL_OES_fragment_precision_high 1 +GLAD_API_CALL int GLAD_GL_OES_fragment_precision_high; +#define GL_OES_geometry_point_size 1 +GLAD_API_CALL int GLAD_GL_OES_geometry_point_size; +#define GL_OES_geometry_shader 1 +GLAD_API_CALL int GLAD_GL_OES_geometry_shader; +#define GL_OES_get_program_binary 1 +GLAD_API_CALL int GLAD_GL_OES_get_program_binary; +#define GL_OES_gpu_shader5 1 +GLAD_API_CALL int GLAD_GL_OES_gpu_shader5; +#define GL_OES_mapbuffer 1 +GLAD_API_CALL int GLAD_GL_OES_mapbuffer; +#define GL_OES_packed_depth_stencil 1 +GLAD_API_CALL int GLAD_GL_OES_packed_depth_stencil; +#define GL_OES_primitive_bounding_box 1 +GLAD_API_CALL int GLAD_GL_OES_primitive_bounding_box; +#define GL_OES_required_internalformat 1 +GLAD_API_CALL int GLAD_GL_OES_required_internalformat; +#define GL_OES_rgb8_rgba8 1 +GLAD_API_CALL int GLAD_GL_OES_rgb8_rgba8; +#define GL_OES_sample_shading 1 +GLAD_API_CALL int GLAD_GL_OES_sample_shading; +#define GL_OES_sample_variables 1 +GLAD_API_CALL int GLAD_GL_OES_sample_variables; +#define GL_OES_shader_image_atomic 1 +GLAD_API_CALL int GLAD_GL_OES_shader_image_atomic; +#define GL_OES_shader_io_blocks 1 +GLAD_API_CALL int GLAD_GL_OES_shader_io_blocks; +#define GL_OES_shader_multisample_interpolation 1 +GLAD_API_CALL int GLAD_GL_OES_shader_multisample_interpolation; +#define GL_OES_standard_derivatives 1 +GLAD_API_CALL int GLAD_GL_OES_standard_derivatives; +#define GL_OES_stencil1 1 +GLAD_API_CALL int GLAD_GL_OES_stencil1; +#define GL_OES_stencil4 1 +GLAD_API_CALL int GLAD_GL_OES_stencil4; +#define GL_OES_surfaceless_context 1 +GLAD_API_CALL int GLAD_GL_OES_surfaceless_context; +#define GL_OES_tessellation_point_size 1 +GLAD_API_CALL int GLAD_GL_OES_tessellation_point_size; +#define GL_OES_tessellation_shader 1 +GLAD_API_CALL int GLAD_GL_OES_tessellation_shader; +#define GL_OES_texture_3D 1 +GLAD_API_CALL int GLAD_GL_OES_texture_3D; +#define GL_OES_texture_border_clamp 1 +GLAD_API_CALL int GLAD_GL_OES_texture_border_clamp; +#define GL_OES_texture_buffer 1 +GLAD_API_CALL int GLAD_GL_OES_texture_buffer; +#define GL_OES_texture_compression_astc 1 +GLAD_API_CALL int GLAD_GL_OES_texture_compression_astc; +#define GL_OES_texture_cube_map_array 1 +GLAD_API_CALL int GLAD_GL_OES_texture_cube_map_array; +#define GL_OES_texture_float 1 +GLAD_API_CALL int GLAD_GL_OES_texture_float; +#define GL_OES_texture_float_linear 1 +GLAD_API_CALL int GLAD_GL_OES_texture_float_linear; +#define GL_OES_texture_half_float 1 +GLAD_API_CALL int GLAD_GL_OES_texture_half_float; +#define GL_OES_texture_half_float_linear 1 +GLAD_API_CALL int GLAD_GL_OES_texture_half_float_linear; +#define GL_OES_texture_npot 1 +GLAD_API_CALL int GLAD_GL_OES_texture_npot; +#define GL_OES_texture_stencil8 1 +GLAD_API_CALL int GLAD_GL_OES_texture_stencil8; +#define GL_OES_texture_storage_multisample_2d_array 1 +GLAD_API_CALL int GLAD_GL_OES_texture_storage_multisample_2d_array; +#define GL_OES_texture_view 1 +GLAD_API_CALL int GLAD_GL_OES_texture_view; +#define GL_OES_vertex_array_object 1 +GLAD_API_CALL int GLAD_GL_OES_vertex_array_object; +#define GL_OES_vertex_half_float 1 +GLAD_API_CALL int GLAD_GL_OES_vertex_half_float; +#define GL_OES_vertex_type_10_10_10_2 1 +GLAD_API_CALL int GLAD_GL_OES_vertex_type_10_10_10_2; +#define GL_OES_viewport_array 1 +GLAD_API_CALL int GLAD_GL_OES_viewport_array; + + +typedef GLboolean (GLAD_API_PTR *PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC)(GLuint memory, GLuint64 key, GLuint timeout); +typedef void (GLAD_API_PTR *PFNGLACTIVESHADERPROGRAMEXTPROC)(GLuint pipeline, GLuint program); +typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); +typedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLBEGINQUERYEXTPROC)(GLenum target, GLuint id); +typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONEXTPROC)(GLuint program, GLuint color, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONINDEXEDEXTPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +typedef void (GLAD_API_PTR *PFNGLBINDPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +typedef void (GLAD_API_PTR *PFNGLBINDVERTEXARRAYOESPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLBLENDBARRIERKHRPROC)(void); +typedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIEXTPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIOESPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONIEXTPROC)(GLuint buf, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONIOESPROC)(GLuint buf, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIEXTPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIOESPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIEXTPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIOESPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); +typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEEXTPROC)(GLenum target, GLsizeiptr size, const void * data, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEEXTERNALEXTPROC)(GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEMEMEXTPROC)(GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); +typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHFPROC)(GLfloat d); +typedef void (GLAD_API_PTR *PFNGLCLEARPIXELLOCALSTORAGEUIEXTPROC)(GLsizei offset, GLsizei n, const GLuint * values); +typedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); +typedef void (GLAD_API_PTR *PFNGLCLEARTEXIMAGEEXTPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLEARTEXSUBIMAGEEXTPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLIPCONTROLEXTPROC)(GLenum origin, GLenum depth); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKIEXTPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKIOESPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DOESPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOPYIMAGESUBDATAEXTPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (GLAD_API_PTR *PFNGLCOPYIMAGESUBDATAOESPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DOESPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCREATEMEMORYOBJECTSEXTPROC)(GLsizei n, GLuint * memoryObjects); +typedef GLuint (GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROGRAMVEXTPROC)(GLenum type, GLsizei count, const GLchar *const* strings); +typedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void * userParam); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); +typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLDELETEMEMORYOBJECTSEXTPROC)(GLsizei n, const GLuint * memoryObjects); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPIPELINESEXTPROC)(GLsizei n, const GLuint * pipelines); +typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESEXTPROC)(GLsizei n, const GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLDELETESEMAPHORESEXTPROC)(GLsizei n, const GLuint * semaphores); +typedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSOESPROC)(GLsizei n, const GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); +typedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEARRAYFVOESPROC)(GLuint first, GLsizei count, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEINDEXEDFOESPROC)(GLuint index, GLfloat n, GLfloat f); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f); +typedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEIEXTPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEIOESPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISCARDFRAMEBUFFEREXTPROC)(GLenum target, GLsizei numAttachments, const GLenum * attachments); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDEXTPROC)(GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSEXTPROC)(GLsizei n, const GLenum * bufs); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSINDEXEDEXTPROC)(GLint n, const GLenum * location, const GLint * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXOESPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKEXTPROC)(GLenum mode, GLuint id); +typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDEXTPROC)(GLenum mode, GLuint id, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC)(GLenum target, GLeglImageOES image); +typedef void (GLAD_API_PTR *PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC)(GLenum target, GLeglImageOES image, const GLint * attrib_list); +typedef void (GLAD_API_PTR *PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum target, GLeglImageOES image); +typedef void (GLAD_API_PTR *PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC)(GLuint texture, GLeglImageOES image, const GLint * attrib_list); +typedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEIEXTPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEIOESPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLENDQUERYEXTPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLFINISHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEEXTPROC)(GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC)(GLuint target, GLsizei size); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERSHADINGRATEEXTPROC)(GLenum target, GLenum attachment, GLuint texture, GLint baseLayer, GLsizei numLayers, GLsizei texelWidth, GLsizei texelHeight); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DOESPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREEXTPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREOESPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLGENPROGRAMPIPELINESEXTPROC)(GLsizei n, GLuint * pipelines); +typedef void (GLAD_API_PTR *PFNGLGENQUERIESEXTPROC)(GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLGENSEMAPHORESEXTPROC)(GLsizei n, GLuint * semaphores); +typedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLGENVERTEXARRAYSOESPROC)(GLsizei n, GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); +typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVOESPROC)(GLenum target, GLenum pname, void ** params); +typedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); +typedef GLenum (GLAD_API_PTR *PFNGLGETERRORPROC)(void); +typedef void (GLAD_API_PTR *PFNGLGETFLOATI_VOESPROC)(GLenum target, GLuint index, GLfloat * data); +typedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); +typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATAINDEXEXTPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETFRAGMENTSHADINGRATESEXTPROC)(GLsizei samples, GLsizei maxCount, GLsizei * count, GLenum * shadingRates); +typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef GLsizei (GLAD_API_PTR *PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC)(GLuint target); +typedef GLenum (GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSEXTPROC)(void); +typedef GLenum (GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void); +typedef void (GLAD_API_PTR *PFNGLGETINTEGER64VEXTPROC)(GLenum pname, GLint64 * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERI_VEXTPROC)(GLenum target, GLuint index, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC)(GLuint memoryObject, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELEXTPROC)(GLenum type, GLuint object, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTPTRLABELKHRPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETPOINTERVKHRPROC)(GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMBINARYOESPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLenum * binaryFormat, void * binary); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC)(GLuint pipeline, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEIVEXTPROC)(GLuint pipeline, GLenum pname, GLint * params); +typedef GLint (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC)(GLuint program, GLenum programInterface, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VEXTPROC)(GLuint id, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVEXTPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUI64VEXTPROC)(GLuint id, GLenum pname, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVEXTPROC)(GLuint id, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVEXTPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVOESPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVEXTPROC)(GLuint sampler, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVOESPROC)(GLuint sampler, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC)(GLuint semaphore, GLenum pname, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision); +typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); +typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVOESPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVEXTPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVOESPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETUNSIGNEDBYTEI_VEXTPROC)(GLenum target, GLuint index, GLubyte * data); +typedef void (GLAD_API_PTR *PFNGLGETUNSIGNEDBYTEVEXTPROC)(GLenum pname, GLubyte * data); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVEXTPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVEXTPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUIVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLIMPORTMEMORYFDEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, GLint fd); +typedef void (GLAD_API_PTR *PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, void * handle); +typedef void (GLAD_API_PTR *PFNGLIMPORTMEMORYWIN32NAMEEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, const void * name); +typedef void (GLAD_API_PTR *PFNGLIMPORTSEMAPHOREFDEXTPROC)(GLuint semaphore, GLenum handleType, GLint fd); +typedef void (GLAD_API_PTR *PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC)(GLuint semaphore, GLenum handleType, void * handle); +typedef void (GLAD_API_PTR *PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC)(GLuint semaphore, GLenum handleType, const void * name); +typedef void (GLAD_API_PTR *PFNGLINSERTEVENTMARKEREXTPROC)(GLsizei length, const GLchar * marker); +typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIEXTPROC)(GLenum target, GLuint index); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIOESPROC)(GLenum target, GLuint index); +typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISMEMORYOBJECTEXTPROC)(GLuint memoryObject); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYEXTPROC)(GLuint id); +typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISSEMAPHOREEXTPROC)(GLuint semaphore); +typedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); +typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); +typedef GLboolean (GLAD_API_PTR *PFNGLISVERTEXARRAYOESPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLLABELOBJECTEXTPROC)(GLenum type, GLuint object, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); +typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFEROESPROC)(GLenum target, GLenum access); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERRANGEEXTPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (GLAD_API_PTR *PFNGLMAXSHADERCOMPILERTHREADSKHRPROC)(GLuint count); +typedef void (GLAD_API_PTR *PFNGLMEMORYOBJECTPARAMETERIVEXTPROC)(GLuint memoryObject, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLMINSAMPLESHADINGOESPROC)(GLfloat value); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSEXTPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSINDIRECTEXTPROC)(GLenum mode, const void * indirect, GLsizei drawcount, GLsizei stride); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount, const GLint * basevertex); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSEXTPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSINDIRECTEXTPROC)(GLenum mode, GLenum type, const void * indirect, GLsizei drawcount, GLsizei stride); +typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC)(GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLOBJECTPTRLABELKHRPROC)(const void * ptr, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLPATCHPARAMETERIEXTPROC)(GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPATCHPARAMETERIOESPROC)(GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETCLAMPEXTPROC)(GLfloat factor, GLfloat units, GLfloat clamp); +typedef void (GLAD_API_PTR *PFNGLPOPDEBUGGROUPKHRPROC)(void); +typedef void (GLAD_API_PTR *PFNGLPOPGROUPMARKEREXTPROC)(void); +typedef void (GLAD_API_PTR *PFNGLPRIMITIVEBOUNDINGBOXEXTPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +typedef void (GLAD_API_PTR *PFNGLPRIMITIVEBOUNDINGBOXOESPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +typedef void (GLAD_API_PTR *PFNGLPROGRAMBINARYOESPROC)(GLuint program, GLenum binaryFormat, const void * binary, GLint length); +typedef void (GLAD_API_PTR *PFNGLPROGRAMPARAMETERIEXTPROC)(GLuint program, GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FEXTPROC)(GLuint program, GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IEXTPROC)(GLuint program, GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIEXTPROC)(GLuint program, GLint location, GLuint v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message); +typedef void (GLAD_API_PTR *PFNGLPUSHGROUPMARKEREXTPROC)(GLsizei length, const GLchar * marker); +typedef void (GLAD_API_PTR *PFNGLQUERYCOUNTEREXTPROC)(GLuint id, GLenum target); +typedef void (GLAD_API_PTR *PFNGLRASTERSAMPLESEXTPROC)(GLuint samples, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLREADBUFFERINDEXEDEXTPROC)(GLenum src, GLint index); +typedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLREADNPIXELSEXTPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); +typedef void (GLAD_API_PTR *PFNGLREADNPIXELSKHRPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); +typedef GLboolean (GLAD_API_PTR *PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC)(GLuint memory, GLuint64 key); +typedef void (GLAD_API_PTR *PFNGLRELEASESHADERCOMPILERPROC)(void); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVEXTPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVOESPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVEXTPROC)(GLuint sampler, GLenum pname, const GLuint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVOESPROC)(GLuint sampler, GLenum pname, const GLuint * param); +typedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSCISSORARRAYVOESPROC)(GLuint first, GLsizei count, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLSCISSORINDEXEDOESPROC)(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSCISSORINDEXEDVOESPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLSEMAPHOREPARAMETERUI64VEXTPROC)(GLuint semaphore, GLenum pname, const GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint * shaders, GLenum binaryFormat, const void * binary, GLsizei length); +typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); +typedef void (GLAD_API_PTR *PFNGLSHADINGRATECOMBINEROPSEXTPROC)(GLenum combinerOp0, GLenum combinerOp1); +typedef void (GLAD_API_PTR *PFNGLSHADINGRATEEXTPROC)(GLenum rate); +typedef void (GLAD_API_PTR *PFNGLSIGNALSEMAPHOREEXTPROC)(GLuint semaphore, GLuint numBufferBarriers, const GLuint * buffers, GLuint numTextureBarriers, const GLuint * textures, const GLenum * dstLayouts); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFEREXTPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFEROESPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFERRANGEEXTPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFERRANGEOESPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DOESPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXPAGECOMMITMENTEXTPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVEXTPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVOESPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVEXTPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVOESPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE1DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE2DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEATTRIBS2DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, const GLint * attrib_list); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEATTRIBS3DEXTPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, const GLint * attrib_list); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEMEM2DEXTPROC)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEMEM3DEXTPROC)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE3DOESPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE1DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE2DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE3DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGEMEM2DEXTPROC)(GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC)(GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGEMEM3DEXTPROC)(GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC)(GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (GLAD_API_PTR *PFNGLTEXTUREVIEWEXTPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (GLAD_API_PTR *PFNGLTEXTUREVIEWOESPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFEROESPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMSTAGESEXTPROC)(GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISOREXTPROC)(GLuint index, GLuint divisor); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTARRAYVOESPROC)(GLuint first, GLsizei count, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFOESPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFVOESPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLWAITSEMAPHOREEXTPROC)(GLuint semaphore, GLuint numBufferBarriers, const GLuint * buffers, GLuint numTextureBarriers, const GLuint * textures, const GLenum * srcLayouts); +typedef void (GLAD_API_PTR *PFNGLWINDOWRECTANGLESEXTPROC)(GLenum mode, GLsizei count, const GLint * box); + +GLAD_API_CALL PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC glad_glAcquireKeyedMutexWin32EXT; +#define glAcquireKeyedMutexWin32EXT glad_glAcquireKeyedMutexWin32EXT +GLAD_API_CALL PFNGLACTIVESHADERPROGRAMEXTPROC glad_glActiveShaderProgramEXT; +#define glActiveShaderProgramEXT glad_glActiveShaderProgramEXT +GLAD_API_CALL PFNGLACTIVETEXTUREPROC glad_glActiveTexture; +#define glActiveTexture glad_glActiveTexture +GLAD_API_CALL PFNGLATTACHSHADERPROC glad_glAttachShader; +#define glAttachShader glad_glAttachShader +GLAD_API_CALL PFNGLBEGINQUERYEXTPROC glad_glBeginQueryEXT; +#define glBeginQueryEXT glad_glBeginQueryEXT +GLAD_API_CALL PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; +#define glBindAttribLocation glad_glBindAttribLocation +GLAD_API_CALL PFNGLBINDBUFFERPROC glad_glBindBuffer; +#define glBindBuffer glad_glBindBuffer +GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONEXTPROC glad_glBindFragDataLocationEXT; +#define glBindFragDataLocationEXT glad_glBindFragDataLocationEXT +GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONINDEXEDEXTPROC glad_glBindFragDataLocationIndexedEXT; +#define glBindFragDataLocationIndexedEXT glad_glBindFragDataLocationIndexedEXT +GLAD_API_CALL PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; +#define glBindFramebuffer glad_glBindFramebuffer +GLAD_API_CALL PFNGLBINDPROGRAMPIPELINEEXTPROC glad_glBindProgramPipelineEXT; +#define glBindProgramPipelineEXT glad_glBindProgramPipelineEXT +GLAD_API_CALL PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; +#define glBindRenderbuffer glad_glBindRenderbuffer +GLAD_API_CALL PFNGLBINDTEXTUREPROC glad_glBindTexture; +#define glBindTexture glad_glBindTexture +GLAD_API_CALL PFNGLBINDVERTEXARRAYOESPROC glad_glBindVertexArrayOES; +#define glBindVertexArrayOES glad_glBindVertexArrayOES +GLAD_API_CALL PFNGLBLENDBARRIERKHRPROC glad_glBlendBarrierKHR; +#define glBlendBarrierKHR glad_glBlendBarrierKHR +GLAD_API_CALL PFNGLBLENDCOLORPROC glad_glBlendColor; +#define glBlendColor glad_glBlendColor +GLAD_API_CALL PFNGLBLENDEQUATIONPROC glad_glBlendEquation; +#define glBlendEquation glad_glBlendEquation +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; +#define glBlendEquationSeparate glad_glBlendEquationSeparate +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEIEXTPROC glad_glBlendEquationSeparateiEXT; +#define glBlendEquationSeparateiEXT glad_glBlendEquationSeparateiEXT +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEIOESPROC glad_glBlendEquationSeparateiOES; +#define glBlendEquationSeparateiOES glad_glBlendEquationSeparateiOES +GLAD_API_CALL PFNGLBLENDEQUATIONIEXTPROC glad_glBlendEquationiEXT; +#define glBlendEquationiEXT glad_glBlendEquationiEXT +GLAD_API_CALL PFNGLBLENDEQUATIONIOESPROC glad_glBlendEquationiOES; +#define glBlendEquationiOES glad_glBlendEquationiOES +GLAD_API_CALL PFNGLBLENDFUNCPROC glad_glBlendFunc; +#define glBlendFunc glad_glBlendFunc +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; +#define glBlendFuncSeparate glad_glBlendFuncSeparate +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEIEXTPROC glad_glBlendFuncSeparateiEXT; +#define glBlendFuncSeparateiEXT glad_glBlendFuncSeparateiEXT +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEIOESPROC glad_glBlendFuncSeparateiOES; +#define glBlendFuncSeparateiOES glad_glBlendFuncSeparateiOES +GLAD_API_CALL PFNGLBLENDFUNCIEXTPROC glad_glBlendFunciEXT; +#define glBlendFunciEXT glad_glBlendFunciEXT +GLAD_API_CALL PFNGLBLENDFUNCIOESPROC glad_glBlendFunciOES; +#define glBlendFunciOES glad_glBlendFunciOES +GLAD_API_CALL PFNGLBUFFERDATAPROC glad_glBufferData; +#define glBufferData glad_glBufferData +GLAD_API_CALL PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT; +#define glBufferStorageEXT glad_glBufferStorageEXT +GLAD_API_CALL PFNGLBUFFERSTORAGEEXTERNALEXTPROC glad_glBufferStorageExternalEXT; +#define glBufferStorageExternalEXT glad_glBufferStorageExternalEXT +GLAD_API_CALL PFNGLBUFFERSTORAGEMEMEXTPROC glad_glBufferStorageMemEXT; +#define glBufferStorageMemEXT glad_glBufferStorageMemEXT +GLAD_API_CALL PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; +#define glBufferSubData glad_glBufferSubData +GLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; +#define glCheckFramebufferStatus glad_glCheckFramebufferStatus +GLAD_API_CALL PFNGLCLEARPROC glad_glClear; +#define glClear glad_glClear +GLAD_API_CALL PFNGLCLEARCOLORPROC glad_glClearColor; +#define glClearColor glad_glClearColor +GLAD_API_CALL PFNGLCLEARDEPTHFPROC glad_glClearDepthf; +#define glClearDepthf glad_glClearDepthf +GLAD_API_CALL PFNGLCLEARPIXELLOCALSTORAGEUIEXTPROC glad_glClearPixelLocalStorageuiEXT; +#define glClearPixelLocalStorageuiEXT glad_glClearPixelLocalStorageuiEXT +GLAD_API_CALL PFNGLCLEARSTENCILPROC glad_glClearStencil; +#define glClearStencil glad_glClearStencil +GLAD_API_CALL PFNGLCLEARTEXIMAGEEXTPROC glad_glClearTexImageEXT; +#define glClearTexImageEXT glad_glClearTexImageEXT +GLAD_API_CALL PFNGLCLEARTEXSUBIMAGEEXTPROC glad_glClearTexSubImageEXT; +#define glClearTexSubImageEXT glad_glClearTexSubImageEXT +GLAD_API_CALL PFNGLCLIPCONTROLEXTPROC glad_glClipControlEXT; +#define glClipControlEXT glad_glClipControlEXT +GLAD_API_CALL PFNGLCOLORMASKPROC glad_glColorMask; +#define glColorMask glad_glColorMask +GLAD_API_CALL PFNGLCOLORMASKIEXTPROC glad_glColorMaskiEXT; +#define glColorMaskiEXT glad_glColorMaskiEXT +GLAD_API_CALL PFNGLCOLORMASKIOESPROC glad_glColorMaskiOES; +#define glColorMaskiOES glad_glColorMaskiOES +GLAD_API_CALL PFNGLCOMPILESHADERPROC glad_glCompileShader; +#define glCompileShader glad_glCompileShader +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; +#define glCompressedTexImage2D glad_glCompressedTexImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DOESPROC glad_glCompressedTexImage3DOES; +#define glCompressedTexImage3DOES glad_glCompressedTexImage3DOES +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; +#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC glad_glCompressedTexSubImage3DOES; +#define glCompressedTexSubImage3DOES glad_glCompressedTexSubImage3DOES +GLAD_API_CALL PFNGLCOPYIMAGESUBDATAEXTPROC glad_glCopyImageSubDataEXT; +#define glCopyImageSubDataEXT glad_glCopyImageSubDataEXT +GLAD_API_CALL PFNGLCOPYIMAGESUBDATAOESPROC glad_glCopyImageSubDataOES; +#define glCopyImageSubDataOES glad_glCopyImageSubDataOES +GLAD_API_CALL PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; +#define glCopyTexImage2D glad_glCopyTexImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; +#define glCopyTexSubImage2D glad_glCopyTexSubImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE3DOESPROC glad_glCopyTexSubImage3DOES; +#define glCopyTexSubImage3DOES glad_glCopyTexSubImage3DOES +GLAD_API_CALL PFNGLCREATEMEMORYOBJECTSEXTPROC glad_glCreateMemoryObjectsEXT; +#define glCreateMemoryObjectsEXT glad_glCreateMemoryObjectsEXT +GLAD_API_CALL PFNGLCREATEPROGRAMPROC glad_glCreateProgram; +#define glCreateProgram glad_glCreateProgram +GLAD_API_CALL PFNGLCREATESHADERPROC glad_glCreateShader; +#define glCreateShader glad_glCreateShader +GLAD_API_CALL PFNGLCREATESHADERPROGRAMVEXTPROC glad_glCreateShaderProgramvEXT; +#define glCreateShaderProgramvEXT glad_glCreateShaderProgramvEXT +GLAD_API_CALL PFNGLCULLFACEPROC glad_glCullFace; +#define glCullFace glad_glCullFace +GLAD_API_CALL PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR; +#define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR +GLAD_API_CALL PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR; +#define glDebugMessageControlKHR glad_glDebugMessageControlKHR +GLAD_API_CALL PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR; +#define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR +GLAD_API_CALL PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; +#define glDeleteBuffers glad_glDeleteBuffers +GLAD_API_CALL PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; +#define glDeleteFramebuffers glad_glDeleteFramebuffers +GLAD_API_CALL PFNGLDELETEMEMORYOBJECTSEXTPROC glad_glDeleteMemoryObjectsEXT; +#define glDeleteMemoryObjectsEXT glad_glDeleteMemoryObjectsEXT +GLAD_API_CALL PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; +#define glDeleteProgram glad_glDeleteProgram +GLAD_API_CALL PFNGLDELETEPROGRAMPIPELINESEXTPROC glad_glDeleteProgramPipelinesEXT; +#define glDeleteProgramPipelinesEXT glad_glDeleteProgramPipelinesEXT +GLAD_API_CALL PFNGLDELETEQUERIESEXTPROC glad_glDeleteQueriesEXT; +#define glDeleteQueriesEXT glad_glDeleteQueriesEXT +GLAD_API_CALL PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; +#define glDeleteRenderbuffers glad_glDeleteRenderbuffers +GLAD_API_CALL PFNGLDELETESEMAPHORESEXTPROC glad_glDeleteSemaphoresEXT; +#define glDeleteSemaphoresEXT glad_glDeleteSemaphoresEXT +GLAD_API_CALL PFNGLDELETESHADERPROC glad_glDeleteShader; +#define glDeleteShader glad_glDeleteShader +GLAD_API_CALL PFNGLDELETETEXTURESPROC glad_glDeleteTextures; +#define glDeleteTextures glad_glDeleteTextures +GLAD_API_CALL PFNGLDELETEVERTEXARRAYSOESPROC glad_glDeleteVertexArraysOES; +#define glDeleteVertexArraysOES glad_glDeleteVertexArraysOES +GLAD_API_CALL PFNGLDEPTHFUNCPROC glad_glDepthFunc; +#define glDepthFunc glad_glDepthFunc +GLAD_API_CALL PFNGLDEPTHMASKPROC glad_glDepthMask; +#define glDepthMask glad_glDepthMask +GLAD_API_CALL PFNGLDEPTHRANGEARRAYFVOESPROC glad_glDepthRangeArrayfvOES; +#define glDepthRangeArrayfvOES glad_glDepthRangeArrayfvOES +GLAD_API_CALL PFNGLDEPTHRANGEINDEXEDFOESPROC glad_glDepthRangeIndexedfOES; +#define glDepthRangeIndexedfOES glad_glDepthRangeIndexedfOES +GLAD_API_CALL PFNGLDEPTHRANGEFPROC glad_glDepthRangef; +#define glDepthRangef glad_glDepthRangef +GLAD_API_CALL PFNGLDETACHSHADERPROC glad_glDetachShader; +#define glDetachShader glad_glDetachShader +GLAD_API_CALL PFNGLDISABLEPROC glad_glDisable; +#define glDisable glad_glDisable +GLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; +#define glDisableVertexAttribArray glad_glDisableVertexAttribArray +GLAD_API_CALL PFNGLDISABLEIEXTPROC glad_glDisableiEXT; +#define glDisableiEXT glad_glDisableiEXT +GLAD_API_CALL PFNGLDISABLEIOESPROC glad_glDisableiOES; +#define glDisableiOES glad_glDisableiOES +GLAD_API_CALL PFNGLDISCARDFRAMEBUFFEREXTPROC glad_glDiscardFramebufferEXT; +#define glDiscardFramebufferEXT glad_glDiscardFramebufferEXT +GLAD_API_CALL PFNGLDRAWARRAYSPROC glad_glDrawArrays; +#define glDrawArrays glad_glDrawArrays +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC glad_glDrawArraysInstancedBaseInstanceEXT; +#define glDrawArraysInstancedBaseInstanceEXT glad_glDrawArraysInstancedBaseInstanceEXT +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT; +#define glDrawArraysInstancedEXT glad_glDrawArraysInstancedEXT +GLAD_API_CALL PFNGLDRAWBUFFERSEXTPROC glad_glDrawBuffersEXT; +#define glDrawBuffersEXT glad_glDrawBuffersEXT +GLAD_API_CALL PFNGLDRAWBUFFERSINDEXEDEXTPROC glad_glDrawBuffersIndexedEXT; +#define glDrawBuffersIndexedEXT glad_glDrawBuffersIndexedEXT +GLAD_API_CALL PFNGLDRAWELEMENTSPROC glad_glDrawElements; +#define glDrawElements glad_glDrawElements +GLAD_API_CALL PFNGLDRAWELEMENTSBASEVERTEXEXTPROC glad_glDrawElementsBaseVertexEXT; +#define glDrawElementsBaseVertexEXT glad_glDrawElementsBaseVertexEXT +GLAD_API_CALL PFNGLDRAWELEMENTSBASEVERTEXOESPROC glad_glDrawElementsBaseVertexOES; +#define glDrawElementsBaseVertexOES glad_glDrawElementsBaseVertexOES +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC glad_glDrawElementsInstancedBaseInstanceEXT; +#define glDrawElementsInstancedBaseInstanceEXT glad_glDrawElementsInstancedBaseInstanceEXT +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC glad_glDrawElementsInstancedBaseVertexBaseInstanceEXT; +#define glDrawElementsInstancedBaseVertexBaseInstanceEXT glad_glDrawElementsInstancedBaseVertexBaseInstanceEXT +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC glad_glDrawElementsInstancedBaseVertexEXT; +#define glDrawElementsInstancedBaseVertexEXT glad_glDrawElementsInstancedBaseVertexEXT +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC glad_glDrawElementsInstancedBaseVertexOES; +#define glDrawElementsInstancedBaseVertexOES glad_glDrawElementsInstancedBaseVertexOES +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT; +#define glDrawElementsInstancedEXT glad_glDrawElementsInstancedEXT +GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC glad_glDrawRangeElementsBaseVertexEXT; +#define glDrawRangeElementsBaseVertexEXT glad_glDrawRangeElementsBaseVertexEXT +GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC glad_glDrawRangeElementsBaseVertexOES; +#define glDrawRangeElementsBaseVertexOES glad_glDrawRangeElementsBaseVertexOES +GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKEXTPROC glad_glDrawTransformFeedbackEXT; +#define glDrawTransformFeedbackEXT glad_glDrawTransformFeedbackEXT +GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDEXTPROC glad_glDrawTransformFeedbackInstancedEXT; +#define glDrawTransformFeedbackInstancedEXT glad_glDrawTransformFeedbackInstancedEXT +GLAD_API_CALL PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glad_glEGLImageTargetRenderbufferStorageOES; +#define glEGLImageTargetRenderbufferStorageOES glad_glEGLImageTargetRenderbufferStorageOES +GLAD_API_CALL PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glad_glEGLImageTargetTexStorageEXT; +#define glEGLImageTargetTexStorageEXT glad_glEGLImageTargetTexStorageEXT +GLAD_API_CALL PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glad_glEGLImageTargetTexture2DOES; +#define glEGLImageTargetTexture2DOES glad_glEGLImageTargetTexture2DOES +GLAD_API_CALL PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC glad_glEGLImageTargetTextureStorageEXT; +#define glEGLImageTargetTextureStorageEXT glad_glEGLImageTargetTextureStorageEXT +GLAD_API_CALL PFNGLENABLEPROC glad_glEnable; +#define glEnable glad_glEnable +GLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; +#define glEnableVertexAttribArray glad_glEnableVertexAttribArray +GLAD_API_CALL PFNGLENABLEIEXTPROC glad_glEnableiEXT; +#define glEnableiEXT glad_glEnableiEXT +GLAD_API_CALL PFNGLENABLEIOESPROC glad_glEnableiOES; +#define glEnableiOES glad_glEnableiOES +GLAD_API_CALL PFNGLENDQUERYEXTPROC glad_glEndQueryEXT; +#define glEndQueryEXT glad_glEndQueryEXT +GLAD_API_CALL PFNGLFINISHPROC glad_glFinish; +#define glFinish glad_glFinish +GLAD_API_CALL PFNGLFLUSHPROC glad_glFlush; +#define glFlush glad_glFlush +GLAD_API_CALL PFNGLFLUSHMAPPEDBUFFERRANGEEXTPROC glad_glFlushMappedBufferRangeEXT; +#define glFlushMappedBufferRangeEXT glad_glFlushMappedBufferRangeEXT +GLAD_API_CALL PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC glad_glFramebufferFetchBarrierEXT; +#define glFramebufferFetchBarrierEXT glad_glFramebufferFetchBarrierEXT +GLAD_API_CALL PFNGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC glad_glFramebufferPixelLocalStorageSizeEXT; +#define glFramebufferPixelLocalStorageSizeEXT glad_glFramebufferPixelLocalStorageSizeEXT +GLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; +#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer +GLAD_API_CALL PFNGLFRAMEBUFFERSHADINGRATEEXTPROC glad_glFramebufferShadingRateEXT; +#define glFramebufferShadingRateEXT glad_glFramebufferShadingRateEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; +#define glFramebufferTexture2D glad_glFramebufferTexture2D +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glad_glFramebufferTexture2DMultisampleEXT; +#define glFramebufferTexture2DMultisampleEXT glad_glFramebufferTexture2DMultisampleEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE3DOESPROC glad_glFramebufferTexture3DOES; +#define glFramebufferTexture3DOES glad_glFramebufferTexture3DOES +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREEXTPROC glad_glFramebufferTextureEXT; +#define glFramebufferTextureEXT glad_glFramebufferTextureEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREOESPROC glad_glFramebufferTextureOES; +#define glFramebufferTextureOES glad_glFramebufferTextureOES +GLAD_API_CALL PFNGLFRONTFACEPROC glad_glFrontFace; +#define glFrontFace glad_glFrontFace +GLAD_API_CALL PFNGLGENBUFFERSPROC glad_glGenBuffers; +#define glGenBuffers glad_glGenBuffers +GLAD_API_CALL PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; +#define glGenFramebuffers glad_glGenFramebuffers +GLAD_API_CALL PFNGLGENPROGRAMPIPELINESEXTPROC glad_glGenProgramPipelinesEXT; +#define glGenProgramPipelinesEXT glad_glGenProgramPipelinesEXT +GLAD_API_CALL PFNGLGENQUERIESEXTPROC glad_glGenQueriesEXT; +#define glGenQueriesEXT glad_glGenQueriesEXT +GLAD_API_CALL PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; +#define glGenRenderbuffers glad_glGenRenderbuffers +GLAD_API_CALL PFNGLGENSEMAPHORESEXTPROC glad_glGenSemaphoresEXT; +#define glGenSemaphoresEXT glad_glGenSemaphoresEXT +GLAD_API_CALL PFNGLGENTEXTURESPROC glad_glGenTextures; +#define glGenTextures glad_glGenTextures +GLAD_API_CALL PFNGLGENVERTEXARRAYSOESPROC glad_glGenVertexArraysOES; +#define glGenVertexArraysOES glad_glGenVertexArraysOES +GLAD_API_CALL PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; +#define glGenerateMipmap glad_glGenerateMipmap +GLAD_API_CALL PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; +#define glGetActiveAttrib glad_glGetActiveAttrib +GLAD_API_CALL PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; +#define glGetActiveUniform glad_glGetActiveUniform +GLAD_API_CALL PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; +#define glGetAttachedShaders glad_glGetAttachedShaders +GLAD_API_CALL PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; +#define glGetAttribLocation glad_glGetAttribLocation +GLAD_API_CALL PFNGLGETBOOLEANVPROC glad_glGetBooleanv; +#define glGetBooleanv glad_glGetBooleanv +GLAD_API_CALL PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; +#define glGetBufferParameteriv glad_glGetBufferParameteriv +GLAD_API_CALL PFNGLGETBUFFERPOINTERVOESPROC glad_glGetBufferPointervOES; +#define glGetBufferPointervOES glad_glGetBufferPointervOES +GLAD_API_CALL PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR; +#define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR +GLAD_API_CALL PFNGLGETERRORPROC glad_glGetError; +#define glGetError glad_glGetError +GLAD_API_CALL PFNGLGETFLOATI_VOESPROC glad_glGetFloati_vOES; +#define glGetFloati_vOES glad_glGetFloati_vOES +GLAD_API_CALL PFNGLGETFLOATVPROC glad_glGetFloatv; +#define glGetFloatv glad_glGetFloatv +GLAD_API_CALL PFNGLGETFRAGDATAINDEXEXTPROC glad_glGetFragDataIndexEXT; +#define glGetFragDataIndexEXT glad_glGetFragDataIndexEXT +GLAD_API_CALL PFNGLGETFRAGMENTSHADINGRATESEXTPROC glad_glGetFragmentShadingRatesEXT; +#define glGetFragmentShadingRatesEXT glad_glGetFragmentShadingRatesEXT +GLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; +#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv +GLAD_API_CALL PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC glad_glGetFramebufferPixelLocalStorageSizeEXT; +#define glGetFramebufferPixelLocalStorageSizeEXT glad_glGetFramebufferPixelLocalStorageSizeEXT +GLAD_API_CALL PFNGLGETGRAPHICSRESETSTATUSEXTPROC glad_glGetGraphicsResetStatusEXT; +#define glGetGraphicsResetStatusEXT glad_glGetGraphicsResetStatusEXT +GLAD_API_CALL PFNGLGETGRAPHICSRESETSTATUSKHRPROC glad_glGetGraphicsResetStatusKHR; +#define glGetGraphicsResetStatusKHR glad_glGetGraphicsResetStatusKHR +GLAD_API_CALL PFNGLGETINTEGER64VEXTPROC glad_glGetInteger64vEXT; +#define glGetInteger64vEXT glad_glGetInteger64vEXT +GLAD_API_CALL PFNGLGETINTEGERI_VEXTPROC glad_glGetIntegeri_vEXT; +#define glGetIntegeri_vEXT glad_glGetIntegeri_vEXT +GLAD_API_CALL PFNGLGETINTEGERVPROC glad_glGetIntegerv; +#define glGetIntegerv glad_glGetIntegerv +GLAD_API_CALL PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC glad_glGetMemoryObjectParameterivEXT; +#define glGetMemoryObjectParameterivEXT glad_glGetMemoryObjectParameterivEXT +GLAD_API_CALL PFNGLGETOBJECTLABELEXTPROC glad_glGetObjectLabelEXT; +#define glGetObjectLabelEXT glad_glGetObjectLabelEXT +GLAD_API_CALL PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR; +#define glGetObjectLabelKHR glad_glGetObjectLabelKHR +GLAD_API_CALL PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR; +#define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR +GLAD_API_CALL PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR; +#define glGetPointervKHR glad_glGetPointervKHR +GLAD_API_CALL PFNGLGETPROGRAMBINARYOESPROC glad_glGetProgramBinaryOES; +#define glGetProgramBinaryOES glad_glGetProgramBinaryOES +GLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; +#define glGetProgramInfoLog glad_glGetProgramInfoLog +GLAD_API_CALL PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC glad_glGetProgramPipelineInfoLogEXT; +#define glGetProgramPipelineInfoLogEXT glad_glGetProgramPipelineInfoLogEXT +GLAD_API_CALL PFNGLGETPROGRAMPIPELINEIVEXTPROC glad_glGetProgramPipelineivEXT; +#define glGetProgramPipelineivEXT glad_glGetProgramPipelineivEXT +GLAD_API_CALL PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC glad_glGetProgramResourceLocationIndexEXT; +#define glGetProgramResourceLocationIndexEXT glad_glGetProgramResourceLocationIndexEXT +GLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; +#define glGetProgramiv glad_glGetProgramiv +GLAD_API_CALL PFNGLGETQUERYOBJECTI64VEXTPROC glad_glGetQueryObjecti64vEXT; +#define glGetQueryObjecti64vEXT glad_glGetQueryObjecti64vEXT +GLAD_API_CALL PFNGLGETQUERYOBJECTIVEXTPROC glad_glGetQueryObjectivEXT; +#define glGetQueryObjectivEXT glad_glGetQueryObjectivEXT +GLAD_API_CALL PFNGLGETQUERYOBJECTUI64VEXTPROC glad_glGetQueryObjectui64vEXT; +#define glGetQueryObjectui64vEXT glad_glGetQueryObjectui64vEXT +GLAD_API_CALL PFNGLGETQUERYOBJECTUIVEXTPROC glad_glGetQueryObjectuivEXT; +#define glGetQueryObjectuivEXT glad_glGetQueryObjectuivEXT +GLAD_API_CALL PFNGLGETQUERYIVEXTPROC glad_glGetQueryivEXT; +#define glGetQueryivEXT glad_glGetQueryivEXT +GLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; +#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIIVEXTPROC glad_glGetSamplerParameterIivEXT; +#define glGetSamplerParameterIivEXT glad_glGetSamplerParameterIivEXT +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIIVOESPROC glad_glGetSamplerParameterIivOES; +#define glGetSamplerParameterIivOES glad_glGetSamplerParameterIivOES +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIUIVEXTPROC glad_glGetSamplerParameterIuivEXT; +#define glGetSamplerParameterIuivEXT glad_glGetSamplerParameterIuivEXT +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIUIVOESPROC glad_glGetSamplerParameterIuivOES; +#define glGetSamplerParameterIuivOES glad_glGetSamplerParameterIuivOES +GLAD_API_CALL PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC glad_glGetSemaphoreParameterui64vEXT; +#define glGetSemaphoreParameterui64vEXT glad_glGetSemaphoreParameterui64vEXT +GLAD_API_CALL PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; +#define glGetShaderInfoLog glad_glGetShaderInfoLog +GLAD_API_CALL PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat; +#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat +GLAD_API_CALL PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; +#define glGetShaderSource glad_glGetShaderSource +GLAD_API_CALL PFNGLGETSHADERIVPROC glad_glGetShaderiv; +#define glGetShaderiv glad_glGetShaderiv +GLAD_API_CALL PFNGLGETSTRINGPROC glad_glGetString; +#define glGetString glad_glGetString +GLAD_API_CALL PFNGLGETTEXPARAMETERIIVEXTPROC glad_glGetTexParameterIivEXT; +#define glGetTexParameterIivEXT glad_glGetTexParameterIivEXT +GLAD_API_CALL PFNGLGETTEXPARAMETERIIVOESPROC glad_glGetTexParameterIivOES; +#define glGetTexParameterIivOES glad_glGetTexParameterIivOES +GLAD_API_CALL PFNGLGETTEXPARAMETERIUIVEXTPROC glad_glGetTexParameterIuivEXT; +#define glGetTexParameterIuivEXT glad_glGetTexParameterIuivEXT +GLAD_API_CALL PFNGLGETTEXPARAMETERIUIVOESPROC glad_glGetTexParameterIuivOES; +#define glGetTexParameterIuivOES glad_glGetTexParameterIuivOES +GLAD_API_CALL PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; +#define glGetTexParameterfv glad_glGetTexParameterfv +GLAD_API_CALL PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; +#define glGetTexParameteriv glad_glGetTexParameteriv +GLAD_API_CALL PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; +#define glGetUniformLocation glad_glGetUniformLocation +GLAD_API_CALL PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; +#define glGetUniformfv glad_glGetUniformfv +GLAD_API_CALL PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; +#define glGetUniformiv glad_glGetUniformiv +GLAD_API_CALL PFNGLGETUNSIGNEDBYTEI_VEXTPROC glad_glGetUnsignedBytei_vEXT; +#define glGetUnsignedBytei_vEXT glad_glGetUnsignedBytei_vEXT +GLAD_API_CALL PFNGLGETUNSIGNEDBYTEVEXTPROC glad_glGetUnsignedBytevEXT; +#define glGetUnsignedBytevEXT glad_glGetUnsignedBytevEXT +GLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; +#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv +GLAD_API_CALL PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; +#define glGetVertexAttribfv glad_glGetVertexAttribfv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; +#define glGetVertexAttribiv glad_glGetVertexAttribiv +GLAD_API_CALL PFNGLGETNUNIFORMFVEXTPROC glad_glGetnUniformfvEXT; +#define glGetnUniformfvEXT glad_glGetnUniformfvEXT +GLAD_API_CALL PFNGLGETNUNIFORMFVKHRPROC glad_glGetnUniformfvKHR; +#define glGetnUniformfvKHR glad_glGetnUniformfvKHR +GLAD_API_CALL PFNGLGETNUNIFORMIVEXTPROC glad_glGetnUniformivEXT; +#define glGetnUniformivEXT glad_glGetnUniformivEXT +GLAD_API_CALL PFNGLGETNUNIFORMIVKHRPROC glad_glGetnUniformivKHR; +#define glGetnUniformivKHR glad_glGetnUniformivKHR +GLAD_API_CALL PFNGLGETNUNIFORMUIVKHRPROC glad_glGetnUniformuivKHR; +#define glGetnUniformuivKHR glad_glGetnUniformuivKHR +GLAD_API_CALL PFNGLHINTPROC glad_glHint; +#define glHint glad_glHint +GLAD_API_CALL PFNGLIMPORTMEMORYFDEXTPROC glad_glImportMemoryFdEXT; +#define glImportMemoryFdEXT glad_glImportMemoryFdEXT +GLAD_API_CALL PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC glad_glImportMemoryWin32HandleEXT; +#define glImportMemoryWin32HandleEXT glad_glImportMemoryWin32HandleEXT +GLAD_API_CALL PFNGLIMPORTMEMORYWIN32NAMEEXTPROC glad_glImportMemoryWin32NameEXT; +#define glImportMemoryWin32NameEXT glad_glImportMemoryWin32NameEXT +GLAD_API_CALL PFNGLIMPORTSEMAPHOREFDEXTPROC glad_glImportSemaphoreFdEXT; +#define glImportSemaphoreFdEXT glad_glImportSemaphoreFdEXT +GLAD_API_CALL PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC glad_glImportSemaphoreWin32HandleEXT; +#define glImportSemaphoreWin32HandleEXT glad_glImportSemaphoreWin32HandleEXT +GLAD_API_CALL PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC glad_glImportSemaphoreWin32NameEXT; +#define glImportSemaphoreWin32NameEXT glad_glImportSemaphoreWin32NameEXT +GLAD_API_CALL PFNGLINSERTEVENTMARKEREXTPROC glad_glInsertEventMarkerEXT; +#define glInsertEventMarkerEXT glad_glInsertEventMarkerEXT +GLAD_API_CALL PFNGLISBUFFERPROC glad_glIsBuffer; +#define glIsBuffer glad_glIsBuffer +GLAD_API_CALL PFNGLISENABLEDPROC glad_glIsEnabled; +#define glIsEnabled glad_glIsEnabled +GLAD_API_CALL PFNGLISENABLEDIEXTPROC glad_glIsEnablediEXT; +#define glIsEnablediEXT glad_glIsEnablediEXT +GLAD_API_CALL PFNGLISENABLEDIOESPROC glad_glIsEnablediOES; +#define glIsEnablediOES glad_glIsEnablediOES +GLAD_API_CALL PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; +#define glIsFramebuffer glad_glIsFramebuffer +GLAD_API_CALL PFNGLISMEMORYOBJECTEXTPROC glad_glIsMemoryObjectEXT; +#define glIsMemoryObjectEXT glad_glIsMemoryObjectEXT +GLAD_API_CALL PFNGLISPROGRAMPROC glad_glIsProgram; +#define glIsProgram glad_glIsProgram +GLAD_API_CALL PFNGLISPROGRAMPIPELINEEXTPROC glad_glIsProgramPipelineEXT; +#define glIsProgramPipelineEXT glad_glIsProgramPipelineEXT +GLAD_API_CALL PFNGLISQUERYEXTPROC glad_glIsQueryEXT; +#define glIsQueryEXT glad_glIsQueryEXT +GLAD_API_CALL PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; +#define glIsRenderbuffer glad_glIsRenderbuffer +GLAD_API_CALL PFNGLISSEMAPHOREEXTPROC glad_glIsSemaphoreEXT; +#define glIsSemaphoreEXT glad_glIsSemaphoreEXT +GLAD_API_CALL PFNGLISSHADERPROC glad_glIsShader; +#define glIsShader glad_glIsShader +GLAD_API_CALL PFNGLISTEXTUREPROC glad_glIsTexture; +#define glIsTexture glad_glIsTexture +GLAD_API_CALL PFNGLISVERTEXARRAYOESPROC glad_glIsVertexArrayOES; +#define glIsVertexArrayOES glad_glIsVertexArrayOES +GLAD_API_CALL PFNGLLABELOBJECTEXTPROC glad_glLabelObjectEXT; +#define glLabelObjectEXT glad_glLabelObjectEXT +GLAD_API_CALL PFNGLLINEWIDTHPROC glad_glLineWidth; +#define glLineWidth glad_glLineWidth +GLAD_API_CALL PFNGLLINKPROGRAMPROC glad_glLinkProgram; +#define glLinkProgram glad_glLinkProgram +GLAD_API_CALL PFNGLMAPBUFFEROESPROC glad_glMapBufferOES; +#define glMapBufferOES glad_glMapBufferOES +GLAD_API_CALL PFNGLMAPBUFFERRANGEEXTPROC glad_glMapBufferRangeEXT; +#define glMapBufferRangeEXT glad_glMapBufferRangeEXT +GLAD_API_CALL PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glad_glMaxShaderCompilerThreadsKHR; +#define glMaxShaderCompilerThreadsKHR glad_glMaxShaderCompilerThreadsKHR +GLAD_API_CALL PFNGLMEMORYOBJECTPARAMETERIVEXTPROC glad_glMemoryObjectParameterivEXT; +#define glMemoryObjectParameterivEXT glad_glMemoryObjectParameterivEXT +GLAD_API_CALL PFNGLMINSAMPLESHADINGOESPROC glad_glMinSampleShadingOES; +#define glMinSampleShadingOES glad_glMinSampleShadingOES +GLAD_API_CALL PFNGLMULTIDRAWARRAYSEXTPROC glad_glMultiDrawArraysEXT; +#define glMultiDrawArraysEXT glad_glMultiDrawArraysEXT +GLAD_API_CALL PFNGLMULTIDRAWARRAYSINDIRECTEXTPROC glad_glMultiDrawArraysIndirectEXT; +#define glMultiDrawArraysIndirectEXT glad_glMultiDrawArraysIndirectEXT +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC glad_glMultiDrawElementsBaseVertexEXT; +#define glMultiDrawElementsBaseVertexEXT glad_glMultiDrawElementsBaseVertexEXT +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSEXTPROC glad_glMultiDrawElementsEXT; +#define glMultiDrawElementsEXT glad_glMultiDrawElementsEXT +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSINDIRECTEXTPROC glad_glMultiDrawElementsIndirectEXT; +#define glMultiDrawElementsIndirectEXT glad_glMultiDrawElementsIndirectEXT +GLAD_API_CALL PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC glad_glNamedBufferStorageExternalEXT; +#define glNamedBufferStorageExternalEXT glad_glNamedBufferStorageExternalEXT +GLAD_API_CALL PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC glad_glNamedBufferStorageMemEXT; +#define glNamedBufferStorageMemEXT glad_glNamedBufferStorageMemEXT +GLAD_API_CALL PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR; +#define glObjectLabelKHR glad_glObjectLabelKHR +GLAD_API_CALL PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR; +#define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR +GLAD_API_CALL PFNGLPATCHPARAMETERIEXTPROC glad_glPatchParameteriEXT; +#define glPatchParameteriEXT glad_glPatchParameteriEXT +GLAD_API_CALL PFNGLPATCHPARAMETERIOESPROC glad_glPatchParameteriOES; +#define glPatchParameteriOES glad_glPatchParameteriOES +GLAD_API_CALL PFNGLPIXELSTOREIPROC glad_glPixelStorei; +#define glPixelStorei glad_glPixelStorei +GLAD_API_CALL PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; +#define glPolygonOffset glad_glPolygonOffset +GLAD_API_CALL PFNGLPOLYGONOFFSETCLAMPEXTPROC glad_glPolygonOffsetClampEXT; +#define glPolygonOffsetClampEXT glad_glPolygonOffsetClampEXT +GLAD_API_CALL PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR; +#define glPopDebugGroupKHR glad_glPopDebugGroupKHR +GLAD_API_CALL PFNGLPOPGROUPMARKEREXTPROC glad_glPopGroupMarkerEXT; +#define glPopGroupMarkerEXT glad_glPopGroupMarkerEXT +GLAD_API_CALL PFNGLPRIMITIVEBOUNDINGBOXEXTPROC glad_glPrimitiveBoundingBoxEXT; +#define glPrimitiveBoundingBoxEXT glad_glPrimitiveBoundingBoxEXT +GLAD_API_CALL PFNGLPRIMITIVEBOUNDINGBOXOESPROC glad_glPrimitiveBoundingBoxOES; +#define glPrimitiveBoundingBoxOES glad_glPrimitiveBoundingBoxOES +GLAD_API_CALL PFNGLPROGRAMBINARYOESPROC glad_glProgramBinaryOES; +#define glProgramBinaryOES glad_glProgramBinaryOES +GLAD_API_CALL PFNGLPROGRAMPARAMETERIEXTPROC glad_glProgramParameteriEXT; +#define glProgramParameteriEXT glad_glProgramParameteriEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1FEXTPROC glad_glProgramUniform1fEXT; +#define glProgramUniform1fEXT glad_glProgramUniform1fEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1FVEXTPROC glad_glProgramUniform1fvEXT; +#define glProgramUniform1fvEXT glad_glProgramUniform1fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1IEXTPROC glad_glProgramUniform1iEXT; +#define glProgramUniform1iEXT glad_glProgramUniform1iEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1IVEXTPROC glad_glProgramUniform1ivEXT; +#define glProgramUniform1ivEXT glad_glProgramUniform1ivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1UIEXTPROC glad_glProgramUniform1uiEXT; +#define glProgramUniform1uiEXT glad_glProgramUniform1uiEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM1UIVEXTPROC glad_glProgramUniform1uivEXT; +#define glProgramUniform1uivEXT glad_glProgramUniform1uivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2FEXTPROC glad_glProgramUniform2fEXT; +#define glProgramUniform2fEXT glad_glProgramUniform2fEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2FVEXTPROC glad_glProgramUniform2fvEXT; +#define glProgramUniform2fvEXT glad_glProgramUniform2fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2IEXTPROC glad_glProgramUniform2iEXT; +#define glProgramUniform2iEXT glad_glProgramUniform2iEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2IVEXTPROC glad_glProgramUniform2ivEXT; +#define glProgramUniform2ivEXT glad_glProgramUniform2ivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2UIEXTPROC glad_glProgramUniform2uiEXT; +#define glProgramUniform2uiEXT glad_glProgramUniform2uiEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM2UIVEXTPROC glad_glProgramUniform2uivEXT; +#define glProgramUniform2uivEXT glad_glProgramUniform2uivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3FEXTPROC glad_glProgramUniform3fEXT; +#define glProgramUniform3fEXT glad_glProgramUniform3fEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3FVEXTPROC glad_glProgramUniform3fvEXT; +#define glProgramUniform3fvEXT glad_glProgramUniform3fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3IEXTPROC glad_glProgramUniform3iEXT; +#define glProgramUniform3iEXT glad_glProgramUniform3iEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3IVEXTPROC glad_glProgramUniform3ivEXT; +#define glProgramUniform3ivEXT glad_glProgramUniform3ivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3UIEXTPROC glad_glProgramUniform3uiEXT; +#define glProgramUniform3uiEXT glad_glProgramUniform3uiEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM3UIVEXTPROC glad_glProgramUniform3uivEXT; +#define glProgramUniform3uivEXT glad_glProgramUniform3uivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4FEXTPROC glad_glProgramUniform4fEXT; +#define glProgramUniform4fEXT glad_glProgramUniform4fEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4FVEXTPROC glad_glProgramUniform4fvEXT; +#define glProgramUniform4fvEXT glad_glProgramUniform4fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4IEXTPROC glad_glProgramUniform4iEXT; +#define glProgramUniform4iEXT glad_glProgramUniform4iEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4IVEXTPROC glad_glProgramUniform4ivEXT; +#define glProgramUniform4ivEXT glad_glProgramUniform4ivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4UIEXTPROC glad_glProgramUniform4uiEXT; +#define glProgramUniform4uiEXT glad_glProgramUniform4uiEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORM4UIVEXTPROC glad_glProgramUniform4uivEXT; +#define glProgramUniform4uivEXT glad_glProgramUniform4uivEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glad_glProgramUniformMatrix2fvEXT; +#define glProgramUniformMatrix2fvEXT glad_glProgramUniformMatrix2fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glad_glProgramUniformMatrix2x3fvEXT; +#define glProgramUniformMatrix2x3fvEXT glad_glProgramUniformMatrix2x3fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glad_glProgramUniformMatrix2x4fvEXT; +#define glProgramUniformMatrix2x4fvEXT glad_glProgramUniformMatrix2x4fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glad_glProgramUniformMatrix3fvEXT; +#define glProgramUniformMatrix3fvEXT glad_glProgramUniformMatrix3fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glad_glProgramUniformMatrix3x2fvEXT; +#define glProgramUniformMatrix3x2fvEXT glad_glProgramUniformMatrix3x2fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glad_glProgramUniformMatrix3x4fvEXT; +#define glProgramUniformMatrix3x4fvEXT glad_glProgramUniformMatrix3x4fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glad_glProgramUniformMatrix4fvEXT; +#define glProgramUniformMatrix4fvEXT glad_glProgramUniformMatrix4fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glad_glProgramUniformMatrix4x2fvEXT; +#define glProgramUniformMatrix4x2fvEXT glad_glProgramUniformMatrix4x2fvEXT +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glad_glProgramUniformMatrix4x3fvEXT; +#define glProgramUniformMatrix4x3fvEXT glad_glProgramUniformMatrix4x3fvEXT +GLAD_API_CALL PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR; +#define glPushDebugGroupKHR glad_glPushDebugGroupKHR +GLAD_API_CALL PFNGLPUSHGROUPMARKEREXTPROC glad_glPushGroupMarkerEXT; +#define glPushGroupMarkerEXT glad_glPushGroupMarkerEXT +GLAD_API_CALL PFNGLQUERYCOUNTEREXTPROC glad_glQueryCounterEXT; +#define glQueryCounterEXT glad_glQueryCounterEXT +GLAD_API_CALL PFNGLRASTERSAMPLESEXTPROC glad_glRasterSamplesEXT; +#define glRasterSamplesEXT glad_glRasterSamplesEXT +GLAD_API_CALL PFNGLREADBUFFERINDEXEDEXTPROC glad_glReadBufferIndexedEXT; +#define glReadBufferIndexedEXT glad_glReadBufferIndexedEXT +GLAD_API_CALL PFNGLREADPIXELSPROC glad_glReadPixels; +#define glReadPixels glad_glReadPixels +GLAD_API_CALL PFNGLREADNPIXELSEXTPROC glad_glReadnPixelsEXT; +#define glReadnPixelsEXT glad_glReadnPixelsEXT +GLAD_API_CALL PFNGLREADNPIXELSKHRPROC glad_glReadnPixelsKHR; +#define glReadnPixelsKHR glad_glReadnPixelsKHR +GLAD_API_CALL PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC glad_glReleaseKeyedMutexWin32EXT; +#define glReleaseKeyedMutexWin32EXT glad_glReleaseKeyedMutexWin32EXT +GLAD_API_CALL PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler; +#define glReleaseShaderCompiler glad_glReleaseShaderCompiler +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; +#define glRenderbufferStorage glad_glRenderbufferStorage +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT; +#define glRenderbufferStorageMultisampleEXT glad_glRenderbufferStorageMultisampleEXT +GLAD_API_CALL PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; +#define glSampleCoverage glad_glSampleCoverage +GLAD_API_CALL PFNGLSAMPLERPARAMETERIIVEXTPROC glad_glSamplerParameterIivEXT; +#define glSamplerParameterIivEXT glad_glSamplerParameterIivEXT +GLAD_API_CALL PFNGLSAMPLERPARAMETERIIVOESPROC glad_glSamplerParameterIivOES; +#define glSamplerParameterIivOES glad_glSamplerParameterIivOES +GLAD_API_CALL PFNGLSAMPLERPARAMETERIUIVEXTPROC glad_glSamplerParameterIuivEXT; +#define glSamplerParameterIuivEXT glad_glSamplerParameterIuivEXT +GLAD_API_CALL PFNGLSAMPLERPARAMETERIUIVOESPROC glad_glSamplerParameterIuivOES; +#define glSamplerParameterIuivOES glad_glSamplerParameterIuivOES +GLAD_API_CALL PFNGLSCISSORPROC glad_glScissor; +#define glScissor glad_glScissor +GLAD_API_CALL PFNGLSCISSORARRAYVOESPROC glad_glScissorArrayvOES; +#define glScissorArrayvOES glad_glScissorArrayvOES +GLAD_API_CALL PFNGLSCISSORINDEXEDOESPROC glad_glScissorIndexedOES; +#define glScissorIndexedOES glad_glScissorIndexedOES +GLAD_API_CALL PFNGLSCISSORINDEXEDVOESPROC glad_glScissorIndexedvOES; +#define glScissorIndexedvOES glad_glScissorIndexedvOES +GLAD_API_CALL PFNGLSEMAPHOREPARAMETERUI64VEXTPROC glad_glSemaphoreParameterui64vEXT; +#define glSemaphoreParameterui64vEXT glad_glSemaphoreParameterui64vEXT +GLAD_API_CALL PFNGLSHADERBINARYPROC glad_glShaderBinary; +#define glShaderBinary glad_glShaderBinary +GLAD_API_CALL PFNGLSHADERSOURCEPROC glad_glShaderSource; +#define glShaderSource glad_glShaderSource +GLAD_API_CALL PFNGLSHADINGRATECOMBINEROPSEXTPROC glad_glShadingRateCombinerOpsEXT; +#define glShadingRateCombinerOpsEXT glad_glShadingRateCombinerOpsEXT +GLAD_API_CALL PFNGLSHADINGRATEEXTPROC glad_glShadingRateEXT; +#define glShadingRateEXT glad_glShadingRateEXT +GLAD_API_CALL PFNGLSIGNALSEMAPHOREEXTPROC glad_glSignalSemaphoreEXT; +#define glSignalSemaphoreEXT glad_glSignalSemaphoreEXT +GLAD_API_CALL PFNGLSTENCILFUNCPROC glad_glStencilFunc; +#define glStencilFunc glad_glStencilFunc +GLAD_API_CALL PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; +#define glStencilFuncSeparate glad_glStencilFuncSeparate +GLAD_API_CALL PFNGLSTENCILMASKPROC glad_glStencilMask; +#define glStencilMask glad_glStencilMask +GLAD_API_CALL PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; +#define glStencilMaskSeparate glad_glStencilMaskSeparate +GLAD_API_CALL PFNGLSTENCILOPPROC glad_glStencilOp; +#define glStencilOp glad_glStencilOp +GLAD_API_CALL PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; +#define glStencilOpSeparate glad_glStencilOpSeparate +GLAD_API_CALL PFNGLTEXBUFFEREXTPROC glad_glTexBufferEXT; +#define glTexBufferEXT glad_glTexBufferEXT +GLAD_API_CALL PFNGLTEXBUFFEROESPROC glad_glTexBufferOES; +#define glTexBufferOES glad_glTexBufferOES +GLAD_API_CALL PFNGLTEXBUFFERRANGEEXTPROC glad_glTexBufferRangeEXT; +#define glTexBufferRangeEXT glad_glTexBufferRangeEXT +GLAD_API_CALL PFNGLTEXBUFFERRANGEOESPROC glad_glTexBufferRangeOES; +#define glTexBufferRangeOES glad_glTexBufferRangeOES +GLAD_API_CALL PFNGLTEXIMAGE2DPROC glad_glTexImage2D; +#define glTexImage2D glad_glTexImage2D +GLAD_API_CALL PFNGLTEXIMAGE3DOESPROC glad_glTexImage3DOES; +#define glTexImage3DOES glad_glTexImage3DOES +GLAD_API_CALL PFNGLTEXPAGECOMMITMENTEXTPROC glad_glTexPageCommitmentEXT; +#define glTexPageCommitmentEXT glad_glTexPageCommitmentEXT +GLAD_API_CALL PFNGLTEXPARAMETERIIVEXTPROC glad_glTexParameterIivEXT; +#define glTexParameterIivEXT glad_glTexParameterIivEXT +GLAD_API_CALL PFNGLTEXPARAMETERIIVOESPROC glad_glTexParameterIivOES; +#define glTexParameterIivOES glad_glTexParameterIivOES +GLAD_API_CALL PFNGLTEXPARAMETERIUIVEXTPROC glad_glTexParameterIuivEXT; +#define glTexParameterIuivEXT glad_glTexParameterIuivEXT +GLAD_API_CALL PFNGLTEXPARAMETERIUIVOESPROC glad_glTexParameterIuivOES; +#define glTexParameterIuivOES glad_glTexParameterIuivOES +GLAD_API_CALL PFNGLTEXPARAMETERFPROC glad_glTexParameterf; +#define glTexParameterf glad_glTexParameterf +GLAD_API_CALL PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; +#define glTexParameterfv glad_glTexParameterfv +GLAD_API_CALL PFNGLTEXPARAMETERIPROC glad_glTexParameteri; +#define glTexParameteri glad_glTexParameteri +GLAD_API_CALL PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; +#define glTexParameteriv glad_glTexParameteriv +GLAD_API_CALL PFNGLTEXSTORAGE1DEXTPROC glad_glTexStorage1DEXT; +#define glTexStorage1DEXT glad_glTexStorage1DEXT +GLAD_API_CALL PFNGLTEXSTORAGE2DEXTPROC glad_glTexStorage2DEXT; +#define glTexStorage2DEXT glad_glTexStorage2DEXT +GLAD_API_CALL PFNGLTEXSTORAGE3DEXTPROC glad_glTexStorage3DEXT; +#define glTexStorage3DEXT glad_glTexStorage3DEXT +GLAD_API_CALL PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC glad_glTexStorage3DMultisampleOES; +#define glTexStorage3DMultisampleOES glad_glTexStorage3DMultisampleOES +GLAD_API_CALL PFNGLTEXSTORAGEATTRIBS2DEXTPROC glad_glTexStorageAttribs2DEXT; +#define glTexStorageAttribs2DEXT glad_glTexStorageAttribs2DEXT +GLAD_API_CALL PFNGLTEXSTORAGEATTRIBS3DEXTPROC glad_glTexStorageAttribs3DEXT; +#define glTexStorageAttribs3DEXT glad_glTexStorageAttribs3DEXT +GLAD_API_CALL PFNGLTEXSTORAGEMEM2DEXTPROC glad_glTexStorageMem2DEXT; +#define glTexStorageMem2DEXT glad_glTexStorageMem2DEXT +GLAD_API_CALL PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC glad_glTexStorageMem2DMultisampleEXT; +#define glTexStorageMem2DMultisampleEXT glad_glTexStorageMem2DMultisampleEXT +GLAD_API_CALL PFNGLTEXSTORAGEMEM3DEXTPROC glad_glTexStorageMem3DEXT; +#define glTexStorageMem3DEXT glad_glTexStorageMem3DEXT +GLAD_API_CALL PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC glad_glTexStorageMem3DMultisampleEXT; +#define glTexStorageMem3DMultisampleEXT glad_glTexStorageMem3DMultisampleEXT +GLAD_API_CALL PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; +#define glTexSubImage2D glad_glTexSubImage2D +GLAD_API_CALL PFNGLTEXSUBIMAGE3DOESPROC glad_glTexSubImage3DOES; +#define glTexSubImage3DOES glad_glTexSubImage3DOES +GLAD_API_CALL PFNGLTEXTURESTORAGE1DEXTPROC glad_glTextureStorage1DEXT; +#define glTextureStorage1DEXT glad_glTextureStorage1DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGE2DEXTPROC glad_glTextureStorage2DEXT; +#define glTextureStorage2DEXT glad_glTextureStorage2DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGE3DEXTPROC glad_glTextureStorage3DEXT; +#define glTextureStorage3DEXT glad_glTextureStorage3DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGEMEM2DEXTPROC glad_glTextureStorageMem2DEXT; +#define glTextureStorageMem2DEXT glad_glTextureStorageMem2DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC glad_glTextureStorageMem2DMultisampleEXT; +#define glTextureStorageMem2DMultisampleEXT glad_glTextureStorageMem2DMultisampleEXT +GLAD_API_CALL PFNGLTEXTURESTORAGEMEM3DEXTPROC glad_glTextureStorageMem3DEXT; +#define glTextureStorageMem3DEXT glad_glTextureStorageMem3DEXT +GLAD_API_CALL PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC glad_glTextureStorageMem3DMultisampleEXT; +#define glTextureStorageMem3DMultisampleEXT glad_glTextureStorageMem3DMultisampleEXT +GLAD_API_CALL PFNGLTEXTUREVIEWEXTPROC glad_glTextureViewEXT; +#define glTextureViewEXT glad_glTextureViewEXT +GLAD_API_CALL PFNGLTEXTUREVIEWOESPROC glad_glTextureViewOES; +#define glTextureViewOES glad_glTextureViewOES +GLAD_API_CALL PFNGLUNIFORM1FPROC glad_glUniform1f; +#define glUniform1f glad_glUniform1f +GLAD_API_CALL PFNGLUNIFORM1FVPROC glad_glUniform1fv; +#define glUniform1fv glad_glUniform1fv +GLAD_API_CALL PFNGLUNIFORM1IPROC glad_glUniform1i; +#define glUniform1i glad_glUniform1i +GLAD_API_CALL PFNGLUNIFORM1IVPROC glad_glUniform1iv; +#define glUniform1iv glad_glUniform1iv +GLAD_API_CALL PFNGLUNIFORM2FPROC glad_glUniform2f; +#define glUniform2f glad_glUniform2f +GLAD_API_CALL PFNGLUNIFORM2FVPROC glad_glUniform2fv; +#define glUniform2fv glad_glUniform2fv +GLAD_API_CALL PFNGLUNIFORM2IPROC glad_glUniform2i; +#define glUniform2i glad_glUniform2i +GLAD_API_CALL PFNGLUNIFORM2IVPROC glad_glUniform2iv; +#define glUniform2iv glad_glUniform2iv +GLAD_API_CALL PFNGLUNIFORM3FPROC glad_glUniform3f; +#define glUniform3f glad_glUniform3f +GLAD_API_CALL PFNGLUNIFORM3FVPROC glad_glUniform3fv; +#define glUniform3fv glad_glUniform3fv +GLAD_API_CALL PFNGLUNIFORM3IPROC glad_glUniform3i; +#define glUniform3i glad_glUniform3i +GLAD_API_CALL PFNGLUNIFORM3IVPROC glad_glUniform3iv; +#define glUniform3iv glad_glUniform3iv +GLAD_API_CALL PFNGLUNIFORM4FPROC glad_glUniform4f; +#define glUniform4f glad_glUniform4f +GLAD_API_CALL PFNGLUNIFORM4FVPROC glad_glUniform4fv; +#define glUniform4fv glad_glUniform4fv +GLAD_API_CALL PFNGLUNIFORM4IPROC glad_glUniform4i; +#define glUniform4i glad_glUniform4i +GLAD_API_CALL PFNGLUNIFORM4IVPROC glad_glUniform4iv; +#define glUniform4iv glad_glUniform4iv +GLAD_API_CALL PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; +#define glUniformMatrix2fv glad_glUniformMatrix2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; +#define glUniformMatrix3fv glad_glUniformMatrix3fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; +#define glUniformMatrix4fv glad_glUniformMatrix4fv +GLAD_API_CALL PFNGLUNMAPBUFFEROESPROC glad_glUnmapBufferOES; +#define glUnmapBufferOES glad_glUnmapBufferOES +GLAD_API_CALL PFNGLUSEPROGRAMPROC glad_glUseProgram; +#define glUseProgram glad_glUseProgram +GLAD_API_CALL PFNGLUSEPROGRAMSTAGESEXTPROC glad_glUseProgramStagesEXT; +#define glUseProgramStagesEXT glad_glUseProgramStagesEXT +GLAD_API_CALL PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; +#define glValidateProgram glad_glValidateProgram +GLAD_API_CALL PFNGLVALIDATEPROGRAMPIPELINEEXTPROC glad_glValidateProgramPipelineEXT; +#define glValidateProgramPipelineEXT glad_glValidateProgramPipelineEXT +GLAD_API_CALL PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; +#define glVertexAttrib1f glad_glVertexAttrib1f +GLAD_API_CALL PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; +#define glVertexAttrib1fv glad_glVertexAttrib1fv +GLAD_API_CALL PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; +#define glVertexAttrib2f glad_glVertexAttrib2f +GLAD_API_CALL PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; +#define glVertexAttrib2fv glad_glVertexAttrib2fv +GLAD_API_CALL PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; +#define glVertexAttrib3f glad_glVertexAttrib3f +GLAD_API_CALL PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; +#define glVertexAttrib3fv glad_glVertexAttrib3fv +GLAD_API_CALL PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; +#define glVertexAttrib4f glad_glVertexAttrib4f +GLAD_API_CALL PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; +#define glVertexAttrib4fv glad_glVertexAttrib4fv +GLAD_API_CALL PFNGLVERTEXATTRIBDIVISOREXTPROC glad_glVertexAttribDivisorEXT; +#define glVertexAttribDivisorEXT glad_glVertexAttribDivisorEXT +GLAD_API_CALL PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; +#define glVertexAttribPointer glad_glVertexAttribPointer +GLAD_API_CALL PFNGLVIEWPORTPROC glad_glViewport; +#define glViewport glad_glViewport +GLAD_API_CALL PFNGLVIEWPORTARRAYVOESPROC glad_glViewportArrayvOES; +#define glViewportArrayvOES glad_glViewportArrayvOES +GLAD_API_CALL PFNGLVIEWPORTINDEXEDFOESPROC glad_glViewportIndexedfOES; +#define glViewportIndexedfOES glad_glViewportIndexedfOES +GLAD_API_CALL PFNGLVIEWPORTINDEXEDFVOESPROC glad_glViewportIndexedfvOES; +#define glViewportIndexedfvOES glad_glViewportIndexedfvOES +GLAD_API_CALL PFNGLWAITSEMAPHOREEXTPROC glad_glWaitSemaphoreEXT; +#define glWaitSemaphoreEXT glad_glWaitSemaphoreEXT +GLAD_API_CALL PFNGLWINDOWRECTANGLESEXTPROC glad_glWindowRectanglesEXT; +#define glWindowRectanglesEXT glad_glWindowRectanglesEXT + + + + + +GLAD_API_CALL int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr); +GLAD_API_CALL int gladLoadGLES2( GLADloadfunc load); + + + +#ifdef __cplusplus +} +#endif +#endif + +/* Source */ +#ifdef GLAD_GLES2_IMPLEMENTATION +/** + * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 + */ +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_GL_ES_VERSION_2_0 = 0; +int GLAD_GL_EXT_EGL_image_array = 0; +int GLAD_GL_EXT_EGL_image_storage = 0; +int GLAD_GL_EXT_EGL_image_storage_compression = 0; +int GLAD_GL_EXT_YUV_target = 0; +int GLAD_GL_EXT_base_instance = 0; +int GLAD_GL_EXT_blend_func_extended = 0; +int GLAD_GL_EXT_blend_minmax = 0; +int GLAD_GL_EXT_buffer_storage = 0; +int GLAD_GL_EXT_clear_texture = 0; +int GLAD_GL_EXT_clip_control = 0; +int GLAD_GL_EXT_clip_cull_distance = 0; +int GLAD_GL_EXT_color_buffer_float = 0; +int GLAD_GL_EXT_color_buffer_half_float = 0; +int GLAD_GL_EXT_conservative_depth = 0; +int GLAD_GL_EXT_copy_image = 0; +int GLAD_GL_EXT_debug_label = 0; +int GLAD_GL_EXT_debug_marker = 0; +int GLAD_GL_EXT_depth_clamp = 0; +int GLAD_GL_EXT_discard_framebuffer = 0; +int GLAD_GL_EXT_disjoint_timer_query = 0; +int GLAD_GL_EXT_draw_buffers = 0; +int GLAD_GL_EXT_draw_buffers_indexed = 0; +int GLAD_GL_EXT_draw_elements_base_vertex = 0; +int GLAD_GL_EXT_draw_instanced = 0; +int GLAD_GL_EXT_draw_transform_feedback = 0; +int GLAD_GL_EXT_external_buffer = 0; +int GLAD_GL_EXT_float_blend = 0; +int GLAD_GL_EXT_fragment_shading_rate = 0; +int GLAD_GL_EXT_geometry_point_size = 0; +int GLAD_GL_EXT_geometry_shader = 0; +int GLAD_GL_EXT_gpu_shader5 = 0; +int GLAD_GL_EXT_instanced_arrays = 0; +int GLAD_GL_EXT_map_buffer_range = 0; +int GLAD_GL_EXT_memory_object = 0; +int GLAD_GL_EXT_memory_object_fd = 0; +int GLAD_GL_EXT_memory_object_win32 = 0; +int GLAD_GL_EXT_multi_draw_arrays = 0; +int GLAD_GL_EXT_multi_draw_indirect = 0; +int GLAD_GL_EXT_multisampled_compatibility = 0; +int GLAD_GL_EXT_multisampled_render_to_texture = 0; +int GLAD_GL_EXT_multisampled_render_to_texture2 = 0; +int GLAD_GL_EXT_multiview_draw_buffers = 0; +int GLAD_GL_EXT_multiview_tessellation_geometry_shader = 0; +int GLAD_GL_EXT_multiview_texture_multisample = 0; +int GLAD_GL_EXT_multiview_timer_query = 0; +int GLAD_GL_EXT_occlusion_query_boolean = 0; +int GLAD_GL_EXT_polygon_offset_clamp = 0; +int GLAD_GL_EXT_post_depth_coverage = 0; +int GLAD_GL_EXT_primitive_bounding_box = 0; +int GLAD_GL_EXT_protected_textures = 0; +int GLAD_GL_EXT_pvrtc_sRGB = 0; +int GLAD_GL_EXT_raster_multisample = 0; +int GLAD_GL_EXT_read_format_bgra = 0; +int GLAD_GL_EXT_render_snorm = 0; +int GLAD_GL_EXT_robustness = 0; +int GLAD_GL_EXT_sRGB = 0; +int GLAD_GL_EXT_sRGB_write_control = 0; +int GLAD_GL_EXT_semaphore = 0; +int GLAD_GL_EXT_semaphore_fd = 0; +int GLAD_GL_EXT_semaphore_win32 = 0; +int GLAD_GL_EXT_separate_depth_stencil = 0; +int GLAD_GL_EXT_separate_shader_objects = 0; +int GLAD_GL_EXT_shader_framebuffer_fetch = 0; +int GLAD_GL_EXT_shader_framebuffer_fetch_non_coherent = 0; +int GLAD_GL_EXT_shader_group_vote = 0; +int GLAD_GL_EXT_shader_implicit_conversions = 0; +int GLAD_GL_EXT_shader_integer_mix = 0; +int GLAD_GL_EXT_shader_io_blocks = 0; +int GLAD_GL_EXT_shader_non_constant_global_initializers = 0; +int GLAD_GL_EXT_shader_pixel_local_storage = 0; +int GLAD_GL_EXT_shader_pixel_local_storage2 = 0; +int GLAD_GL_EXT_shader_samples_identical = 0; +int GLAD_GL_EXT_shader_texture_lod = 0; +int GLAD_GL_EXT_shadow_samplers = 0; +int GLAD_GL_EXT_sparse_texture = 0; +int GLAD_GL_EXT_sparse_texture2 = 0; +int GLAD_GL_EXT_tessellation_point_size = 0; +int GLAD_GL_EXT_tessellation_shader = 0; +int GLAD_GL_EXT_texture_border_clamp = 0; +int GLAD_GL_EXT_texture_buffer = 0; +int GLAD_GL_EXT_texture_compression_astc_decode_mode = 0; +int GLAD_GL_EXT_texture_compression_bptc = 0; +int GLAD_GL_EXT_texture_compression_dxt1 = 0; +int GLAD_GL_EXT_texture_compression_rgtc = 0; +int GLAD_GL_EXT_texture_compression_s3tc = 0; +int GLAD_GL_EXT_texture_compression_s3tc_srgb = 0; +int GLAD_GL_EXT_texture_cube_map_array = 0; +int GLAD_GL_EXT_texture_filter_anisotropic = 0; +int GLAD_GL_EXT_texture_filter_minmax = 0; +int GLAD_GL_EXT_texture_format_BGRA8888 = 0; +int GLAD_GL_EXT_texture_format_sRGB_override = 0; +int GLAD_GL_EXT_texture_mirror_clamp_to_edge = 0; +int GLAD_GL_EXT_texture_norm16 = 0; +int GLAD_GL_EXT_texture_query_lod = 0; +int GLAD_GL_EXT_texture_rg = 0; +int GLAD_GL_EXT_texture_sRGB_R8 = 0; +int GLAD_GL_EXT_texture_sRGB_RG8 = 0; +int GLAD_GL_EXT_texture_sRGB_decode = 0; +int GLAD_GL_EXT_texture_shadow_lod = 0; +int GLAD_GL_EXT_texture_storage = 0; +int GLAD_GL_EXT_texture_storage_compression = 0; +int GLAD_GL_EXT_texture_type_2_10_10_10_REV = 0; +int GLAD_GL_EXT_texture_view = 0; +int GLAD_GL_EXT_unpack_subimage = 0; +int GLAD_GL_EXT_win32_keyed_mutex = 0; +int GLAD_GL_EXT_window_rectangles = 0; +int GLAD_GL_KHR_blend_equation_advanced = 0; +int GLAD_GL_KHR_blend_equation_advanced_coherent = 0; +int GLAD_GL_KHR_context_flush_control = 0; +int GLAD_GL_KHR_debug = 0; +int GLAD_GL_KHR_no_error = 0; +int GLAD_GL_KHR_parallel_shader_compile = 0; +int GLAD_GL_KHR_robust_buffer_access_behavior = 0; +int GLAD_GL_KHR_robustness = 0; +int GLAD_GL_KHR_shader_subgroup = 0; +int GLAD_GL_KHR_texture_compression_astc_hdr = 0; +int GLAD_GL_KHR_texture_compression_astc_ldr = 0; +int GLAD_GL_KHR_texture_compression_astc_sliced_3d = 0; +int GLAD_GL_OES_EGL_image = 0; +int GLAD_GL_OES_EGL_image_external = 0; +int GLAD_GL_OES_EGL_image_external_essl3 = 0; +int GLAD_GL_OES_compressed_ETC1_RGB8_sub_texture = 0; +int GLAD_GL_OES_compressed_ETC1_RGB8_texture = 0; +int GLAD_GL_OES_compressed_paletted_texture = 0; +int GLAD_GL_OES_copy_image = 0; +int GLAD_GL_OES_depth24 = 0; +int GLAD_GL_OES_depth32 = 0; +int GLAD_GL_OES_depth_texture = 0; +int GLAD_GL_OES_draw_buffers_indexed = 0; +int GLAD_GL_OES_draw_elements_base_vertex = 0; +int GLAD_GL_OES_element_index_uint = 0; +int GLAD_GL_OES_fbo_render_mipmap = 0; +int GLAD_GL_OES_fragment_precision_high = 0; +int GLAD_GL_OES_geometry_point_size = 0; +int GLAD_GL_OES_geometry_shader = 0; +int GLAD_GL_OES_get_program_binary = 0; +int GLAD_GL_OES_gpu_shader5 = 0; +int GLAD_GL_OES_mapbuffer = 0; +int GLAD_GL_OES_packed_depth_stencil = 0; +int GLAD_GL_OES_primitive_bounding_box = 0; +int GLAD_GL_OES_required_internalformat = 0; +int GLAD_GL_OES_rgb8_rgba8 = 0; +int GLAD_GL_OES_sample_shading = 0; +int GLAD_GL_OES_sample_variables = 0; +int GLAD_GL_OES_shader_image_atomic = 0; +int GLAD_GL_OES_shader_io_blocks = 0; +int GLAD_GL_OES_shader_multisample_interpolation = 0; +int GLAD_GL_OES_standard_derivatives = 0; +int GLAD_GL_OES_stencil1 = 0; +int GLAD_GL_OES_stencil4 = 0; +int GLAD_GL_OES_surfaceless_context = 0; +int GLAD_GL_OES_tessellation_point_size = 0; +int GLAD_GL_OES_tessellation_shader = 0; +int GLAD_GL_OES_texture_3D = 0; +int GLAD_GL_OES_texture_border_clamp = 0; +int GLAD_GL_OES_texture_buffer = 0; +int GLAD_GL_OES_texture_compression_astc = 0; +int GLAD_GL_OES_texture_cube_map_array = 0; +int GLAD_GL_OES_texture_float = 0; +int GLAD_GL_OES_texture_float_linear = 0; +int GLAD_GL_OES_texture_half_float = 0; +int GLAD_GL_OES_texture_half_float_linear = 0; +int GLAD_GL_OES_texture_npot = 0; +int GLAD_GL_OES_texture_stencil8 = 0; +int GLAD_GL_OES_texture_storage_multisample_2d_array = 0; +int GLAD_GL_OES_texture_view = 0; +int GLAD_GL_OES_vertex_array_object = 0; +int GLAD_GL_OES_vertex_half_float = 0; +int GLAD_GL_OES_vertex_type_10_10_10_2 = 0; +int GLAD_GL_OES_viewport_array = 0; + + + +PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC glad_glAcquireKeyedMutexWin32EXT = NULL; +PFNGLACTIVESHADERPROGRAMEXTPROC glad_glActiveShaderProgramEXT = NULL; +PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; +PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; +PFNGLBEGINQUERYEXTPROC glad_glBeginQueryEXT = NULL; +PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; +PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; +PFNGLBINDFRAGDATALOCATIONEXTPROC glad_glBindFragDataLocationEXT = NULL; +PFNGLBINDFRAGDATALOCATIONINDEXEDEXTPROC glad_glBindFragDataLocationIndexedEXT = NULL; +PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; +PFNGLBINDPROGRAMPIPELINEEXTPROC glad_glBindProgramPipelineEXT = NULL; +PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; +PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; +PFNGLBINDVERTEXARRAYOESPROC glad_glBindVertexArrayOES = NULL; +PFNGLBLENDBARRIERKHRPROC glad_glBlendBarrierKHR = NULL; +PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; +PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; +PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; +PFNGLBLENDEQUATIONSEPARATEIEXTPROC glad_glBlendEquationSeparateiEXT = NULL; +PFNGLBLENDEQUATIONSEPARATEIOESPROC glad_glBlendEquationSeparateiOES = NULL; +PFNGLBLENDEQUATIONIEXTPROC glad_glBlendEquationiEXT = NULL; +PFNGLBLENDEQUATIONIOESPROC glad_glBlendEquationiOES = NULL; +PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; +PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; +PFNGLBLENDFUNCSEPARATEIEXTPROC glad_glBlendFuncSeparateiEXT = NULL; +PFNGLBLENDFUNCSEPARATEIOESPROC glad_glBlendFuncSeparateiOES = NULL; +PFNGLBLENDFUNCIEXTPROC glad_glBlendFunciEXT = NULL; +PFNGLBLENDFUNCIOESPROC glad_glBlendFunciOES = NULL; +PFNGLBUFFERDATAPROC glad_glBufferData = NULL; +PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT = NULL; +PFNGLBUFFERSTORAGEEXTERNALEXTPROC glad_glBufferStorageExternalEXT = NULL; +PFNGLBUFFERSTORAGEMEMEXTPROC glad_glBufferStorageMemEXT = NULL; +PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; +PFNGLCLEARPROC glad_glClear = NULL; +PFNGLCLEARCOLORPROC glad_glClearColor = NULL; +PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL; +PFNGLCLEARPIXELLOCALSTORAGEUIEXTPROC glad_glClearPixelLocalStorageuiEXT = NULL; +PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; +PFNGLCLEARTEXIMAGEEXTPROC glad_glClearTexImageEXT = NULL; +PFNGLCLEARTEXSUBIMAGEEXTPROC glad_glClearTexSubImageEXT = NULL; +PFNGLCLIPCONTROLEXTPROC glad_glClipControlEXT = NULL; +PFNGLCOLORMASKPROC glad_glColorMask = NULL; +PFNGLCOLORMASKIEXTPROC glad_glColorMaskiEXT = NULL; +PFNGLCOLORMASKIOESPROC glad_glColorMaskiOES = NULL; +PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; +PFNGLCOMPRESSEDTEXIMAGE3DOESPROC glad_glCompressedTexImage3DOES = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC glad_glCompressedTexSubImage3DOES = NULL; +PFNGLCOPYIMAGESUBDATAEXTPROC glad_glCopyImageSubDataEXT = NULL; +PFNGLCOPYIMAGESUBDATAOESPROC glad_glCopyImageSubDataOES = NULL; +PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE3DOESPROC glad_glCopyTexSubImage3DOES = NULL; +PFNGLCREATEMEMORYOBJECTSEXTPROC glad_glCreateMemoryObjectsEXT = NULL; +PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; +PFNGLCREATESHADERPROC glad_glCreateShader = NULL; +PFNGLCREATESHADERPROGRAMVEXTPROC glad_glCreateShaderProgramvEXT = NULL; +PFNGLCULLFACEPROC glad_glCullFace = NULL; +PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR = NULL; +PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR = NULL; +PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR = NULL; +PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; +PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; +PFNGLDELETEMEMORYOBJECTSEXTPROC glad_glDeleteMemoryObjectsEXT = NULL; +PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; +PFNGLDELETEPROGRAMPIPELINESEXTPROC glad_glDeleteProgramPipelinesEXT = NULL; +PFNGLDELETEQUERIESEXTPROC glad_glDeleteQueriesEXT = NULL; +PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; +PFNGLDELETESEMAPHORESEXTPROC glad_glDeleteSemaphoresEXT = NULL; +PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; +PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; +PFNGLDELETEVERTEXARRAYSOESPROC glad_glDeleteVertexArraysOES = NULL; +PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; +PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; +PFNGLDEPTHRANGEARRAYFVOESPROC glad_glDepthRangeArrayfvOES = NULL; +PFNGLDEPTHRANGEINDEXEDFOESPROC glad_glDepthRangeIndexedfOES = NULL; +PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL; +PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; +PFNGLDISABLEPROC glad_glDisable = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; +PFNGLDISABLEIEXTPROC glad_glDisableiEXT = NULL; +PFNGLDISABLEIOESPROC glad_glDisableiOES = NULL; +PFNGLDISCARDFRAMEBUFFEREXTPROC glad_glDiscardFramebufferEXT = NULL; +PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; +PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC glad_glDrawArraysInstancedBaseInstanceEXT = NULL; +PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT = NULL; +PFNGLDRAWBUFFERSEXTPROC glad_glDrawBuffersEXT = NULL; +PFNGLDRAWBUFFERSINDEXEDEXTPROC glad_glDrawBuffersIndexedEXT = NULL; +PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; +PFNGLDRAWELEMENTSBASEVERTEXEXTPROC glad_glDrawElementsBaseVertexEXT = NULL; +PFNGLDRAWELEMENTSBASEVERTEXOESPROC glad_glDrawElementsBaseVertexOES = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC glad_glDrawElementsInstancedBaseInstanceEXT = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC glad_glDrawElementsInstancedBaseVertexBaseInstanceEXT = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC glad_glDrawElementsInstancedBaseVertexEXT = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC glad_glDrawElementsInstancedBaseVertexOES = NULL; +PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT = NULL; +PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC glad_glDrawRangeElementsBaseVertexEXT = NULL; +PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC glad_glDrawRangeElementsBaseVertexOES = NULL; +PFNGLDRAWTRANSFORMFEEDBACKEXTPROC glad_glDrawTransformFeedbackEXT = NULL; +PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDEXTPROC glad_glDrawTransformFeedbackInstancedEXT = NULL; +PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glad_glEGLImageTargetRenderbufferStorageOES = NULL; +PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glad_glEGLImageTargetTexStorageEXT = NULL; +PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glad_glEGLImageTargetTexture2DOES = NULL; +PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC glad_glEGLImageTargetTextureStorageEXT = NULL; +PFNGLENABLEPROC glad_glEnable = NULL; +PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; +PFNGLENABLEIEXTPROC glad_glEnableiEXT = NULL; +PFNGLENABLEIOESPROC glad_glEnableiOES = NULL; +PFNGLENDQUERYEXTPROC glad_glEndQueryEXT = NULL; +PFNGLFINISHPROC glad_glFinish = NULL; +PFNGLFLUSHPROC glad_glFlush = NULL; +PFNGLFLUSHMAPPEDBUFFERRANGEEXTPROC glad_glFlushMappedBufferRangeEXT = NULL; +PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC glad_glFramebufferFetchBarrierEXT = NULL; +PFNGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC glad_glFramebufferPixelLocalStorageSizeEXT = NULL; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; +PFNGLFRAMEBUFFERSHADINGRATEEXTPROC glad_glFramebufferShadingRateEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; +PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glad_glFramebufferTexture2DMultisampleEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE3DOESPROC glad_glFramebufferTexture3DOES = NULL; +PFNGLFRAMEBUFFERTEXTUREEXTPROC glad_glFramebufferTextureEXT = NULL; +PFNGLFRAMEBUFFERTEXTUREOESPROC glad_glFramebufferTextureOES = NULL; +PFNGLFRONTFACEPROC glad_glFrontFace = NULL; +PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; +PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; +PFNGLGENPROGRAMPIPELINESEXTPROC glad_glGenProgramPipelinesEXT = NULL; +PFNGLGENQUERIESEXTPROC glad_glGenQueriesEXT = NULL; +PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; +PFNGLGENSEMAPHORESEXTPROC glad_glGenSemaphoresEXT = NULL; +PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; +PFNGLGENVERTEXARRAYSOESPROC glad_glGenVertexArraysOES = NULL; +PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; +PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; +PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; +PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; +PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; +PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; +PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; +PFNGLGETBUFFERPOINTERVOESPROC glad_glGetBufferPointervOES = NULL; +PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR = NULL; +PFNGLGETERRORPROC glad_glGetError = NULL; +PFNGLGETFLOATI_VOESPROC glad_glGetFloati_vOES = NULL; +PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; +PFNGLGETFRAGDATAINDEXEXTPROC glad_glGetFragDataIndexEXT = NULL; +PFNGLGETFRAGMENTSHADINGRATESEXTPROC glad_glGetFragmentShadingRatesEXT = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; +PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC glad_glGetFramebufferPixelLocalStorageSizeEXT = NULL; +PFNGLGETGRAPHICSRESETSTATUSEXTPROC glad_glGetGraphicsResetStatusEXT = NULL; +PFNGLGETGRAPHICSRESETSTATUSKHRPROC glad_glGetGraphicsResetStatusKHR = NULL; +PFNGLGETINTEGER64VEXTPROC glad_glGetInteger64vEXT = NULL; +PFNGLGETINTEGERI_VEXTPROC glad_glGetIntegeri_vEXT = NULL; +PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; +PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC glad_glGetMemoryObjectParameterivEXT = NULL; +PFNGLGETOBJECTLABELEXTPROC glad_glGetObjectLabelEXT = NULL; +PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR = NULL; +PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR = NULL; +PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR = NULL; +PFNGLGETPROGRAMBINARYOESPROC glad_glGetProgramBinaryOES = NULL; +PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; +PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC glad_glGetProgramPipelineInfoLogEXT = NULL; +PFNGLGETPROGRAMPIPELINEIVEXTPROC glad_glGetProgramPipelineivEXT = NULL; +PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC glad_glGetProgramResourceLocationIndexEXT = NULL; +PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; +PFNGLGETQUERYOBJECTI64VEXTPROC glad_glGetQueryObjecti64vEXT = NULL; +PFNGLGETQUERYOBJECTIVEXTPROC glad_glGetQueryObjectivEXT = NULL; +PFNGLGETQUERYOBJECTUI64VEXTPROC glad_glGetQueryObjectui64vEXT = NULL; +PFNGLGETQUERYOBJECTUIVEXTPROC glad_glGetQueryObjectuivEXT = NULL; +PFNGLGETQUERYIVEXTPROC glad_glGetQueryivEXT = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; +PFNGLGETSAMPLERPARAMETERIIVEXTPROC glad_glGetSamplerParameterIivEXT = NULL; +PFNGLGETSAMPLERPARAMETERIIVOESPROC glad_glGetSamplerParameterIivOES = NULL; +PFNGLGETSAMPLERPARAMETERIUIVEXTPROC glad_glGetSamplerParameterIuivEXT = NULL; +PFNGLGETSAMPLERPARAMETERIUIVOESPROC glad_glGetSamplerParameterIuivOES = NULL; +PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC glad_glGetSemaphoreParameterui64vEXT = NULL; +PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; +PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL; +PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; +PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; +PFNGLGETSTRINGPROC glad_glGetString = NULL; +PFNGLGETTEXPARAMETERIIVEXTPROC glad_glGetTexParameterIivEXT = NULL; +PFNGLGETTEXPARAMETERIIVOESPROC glad_glGetTexParameterIivOES = NULL; +PFNGLGETTEXPARAMETERIUIVEXTPROC glad_glGetTexParameterIuivEXT = NULL; +PFNGLGETTEXPARAMETERIUIVOESPROC glad_glGetTexParameterIuivOES = NULL; +PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; +PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; +PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; +PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; +PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; +PFNGLGETUNSIGNEDBYTEI_VEXTPROC glad_glGetUnsignedBytei_vEXT = NULL; +PFNGLGETUNSIGNEDBYTEVEXTPROC glad_glGetUnsignedBytevEXT = NULL; +PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; +PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; +PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; +PFNGLGETNUNIFORMFVEXTPROC glad_glGetnUniformfvEXT = NULL; +PFNGLGETNUNIFORMFVKHRPROC glad_glGetnUniformfvKHR = NULL; +PFNGLGETNUNIFORMIVEXTPROC glad_glGetnUniformivEXT = NULL; +PFNGLGETNUNIFORMIVKHRPROC glad_glGetnUniformivKHR = NULL; +PFNGLGETNUNIFORMUIVKHRPROC glad_glGetnUniformuivKHR = NULL; +PFNGLHINTPROC glad_glHint = NULL; +PFNGLIMPORTMEMORYFDEXTPROC glad_glImportMemoryFdEXT = NULL; +PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC glad_glImportMemoryWin32HandleEXT = NULL; +PFNGLIMPORTMEMORYWIN32NAMEEXTPROC glad_glImportMemoryWin32NameEXT = NULL; +PFNGLIMPORTSEMAPHOREFDEXTPROC glad_glImportSemaphoreFdEXT = NULL; +PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC glad_glImportSemaphoreWin32HandleEXT = NULL; +PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC glad_glImportSemaphoreWin32NameEXT = NULL; +PFNGLINSERTEVENTMARKEREXTPROC glad_glInsertEventMarkerEXT = NULL; +PFNGLISBUFFERPROC glad_glIsBuffer = NULL; +PFNGLISENABLEDPROC glad_glIsEnabled = NULL; +PFNGLISENABLEDIEXTPROC glad_glIsEnablediEXT = NULL; +PFNGLISENABLEDIOESPROC glad_glIsEnablediOES = NULL; +PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; +PFNGLISMEMORYOBJECTEXTPROC glad_glIsMemoryObjectEXT = NULL; +PFNGLISPROGRAMPROC glad_glIsProgram = NULL; +PFNGLISPROGRAMPIPELINEEXTPROC glad_glIsProgramPipelineEXT = NULL; +PFNGLISQUERYEXTPROC glad_glIsQueryEXT = NULL; +PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; +PFNGLISSEMAPHOREEXTPROC glad_glIsSemaphoreEXT = NULL; +PFNGLISSHADERPROC glad_glIsShader = NULL; +PFNGLISTEXTUREPROC glad_glIsTexture = NULL; +PFNGLISVERTEXARRAYOESPROC glad_glIsVertexArrayOES = NULL; +PFNGLLABELOBJECTEXTPROC glad_glLabelObjectEXT = NULL; +PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; +PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; +PFNGLMAPBUFFEROESPROC glad_glMapBufferOES = NULL; +PFNGLMAPBUFFERRANGEEXTPROC glad_glMapBufferRangeEXT = NULL; +PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glad_glMaxShaderCompilerThreadsKHR = NULL; +PFNGLMEMORYOBJECTPARAMETERIVEXTPROC glad_glMemoryObjectParameterivEXT = NULL; +PFNGLMINSAMPLESHADINGOESPROC glad_glMinSampleShadingOES = NULL; +PFNGLMULTIDRAWARRAYSEXTPROC glad_glMultiDrawArraysEXT = NULL; +PFNGLMULTIDRAWARRAYSINDIRECTEXTPROC glad_glMultiDrawArraysIndirectEXT = NULL; +PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC glad_glMultiDrawElementsBaseVertexEXT = NULL; +PFNGLMULTIDRAWELEMENTSEXTPROC glad_glMultiDrawElementsEXT = NULL; +PFNGLMULTIDRAWELEMENTSINDIRECTEXTPROC glad_glMultiDrawElementsIndirectEXT = NULL; +PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC glad_glNamedBufferStorageExternalEXT = NULL; +PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC glad_glNamedBufferStorageMemEXT = NULL; +PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR = NULL; +PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR = NULL; +PFNGLPATCHPARAMETERIEXTPROC glad_glPatchParameteriEXT = NULL; +PFNGLPATCHPARAMETERIOESPROC glad_glPatchParameteriOES = NULL; +PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; +PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; +PFNGLPOLYGONOFFSETCLAMPEXTPROC glad_glPolygonOffsetClampEXT = NULL; +PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR = NULL; +PFNGLPOPGROUPMARKEREXTPROC glad_glPopGroupMarkerEXT = NULL; +PFNGLPRIMITIVEBOUNDINGBOXEXTPROC glad_glPrimitiveBoundingBoxEXT = NULL; +PFNGLPRIMITIVEBOUNDINGBOXOESPROC glad_glPrimitiveBoundingBoxOES = NULL; +PFNGLPROGRAMBINARYOESPROC glad_glProgramBinaryOES = NULL; +PFNGLPROGRAMPARAMETERIEXTPROC glad_glProgramParameteriEXT = NULL; +PFNGLPROGRAMUNIFORM1FEXTPROC glad_glProgramUniform1fEXT = NULL; +PFNGLPROGRAMUNIFORM1FVEXTPROC glad_glProgramUniform1fvEXT = NULL; +PFNGLPROGRAMUNIFORM1IEXTPROC glad_glProgramUniform1iEXT = NULL; +PFNGLPROGRAMUNIFORM1IVEXTPROC glad_glProgramUniform1ivEXT = NULL; +PFNGLPROGRAMUNIFORM1UIEXTPROC glad_glProgramUniform1uiEXT = NULL; +PFNGLPROGRAMUNIFORM1UIVEXTPROC glad_glProgramUniform1uivEXT = NULL; +PFNGLPROGRAMUNIFORM2FEXTPROC glad_glProgramUniform2fEXT = NULL; +PFNGLPROGRAMUNIFORM2FVEXTPROC glad_glProgramUniform2fvEXT = NULL; +PFNGLPROGRAMUNIFORM2IEXTPROC glad_glProgramUniform2iEXT = NULL; +PFNGLPROGRAMUNIFORM2IVEXTPROC glad_glProgramUniform2ivEXT = NULL; +PFNGLPROGRAMUNIFORM2UIEXTPROC glad_glProgramUniform2uiEXT = NULL; +PFNGLPROGRAMUNIFORM2UIVEXTPROC glad_glProgramUniform2uivEXT = NULL; +PFNGLPROGRAMUNIFORM3FEXTPROC glad_glProgramUniform3fEXT = NULL; +PFNGLPROGRAMUNIFORM3FVEXTPROC glad_glProgramUniform3fvEXT = NULL; +PFNGLPROGRAMUNIFORM3IEXTPROC glad_glProgramUniform3iEXT = NULL; +PFNGLPROGRAMUNIFORM3IVEXTPROC glad_glProgramUniform3ivEXT = NULL; +PFNGLPROGRAMUNIFORM3UIEXTPROC glad_glProgramUniform3uiEXT = NULL; +PFNGLPROGRAMUNIFORM3UIVEXTPROC glad_glProgramUniform3uivEXT = NULL; +PFNGLPROGRAMUNIFORM4FEXTPROC glad_glProgramUniform4fEXT = NULL; +PFNGLPROGRAMUNIFORM4FVEXTPROC glad_glProgramUniform4fvEXT = NULL; +PFNGLPROGRAMUNIFORM4IEXTPROC glad_glProgramUniform4iEXT = NULL; +PFNGLPROGRAMUNIFORM4IVEXTPROC glad_glProgramUniform4ivEXT = NULL; +PFNGLPROGRAMUNIFORM4UIEXTPROC glad_glProgramUniform4uiEXT = NULL; +PFNGLPROGRAMUNIFORM4UIVEXTPROC glad_glProgramUniform4uivEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glad_glProgramUniformMatrix2fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glad_glProgramUniformMatrix2x3fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glad_glProgramUniformMatrix2x4fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glad_glProgramUniformMatrix3fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glad_glProgramUniformMatrix3x2fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glad_glProgramUniformMatrix3x4fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glad_glProgramUniformMatrix4fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glad_glProgramUniformMatrix4x2fvEXT = NULL; +PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glad_glProgramUniformMatrix4x3fvEXT = NULL; +PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR = NULL; +PFNGLPUSHGROUPMARKEREXTPROC glad_glPushGroupMarkerEXT = NULL; +PFNGLQUERYCOUNTEREXTPROC glad_glQueryCounterEXT = NULL; +PFNGLRASTERSAMPLESEXTPROC glad_glRasterSamplesEXT = NULL; +PFNGLREADBUFFERINDEXEDEXTPROC glad_glReadBufferIndexedEXT = NULL; +PFNGLREADPIXELSPROC glad_glReadPixels = NULL; +PFNGLREADNPIXELSEXTPROC glad_glReadnPixelsEXT = NULL; +PFNGLREADNPIXELSKHRPROC glad_glReadnPixelsKHR = NULL; +PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC glad_glReleaseKeyedMutexWin32EXT = NULL; +PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL; +PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT = NULL; +PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; +PFNGLSAMPLERPARAMETERIIVEXTPROC glad_glSamplerParameterIivEXT = NULL; +PFNGLSAMPLERPARAMETERIIVOESPROC glad_glSamplerParameterIivOES = NULL; +PFNGLSAMPLERPARAMETERIUIVEXTPROC glad_glSamplerParameterIuivEXT = NULL; +PFNGLSAMPLERPARAMETERIUIVOESPROC glad_glSamplerParameterIuivOES = NULL; +PFNGLSCISSORPROC glad_glScissor = NULL; +PFNGLSCISSORARRAYVOESPROC glad_glScissorArrayvOES = NULL; +PFNGLSCISSORINDEXEDOESPROC glad_glScissorIndexedOES = NULL; +PFNGLSCISSORINDEXEDVOESPROC glad_glScissorIndexedvOES = NULL; +PFNGLSEMAPHOREPARAMETERUI64VEXTPROC glad_glSemaphoreParameterui64vEXT = NULL; +PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL; +PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; +PFNGLSHADINGRATECOMBINEROPSEXTPROC glad_glShadingRateCombinerOpsEXT = NULL; +PFNGLSHADINGRATEEXTPROC glad_glShadingRateEXT = NULL; +PFNGLSIGNALSEMAPHOREEXTPROC glad_glSignalSemaphoreEXT = NULL; +PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; +PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; +PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; +PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; +PFNGLSTENCILOPPROC glad_glStencilOp = NULL; +PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; +PFNGLTEXBUFFEREXTPROC glad_glTexBufferEXT = NULL; +PFNGLTEXBUFFEROESPROC glad_glTexBufferOES = NULL; +PFNGLTEXBUFFERRANGEEXTPROC glad_glTexBufferRangeEXT = NULL; +PFNGLTEXBUFFERRANGEOESPROC glad_glTexBufferRangeOES = NULL; +PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; +PFNGLTEXIMAGE3DOESPROC glad_glTexImage3DOES = NULL; +PFNGLTEXPAGECOMMITMENTEXTPROC glad_glTexPageCommitmentEXT = NULL; +PFNGLTEXPARAMETERIIVEXTPROC glad_glTexParameterIivEXT = NULL; +PFNGLTEXPARAMETERIIVOESPROC glad_glTexParameterIivOES = NULL; +PFNGLTEXPARAMETERIUIVEXTPROC glad_glTexParameterIuivEXT = NULL; +PFNGLTEXPARAMETERIUIVOESPROC glad_glTexParameterIuivOES = NULL; +PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; +PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; +PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; +PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; +PFNGLTEXSTORAGE1DEXTPROC glad_glTexStorage1DEXT = NULL; +PFNGLTEXSTORAGE2DEXTPROC glad_glTexStorage2DEXT = NULL; +PFNGLTEXSTORAGE3DEXTPROC glad_glTexStorage3DEXT = NULL; +PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC glad_glTexStorage3DMultisampleOES = NULL; +PFNGLTEXSTORAGEATTRIBS2DEXTPROC glad_glTexStorageAttribs2DEXT = NULL; +PFNGLTEXSTORAGEATTRIBS3DEXTPROC glad_glTexStorageAttribs3DEXT = NULL; +PFNGLTEXSTORAGEMEM2DEXTPROC glad_glTexStorageMem2DEXT = NULL; +PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC glad_glTexStorageMem2DMultisampleEXT = NULL; +PFNGLTEXSTORAGEMEM3DEXTPROC glad_glTexStorageMem3DEXT = NULL; +PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC glad_glTexStorageMem3DMultisampleEXT = NULL; +PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; +PFNGLTEXSUBIMAGE3DOESPROC glad_glTexSubImage3DOES = NULL; +PFNGLTEXTURESTORAGE1DEXTPROC glad_glTextureStorage1DEXT = NULL; +PFNGLTEXTURESTORAGE2DEXTPROC glad_glTextureStorage2DEXT = NULL; +PFNGLTEXTURESTORAGE3DEXTPROC glad_glTextureStorage3DEXT = NULL; +PFNGLTEXTURESTORAGEMEM2DEXTPROC glad_glTextureStorageMem2DEXT = NULL; +PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC glad_glTextureStorageMem2DMultisampleEXT = NULL; +PFNGLTEXTURESTORAGEMEM3DEXTPROC glad_glTextureStorageMem3DEXT = NULL; +PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC glad_glTextureStorageMem3DMultisampleEXT = NULL; +PFNGLTEXTUREVIEWEXTPROC glad_glTextureViewEXT = NULL; +PFNGLTEXTUREVIEWOESPROC glad_glTextureViewOES = NULL; +PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; +PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; +PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; +PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; +PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; +PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; +PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; +PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; +PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; +PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; +PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; +PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; +PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; +PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; +PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; +PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; +PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; +PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; +PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; +PFNGLUNMAPBUFFEROESPROC glad_glUnmapBufferOES = NULL; +PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; +PFNGLUSEPROGRAMSTAGESEXTPROC glad_glUseProgramStagesEXT = NULL; +PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; +PFNGLVALIDATEPROGRAMPIPELINEEXTPROC glad_glValidateProgramPipelineEXT = NULL; +PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; +PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; +PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; +PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; +PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; +PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; +PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; +PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; +PFNGLVERTEXATTRIBDIVISOREXTPROC glad_glVertexAttribDivisorEXT = NULL; +PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; +PFNGLVIEWPORTPROC glad_glViewport = NULL; +PFNGLVIEWPORTARRAYVOESPROC glad_glViewportArrayvOES = NULL; +PFNGLVIEWPORTINDEXEDFOESPROC glad_glViewportIndexedfOES = NULL; +PFNGLVIEWPORTINDEXEDFVOESPROC glad_glViewportIndexedfvOES = NULL; +PFNGLWAITSEMAPHOREEXTPROC glad_glWaitSemaphoreEXT = NULL; +PFNGLWINDOWRECTANGLESEXTPROC glad_glWindowRectanglesEXT = NULL; + + +static void glad_gl_load_GL_ES_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ES_VERSION_2_0) return; + glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); + glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); + glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); + glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); + glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); + glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); + glad_glBindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, "glBindTexture"); + glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); + glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); + glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, "glBlendEquationSeparate"); + glad_glBlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, "glBlendFunc"); + glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, "glBlendFuncSeparate"); + glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); + glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); + glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); + glad_glClear = (PFNGLCLEARPROC) load(userptr, "glClear"); + glad_glClearColor = (PFNGLCLEARCOLORPROC) load(userptr, "glClearColor"); + glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC) load(userptr, "glClearDepthf"); + glad_glClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, "glClearStencil"); + glad_glColorMask = (PFNGLCOLORMASKPROC) load(userptr, "glColorMask"); + glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); + glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); + glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); + glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, "glCopyTexImage2D"); + glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, "glCopyTexSubImage2D"); + glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); + glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); + glad_glCullFace = (PFNGLCULLFACEPROC) load(userptr, "glCullFace"); + glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); + glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); + glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, "glDeleteProgram"); + glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); + glad_glDeleteShader = (PFNGLDELETESHADERPROC) load(userptr, "glDeleteShader"); + glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, "glDeleteTextures"); + glad_glDepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, "glDepthFunc"); + glad_glDepthMask = (PFNGLDEPTHMASKPROC) load(userptr, "glDepthMask"); + glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC) load(userptr, "glDepthRangef"); + glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); + glad_glDisable = (PFNGLDISABLEPROC) load(userptr, "glDisable"); + glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); + glad_glDrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, "glDrawArrays"); + glad_glDrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, "glDrawElements"); + glad_glEnable = (PFNGLENABLEPROC) load(userptr, "glEnable"); + glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); + glad_glFinish = (PFNGLFINISHPROC) load(userptr, "glFinish"); + glad_glFlush = (PFNGLFLUSHPROC) load(userptr, "glFlush"); + glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); + glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); + glad_glFrontFace = (PFNGLFRONTFACEPROC) load(userptr, "glFrontFace"); + glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); + glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); + glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); + glad_glGenTextures = (PFNGLGENTEXTURESPROC) load(userptr, "glGenTextures"); + glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); + glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); + glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); + glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, "glGetAttachedShaders"); + glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); + glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, "glGetBooleanv"); + glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); + glad_glGetError = (PFNGLGETERRORPROC) load(userptr, "glGetError"); + glad_glGetFloatv = (PFNGLGETFLOATVPROC) load(userptr, "glGetFloatv"); + glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); + glad_glGetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, "glGetIntegerv"); + glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, "glGetProgramInfoLog"); + glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, "glGetProgramiv"); + glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); + glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, "glGetShaderInfoLog"); + glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC) load(userptr, "glGetShaderPrecisionFormat"); + glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); + glad_glGetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, "glGetShaderiv"); + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, "glGetTexParameterfv"); + glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, "glGetTexParameteriv"); + glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); + glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); + glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); + glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); + glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); + glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); + glad_glHint = (PFNGLHINTPROC) load(userptr, "glHint"); + glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); + glad_glIsEnabled = (PFNGLISENABLEDPROC) load(userptr, "glIsEnabled"); + glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); + glad_glIsProgram = (PFNGLISPROGRAMPROC) load(userptr, "glIsProgram"); + glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); + glad_glIsShader = (PFNGLISSHADERPROC) load(userptr, "glIsShader"); + glad_glIsTexture = (PFNGLISTEXTUREPROC) load(userptr, "glIsTexture"); + glad_glLineWidth = (PFNGLLINEWIDTHPROC) load(userptr, "glLineWidth"); + glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); + glad_glPixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, "glPixelStorei"); + glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, "glPolygonOffset"); + glad_glReadPixels = (PFNGLREADPIXELSPROC) load(userptr, "glReadPixels"); + glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC) load(userptr, "glReleaseShaderCompiler"); + glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); + glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); + glad_glScissor = (PFNGLSCISSORPROC) load(userptr, "glScissor"); + glad_glShaderBinary = (PFNGLSHADERBINARYPROC) load(userptr, "glShaderBinary"); + glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); + glad_glStencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, "glStencilFunc"); + glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, "glStencilFuncSeparate"); + glad_glStencilMask = (PFNGLSTENCILMASKPROC) load(userptr, "glStencilMask"); + glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, "glStencilMaskSeparate"); + glad_glStencilOp = (PFNGLSTENCILOPPROC) load(userptr, "glStencilOp"); + glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, "glStencilOpSeparate"); + glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, "glTexImage2D"); + glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, "glTexParameterf"); + glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, "glTexParameterfv"); + glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, "glTexParameteri"); + glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, "glTexParameteriv"); + glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, "glTexSubImage2D"); + glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); + glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); + glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); + glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); + glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); + glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); + glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); + glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); + glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); + glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); + glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); + glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); + glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); + glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); + glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); + glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); + glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); + glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); + glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); + glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); + glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); + glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); + glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); + glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); + glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); + glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); + glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); + glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); + glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); + glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); + glad_glViewport = (PFNGLVIEWPORTPROC) load(userptr, "glViewport"); +} +static void glad_gl_load_GL_EXT_EGL_image_storage( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_EGL_image_storage) return; + glad_glEGLImageTargetTexStorageEXT = (PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) load(userptr, "glEGLImageTargetTexStorageEXT"); + glad_glEGLImageTargetTextureStorageEXT = (PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) load(userptr, "glEGLImageTargetTextureStorageEXT"); +} +static void glad_gl_load_GL_EXT_base_instance( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_base_instance) return; + glad_glDrawArraysInstancedBaseInstanceEXT = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEEXTPROC) load(userptr, "glDrawArraysInstancedBaseInstanceEXT"); + glad_glDrawElementsInstancedBaseInstanceEXT = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEEXTPROC) load(userptr, "glDrawElementsInstancedBaseInstanceEXT"); + glad_glDrawElementsInstancedBaseVertexBaseInstanceEXT = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEEXTPROC) load(userptr, "glDrawElementsInstancedBaseVertexBaseInstanceEXT"); +} +static void glad_gl_load_GL_EXT_blend_func_extended( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_blend_func_extended) return; + glad_glBindFragDataLocationEXT = (PFNGLBINDFRAGDATALOCATIONEXTPROC) load(userptr, "glBindFragDataLocationEXT"); + glad_glBindFragDataLocationIndexedEXT = (PFNGLBINDFRAGDATALOCATIONINDEXEDEXTPROC) load(userptr, "glBindFragDataLocationIndexedEXT"); + glad_glGetFragDataIndexEXT = (PFNGLGETFRAGDATAINDEXEXTPROC) load(userptr, "glGetFragDataIndexEXT"); + glad_glGetProgramResourceLocationIndexEXT = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC) load(userptr, "glGetProgramResourceLocationIndexEXT"); +} +static void glad_gl_load_GL_EXT_buffer_storage( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_buffer_storage) return; + glad_glBufferStorageEXT = (PFNGLBUFFERSTORAGEEXTPROC) load(userptr, "glBufferStorageEXT"); +} +static void glad_gl_load_GL_EXT_clear_texture( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_clear_texture) return; + glad_glClearTexImageEXT = (PFNGLCLEARTEXIMAGEEXTPROC) load(userptr, "glClearTexImageEXT"); + glad_glClearTexSubImageEXT = (PFNGLCLEARTEXSUBIMAGEEXTPROC) load(userptr, "glClearTexSubImageEXT"); +} +static void glad_gl_load_GL_EXT_clip_control( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_clip_control) return; + glad_glClipControlEXT = (PFNGLCLIPCONTROLEXTPROC) load(userptr, "glClipControlEXT"); +} +static void glad_gl_load_GL_EXT_copy_image( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_copy_image) return; + glad_glCopyImageSubDataEXT = (PFNGLCOPYIMAGESUBDATAEXTPROC) load(userptr, "glCopyImageSubDataEXT"); +} +static void glad_gl_load_GL_EXT_debug_label( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_debug_label) return; + glad_glGetObjectLabelEXT = (PFNGLGETOBJECTLABELEXTPROC) load(userptr, "glGetObjectLabelEXT"); + glad_glLabelObjectEXT = (PFNGLLABELOBJECTEXTPROC) load(userptr, "glLabelObjectEXT"); +} +static void glad_gl_load_GL_EXT_debug_marker( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_debug_marker) return; + glad_glInsertEventMarkerEXT = (PFNGLINSERTEVENTMARKEREXTPROC) load(userptr, "glInsertEventMarkerEXT"); + glad_glPopGroupMarkerEXT = (PFNGLPOPGROUPMARKEREXTPROC) load(userptr, "glPopGroupMarkerEXT"); + glad_glPushGroupMarkerEXT = (PFNGLPUSHGROUPMARKEREXTPROC) load(userptr, "glPushGroupMarkerEXT"); +} +static void glad_gl_load_GL_EXT_discard_framebuffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_discard_framebuffer) return; + glad_glDiscardFramebufferEXT = (PFNGLDISCARDFRAMEBUFFEREXTPROC) load(userptr, "glDiscardFramebufferEXT"); +} +static void glad_gl_load_GL_EXT_disjoint_timer_query( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_disjoint_timer_query) return; + glad_glBeginQueryEXT = (PFNGLBEGINQUERYEXTPROC) load(userptr, "glBeginQueryEXT"); + glad_glDeleteQueriesEXT = (PFNGLDELETEQUERIESEXTPROC) load(userptr, "glDeleteQueriesEXT"); + glad_glEndQueryEXT = (PFNGLENDQUERYEXTPROC) load(userptr, "glEndQueryEXT"); + glad_glGenQueriesEXT = (PFNGLGENQUERIESEXTPROC) load(userptr, "glGenQueriesEXT"); + glad_glGetInteger64vEXT = (PFNGLGETINTEGER64VEXTPROC) load(userptr, "glGetInteger64vEXT"); + glad_glGetQueryObjecti64vEXT = (PFNGLGETQUERYOBJECTI64VEXTPROC) load(userptr, "glGetQueryObjecti64vEXT"); + glad_glGetQueryObjectivEXT = (PFNGLGETQUERYOBJECTIVEXTPROC) load(userptr, "glGetQueryObjectivEXT"); + glad_glGetQueryObjectui64vEXT = (PFNGLGETQUERYOBJECTUI64VEXTPROC) load(userptr, "glGetQueryObjectui64vEXT"); + glad_glGetQueryObjectuivEXT = (PFNGLGETQUERYOBJECTUIVEXTPROC) load(userptr, "glGetQueryObjectuivEXT"); + glad_glGetQueryivEXT = (PFNGLGETQUERYIVEXTPROC) load(userptr, "glGetQueryivEXT"); + glad_glIsQueryEXT = (PFNGLISQUERYEXTPROC) load(userptr, "glIsQueryEXT"); + glad_glQueryCounterEXT = (PFNGLQUERYCOUNTEREXTPROC) load(userptr, "glQueryCounterEXT"); +} +static void glad_gl_load_GL_EXT_draw_buffers( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_buffers) return; + glad_glDrawBuffersEXT = (PFNGLDRAWBUFFERSEXTPROC) load(userptr, "glDrawBuffersEXT"); +} +static void glad_gl_load_GL_EXT_draw_buffers_indexed( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_buffers_indexed) return; + glad_glBlendEquationSeparateiEXT = (PFNGLBLENDEQUATIONSEPARATEIEXTPROC) load(userptr, "glBlendEquationSeparateiEXT"); + glad_glBlendEquationiEXT = (PFNGLBLENDEQUATIONIEXTPROC) load(userptr, "glBlendEquationiEXT"); + glad_glBlendFuncSeparateiEXT = (PFNGLBLENDFUNCSEPARATEIEXTPROC) load(userptr, "glBlendFuncSeparateiEXT"); + glad_glBlendFunciEXT = (PFNGLBLENDFUNCIEXTPROC) load(userptr, "glBlendFunciEXT"); + glad_glColorMaskiEXT = (PFNGLCOLORMASKIEXTPROC) load(userptr, "glColorMaskiEXT"); + glad_glDisableiEXT = (PFNGLDISABLEIEXTPROC) load(userptr, "glDisableiEXT"); + glad_glEnableiEXT = (PFNGLENABLEIEXTPROC) load(userptr, "glEnableiEXT"); + glad_glIsEnablediEXT = (PFNGLISENABLEDIEXTPROC) load(userptr, "glIsEnablediEXT"); +} +static void glad_gl_load_GL_EXT_draw_elements_base_vertex( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_elements_base_vertex) return; + glad_glDrawElementsBaseVertexEXT = (PFNGLDRAWELEMENTSBASEVERTEXEXTPROC) load(userptr, "glDrawElementsBaseVertexEXT"); + glad_glDrawElementsInstancedBaseVertexEXT = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC) load(userptr, "glDrawElementsInstancedBaseVertexEXT"); + glad_glDrawRangeElementsBaseVertexEXT = (PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC) load(userptr, "glDrawRangeElementsBaseVertexEXT"); + glad_glMultiDrawElementsBaseVertexEXT = (PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC) load(userptr, "glMultiDrawElementsBaseVertexEXT"); +} +static void glad_gl_load_GL_EXT_draw_instanced( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_instanced) return; + glad_glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC) load(userptr, "glDrawArraysInstancedEXT"); + glad_glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC) load(userptr, "glDrawElementsInstancedEXT"); +} +static void glad_gl_load_GL_EXT_draw_transform_feedback( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_transform_feedback) return; + glad_glDrawTransformFeedbackEXT = (PFNGLDRAWTRANSFORMFEEDBACKEXTPROC) load(userptr, "glDrawTransformFeedbackEXT"); + glad_glDrawTransformFeedbackInstancedEXT = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDEXTPROC) load(userptr, "glDrawTransformFeedbackInstancedEXT"); +} +static void glad_gl_load_GL_EXT_external_buffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_external_buffer) return; + glad_glBufferStorageExternalEXT = (PFNGLBUFFERSTORAGEEXTERNALEXTPROC) load(userptr, "glBufferStorageExternalEXT"); + glad_glNamedBufferStorageExternalEXT = (PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC) load(userptr, "glNamedBufferStorageExternalEXT"); +} +static void glad_gl_load_GL_EXT_fragment_shading_rate( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_fragment_shading_rate) return; + glad_glFramebufferShadingRateEXT = (PFNGLFRAMEBUFFERSHADINGRATEEXTPROC) load(userptr, "glFramebufferShadingRateEXT"); + glad_glGetFragmentShadingRatesEXT = (PFNGLGETFRAGMENTSHADINGRATESEXTPROC) load(userptr, "glGetFragmentShadingRatesEXT"); + glad_glShadingRateCombinerOpsEXT = (PFNGLSHADINGRATECOMBINEROPSEXTPROC) load(userptr, "glShadingRateCombinerOpsEXT"); + glad_glShadingRateEXT = (PFNGLSHADINGRATEEXTPROC) load(userptr, "glShadingRateEXT"); +} +static void glad_gl_load_GL_EXT_geometry_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_geometry_shader) return; + glad_glFramebufferTextureEXT = (PFNGLFRAMEBUFFERTEXTUREEXTPROC) load(userptr, "glFramebufferTextureEXT"); +} +static void glad_gl_load_GL_EXT_instanced_arrays( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_instanced_arrays) return; + glad_glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC) load(userptr, "glDrawArraysInstancedEXT"); + glad_glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC) load(userptr, "glDrawElementsInstancedEXT"); + glad_glVertexAttribDivisorEXT = (PFNGLVERTEXATTRIBDIVISOREXTPROC) load(userptr, "glVertexAttribDivisorEXT"); +} +static void glad_gl_load_GL_EXT_map_buffer_range( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_map_buffer_range) return; + glad_glFlushMappedBufferRangeEXT = (PFNGLFLUSHMAPPEDBUFFERRANGEEXTPROC) load(userptr, "glFlushMappedBufferRangeEXT"); + glad_glMapBufferRangeEXT = (PFNGLMAPBUFFERRANGEEXTPROC) load(userptr, "glMapBufferRangeEXT"); +} +static void glad_gl_load_GL_EXT_memory_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_memory_object) return; + glad_glBufferStorageMemEXT = (PFNGLBUFFERSTORAGEMEMEXTPROC) load(userptr, "glBufferStorageMemEXT"); + glad_glCreateMemoryObjectsEXT = (PFNGLCREATEMEMORYOBJECTSEXTPROC) load(userptr, "glCreateMemoryObjectsEXT"); + glad_glDeleteMemoryObjectsEXT = (PFNGLDELETEMEMORYOBJECTSEXTPROC) load(userptr, "glDeleteMemoryObjectsEXT"); + glad_glGetMemoryObjectParameterivEXT = (PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC) load(userptr, "glGetMemoryObjectParameterivEXT"); + glad_glGetUnsignedBytei_vEXT = (PFNGLGETUNSIGNEDBYTEI_VEXTPROC) load(userptr, "glGetUnsignedBytei_vEXT"); + glad_glGetUnsignedBytevEXT = (PFNGLGETUNSIGNEDBYTEVEXTPROC) load(userptr, "glGetUnsignedBytevEXT"); + glad_glIsMemoryObjectEXT = (PFNGLISMEMORYOBJECTEXTPROC) load(userptr, "glIsMemoryObjectEXT"); + glad_glMemoryObjectParameterivEXT = (PFNGLMEMORYOBJECTPARAMETERIVEXTPROC) load(userptr, "glMemoryObjectParameterivEXT"); + glad_glNamedBufferStorageMemEXT = (PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC) load(userptr, "glNamedBufferStorageMemEXT"); + glad_glTexStorageMem2DEXT = (PFNGLTEXSTORAGEMEM2DEXTPROC) load(userptr, "glTexStorageMem2DEXT"); + glad_glTexStorageMem2DMultisampleEXT = (PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC) load(userptr, "glTexStorageMem2DMultisampleEXT"); + glad_glTexStorageMem3DEXT = (PFNGLTEXSTORAGEMEM3DEXTPROC) load(userptr, "glTexStorageMem3DEXT"); + glad_glTexStorageMem3DMultisampleEXT = (PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC) load(userptr, "glTexStorageMem3DMultisampleEXT"); + glad_glTextureStorageMem2DEXT = (PFNGLTEXTURESTORAGEMEM2DEXTPROC) load(userptr, "glTextureStorageMem2DEXT"); + glad_glTextureStorageMem2DMultisampleEXT = (PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC) load(userptr, "glTextureStorageMem2DMultisampleEXT"); + glad_glTextureStorageMem3DEXT = (PFNGLTEXTURESTORAGEMEM3DEXTPROC) load(userptr, "glTextureStorageMem3DEXT"); + glad_glTextureStorageMem3DMultisampleEXT = (PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC) load(userptr, "glTextureStorageMem3DMultisampleEXT"); +} +static void glad_gl_load_GL_EXT_memory_object_fd( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_memory_object_fd) return; + glad_glImportMemoryFdEXT = (PFNGLIMPORTMEMORYFDEXTPROC) load(userptr, "glImportMemoryFdEXT"); +} +static void glad_gl_load_GL_EXT_memory_object_win32( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_memory_object_win32) return; + glad_glImportMemoryWin32HandleEXT = (PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC) load(userptr, "glImportMemoryWin32HandleEXT"); + glad_glImportMemoryWin32NameEXT = (PFNGLIMPORTMEMORYWIN32NAMEEXTPROC) load(userptr, "glImportMemoryWin32NameEXT"); +} +static void glad_gl_load_GL_EXT_multi_draw_arrays( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_multi_draw_arrays) return; + glad_glMultiDrawArraysEXT = (PFNGLMULTIDRAWARRAYSEXTPROC) load(userptr, "glMultiDrawArraysEXT"); + glad_glMultiDrawElementsEXT = (PFNGLMULTIDRAWELEMENTSEXTPROC) load(userptr, "glMultiDrawElementsEXT"); +} +static void glad_gl_load_GL_EXT_multi_draw_indirect( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_multi_draw_indirect) return; + glad_glMultiDrawArraysIndirectEXT = (PFNGLMULTIDRAWARRAYSINDIRECTEXTPROC) load(userptr, "glMultiDrawArraysIndirectEXT"); + glad_glMultiDrawElementsIndirectEXT = (PFNGLMULTIDRAWELEMENTSINDIRECTEXTPROC) load(userptr, "glMultiDrawElementsIndirectEXT"); +} +static void glad_gl_load_GL_EXT_multisampled_render_to_texture( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_multisampled_render_to_texture) return; + glad_glFramebufferTexture2DMultisampleEXT = (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC) load(userptr, "glFramebufferTexture2DMultisampleEXT"); + glad_glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) load(userptr, "glRenderbufferStorageMultisampleEXT"); +} +static void glad_gl_load_GL_EXT_multiview_draw_buffers( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_multiview_draw_buffers) return; + glad_glDrawBuffersIndexedEXT = (PFNGLDRAWBUFFERSINDEXEDEXTPROC) load(userptr, "glDrawBuffersIndexedEXT"); + glad_glGetIntegeri_vEXT = (PFNGLGETINTEGERI_VEXTPROC) load(userptr, "glGetIntegeri_vEXT"); + glad_glReadBufferIndexedEXT = (PFNGLREADBUFFERINDEXEDEXTPROC) load(userptr, "glReadBufferIndexedEXT"); +} +static void glad_gl_load_GL_EXT_occlusion_query_boolean( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_occlusion_query_boolean) return; + glad_glBeginQueryEXT = (PFNGLBEGINQUERYEXTPROC) load(userptr, "glBeginQueryEXT"); + glad_glDeleteQueriesEXT = (PFNGLDELETEQUERIESEXTPROC) load(userptr, "glDeleteQueriesEXT"); + glad_glEndQueryEXT = (PFNGLENDQUERYEXTPROC) load(userptr, "glEndQueryEXT"); + glad_glGenQueriesEXT = (PFNGLGENQUERIESEXTPROC) load(userptr, "glGenQueriesEXT"); + glad_glGetQueryObjectuivEXT = (PFNGLGETQUERYOBJECTUIVEXTPROC) load(userptr, "glGetQueryObjectuivEXT"); + glad_glGetQueryivEXT = (PFNGLGETQUERYIVEXTPROC) load(userptr, "glGetQueryivEXT"); + glad_glIsQueryEXT = (PFNGLISQUERYEXTPROC) load(userptr, "glIsQueryEXT"); +} +static void glad_gl_load_GL_EXT_polygon_offset_clamp( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_polygon_offset_clamp) return; + glad_glPolygonOffsetClampEXT = (PFNGLPOLYGONOFFSETCLAMPEXTPROC) load(userptr, "glPolygonOffsetClampEXT"); +} +static void glad_gl_load_GL_EXT_primitive_bounding_box( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_primitive_bounding_box) return; + glad_glPrimitiveBoundingBoxEXT = (PFNGLPRIMITIVEBOUNDINGBOXEXTPROC) load(userptr, "glPrimitiveBoundingBoxEXT"); +} +static void glad_gl_load_GL_EXT_raster_multisample( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_raster_multisample) return; + glad_glRasterSamplesEXT = (PFNGLRASTERSAMPLESEXTPROC) load(userptr, "glRasterSamplesEXT"); +} +static void glad_gl_load_GL_EXT_robustness( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_robustness) return; + glad_glGetGraphicsResetStatusEXT = (PFNGLGETGRAPHICSRESETSTATUSEXTPROC) load(userptr, "glGetGraphicsResetStatusEXT"); + glad_glGetnUniformfvEXT = (PFNGLGETNUNIFORMFVEXTPROC) load(userptr, "glGetnUniformfvEXT"); + glad_glGetnUniformivEXT = (PFNGLGETNUNIFORMIVEXTPROC) load(userptr, "glGetnUniformivEXT"); + glad_glReadnPixelsEXT = (PFNGLREADNPIXELSEXTPROC) load(userptr, "glReadnPixelsEXT"); +} +static void glad_gl_load_GL_EXT_semaphore( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_semaphore) return; + glad_glDeleteSemaphoresEXT = (PFNGLDELETESEMAPHORESEXTPROC) load(userptr, "glDeleteSemaphoresEXT"); + glad_glGenSemaphoresEXT = (PFNGLGENSEMAPHORESEXTPROC) load(userptr, "glGenSemaphoresEXT"); + glad_glGetSemaphoreParameterui64vEXT = (PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC) load(userptr, "glGetSemaphoreParameterui64vEXT"); + glad_glGetUnsignedBytei_vEXT = (PFNGLGETUNSIGNEDBYTEI_VEXTPROC) load(userptr, "glGetUnsignedBytei_vEXT"); + glad_glGetUnsignedBytevEXT = (PFNGLGETUNSIGNEDBYTEVEXTPROC) load(userptr, "glGetUnsignedBytevEXT"); + glad_glIsSemaphoreEXT = (PFNGLISSEMAPHOREEXTPROC) load(userptr, "glIsSemaphoreEXT"); + glad_glSemaphoreParameterui64vEXT = (PFNGLSEMAPHOREPARAMETERUI64VEXTPROC) load(userptr, "glSemaphoreParameterui64vEXT"); + glad_glSignalSemaphoreEXT = (PFNGLSIGNALSEMAPHOREEXTPROC) load(userptr, "glSignalSemaphoreEXT"); + glad_glWaitSemaphoreEXT = (PFNGLWAITSEMAPHOREEXTPROC) load(userptr, "glWaitSemaphoreEXT"); +} +static void glad_gl_load_GL_EXT_semaphore_fd( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_semaphore_fd) return; + glad_glImportSemaphoreFdEXT = (PFNGLIMPORTSEMAPHOREFDEXTPROC) load(userptr, "glImportSemaphoreFdEXT"); +} +static void glad_gl_load_GL_EXT_semaphore_win32( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_semaphore_win32) return; + glad_glImportSemaphoreWin32HandleEXT = (PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC) load(userptr, "glImportSemaphoreWin32HandleEXT"); + glad_glImportSemaphoreWin32NameEXT = (PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC) load(userptr, "glImportSemaphoreWin32NameEXT"); +} +static void glad_gl_load_GL_EXT_separate_shader_objects( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_separate_shader_objects) return; + glad_glActiveShaderProgramEXT = (PFNGLACTIVESHADERPROGRAMEXTPROC) load(userptr, "glActiveShaderProgramEXT"); + glad_glBindProgramPipelineEXT = (PFNGLBINDPROGRAMPIPELINEEXTPROC) load(userptr, "glBindProgramPipelineEXT"); + glad_glCreateShaderProgramvEXT = (PFNGLCREATESHADERPROGRAMVEXTPROC) load(userptr, "glCreateShaderProgramvEXT"); + glad_glDeleteProgramPipelinesEXT = (PFNGLDELETEPROGRAMPIPELINESEXTPROC) load(userptr, "glDeleteProgramPipelinesEXT"); + glad_glGenProgramPipelinesEXT = (PFNGLGENPROGRAMPIPELINESEXTPROC) load(userptr, "glGenProgramPipelinesEXT"); + glad_glGetProgramPipelineInfoLogEXT = (PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC) load(userptr, "glGetProgramPipelineInfoLogEXT"); + glad_glGetProgramPipelineivEXT = (PFNGLGETPROGRAMPIPELINEIVEXTPROC) load(userptr, "glGetProgramPipelineivEXT"); + glad_glIsProgramPipelineEXT = (PFNGLISPROGRAMPIPELINEEXTPROC) load(userptr, "glIsProgramPipelineEXT"); + glad_glProgramParameteriEXT = (PFNGLPROGRAMPARAMETERIEXTPROC) load(userptr, "glProgramParameteriEXT"); + glad_glProgramUniform1fEXT = (PFNGLPROGRAMUNIFORM1FEXTPROC) load(userptr, "glProgramUniform1fEXT"); + glad_glProgramUniform1fvEXT = (PFNGLPROGRAMUNIFORM1FVEXTPROC) load(userptr, "glProgramUniform1fvEXT"); + glad_glProgramUniform1iEXT = (PFNGLPROGRAMUNIFORM1IEXTPROC) load(userptr, "glProgramUniform1iEXT"); + glad_glProgramUniform1ivEXT = (PFNGLPROGRAMUNIFORM1IVEXTPROC) load(userptr, "glProgramUniform1ivEXT"); + glad_glProgramUniform1uiEXT = (PFNGLPROGRAMUNIFORM1UIEXTPROC) load(userptr, "glProgramUniform1uiEXT"); + glad_glProgramUniform1uivEXT = (PFNGLPROGRAMUNIFORM1UIVEXTPROC) load(userptr, "glProgramUniform1uivEXT"); + glad_glProgramUniform2fEXT = (PFNGLPROGRAMUNIFORM2FEXTPROC) load(userptr, "glProgramUniform2fEXT"); + glad_glProgramUniform2fvEXT = (PFNGLPROGRAMUNIFORM2FVEXTPROC) load(userptr, "glProgramUniform2fvEXT"); + glad_glProgramUniform2iEXT = (PFNGLPROGRAMUNIFORM2IEXTPROC) load(userptr, "glProgramUniform2iEXT"); + glad_glProgramUniform2ivEXT = (PFNGLPROGRAMUNIFORM2IVEXTPROC) load(userptr, "glProgramUniform2ivEXT"); + glad_glProgramUniform2uiEXT = (PFNGLPROGRAMUNIFORM2UIEXTPROC) load(userptr, "glProgramUniform2uiEXT"); + glad_glProgramUniform2uivEXT = (PFNGLPROGRAMUNIFORM2UIVEXTPROC) load(userptr, "glProgramUniform2uivEXT"); + glad_glProgramUniform3fEXT = (PFNGLPROGRAMUNIFORM3FEXTPROC) load(userptr, "glProgramUniform3fEXT"); + glad_glProgramUniform3fvEXT = (PFNGLPROGRAMUNIFORM3FVEXTPROC) load(userptr, "glProgramUniform3fvEXT"); + glad_glProgramUniform3iEXT = (PFNGLPROGRAMUNIFORM3IEXTPROC) load(userptr, "glProgramUniform3iEXT"); + glad_glProgramUniform3ivEXT = (PFNGLPROGRAMUNIFORM3IVEXTPROC) load(userptr, "glProgramUniform3ivEXT"); + glad_glProgramUniform3uiEXT = (PFNGLPROGRAMUNIFORM3UIEXTPROC) load(userptr, "glProgramUniform3uiEXT"); + glad_glProgramUniform3uivEXT = (PFNGLPROGRAMUNIFORM3UIVEXTPROC) load(userptr, "glProgramUniform3uivEXT"); + glad_glProgramUniform4fEXT = (PFNGLPROGRAMUNIFORM4FEXTPROC) load(userptr, "glProgramUniform4fEXT"); + glad_glProgramUniform4fvEXT = (PFNGLPROGRAMUNIFORM4FVEXTPROC) load(userptr, "glProgramUniform4fvEXT"); + glad_glProgramUniform4iEXT = (PFNGLPROGRAMUNIFORM4IEXTPROC) load(userptr, "glProgramUniform4iEXT"); + glad_glProgramUniform4ivEXT = (PFNGLPROGRAMUNIFORM4IVEXTPROC) load(userptr, "glProgramUniform4ivEXT"); + glad_glProgramUniform4uiEXT = (PFNGLPROGRAMUNIFORM4UIEXTPROC) load(userptr, "glProgramUniform4uiEXT"); + glad_glProgramUniform4uivEXT = (PFNGLPROGRAMUNIFORM4UIVEXTPROC) load(userptr, "glProgramUniform4uivEXT"); + glad_glProgramUniformMatrix2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) load(userptr, "glProgramUniformMatrix2fvEXT"); + glad_glProgramUniformMatrix2x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) load(userptr, "glProgramUniformMatrix2x3fvEXT"); + glad_glProgramUniformMatrix2x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) load(userptr, "glProgramUniformMatrix2x4fvEXT"); + glad_glProgramUniformMatrix3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) load(userptr, "glProgramUniformMatrix3fvEXT"); + glad_glProgramUniformMatrix3x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) load(userptr, "glProgramUniformMatrix3x2fvEXT"); + glad_glProgramUniformMatrix3x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) load(userptr, "glProgramUniformMatrix3x4fvEXT"); + glad_glProgramUniformMatrix4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) load(userptr, "glProgramUniformMatrix4fvEXT"); + glad_glProgramUniformMatrix4x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) load(userptr, "glProgramUniformMatrix4x2fvEXT"); + glad_glProgramUniformMatrix4x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) load(userptr, "glProgramUniformMatrix4x3fvEXT"); + glad_glUseProgramStagesEXT = (PFNGLUSEPROGRAMSTAGESEXTPROC) load(userptr, "glUseProgramStagesEXT"); + glad_glValidateProgramPipelineEXT = (PFNGLVALIDATEPROGRAMPIPELINEEXTPROC) load(userptr, "glValidateProgramPipelineEXT"); +} +static void glad_gl_load_GL_EXT_shader_framebuffer_fetch_non_coherent( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_shader_framebuffer_fetch_non_coherent) return; + glad_glFramebufferFetchBarrierEXT = (PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) load(userptr, "glFramebufferFetchBarrierEXT"); +} +static void glad_gl_load_GL_EXT_shader_pixel_local_storage2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_shader_pixel_local_storage2) return; + glad_glClearPixelLocalStorageuiEXT = (PFNGLCLEARPIXELLOCALSTORAGEUIEXTPROC) load(userptr, "glClearPixelLocalStorageuiEXT"); + glad_glFramebufferPixelLocalStorageSizeEXT = (PFNGLFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC) load(userptr, "glFramebufferPixelLocalStorageSizeEXT"); + glad_glGetFramebufferPixelLocalStorageSizeEXT = (PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC) load(userptr, "glGetFramebufferPixelLocalStorageSizeEXT"); +} +static void glad_gl_load_GL_EXT_sparse_texture( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_sparse_texture) return; + glad_glTexPageCommitmentEXT = (PFNGLTEXPAGECOMMITMENTEXTPROC) load(userptr, "glTexPageCommitmentEXT"); +} +static void glad_gl_load_GL_EXT_tessellation_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_tessellation_shader) return; + glad_glPatchParameteriEXT = (PFNGLPATCHPARAMETERIEXTPROC) load(userptr, "glPatchParameteriEXT"); +} +static void glad_gl_load_GL_EXT_texture_border_clamp( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_border_clamp) return; + glad_glGetSamplerParameterIivEXT = (PFNGLGETSAMPLERPARAMETERIIVEXTPROC) load(userptr, "glGetSamplerParameterIivEXT"); + glad_glGetSamplerParameterIuivEXT = (PFNGLGETSAMPLERPARAMETERIUIVEXTPROC) load(userptr, "glGetSamplerParameterIuivEXT"); + glad_glGetTexParameterIivEXT = (PFNGLGETTEXPARAMETERIIVEXTPROC) load(userptr, "glGetTexParameterIivEXT"); + glad_glGetTexParameterIuivEXT = (PFNGLGETTEXPARAMETERIUIVEXTPROC) load(userptr, "glGetTexParameterIuivEXT"); + glad_glSamplerParameterIivEXT = (PFNGLSAMPLERPARAMETERIIVEXTPROC) load(userptr, "glSamplerParameterIivEXT"); + glad_glSamplerParameterIuivEXT = (PFNGLSAMPLERPARAMETERIUIVEXTPROC) load(userptr, "glSamplerParameterIuivEXT"); + glad_glTexParameterIivEXT = (PFNGLTEXPARAMETERIIVEXTPROC) load(userptr, "glTexParameterIivEXT"); + glad_glTexParameterIuivEXT = (PFNGLTEXPARAMETERIUIVEXTPROC) load(userptr, "glTexParameterIuivEXT"); +} +static void glad_gl_load_GL_EXT_texture_buffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_buffer) return; + glad_glTexBufferEXT = (PFNGLTEXBUFFEREXTPROC) load(userptr, "glTexBufferEXT"); + glad_glTexBufferRangeEXT = (PFNGLTEXBUFFERRANGEEXTPROC) load(userptr, "glTexBufferRangeEXT"); +} +static void glad_gl_load_GL_EXT_texture_storage( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_storage) return; + glad_glTexStorage1DEXT = (PFNGLTEXSTORAGE1DEXTPROC) load(userptr, "glTexStorage1DEXT"); + glad_glTexStorage2DEXT = (PFNGLTEXSTORAGE2DEXTPROC) load(userptr, "glTexStorage2DEXT"); + glad_glTexStorage3DEXT = (PFNGLTEXSTORAGE3DEXTPROC) load(userptr, "glTexStorage3DEXT"); + glad_glTextureStorage1DEXT = (PFNGLTEXTURESTORAGE1DEXTPROC) load(userptr, "glTextureStorage1DEXT"); + glad_glTextureStorage2DEXT = (PFNGLTEXTURESTORAGE2DEXTPROC) load(userptr, "glTextureStorage2DEXT"); + glad_glTextureStorage3DEXT = (PFNGLTEXTURESTORAGE3DEXTPROC) load(userptr, "glTextureStorage3DEXT"); +} +static void glad_gl_load_GL_EXT_texture_storage_compression( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_storage_compression) return; + glad_glTexStorageAttribs2DEXT = (PFNGLTEXSTORAGEATTRIBS2DEXTPROC) load(userptr, "glTexStorageAttribs2DEXT"); + glad_glTexStorageAttribs3DEXT = (PFNGLTEXSTORAGEATTRIBS3DEXTPROC) load(userptr, "glTexStorageAttribs3DEXT"); +} +static void glad_gl_load_GL_EXT_texture_view( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_texture_view) return; + glad_glTextureViewEXT = (PFNGLTEXTUREVIEWEXTPROC) load(userptr, "glTextureViewEXT"); +} +static void glad_gl_load_GL_EXT_win32_keyed_mutex( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_win32_keyed_mutex) return; + glad_glAcquireKeyedMutexWin32EXT = (PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC) load(userptr, "glAcquireKeyedMutexWin32EXT"); + glad_glReleaseKeyedMutexWin32EXT = (PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC) load(userptr, "glReleaseKeyedMutexWin32EXT"); +} +static void glad_gl_load_GL_EXT_window_rectangles( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_window_rectangles) return; + glad_glWindowRectanglesEXT = (PFNGLWINDOWRECTANGLESEXTPROC) load(userptr, "glWindowRectanglesEXT"); +} +static void glad_gl_load_GL_KHR_blend_equation_advanced( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_blend_equation_advanced) return; + glad_glBlendBarrierKHR = (PFNGLBLENDBARRIERKHRPROC) load(userptr, "glBlendBarrierKHR"); +} +static void glad_gl_load_GL_KHR_debug( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_debug) return; + glad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC) load(userptr, "glDebugMessageCallbackKHR"); + glad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC) load(userptr, "glDebugMessageControlKHR"); + glad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC) load(userptr, "glDebugMessageInsertKHR"); + glad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC) load(userptr, "glGetDebugMessageLogKHR"); + glad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC) load(userptr, "glGetObjectLabelKHR"); + glad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC) load(userptr, "glGetObjectPtrLabelKHR"); + glad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC) load(userptr, "glGetPointervKHR"); + glad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC) load(userptr, "glObjectLabelKHR"); + glad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC) load(userptr, "glObjectPtrLabelKHR"); + glad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC) load(userptr, "glPopDebugGroupKHR"); + glad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC) load(userptr, "glPushDebugGroupKHR"); +} +static void glad_gl_load_GL_KHR_parallel_shader_compile( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_parallel_shader_compile) return; + glad_glMaxShaderCompilerThreadsKHR = (PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) load(userptr, "glMaxShaderCompilerThreadsKHR"); +} +static void glad_gl_load_GL_KHR_robustness( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_KHR_robustness) return; + glad_glGetGraphicsResetStatusKHR = (PFNGLGETGRAPHICSRESETSTATUSKHRPROC) load(userptr, "glGetGraphicsResetStatusKHR"); + glad_glGetnUniformfvKHR = (PFNGLGETNUNIFORMFVKHRPROC) load(userptr, "glGetnUniformfvKHR"); + glad_glGetnUniformivKHR = (PFNGLGETNUNIFORMIVKHRPROC) load(userptr, "glGetnUniformivKHR"); + glad_glGetnUniformuivKHR = (PFNGLGETNUNIFORMUIVKHRPROC) load(userptr, "glGetnUniformuivKHR"); + glad_glReadnPixelsKHR = (PFNGLREADNPIXELSKHRPROC) load(userptr, "glReadnPixelsKHR"); +} +static void glad_gl_load_GL_OES_EGL_image( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_EGL_image) return; + glad_glEGLImageTargetRenderbufferStorageOES = (PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) load(userptr, "glEGLImageTargetRenderbufferStorageOES"); + glad_glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) load(userptr, "glEGLImageTargetTexture2DOES"); +} +static void glad_gl_load_GL_OES_copy_image( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_copy_image) return; + glad_glCopyImageSubDataOES = (PFNGLCOPYIMAGESUBDATAOESPROC) load(userptr, "glCopyImageSubDataOES"); +} +static void glad_gl_load_GL_OES_draw_buffers_indexed( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_draw_buffers_indexed) return; + glad_glBlendEquationSeparateiOES = (PFNGLBLENDEQUATIONSEPARATEIOESPROC) load(userptr, "glBlendEquationSeparateiOES"); + glad_glBlendEquationiOES = (PFNGLBLENDEQUATIONIOESPROC) load(userptr, "glBlendEquationiOES"); + glad_glBlendFuncSeparateiOES = (PFNGLBLENDFUNCSEPARATEIOESPROC) load(userptr, "glBlendFuncSeparateiOES"); + glad_glBlendFunciOES = (PFNGLBLENDFUNCIOESPROC) load(userptr, "glBlendFunciOES"); + glad_glColorMaskiOES = (PFNGLCOLORMASKIOESPROC) load(userptr, "glColorMaskiOES"); + glad_glDisableiOES = (PFNGLDISABLEIOESPROC) load(userptr, "glDisableiOES"); + glad_glEnableiOES = (PFNGLENABLEIOESPROC) load(userptr, "glEnableiOES"); + glad_glIsEnablediOES = (PFNGLISENABLEDIOESPROC) load(userptr, "glIsEnablediOES"); +} +static void glad_gl_load_GL_OES_draw_elements_base_vertex( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_draw_elements_base_vertex) return; + glad_glDrawElementsBaseVertexOES = (PFNGLDRAWELEMENTSBASEVERTEXOESPROC) load(userptr, "glDrawElementsBaseVertexOES"); + glad_glDrawElementsInstancedBaseVertexOES = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC) load(userptr, "glDrawElementsInstancedBaseVertexOES"); + glad_glDrawRangeElementsBaseVertexOES = (PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC) load(userptr, "glDrawRangeElementsBaseVertexOES"); + glad_glMultiDrawElementsBaseVertexEXT = (PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC) load(userptr, "glMultiDrawElementsBaseVertexEXT"); +} +static void glad_gl_load_GL_OES_geometry_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_geometry_shader) return; + glad_glFramebufferTextureOES = (PFNGLFRAMEBUFFERTEXTUREOESPROC) load(userptr, "glFramebufferTextureOES"); +} +static void glad_gl_load_GL_OES_get_program_binary( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_get_program_binary) return; + glad_glGetProgramBinaryOES = (PFNGLGETPROGRAMBINARYOESPROC) load(userptr, "glGetProgramBinaryOES"); + glad_glProgramBinaryOES = (PFNGLPROGRAMBINARYOESPROC) load(userptr, "glProgramBinaryOES"); +} +static void glad_gl_load_GL_OES_mapbuffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_mapbuffer) return; + glad_glGetBufferPointervOES = (PFNGLGETBUFFERPOINTERVOESPROC) load(userptr, "glGetBufferPointervOES"); + glad_glMapBufferOES = (PFNGLMAPBUFFEROESPROC) load(userptr, "glMapBufferOES"); + glad_glUnmapBufferOES = (PFNGLUNMAPBUFFEROESPROC) load(userptr, "glUnmapBufferOES"); +} +static void glad_gl_load_GL_OES_primitive_bounding_box( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_primitive_bounding_box) return; + glad_glPrimitiveBoundingBoxOES = (PFNGLPRIMITIVEBOUNDINGBOXOESPROC) load(userptr, "glPrimitiveBoundingBoxOES"); +} +static void glad_gl_load_GL_OES_sample_shading( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_sample_shading) return; + glad_glMinSampleShadingOES = (PFNGLMINSAMPLESHADINGOESPROC) load(userptr, "glMinSampleShadingOES"); +} +static void glad_gl_load_GL_OES_tessellation_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_tessellation_shader) return; + glad_glPatchParameteriOES = (PFNGLPATCHPARAMETERIOESPROC) load(userptr, "glPatchParameteriOES"); +} +static void glad_gl_load_GL_OES_texture_3D( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_3D) return; + glad_glCompressedTexImage3DOES = (PFNGLCOMPRESSEDTEXIMAGE3DOESPROC) load(userptr, "glCompressedTexImage3DOES"); + glad_glCompressedTexSubImage3DOES = (PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC) load(userptr, "glCompressedTexSubImage3DOES"); + glad_glCopyTexSubImage3DOES = (PFNGLCOPYTEXSUBIMAGE3DOESPROC) load(userptr, "glCopyTexSubImage3DOES"); + glad_glFramebufferTexture3DOES = (PFNGLFRAMEBUFFERTEXTURE3DOESPROC) load(userptr, "glFramebufferTexture3DOES"); + glad_glTexImage3DOES = (PFNGLTEXIMAGE3DOESPROC) load(userptr, "glTexImage3DOES"); + glad_glTexSubImage3DOES = (PFNGLTEXSUBIMAGE3DOESPROC) load(userptr, "glTexSubImage3DOES"); +} +static void glad_gl_load_GL_OES_texture_border_clamp( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_border_clamp) return; + glad_glGetSamplerParameterIivOES = (PFNGLGETSAMPLERPARAMETERIIVOESPROC) load(userptr, "glGetSamplerParameterIivOES"); + glad_glGetSamplerParameterIuivOES = (PFNGLGETSAMPLERPARAMETERIUIVOESPROC) load(userptr, "glGetSamplerParameterIuivOES"); + glad_glGetTexParameterIivOES = (PFNGLGETTEXPARAMETERIIVOESPROC) load(userptr, "glGetTexParameterIivOES"); + glad_glGetTexParameterIuivOES = (PFNGLGETTEXPARAMETERIUIVOESPROC) load(userptr, "glGetTexParameterIuivOES"); + glad_glSamplerParameterIivOES = (PFNGLSAMPLERPARAMETERIIVOESPROC) load(userptr, "glSamplerParameterIivOES"); + glad_glSamplerParameterIuivOES = (PFNGLSAMPLERPARAMETERIUIVOESPROC) load(userptr, "glSamplerParameterIuivOES"); + glad_glTexParameterIivOES = (PFNGLTEXPARAMETERIIVOESPROC) load(userptr, "glTexParameterIivOES"); + glad_glTexParameterIuivOES = (PFNGLTEXPARAMETERIUIVOESPROC) load(userptr, "glTexParameterIuivOES"); +} +static void glad_gl_load_GL_OES_texture_buffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_buffer) return; + glad_glTexBufferOES = (PFNGLTEXBUFFEROESPROC) load(userptr, "glTexBufferOES"); + glad_glTexBufferRangeOES = (PFNGLTEXBUFFERRANGEOESPROC) load(userptr, "glTexBufferRangeOES"); +} +static void glad_gl_load_GL_OES_texture_storage_multisample_2d_array( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_storage_multisample_2d_array) return; + glad_glTexStorage3DMultisampleOES = (PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC) load(userptr, "glTexStorage3DMultisampleOES"); +} +static void glad_gl_load_GL_OES_texture_view( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_texture_view) return; + glad_glTextureViewOES = (PFNGLTEXTUREVIEWOESPROC) load(userptr, "glTextureViewOES"); +} +static void glad_gl_load_GL_OES_vertex_array_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_vertex_array_object) return; + glad_glBindVertexArrayOES = (PFNGLBINDVERTEXARRAYOESPROC) load(userptr, "glBindVertexArrayOES"); + glad_glDeleteVertexArraysOES = (PFNGLDELETEVERTEXARRAYSOESPROC) load(userptr, "glDeleteVertexArraysOES"); + glad_glGenVertexArraysOES = (PFNGLGENVERTEXARRAYSOESPROC) load(userptr, "glGenVertexArraysOES"); + glad_glIsVertexArrayOES = (PFNGLISVERTEXARRAYOESPROC) load(userptr, "glIsVertexArrayOES"); +} +static void glad_gl_load_GL_OES_viewport_array( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_viewport_array) return; + glad_glDepthRangeArrayfvOES = (PFNGLDEPTHRANGEARRAYFVOESPROC) load(userptr, "glDepthRangeArrayfvOES"); + glad_glDepthRangeIndexedfOES = (PFNGLDEPTHRANGEINDEXEDFOESPROC) load(userptr, "glDepthRangeIndexedfOES"); + glad_glDisableiOES = (PFNGLDISABLEIOESPROC) load(userptr, "glDisableiOES"); + glad_glEnableiOES = (PFNGLENABLEIOESPROC) load(userptr, "glEnableiOES"); + glad_glGetFloati_vOES = (PFNGLGETFLOATI_VOESPROC) load(userptr, "glGetFloati_vOES"); + glad_glIsEnablediOES = (PFNGLISENABLEDIOESPROC) load(userptr, "glIsEnablediOES"); + glad_glScissorArrayvOES = (PFNGLSCISSORARRAYVOESPROC) load(userptr, "glScissorArrayvOES"); + glad_glScissorIndexedOES = (PFNGLSCISSORINDEXEDOESPROC) load(userptr, "glScissorIndexedOES"); + glad_glScissorIndexedvOES = (PFNGLSCISSORINDEXEDVOESPROC) load(userptr, "glScissorIndexedvOES"); + glad_glViewportArrayvOES = (PFNGLVIEWPORTARRAYVOESPROC) load(userptr, "glViewportArrayvOES"); + glad_glViewportIndexedfOES = (PFNGLVIEWPORTINDEXEDFOESPROC) load(userptr, "glViewportIndexedfOES"); + glad_glViewportIndexedfvOES = (PFNGLVIEWPORTINDEXEDFVOESPROC) load(userptr, "glViewportIndexedfvOES"); +} + + + +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define GLAD_GL_IS_SOME_NEW_VERSION 1 +#else +#define GLAD_GL_IS_SOME_NEW_VERSION 0 +#endif + +static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { +#if GLAD_GL_IS_SOME_NEW_VERSION + if(GLAD_VERSION_MAJOR(version) < 3) { +#else + GLAD_UNUSED(version); + GLAD_UNUSED(out_num_exts_i); + GLAD_UNUSED(out_exts_i); +#endif + if (glad_glGetString == NULL) { + return 0; + } + *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); +#if GLAD_GL_IS_SOME_NEW_VERSION + } else { + unsigned int index = 0; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) { + return 0; + } + glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); + if (num_exts_i > 0) { + exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); + } + if (exts_i == NULL) { + return 0; + } + for(index = 0; index < num_exts_i; index++) { + const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index); + size_t len = strlen(gl_str_tmp) + 1; + + char *local_str = (char*) malloc(len * sizeof(char)); + if(local_str != NULL) { + memcpy(local_str, gl_str_tmp, len * sizeof(char)); + } + + exts_i[index] = local_str; + } + + *out_num_exts_i = num_exts_i; + *out_exts_i = exts_i; + } +#endif + return 1; +} +static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { + if (exts_i != NULL) { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + free((void *) (exts_i[index])); + } + free((void *)exts_i); + exts_i = NULL; + } +} +static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { + if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { + const char *extensions; + const char *loc; + const char *terminator; + extensions = exts; + if(extensions == NULL || ext == NULL) { + return 0; + } + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } + } else { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + if(strcmp(e, ext) == 0) { + return 1; + } + } + } + return 0; +} + +static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_gl_find_extensions_gles2( int version) { + const char *exts = NULL; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; + + GLAD_GL_EXT_EGL_image_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_EGL_image_array"); + GLAD_GL_EXT_EGL_image_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_EGL_image_storage"); + GLAD_GL_EXT_EGL_image_storage_compression = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_EGL_image_storage_compression"); + GLAD_GL_EXT_YUV_target = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_YUV_target"); + GLAD_GL_EXT_base_instance = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_base_instance"); + GLAD_GL_EXT_blend_func_extended = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_blend_func_extended"); + GLAD_GL_EXT_blend_minmax = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_blend_minmax"); + GLAD_GL_EXT_buffer_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_buffer_storage"); + GLAD_GL_EXT_clear_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_clear_texture"); + GLAD_GL_EXT_clip_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_clip_control"); + GLAD_GL_EXT_clip_cull_distance = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_clip_cull_distance"); + GLAD_GL_EXT_color_buffer_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_color_buffer_float"); + GLAD_GL_EXT_color_buffer_half_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_color_buffer_half_float"); + GLAD_GL_EXT_conservative_depth = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_conservative_depth"); + GLAD_GL_EXT_copy_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_copy_image"); + GLAD_GL_EXT_debug_label = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_debug_label"); + GLAD_GL_EXT_debug_marker = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_debug_marker"); + GLAD_GL_EXT_depth_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_depth_clamp"); + GLAD_GL_EXT_discard_framebuffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_discard_framebuffer"); + GLAD_GL_EXT_disjoint_timer_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_disjoint_timer_query"); + GLAD_GL_EXT_draw_buffers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_buffers"); + GLAD_GL_EXT_draw_buffers_indexed = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_buffers_indexed"); + GLAD_GL_EXT_draw_elements_base_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_elements_base_vertex"); + GLAD_GL_EXT_draw_instanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_instanced"); + GLAD_GL_EXT_draw_transform_feedback = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_transform_feedback"); + GLAD_GL_EXT_external_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_external_buffer"); + GLAD_GL_EXT_float_blend = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_float_blend"); + GLAD_GL_EXT_fragment_shading_rate = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_fragment_shading_rate"); + GLAD_GL_EXT_geometry_point_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_geometry_point_size"); + GLAD_GL_EXT_geometry_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_geometry_shader"); + GLAD_GL_EXT_gpu_shader5 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_gpu_shader5"); + GLAD_GL_EXT_instanced_arrays = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_instanced_arrays"); + GLAD_GL_EXT_map_buffer_range = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_map_buffer_range"); + GLAD_GL_EXT_memory_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_memory_object"); + GLAD_GL_EXT_memory_object_fd = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_memory_object_fd"); + GLAD_GL_EXT_memory_object_win32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_memory_object_win32"); + GLAD_GL_EXT_multi_draw_arrays = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multi_draw_arrays"); + GLAD_GL_EXT_multi_draw_indirect = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multi_draw_indirect"); + GLAD_GL_EXT_multisampled_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multisampled_compatibility"); + GLAD_GL_EXT_multisampled_render_to_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multisampled_render_to_texture"); + GLAD_GL_EXT_multisampled_render_to_texture2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multisampled_render_to_texture2"); + GLAD_GL_EXT_multiview_draw_buffers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multiview_draw_buffers"); + GLAD_GL_EXT_multiview_tessellation_geometry_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multiview_tessellation_geometry_shader"); + GLAD_GL_EXT_multiview_texture_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multiview_texture_multisample"); + GLAD_GL_EXT_multiview_timer_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_multiview_timer_query"); + GLAD_GL_EXT_occlusion_query_boolean = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_occlusion_query_boolean"); + GLAD_GL_EXT_polygon_offset_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_polygon_offset_clamp"); + GLAD_GL_EXT_post_depth_coverage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_post_depth_coverage"); + GLAD_GL_EXT_primitive_bounding_box = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_primitive_bounding_box"); + GLAD_GL_EXT_protected_textures = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_protected_textures"); + GLAD_GL_EXT_pvrtc_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_pvrtc_sRGB"); + GLAD_GL_EXT_raster_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_raster_multisample"); + GLAD_GL_EXT_read_format_bgra = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_read_format_bgra"); + GLAD_GL_EXT_render_snorm = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_render_snorm"); + GLAD_GL_EXT_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_robustness"); + GLAD_GL_EXT_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_sRGB"); + GLAD_GL_EXT_sRGB_write_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_sRGB_write_control"); + GLAD_GL_EXT_semaphore = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_semaphore"); + GLAD_GL_EXT_semaphore_fd = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_semaphore_fd"); + GLAD_GL_EXT_semaphore_win32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_semaphore_win32"); + GLAD_GL_EXT_separate_depth_stencil = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_separate_depth_stencil"); + GLAD_GL_EXT_separate_shader_objects = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_separate_shader_objects"); + GLAD_GL_EXT_shader_framebuffer_fetch = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_framebuffer_fetch"); + GLAD_GL_EXT_shader_framebuffer_fetch_non_coherent = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_framebuffer_fetch_non_coherent"); + GLAD_GL_EXT_shader_group_vote = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_group_vote"); + GLAD_GL_EXT_shader_implicit_conversions = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_implicit_conversions"); + GLAD_GL_EXT_shader_integer_mix = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_integer_mix"); + GLAD_GL_EXT_shader_io_blocks = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_io_blocks"); + GLAD_GL_EXT_shader_non_constant_global_initializers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_non_constant_global_initializers"); + GLAD_GL_EXT_shader_pixel_local_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_pixel_local_storage"); + GLAD_GL_EXT_shader_pixel_local_storage2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_pixel_local_storage2"); + GLAD_GL_EXT_shader_samples_identical = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_samples_identical"); + GLAD_GL_EXT_shader_texture_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shader_texture_lod"); + GLAD_GL_EXT_shadow_samplers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_shadow_samplers"); + GLAD_GL_EXT_sparse_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_sparse_texture"); + GLAD_GL_EXT_sparse_texture2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_sparse_texture2"); + GLAD_GL_EXT_tessellation_point_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_tessellation_point_size"); + GLAD_GL_EXT_tessellation_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_tessellation_shader"); + GLAD_GL_EXT_texture_border_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_border_clamp"); + GLAD_GL_EXT_texture_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_buffer"); + GLAD_GL_EXT_texture_compression_astc_decode_mode = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_astc_decode_mode"); + GLAD_GL_EXT_texture_compression_bptc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_bptc"); + GLAD_GL_EXT_texture_compression_dxt1 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_dxt1"); + GLAD_GL_EXT_texture_compression_rgtc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_rgtc"); + GLAD_GL_EXT_texture_compression_s3tc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc"); + GLAD_GL_EXT_texture_compression_s3tc_srgb = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc_srgb"); + GLAD_GL_EXT_texture_cube_map_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_cube_map_array"); + GLAD_GL_EXT_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_filter_anisotropic"); + GLAD_GL_EXT_texture_filter_minmax = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_filter_minmax"); + GLAD_GL_EXT_texture_format_BGRA8888 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_format_BGRA8888"); + GLAD_GL_EXT_texture_format_sRGB_override = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_format_sRGB_override"); + GLAD_GL_EXT_texture_mirror_clamp_to_edge = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_mirror_clamp_to_edge"); + GLAD_GL_EXT_texture_norm16 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_norm16"); + GLAD_GL_EXT_texture_query_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_query_lod"); + GLAD_GL_EXT_texture_rg = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_rg"); + GLAD_GL_EXT_texture_sRGB_R8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_sRGB_R8"); + GLAD_GL_EXT_texture_sRGB_RG8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_sRGB_RG8"); + GLAD_GL_EXT_texture_sRGB_decode = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_sRGB_decode"); + GLAD_GL_EXT_texture_shadow_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_shadow_lod"); + GLAD_GL_EXT_texture_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_storage"); + GLAD_GL_EXT_texture_storage_compression = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_storage_compression"); + GLAD_GL_EXT_texture_type_2_10_10_10_REV = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_type_2_10_10_10_REV"); + GLAD_GL_EXT_texture_view = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_view"); + GLAD_GL_EXT_unpack_subimage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_unpack_subimage"); + GLAD_GL_EXT_win32_keyed_mutex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_win32_keyed_mutex"); + GLAD_GL_EXT_window_rectangles = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_window_rectangles"); + GLAD_GL_KHR_blend_equation_advanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_blend_equation_advanced"); + GLAD_GL_KHR_blend_equation_advanced_coherent = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_blend_equation_advanced_coherent"); + GLAD_GL_KHR_context_flush_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_context_flush_control"); + GLAD_GL_KHR_debug = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_debug"); + GLAD_GL_KHR_no_error = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_no_error"); + GLAD_GL_KHR_parallel_shader_compile = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_parallel_shader_compile"); + GLAD_GL_KHR_robust_buffer_access_behavior = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_robust_buffer_access_behavior"); + GLAD_GL_KHR_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_robustness"); + GLAD_GL_KHR_shader_subgroup = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_shader_subgroup"); + GLAD_GL_KHR_texture_compression_astc_hdr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_hdr"); + GLAD_GL_KHR_texture_compression_astc_ldr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_ldr"); + GLAD_GL_KHR_texture_compression_astc_sliced_3d = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_sliced_3d"); + GLAD_GL_OES_EGL_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_EGL_image"); + GLAD_GL_OES_EGL_image_external = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_EGL_image_external"); + GLAD_GL_OES_EGL_image_external_essl3 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_EGL_image_external_essl3"); + GLAD_GL_OES_compressed_ETC1_RGB8_sub_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_ETC1_RGB8_sub_texture"); + GLAD_GL_OES_compressed_ETC1_RGB8_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_ETC1_RGB8_texture"); + GLAD_GL_OES_compressed_paletted_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_paletted_texture"); + GLAD_GL_OES_copy_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_copy_image"); + GLAD_GL_OES_depth24 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_depth24"); + GLAD_GL_OES_depth32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_depth32"); + GLAD_GL_OES_depth_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_depth_texture"); + GLAD_GL_OES_draw_buffers_indexed = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_draw_buffers_indexed"); + GLAD_GL_OES_draw_elements_base_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_draw_elements_base_vertex"); + GLAD_GL_OES_element_index_uint = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_element_index_uint"); + GLAD_GL_OES_fbo_render_mipmap = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_fbo_render_mipmap"); + GLAD_GL_OES_fragment_precision_high = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_fragment_precision_high"); + GLAD_GL_OES_geometry_point_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_geometry_point_size"); + GLAD_GL_OES_geometry_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_geometry_shader"); + GLAD_GL_OES_get_program_binary = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_get_program_binary"); + GLAD_GL_OES_gpu_shader5 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_gpu_shader5"); + GLAD_GL_OES_mapbuffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_mapbuffer"); + GLAD_GL_OES_packed_depth_stencil = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_packed_depth_stencil"); + GLAD_GL_OES_primitive_bounding_box = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_primitive_bounding_box"); + GLAD_GL_OES_required_internalformat = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_required_internalformat"); + GLAD_GL_OES_rgb8_rgba8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_rgb8_rgba8"); + GLAD_GL_OES_sample_shading = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_sample_shading"); + GLAD_GL_OES_sample_variables = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_sample_variables"); + GLAD_GL_OES_shader_image_atomic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_shader_image_atomic"); + GLAD_GL_OES_shader_io_blocks = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_shader_io_blocks"); + GLAD_GL_OES_shader_multisample_interpolation = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_shader_multisample_interpolation"); + GLAD_GL_OES_standard_derivatives = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_standard_derivatives"); + GLAD_GL_OES_stencil1 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_stencil1"); + GLAD_GL_OES_stencil4 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_stencil4"); + GLAD_GL_OES_surfaceless_context = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_surfaceless_context"); + GLAD_GL_OES_tessellation_point_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_tessellation_point_size"); + GLAD_GL_OES_tessellation_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_tessellation_shader"); + GLAD_GL_OES_texture_3D = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_3D"); + GLAD_GL_OES_texture_border_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_border_clamp"); + GLAD_GL_OES_texture_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_buffer"); + GLAD_GL_OES_texture_compression_astc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_compression_astc"); + GLAD_GL_OES_texture_cube_map_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_cube_map_array"); + GLAD_GL_OES_texture_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_float"); + GLAD_GL_OES_texture_float_linear = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_float_linear"); + GLAD_GL_OES_texture_half_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_half_float"); + GLAD_GL_OES_texture_half_float_linear = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_half_float_linear"); + GLAD_GL_OES_texture_npot = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_npot"); + GLAD_GL_OES_texture_stencil8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_stencil8"); + GLAD_GL_OES_texture_storage_multisample_2d_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_storage_multisample_2d_array"); + GLAD_GL_OES_texture_view = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_texture_view"); + GLAD_GL_OES_vertex_array_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_vertex_array_object"); + GLAD_GL_OES_vertex_half_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_vertex_half_float"); + GLAD_GL_OES_vertex_type_10_10_10_2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_vertex_type_10_10_10_2"); + GLAD_GL_OES_viewport_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_viewport_array"); + + glad_gl_free_extensions(exts_i, num_exts_i); + + return 1; +} + +static int glad_gl_find_core_gles2(void) { + int i; + const char* version; + const char* prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + "OpenGL SC ", + NULL + }; + int major = 0; + int minor = 0; + version = (const char*) glad_glGetString(GL_VERSION); + if (!version) return 0; + for (i = 0; prefixes[i]; i++) { + const size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } + + GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); + + GLAD_GL_ES_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr) { + int version; + + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + if(glad_glGetString == NULL) return 0; + if(glad_glGetString(GL_VERSION) == NULL) return 0; + version = glad_gl_find_core_gles2(); + + glad_gl_load_GL_ES_VERSION_2_0(load, userptr); + + if (!glad_gl_find_extensions_gles2(version)) return 0; + glad_gl_load_GL_EXT_EGL_image_storage(load, userptr); + glad_gl_load_GL_EXT_base_instance(load, userptr); + glad_gl_load_GL_EXT_blend_func_extended(load, userptr); + glad_gl_load_GL_EXT_buffer_storage(load, userptr); + glad_gl_load_GL_EXT_clear_texture(load, userptr); + glad_gl_load_GL_EXT_clip_control(load, userptr); + glad_gl_load_GL_EXT_copy_image(load, userptr); + glad_gl_load_GL_EXT_debug_label(load, userptr); + glad_gl_load_GL_EXT_debug_marker(load, userptr); + glad_gl_load_GL_EXT_discard_framebuffer(load, userptr); + glad_gl_load_GL_EXT_disjoint_timer_query(load, userptr); + glad_gl_load_GL_EXT_draw_buffers(load, userptr); + glad_gl_load_GL_EXT_draw_buffers_indexed(load, userptr); + glad_gl_load_GL_EXT_draw_elements_base_vertex(load, userptr); + glad_gl_load_GL_EXT_draw_instanced(load, userptr); + glad_gl_load_GL_EXT_draw_transform_feedback(load, userptr); + glad_gl_load_GL_EXT_external_buffer(load, userptr); + glad_gl_load_GL_EXT_fragment_shading_rate(load, userptr); + glad_gl_load_GL_EXT_geometry_shader(load, userptr); + glad_gl_load_GL_EXT_instanced_arrays(load, userptr); + glad_gl_load_GL_EXT_map_buffer_range(load, userptr); + glad_gl_load_GL_EXT_memory_object(load, userptr); + glad_gl_load_GL_EXT_memory_object_fd(load, userptr); + glad_gl_load_GL_EXT_memory_object_win32(load, userptr); + glad_gl_load_GL_EXT_multi_draw_arrays(load, userptr); + glad_gl_load_GL_EXT_multi_draw_indirect(load, userptr); + glad_gl_load_GL_EXT_multisampled_render_to_texture(load, userptr); + glad_gl_load_GL_EXT_multiview_draw_buffers(load, userptr); + glad_gl_load_GL_EXT_occlusion_query_boolean(load, userptr); + glad_gl_load_GL_EXT_polygon_offset_clamp(load, userptr); + glad_gl_load_GL_EXT_primitive_bounding_box(load, userptr); + glad_gl_load_GL_EXT_raster_multisample(load, userptr); + glad_gl_load_GL_EXT_robustness(load, userptr); + glad_gl_load_GL_EXT_semaphore(load, userptr); + glad_gl_load_GL_EXT_semaphore_fd(load, userptr); + glad_gl_load_GL_EXT_semaphore_win32(load, userptr); + glad_gl_load_GL_EXT_separate_shader_objects(load, userptr); + glad_gl_load_GL_EXT_shader_framebuffer_fetch_non_coherent(load, userptr); + glad_gl_load_GL_EXT_shader_pixel_local_storage2(load, userptr); + glad_gl_load_GL_EXT_sparse_texture(load, userptr); + glad_gl_load_GL_EXT_tessellation_shader(load, userptr); + glad_gl_load_GL_EXT_texture_border_clamp(load, userptr); + glad_gl_load_GL_EXT_texture_buffer(load, userptr); + glad_gl_load_GL_EXT_texture_storage(load, userptr); + glad_gl_load_GL_EXT_texture_storage_compression(load, userptr); + glad_gl_load_GL_EXT_texture_view(load, userptr); + glad_gl_load_GL_EXT_win32_keyed_mutex(load, userptr); + glad_gl_load_GL_EXT_window_rectangles(load, userptr); + glad_gl_load_GL_KHR_blend_equation_advanced(load, userptr); + glad_gl_load_GL_KHR_debug(load, userptr); + glad_gl_load_GL_KHR_parallel_shader_compile(load, userptr); + glad_gl_load_GL_KHR_robustness(load, userptr); + glad_gl_load_GL_OES_EGL_image(load, userptr); + glad_gl_load_GL_OES_copy_image(load, userptr); + glad_gl_load_GL_OES_draw_buffers_indexed(load, userptr); + glad_gl_load_GL_OES_draw_elements_base_vertex(load, userptr); + glad_gl_load_GL_OES_geometry_shader(load, userptr); + glad_gl_load_GL_OES_get_program_binary(load, userptr); + glad_gl_load_GL_OES_mapbuffer(load, userptr); + glad_gl_load_GL_OES_primitive_bounding_box(load, userptr); + glad_gl_load_GL_OES_sample_shading(load, userptr); + glad_gl_load_GL_OES_tessellation_shader(load, userptr); + glad_gl_load_GL_OES_texture_3D(load, userptr); + glad_gl_load_GL_OES_texture_border_clamp(load, userptr); + glad_gl_load_GL_OES_texture_buffer(load, userptr); + glad_gl_load_GL_OES_texture_storage_multisample_2d_array(load, userptr); + glad_gl_load_GL_OES_texture_view(load, userptr); + glad_gl_load_GL_OES_vertex_array_object(load, userptr); + glad_gl_load_GL_OES_viewport_array(load, userptr); + + + + return version; +} + + +int gladLoadGLES2( GLADloadfunc load) { + return gladLoadGLES2UserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* GLAD_GLES2_IMPLEMENTATION */ + diff --git a/src/rlgl.h b/src/rlgl.h index a7226a818..ba0f08713 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -789,10 +789,16 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #endif #if defined(GRAPHICS_API_OPENGL_ES2) + + #if defined(PLATFORM_DESKTOP) + #define GLAD_GLES2_IMPLEMENTATION + #include "external/glad_gles2.h" + #else #define GL_GLEXT_PROTOTYPES //#include // EGL library -> not required, platform layer #include // OpenGL ES 2.0 library #include // OpenGL ES 2.0 extensions library + #endif // 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) @@ -2144,6 +2150,12 @@ void rlLoadExtensions(void *loader) #endif // GRAPHICS_API_OPENGL_33 #if defined(GRAPHICS_API_OPENGL_ES2) + + #if defined(PLATFORM_DESKTOP) + if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES2.0 loaded successfully"); + #endif + // Get supported extensions list GLint numExt = 0; const char **extList = RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) From 3028bffd4c39e622c0a2b95f275ae6156a86639f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Jan 2023 12:38:21 +0100 Subject: [PATCH 0208/1710] Minor tweaks --- src/rlgl.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index ba0f08713..fbf70051a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -789,15 +789,16 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #endif #if defined(GRAPHICS_API_OPENGL_ES2) - + // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, + // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 #if defined(PLATFORM_DESKTOP) - #define GLAD_GLES2_IMPLEMENTATION - #include "external/glad_gles2.h" + #define GLAD_GLES2_IMPLEMENTATION + #include "external/glad_gles2.h" #else - #define GL_GLEXT_PROTOTYPES - //#include // EGL library -> not required, platform layer - #include // OpenGL ES 2.0 library - #include // OpenGL ES 2.0 extensions library + #define GL_GLEXT_PROTOTYPES + //#include // EGL library -> not required, platform layer + #include // OpenGL ES 2.0 library + #include // OpenGL ES 2.0 extensions library #endif // It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi From c6376acfc465d589db584ff36788a01360ec0771 Mon Sep 17 00:00:00 2001 From: Daijiro Fukuda Date: Tue, 10 Jan 2023 20:45:53 +0900 Subject: [PATCH 0209/1710] Set initial window position for display-sized fullscreen (#2742) --- src/rcore.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 2d68606d8..27b1a1f33 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4091,8 +4091,18 @@ static bool InitGraphicsDevice(int width, int height) if (CORE.Window.fullscreen) { // remember center for switchinging from fullscreen to window - 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; + if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + { + // If screen width/height equal to the dislpay, we can't calclulate the window pos for toggling fullscreened/windowed. + // Toggling fullscreened/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; + } + 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; + } if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; From d224414eb1fcbe93bcaf2ba02490bbd6f8db624a Mon Sep 17 00:00:00 2001 From: Tobias Mock Date: Wed, 11 Jan 2023 18:07:35 +0100 Subject: [PATCH 0210/1710] Update raylib-ocaml to 4.2.0 (#2853) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 39a333e51..80dc1b859 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -42,7 +42,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | node-raylib | **4.0** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | | raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | -| raylib-ocaml | **4.0** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | +| raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | | TurboRaylib | **4.2** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.2** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | From aed131a8f0746065d97f2ff35f5a90713236efc0 Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Sat, 14 Jan 2023 18:40:48 +0000 Subject: [PATCH 0211/1710] Update BINDINGS.md (#2858) * Update BINDINGS.md Add Kaylib - Kotlin/Native binding for 4.5-dev (proper binding). Add Raylib-Nelua - Nelua binding for 4.5-dev with working wasm export. * Update BINDINGS.md --- BINDINGS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BINDINGS.md b/BINDINGS.md index 80dc1b859..6eec7bb38 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -33,9 +33,11 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | **4.2** | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | +| kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | MIT | https://codeberg.org/Kenta/Kaylib | | raylib-lua | **4.2** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | **4.0** | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | +| Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | From 2a2f2b20b8d918f9597334efad067683ae2328b0 Mon Sep 17 00:00:00 2001 From: Ghost <122651011+ImazighenGhost@users.noreply.github.com> Date: Sat, 14 Jan 2023 19:41:42 +0100 Subject: [PATCH 0212/1710] Fixed bug : touches become sticky (#2857) Touches became sticky and didn't disappear after using more than 2 fingers, fixed by getting the touch count of how many fingers are on the screen, and only looping through the available/pressed down touch points instead of looping through the maximum touch points. Tested with more than 10 touch points, and with different MAX points value, working perfectly. --- examples/core/core_input_multitouch.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/core/core_input_multitouch.c b/examples/core/core_input_multitouch.c index 980af3801..37817e5a0 100644 --- a/examples/core/core_input_multitouch.c +++ b/examples/core/core_input_multitouch.c @@ -39,8 +39,12 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - // Get multiple touchpoints - for (int i = 0; i < MAX_TOUCH_POINTS; ++i) touchPositions[i] = GetTouchPosition(i); + // 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; + // Get touch points positions + for (int i = 0; i < tCount; ++i) touchPositions[i] = GetTouchPosition(i); //---------------------------------------------------------------------------------- // Draw @@ -49,7 +53,7 @@ int main(void) ClearBackground(RAYWHITE); - for (int i = 0; i < MAX_TOUCH_POINTS; ++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)) @@ -72,4 +76,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} From 76468bb8d8e4e588e1248df3a0db63f44605a24d Mon Sep 17 00:00:00 2001 From: Uneven Prankster <33995085+GithubPrankster@users.noreply.github.com> Date: Thu, 19 Jan 2023 16:53:30 -0300 Subject: [PATCH 0213/1710] Add rlCubemapParameters to rlgl.h (#2862) Co-authored-by: Uneven Prankster --- src/rlgl.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index fbf70051a..3b055b23e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -596,6 +596,7 @@ RLAPI void rlDisableTexture(void); // Disable texture RLAPI void rlEnableTextureCubemap(unsigned int id); // Enable texture cubemap RLAPI void rlDisableTextureCubemap(void); // Disable texture cubemap RLAPI void rlTextureParameters(unsigned int id, int param, int value); // Set texture parameters (filter, wrap) +RLAPI void rlCubemapParameters(unsigned int id, int param, int value); // Set cubemap parameters (filter, wrap) // Shader state RLAPI void rlEnableShader(unsigned int id); // Enable shader program @@ -1624,6 +1625,54 @@ void rlTextureParameters(unsigned int id, int param, int value) glBindTexture(GL_TEXTURE_2D, 0); } +// Set cubemap parameters (wrap mode/filter mode) +void rlCubemapParameters(unsigned int id, int param, int value) +{ + glBindTexture(GL_TEXTURE_CUBE_MAP, id); + +#if !defined(GRAPHICS_API_OPENGL_11) + // Reset anisotropy filter, in case it was set + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); +#endif + + switch (param) + { + case RL_TEXTURE_WRAP_S: + case RL_TEXTURE_WRAP_T: + { + if (value == RL_TEXTURE_WRAP_MIRROR_CLAMP) + { +#if !defined(GRAPHICS_API_OPENGL_11) + if (RLGL.ExtSupported.texMirrorClamp) glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); + else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); +#endif + } + 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; + case RL_TEXTURE_FILTER_ANISOTROPIC: + { +#if !defined(GRAPHICS_API_OPENGL_11) + if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) + { + TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, (int)RLGL.ExtSupported.maxAnisotropyLevel); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + } + else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); +#endif + } break; +#if defined(GRAPHICS_API_OPENGL_33) + case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_LOD_BIAS, value/100.0f); +#endif + default: break; + } + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +} + // Enable shader program void rlEnableShader(unsigned int id) { From 116603e61c43ea272c6114c45f22faf924dafac9 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Thu, 19 Jan 2023 12:21:05 -0800 Subject: [PATCH 0214/1710] don't try to free a void* buffer as if it's a cgltf_data structure (#2867) --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 786e5ac79..2e7deb89a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4658,7 +4658,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat if (result == cgltf_result_success) { image = LoadImageFromMemory(".png", (unsigned char *)data, outSize); - cgltf_free((cgltf_data*)data); + MemFree((cgltf_data*)data); } } } From edaca16d7c007846422cd6e098b117f6f8bab99d Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 20 Jan 2023 07:05:19 -0800 Subject: [PATCH 0215/1710] Fix warnings in raylib project from MSVC (#2871) --- src/raudio.c | 17 +++++++++++++---- src/rmodels.c | 45 ++++++++++++++++++++++++++++----------------- src/rshapes.c | 12 ++++++------ src/rtextures.c | 9 +++++++++ 4 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 40b89106a..34ab0702e 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -198,7 +198,6 @@ typedef struct tagBITMAPINFOHEADER { #if defined(SUPPORT_FILEFORMAT_OGG) // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE - #define STB_VORBIS_IMPLEMENTATION #include "external/stb_vorbis.h" // OGG loading functions #endif @@ -207,8 +206,18 @@ typedef struct tagBITMAPINFOHEADER { #define JARXM_MALLOC RL_MALLOC #define JARXM_FREE RL_FREE +#if defined(_MSC_VER ) // jar xm has warnings on windows, so disable them just for this file +#pragma warning( push ) +#pragma warning( disable : 4244) +#endif + #define JAR_XM_IMPLEMENTATION #include "external/jar_xm.h" // XM loading functions + +#if defined(_MSC_VER ) +#pragma warning( pop ) +#endif + #endif #if defined(SUPPORT_FILEFORMAT_MOD) @@ -1756,7 +1765,7 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); + int frameCountRed = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); frameCountRedTotal += frameCountRed; frameCountStillNeeded -= frameCountRed; if (frameCountStillNeeded == 0) break; @@ -1767,7 +1776,7 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); + int frameCountRed = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); frameCountRedTotal += frameCountRed; frameCountStillNeeded -= frameCountRed; if (frameCountStillNeeded == 0) break; @@ -1807,7 +1816,7 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); + int frameCountRed = (int)drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); frameCountRedTotal += frameCountRed; frameCountStillNeeded -= frameCountRed; if (frameCountStillNeeded == 0) break; diff --git a/src/rmodels.c b/src/rmodels.c index 2e7deb89a..6b142cd14 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -102,8 +102,19 @@ #define PAR_REALLOC(T, BUF, N) ((T*)RL_REALLOC(BUF, sizeof(T)*(N))) #define PAR_FREE RL_FREE +#if defined(_MSC_VER ) // par shapes has 2 warnings on windows, so disable them just fof this file +#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 ) // disable MSVC warning suppression for par shapes +#pragma warning( pop ) +#endif + #endif #if defined(_WIN32) @@ -690,7 +701,7 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int Vector3 capCenter = endPos; float baseSliceAngle = (2.0f*PI)/slices; - float baseRingAngle = PI * 0.5 / rings; + float baseRingAngle = PI * 0.5f / rings; rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -833,7 +844,7 @@ void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices Vector3 capCenter = endPos; float baseSliceAngle = (2.0f*PI)/slices; - float baseRingAngle = PI * 0.5 / rings; + float baseRingAngle = PI * 0.5f / rings; rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -4697,7 +4708,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat // Load bone info from GLTF skin data static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount) { - *boneCount = skin.joints_count; + *boneCount = (int)skin.joints_count; BoneInfo *bones = RL_MALLOC(skin.joints_count*sizeof(BoneInfo)); for (unsigned int i = 0; i < skin.joints_count; i++) @@ -5097,7 +5108,7 @@ static Model LoadGLTF(const char *fileName) model.bones = LoadBoneInfoGLTF(skin, &model.boneCount); model.bindPose = RL_MALLOC(model.boneCount*sizeof(Transform)); - for (unsigned int i = 0; i < model.boneCount; i++) + for (int i = 0; i < model.boneCount; i++) { cgltf_node node = *skin.joints[i]; model.bindPose[i].translation.x = node.translation[0]; @@ -5275,7 +5286,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in if (data->skins_count == 1) { cgltf_skin skin = data->skins[0]; - *animCount = data->animations_count; + *animCount = (int)data->animations_count; animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation)); for (unsigned int i = 0; i < data->animations_count; i++) @@ -5349,12 +5360,12 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY); animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); - for (unsigned int j = 0; j < animations[i].frameCount; j++) + 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 (unsigned int k = 0; k < animations[i].boneCount; k++) + for (int k = 0; k < animations[i].boneCount; k++) { Vector3 translation = {0, 0, 0}; Quaternion rotation = {0, 0, 0, 1}; @@ -5567,7 +5578,7 @@ static Model LoadM3D(const char *fileName) // Map no material to index 0 with default shader, everything else materialid + 1 model.materials[0] = LoadMaterialDefault(); - for (i = l = 0, k = -1; i < m3d->numface; i++, l++) + for (i = l = 0, k = -1; i < (int)m3d->numface; i++, l++) { // Materials are grouped together if (mi != m3d->face[i].materialid) @@ -5584,7 +5595,7 @@ static Model LoadM3D(const char *fileName) k++; mi = m3d->face[i].materialid; - for (j = i, l = 0; (j < m3d->numface) && (mi == m3d->face[j].materialid); j++, l++); + for (j = i, l = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++); model.meshes[k].vertexCount = l*3; model.meshes[k].triangleCount = l; @@ -5636,11 +5647,11 @@ static Model LoadM3D(const char *fileName) 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.0 - m3d->tmap[m3d->face[i].texcoord[0]].v; + 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.0 - m3d->tmap[m3d->face[i].texcoord[1]].v; + 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.0 - m3d->tmap[m3d->face[i].texcoord[2]].v; + 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) @@ -5664,7 +5675,7 @@ static Model LoadM3D(const char *fileName) 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 < m3d->numskin) + if (skinid != M3D_UNDEF && skinid < (int)m3d->numskin) { for (j = 0; j < 4; j++) { @@ -5684,7 +5695,7 @@ static Model LoadM3D(const char *fileName) } // Load materials - for (i = 0; i < m3d->nummaterial; i++) + for (i = 0; i < (int)m3d->nummaterial; i++) { model.materials[i + 1] = LoadMaterialDefault(); @@ -5761,7 +5772,7 @@ static Model LoadM3D(const char *fileName) model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo)); model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform)); - for (i = 0; i < m3d->numbone; i++) + 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)); @@ -5864,7 +5875,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int // strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name)); TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount); - for (i = 0; i < m3d->numbone; i++) + 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)); @@ -5884,7 +5895,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int if (pose != NULL) { - for (j = 0; j < m3d->numbone; j++) + for (j = 0; j < (int)m3d->numbone; j++) { animations[a].framePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x*m3d->scale; animations[a].framePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y*m3d->scale; diff --git a/src/rshapes.c b/src/rshapes.c index 371abe2c7..86e014e2a 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -105,7 +105,7 @@ void SetShapesTexture(Texture2D texture, Rectangle source) // Draw a pixel void DrawPixel(int posX, int posY, Color color) { - DrawPixelV((Vector2){ posX, posY }, color); + DrawPixelV((Vector2){ (float)posX, (float)posY }, color); } // Draw a pixel (Vector version) @@ -156,8 +156,8 @@ void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color colo { rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(startPosX, startPosY); - rlVertex2f(endPosX, endPosY); + rlVertex2f((float)startPosX, (float)startPosY); + rlVertex2f((float)endPosX, (float)endPosY); rlEnd(); } @@ -209,7 +209,7 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) float dy = current.y-previous.y; float dx = current.x-previous.x; - float size = 0.5*thick/sqrt(dx*dx+dy*dy); + float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); if (i==1) { @@ -254,7 +254,7 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl float dy = current.y-previous.y; float dx = current.x-previous.x; - float size = 0.5*thick/sqrt(dx*dx+dy*dy); + float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); if (i==1) { @@ -299,7 +299,7 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP float dy = current.y-previous.y; float dx = current.x-previous.x; - float size = 0.5*thick/sqrt(dx*dx+dy*dy); + float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); if (i==1) { diff --git a/src/rtextures.c b/src/rtextures.c index 5d1718489..05dacb21a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -163,8 +163,17 @@ #define QOI_MALLOC RL_MALLOC #define QOI_FREE RL_FREE +#if defined(_MSC_VER ) // qoi has warnings on windows, so disable them just for this file +#pragma warning( push ) +#pragma warning( disable : 4267) +#endif #define QOI_IMPLEMENTATION #include "external/qoi.h" + +#if defined(_MSC_VER ) +#pragma warning( pop ) +#endif + #endif #if defined(SUPPORT_IMAGE_EXPORT) From e64606a82f7cc66c57df31815371f0328c723bf4 Mon Sep 17 00:00:00 2001 From: Masoud Naservand Date: Fri, 20 Jan 2023 18:37:43 +0330 Subject: [PATCH 0216/1710] Correct the set paths in bat files in examples/ (#2870) Co-authored-by: Masoud Naservand --- examples/raylib_compile_execute.bat | 8 ++++---- examples/raylib_makefile_example.bat | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/raylib_compile_execute.bat b/examples/raylib_compile_execute.bat index 59a25fc51..0dc43e187 100644 --- a/examples/raylib_compile_execute.bat +++ b/examples/raylib_compile_execute.bat @@ -4,10 +4,10 @@ :: . :: > Setup required Environment :: ------------------------------------- -set RAYLIB_INCLUDE_DIR=C:\raylib\src -set RAYLIB_LIB_DIR=C:\raylib\src -set RAYLIB_RES_FILE=C:\raylib\src\raylib.rc.data -set COMPILER_DIR=C:\raylib\mingw\bin +set RAYLIB_INCLUDE_DIR=C:\raylib\raylib\src +set RAYLIB_LIB_DIR=C:\raylib\raylib\src +set RAYLIB_RES_FILE=C:\raylib\raylib\src\raylib.rc.data +set COMPILER_DIR=C:\raylib\w64devkit\bin set PATH=%PATH%;%COMPILER_DIR% :: Get full filename path for input file %1 set FILENAME=%~f1 diff --git a/examples/raylib_makefile_example.bat b/examples/raylib_makefile_example.bat index 83fa06e10..cc2fdc225 100644 --- a/examples/raylib_makefile_example.bat +++ b/examples/raylib_makefile_example.bat @@ -4,10 +4,10 @@ :: . :: > Setup required Environment :: ------------------------------------- -set RAYLIB_INCLUDE_DIR=C:\raylib\src -set RAYLIB_LIB_DIR=C:\raylib\src -set RAYLIB_RES_FILE=C:\raylib\src\raylib.rc.data -set COMPILER_DIR=C:\raylib\mingw\bin +set RAYLIB_INCLUDE_DIR=C:\raylib\raylib\src +set RAYLIB_LIB_DIR=C:\raylib\raylib\src +set RAYLIB_RES_FILE=C:\raylib\raylib\src\raylib.rc.data +set COMPILER_DIR=C:\raylib\w64devkit\bin set PATH=%PATH%;%COMPILER_DIR% set FILENAME=%1 set FILENAME_FULL_PATH=%~f1 @@ -24,4 +24,4 @@ mingw32-make %FILENAME% -B PLATFORM=PLATFORM_DESKTOP :: . :: > Executing program :: ------------------------- -cmd /c if exist %FILENAME_FULL_PATH%.exe %FILENAME_FULL_PATH%.exe \ No newline at end of file +cmd /c if exist %FILENAME_FULL_PATH%.exe %FILENAME_FULL_PATH%.exe From c649bec26cafb5415d9becae3e39720692146d40 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 20 Jan 2023 07:13:19 -0800 Subject: [PATCH 0217/1710] Have LoadMaterials call the same code that OBJ loader does so that we can read MTL files (#2872) --- src/rmodels.c | 77 ++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 6b142cd14..89538d7a6 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1862,6 +1862,42 @@ bool ExportMesh(Mesh mesh, const char *fileName) return success; } +#if defined(SUPPORT_FILEFORMAT_MTL) +// Process obj materials + +static void ProcessOBJMaterials(Material* rayMaterials, tinyobj_material_t* materials, int materialCount) +{ + // Init model materials + for (unsigned int m = 0; m < materialCount; m++) + { + // Init material to default + // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE + rayMaterials[m] = LoadMaterialDefault(); + + // Get default texture, in case no texture is defined + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd + + rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0] * 255.0f), (unsigned char)(materials[m].diffuse[1] * 255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; + rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; + + if (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks + rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0] * 255.0f), (unsigned char)(materials[m].specular[1] * 255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3]; + rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; + + if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump + rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; + rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess; + + rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0] * 255.0f), (unsigned char)(materials[m].emission[1] * 255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3]; + + if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp + } +} +#endif + // Load materials from model file Material *LoadMaterials(const char *fileName, int *materialCount) { @@ -1878,7 +1914,8 @@ Material *LoadMaterials(const char *fileName, int *materialCount) int result = tinyobj_parse_mtl_file(&mats, &count, fileName); if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName); - // TODO: Process materials to return + materials = MemAlloc(sizeof(Material) * count); + ProcessOBJMaterials(materials, mats, count); tinyobj_materials_free(mats, count); } @@ -1886,16 +1923,6 @@ Material *LoadMaterials(const char *fileName, int *materialCount) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load material file", fileName); #endif - // Set materials shader to default (DIFFUSE, SPECULAR, NORMAL) - if (materials != NULL) - { - for (unsigned int i = 0; i < count; i++) - { - materials[i].shader.id = rlGetShaderIdDefault(); - materials[i].shader.locs = rlGetShaderLocsDefault(); - } - } - *materialCount = count; return materials; } @@ -4000,33 +4027,7 @@ static Model LoadOBJ(const char *fileName) } // Init model materials - for (unsigned int m = 0; m < materialCount; m++) - { - // Init material to default - // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE - model.materials[m] = LoadMaterialDefault(); - - // Get default texture, in case no texture is defined - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - model.materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd - - model.materials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; - model.materials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; - - if (materials[m].specular_texname != NULL) model.materials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks - model.materials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2]*255.0f), 255 }; //float specular[3]; - model.materials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; - - if (materials[m].bump_texname != NULL) model.materials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump - model.materials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; - model.materials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess; - - model.materials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2]*255.0f), 255 }; //float emission[3]; - - if (materials[m].displacement_texname != NULL) model.materials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp - } + ProcessOBJMaterials(model.materials, materials, materialCount); tinyobj_attrib_free(&attrib); tinyobj_shapes_free(meshes, meshCount); From 342b18da034b34cb9d7713fd03e321e4049e79c2 Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Sat, 21 Jan 2023 17:50:38 +0000 Subject: [PATCH 0218/1710] Add packaging for distros with deb- and rpm-based packages. (#2877) --- cmake/PackConfigurations.cmake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/PackConfigurations.cmake b/cmake/PackConfigurations.cmake index 74eded025..ad00ca6b6 100644 --- a/cmake/PackConfigurations.cmake +++ b/cmake/PackConfigurations.cmake @@ -1,5 +1,6 @@ # Packaging SET(CPACK_PACKAGE_NAME "raylib") +SET(CPACK_PACKAGE_CONTACT "raysan5") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simple and easy-to-use library to enjoy videogames programming") SET(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") @@ -9,5 +10,9 @@ SET(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/../README.md") SET(CPACK_RESOURCE_FILE_WELCOME "${PROJECT_SOURCE_DIR}/../README.md") SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/../LICENSE") SET(CPACK_PACKAGE_FILE_NAME "raylib-${PROJECT_VERSION}$ENV{RAYLIB_PACKAGE_SUFFIX}") -SET(CPACK_GENERATOR "ZIP;TGZ") # Remove this, if you want the NSIS installer on Windows -include(CPack) \ No newline at end of file +SET(CPACK_GENERATOR "ZIP;TGZ;DEB;RPM") # Remove this, if you want the NSIS installer on Windows +SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF) # can be used to generate deps, slow and requires tools. +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libatomic1, libc6, libglfw3, libglu1-mesa | libglu1, libglx0, libopengl0") +SET(CPACK_DEBIAN_PACKAGE_NAME "lib${CPACK_PACKAGE_NAME}-dev") +SET(CPACK_RPM_PACKAGE_NAME "lib${CPACK_PACKAGE_NAME}-devel") +include(CPack) From 19715546b3eddb0d6171d6b68e3642497b007a4e Mon Sep 17 00:00:00 2001 From: Uneven Prankster <33995085+GithubPrankster@users.noreply.github.com> Date: Sat, 21 Jan 2023 14:51:33 -0300 Subject: [PATCH 0219/1710] Stub out rlCubemapParameters if under GL 1.1. (#2876) Co-authored-by: Uneven Prankster --- src/rlgl.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 3b055b23e..12f62771e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1628,12 +1628,11 @@ void rlTextureParameters(unsigned int id, int param, int value) // Set cubemap parameters (wrap mode/filter mode) void rlCubemapParameters(unsigned int id, int param, int value) { +#if !defined(GRAPHICS_API_OPENGL_11) glBindTexture(GL_TEXTURE_CUBE_MAP, id); -#if !defined(GRAPHICS_API_OPENGL_11) // Reset anisotropy filter, in case it was set glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); -#endif switch (param) { @@ -1642,10 +1641,8 @@ void rlCubemapParameters(unsigned int id, int param, int value) { if (value == RL_TEXTURE_WRAP_MIRROR_CLAMP) { -#if !defined(GRAPHICS_API_OPENGL_11) if (RLGL.ExtSupported.texMirrorClamp) glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); -#endif } else glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); @@ -1654,7 +1651,6 @@ void rlCubemapParameters(unsigned int id, int param, int value) case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); break; case RL_TEXTURE_FILTER_ANISOTROPIC: { -#if !defined(GRAPHICS_API_OPENGL_11) if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) { @@ -1662,7 +1658,6 @@ void rlCubemapParameters(unsigned int id, int param, int value) glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); } else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); -#endif } break; #if defined(GRAPHICS_API_OPENGL_33) case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_LOD_BIAS, value/100.0f); @@ -1671,6 +1666,7 @@ void rlCubemapParameters(unsigned int id, int param, int value) } glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif } // Enable shader program From e539aad1184373fe32c4dd8052e888df9ab4d8fe Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sat, 21 Jan 2023 19:52:31 +0200 Subject: [PATCH 0220/1710] Fix android sound issue #2118 (#2875) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index fdc52a839..c2e227f7a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -389,7 +389,7 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) # -Werror=format-security CFLAGS += -Wa,--noexecstack -Wformat -no-canonical-prefixes # Preprocessor macro definitions - CFLAGS += -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=$(ANDROID_API_VERSION) -DMAL_NO_OSS + CFLAGS += -D__ANDROID__ -DPLATFORM_ANDROID -D__ANDROID_API__=$(ANDROID_API_VERSION) -DMAL_NO_OSS endif # Define required compilation flags for raylib SHARED lib From 78ae3b38a6e7d1386d233da620e7b31aa1a0a94e Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 21 Jan 2023 19:31:47 +0100 Subject: [PATCH 0221/1710] minor format tweak --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 27b1a1f33..23a6597a4 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4043,7 +4043,7 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context #endif } - else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); From d8af76f67c54a6fa7b20c0fadff7c15ef5478945 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 22 Jan 2023 05:10:38 -0500 Subject: [PATCH 0222/1710] Fix to use TRACELOG() instead of TraceLog() for internal modules (#2881) There were a few raylib modules that continued to use TraceLog() instead of the TRACELOG() macro. This change ensures that all the internal raylib modules use the TRACELOG() pattern consistently. --- src/rcore.c | 6 +++--- src/rmodels.c | 4 ++-- src/rtext.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 23a6597a4..1f97edb2a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3310,7 +3310,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa compData = (unsigned char *)RL_CALLOC(bounds, 1); *compDataSize = sdeflate(&sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbwi - TraceLog(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize); + TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize); #endif return compData; @@ -3335,7 +3335,7 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i *dataSize = length; - TraceLog(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize); + TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize); #endif return data; @@ -6854,7 +6854,7 @@ static void LoadAutomationEvents(const char *fileName) if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) { fread(&eventCount, sizeof(int), 1, repFile); - TraceLog(LOG_WARNING, "Events loaded: %i\n", eventCount); + TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount); fread(events, sizeof(AutomationEvent), eventCount, repFile); } diff --git a/src/rmodels.c b/src/rmodels.c index 89538d7a6..80ea91644 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3937,12 +3937,12 @@ static Model LoadOBJ(const char *fileName) { model.materialCount = materialCount; model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - TraceLog(LOG_INFO, "MODEL: model has %i material meshes", materialCount); + TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", materialCount); } else { model.meshCount = 1; - TraceLog(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); + TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); } model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); diff --git a/src/rtext.c b/src/rtext.c index f98386702..718fda04b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -669,7 +669,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC if (chars == NULL) { - TraceLog(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas"); + TRACELOG(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas"); return atlas; } From 4706891cae7d7b07ab7f85aee9d858bdd11d4f42 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 22 Jan 2023 09:29:29 -0500 Subject: [PATCH 0223/1710] Add RAYLIB_VERSION numbers to raylib.h (#2856) Ran into an issue in raylib-cpp where a user was using raylib 4.5-dev, even though the library currently only targets 4.2. With having RAYLIB_VERSION_MAJOR and RAYLIB_VERSION_MINOR, we will be able to target different versions of raylib in different ways, via C preprocessor conditionals. For example: ``` c newColor = ColorTint(BLUE, RED); TraceLog(LOG_INFO, "The color should be tinted, but this isn't supported in ryalib <= 4.2"); ``` --- src/raylib.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 1c3f77669..10e69acdf 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -81,6 +81,9 @@ #include // Required for: va_list - Only used by TraceLogCallback +#define RAYLIB_VERSION_MAJOR 4 +#define RAYLIB_VERSION_MINOR 5 +#define RAYLIB_VERSION_PATCH 0 #define RAYLIB_VERSION "4.5-dev" // Function specifiers in case library is build/used as a shared library (Windows) From 393a03a46eec038d49606ef5f3459621decb27b9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Jan 2023 15:28:45 +0100 Subject: [PATCH 0224/1710] Removed twitter badge, twitter API seems to be limited --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 030cc93b0..c66eed75e 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,6 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) [![Subreddit Subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?label=reddit%20r%2Fraylib&logo=reddit)](https://www.reddit.com/r/raylib/) [![Youtube Subscribers](https://img.shields.io/youtube/channel/subscribers/UC8WIBkhYb5sBNqXO1mZ7WSQ?style=flat&label=Youtube&logo=youtube)](https://www.youtube.com/c/raylib) [![Twitch Status](https://img.shields.io/twitch/status/raysan5?style=flat&label=Twitch&logo=twitch)](https://www.twitch.tv/raysan5) -[![Twitter Followers](https://img.shields.io/twitter/follow/raysan5?style=flat&label=@raysan5&color=1da1f2&logo=twitter)](https://twitter.com/raysan5) - [![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows) [![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux) From 542ef8904a2ce9d87ab53289026da7a715907b87 Mon Sep 17 00:00:00 2001 From: PencilAmazing Date: Mon, 23 Jan 2023 13:55:02 -0500 Subject: [PATCH 0225/1710] [models] Load bone names from IQM file if available (#2882) * Load bone names from IQM file if available * Formatting and default bone name --- src/rmodels.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 80ea91644..78219a70e 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4450,6 +4450,12 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int 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; @@ -4503,6 +4509,10 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int //fread(framedata, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short), 1, 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; @@ -4513,7 +4523,11 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int for (unsigned int j = 0; j < iqmHeader->num_poses; j++) { - strcpy(animations[a].bones[j].name, "ANIMJOINTNAME"); + // 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; } @@ -4627,6 +4641,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int RL_FREE(fileData); + RL_FREE(joints); RL_FREE(framedata); RL_FREE(poses); RL_FREE(anim); From 5b3c5e1a16c2d0f97e407542f1a0837b9d5b7c71 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Jan 2023 17:16:35 +0100 Subject: [PATCH 0226/1710] REVIEWED: `ProcessMaterialsOBJ()` available when required --- src/rmodels.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 89538d7a6..b500e1e85 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -166,6 +166,9 @@ static Model LoadVOX(const char *filename); // Load VOX mesh data static Model LoadM3D(const char *filename); // Load M3D mesh data static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned 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 @@ -1862,10 +1865,9 @@ bool ExportMesh(Mesh mesh, const char *fileName) return success; } -#if defined(SUPPORT_FILEFORMAT_MTL) +#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) // Process obj materials - -static void ProcessOBJMaterials(Material* rayMaterials, tinyobj_material_t* materials, int materialCount) +static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount) { // Init model materials for (unsigned int m = 0; m < materialCount; m++) @@ -1880,23 +1882,23 @@ static void ProcessOBJMaterials(Material* rayMaterials, tinyobj_material_t* mate if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0] * 255.0f), (unsigned char)(materials[m].diffuse[1] * 255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; + rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; if (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks - rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0] * 255.0f), (unsigned char)(materials[m].specular[1] * 255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3]; + rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3]; rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess; - rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0] * 255.0f), (unsigned char)(materials[m].emission[1] * 255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3]; + rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3]; if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp } } -#endif +#endif // Load materials from model file Material *LoadMaterials(const char *fileName, int *materialCount) @@ -1914,8 +1916,8 @@ Material *LoadMaterials(const char *fileName, int *materialCount) 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 = MemAlloc(sizeof(Material) * count); - ProcessOBJMaterials(materials, mats, count); + materials = MemAlloc(count*sizeof(Material)); + ProcessMaterialsOBJ(materials, mats, count); tinyobj_materials_free(mats, count); } @@ -3951,7 +3953,7 @@ static Model LoadOBJ(const char *fileName) // Count the faces for each material int *matFaces = RL_CALLOC(model.meshCount, sizeof(int)); - // iff no materials are present use all faces on one mesh + // if no materials are present use all faces on one mesh if (materialCount > 0) { for (unsigned int fi = 0; fi < attrib.num_faces; fi++) @@ -4027,7 +4029,7 @@ static Model LoadOBJ(const char *fileName) } // Init model materials - ProcessOBJMaterials(model.materials, materials, materialCount); + ProcessMaterialsOBJ(model.materials, materials, materialCount); tinyobj_attrib_free(&attrib); tinyobj_shapes_free(meshes, meshCount); From f68bb8c7077a080cd878c3887cab1b4950b2acca Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 24 Jan 2023 17:17:25 +0100 Subject: [PATCH 0227/1710] REVIEWED: `rlGenTextureMipmaps()`, GPU generation only --- src/rlgl.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 12f62771e..2f1847e0d 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3248,6 +3248,7 @@ void rlUnloadTexture(unsigned int id) // NOTE: Only supports GPU mipmap generation void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindTexture(GL_TEXTURE_2D, id); // Check if texture is power-of-two (POT) @@ -3256,7 +3257,6 @@ void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) { //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorithm: GL_FASTEST, GL_NICEST, GL_DONT_CARE @@ -3268,10 +3268,12 @@ void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps = 1 + (int)floor(log(MAX(width, height))/log(2)); TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Mipmaps generated automatically, total: %i", id, *mipmaps); } -#endif else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps", id); glBindTexture(GL_TEXTURE_2D, 0); +#else + TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] GPU mipmap generation not supported", id); +#endif } From 7cdffcec5274b1ea4505909bc01058091fe906c3 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Wed, 25 Jan 2023 00:43:09 +0200 Subject: [PATCH 0228/1710] fixed typo (#2886) pilepine=>pipeline chosing=>choosing additioanlly=>additionally attachmment=>attachment initialize=>inititialize Binded=>Bound lattest=>latest hi @raysan5, I ran rlgl.h into some "typo checking" program(basically a JetBrains IDE), and here are all the things that the program was able to spot and fix. as my English isn't really well I would like it if you could check that I didn't make any mistakes in the typo fixing... --- src/rlgl.h | 57 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 2f1847e0d..2b982a9a1 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -5,11 +5,11 @@ * An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) * that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) * -* When chosing an OpenGL backend different than OpenGL 1.1, some internal buffer are +* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are * initialized on rlglInit() to accumulate vertex data. * * When an internal state change is required all the stored vertex data is renderer in batch, -* additioanlly, rlDrawRenderBatchActive() could be called to force flushing of the batch. +* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. * * Some additional resources are also loaded for convenience, here the complete list: * - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data @@ -61,12 +61,11 @@ * When loading a shader, the following vertex attribute and uniform * location names are tried to be set automatically: * -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Binded by default to shader location: 0 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Binded by default to shader location: 1 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Binded by default to shader location: 2 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Binded by default to shader location: 3 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Binded by default to shader location: 4 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Binded by default to shader location: 5 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 * #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix * #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix * #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix @@ -505,16 +504,16 @@ typedef enum { // Framebuffer attachment type // NOTE: By default up to 8 color channels defined but it can be more typedef enum { - RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachmment type: color 0 - RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachmment type: color 1 - RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachmment type: color 2 - RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachmment type: color 3 - RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachmment type: color 4 - RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachmment type: color 5 - RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachmment type: color 6 - RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachmment type: color 7 - RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachmment type: depth - RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachmment type: stencil + RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 + RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1 + RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachment type: color 2 + RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachment type: color 3 + RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachment type: color 4 + RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachment type: color 5 + RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachment type: color 6 + RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachment type: color 7 + RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth + RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil } rlFramebufferAttachType; // Framebuffer texture attachment type @@ -545,7 +544,7 @@ extern "C" { // Prevents name mangling of functions RLAPI void rlMatrixMode(int mode); // Choose the current matrix to be transformed RLAPI void rlPushMatrix(void); // Push the current matrix to stack -RLAPI void rlPopMatrix(void); // Pop lattest inserted matrix from stack +RLAPI void rlPopMatrix(void); // Pop latest inserted matrix from stack RLAPI void rlLoadIdentity(void); // Reset current matrix to identity matrix RLAPI void rlTranslatef(float x, float y, float z); // Multiply the current matrix by a translation matrix RLAPI void rlRotatef(float angle, float x, float y, float z); // Multiply the current matrix by a rotation matrix @@ -642,7 +641,7 @@ RLAPI void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, //------------------------------------------------------------------------------------ // rlgl initialization functions RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) -RLAPI void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures) +RLAPI void rlglClose(void); // De-initialize rlgl (buffers, shaders, textures) RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required) RLAPI int rlGetVersion(void); // Get current OpenGL version RLAPI void rlSetFramebufferWidth(int width); // Set current framebuffer width @@ -716,7 +715,7 @@ RLAPI void rlSetShader(unsigned int id, int *locs); // Compute shader management RLAPI unsigned int rlLoadComputeShaderProgram(unsigned int shaderId); // Load compute shader program -RLAPI void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ); // Dispatch compute shader (equivalent to *draw* for graphics pilepine) +RLAPI void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ); // Dispatch compute shader (equivalent to *draw* for graphics pipeline) // Shader buffer storage object management (ssbo) RLAPI unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint); // Load shader storage buffer object (SSBO) @@ -892,22 +891,22 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad // Default shader vertex attribute names to set location points #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION - #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Binded by default to shader location: 0 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Binded by default to shader location: 1 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL - #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Binded by default to shader location: 2 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR - #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Binded by default to shader location: 3 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Binded by default to shader location: 4 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 #endif #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Binded by default to shader location: 5 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5 #endif #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP @@ -2767,7 +2766,7 @@ void rlDrawRenderBatch(rlRenderBatch *batch) for (int i = 0, vertexOffset = 0; i < batch->drawCounter; i++) { - // Bind current draw call texture, activated as GL_TEXTURE0 and binded 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); @@ -3856,7 +3855,7 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) glAttachShader(program, vShaderId); glAttachShader(program, fShaderId); - // NOTE: Default attribute shader locations must be binded before linking + // NOTE: Default attribute shader locations must be Bound before linking glBindAttribLocation(program, 0, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION); glBindAttribLocation(program, 1, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); glBindAttribLocation(program, 2, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL); From 4adba0d3c3df93592346a592e7a2b79d5b76bd90 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Wed, 25 Jan 2023 23:25:25 +0200 Subject: [PATCH 0229/1710] Add wayland support (#2883) --- src/Makefile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Makefile b/src/Makefile index c2e227f7a..4d9c8723b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -409,6 +409,24 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS), LINUX) ifeq ($(USE_WAYLAND_DISPLAY),TRUE) CFLAGS += -D_GLFW_WAYLAND + LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs) + + WL_PROTOCOLS_DIR := $(shell pkg-config wayland-protocols --variable=pkgdatadir) + WL_CLIENT_DIR := $(shell pkg-config wayland-client --variable=pkgdatadir) + + wl_generate = \ + $(eval protocol=$(1)) \ + $(eval basename=$(2)) \ + $(shell wayland-scanner client-header $(protocol) $(RAYLIB_SRC_PATH)/$(basename).h) \ + $(shell wayland-scanner private-code $(protocol) $(RAYLIB_SRC_PATH)/$(basename)-code.h) + + $(call wl_generate, $(WL_CLIENT_DIR)/wayland.xml, wayland-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/stable/xdg-shell/xdg-shell.xml, wayland-xdg-shell-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml, wayland-xdg-decoration-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/stable/viewporter/viewporter.xml, wayland-viewporter-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/unstable/relative-pointer/relative-pointer-unstable-v1.xml, wayland-relative-pointer-unstable-v1-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml, wayland-pointer-constraints-unstable-v1-client-protocol) + $(call wl_generate, $(WL_PROTOCOLS_DIR)/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml, wayland-idle-inhibit-unstable-v1-client-protocol) endif endif endif From 63da7cdec6209b0a7b29cbdc12afe97c18be6aac Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 25 Jan 2023 13:26:03 -0800 Subject: [PATCH 0230/1710] add include guards on config.h (#2888) --- src/config.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index 17334b8ac..f67ea3cb0 100644 --- a/src/config.h +++ b/src/config.h @@ -24,7 +24,8 @@ * 3. This notice may not be removed or altered from any source distribution. * **********************************************************************************************/ - +#ifndef CONFIG_H +#define CONFIG_H //------------------------------------------------------------------------------------ // Module selection - Some modules could be avoided // Mandatory modules: rcore, rlgl, utils @@ -236,3 +237,5 @@ // utils: Configuration values //------------------------------------------------------------------------------------ #define MAX_TRACELOG_MSG_LENGTH 128 // Max length of one trace-log message + +#endif // CONFIG_H From 01257908015f816cc6301565633cfc7d5b72f5a5 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Jan 2023 23:13:17 +0100 Subject: [PATCH 0231/1710] Update rmodels.c --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 91ae68a3b..953d016ad 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4687,7 +4687,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat if (result == cgltf_result_success) { image = LoadImageFromMemory(".png", (unsigned char *)data, outSize); - MemFree((cgltf_data*)data); + MemFree(data); } } } From f088548261fd894d7b607b7896f8ff7f00629420 Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Thu, 26 Jan 2023 09:52:59 +0000 Subject: [PATCH 0232/1710] Update BINDINGS.md (#2889) Update Kaylib license to Zlib --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 6eec7bb38..1bc2c57ac 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -33,7 +33,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | **4.2** | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | -| kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | MIT | https://codeberg.org/Kenta/Kaylib | +| kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/Kaylib | | raylib-lua | **4.2** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | **4.0** | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | From 000cb5a47d8487a899fc7c55b3aeb6fdf328a884 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Jan 2023 23:18:06 +0100 Subject: [PATCH 0233/1710] Update CHANGELOG --- CHANGELOG | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index f1207ae5c..5900ff803 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,146 @@ changelog Current Release: raylib 4.2.0 (11 August 2022) +------------------------------------------------------------------------- +Release: raylib 4.5 (xx February 2022?) -WIP- +------------------------------------------------------------------------- +KEY CHANGES: + - ADDED: M3D model format support with animations + - ADDED: GLTF animation support + - rlgl redesign to avoid render batch triangles limits pre-check: rlCheckRenderBatchLimit() + - rshapes simplification to minimize the requirement of rlgl functionality, now it only depends on 6 functions + - rl_gputex.h: Compressed textures loading, required by rtextures module, has been moved to a separate self-contained library + - raygui 3.5: New version of the immediate-mode gui system for tools development with raylib + + +Detailed changes: + +[core] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach +[core] ADDED: Basic gamepad support for Android (#2709) by @deniska +[core] ADDED: Support CAPS/NUM lock keys registering if locked +[core] ADDED: _GNU_SOURCE define on Linux (#2729) +[core] `WARNING`: RENAMED: Exported raylib version symbol to raylib_version #2671 +[core] REMOVED: Touch points on touch up events on Android (#2711) by @deniska +[core] REVIEWED: Window position setup on InitWindow() (#2732) by @RandomErrorMessage +[core] REVIEWED: Touchscreen input related functions on Android (#2702) by @deniska +[core] REVIEWED: Viewport scaling on Android after context rebind (#2703) by @deniska +[core] REVIEWED: ScanDirectoryFilesRecursively() (#2704) +[core] REVIEWED: Gamepad mappings with latest gamecontrollerdb (#2725) +[core] REVIEWED: Monitor order check on app initialization +[core] REVIEWED: Application monitor when opening (#2728, #2731) by @RandomErrorMessage +[core] REVIEWED: Gestures module to use GetTime() if available (#2733) by @RobLoach +[core] REVIEWED: Resolve GLFW3 some symbols re-definition of windows.h in glfw3native (#2643) by @daipom +[core] REVIEWED: OpenURL(), string buffer too short sometimes +[core] REVIEWED: GetRandomValue() range limit warning (#2800) by @Pere001 +[core] REVIEWED: UnloadDirectoryFiles() +[core] REVIEWED: GetKeyPressed(), out of range issue (#2814) by @daipom +[core] REVIEWED: GetTime(), renamed variable 'time' to 'nanoSeconds' (#2816) by @jtainer +[core] REVIEWED: LoadShaderFromMemory(), issue with shader linkage +[core] REVIEWED: Avoid possible gamepad index as -1 (#2839) +[core] REVIEWED: SetShaderValue*(), avoid setup uniforms for invalid locations +[core] REVIEWED: GetClipboardText() on PLATFORM_WEB, permissions issues +[core] REVIEWED: Initial window position for display-sized fullscreen (#2742) by @daipom +[core] REVIEWED: Sticky touches input (#2857) by @ImazighenGhost +[rlgl] ADDED: OpenGL ES 2.0 support on PLATFORM_DESKTOP (#2840) by @wtnbgo +[rlgl] ADDED: Separate blending modes for color and alpha, BLEND_CUSTOM_SEPARATE (#2741) +[rlgl] ADDED: rlSetBlendFactorsSeparate and custom blend mode modification checks (#2741) by @pure01fx +[rlgl] ADDED: RL_TEXTURE_MIPMAP_BIAS_RATIO support to `rlTextureParameters()` for OpenGL 3.3 #2674 +[rlgl] ADDED: rlCubemapParameters() (#2862) by @GithubPrankster +[rlgl] ADDED: rlSetCullFace() (#2797) by @jtainer +[rlgl] REMOVED: Mipmaps software generation for OpenGL 1.1 +[rlgl] REVIEWED: Check for extensions before enabling them (#2706) by @Not-Nik +[rlgl] REVIEWED: SSBO usage to avoid long long data types +[rlgl] REVIEWED: Enable DXT compression on __APPLE__ targets (#2694) by @Not-Nik +[rlgl] REVIEWED: enums exposed and description comments +[rlgl] REVIEWED: rlBindImageTexture(), correct data types (#2808) by @planetis-m +[rlgl] REVIEWED: rlMultMatrixf(), use const pointer (#2807) by @planetis-m +[rlgl] REVIEWED: Expose OpenGL blending mode factors and functions/equations +[rlgl] REVIEWED: rLoadTextureDepth(), issue with depth textures on WebGL (#2824) +[raymath] REVIEWED: Vector2Angle() (#2829, #2832) by @AlxHnr and @planetis-m +[shapes] ADDED: CheckCollisionPointPoly() (#2685) by @acejacek +[shapes] REVIEWED: DrawPixel*(), use RL_QUADS/RL_TRIANGLES (#2750) by @hatkidchan +[shapes] REVIEWED: DrawLineBezier*(), fix bezier line breaking (#2735, #2767) by @nobytesgiven +[textures] ADDED: ColorBrightness() +[textures] ADDED: ColorTint() +[textures] ADDED: ColorContrast() +[textures] ADDED: Support for PNM images (.ppm, .pgm) +[textures] ADDED: GenImagePerlinNoise() +[textures] ADDED: GenImageText(), generate grayscale image from text byte data +[textures] ADDED: ImageDrawCircleLines(), ImageDrawCircleLinesV() (#2713) by @RobLoach +[textures] ADDED: ImageBlurGaussian() (#2770) by @nobytesgiven +[textures] REVIEWED: Image fileformat support: PIC, PNM +[textures] REVIEWED: ImageTextEx() and ImageDrawTextEx() scaling (#2756) by @hatkidchan +[textures] `WARNING`: REMOVED: DrawTextureQuad() +[textures] `WARNING`: REMOVED: DrawTexturePoly(), function moved to example: `textures_polygon` +[textures] `WARNING`: REMOVED: DrawTextureTiled(),function implementation moved to the textures_tiled.c +[text] ADDED: GetCodepointPrevious() +[text] ADDED: UnloadUTF8(), aligned with LoadUTF8() +[text] `WARNING`: RENAMED: TextCodepointsToUTF8() to LoadUTF8() +[text] `WARNING`: RENAMED: GetCodepoint() -> GetCodepointNext() +[text] REDESIGNED: GetCodepointNext() +[text] REVIEWED text_codepoints_loading.c +[text] REVIEWED: MeasureTextEx(), avoid crash on bad data +[text] REVIEWED: UnloadFontData(), avoid crash on invalid font data +[models] ADDED: Support M3D model file format (meshes and materials) (#2648) by @bztsrc +[models] ADDED: Support for M3D animations (#2648) by @bztsrc +[models] ADDED: GLTF animation support (#2844) by @charles-l +[models] ADDED: DrawCapsule() and DrawCapsuleWires() (#2761) by @IanBand +[models] ADDED: LoadMaterials(), MTL files loading, same code as OBJ loader (#2872) by @JeffM2501 +[models] REVIEWED: GenMeshHeightmap() (#2716) +[models] REVIEWED: LoadIQM() (#2676) +[models] REVIEWED: Simplify .vox signature check (#2752) by @CrezyDud +[models] REVIEWED: LoadIQM(), support bone names loading if available (#2882) by @PencilAmazing +[models] `WARNING`: REMOVED: DrawCubeTexture(), DrawCubeTextureRec(), functions moved to new example: `models_draw_cube_texture` +[audio] REVIEWED: Clear PCM buffer state when closing audio device (#2736) by @veins1 +[audio] REVIEWED: Android backend selected (#2118, #2875) by @planetis-m +[multi] REVIEWED: Multiple code/comment typos by @sDos280 +[multi] REVIEWED: Use TRACELOG() macro instead of TraceLog() in internal modules (#2881) by @RobLoach +[examples] ADDED: textures_textured_curve (#2821) by @JeffM2501 +[examples] ADDED: shaders_write_depth (#2836) by @BugraAlptekinSari +[examples] RENAMED: Several shaders for naming consistency (#2707) +[examples] RENAMED: lighting_instanced.fs to lighting_instancing.fs (glsl100) (#2805) by @gtrxAC +[examples] REVIEWED: core_custom_logging.c (#2692) by @hartmannathan +[examples] REVIEWED: core_camera_2d_platformer (#2687) by @skylar779 +[examples] REVIEWED: core_custom_frame_control +[examples] REVIEWED: text_rectangle_bounds (#2746) by @SzieberthAdam +[examples] REVIEWED: textures_image_processing, added gaussian blurring (#2775) by @nobytesgiven +[examples] REVIEWED: models_billboard, highlighting rotation and draw order (#2779) by @nobytesgiven +[examples] REVIEWED: core_loading_thread, join thread on completion (#2845) by @planetis-m +[examples] REVIEWED: models_loading_gltf +[examples] REVIEWED: Shader lighting.fs for GLSL120 (#2651) +[parser] REVIEWED: raylib-parser Makefile (#2765) by @Peter0x44 +[build] ADDED: Packaging for distros with deb-based and rpm-based packages (#2877) by @KOLANICH +[build] ADDED: Linkage library -latomic on Linux (only required for ARM32) +[build] ADDED: Required frameworks on macOS (#2793) by @SpexGuy +[build] REVIEWED: CMake project template to easily target raylib version (#2700) by @RobLoach +[build] REVIEWED: PATH for PLATFORM_WEB target (#2647) by @futureapricot +[build] REVIEWED: build.zig to let user decide how to set build mode and linker fixes by @InKryption +[build] REVIEWED: Deprecation error on Android API higher than 23 (#2778) by @anggape +[build] REVIEWED: Android x86 Architecture name (#2783) by @IsaacTCB +[build] REVIEWED: examples/build.zig for the latest Zig version (#2786) by @RomanAkberov +[utils] REVIEWED: ExportDataAsCode() data types (#2787) by @RGDTAB +[build] REVIEWED: Makefile emscripten path (#2785) by @Julianiolo +[build] REVIEWED: Several compilation warnings (for strict rules) +[build] REVIEWED: All github workflows using deprecated actions +[build] REVIEWED: CMake when compiling for web (#2820) by @object71 +[build] REVIEWED: Avoid MSVC warnings in raylib project (#2871) by @JeffM2501 +[build] REVIEWED: Paths in .bat files to build examples (#2870) by @masoudd +[build] REVIEWED: CMake, use GLVND for old cmake versions (#2826) by @simendsjo +[build] REVIEWED: Makefile, multiple tweaks +[build] REVIEWED: CI action: linux_examples.yml +[build] REVIEWED: CI action: cmake.yml +[bindings] ADDED: h-raylib (Haskell) by @Anut-py +[bindings] ADDED: raylib-c3 (C3) by @Its-Kenta +[bindings] ADDED: raylib-umka (Umka) by @RobLoach +[bindings] ADDED: chez-raylib (Chez Scheme) by @Yunoinsky +[bindings] ADDED: raylib-python-ctypes (Python) by @sDos280 +[bindings] ADDED: claylib (Common Lisp) by @shelvick +[bindings] ADDED: raylib-vapi (Vala) by @lxmcf +[bindings] ADDED: TurboRaylib (Object Pascal) by @turborium +[bindings] ADDED: Kaylib (Kotlin/Native) by @Its-Kenta +[bindings] ADDED: Raylib-Nelua (Nelua) by @Its-Kenta +[misc] REVIEWED: Update some external libraries to latest versions + ------------------------------------------------------------------------- Release: raylib 4.2 (11 August 2022) ------------------------------------------------------------------------- From af66e751db58b0576eb50865aac0842f6df153ea Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Jan 2023 23:20:14 +0100 Subject: [PATCH 0234/1710] Update CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 5900ff803..602e9a4e0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ changelog Current Release: raylib 4.2.0 (11 August 2022) ------------------------------------------------------------------------- -Release: raylib 4.5 (xx February 2022?) -WIP- +Release: raylib 4.5 (xx February 2023) -WIP- ------------------------------------------------------------------------- KEY CHANGES: - ADDED: M3D model format support with animations From 81ca2f0bf36e59623084d583f3e26c3709b96a27 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 27 Jan 2023 10:20:42 -0800 Subject: [PATCH 0235/1710] Fix warnings and bad project settings for 4.5 release (#2894) --- examples/audio/audio_raw_stream.c | 2 +- examples/core/core_custom_frame_control.c | 6 +- examples/core/core_drop_files.c | 2 +- examples/shaders/shaders_hot_reloading.c | 2 +- examples/shapes/raygui.h | 8 +-- examples/text/text_draw_3d.c | 6 +- examples/textures/textures_fog_of_war.c | 22 +++---- examples/textures/textures_polygon.c | 2 +- .../examples/rlgl_compute_shaders.vcxproj | 63 +++++++++---------- .../VS2022/examples/rlgl_standalone.vcxproj | 16 ++--- .../examples/shaders_hot_reloading.vcxproj | 16 ++--- .../shapes_draw_circle_sector.vcxproj | 16 ++--- .../shapes_draw_rectangle_rounded.vcxproj | 16 ++--- .../VS2022/examples/shapes_draw_ring.vcxproj | 16 ++--- projects/VS2022/examples/text_draw_3d.vcxproj | 16 ++--- .../examples/textures_fog_of_war.vcxproj | 16 ++--- .../VS2022/examples/textures_polygon.vcxproj | 16 ++--- src/rmodels.c | 2 +- 18 files changed, 120 insertions(+), 123 deletions(-) diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 142450e83..6befa765a 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -43,7 +43,7 @@ void AudioInputCallback(void *buffer, unsigned int frames) float incr = audioFrequency/44100.0f; short *d = (short *)buffer; - for (int i = 0; i < frames; i++) + for (unsigned int i = 0; i < frames; i++) { d[i] = (short)(32000.0f*sinf(2*PI*sineIdx)); sineIdx += incr; diff --git a/examples/core/core_custom_frame_control.c b/examples/core/core_custom_frame_control.c index ed0dbc661..9bfd25d3e 100644 --- a/examples/core/core_custom_frame_control.c +++ b/examples/core/core_custom_frame_control.c @@ -86,8 +86,8 @@ int main(void) DrawCircle((int)position, GetScreenHeight()/2 - 25, 50, RED); - DrawText(TextFormat("%03.0f ms", timeCounter*1000.0f), position - 40, GetScreenHeight()/2 - 100, 20, MAROON); - DrawText(TextFormat("PosX: %03.0f", position), position - 50, GetScreenHeight()/2 + 40, 20, BLACK); + 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); @@ -115,7 +115,7 @@ int main(void) deltaTime = (float)(currentTime - previousTime); } } - else deltaTime = updateDrawTime; // Framerate could be variable + else deltaTime = (float)updateDrawTime; // Framerate could be variable previousTime = currentTime; //---------------------------------------------------------------------------------- diff --git a/examples/core/core_drop_files.c b/examples/core/core_drop_files.c index 9ebe0f957..cfe03e196 100644 --- a/examples/core/core_drop_files.c +++ b/examples/core/core_drop_files.c @@ -58,7 +58,7 @@ int main(void) { DrawText("Dropped files:", 100, 40, 20, DARKGRAY); - for (int i = 0; i < droppedFiles.count; i++) + for (unsigned int i = 0; i < droppedFiles.count; i++) { if (i%2 == 0) DrawRectangle(0, 85 + 40*i, screenWidth, 40, Fade(LIGHTGRAY, 0.5f)); else DrawRectangle(0, 85 + 40*i, screenWidth, 40, Fade(LIGHTGRAY, 0.3f)); diff --git a/examples/shaders/shaders_hot_reloading.c b/examples/shaders/shaders_hot_reloading.c index e58c45f3c..395d44cfe 100644 --- a/examples/shaders/shaders_hot_reloading.c +++ b/examples/shaders/shaders_hot_reloading.c @@ -38,7 +38,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hot reloading"); const char *fragShaderFileName = "resources/shaders/glsl%i/reload.fs"; - long fragShaderFileModTime = GetFileModTime(TextFormat(fragShaderFileName, GLSL_VERSION)); + time_t fragShaderFileModTime = GetFileModTime(TextFormat(fragShaderFileName, GLSL_VERSION)); // Load raymarching shader // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 3e7dea442..112370aca 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1511,7 +1511,7 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, 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 : -1; + float verticalMin = hasVerticalScrollBar? 0 : -1.0f; float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); // Update control @@ -3205,8 +3205,8 @@ Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs) if (CheckCollisionPointRec(mousePoint, bounds)) { // NOTE: Cell values must be rounded to int - currentCell.x = (int)((mousePoint.x - bounds.x)/spacing); - currentCell.y = (int)((mousePoint.y - bounds.y)/spacing); + currentCell.x = (float)((mousePoint.x - bounds.x)/spacing); + currentCell.y = (float)((mousePoint.y - bounds.y)/spacing); } } //-------------------------------------------------------------------- @@ -3842,7 +3842,7 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color // NOTE: We get text size after icon has been processed // TODO: REVIEW: We consider text size in case of line breaks! -> MeasureTextEx() depends on raylib! - Vector2 textSize = MeasureTextEx(GuiGetFont(), text, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING)); + Vector2 textSize = MeasureTextEx(GuiGetFont(), text, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); //int textWidth = GetTextWidth(text); //int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index 0711d54cd..127eb7c6a 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -152,13 +152,13 @@ int main(void) if (IsFileExtension(droppedFiles.paths[0], ".ttf")) { UnloadFont(font); - font = LoadFontEx(droppedFiles.paths[0], fontSize, 0, 0); + font = LoadFontEx(droppedFiles.paths[0], (int)fontSize, 0, 0); } else if (IsFileExtension(droppedFiles.paths[0], ".fnt")) { UnloadFont(font); font = LoadFont(droppedFiles.paths[0]); - fontSize = font.baseSize; + fontSize = (float)font.baseSize; } UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory @@ -742,7 +742,7 @@ static Vector3 MeasureTextWave3D(Font font, const char* text, float fontSize, fl static Color GenerateRandomColor(float s, float v) { const float Phi = 0.618033988749895f; // Golden ratio conjugate - float h = GetRandomValue(0, 360); + float h = (float)GetRandomValue(0, 360); h = fmodf((h + h*Phi), 360.0f); return ColorFromHSV(h, s, v); } diff --git a/examples/textures/textures_fog_of_war.c b/examples/textures/textures_fog_of_war.c index 7c3d66642..433f364a6 100644 --- a/examples/textures/textures_fog_of_war.c +++ b/examples/textures/textures_fog_of_war.c @@ -51,7 +51,7 @@ int main(void) // Load map tiles (generating 2 random tile ids for testing) // NOTE: Map tile ids should be probably loaded from an external map file - for (int i = 0; i < map.tilesY*map.tilesX; i++) map.tileIds[i] = GetRandomValue(0, 1); + for (unsigned int i = 0; i < map.tilesY*map.tilesX; i++) map.tileIds[i] = GetRandomValue(0, 1); // Player position on the screen (pixel coordinates, not tile coordinates) Vector2 playerPosition = { 180, 130 }; @@ -80,12 +80,12 @@ int main(void) // Check player position to avoid moving outside tilemap limits if (playerPosition.x < 0) playerPosition.x = 0; - else if ((playerPosition.x + PLAYER_SIZE) > (map.tilesX*MAP_TILE_SIZE)) playerPosition.x = map.tilesX*MAP_TILE_SIZE - PLAYER_SIZE; + else if ((playerPosition.x + PLAYER_SIZE) > (map.tilesX*MAP_TILE_SIZE)) playerPosition.x = (float)map.tilesX*MAP_TILE_SIZE - PLAYER_SIZE; if (playerPosition.y < 0) playerPosition.y = 0; - else if ((playerPosition.y + PLAYER_SIZE) > (map.tilesY*MAP_TILE_SIZE)) playerPosition.y = map.tilesY*MAP_TILE_SIZE - PLAYER_SIZE; + else if ((playerPosition.y + PLAYER_SIZE) > (map.tilesY*MAP_TILE_SIZE)) playerPosition.y = (float)map.tilesY*MAP_TILE_SIZE - PLAYER_SIZE; // Previous visited tiles are set to partial fog - for (int i = 0; i < map.tilesX*map.tilesY; i++) if (map.tileFog[i] == 1) map.tileFog[i] = 2; + for (unsigned int i = 0; i < map.tilesX*map.tilesY; i++) if (map.tileFog[i] == 1) map.tileFog[i] = 2; // Get current tile position from player pixel position playerTileX = (int)((playerPosition.x + MAP_TILE_SIZE/2)/MAP_TILE_SIZE); @@ -95,7 +95,7 @@ int main(void) // NOTE: We check tilemap limits to avoid processing tiles out-of-array-bounds (it could crash program) for (int y = (playerTileY - PLAYER_TILE_VISIBILITY); y < (playerTileY + PLAYER_TILE_VISIBILITY); y++) for (int x = (playerTileX - PLAYER_TILE_VISIBILITY); x < (playerTileX + PLAYER_TILE_VISIBILITY); x++) - if ((x >= 0) && (x < map.tilesX) && (y >= 0) && (y < map.tilesY)) map.tileFog[y*map.tilesX + x] = 1; + if ((x >= 0) && (x < (int)map.tilesX) && (y >= 0) && (y < (int)map.tilesY)) map.tileFog[y*map.tilesX + x] = 1; //---------------------------------------------------------------------------------- // Draw @@ -103,8 +103,8 @@ int main(void) // Draw fog of war to a small render texture for automatic smoothing on scaling BeginTextureMode(fogOfWar); ClearBackground(BLANK); - for (int y = 0; y < map.tilesY; y++) - for (int x = 0; x < map.tilesX; x++) + for (unsigned int y = 0; y < map.tilesY; y++) + for (unsigned int x = 0; x < map.tilesX; x++) if (map.tileFog[y*map.tilesX + x] == 0) DrawRectangle(x, y, 1, 1, BLACK); else if (map.tileFog[y*map.tilesX + x] == 2) DrawRectangle(x, y, 1, 1, Fade(BLACK, 0.8f)); EndTextureMode(); @@ -113,9 +113,9 @@ int main(void) ClearBackground(RAYWHITE); - for (int y = 0; y < map.tilesY; y++) + for (unsigned int y = 0; y < map.tilesY; y++) { - for (int x = 0; x < map.tilesX; x++) + for (unsigned int x = 0; x < map.tilesX; x++) { // Draw tiles from id (and tile borders) DrawRectangle(x*MAP_TILE_SIZE, y*MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE, @@ -129,8 +129,8 @@ int main(void) // Draw fog of war (scaled to full map, bilinear filtering) - DrawTexturePro(fogOfWar.texture, (Rectangle){ 0, 0, fogOfWar.texture.width, -fogOfWar.texture.height }, - (Rectangle){ 0, 0, map.tilesX*MAP_TILE_SIZE, map.tilesY*MAP_TILE_SIZE }, + DrawTexturePro(fogOfWar.texture, (Rectangle){ 0, 0, (float)fogOfWar.texture.width, (float)-fogOfWar.texture.height }, + (Rectangle){ 0, 0, (float)map.tilesX*MAP_TILE_SIZE, (float)map.tilesY*MAP_TILE_SIZE }, (Vector2){ 0, 0 }, 0.0f, WHITE); // Draw player current tile diff --git a/examples/textures/textures_polygon.c b/examples/textures/textures_polygon.c index 18fc95249..cf59af979 100644 --- a/examples/textures/textures_polygon.c +++ b/examples/textures/textures_polygon.c @@ -91,7 +91,7 @@ int main(void) DrawText("textured polygon", 20, 20, 20, DARKGRAY); - DrawTexturePoly(texture, (Vector2){ GetScreenWidth()/2, GetScreenHeight()/2 }, + DrawTexturePoly(texture, (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }, positions, texcoords, MAX_POINTS, WHITE); EndDrawing(); diff --git a/projects/VS2022/examples/rlgl_compute_shaders.vcxproj b/projects/VS2022/examples/rlgl_compute_shaders.vcxproj index 14fe2f4ee..2e514f3d4 100644 --- a/projects/VS2022/examples/rlgl_compute_shaders.vcxproj +++ b/projects/VS2022/examples/rlgl_compute_shaders.vcxproj @@ -202,16 +202,15 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) Console true - - - opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -220,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -228,9 +227,8 @@ Console true - - - opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -239,16 +237,15 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) Console true - - - opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" @@ -261,16 +258,15 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) Console true - - - opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" @@ -285,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) CompileAsC true @@ -295,9 +291,8 @@ true true true - opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ @@ -308,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) CompileAsC true @@ -318,9 +313,8 @@ true true true - opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ @@ -331,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) CompileAsC true @@ -341,9 +335,8 @@ true true true - opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" @@ -360,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) CompileAsC true @@ -370,9 +363,8 @@ true true true - opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" @@ -382,9 +374,14 @@ - + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + diff --git a/projects/VS2022/examples/rlgl_standalone.vcxproj b/projects/VS2022/examples/rlgl_standalone.vcxproj index 466740431..707a73a91 100644 --- a/projects/VS2022/examples/rlgl_standalone.vcxproj +++ b/projects/VS2022/examples/rlgl_standalone.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) @@ -220,7 +220,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -239,7 +239,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) @@ -261,7 +261,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) @@ -285,7 +285,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) CompileAsC true @@ -308,7 +308,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) CompileAsC true @@ -331,7 +331,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) CompileAsC true @@ -360,7 +360,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;$(SolutionDir)..\..\examples\others\external\include;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/shaders_hot_reloading.vcxproj b/projects/VS2022/examples/shaders_hot_reloading.vcxproj index e10ec74e5..900246daf 100644 --- a/projects/VS2022/examples/shaders_hot_reloading.vcxproj +++ b/projects/VS2022/examples/shaders_hot_reloading.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -219,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -237,7 +237,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -258,7 +258,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -281,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -303,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -325,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -353,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/shapes_draw_circle_sector.vcxproj b/projects/VS2022/examples/shapes_draw_circle_sector.vcxproj index be37881fc..df61cdb1e 100644 --- a/projects/VS2022/examples/shapes_draw_circle_sector.vcxproj +++ b/projects/VS2022/examples/shapes_draw_circle_sector.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -219,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -237,7 +237,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -258,7 +258,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -281,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -303,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -325,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -353,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/shapes_draw_rectangle_rounded.vcxproj b/projects/VS2022/examples/shapes_draw_rectangle_rounded.vcxproj index af8ce6fd5..207d38f54 100644 --- a/projects/VS2022/examples/shapes_draw_rectangle_rounded.vcxproj +++ b/projects/VS2022/examples/shapes_draw_rectangle_rounded.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -219,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -237,7 +237,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -258,7 +258,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -281,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -303,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -325,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -353,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/shapes_draw_ring.vcxproj b/projects/VS2022/examples/shapes_draw_ring.vcxproj index 1488c9522..490e8e2c8 100644 --- a/projects/VS2022/examples/shapes_draw_ring.vcxproj +++ b/projects/VS2022/examples/shapes_draw_ring.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -219,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -237,7 +237,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -258,7 +258,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -281,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -303,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -325,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -353,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/text_draw_3d.vcxproj b/projects/VS2022/examples/text_draw_3d.vcxproj index c078bb010..0ea41840c 100644 --- a/projects/VS2022/examples/text_draw_3d.vcxproj +++ b/projects/VS2022/examples/text_draw_3d.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -219,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -237,7 +237,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -258,7 +258,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -281,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -303,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -325,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -353,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/textures_fog_of_war.vcxproj b/projects/VS2022/examples/textures_fog_of_war.vcxproj index aa93c01a8..344a38cc5 100644 --- a/projects/VS2022/examples/textures_fog_of_war.vcxproj +++ b/projects/VS2022/examples/textures_fog_of_war.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -219,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -237,7 +237,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -258,7 +258,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -281,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -303,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -325,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -353,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/textures_polygon.vcxproj b/projects/VS2022/examples/textures_polygon.vcxproj index 9467c8aeb..1788f426a 100644 --- a/projects/VS2022/examples/textures_polygon.vcxproj +++ b/projects/VS2022/examples/textures_polygon.vcxproj @@ -202,7 +202,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -219,7 +219,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -237,7 +237,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -258,7 +258,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -281,7 +281,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -303,7 +303,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -325,7 +325,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -353,7 +353,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/src/rmodels.c b/src/rmodels.c index 953d016ad..efbe431cf 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1870,7 +1870,7 @@ bool ExportMesh(Mesh mesh, const char *fileName) static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount) { // Init model materials - for (unsigned int m = 0; m < materialCount; m++) + for (int m = 0; m < materialCount; m++) { // Init material to default // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE From 83ff7b246613c57dd5703d24965b4036332a100f Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Fri, 27 Jan 2023 13:24:03 -0500 Subject: [PATCH 0236/1710] ADDED: `IsShaderReady()`, `IsImageReady()`, `IsFontReady()`, `IsWaveReady()`, `IsSoundReady()`, `IsMusicReady()` (#2892) These IsReady() functions provide a method in order to verify whether or not the object was loaded successfully. They're useful to make sure the assets are there prior to using them. --- src/raudio.c | 24 ++++++++++++++++++++++++ src/raylib.h | 7 +++++++ src/rcore.c | 6 ++++++ src/rtext.c | 6 ++++++ src/rtextures.c | 6 ++++++ 5 files changed, 49 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index 34ab0702e..46d8223d9 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -836,6 +836,12 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int return wave; } +// Checks if wave data is ready +bool IsWaveReady(Wave wave) +{ + return wave.data != NULL; +} + // Load sound from file // NOTE: The entire file is loaded to memory to be played (no-streaming) Sound LoadSound(const char *fileName) @@ -892,6 +898,12 @@ Sound LoadSoundFromWave(Wave wave) return sound; } +// Checks if a sound is ready +bool IsSoundReady(Sound sound) +{ + return sound.stream.buffer != NULL; +} + // Unload wave data void UnloadWave(Wave wave) { @@ -1614,6 +1626,12 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, return music; } +// Checks if a music stream is ready +bool IsMusicReady(Music music) +{ + return music.ctxData != NULL; +} + // Unload music stream void UnloadMusicStream(Music music) { @@ -1967,6 +1985,12 @@ AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, un return stream; } +// Checks if an audio stream is ready +RLAPI bool IsAudioStreamReady(AudioStream stream) +{ + return stream.buffer != NULL; +} + // Unload audio stream and free memory void UnloadAudioStream(AudioStream stream) { diff --git a/src/raylib.h b/src/raylib.h index 10e69acdf..168b10443 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1012,6 +1012,7 @@ RLAPI void UnloadVrStereoConfig(VrStereoConfig config); // Unload VR s // 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 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 @@ -1234,6 +1235,7 @@ RLAPI Image LoadImageAnim(const char *fileName, int *frames); 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 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 bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success @@ -1354,6 +1356,7 @@ RLAPI Font LoadFont(const char *fileName); RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set 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 *fontChars, int glyphCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +RLAPI bool IsFontReady(Font font); // Check if a font is ready RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type); // Load font data for further use RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI void UnloadFontData(GlyphInfo *chars, int glyphCount); // Unload font chars info data (RAM) @@ -1513,8 +1516,10 @@ RLAPI void SetMasterVolume(float volume); // Set mas // 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 Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data +RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready 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 @@ -1542,6 +1547,7 @@ RLAPI void UnloadWaveSamples(float *samples); // Unload // 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 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 @@ -1558,6 +1564,7 @@ RLAPI float GetMusicTimePlayed(Music music); // Get cur // 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 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 diff --git a/src/rcore.c b/src/rcore.c index 1f97edb2a..f2f0cbbe0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2509,6 +2509,12 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) return shader; } +// Check if a shader is ready +bool IsShaderReady(Shader shader) +{ + return shader.locs != NULL; +} + // Unload shader from GPU memory (VRAM) void UnloadShader(Shader shader) { diff --git a/src/rtext.c b/src/rtext.c index 718fda04b..9908b44df 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -535,6 +535,12 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int return font; } +// Check if a font is ready +bool IsFontReady(Font font) +{ + return font.glyphs != NULL; +} + // 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 *fontChars, int glyphCount, int type) diff --git a/src/rtextures.c b/src/rtextures.c index 05dacb21a..e2d5ca083 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -502,6 +502,12 @@ Image LoadImageFromScreen(void) return image; } +// Check if an image is ready +bool IsImageReady(Image image) +{ + return image.data != NULL; +} + // Unload image from CPU memory (RAM) void UnloadImage(Image image) { From 0b42da4085e4ef840c069e9de8f694d1540b573d Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sat, 28 Jan 2023 00:33:15 +0200 Subject: [PATCH 0237/1710] Raylib.h has exceeded 512 functions! (#2896) --- parser/raylib_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 97e1e79a9..7bb8cf7fa 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -70,7 +70,7 @@ #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 512 // Maximum number of functions to parse +#define MAX_FUNCS_TO_PARSE 1024 // Maximum number of functions to parse #define MAX_LINE_LENGTH 512 // Maximum length of one line (including comments) From 7fff1ba0b035cc4f9ac5d1711686299b074baea1 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 28 Jan 2023 06:13:09 -0500 Subject: [PATCH 0238/1710] ADDED: IsModelReady(), IsMaterialReady(), IsTextureReady(), IsRenderTextureReady() (#2895) --- src/raylib.h | 4 ++++ src/rmodels.c | 12 ++++++++++++ src/rtextures.c | 14 +++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 168b10443..e0b7a3159 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1311,7 +1311,9 @@ RLAPI Texture2D LoadTexture(const char *fileName); 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 void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) +RLAPI bool IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready 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 @@ -1441,6 +1443,7 @@ RLAPI void DrawGrid(int slices, float spacing); // 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 void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM) RLAPI void UnloadModelKeepMeshes(Model model); // Unload model (but not meshes) from memory (RAM and/or VRAM) RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) @@ -1481,6 +1484,7 @@ RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // 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 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 diff --git a/src/rmodels.c b/src/rmodels.c index efbe431cf..15de05c09 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1109,6 +1109,12 @@ Model LoadModelFromMesh(Mesh mesh) return model; } +// Check if a model is ready +bool IsModelReady(Model model) +{ + return model.meshes != NULL && model.materials != NULL && model.meshMaterial != NULL && model.meshCount > 0 && model.materialCount > 0; +} + // 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() @@ -1950,6 +1956,12 @@ Material LoadMaterialDefault(void) return material; } +// Check if a material is ready +bool IsMaterialReady(Material material) +{ + return material.maps != NULL; +} + // Unload material from memory void UnloadMaterial(Material material) { diff --git a/src/rtextures.c b/src/rtextures.c index e2d5ca083..89a91b051 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -505,7 +505,7 @@ Image LoadImageFromScreen(void) // Check if an image is ready bool IsImageReady(Image image) { - return image.data != NULL; + return image.data != NULL && image.width > 0 && image.height > 0 && image.format > 0; } // Unload image from CPU memory (RAM) @@ -3330,6 +3330,12 @@ RenderTexture2D LoadRenderTexture(int width, int height) return target; } +// Check if a texture is ready +bool IsTextureReady(Texture2D texture) +{ + return texture.id > 0 && texture.width > 0 && texture.height > 0 && texture.format > 0; +} + // Unload texture from GPU memory (VRAM) void UnloadTexture(Texture2D texture) { @@ -3341,6 +3347,12 @@ void UnloadTexture(Texture2D texture) } } +// Check if a render texture is ready +bool IsRenderTextureReady(RenderTexture2D target) +{ + return target.id > 0 && IsTextureReady(target.depth) && IsTextureReady(target.texture); +} + // Unload render texture from GPU memory (VRAM) void UnloadRenderTexture(RenderTexture2D target) { From df5a736c009d98187e703a55c604345e12777bb7 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Sat, 28 Jan 2023 20:26:41 +0200 Subject: [PATCH 0239/1710] fix typo (#2899) patrix=>matrix --- src/raymath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raymath.h b/src/raymath.h index 435ea3c6e..eb244ad44 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -903,7 +903,7 @@ RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) { Vector3 result = { 0 }; - // Calculate unproject matrix (multiply view patrix by projection matrix) and invert it + // Calculate unproject matrix (multiply view matrix by projection matrix) and invert it Matrix matViewProj = { // MatrixMultiply(view, projection); view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, From 929a46cbab648955ddad1a04d1ed848c488fc9d4 Mon Sep 17 00:00:00 2001 From: Uneven Prankster <33995085+GithubPrankster@users.noreply.github.com> Date: Mon, 30 Jan 2023 13:00:39 -0300 Subject: [PATCH 0240/1710] [models] Fix M3D vertex color import. (#2878) * Fix vertex color import for .m3d * Only load vertex colors when color map and/or materials are present * Only execute when color array is present --------- Co-authored-by: Uneven Prankster --- src/rmodels.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 15de05c09..af61b8e5d 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5633,11 +5633,9 @@ static Model LoadM3D(const char *fileName) 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)); - // without material, we rely on vertex colors - if (mi == M3D_UNDEF && model.meshes[k].colors == NULL) + if(m3d->cmap != NULL || mi != M3D_UNDEF) { model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); - for (j = 0; j < model.meshes[k].vertexCount*4; j += 4) memcpy(&model.meshes[k].colors[j], &WHITE, 4); } if (m3d->numbone && m3d->numskin) @@ -5662,10 +5660,10 @@ static Model LoadM3D(const char *fileName) 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; - - if (mi == M3D_UNDEF) + + // without vertex color (full transparency), we use the default color + if(model.meshes[k].colors != NULL) { - // without vertex color (full transparency), we use the default color 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) From c94c666d0493fa32a810863d579d34289d2bfc1b Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Jan 2023 17:05:12 +0100 Subject: [PATCH 0241/1710] Review formatting for M3D loading vertex colors --- src/rmodels.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index af61b8e5d..b5d8578f6 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5633,11 +5633,9 @@ static Model LoadM3D(const char *fileName) 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(m3d->cmap != NULL || mi != M3D_UNDEF) - { - model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); - } - + // If color map is provided, we allocate storage for vertex colors + if (m3d->cmap != NULL) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); + if (m3d->numbone && m3d->numskin) { model.meshes[k].boneIds = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); @@ -5651,25 +5649,25 @@ static Model LoadM3D(const char *fileName) } // 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; + 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 (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); + 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); + 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); + 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) From 89171a26083da40a3a78dc0f1dee466542ed7668 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Wed, 1 Feb 2023 11:09:03 +0100 Subject: [PATCH 0242/1710] Add WASM support for Zig build (#2901) * Add WASM support for Zig build * Improve Web example building * Remove emscript example building with Zig again * Readd windows emscripten variables --- examples/Makefile | 29 +++++++++++++++++------------ examples/build.zig | 20 +++++++++++++------- examples/shapes/resources/.gitkeep | 0 src/build.zig | 27 +++++++++++++++++++++++++-- 4 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 examples/shapes/resources/.gitkeep diff --git a/examples/Makefile b/examples/Makefile index bc2c8d711..dce02218a 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -67,8 +67,8 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif endif -# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +# Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -126,16 +126,18 @@ ifeq ($(PLATFORM),PLATFORM_DRM) endif # Define raylib release directory for compiled library -RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src +RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src ifeq ($(PLATFORM),PLATFORM_WEB) - # Emscripten required variables - EMSDK_PATH ?= C:/emsdk - EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten - CLANG_PATH = $(EMSDK_PATH)/upstream/bin - PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-1_64bit - NODE_PATH = $(EMSDK_PATH)/node/14.15.5_64bit/bin - export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) + ifeq ($(PLATFORM_OS),WINDOWS) + # Emscripten required variables + EMSDK_PATH ?= C:/emsdk + EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten + CLANG_PATH = $(EMSDK_PATH)/upstream/bin + PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-1_64bit + NODE_PATH = $(EMSDK_PATH)/node/14.15.5_64bit/bin + export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) + endif endif # Define default C compiler: CC @@ -579,7 +581,10 @@ ifeq ($(PLATFORM),PLATFORM_DRM) rm -fv *.o endif ifeq ($(PLATFORM),PLATFORM_WEB) - del *.o *.html *.js + ifeq ($(PLATFORM_OS),WINDOWS) + del *.wasm *.html *.js *.data + else + rm -f */*.wasm */*.html */*.js */*.data + endif endif @echo Cleaning done - diff --git a/examples/build.zig b/examples/build.zig index cb211aefd..eb040fd2f 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -2,6 +2,10 @@ const std = @import("std"); const builtin = @import("builtin"); fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zig.CrossTarget) !*std.build.Step { + if (target.getOsTag() == .emscripten) { + @panic("Emscripten building via Zig unsupported"); + } + // Standard release options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardReleaseOptions(); @@ -19,12 +23,7 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi if (std.mem.eql(u8, "core_loading_thread", name) and target.getOsTag() == .windows) continue; const exe = b.addExecutable(name, null); - exe.addCSourceFile(path, switch (target.getOsTag()) { - .windows => &[_][]const u8{}, - .linux => &[_][]const u8{}, - .macos => &[_][]const u8{"-DPLATFORM_DESKTOP"}, - else => @panic("Unsupported OS"), - }); + exe.addCSourceFile(path, &[_][]const u8{}); exe.setTarget(target); exe.setBuildMode(mode); exe.linkLibC(); @@ -32,6 +31,7 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi .windows => "../src/raylib.lib", .linux => "../src/libraylib.a", .macos => "../src/libraylib.a", + .emscripten => "../src/libraylib.a", else => @panic("Unsupported OS"), }); @@ -39,12 +39,14 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi exe.addIncludePath("../src/external"); exe.addIncludePath("../src/external/glfw/include"); - switch (exe.target.toTarget().os.tag) { + switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); exe.addIncludePath("external/glfw/deps/mingw"); + + exe.defineCMacro("PLATFORM_DESKTOP", null); }, .linux => { exe.linkSystemLibrary("GL"); @@ -52,6 +54,8 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi exe.linkSystemLibrary("dl"); exe.linkSystemLibrary("m"); exe.linkSystemLibrary("X11"); + + exe.defineCMacro("PLATFORM_DESKTOP", null); }, .macos => { exe.linkFramework("Foundation"); @@ -60,6 +64,8 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi exe.linkFramework("CoreAudio"); exe.linkFramework("CoreVideo"); exe.linkFramework("IOKit"); + + exe.defineCMacro("PLATFORM_DESKTOP", null); }, else => { @panic("Unsupported OS"); diff --git a/examples/shapes/resources/.gitkeep b/examples/shapes/resources/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/build.zig b/src/build.zig index e44271e8d..919db3e9f 100644 --- a/src/build.zig +++ b/src/build.zig @@ -3,7 +3,6 @@ const std = @import("std"); pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.LibExeObjStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", - "-DPLATFORM_DESKTOP", "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 @@ -25,13 +24,15 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. srcdir ++ "/utils.c", }, raylib_flags); - switch (raylib.target.toTarget().os.tag) { + switch (target.getOsTag()) { .windows => { raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); raylib.addIncludePath("external/glfw/deps/mingw"); + + raylib.defineCMacro("PLATFORM_DESKTOP", null); }, .linux => { raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); @@ -40,6 +41,8 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. raylib.linkSystemLibrary("dl"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("X11"); + + raylib.defineCMacro("PLATFORM_DESKTOP", null); }, .freebsd, .openbsd, .netbsd, .dragonfly => { raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); @@ -53,6 +56,8 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. raylib.linkSystemLibrary("Xi"); raylib.linkSystemLibrary("Xxf86vm"); raylib.linkSystemLibrary("Xcursor"); + + raylib.defineCMacro("PLATFORM_DESKTOP", null); }, .macos => { // On macos rglfw.c include Objective-C files. @@ -68,6 +73,24 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. raylib.linkFramework("CoreGraphics"); raylib.linkFramework("AppKit"); raylib.linkFramework("IOKit"); + + raylib.defineCMacro("PLATFORM_DESKTOP", null); + }, + .emscripten => { + raylib.defineCMacro("PLATFORM_WEB", null); + raylib.defineCMacro("GRAPHICS_API_OPENGL_ES2", null); + + if (b.sysroot == null) { + @panic("Pass '--sysroot \"$EMSDK/upstream/emscripten\"'"); + } + + const cache_include = std.fs.path.join(b.allocator, &.{ b.sysroot.?, "cache", "sysroot", "include" }) catch @panic("Out of memory"); + defer b.allocator.free(cache_include); + + var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{.access_sub_paths = true, .no_follow = true}) catch @panic("No emscripten cache. Generate it!"); + dir.close(); + + raylib.addIncludePath(cache_include); }, else => { @panic("Unsupported OS"); From a151cbd37ace03a377fd582a29f660dd0ea9f97a Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Feb 2023 11:18:55 +0100 Subject: [PATCH 0243/1710] ADDED: `Vector2LineAngle()` #2887 REVIEWED: `Vector2Angle()` --- src/raymath.h | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index eb244ad44..96c241843 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -306,24 +306,33 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) return result; } -// Calculate angle from two vectors +// Calculate angle between two vectors +// NOTE: Angle is calculated from origin point (0, 0) +RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) +{ + float result = atan2f(v2.y - v1.y, v2.x - v1.x); + + return result; +} + +// Calculate angle defined by a two vectors line // NOTE: Parameters need to be normalized // Current implementation should be aligned with glm::angle -RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) +RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - float dot = v1.x*v2.x + v1.y*v2.y; // Dot product + float dot = start.x*end.x + start.y*end.y; // Dot product - float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp + float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp if (dotClamp > 1.0f) dotClamp = 1.0f; result = acosf(dotClamp); // Alternative implementation, more costly - //float v1Length = sqrtf((v1.x*v1.x) + (v1.y*v1.y)); - //float v2Length = sqrtf((v2.x*v2.x) + (v2.y*v2.y)); - //float result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); + //float v1Length = sqrtf((start.x*start.x) + (start.y*start.y)); + //float v2Length = sqrtf((end.x*end.x) + (end.y*end.y)); + //float result = -acosf((start.x*end.x + start.y*end.y)/(v1Length*v2Length)); return result; } From d827a65e59c8474b4899ff7034dcc4d17b2fb4c0 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Feb 2023 11:45:42 +0100 Subject: [PATCH 0244/1710] Update external libraries Switch to official `stb_vorbis.c` instead of using an outdated fork --- src/external/cgltf.h | 265 +- src/external/dr_flac.h | 588 +++-- src/external/dr_mp3.h | 84 +- src/external/dr_wav.h | 111 +- src/external/miniaudio.h | 106 +- src/external/stb_image.h | 144 +- src/external/{stb_vorbis.h => stb_vorbis.c} | 2497 ++++++++++--------- src/raudio.c | 3 +- 8 files changed, 2230 insertions(+), 1568 deletions(-) rename src/external/{stb_vorbis.h => stb_vorbis.c} (68%) diff --git a/src/external/cgltf.h b/src/external/cgltf.h index 02064103d..a4d9c72de 100644 --- a/src/external/cgltf.h +++ b/src/external/cgltf.h @@ -1,7 +1,7 @@ /** * cgltf - a single-file glTF 2.0 parser written in C99. * - * Version: 1.12 + * Version: 1.13 * * Website: https://github.com/jkuhlmann/cgltf * @@ -80,19 +80,16 @@ * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t * and only works with single-component data types. * - * `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*, - * char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that - * can be attached to many glTF objects (which can be arbitrary JSON data). The - * `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data - * as it appears in the complete glTF JSON data. This function copies the extras data - * into the provided buffer. If `dest` is NULL, the length of the data is written into - * `dest_size`. You can then parse this data using your own JSON parser + * `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many + * glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using + * cgltf_extras::data directly instead. You can parse this data using your own JSON parser * or, if you've included the cgltf implementation using the integrated JSMN JSON parser. */ #ifndef CGLTF_H_INCLUDED__ #define CGLTF_H_INCLUDED__ #include +#include /* For uint8_t, uint32_t */ #ifdef __cplusplus extern "C" { @@ -256,8 +253,10 @@ typedef enum cgltf_data_free_method { } cgltf_data_free_method; typedef struct cgltf_extras { - cgltf_size start_offset; - cgltf_size end_offset; + cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */ + cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */ + + char* data; } cgltf_extras; typedef struct cgltf_extension { @@ -432,8 +431,6 @@ typedef struct cgltf_pbr_metallic_roughness cgltf_float base_color_factor[4]; cgltf_float metallic_factor; cgltf_float roughness_factor; - - cgltf_extras extras; } cgltf_pbr_metallic_roughness; typedef struct cgltf_pbr_specular_glossiness @@ -833,6 +830,8 @@ void cgltf_free(cgltf_data* data); void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); +const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view); + cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); @@ -841,6 +840,7 @@ cgltf_size cgltf_num_components(cgltf_type type); cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); +/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); #ifdef __cplusplus @@ -863,7 +863,6 @@ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* #ifdef CGLTF_IMPLEMENTATION -#include /* For uint8_t, uint32_t */ #include /* For strncpy */ #include /* For fopen */ #include /* For UINT_MAX etc */ @@ -905,15 +904,15 @@ enum jsmnerr { }; typedef struct { jsmntype_t type; - int start; - int end; + ptrdiff_t start; + ptrdiff_t end; int size; #ifdef JSMN_PARENT_LINKS int parent; #endif } jsmntok_t; typedef struct { - unsigned int pos; /* offset in the JSON string */ + size_t pos; /* offset in the JSON string */ unsigned int toknext; /* next token to allocate */ int toksuper; /* superior token node, e.g parent object or array */ } jsmn_parser; @@ -924,12 +923,15 @@ static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t */ +#ifndef CGLTF_CONSTS static const cgltf_size GlbHeaderSize = 12; static const cgltf_size GlbChunkHeaderSize = 8; static const uint32_t GlbVersion = 2; static const uint32_t GlbMagic = 0x46546C67; static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; static const uint32_t GlbMagicBinChunk = 0x004E4942; +#define CGLTF_CONSTS +#endif #ifndef CGLTF_MALLOC #define CGLTF_MALLOC(size) malloc(size) @@ -1745,7 +1747,12 @@ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* return cgltf_result_success; } -void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) +static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras) +{ + data->memory.free_func(data->memory.user_data, extras->data); +} + +static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) { for (cgltf_size i = 0; i < extensions_count; ++i) { @@ -1755,6 +1762,12 @@ void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_ data->memory.free_func(data->memory.user_data, extensions); } +static void cgltf_free_texture_view(cgltf_data* data, cgltf_texture_view* view) +{ + cgltf_free_extensions(data, view->extensions, view->extensions_count); + cgltf_free_extras(data, &view->extras); +} + void cgltf_free(cgltf_data* data) { if (!data) @@ -1770,6 +1783,7 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->asset.min_version); cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count); + cgltf_free_extras(data, &data->asset.extras); for (cgltf_size i = 0; i < data->accessors_count; ++i) { @@ -1780,8 +1794,12 @@ void cgltf_free(cgltf_data* data) cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count); cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count); cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count); + cgltf_free_extras(data, &data->accessors[i].sparse.extras); + cgltf_free_extras(data, &data->accessors[i].sparse.indices_extras); + cgltf_free_extras(data, &data->accessors[i].sparse.values_extras); } cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count); + cgltf_free_extras(data, &data->accessors[i].extras); } data->memory.free_func(data->memory.user_data, data->accessors); @@ -1791,6 +1809,7 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->buffer_views[i].data); cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count); + cgltf_free_extras(data, &data->buffer_views[i].extras); } data->memory.free_func(data->memory.user_data, data->buffer_views); @@ -1810,8 +1829,8 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->buffers[i].uri); cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count); + cgltf_free_extras(data, &data->buffers[i].extras); } - data->memory.free_func(data->memory.user_data, data->buffers); for (cgltf_size i = 0; i < data->meshes_count; ++i) @@ -1849,9 +1868,15 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes); } + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras); + } + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings); cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count); + cgltf_free_extras(data, &data->meshes[i].primitives[j].extras); } data->memory.free_func(data->memory.user_data, data->meshes[i].primitives); @@ -1863,6 +1888,7 @@ void cgltf_free(cgltf_data* data) } cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count); + cgltf_free_extras(data, &data->meshes[i].extras); data->memory.free_func(data->memory.user_data, data->meshes[i].target_names); } @@ -1875,49 +1901,50 @@ void cgltf_free(cgltf_data* data) if(data->materials[i].has_pbr_metallic_roughness) { - cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.metallic_roughness_texture); + cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.base_color_texture); } if(data->materials[i].has_pbr_specular_glossiness) { - cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.diffuse_texture); + cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.specular_glossiness_texture); } if(data->materials[i].has_clearcoat) { - cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_texture.extensions, data->materials[i].clearcoat.clearcoat_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_normal_texture.extensions, data->materials[i].clearcoat.clearcoat_normal_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_texture); + cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_roughness_texture); + cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_normal_texture); } if(data->materials[i].has_specular) { - cgltf_free_extensions(data, data->materials[i].specular.specular_texture.extensions, data->materials[i].specular.specular_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].specular.specular_color_texture.extensions, data->materials[i].specular.specular_color_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].specular.specular_texture); + cgltf_free_texture_view(data, &data->materials[i].specular.specular_color_texture); } if(data->materials[i].has_transmission) { - cgltf_free_extensions(data, data->materials[i].transmission.transmission_texture.extensions, data->materials[i].transmission.transmission_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].transmission.transmission_texture); } if (data->materials[i].has_volume) { - cgltf_free_extensions(data, data->materials[i].volume.thickness_texture.extensions, data->materials[i].volume.thickness_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].volume.thickness_texture); } if(data->materials[i].has_sheen) { - cgltf_free_extensions(data, data->materials[i].sheen.sheen_color_texture.extensions, data->materials[i].sheen.sheen_color_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].sheen.sheen_roughness_texture.extensions, data->materials[i].sheen.sheen_roughness_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_color_texture); + cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_roughness_texture); } if(data->materials[i].has_iridescence) { - cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_texture.extensions, data->materials[i].iridescence.iridescence_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_thickness_texture.extensions, data->materials[i].iridescence.iridescence_thickness_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_texture); + cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_thickness_texture); } - cgltf_free_extensions(data, data->materials[i].normal_texture.extensions, data->materials[i].normal_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].occlusion_texture.extensions, data->materials[i].occlusion_texture.extensions_count); - cgltf_free_extensions(data, data->materials[i].emissive_texture.extensions, data->materials[i].emissive_texture.extensions_count); + cgltf_free_texture_view(data, &data->materials[i].normal_texture); + cgltf_free_texture_view(data, &data->materials[i].occlusion_texture); + cgltf_free_texture_view(data, &data->materials[i].emissive_texture); cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count); + cgltf_free_extras(data, &data->materials[i].extras); } data->memory.free_func(data->memory.user_data, data->materials); @@ -1929,6 +1956,7 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->images[i].mime_type); cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count); + cgltf_free_extras(data, &data->images[i].extras); } data->memory.free_func(data->memory.user_data, data->images); @@ -1936,7 +1964,9 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->textures_count; ++i) { data->memory.free_func(data->memory.user_data, data->textures[i].name); + cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count); + cgltf_free_extras(data, &data->textures[i].extras); } data->memory.free_func(data->memory.user_data, data->textures); @@ -1944,7 +1974,9 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->samplers_count; ++i) { data->memory.free_func(data->memory.user_data, data->samplers[i].name); + cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count); + cgltf_free_extras(data, &data->samplers[i].extras); } data->memory.free_func(data->memory.user_data, data->samplers); @@ -1955,6 +1987,7 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->skins[i].joints); cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count); + cgltf_free_extras(data, &data->skins[i].extras); } data->memory.free_func(data->memory.user_data, data->skins); @@ -1962,7 +1995,18 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->cameras_count; ++i) { data->memory.free_func(data->memory.user_data, data->cameras[i].name); + + if (data->cameras[i].type == cgltf_camera_type_perspective) + { + cgltf_free_extras(data, &data->cameras[i].data.perspective.extras); + } + else if (data->cameras[i].type == cgltf_camera_type_orthographic) + { + cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras); + } + cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count); + cgltf_free_extras(data, &data->cameras[i].extras); } data->memory.free_func(data->memory.user_data, data->cameras); @@ -1970,6 +2014,8 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->lights_count; ++i) { data->memory.free_func(data->memory.user_data, data->lights[i].name); + + cgltf_free_extras(data, &data->lights[i].extras); } data->memory.free_func(data->memory.user_data, data->lights); @@ -1979,7 +2025,19 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->nodes[i].name); data->memory.free_func(data->memory.user_data, data->nodes[i].children); data->memory.free_func(data->memory.user_data, data->nodes[i].weights); + + if (data->nodes[i].has_mesh_gpu_instancing) + { + for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j) + { + data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name); + } + + data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes); + } + cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count); + cgltf_free_extras(data, &data->nodes[i].extras); } data->memory.free_func(data->memory.user_data, data->nodes); @@ -1990,6 +2048,7 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->scenes[i].nodes); cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count); + cgltf_free_extras(data, &data->scenes[i].extras); } data->memory.free_func(data->memory.user_data, data->scenes); @@ -2000,16 +2059,19 @@ void cgltf_free(cgltf_data* data) for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) { cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count); + cgltf_free_extras(data, &data->animations[i].samplers[j].extras); } data->memory.free_func(data->memory.user_data, data->animations[i].samplers); for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) { cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count); + cgltf_free_extras(data, &data->animations[i].channels[j].extras); } data->memory.free_func(data->memory.user_data, data->animations[i].channels); cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count); + cgltf_free_extras(data, &data->animations[i].extras); } data->memory.free_func(data->memory.user_data, data->animations); @@ -2017,11 +2079,14 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->variants_count; ++i) { data->memory.free_func(data->memory.user_data, data->variants[i].name); + + cgltf_free_extras(data, &data->variants[i].extras); } data->memory.free_func(data->memory.user_data, data->variants); cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count); + cgltf_free_extras(data, &data->extras); for (cgltf_size i = 0; i < data->extensions_used_count; ++i) { @@ -2440,7 +2505,7 @@ static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, co { CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); size_t const str_len = strlen(str); - size_t const name_length = tok->end - tok->start; + size_t const name_length = (size_t)(tok->end - tok->start); return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128; } @@ -2448,7 +2513,7 @@ static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) { CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); char tmp[128]; - int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); strncpy(tmp, (const char*)json_chunk + tok->start, size); tmp[size] = 0; return CGLTF_ATOI(tmp); @@ -2458,7 +2523,7 @@ static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_c { CGLTF_CHECK_TOKTYPE_RETTYPE(*tok, JSMN_PRIMITIVE, cgltf_size); char tmp[128]; - int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); strncpy(tmp, (const char*)json_chunk + tok->start, size); tmp[size] = 0; return (cgltf_size)CGLTF_ATOLL(tmp); @@ -2468,7 +2533,7 @@ static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json { CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); char tmp[128]; - int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); strncpy(tmp, (const char*)json_chunk + tok->start, size); tmp[size] = 0; return (cgltf_float)CGLTF_ATOF(tmp); @@ -2476,7 +2541,7 @@ static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) { - int size = tok->end - tok->start; + int size = (int)(tok->end - tok->start); return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0; } @@ -2542,7 +2607,7 @@ static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* toke { return CGLTF_ERROR_JSON; } - int size = tokens[i].end - tokens[i].start; + int size = (int)(tokens[i].end - tokens[i].start); char* result = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); if (!result) { @@ -2683,11 +2748,27 @@ static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t con return i; } -static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) +static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) { - (void)json_chunk; + if (out_extras->data) + { + return CGLTF_ERROR_JSON; + } + + /* fill deprecated fields for now, this will be removed in the future */ out_extras->start_offset = tokens[i].start; out_extras->end_offset = tokens[i].end; + + size_t start = tokens[i].start; + size_t size = tokens[i].end - start; + out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!out_extras->data) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extras->data, (const char*)json_chunk + start, size); + out_extras->data[size] = '\0'; + i = cgltf_skip_json(tokens, i); return i; } @@ -2842,7 +2923,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto int material = -1; int variants_tok = -1; - cgltf_extras extras = {0, 0}; + int extras_tok = -1; for (int k = 0; k < obj_size; ++k) { @@ -2863,7 +2944,8 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &extras); + extras_tok = i + 1; + i = cgltf_skip_json(tokens, extras_tok); } else { @@ -2891,7 +2973,13 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material); out_mappings[*offset].variant = variant; - out_mappings[*offset].extras = extras; + + if (extras_tok >= 0) + { + int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras); + if (e < 0) + return e; + } (*offset)++; } @@ -3006,7 +3094,7 @@ static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* t } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3253,7 +3341,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->indices_extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3296,7 +3384,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->values_extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3315,7 +3403,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3438,7 +3526,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3544,7 +3632,7 @@ static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture_view->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3640,10 +3728,6 @@ static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmnt i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->metallic_roughness_texture); } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) - { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras); - } else { i = cgltf_skip_json(tokens, i+1); @@ -4076,7 +4160,7 @@ static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* token } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4145,7 +4229,7 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4194,7 +4278,7 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4357,7 +4441,7 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4710,7 +4794,7 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4813,7 +4897,7 @@ static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* toke } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4897,7 +4981,7 @@ static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4951,19 +5035,6 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke { i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name); } - else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) - { - ++i; - if (cgltf_json_strcmp(tokens + i, json_chunk, "perspective") == 0) - { - out_camera->type = cgltf_camera_type_perspective; - } - else if (cgltf_json_strcmp(tokens + i, json_chunk, "orthographic") == 0) - { - out_camera->type = cgltf_camera_type_orthographic; - } - ++i; - } else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0) { ++i; @@ -4973,6 +5044,11 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke int data_size = tokens[i].size; ++i; + if (out_camera->type != cgltf_camera_type_invalid) + { + return CGLTF_ERROR_JSON; + } + out_camera->type = cgltf_camera_type_perspective; for (int k = 0; k < data_size; ++k) @@ -5007,7 +5083,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); } else { @@ -5029,6 +5105,11 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke int data_size = tokens[i].size; ++i; + if (out_camera->type != cgltf_camera_type_invalid) + { + return CGLTF_ERROR_JSON; + } + out_camera->type = cgltf_camera_type_orthographic; for (int k = 0; k < data_size; ++k) @@ -5061,7 +5142,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); } else { @@ -5076,7 +5157,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5209,7 +5290,7 @@ static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* token } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_light->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras); } else { @@ -5335,7 +5416,7 @@ static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5473,7 +5554,7 @@ static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* token } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5555,7 +5636,7 @@ static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5635,7 +5716,7 @@ static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5717,7 +5798,7 @@ static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* t } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5773,7 +5854,7 @@ static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tok } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_variant->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras); } else { @@ -5837,7 +5918,7 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5993,7 +6074,7 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens } else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras); + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -6420,7 +6501,7 @@ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, * Fills token type and boundaries. */ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, - int start, int end) { + ptrdiff_t start, ptrdiff_t end) { token->type = type; token->start = start; token->end = end; @@ -6433,7 +6514,7 @@ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *token; - int start; + ptrdiff_t start; start = parser->pos; @@ -6483,7 +6564,7 @@ static int jsmn_parse_string(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *token; - int start = parser->pos; + ptrdiff_t start = parser->pos; parser->pos++; diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h index e0b3649a2..0c43eed7f 100644 --- a/src/external/dr_flac.h +++ b/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.31 - 2021-08-16 +dr_flac - v0.12.39 - 2022-09-17 David Reid - mackron@gmail.com @@ -210,6 +210,9 @@ Build Options #define DR_FLAC_NO_SIMD Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having compatibility issues with your compiler. +#define DR_FLAC_NO_WCHAR + Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_FLAC_NO_STDIO is also defined. + Notes @@ -232,7 +235,7 @@ extern "C" { #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 31 +#define DRFLAC_VERSION_REVISION 39 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include /* For size_t. */ @@ -244,7 +247,7 @@ typedef signed short drflac_int16; typedef unsigned short drflac_uint16; typedef signed int drflac_int32; typedef unsigned int drflac_uint32; -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 drflac_int64; typedef unsigned __int64 drflac_uint64; #else @@ -261,7 +264,7 @@ typedef unsigned int drflac_uint32; #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(__powerpc64__) typedef drflac_uint64 drflac_uintptr; #else typedef drflac_uint32 drflac_uintptr; @@ -383,15 +386,13 @@ typedef enum drflac_seek_origin_current } drflac_seek_origin; -/* Packing is important on this structure because we map this directly to the raw data within the SEEKTABLE metadata block. */ -#pragma pack(2) +/* The order of members in this structure is important because we map this directly to the raw data within the SEEKTABLE metadata block. */ typedef struct { drflac_uint64 firstPCMFrame; drflac_uint64 flacFrameOffset; /* The offset from the first byte of the header of the first frame. */ drflac_uint16 pcmFrameCount; } drflac_seekpoint; -#pragma pack() typedef struct { @@ -1280,15 +1281,13 @@ typedef struct const char* pRunningData; } drflac_cuesheet_track_iterator; -/* Packing is important on this structure because we map this directly to the raw data within the CUESHEET metadata block. */ -#pragma pack(4) +/* The order of members here is important because we map this directly to the raw data within the CUESHEET metadata block. */ typedef struct { drflac_uint64 offset; drflac_uint8 index; drflac_uint8 reserved[3]; } drflac_cuesheet_track_index; -#pragma pack() typedef struct { @@ -1363,9 +1362,15 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat I am using "__inline__" only when we're compiling in strict ANSI mode. */ #if defined(__STRICT_ANSI__) - #define DRFLAC_INLINE __inline__ __attribute__((always_inline)) + #define DRFLAC_GNUC_INLINE_HINT __inline__ #else - #define DRFLAC_INLINE inline __attribute__((always_inline)) + #define DRFLAC_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRFLAC_INLINE __inline @@ -1378,7 +1383,7 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat #define DRFLAC_X64 #elif defined(__i386) || defined(_M_IX86) #define DRFLAC_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARM64) +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define DRFLAC_ARM #endif @@ -1431,16 +1436,6 @@ Unfortuantely dr_flac depends on this for a few things so we're just going to di #if defined(DRFLAC_ARM) #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) #define DRFLAC_SUPPORT_NEON - #endif - - /* Fall back to looking for the #include file. */ - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(DRFLAC_SUPPORT_NEON) && !defined(DRFLAC_NO_NEON) && __has_include() - #define DRFLAC_SUPPORT_NEON - #endif - #endif - - #if defined(DRFLAC_SUPPORT_NEON) #include #endif #endif @@ -1519,9 +1514,7 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) { #if defined(DRFLAC_SUPPORT_SSE41) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) - #if defined(DRFLAC_X64) - return DRFLAC_TRUE; /* 64-bit targets always support SSE4.1. */ - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__) + #if defined(__SSE4_1__) || defined(__AVX__) return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE41 code we can assume support. */ #else #if defined(DRFLAC_NO_CPUID) @@ -1586,18 +1579,21 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); #pragma aux _watcom_bswap16 = \ "xchg al, ah" \ - parm [ax] \ - modify [ax]; + parm [ax] \ + value [ax] \ + modify nomemory; #pragma aux _watcom_bswap32 = \ - "bswap eax" \ - parm [eax] \ - modify [eax]; + "bswap eax" \ + parm [eax] \ + value [eax] \ + modify nomemory; #pragma aux _watcom_bswap64 = \ "bswap eax" \ "bswap edx" \ "xchg eax,edx" \ parm [eax edx] \ - modify [eax edx]; + value [eax edx] \ + modify nomemory; #endif @@ -1698,6 +1694,10 @@ typedef drflac_int32 drflac_result; #define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 +#define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 + #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) @@ -1909,6 +1909,12 @@ static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) return n; } +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) +{ + const drflac_uint8* pNum = (drflac_uint8*)pData; + return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); +} + static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) { if (drflac__is_little_endian()) { @@ -1928,6 +1934,12 @@ static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) return n; } +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) +{ + const drflac_uint8* pNum = (drflac_uint8*)pData; + return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; +} + static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) { @@ -2429,6 +2441,10 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; @@ -2684,6 +2700,10 @@ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) #if defined(__WATCOMC__) && defined(__386__) #define DRFLAC_IMPLEMENT_CLZ_WATCOM #endif +#ifdef __MRC__ +#include +#define DRFLAC_IMPLEMENT_CLZ_MRC +#endif static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) { @@ -2724,6 +2744,8 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) /* Fast compile time check for ARM. */ #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) return DRFLAC_TRUE; +#elif defined(__MRC__) + return DRFLAC_TRUE; #else /* If the compiler itself does not support the intrinsic then we'll need to return false. */ #ifdef DRFLAC_HAS_LZCNT_INTRINSIC @@ -2833,6 +2855,15 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +/* Use the LZCNT instruction (only available on some processors since the 2010s). */ +#pragma aux drflac__clz_watcom_lzcnt = \ + "db 0F3h, 0Fh, 0BDh, 0C0h" /* lzcnt eax, eax */ \ + parm [eax] \ + value [eax] \ + modify nomemory; +#else +/* Use the 386+-compatible implementation. */ #pragma aux drflac__clz_watcom = \ "bsr eax, eax" \ "xor eax, 31" \ @@ -2840,6 +2871,7 @@ static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); value [eax] \ modify exact [eax] nomemory; #endif +#endif static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { @@ -2851,8 +2883,12 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC return drflac__clz_msvc(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return drflac__clz_watcom_lzcnt(x); #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); +#elif defined(__MRC__) + return __cntlzw(x); #else return drflac__clz_software(x); #endif @@ -2872,9 +2908,24 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, } } + if (bs->cache == 1) { + /* Not catching this would lead to undefined behaviour: a shift of a 32-bit number by 32 or more is undefined */ + *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; + } + setBitOffsetPlus1 = drflac__clz(bs->cache); setBitOffsetPlus1 += 1; + if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + bs->consumedBits += setBitOffsetPlus1; bs->cache <<= setBitOffsetPlus1; @@ -2989,6 +3040,25 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 } +static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) +{ +#if 1 /* Needs optimizing. */ + drflac_uint32 result = 0; + while (x > 0) { + result += 1; + x >>= 1; + } + + return result; +#endif +} + +static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) +{ + /* https://web.archive.org/web/20220205005724/https://github.com/ietf-wg-cellar/flac-specification/blob/37a49aa48ba4ba12e8757badfc59c0df35435fec/rfc_backmatter.md */ + return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; +} + /* The next two functions are responsible for calculating the prediction. @@ -2996,6 +3066,9 @@ The next two functions are responsible for calculating the prediction. When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. */ +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_int32 prediction = 0; @@ -3231,7 +3304,7 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the sake of readability and should only be used as a reference. */ -static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 i; @@ -3270,10 +3343,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drfla } - if (bitsPerSample+shift >= 32) { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } @@ -3370,6 +3443,10 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } } riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); @@ -3450,6 +3527,10 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drf if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; @@ -3560,6 +3641,11 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac return DRFLAC_FALSE; } + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } @@ -3646,7 +3732,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorde return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; drflac_uint32 zeroCountPart0 = 0; @@ -3664,14 +3750,14 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); - if (order == 0) { - return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + if (lpcOrder == 0) { + return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } riceParamMask = (drflac_uint32)~((~0UL) << riceParam); pSamplesOutEnd = pSamplesOut + (count & ~3); - if (bitsPerSample+shift > 32) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { while (pSamplesOut < pSamplesOutEnd) { /* Rice extraction. It's faster to do this one at a time against local variables than it is to use the x4 version @@ -3699,10 +3785,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } @@ -3730,10 +3816,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } @@ -3753,10 +3839,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b /*riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);*/ /* Sample reconstruction. */ - if (bitsPerSample+shift > 32) { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } else { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } i += 1; @@ -4212,20 +4298,20 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); /* In my testing the order is rarely > 12, so in this case I'm going to simplify the SSE implementation by only handling order <= 12. */ - if (order > 0 && order <= 12) { - if (bitsPerSample+shift > 32) { - return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut); + if (lpcOrder > 0 && lpcOrder <= 12) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif @@ -4364,7 +4450,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = ~((~0UL) << riceParam); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); @@ -4550,10 +4636,13 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ int32x4_t riceParam128; int64x1_t shift64; uint32x4_t one128; + int64x2_t prediction128 = { 0 }; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = ~((~0UL) << riceParam); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); @@ -4562,7 +4651,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ /* Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than - what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results + what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted so I think there's opportunity for this to be simplified. */ @@ -4630,10 +4719,6 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ /* For this version we are doing one sample at a time. */ while (pDecodedSamples < pDecodedSamplesEnd) { - int64x2_t prediction128; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || @@ -4710,41 +4795,41 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); /* In my testing the order is rarely > 12, so in this case I'm going to simplify the NEON implementation by only handling order <= 12. */ - if (order > 0 && order <= 12) { - if (bitsPerSample+shift > 32) { - return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut); + if (lpcOrder > 0 && lpcOrder <= 12) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif -static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { #if defined(DRFLAC_SUPPORT_SSE41) if (drflac__gIsSSE41Supported) { - return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported) { - return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #endif { /* Scalar fallback. */ #if 0 - return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #else - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #endif } } @@ -4765,7 +4850,10 @@ static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_ return DRFLAC_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 i; @@ -4782,10 +4870,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* pSamplesOut[i] = 0; } - if (bitsPerSample >= 24) { - pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } @@ -4798,7 +4886,7 @@ Reads and decodes the residual for the sub-frame the decoder is currently sittin when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The and parameters are used to determine how many residual values need to be decoded. */ -static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_uint8 residualMethod; drflac_uint8 partitionOrder; @@ -4818,7 +4906,7 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ } /* Ignore the first values. */ - pDecodedSamples += order; + pDecodedSamples += lpcOrder; if (!drflac__read_uint8(bs, 4, &partitionOrder)) { return DRFLAC_FALSE; @@ -4833,11 +4921,11 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ } /* Validation check. */ - if ((blockSize / (1 << partitionOrder)) < order) { + if ((blockSize / (1 << partitionOrder)) < lpcOrder) { return DRFLAC_FALSE; } - samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; partitionsRemaining = (1 << partitionOrder); for (;;) { drflac_uint8 riceParam = 0; @@ -4858,7 +4946,7 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ } if (riceParam != 0xFF) { - if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } } else { @@ -4867,7 +4955,7 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ return DRFLAC_FALSE; } - if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) { + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } } @@ -5036,7 +5124,7 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 pDecodedSamples[i] = sample; } - if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { return DRFLAC_FALSE; } @@ -5091,7 +5179,7 @@ static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 bl } } - if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) { + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } @@ -5219,6 +5307,9 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); + if (header->blockSizeInPCMFrames == 0xFFFF) { + return DRFLAC_FALSE; /* Frame is too big. This is the size of the frame minus 1. The STREAMINFO block defines the max block size which is 16-bits. Adding one will make it 17 bits and therefore too big. */ + } header->blockSizeInPCMFrames += 1; } else { DRFLAC_ASSERT(blockSize >= 8); @@ -5257,6 +5348,11 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u header->bitsPerSample = streaminfoBitsPerSample; } + if (header->bitsPerSample != streaminfoBitsPerSample) { + /* If this subframe has a different bitsPerSample then streaminfo or the first frame, reject it */ + return DRFLAC_FALSE; + } + if (!drflac__read_uint8(bs, 8, &header->crc8)) { return DRFLAC_FALSE; } @@ -5343,6 +5439,11 @@ static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, subframeBitsPerSample += 1; } + if (subframeBitsPerSample > 32) { + /* libFLAC and ffmpeg reject 33-bit subframes as well */ + return DRFLAC_FALSE; + } + /* Need to handle wasted bits per sample. */ if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { return DRFLAC_FALSE; @@ -6013,6 +6114,11 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac return DRFLAC_FALSE; } + /* Do not use the seektable if pcmFramIndex is not coverd by it. */ + if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { + return DRFLAC_FALSE; + } + for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { break; @@ -6360,7 +6466,7 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac } -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* pSeektableSize, drflac_allocation_callbacks* pAllocationCallbacks) +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) { /* 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 @@ -6420,32 +6526,37 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d seektableSize = blockSize; if (onMeta) { + drflac_uint32 seekpointCount; drflac_uint32 iSeekpoint; void* pRawData; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + + pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint); - metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; - - /* Endian swap. */ - for (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { + /* We need to read seekpoint by seekpoint and do some processing. */ + for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + + if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + /* Endian swap. */ pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = seekpointCount; + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; + onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); @@ -6480,7 +6591,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; /* Need space for the rest of the block */ if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ @@ -6488,7 +6599,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d return DRFLAC_FALSE; } metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; /* Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment */ if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { /* <-- Note the order of operations to avoid overflow to a valid value */ @@ -6506,7 +6617,7 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d return DRFLAC_FALSE; } - commentLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; @@ -6530,9 +6641,15 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d void* pRawData; const char* pRunningData; const char* pRunningDataEnd; + size_t bufferSize; drflac_uint8 iTrack; drflac_uint8 iIndex; + void* pTrackData; + /* + This needs to be loaded in two passes. The first pass is used to calculate the size of the memory allocation + we need for storing the necessary data. The second pass will fill that buffer with usable data. + */ pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; @@ -6553,38 +6670,91 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = pRunningData; + metadata.data.cuesheet.pTrackData = NULL; /* Will be filled later. */ - /* Check that the cuesheet tracks are valid before passing it to the callback */ - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - drflac_uint8 indexCount; - drflac_uint32 indexPointSize; + /* Pass 1: Calculate the size of the buffer for the track data. */ + { + const char* pRunningDataSaved = pRunningData; /* Will be restored at the end in preparation for the second pass. */ - if (pRunningDataEnd - pRunningData < 36) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + drflac_uint32 indexPointSize; + + if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + /* Skip to the index point count */ + pRunningData += 35; + + indexCount = pRunningData[0]; + pRunningData += 1; + + bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); + + /* Quick validation check. */ + indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + pRunningData += indexPointSize; } - /* Skip to the index point count */ - pRunningData += 35; - indexCount = pRunningData[0]; pRunningData += 1; - indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index); - if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; - } - - /* Endian swap. */ - for (iIndex = 0; iIndex < indexCount; ++iIndex) { - drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData; - pRunningData += sizeof(drflac_cuesheet_track_index); - pTrack->offset = drflac__be2host_64(pTrack->offset); - } + pRunningData = pRunningDataSaved; } + /* Pass 2: Allocate a buffer and fill the data. Validation was done in the step above so can be skipped. */ + { + char* pRunningTrackData; + + pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + if (pTrackData == NULL) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + pRunningTrackData = (char*)pTrackData; + + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; /* Skip forward, but not beyond the last byte in the CUESHEET_TRACK block which is the index count. */ + pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + + /* Grab the index count for the next part. */ + indexCount = pRunningData[0]; + pRunningData += 1; + pRunningTrackData += 1; + + /* Extract each track index. */ + for (iIndex = 0; iIndex < indexCount; ++iIndex) { + drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; + + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(drflac_cuesheet_track_index); + + pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); + } + } + + metadata.data.cuesheet.pTrackData = pTrackData; + } + + /* The original data is no longer needed. */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + pRawData = NULL; + onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); + pTrackData = NULL; } } break; @@ -6615,28 +6785,28 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; /* Need space for the rest of the block */ if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; /* Need space for the rest of the block */ if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.height = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; + metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; /* Need space for the picture after the block */ @@ -6714,9 +6884,9 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d } } - *pSeektablePos = seektablePos; - *pSeektableSize = seektableSize; - *pFirstFramePos = runningFilePos; + *pSeektablePos = seektablePos; + *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + *pFirstFramePos = runningFilePos; return DRFLAC_TRUE; } @@ -7746,11 +7916,11 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac drflac_uint32 wholeSIMDVectorCountPerChannel; drflac_uint32 decodedSamplesAllocationSize; #ifndef DR_FLAC_NO_OGG - drflac_oggbs oggbs; + drflac_oggbs* pOggbs = NULL; #endif drflac_uint64 firstFramePos; drflac_uint64 seektablePos; - drflac_uint32 seektableSize; + drflac_uint32 seekpointCount; drflac_allocation_callbacks allocationCallbacks; drflac* pFlac; @@ -7804,18 +7974,21 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac /* There's additional data required for Ogg streams. */ if (init.container == drflac_container_ogg) { allocationSize += sizeof(drflac_oggbs); - } - DRFLAC_ZERO_MEMORY(&oggbs, sizeof(oggbs)); - if (init.container == drflac_container_ogg) { - oggbs.onRead = onRead; - oggbs.onSeek = onSeek; - oggbs.pUserData = pUserData; - oggbs.currentBytePos = init.oggFirstBytePos; - oggbs.firstBytePos = init.oggFirstBytePos; - oggbs.serialNumber = init.oggSerial; - oggbs.bosPageHeader = init.oggBosHeader; - oggbs.bytesRemainingInPage = 0; + pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + if (pOggbs == NULL) { + return NULL; /*DRFLAC_OUT_OF_MEMORY;*/ + } + + DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + pOggbs->onRead = onRead; + pOggbs->onSeek = onSeek; + pOggbs->pUserData = pUserData; + pOggbs->currentBytePos = init.oggFirstBytePos; + pOggbs->firstBytePos = init.oggFirstBytePos; + pOggbs->serialNumber = init.oggSerial; + pOggbs->bosPageHeader = init.oggBosHeader; + pOggbs->bytesRemainingInPage = 0; } #endif @@ -7824,9 +7997,9 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading and decoding the metadata. */ - firstFramePos = 42; /* <-- We know we are at byte 42 at this point. */ - seektablePos = 0; - seektableSize = 0; + firstFramePos = 42; /* <-- We know we are at byte 42 at this point. */ + seektablePos = 0; + seekpointCount = 0; if (init.hasMetadataBlocks) { drflac_read_proc onReadOverride = onRead; drflac_seek_proc onSeekOverride = onSeek; @@ -7836,20 +8009,26 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac if (init.container == drflac_container_ogg) { onReadOverride = drflac__on_read_ogg; onSeekOverride = drflac__on_seek_ogg; - pUserDataOverride = (void*)&oggbs; + pUserDataOverride = (void*)pOggbs; } #endif - if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize, &allocationCallbacks)) { + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif return NULL; } - allocationSize += seektableSize; + allocationSize += seekpointCount * sizeof(drflac_seekpoint); } pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); if (pFlac == NULL) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif return NULL; } @@ -7859,8 +8038,12 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { - drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize); - *pInternalOggbs = oggbs; + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); + DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + + /* At this point the pOggbs object has been handed over to pInternalOggbs and can be freed. */ + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + pOggbs = NULL; /* The Ogg bistream needs to be layered on top of the original bitstream. */ pFlac->bs.onRead = drflac__on_read_ogg; @@ -7884,7 +8067,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac { /* If we have a seektable we need to load it now, making sure we move back to where we were previously. */ if (seektablePos != 0) { - pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints); + pFlac->seekpointCount = seekpointCount; pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); @@ -7892,18 +8075,20 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac /* 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.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) { - /* Endian swap. */ - drflac_uint32 iSeekpoint; - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + drflac_uint32 iSeekpoint; + + for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + /* Endian swap. */ pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + } else { + /* Failed to read the seektable. Pretend we don't have one. */ + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + break; } - } else { - /* Failed to read the seektable. Pretend we don't have one. */ - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; } /* We need to seek back to where we were. If this fails it's a critical error. */ @@ -7952,7 +8137,9 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac #ifndef DR_FLAC_NO_STDIO #include +#ifndef DR_FLAC_NO_WCHAR #include /* For wcslen(), wcsrtombs() */ +#endif /* drflac_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ #include @@ -8418,6 +8605,7 @@ fallback, so if you notice your compiler not detecting this properly I'm happy t #endif #endif +#ifndef DR_FLAC_NO_WCHAR static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { @@ -8446,10 +8634,23 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons } #else /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because + fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note + that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler + error I'll look into improving compatibility. */ + + /* + Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just + need to abort with an error. If you encounter a compiler lacking such support, add it to this list + and submit a bug report and it'll be added to the library upstream. + */ + #if defined(__DJGPP__) + { + /* Nothing to do here. This will fall through to the error check below. */ + } + #else { mbstate_t mbs; size_t lenMB; @@ -8491,6 +8692,7 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRFLAC_ERROR; @@ -8499,6 +8701,7 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons return DRFLAC_SUCCESS; } +#endif static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { @@ -8531,6 +8734,7 @@ DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocati return pFlac; } +#ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -8548,6 +8752,7 @@ DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_all return pFlac; } +#endif DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { @@ -8567,6 +8772,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_ return pFlac; } +#ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; @@ -8584,6 +8790,7 @@ DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, dr return pFlac; } +#endif #endif /* DR_FLAC_NO_STDIO */ static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) @@ -11781,7 +11988,7 @@ DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator return NULL; } - length = drflac__le2host_32(*(const drflac_uint32*)pIter->pRunningData); + length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); pIter->pRunningData += 4; pComment = pIter->pRunningData; @@ -11851,6 +12058,37 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat /* REVISION HISTORY ================ +v0.12.39 - 2022-09-17 + - Fix compilation with DJGPP. + - Fix compilation error with Visual Studio 2019 and the ARM build. + - Fix an error with SSE 4.1 detection. + - Add support for disabling wchar_t with DR_WAV_NO_WCHAR. + - Improve compatibility with compilers which lack support for explicit struct packing. + - Improve compatibility with low-end and embedded hardware by reducing the amount of stack + allocation when loading an Ogg encapsulated file. + +v0.12.38 - 2022-04-10 + - Fix compilation error on older versions of GCC. + +v0.12.37 - 2022-02-12 + - Improve ARM detection. + +v0.12.36 - 2022-02-07 + - Fix a compilation error with the ARM build. + +v0.12.35 - 2022-02-06 + - Fix a bug due to underestimating the amount of precision required for the prediction stage. + - Fix some bugs found from fuzz testing. + +v0.12.34 - 2022-01-07 + - Fix some misalignment bugs when reading metadata. + +v0.12.33 - 2021-12-22 + - Fix a bug with seeking when the seek table does not start at PCM frame 0. + +v0.12.32 - 2021-12-11 + - Fix a warning with Clang. + v0.12.31 - 2021-08-16 - Silence some warnings. diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h index 7d752d072..59876c877 100644 --- a/src/external/dr_mp3.h +++ b/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.31 - 2021-08-22 +dr_mp3 - v0.6.34 - 2022-09-17 David Reid - mackron@gmail.com @@ -95,7 +95,7 @@ extern "C" { #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 31 +#define DRMP3_VERSION_REVISION 34 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include /* For size_t. */ @@ -107,7 +107,7 @@ typedef signed short drmp3_int16; typedef unsigned short drmp3_uint16; typedef signed int drmp3_int32; typedef unsigned int drmp3_uint32; -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 drmp3_int64; typedef unsigned __int64 drmp3_uint64; #else @@ -235,9 +235,15 @@ typedef drmp3_int32 drmp3_result; I am using "__inline__" only when we're compiling in strict ANSI mode. */ #if defined(__STRICT_ANSI__) - #define DRMP3_INLINE __inline__ __attribute__((always_inline)) + #define DRMP3_GNUC_INLINE_HINT __inline__ #else - #define DRMP3_INLINE inline __attribute__((always_inline)) + #define DRMP3_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRMP3_INLINE __inline @@ -340,7 +346,6 @@ typedef struct typedef struct { drmp3dec decoder; - drmp3dec_frame_info frameInfo; drmp3_uint32 channels; drmp3_uint32 sampleRate; drmp3_read_proc onRead; @@ -595,7 +600,7 @@ DRMP3_API const char* drmp3_version_string(void) #define DR_MP3_ONLY_SIMD #endif -#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) +#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) #if defined(_MSC_VER) #include #endif @@ -1296,7 +1301,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n)) +#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) #define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } #define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } #define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) @@ -1864,7 +1869,7 @@ static void drmp3d_DCT_II(float *grbuf, int n) #if DRMP3_HAVE_SSE #define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) #else -#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v)) +#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) #endif for (i = 0; i < 7; i++, y += 4*18) { @@ -1880,7 +1885,7 @@ static void drmp3d_DCT_II(float *grbuf, int n) DRMP3_VSAVE2(3, t[3][7]); } else { -#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v) +#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) for (i = 0; i < 7; i++, y += 4*18) { drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); @@ -2103,7 +2108,11 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); #endif #else + #if DRMP3_HAVE_SSE static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #else + const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + #endif a = DRMP3_VMUL(a, g_scale); b = DRMP3_VMUL(b, g_scale); #if DRMP3_HAVE_SSE @@ -2406,8 +2415,6 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num Main Public API ************************************************************************************************************************************************************/ -#include /* For sin() and exp(). */ - #if defined(SIZE_MAX) #define DRMP3_SIZE_MAX SIZE_MAX #else @@ -2427,7 +2434,7 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num /* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends at least 16K, but in an attempt to reduce data movement I'm making this slightly larger. */ #ifndef DRMP3_DATA_CHUNK_SIZE -#define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4 +#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) #endif @@ -2472,24 +2479,6 @@ static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) } -static DRMP3_INLINE double drmp3_sin(double x) -{ - /* TODO: Implement custom sin(x). */ - return sin(x); -} - -static DRMP3_INLINE double drmp3_exp(double x) -{ - /* TODO: Implement custom exp(x). */ - return exp(x); -} - -static DRMP3_INLINE double drmp3_cos(double x) -{ - return drmp3_sin((DRMP3_PI_D*0.5) - x); -} - - static void* drmp3__malloc_default(size_t sz, void* pUserData) { (void)pUserData; @@ -3434,10 +3423,23 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const } #else /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because + fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note + that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler + error I'll look into improving compatibility. */ + + /* + Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just + need to abort with an error. If you encounter a compiler lacking such support, add it to this list + and submit a bug report and it'll be added to the library upstream. + */ + #if defined(__DJGPP__) + { + /* Nothing to do here. This will fall through to the error check below. */ + } + #else { mbstate_t mbs; size_t lenMB; @@ -3479,6 +3481,7 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRMP3_ERROR; @@ -4473,6 +4476,19 @@ counts rather than sample counts. /* REVISION HISTORY ================ +v0.6.34 - 2022-09-17 + - Fix compilation with DJGPP. + - Fix compilation when compiling with x86 with no SSE2. + - Remove an unnecessary variable from the drmp3 structure. + +v0.6.33 - 2022-04-10 + - Fix compilation error with the MSVC ARM64 build. + - Fix compilation error on older versions of GCC. + - Remove some unused functions. + +v0.6.32 - 2021-12-11 + - Fix a warning with Clang. + v0.6.31 - 2021-08-22 - Fix a bug when loading from memory. diff --git a/src/external/dr_wav.h b/src/external/dr_wav.h index a119d66af..2f885a04c 100644 --- a/src/external/dr_wav.h +++ b/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.4 - 2021-12-08 +dr_wav - v0.13.7 - 2022-09-17 David Reid - mackron@gmail.com @@ -92,6 +92,9 @@ Build Options #define DR_WAV_NO_STDIO Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc. +#define DR_WAV_NO_WCHAR + Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_WAV_NO_STDIO is also defined. + Notes @@ -125,7 +128,7 @@ extern "C" { #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 4 +#define DRWAV_VERSION_REVISION 7 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include /* For size_t. */ @@ -1297,14 +1300,21 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); #ifndef dr_wav_c #define dr_wav_c +#ifdef __MRC__ +/* MrC currently doesn't compile dr_wav correctly with any optimizations enabled. */ +#pragma options opt off +#endif + #include -#include /* For memcpy(), memset() */ +#include #include /* For INT_MAX */ #ifndef DR_WAV_NO_STDIO #include +#ifndef DR_WAV_NO_WCHAR #include #endif +#endif /* Standard library stuff. */ #ifndef DRWAV_ASSERT @@ -1359,9 +1369,15 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); I am using "__inline__" only when we're compiling in strict ANSI mode. */ #if defined(__STRICT_ANSI__) - #define DRWAV_INLINE __inline__ __attribute__((always_inline)) + #define DRWAV_GNUC_INLINE_HINT __inline__ #else - #define DRWAV_INLINE inline __attribute__((always_inline)) + #define DRWAV_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRWAV_INLINE __inline @@ -1966,7 +1982,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_pr fmtOut->extendedSize = 0; fmtOut->validBitsPerSample = 0; fmtOut->channelMask = 0; - memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat)); + DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat)); if (header.sizeInBytes > 16) { drwav_uint8 fmt_cbSize[2]; @@ -2137,7 +2153,7 @@ DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metad DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) { if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { - free(pParser->pData); + pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); pParser->pDataCursor = pParser->pData; @@ -2316,6 +2332,17 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_pars return bytesRead; } +DRWAV_PRIVATE size_t drwav__strlen(const char* str) +{ + size_t result = 0; + + while (*str++) { + result += 1; + } + + return result; +} + DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) { size_t result = 0; @@ -2335,7 +2362,7 @@ DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); DRWAV_ASSERT(result != NULL); - memcpy(result, str, len); + DRWAV_COPY_MEMORY(result, str, len); result[len] = '\0'; return result; @@ -2516,7 +2543,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_pars DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (drwav_uint32)strlen(pMetadata->data.bext.pCodingHistory); + pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory); } else { pMetadata->data.bext.pCodingHistory = NULL; pMetadata->data.bext.codingHistorySize = 0; @@ -2780,21 +2807,21 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { return bytesRead; } - allocSizeNeeded += strlen(buffer) + 1; + allocSizeNeeded += drwav__strlen(buffer) + 1; buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { return bytesRead; } - allocSizeNeeded += strlen(buffer) + 1; + allocSizeNeeded += drwav__strlen(buffer) + 1; buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { return bytesRead; } - allocSizeNeeded += strlen(buffer) + 1; + allocSizeNeeded += drwav__strlen(buffer) + 1; allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; /* Coding history. */ drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); @@ -3157,7 +3184,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0); } - memset(&metadataParser, 0, sizeof(metadataParser)); + DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); /* Not tested on W64. */ if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { @@ -3746,7 +3773,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - memset(reservedBuf, 0, sizeof(reservedBuf)); + DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); if (pMetadata->data.bext.codingHistorySize > 0) { @@ -4704,6 +4731,7 @@ fallback, so if you notice your compiler not detecting this properly I'm happy t #endif #endif +#ifndef DR_WAV_NO_WCHAR DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { @@ -4731,11 +4759,24 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, (void)pAllocationCallbacks; } #else - /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because + fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note + that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler + error I'll look into improving compatibility. */ + + /* + Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just + need to abort with an error. If you encounter a compiler lacking such support, add it to this list + and submit a bug report and it'll be added to the library upstream. + */ + #if defined(__DJGPP__) + { + /* Nothing to do here. This will fall through to the error check below. */ + } + #else { mbstate_t mbs; size_t lenMB; @@ -4777,6 +4818,7 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } + #endif if (*ppFile == NULL) { return DRWAV_ERROR; @@ -4785,6 +4827,7 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, return DRWAV_SUCCESS; } +#endif DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) @@ -4840,6 +4883,7 @@ DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drw return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); @@ -4855,6 +4899,7 @@ DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename /* This takes ownership of the FILE* object. */ return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } +#endif DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { @@ -4867,6 +4912,7 @@ DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* fi return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; @@ -4877,6 +4923,7 @@ DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_ /* This takes ownership of the FILE* object. */ return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } +#endif DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) @@ -4909,6 +4956,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const ch return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; @@ -4919,6 +4967,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const /* This takes ownership of the FILE* object. */ return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } +#endif DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { @@ -4939,6 +4988,7 @@ DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); @@ -4957,6 +5007,7 @@ DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } +#endif #endif /* DR_WAV_NO_STDIO */ @@ -5441,8 +5492,8 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF } /* Make sure the sample is clamped. */ - if (targetFrameIndex >= pWav->totalPCMFrameCount) { - targetFrameIndex = pWav->totalPCMFrameCount - 1; + if (targetFrameIndex > pWav->totalPCMFrameCount) { + targetFrameIndex = pWav->totalPCMFrameCount; } /* @@ -7650,6 +7701,7 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filen } +#ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; @@ -7712,7 +7764,8 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -#endif +#endif /* DR_WAV_NO_WCHAR */ +#endif /* DR_WAV_NO_STDIO */ DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { @@ -7853,12 +7906,28 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) a[3] == b[3]; } +#ifdef __MRC__ +/* Undo the pragma at the beginning of this file. */ +#pragma options opt reset +#endif + #endif /* dr_wav_c */ #endif /* DR_WAV_IMPLEMENTATION */ /* REVISION HISTORY ================ +v0.13.7 - 2022-09-17 + - Fix compilation with DJGPP. + - Add support for disabling wchar_t with DR_WAV_NO_WCHAR. + +v0.13.6 - 2022-04-10 + - Fix compilation error on older versions of GCC. + - Remove some dependencies on the standard library. + +v0.13.5 - 2022-01-26 + - Fix an error when seeking to the end of the file. + v0.13.4 - 2021-12-08 - Fix some static analysis warnings. diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index d454c4978..ad3651503 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.11 - TBD +miniaudio - v0.11.11 - 2022-11-04 David Reid - mackron@gmail.com @@ -1273,6 +1273,14 @@ When streaming sounds, 2 seconds worth of audio data is stored in memory. Althou fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music tracks in games. +When loading a sound from a file path, the engine will reference count the file to prevent it from +being loaded if it's already in memory. When you uninitialize a sound, the reference count will be +decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting +system is not used for streams. The engine will use a 64-bit hash of the file name when comparing +file paths which means there's a small chance you might encounter a name collision. If this is an +issue, you'll need to use a different name for one of the colliding file paths, or just not load +from files and instead load from a data source. + When you initialize a sound, if you specify a sound group the sound will be attached to that group automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. If you would instead rather leave the sound unattached by default, you can can specify the @@ -1870,9 +1878,11 @@ A binary search tree (BST) is used for storing data buffers as it has good balan efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST -due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If -this is an issue, you should normalize your file names to upper- or lower-case before initializing -your data sources. +due to the random nature of the hash. The disadvantages are that file names are case-sensitive and +there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize +your file names to upper- or lower-case before initializing your data sources. If name collisions +become an issue, you'll need to change the name of one of the colliding names or just not use the +resource manager. When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is excluded, the file will be decoded synchronously by the calling thread. There are two @@ -22223,50 +22233,100 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui } else { /* We don't have any cached data pointer, so grab another one. */ HRESULT hr; - DWORD flags; + DWORD flags = 0; /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); if (hr == S_OK) { /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + + /* + There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every + call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially + work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution + would be to figure out why the flag is always getting reported. + */ + #if defined(MA_DEBUG_OUTPUT) + { + if (flags != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); + + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); + } + } + } + #endif /* Overrun detection. */ if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { /* Glitched. Probably due to an overrun. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); /* - If we got an overrun it probably means we're straddling the end of the buffer. In order to prevent - a never-ending sequence of glitches we're going to recover by completely clearing out the capture - buffer. + If we got an overrun it probably means we're straddling the end of the buffer. In normal capture + mode this is the fault of the client application because they're responsible for ensuring data is + processed fast enough. In duplex mode, however, the processing of audio is tied to the playback + device, so this can possibly be the result of a timing de-sync. + + In capture mode we're not going to do any kind of recovery because the real fix is for the client + application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers + to prevent a never-ending sequence of glitches due to straddling the end of the buffer. */ - { - ma_uint32 iterationCount = 4; /* Safety to prevent an infinite loop. */ + if (pDevice->type == ma_device_type_duplex) { + /* + Experiment: + + If we empty out the *entire* buffer we may end up putting ourselves into an underrun position + which isn't really any better than the overrun we're probably in right now. Instead we'll just + empty out about half. + */ ma_uint32 i; + ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); + ma_uint32 iterationCount = periodCount / 2; + if ((periodCount % 2) > 0) { + iterationCount += 1; + } for (i = 0; i < iterationCount; i += 1) { hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %d.\n", hr); break; } + flags = 0; hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { + /* + The buffer has been completely emptied or an error occurred. In this case we'll need + to reset the state of the mapped buffer which will trigger the next iteration to get + a fresh buffer from WASAPI. + */ + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); + } + } + + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %d.\n", hr); + } + break; } } - } - /* We should not have a valid buffer at this point so make sure everything is empty. */ - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } else { - /* The data is clean. */ - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - - if (flags != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); + /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + } } } @@ -22279,7 +22339,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui microphone isn't delivering data for whatever reason. In this case we'll just abort the read and return whatever we were able to get. The other situations is loopback mode, in which case a timeout probably just means the nothing is playing - through the speakers. + through the speakers. */ /* Experiment: Use a shorter timeout for loopback mode. */ diff --git a/src/external/stb_image.h b/src/external/stb_image.h index d60371b95..5e807a0a6 100644 --- a/src/external/stb_image.h +++ b/src/external/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,7 @@ LICENSE RECENT REVISION HISTORY: + 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 2.25 (2020-02-02) fix warnings @@ -108,7 +109,7 @@ RECENT REVISION HISTORY: Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 Brad Weinberger Matvey Cherevko github:mosra @@ -140,7 +141,7 @@ RECENT REVISION HISTORY: // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) +// stbi_image_free(data); // // Standard parameters: // int *x -- outputs image width in pixels @@ -635,7 +636,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #endif #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(__SYMBIAN32__) typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; @@ -1063,6 +1064,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) } #endif +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + 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) +{ + 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 + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -1985,9 +2003,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) int i,j,k=0; unsigned int code; // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } h->size[k] = 0; // compute actual symbols (from jpeg spec) @@ -2112,6 +2133,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol @@ -2130,6 +2153,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) unsigned int k; int sgn; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); @@ -2144,6 +2168,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; @@ -2155,6 +2180,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; @@ -2192,8 +2218,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec @@ -2207,6 +2235,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location @@ -2246,8 +2275,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * (1 << j->succ_low)); } else { // refinement scan for DC coefficient @@ -2282,6 +2313,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; @@ -3102,6 +3134,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) sizes[i] = stbi__get8(z->s); n += sizes[i]; } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; @@ -3351,6 +3384,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) return 1; } +static int 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 + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { @@ -3367,25 +3422,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } + j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); } else { - if (!stbi__process_marker(j, m)) return 0; + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); } - m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); @@ -3976,6 +4028,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); @@ -3989,6 +4042,7 @@ static int stbi__jpeg_test(stbi__context *s) int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -4014,6 +4068,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); @@ -4256,11 +4311,12 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) a->zout = zout; 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 z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data 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"); @@ -4955,7 +5011,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; -STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; stbi__unpremultiply_on_load_set = 1; @@ -5064,14 +5120,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS } + // even with SCAN_header, have to scan to see if we have a tRNS break; } @@ -5103,6 +5158,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; + // 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 } else { @@ -5115,7 +5172,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = idata_limit; @@ -5498,8 +5561,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { - if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); } } @@ -7187,12 +7264,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re // Run value = stbi__get8(s); count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -7446,10 +7523,17 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; @@ -7486,6 +7570,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); } return value; @@ -7516,9 +7602,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value diff --git a/src/external/stb_vorbis.h b/src/external/stb_vorbis.c similarity index 68% rename from src/external/stb_vorbis.h rename to src/external/stb_vorbis.c index 2fcbc4afc..3e5c2504c 100644 --- a/src/external/stb_vorbis.h +++ b/src/external/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.14 - public domain +// Ogg Vorbis audio decoder - v1.22 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -26,18 +26,29 @@ // Terje Mathisen Niklas Frykholm Andy Hill // Casey Muratori John Bolton Gargaj // Laurent Gomila Marc LeBlanc Ronny Chevalier -// Bernhard Wodo Evan Balster alxprd@github +// Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// manxorist@github saga musix github:infatum -// Timur Gagiev BareRose +// github:manxorist Saga Musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier // // Partial history: +// 1.22 - 2021-07-11 - various small fixes +// 1.21 - 2021-07-02 - fix bug for files with no comments +// 1.20 - 2020-07-11 - several small fixes +// 1.19 - 2020-02-05 - warnings +// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. +// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) +// 1.16 - 2019-03-04 - fix warnings +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files -// 1.11 - 2017-07-23 - fix MinGW compilation -// 1.10 - 2017-03-03 - more robust seeking; fix negative stbv_ilog(); clear error in open_memory +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory // 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version // 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame // 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const @@ -64,28 +75,17 @@ #define STB_VORBIS_INCLUDE_STB_VORBIS_H #if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) -#define STB_VORBIS_NO_STDIO +#define STB_VORBIS_NO_STDIO 1 #endif #ifndef STB_VORBIS_NO_STDIO #include #endif -// NOTE: Added to work with raylib on Android -#if defined(PLATFORM_ANDROID) - #include "utils.h" // Android fopen function map -#endif - #ifdef __cplusplus extern "C" { #endif -#ifdef STB_VORBIS_STATIC -#define STBVDEF static -#else -#define STBVDEF extern -#endif - /////////// THREAD SAFETY // Individual stb_vorbis* handles are not thread-safe; you cannot decode from @@ -101,8 +101,8 @@ extern "C" { // data in the file and how you set the compile flags for speed // vs. size. In my test files the maximal-size usage is ~150KB.) // -// You can modify the wrapper functions in the source (stbv_setup_malloc, -// stbv_setup_temp_malloc, temp_malloc) to change this behavior, or you +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you // can use a simpler allocation model: you pass in a buffer from // which stb_vorbis will allocate _all_ its memory (including the // temp memory). "open" may fail with a VORBIS_outofmem if you @@ -138,25 +138,36 @@ typedef struct int max_frame_size; } stb_vorbis_info; +typedef struct +{ + char *vendor; + + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + // get general information about the file -STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get ogg comments +extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); // get the last error detected (clears it, too) -STBVDEF int stb_vorbis_get_error(stb_vorbis *f); +extern int stb_vorbis_get_error(stb_vorbis *f); // close an ogg vorbis file and free all memory in use -STBVDEF void stb_vorbis_close(stb_vorbis *f); +extern void stb_vorbis_close(stb_vorbis *f); // this function returns the offset (in samples) from the beginning of the // file that will be returned by the next decode, if it is known, or -1 // otherwise. after a flush_pushdata() call, this may take a while before // it becomes valid again. // NOT WORKING YET after a seek with PULLDATA API -STBVDEF int stb_vorbis_get_sample_offset(stb_vorbis *f); +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); // returns the current seek point within the file, or offset from the beginning // of the memory buffer. In pushdata mode it returns 0. -STBVDEF unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); /////////// PUSHDATA API @@ -169,7 +180,7 @@ STBVDEF unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); // need to give it the same data again PLUS more. Note that the Vorbis // specification does not bound the size of an individual frame. -STBVDEF stb_vorbis *stb_vorbis_open_pushdata( +extern stb_vorbis *stb_vorbis_open_pushdata( const unsigned char * datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, @@ -183,7 +194,7 @@ STBVDEF stb_vorbis *stb_vorbis_open_pushdata( // if returns NULL and *error is VORBIS_need_more_data, then the input block was // incomplete and you need to pass in a larger block from the start of the file -STBVDEF int stb_vorbis_decode_frame_pushdata( +extern int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, // place to write number of float * buffers @@ -212,8 +223,14 @@ STBVDEF int stb_vorbis_decode_frame_pushdata( // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. -STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f); +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with // previous ones (e.g. you've seeked in the data); future attempts to decode // frames will cause stb_vorbis to resynchronize (as noted above), and @@ -238,38 +255,38 @@ STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f); // just want to go ahead and use pushdata.) #if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -STBVDEF int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); #endif -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -STBVDEF int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); #endif // decode an entire file and output the data interleaved into a malloc()ed // buffer stored in *output. The return value is the number of samples // decoded, or -1 if the file could not be opened or was not an ogg vorbis file. // When you're done with it, just free() the pointer returned in *output. -STBVDEF stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an ogg vorbis stream in memory (note // this must be the entire stream!). on failure, returns NULL and sets *error #ifndef STB_VORBIS_NO_STDIO -STBVDEF stb_vorbis * stb_vorbis_open_filename(const char *filename, +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from a filename via fopen(). on failure, // returns NULL and sets *error (possibly to VORBIS_file_open_failure). -STBVDEF stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell). on failure, returns NULL and sets *error. // note that stb_vorbis must "own" this stream; if you seek it in between -// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to // perform stb_vorbis_seek_*() operations on this file, it will assume it // owns the _entire_ rest of the file after the start point. Use the next // function, stb_vorbis_open_file_section(), to limit it. -STBVDEF stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell); the stream will be of length 'len' bytes. @@ -278,8 +295,8 @@ STBVDEF stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_c // confused. #endif -STBVDEF int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); -STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); // these functions seek in the Vorbis file to (approximately) 'sample_number'. // after calling seek_frame(), the next call to get_frame_*() will include // the specified sample. after calling stb_vorbis_seek(), the next call to @@ -287,14 +304,14 @@ STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); // do not need to seek to EXACTLY the target sample when using get_samples_*, // you can also use seek_frame(). -STBVDEF int stb_vorbis_seek_start(stb_vorbis *f); +extern int stb_vorbis_seek_start(stb_vorbis *f); // this function is equivalent to stb_vorbis_seek(f,0) -STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); -STBVDEF float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); // these functions return the total length of the vorbis stream -STBVDEF int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); // decode the next frame and return the number of samples. the number of // channels returned are stored in *channels (which can be NULL--it is always // the same as the number of channels reported by get_info). *output will @@ -305,8 +322,8 @@ STBVDEF int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***ou // and stb_vorbis_get_samples_*(), since the latter calls the former. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION -STBVDEF int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); -STBVDEF int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); #endif // decode the next frame and return the number of *samples* per channel. // Note that for interleaved data, you pass in the number of shorts (the @@ -333,16 +350,16 @@ STBVDEF int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, sho // Note that this is not _good_ surround etc. mixing at all! It's just so // you get something useful. -STBVDEF int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); -STBVDEF int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. // Returns the number of samples stored per channel; it may be less than requested // at the end of the file. If there are no more samples in the file, returns 0. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION -STBVDEF int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); -STBVDEF int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); #endif // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. Applies the coercion rules above @@ -385,7 +402,8 @@ enum STBVorbisError VORBIS_invalid_first_page, VORBIS_bad_packet_type, VORBIS_cant_find_last_page, - VORBIS_seek_failed + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported }; @@ -399,7 +417,7 @@ enum STBVorbisError // ////////////////////////////////////////////////////////////////////////////// -#ifdef STB_VORBIS_IMPLEMENTATION +#ifndef STB_VORBIS_HEADER_ONLY // global configuration settings (e.g. set these in the project/makefile), // or just set them in this file at the top (although ideally the first few @@ -428,7 +446,7 @@ enum STBVorbisError // STB_VORBIS_NO_FAST_SCALED_FLOAT // does not use a fast float-to-int trick to accelerate float-to-int on // most platforms which requires endianness be defined correctly. -// #define STB_VORBIS_NO_FAST_SCALED_FLOAT +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT // STB_VORBIS_MAX_CHANNELS [number] @@ -570,7 +588,7 @@ enum STBVorbisError #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif - #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__APPLE__) || defined(__CYGWIN__) + #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include #endif #else // STB_VORBIS_NO_CRT @@ -593,7 +611,7 @@ enum STBVorbisError #endif #define __forceinline #ifndef alloca - #define alloca(s) __builtin_alloca(s) + #define alloca __builtin_alloca #endif #elif !defined(_MSC_VER) #if __GNUC__ @@ -614,28 +632,34 @@ enum STBVorbisError #if 0 #include -#define STBV_CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) #else -#define STBV_CHECK(f) ((void) 0) +#define CHECK(f) ((void) 0) #endif -#define STBV_MAX_BLOCKSIZE_LOG 13 // from specification -#define STBV_MAX_BLOCKSIZE (1 << STBV_MAX_BLOCKSIZE_LOG) +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) -typedef unsigned char stbv_uint8; -typedef signed char stbv_int8; -typedef unsigned short stbv_uint16; -typedef signed short stbv_int16; -typedef unsigned int stbv_uint32; -typedef signed int stbv_int32; +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif -typedef float stbv_codetype; +typedef float codetype; + +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif // @NOTE // @@ -650,113 +674,113 @@ typedef float stbv_codetype; // the sizes larger--nothing relies on silently truncating etc., nor the // order of variables. -#define STBV_FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) -#define STBV_FAST_HUFFMAN_TABLE_MASK (STBV_FAST_HUFFMAN_TABLE_SIZE - 1) +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) typedef struct { int dimensions, entries; - stbv_uint8 *codeword_lengths; + uint8 *codeword_lengths; float minimum_value; float delta_value; - stbv_uint8 value_bits; - stbv_uint8 lookup_type; - stbv_uint8 sequence_p; - stbv_uint8 sparse; - stbv_uint32 lookup_values; - stbv_codetype *multiplicands; - stbv_uint32 *codewords; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT - stbv_int16 fast_huffman[STBV_FAST_HUFFMAN_TABLE_SIZE]; + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #else - stbv_int32 fast_huffman[STBV_FAST_HUFFMAN_TABLE_SIZE]; + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #endif - stbv_uint32 *sorted_codewords; + uint32 *sorted_codewords; int *sorted_values; int sorted_entries; -} StbvCodebook; +} Codebook; typedef struct { - stbv_uint8 order; - stbv_uint16 rate; - stbv_uint16 bark_map_size; - stbv_uint8 amplitude_bits; - stbv_uint8 amplitude_offset; - stbv_uint8 number_of_books; - stbv_uint8 book_list[16]; // varies -} StbvFloor0; + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; typedef struct { - stbv_uint8 partitions; - stbv_uint8 partition_class_list[32]; // varies - stbv_uint8 class_dimensions[16]; // varies - stbv_uint8 class_subclasses[16]; // varies - stbv_uint8 class_masterbooks[16]; // varies - stbv_int16 subclass_books[16][8]; // varies - stbv_uint16 Xlist[31*8+2]; // varies - stbv_uint8 sorted_order[31*8+2]; - stbv_uint8 stbv_neighbors[31*8+2][2]; - stbv_uint8 floor1_multiplier; - stbv_uint8 rangebits; + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; int values; -} StbvFloor1; +} Floor1; typedef union { - StbvFloor0 floor0; - StbvFloor1 floor1; -} StbvFloor; + Floor0 floor0; + Floor1 floor1; +} Floor; typedef struct { - stbv_uint32 begin, end; - stbv_uint32 part_size; - stbv_uint8 classifications; - stbv_uint8 classbook; - stbv_uint8 **classdata; - stbv_int16 (*residue_books)[8]; -} StbvResidue; + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; typedef struct { - stbv_uint8 magnitude; - stbv_uint8 angle; - stbv_uint8 mux; -} StbvMappingChannel; + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; typedef struct { - stbv_uint16 coupling_steps; - StbvMappingChannel *chan; - stbv_uint8 submaps; - stbv_uint8 submap_floor[15]; // varies - stbv_uint8 submap_residue[15]; // varies -} StbvMapping; + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; typedef struct { - stbv_uint8 blockflag; - stbv_uint8 mapping; - stbv_uint16 windowtype; - stbv_uint16 transformtype; -} StbvMode; + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; typedef struct { - stbv_uint32 goal_crc; // expected crc if match + uint32 goal_crc; // expected crc if match int bytes_left; // bytes left in packet - stbv_uint32 crc_so_far; // running crc + uint32 crc_so_far; // running crc int bytes_done; // bytes processed in _current_ chunk - stbv_uint32 sample_loc; // granule pos encoded in page -} StbvCRCscan; + uint32 sample_loc; // granule pos encoded in page +} CRCscan; typedef struct { - stbv_uint32 page_start, page_end; - stbv_uint32 last_decoded_sample; -} StbvProbedPage; + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; struct stb_vorbis { @@ -768,24 +792,31 @@ struct stb_vorbis unsigned int temp_memory_required; unsigned int setup_temp_memory_required; + char *vendor; + int comment_list_length; + char **comment_list; + // input config #ifndef STB_VORBIS_NO_STDIO FILE *f; - stbv_uint32 f_start; + uint32 f_start; int close_on_free; #endif - stbv_uint8 *stream; - stbv_uint8 *stream_start; - stbv_uint8 *stream_end; + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; - stbv_uint32 stream_len; + uint32 stream_len; - stbv_uint8 push_mode; + uint8 push_mode; - stbv_uint32 first_audio_page_offset; + // the page to seek to when seeking to start, may be zero + uint32 first_audio_page_offset; - StbvProbedPage p_first, p_last; + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) + ProbedPage p_first, p_last; // memory management stb_vorbis_alloc alloc; @@ -802,19 +833,19 @@ struct stb_vorbis int blocksize[2]; int blocksize_0, blocksize_1; int codebook_count; - StbvCodebook *codebooks; + Codebook *codebooks; int floor_count; - stbv_uint16 floor_types[64]; // varies - StbvFloor *floor_config; + uint16 floor_types[64]; // varies + Floor *floor_config; int residue_count; - stbv_uint16 residue_types[64]; // varies - StbvResidue *residue_config; + uint16 residue_types[64]; // varies + Residue *residue_config; int mapping_count; - StbvMapping *mapping; + Mapping *mapping; int mode_count; - StbvMode mode_config[64]; // varies + Mode mode_config[64]; // varies - stbv_uint32 total_samples; + uint32 total_samples; // decode buffer float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; @@ -824,44 +855,44 @@ struct stb_vorbis int previous_length; #ifndef STB_VORBIS_NO_DEFER_FLOOR - stbv_int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; #else float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; #endif - stbv_uint32 current_loc; // sample location of next frame to decode + uint32 current_loc; // sample location of next frame to decode int current_loc_valid; // per-blocksize precomputed data - + // twiddle factors float *A[2],*B[2],*C[2]; float *window[2]; - stbv_uint16 *stbv_bit_reverse[2]; + uint16 *bit_reverse[2]; // current page/packet/segment streaming info - stbv_uint32 serial; // stream serial number for verification + uint32 serial; // stream serial number for verification int last_page; int segment_count; - stbv_uint8 segments[255]; - stbv_uint8 page_flag; - stbv_uint8 bytes_in_seg; - stbv_uint8 first_decode; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; int next_seg; int last_seg; // flag that we're on the last segment int last_seg_which; // what was the segment number of the last seg? - stbv_uint32 acc; + uint32 acc; int valid_bits; int packet_bytes; int end_seg_with_known_loc; - stbv_uint32 known_loc_for_packet; + uint32 known_loc_for_packet; int discard_samples_deferred; - stbv_uint32 samples_output; + uint32 samples_output; // push mode scanning int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching #ifndef STB_VORBIS_NO_PUSHDATA_API - StbvCRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; #endif // sample-access @@ -870,16 +901,16 @@ struct stb_vorbis }; #if defined(STB_VORBIS_NO_PUSHDATA_API) - #define STBV_IS_PUSH_MODE(f) FALSE + #define IS_PUSH_MODE(f) FALSE #elif defined(STB_VORBIS_NO_PULLDATA_API) - #define STBV_IS_PUSH_MODE(f) TRUE + #define IS_PUSH_MODE(f) TRUE #else - #define STBV_IS_PUSH_MODE(f) ((f)->push_mode) + #define IS_PUSH_MODE(f) ((f)->push_mode) #endif -typedef struct stb_vorbis stbv_vorb; +typedef struct stb_vorbis vorb; -static int stbv_error(stbv_vorb *f, enum STBVorbisError e) +static int error(vorb *f, enum STBVorbisError e) { f->error = e; if (!f->eof && e != VORBIS_need_more_data) { @@ -894,17 +925,17 @@ static int stbv_error(stbv_vorb *f, enum STBVorbisError e) // alloca(); otherwise, provide a temp buffer and it will // allocate out of those. -#define stbv_array_size_required(count,size) (count*(sizeof(void *)+(size))) +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) -#define stbv_temp_alloc(f,size) (f->alloc.alloc_buffer ? stbv_setup_temp_malloc(f,size) : alloca(size)) -#define stbv_temp_free(f,p) 0 -#define stbv_temp_alloc_save(f) ((f)->temp_offset) -#define stbv_temp_alloc_restore(f,p) ((f)->temp_offset = (p)) +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#define temp_free(f,p) (void)0 +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) -#define stbv_temp_block_array(f,count,size) stbv_make_block_array(stbv_temp_alloc(f,stbv_array_size_required(count,size)), count, size) +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) // given a sufficiently large block of memory, make an array of pointers to subblocks of it -static void *stbv_make_block_array(void *mem, int count, int size) +static void *make_block_array(void *mem, int count, int size) { int i; void ** p = (void **) mem; @@ -916,9 +947,9 @@ static void *stbv_make_block_array(void *mem, int count, int size) return p; } -static void *stbv_setup_malloc(stbv_vorb *f, int sz) +static void *setup_malloc(vorb *f, int sz) { - sz = (sz+3) & ~3; + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. f->setup_memory_required += sz; if (f->alloc.alloc_buffer) { void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; @@ -929,15 +960,15 @@ static void *stbv_setup_malloc(stbv_vorb *f, int sz) return sz ? malloc(sz) : NULL; } -static void stbv_setup_free(stbv_vorb *f, void *p) +static void setup_free(vorb *f, void *p) { if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack free(p); } -static void *stbv_setup_temp_malloc(stbv_vorb *f, int sz) +static void *setup_temp_malloc(vorb *f, int sz) { - sz = (sz+3) & ~3; + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. if (f->alloc.alloc_buffer) { if (f->temp_offset - sz < f->setup_offset) return NULL; f->temp_offset -= sz; @@ -946,37 +977,37 @@ static void *stbv_setup_temp_malloc(stbv_vorb *f, int sz) return malloc(sz); } -static void stbv_setup_temp_free(stbv_vorb *f, void *p, int sz) +static void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { - f->temp_offset += (sz+3)&~3; + f->temp_offset += (sz+7)&~7; return; } free(p); } -#define STBV_CRC32_POLY 0x04c11db7 // from spec +#define CRC32_POLY 0x04c11db7 // from spec -static stbv_uint32 stbv_crc_table[256]; -static void stbv_crc32_init(void) +static uint32 crc_table[256]; +static void crc32_init(void) { int i,j; - stbv_uint32 s; + uint32 s; for(i=0; i < 256; i++) { - for (s=(stbv_uint32) i << 24, j=0; j < 8; ++j) - s = (s << 1) ^ (s >= (1U<<31) ? STBV_CRC32_POLY : 0); - stbv_crc_table[i] = s; + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; } } -static __forceinline stbv_uint32 stbv_crc32_update(stbv_uint32 crc, stbv_uint8 byte) +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) { - return (crc << 8) ^ stbv_crc_table[byte ^ (crc >> 24)]; + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; } // used in setup, and for huffman that doesn't go fast path -static unsigned int stbv_bit_reverse(unsigned int n) +static unsigned int bit_reverse(unsigned int n) { n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); @@ -985,7 +1016,7 @@ static unsigned int stbv_bit_reverse(unsigned int n) return (n >> 16) | (n << 16); } -static float stbv_square(float x) +static float square(float x) { return x*x; } @@ -993,7 +1024,7 @@ static float stbv_square(float x) // this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 // as required by the specification. fast(?) implementation from stb.h // @OPTIMIZE: called multiple times per-packet with "constants"; move to setup -static int stbv_ilog(stbv_int32 n) +static int ilog(int32 n) { static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; @@ -1023,14 +1054,14 @@ static int stbv_ilog(stbv_int32 n) // these functions are only called at setup, and only a few times // per file -static float stbv_float32_unpack(stbv_uint32 x) +static float float32_unpack(uint32 x) { // from the specification - stbv_uint32 mantissa = x & 0x1fffff; - stbv_uint32 sign = x & 0x80000000; - stbv_uint32 exp = (x & 0x7fe00000) >> 21; + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; - return (float) ldexp((float)res, exp-788); + return (float) ldexp((float)res, (int)exp-788); } @@ -1041,7 +1072,7 @@ static float stbv_float32_unpack(stbv_uint32 x) // vorbis allows a huffman table with non-sorted lengths. This // requires a more sophisticated construction, since symbols in // order do not map to huffman codes "in order". -static void stbv_add_entry(StbvCodebook *c, stbv_uint32 huff_code, int symbol, int count, int len, stbv_uint32 *values) +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) { if (!c->sparse) { c->codewords [symbol] = huff_code; @@ -1052,17 +1083,18 @@ static void stbv_add_entry(StbvCodebook *c, stbv_uint32 huff_code, int symbol, i } } -static int stbv_compute_codewords(StbvCodebook *c, stbv_uint8 *len, int n, stbv_uint32 *values) +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { int i,k,m=0; - stbv_uint32 available[32]; + uint32 available[32]; memset(available, 0, sizeof(available)); // find the first entry for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this // add to the list - stbv_add_entry(c, 0, k, m++, len[k], values); + add_entry(c, 0, k, m++, len[k], values); // add all available leaves for (i=1; i <= len[k]; ++i) available[i] = 1U << (32-i); @@ -1071,9 +1103,10 @@ static int stbv_compute_codewords(StbvCodebook *c, stbv_uint8 *len, int n, stbv_ // could probably be combined (except the initial code is 0, // and I use 0 in available[] to mean 'empty') for (i=k+1; i < n; ++i) { - stbv_uint32 res; + uint32 res; int z = len[i], y; if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have @@ -1083,12 +1116,10 @@ static int stbv_compute_codewords(StbvCodebook *c, stbv_uint8 *len, int n, stbv_ while (z > 0 && !available[z]) --z; if (z == 0) { return FALSE; } res = available[z]; - assert(z >= 0 && z < 32); available[z] = 0; - stbv_add_entry(c, stbv_bit_reverse(res), i, m++, len[i], values); - // propogate availability up the tree + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propagate availability up the tree if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); for (y=len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32-y)); @@ -1100,10 +1131,10 @@ static int stbv_compute_codewords(StbvCodebook *c, stbv_uint8 *len, int n, stbv_ // accelerated huffman table allows fast O(1) match of all symbols // of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH -static void stbv_compute_accelerated_huffman(StbvCodebook *c) +static void compute_accelerated_huffman(Codebook *c) { int i, len; - for (i=0; i < STBV_FAST_HUFFMAN_TABLE_SIZE; ++i) + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) c->fast_huffman[i] = -1; len = c->sparse ? c->sorted_entries : c->entries; @@ -1112,9 +1143,9 @@ static void stbv_compute_accelerated_huffman(StbvCodebook *c) #endif for (i=0; i < len; ++i) { if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { - stbv_uint32 z = c->sparse ? stbv_bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; // set table entries for all bit combinations in the higher bits - while (z < STBV_FAST_HUFFMAN_TABLE_SIZE) { + while (z < FAST_HUFFMAN_TABLE_SIZE) { c->fast_huffman[z] = i; z += 1 << c->codeword_lengths[i]; } @@ -1128,14 +1159,14 @@ static void stbv_compute_accelerated_huffman(StbvCodebook *c) #define STBV_CDECL #endif -static int STBV_CDECL stbv_uint32_compare(const void *p, const void *q) +static int STBV_CDECL uint32_compare(const void *p, const void *q) { - stbv_uint32 x = * (stbv_uint32 *) p; - stbv_uint32 y = * (stbv_uint32 *) q; + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; return x < y ? -1 : x > y; } -static int stbv_include_in_sort(StbvCodebook *c, stbv_uint8 len) +static int include_in_sort(Codebook *c, uint8 len) { if (c->sparse) { assert(len != NO_CODE); return TRUE; } if (len == NO_CODE) return FALSE; @@ -1145,7 +1176,7 @@ static int stbv_include_in_sort(StbvCodebook *c, stbv_uint8 len) // if the fast table above doesn't work, we want to binary // search them... need to reverse the bits -static void stbv_compute_sorted_huffman(StbvCodebook *c, stbv_uint8 *lengths, stbv_uint32 *values) +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) { int i, len; // build a list of all the entries @@ -1155,15 +1186,15 @@ static void stbv_compute_sorted_huffman(StbvCodebook *c, stbv_uint8 *lengths, st if (!c->sparse) { int k = 0; for (i=0; i < c->entries; ++i) - if (stbv_include_in_sort(c, lengths[i])) - c->sorted_codewords[k++] = stbv_bit_reverse(c->codewords[i]); + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); assert(k == c->sorted_entries); } else { for (i=0; i < c->sorted_entries; ++i) - c->sorted_codewords[i] = stbv_bit_reverse(c->codewords[i]); + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); } - qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), stbv_uint32_compare); + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); c->sorted_codewords[c->sorted_entries] = 0xffffffff; len = c->sparse ? c->sorted_entries : c->entries; @@ -1174,8 +1205,8 @@ static void stbv_compute_sorted_huffman(StbvCodebook *c, stbv_uint8 *lengths, st // #1 requires extra storage, #2 is slow, #3 can use binary search! for (i=0; i < len; ++i) { int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; - if (stbv_include_in_sort(c,huff_len)) { - stbv_uint32 code = stbv_bit_reverse(c->codewords[i]); + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); int x=0, n=c->sorted_entries; while (n > 1) { // invariant: sc[x] <= code < sc[x+n] @@ -1199,26 +1230,28 @@ static void stbv_compute_sorted_huffman(StbvCodebook *c, stbv_uint8 *lengths, st } // only run while parsing the header (3 times) -static int stbv_vorbis_validate(stbv_uint8 *data) +static int vorbis_validate(uint8 *data) { - static stbv_uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; return memcmp(data, vorbis, 6) == 0; } // called from setup only, once per code book // (formula implied by specification) -static int stbv_lookup1_values(int entries, int dim) +static int lookup1_values(int entries, int dim) { int r = (int) floor(exp((float) log((float) entries) / dim)); if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; ++r; // floor() to avoid _ftol() when non-CRT - assert(pow((float) r+1, dim) > entries); - assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + if (pow((float) r+1, dim) <= entries) + return -1; + if ((int) floor(pow((float) r, dim)) > entries) + return -1; return r; } // called twice per file -static void stbv_compute_twiddle_factors(int n, float *A, float *B, float *C) +static void compute_twiddle_factors(int n, float *A, float *B, float *C) { int n4 = n >> 2, n8 = n >> 3; int k,k2; @@ -1235,39 +1268,39 @@ static void stbv_compute_twiddle_factors(int n, float *A, float *B, float *C) } } -static void stbv_compute_window(int n, float *window) +static void compute_window(int n, float *window) { int n2 = n >> 1, i; for (i=0; i < n2; ++i) - window[i] = (float) sin(0.5 * M_PI * stbv_square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); } -static void stbv_compute_bitreverse(int n, stbv_uint16 *rev) +static void compute_bitreverse(int n, uint16 *rev) { - int ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions int i, n8 = n >> 3; for (i=0; i < n8; ++i) - rev[i] = (stbv_bit_reverse(i) >> (32-ld+3)) << 2; + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; } -static int stbv_init_blocksize(stbv_vorb *f, int b, int n) +static int init_blocksize(vorb *f, int b, int n) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; - f->A[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); - f->B[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); - f->C[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n4); - if (!f->A[b] || !f->B[b] || !f->C[b]) return stbv_error(f, VORBIS_outofmem); - stbv_compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); - f->window[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); - if (!f->window[b]) return stbv_error(f, VORBIS_outofmem); - stbv_compute_window(n, f->window[b]); - f->stbv_bit_reverse[b] = (stbv_uint16 *) stbv_setup_malloc(f, sizeof(stbv_uint16) * n8); - if (!f->stbv_bit_reverse[b]) return stbv_error(f, VORBIS_outofmem); - stbv_compute_bitreverse(n, f->stbv_bit_reverse[b]); + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); return TRUE; } -static void stbv_neighbors(stbv_uint16 *x, int n, int *plow, int *phigh) +static void neighbors(uint16 *x, int n, int *plow, int *phigh) { int low = -1; int high = 65536; @@ -1281,13 +1314,13 @@ static void stbv_neighbors(stbv_uint16 *x, int n, int *plow, int *phigh) // this has been repurposed so y is now the original index instead of y typedef struct { - stbv_uint16 x,id; -} stbv_floor_ordering; + uint16 x,id; +} stbv__floor_ordering; -static int STBV_CDECL stbv_point_compare(const void *p, const void *q) +static int STBV_CDECL point_compare(const void *p, const void *q) { - stbv_floor_ordering *a = (stbv_floor_ordering *) p; - stbv_floor_ordering *b = (stbv_floor_ordering *) q; + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; return a->x < b->x ? -1 : a->x > b->x; } @@ -1296,14 +1329,14 @@ static int STBV_CDECL stbv_point_compare(const void *p, const void *q) #if defined(STB_VORBIS_NO_STDIO) - #define STBV_USE_MEMORY(z) TRUE + #define USE_MEMORY(z) TRUE #else - #define STBV_USE_MEMORY(z) ((z)->stream) + #define USE_MEMORY(z) ((z)->stream) #endif -static stbv_uint8 stbv_get8(stbv_vorb *z) +static uint8 get8(vorb *z) { - if (STBV_USE_MEMORY(z)) { + if (USE_MEMORY(z)) { if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } return *z->stream++; } @@ -1317,26 +1350,26 @@ static stbv_uint8 stbv_get8(stbv_vorb *z) #endif } -static stbv_uint32 stbv_get32(stbv_vorb *f) +static uint32 get32(vorb *f) { - stbv_uint32 x; - x = stbv_get8(f); - x += stbv_get8(f) << 8; - x += stbv_get8(f) << 16; - x += (stbv_uint32) stbv_get8(f) << 24; + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; return x; } -static int stbv_getn(stbv_vorb *z, stbv_uint8 *data, int n) +static int getn(vorb *z, uint8 *data, int n) { - if (STBV_USE_MEMORY(z)) { + if (USE_MEMORY(z)) { if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } memcpy(data, z->stream, n); z->stream += n; return 1; } - #ifndef STB_VORBIS_NO_STDIO + #ifndef STB_VORBIS_NO_STDIO if (fread(data, n, 1, z->f) == 1) return 1; else { @@ -1346,9 +1379,9 @@ static int stbv_getn(stbv_vorb *z, stbv_uint8 *data, int n) #endif } -static void stbv_skip(stbv_vorb *z, int n) +static void skip(vorb *z, int n) { - if (STBV_USE_MEMORY(z)) { + if (USE_MEMORY(z)) { z->stream += n; if (z->stream >= z->stream_end) z->eof = 1; return; @@ -1361,13 +1394,13 @@ static void stbv_skip(stbv_vorb *z, int n) #endif } -static int stbv_set_file_offset(stb_vorbis *f, unsigned int loc) +static int set_file_offset(stb_vorbis *f, unsigned int loc) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (f->push_mode) return 0; #endif f->eof = 0; - if (STBV_USE_MEMORY(f)) { + if (USE_MEMORY(f)) { if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { f->stream = f->stream_end; f->eof = 1; @@ -1393,44 +1426,47 @@ static int stbv_set_file_offset(stb_vorbis *f, unsigned int loc) } -static stbv_uint8 stbv_ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; -static int stbv_capture_pattern(stbv_vorb *f) +static int capture_pattern(vorb *f) { - if (0x4f != stbv_get8(f)) return FALSE; - if (0x67 != stbv_get8(f)) return FALSE; - if (0x67 != stbv_get8(f)) return FALSE; - if (0x53 != stbv_get8(f)) return FALSE; + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; return TRUE; } -#define STBV_PAGEFLAG_continued_packet 1 -#define STBV_PAGEFLAG_first_page 2 -#define STBV_PAGEFLAG_last_page 4 +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 -static int stbv_start_page_no_capturepattern(stbv_vorb *f) +static int start_page_no_capturepattern(vorb *f) { - stbv_uint32 loc0,loc1,n; + uint32 loc0,loc1,n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } // stream structure version - if (0 != stbv_get8(f)) return stbv_error(f, VORBIS_invalid_stream_structure_version); + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); // header flag - f->page_flag = stbv_get8(f); + f->page_flag = get8(f); // absolute granule position - loc0 = stbv_get32(f); - loc1 = stbv_get32(f); + loc0 = get32(f); + loc1 = get32(f); // @TODO: validate loc0,loc1 as valid positions? // stream serial number -- vorbis doesn't interleave, so discard - stbv_get32(f); - //if (f->serial != stbv_get32(f)) return stbv_error(f, VORBIS_incorrect_stream_serial_number); + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); // page sequence number - n = stbv_get32(f); + n = get32(f); f->last_page = n; // CRC32 - stbv_get32(f); + get32(f); // page_segments - f->segment_count = stbv_get8(f); - if (!stbv_getn(f, f->segments, f->segment_count)) - return stbv_error(f, VORBIS_unexpected_eof); + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); // assume we _don't_ know any the sample position of any segments f->end_seg_with_known_loc = -2; if (loc0 != ~0U || loc1 != ~0U) { @@ -1447,32 +1483,29 @@ static int stbv_start_page_no_capturepattern(stbv_vorb *f) } if (f->first_decode) { int i,len; - StbvProbedPage p; len = 0; for (i=0; i < f->segment_count; ++i) len += f->segments[i]; len += 27 + f->segment_count; - p.page_start = f->first_audio_page_offset; - p.page_end = p.page_start + len; - p.last_decoded_sample = loc0; - f->p_first = p; + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; } f->next_seg = 0; return TRUE; } -static int stbv_start_page(stbv_vorb *f) +static int start_page(vorb *f) { - if (!stbv_capture_pattern(f)) return stbv_error(f, VORBIS_missing_capture_pattern); - return stbv_start_page_no_capturepattern(f); + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); } -static int stbv_start_packet(stbv_vorb *f) +static int start_packet(vorb *f) { while (f->next_seg == -1) { - if (!stbv_start_page(f)) return FALSE; - if (f->page_flag & STBV_PAGEFLAG_continued_packet) - return stbv_error(f, VORBIS_continued_packet_flag_invalid); + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); } f->last_seg = FALSE; f->valid_bits = 0; @@ -1482,35 +1515,35 @@ static int stbv_start_packet(stbv_vorb *f) return TRUE; } -static int stbv_maybe_start_packet(stbv_vorb *f) +static int maybe_start_packet(vorb *f) { if (f->next_seg == -1) { - int x = stbv_get8(f); + int x = get8(f); if (f->eof) return FALSE; // EOF at page boundary is not an error! - if (0x4f != x ) return stbv_error(f, VORBIS_missing_capture_pattern); - if (0x67 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); - if (0x67 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); - if (0x53 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); - if (!stbv_start_page_no_capturepattern(f)) return FALSE; - if (f->page_flag & STBV_PAGEFLAG_continued_packet) { + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { // set up enough state that we can read this packet if we want, // e.g. during recovery f->last_seg = FALSE; f->bytes_in_seg = 0; - return stbv_error(f, VORBIS_continued_packet_flag_invalid); + return error(f, VORBIS_continued_packet_flag_invalid); } } - return stbv_start_packet(f); + return start_packet(f); } -static int stbv_next_segment(stbv_vorb *f) +static int next_segment(vorb *f) { int len; if (f->last_seg) return 0; if (f->next_seg == -1) { - f->last_seg_which = f->segment_count-1; // in case stbv_start_page fails - if (!stbv_start_page(f)) { f->last_seg = 1; return 0; } - if (!(f->page_flag & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_continued_packet_flag_invalid); + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); } len = f->segments[f->next_seg++]; if (len < 255) { @@ -1524,59 +1557,70 @@ static int stbv_next_segment(stbv_vorb *f) return len; } -#define STBV_EOP (-1) -#define STBV_INVALID_BITS (-1) +#define EOP (-1) +#define INVALID_BITS (-1) -static int stbv_get8_packet_raw(stbv_vorb *f) +static int get8_packet_raw(vorb *f) { if (!f->bytes_in_seg) { // CLANG! - if (f->last_seg) return STBV_EOP; - else if (!stbv_next_segment(f)) return STBV_EOP; + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; } assert(f->bytes_in_seg > 0); --f->bytes_in_seg; ++f->packet_bytes; - return stbv_get8(f); + return get8(f); } -static int stbv_get8_packet(stbv_vorb *f) +static int get8_packet(vorb *f) { - int x = stbv_get8_packet_raw(f); + int x = get8_packet_raw(f); f->valid_bits = 0; return x; } -static void stbv_flush_packet(stbv_vorb *f) +static int get32_packet(vorb *f) { - while (stbv_get8_packet_raw(f) != STBV_EOP); + uint32 x; + x = get8_packet(f); + x += get8_packet(f) << 8; + x += get8_packet(f) << 16; + x += (uint32) get8_packet(f) << 24; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); } // @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important // as the huffman decoder? -static stbv_uint32 stbv_get_bits(stbv_vorb *f, int n) +static uint32 get_bits(vorb *f, int n) { - stbv_uint32 z; + uint32 z; if (f->valid_bits < 0) return 0; if (f->valid_bits < n) { if (n > 24) { // the accumulator technique below would not work correctly in this case - z = stbv_get_bits(f, 24); - z += stbv_get_bits(f, n-24) << 24; + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; return z; } if (f->valid_bits == 0) f->acc = 0; while (f->valid_bits < n) { - int z = stbv_get8_packet_raw(f); - if (z == STBV_EOP) { - f->valid_bits = STBV_INVALID_BITS; + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; return 0; } f->acc += z << f->valid_bits; f->valid_bits += 8; } } - if (f->valid_bits < 0) return 0; + + assert(f->valid_bits >= n); z = f->acc & ((1 << n)-1); f->acc >>= n; f->valid_bits -= n; @@ -1587,15 +1631,15 @@ static stbv_uint32 stbv_get_bits(stbv_vorb *f, int n) // expand the buffer to as many bits as possible without reading off end of packet // it might be nice to allow f->valid_bits and f->acc to be stored in registers, // e.g. cache them locally and decode locally -static __forceinline void stbv_prep_huffman(stbv_vorb *f) +static __forceinline void prep_huffman(vorb *f) { if (f->valid_bits <= 24) { if (f->valid_bits == 0) f->acc = 0; do { int z; if (f->last_seg && !f->bytes_in_seg) return; - z = stbv_get8_packet_raw(f); - if (z == STBV_EOP) return; + z = get8_packet_raw(f); + if (z == EOP) return; f->acc += (unsigned) z << f->valid_bits; f->valid_bits += 8; } while (f->valid_bits <= 24); @@ -1604,15 +1648,15 @@ static __forceinline void stbv_prep_huffman(stbv_vorb *f) enum { - STBV_VORBIS_packet_id = 1, - STBV_VORBIS_packet_comment = 3, - STBV_VORBIS_packet_setup = 5 + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 }; -static int stbv_codebook_decode_scalar_raw(stbv_vorb *f, StbvCodebook *c) +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) { int i; - stbv_prep_huffman(f); + prep_huffman(f); if (c->codewords == NULL && c->sorted_codewords == NULL) return -1; @@ -1621,7 +1665,7 @@ static int stbv_codebook_decode_scalar_raw(stbv_vorb *f, StbvCodebook *c) // sorted_codewords && c->entries > 8 if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { // binary search - stbv_uint32 code = stbv_bit_reverse(f->acc); + uint32 code = bit_reverse(f->acc); int x=0, n=c->sorted_entries, len; while (n > 1) { @@ -1663,17 +1707,17 @@ static int stbv_codebook_decode_scalar_raw(stbv_vorb *f, StbvCodebook *c) } } - stbv_error(f, VORBIS_invalid_stream); + error(f, VORBIS_invalid_stream); f->valid_bits = 0; return -1; } #ifndef STB_VORBIS_NO_INLINE_DECODE -#define STBV_DECODE_RAW(var, f,c) \ +#define DECODE_RAW(var, f,c) \ if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ - stbv_prep_huffman(f); \ - var = f->acc & STBV_FAST_HUFFMAN_TABLE_MASK; \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ var = c->fast_huffman[var]; \ if (var >= 0) { \ int n = c->codeword_lengths[var]; \ @@ -1681,18 +1725,18 @@ static int stbv_codebook_decode_scalar_raw(stbv_vorb *f, StbvCodebook *c) f->valid_bits -= n; \ if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ } else { \ - var = stbv_codebook_decode_scalar_raw(f,c); \ + var = codebook_decode_scalar_raw(f,c); \ } #else -static int stbv_codebook_decode_scalar(stbv_vorb *f, StbvCodebook *c) +static int codebook_decode_scalar(vorb *f, Codebook *c) { int i; if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) - stbv_prep_huffman(f); + prep_huffman(f); // fast huffman table lookup - i = f->acc & STBV_FAST_HUFFMAN_TABLE_MASK; + i = f->acc & FAST_HUFFMAN_TABLE_MASK; i = c->fast_huffman[i]; if (i >= 0) { f->acc >>= c->codeword_lengths[i]; @@ -1700,21 +1744,21 @@ static int stbv_codebook_decode_scalar(stbv_vorb *f, StbvCodebook *c) if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } return i; } - return stbv_codebook_decode_scalar_raw(f,c); + return codebook_decode_scalar_raw(f,c); } -#define STBV_DECODE_RAW(var,f,c) var = stbv_codebook_decode_scalar(f,c); +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); #endif -#define STBV_DECODE(var,f,c) \ - STBV_DECODE_RAW(var,f,c) \ +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ if (c->sparse) var = c->sorted_values[var]; #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - #define DECODE_VQ(var,f,c) STBV_DECODE_RAW(var,f,c) + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) #else - #define DECODE_VQ(var,f,c) STBV_DECODE(var,f,c) + #define DECODE_VQ(var,f,c) DECODE(var,f,c) #endif @@ -1722,45 +1766,45 @@ static int stbv_codebook_decode_scalar(stbv_vorb *f, StbvCodebook *c) -// STBV_CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case // where we avoid one addition -#define STBV_CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) -#define STBV_CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) -#define STBV_CODEBOOK_ELEMENT_BASE(c) (0) +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) -static int stbv_codebook_decode_start(stbv_vorb *f, StbvCodebook *c) +static int codebook_decode_start(vorb *f, Codebook *c) { int z = -1; // type 0 is only legal in a scalar context if (c->lookup_type == 0) - stbv_error(f, VORBIS_invalid_stream); + error(f, VORBIS_invalid_stream); else { DECODE_VQ(z,f,c); if (c->sparse) assert(z < c->sorted_entries); - if (z < 0) { // check for STBV_EOP + if (z < 0) { // check for EOP if (!f->bytes_in_seg) if (f->last_seg) return z; - stbv_error(f, VORBIS_invalid_stream); + error(f, VORBIS_invalid_stream); } } return z; } -static int stbv_codebook_decode(stbv_vorb *f, StbvCodebook *c, float *output, int len) +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) { - int i,z = stbv_codebook_decode_start(f,c); + int i,z = codebook_decode_start(f,c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { - float last = STBV_CODEBOOK_ELEMENT_BASE(c); + float last = CODEBOOK_ELEMENT_BASE(c); int div = 1; for (i=0; i < len; ++i) { int off = (z / div) % c->lookup_values; - float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; output[i] += val; if (c->sequence_p) last = val + c->minimum_value; div *= c->lookup_values; @@ -1771,26 +1815,26 @@ static int stbv_codebook_decode(stbv_vorb *f, StbvCodebook *c, float *output, in z *= c->dimensions; if (c->sequence_p) { - float last = STBV_CODEBOOK_ELEMENT_BASE(c); + float last = CODEBOOK_ELEMENT_BASE(c); for (i=0; i < len; ++i) { - float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; output[i] += val; last = val + c->minimum_value; } } else { - float last = STBV_CODEBOOK_ELEMENT_BASE(c); + float last = CODEBOOK_ELEMENT_BASE(c); for (i=0; i < len; ++i) { - output[i] += STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; } } return TRUE; } -static int stbv_codebook_decode_step(stbv_vorb *f, StbvCodebook *c, float *output, int len, int step) +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) { - int i,z = stbv_codebook_decode_start(f,c); - float last = STBV_CODEBOOK_ELEMENT_BASE(c); + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; @@ -1799,7 +1843,7 @@ static int stbv_codebook_decode_step(stbv_vorb *f, StbvCodebook *c, float *outpu int div = 1; for (i=0; i < len; ++i) { int off = (z / div) % c->lookup_values; - float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; output[i*step] += val; if (c->sequence_p) last = val; div *= c->lookup_values; @@ -1810,7 +1854,7 @@ static int stbv_codebook_decode_step(stbv_vorb *f, StbvCodebook *c, float *outpu z *= c->dimensions; for (i=0; i < len; ++i) { - float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; output[i*step] += val; if (c->sequence_p) last = val; } @@ -1818,17 +1862,17 @@ static int stbv_codebook_decode_step(stbv_vorb *f, StbvCodebook *c, float *outpu return TRUE; } -static int stbv_codebook_decode_deinterleave_repeat(stbv_vorb *f, StbvCodebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) { int c_inter = *c_inter_p; int p_inter = *p_inter_p; int i,z, effective = c->dimensions; // type 0 is only legal in a scalar context - if (c->lookup_type == 0) return stbv_error(f, VORBIS_invalid_stream); + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); while (total_decode > 0) { - float last = STBV_CODEBOOK_ELEMENT_BASE(c); + float last = CODEBOOK_ELEMENT_BASE(c); DECODE_VQ(z,f,c); #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK assert(!c->sparse || z < c->sorted_entries); @@ -1836,7 +1880,7 @@ static int stbv_codebook_decode_deinterleave_repeat(stbv_vorb *f, StbvCodebook * if (z < 0) { if (!f->bytes_in_seg) if (f->last_seg) return FALSE; - return stbv_error(f, VORBIS_invalid_stream); + return error(f, VORBIS_invalid_stream); } // if this will take us off the end of the buffers, stop short! @@ -1852,7 +1896,7 @@ static int stbv_codebook_decode_deinterleave_repeat(stbv_vorb *f, StbvCodebook * int div = 1; for (i=0; i < effective; ++i) { int off = (z / div) % c->lookup_values; - float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } @@ -1865,7 +1909,7 @@ static int stbv_codebook_decode_deinterleave_repeat(stbv_vorb *f, StbvCodebook * z *= c->dimensions; if (c->sequence_p) { for (i=0; i < effective; ++i) { - float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } @@ -1873,7 +1917,7 @@ static int stbv_codebook_decode_deinterleave_repeat(stbv_vorb *f, StbvCodebook * } } else { for (i=0; i < effective; ++i) { - float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } @@ -1888,7 +1932,7 @@ static int stbv_codebook_decode_deinterleave_repeat(stbv_vorb *f, StbvCodebook * return TRUE; } -static int stbv_predict_point(int x, int x0, int x1, int y0, int y1) +static int predict_point(int x, int x0, int x1, int y0, int y1) { int dy = y1 - y0; int adx = x1 - x0; @@ -1899,71 +1943,71 @@ static int stbv_predict_point(int x, int x0, int x1, int y0, int y1) } // the following table is block-copied from the specification -static float stbv_inverse_db_table[256] = +static float inverse_db_table[256] = { - 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, - 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, - 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, - 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, - 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, - 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, - 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, - 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, - 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, - 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, - 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, - 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, - 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, - 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, - 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, - 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, - 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, - 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, - 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, - 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, - 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, - 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, - 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, - 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, - 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, - 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, - 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, - 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, - 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, - 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, - 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, - 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, - 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, - 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, - 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, - 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, - 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, - 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, - 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, - 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, - 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, - 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, - 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, - 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, - 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, - 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, - 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, - 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, - 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, - 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, - 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, - 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, - 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, - 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, - 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, - 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, - 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, - 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, - 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, - 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, - 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, - 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, - 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, 0.82788260f, 0.88168307f, 0.9389798f, 1.0f }; @@ -1976,18 +2020,18 @@ static float stbv_inverse_db_table[256] = // ... also, isn't the whole point of Bresenham's algorithm to NOT // have to divide in the setup? sigh. #ifndef STB_VORBIS_NO_DEFER_FLOOR -#define STBV_LINE_OP(a,b) a *= b +#define LINE_OP(a,b) a *= b #else -#define STBV_LINE_OP(a,b) a = b +#define LINE_OP(a,b) a = b #endif #ifdef STB_VORBIS_DIVIDE_TABLE -#define STBV_DIVTAB_NUMER 32 -#define STBV_DIVTAB_DENOM 64 -stbv_int8 stbv_integer_divide_table[STBV_DIVTAB_NUMER][STBV_DIVTAB_DENOM]; // 2KB +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB #endif -static __forceinline void stbv_draw_line(float *output, int x0, int y0, int x1, int y1, int n) +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) { int dy = y1 - y0; int adx = x1 - x0; @@ -1998,12 +2042,12 @@ static __forceinline void stbv_draw_line(float *output, int x0, int y0, int x1, int sy; #ifdef STB_VORBIS_DIVIDE_TABLE - if (adx < STBV_DIVTAB_DENOM && ady < STBV_DIVTAB_NUMER) { + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { if (dy < 0) { - base = -stbv_integer_divide_table[ady][adx]; + base = -integer_divide_table[ady][adx]; sy = base-1; } else { - base = stbv_integer_divide_table[ady][adx]; + base = integer_divide_table[ady][adx]; sy = base+1; } } else { @@ -2023,7 +2067,7 @@ static __forceinline void stbv_draw_line(float *output, int x0, int y0, int x1, ady -= abs(base) * adx; if (x1 > n) x1 = n; if (x < x1) { - STBV_LINE_OP(output[x], stbv_inverse_db_table[y]); + LINE_OP(output[x], inverse_db_table[y&255]); for (++x; x < x1; ++x) { err += ady; if (err >= adx) { @@ -2031,22 +2075,22 @@ static __forceinline void stbv_draw_line(float *output, int x0, int y0, int x1, y += sy; } else y += base; - STBV_LINE_OP(output[x], stbv_inverse_db_table[y]); + LINE_OP(output[x], inverse_db_table[y&255]); } } } -static int stbv_residue_decode(stbv_vorb *f, StbvCodebook *book, float *target, int offset, int n, int rtype) +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) { int k; if (rtype == 0) { int step = n / book->dimensions; for (k=0; k < step; ++k) - if (!stbv_codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) return FALSE; } else { for (k=0; k < n; ) { - if (!stbv_codebook_decode(f, book, target+offset, n-k)) + if (!codebook_decode(f, book, target+offset, n-k)) return FALSE; k += book->dimensions; offset += book->dimensions; @@ -2057,10 +2101,10 @@ static int stbv_residue_decode(stbv_vorb *f, StbvCodebook *book, float *target, // n is 1/2 of the blocksize -- // specification: "Correct per-vector decode length is [n]/2" -static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, int n, int rn, stbv_uint8 *do_not_decode) +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) { int i,j,pass; - StbvResidue *r = f->residue_config + rn; + Residue *r = f->residue_config + rn; int rtype = f->residue_types[rn]; int c = r->classbook; int classwords = f->codebooks[c].dimensions; @@ -2069,14 +2113,14 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; - int temp_alloc_point = stbv_temp_alloc_save(f); + int temp_alloc_point = temp_alloc_save(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - stbv_uint8 ***part_classdata = (stbv_uint8 ***) stbv_temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); #else - int **classifications = (int **) stbv_temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); #endif - STBV_CHECK(f); + CHECK(f); for (i=0; i < ch; ++i) if (!do_not_decode[i]) @@ -2096,10 +2140,10 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, int z = r->begin + pcount*r->part_size; int c_inter = (z & 1), p_inter = z>>1; if (pass == 0) { - StbvCodebook *c = f->codebooks+r->classbook; + Codebook *c = f->codebooks+r->classbook; int q; - STBV_DECODE(q,f,c); - if (q == STBV_EOP) goto done; + DECODE(q,f,c); + if (q == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[0][class_set] = r->classdata[q]; #else @@ -2118,13 +2162,13 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, #endif int b = r->residue_books[c][pass]; if (b >= 0) { - StbvCodebook *book = f->codebooks + b; + Codebook *book = f->codebooks + b; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; #else // saves 1% - if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; #endif } else { @@ -2137,55 +2181,15 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, ++class_set; #endif } - } else if (ch == 1) { - while (pcount < part_read) { - int z = r->begin + pcount*r->part_size; - int c_inter = 0, p_inter = z; - if (pass == 0) { - StbvCodebook *c = f->codebooks+r->classbook; - int q; - STBV_DECODE(q,f,c); - if (q == STBV_EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[0][i+pcount] = q % r->classifications; - q /= r->classifications; - } - #endif - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount*r->part_size; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; - #else - int c = classifications[0][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - StbvCodebook *book = f->codebooks + b; - if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - } else { - z += r->part_size; - c_inter = 0; - p_inter = z; - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } else { + } else if (ch > 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = z % ch, p_inter = z/ch; if (pass == 0) { - StbvCodebook *c = f->codebooks+r->classbook; + Codebook *c = f->codebooks+r->classbook; int q; - STBV_DECODE(q,f,c); - if (q == STBV_EOP) goto done; + DECODE(q,f,c); + if (q == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[0][class_set] = r->classdata[q]; #else @@ -2204,8 +2208,8 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, #endif int b = r->residue_books[c][pass]; if (b >= 0) { - StbvCodebook *book = f->codebooks + b; - if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; } else { z += r->part_size; @@ -2221,7 +2225,7 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, } goto done; } - STBV_CHECK(f); + CHECK(f); for (pass=0; pass < 8; ++pass) { int pcount = 0, class_set=0; @@ -2229,10 +2233,10 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, if (pass == 0) { for (j=0; j < ch; ++j) { if (!do_not_decode[j]) { - StbvCodebook *c = f->codebooks+r->classbook; + Codebook *c = f->codebooks+r->classbook; int temp; - STBV_DECODE(temp,f,c); - if (temp == STBV_EOP) goto done; + DECODE(temp,f,c); + if (temp == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[j][class_set] = r->classdata[temp]; #else @@ -2257,8 +2261,8 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, float *target = residue_buffers[j]; int offset = r->begin + pcount * r->part_size; int n = r->part_size; - StbvCodebook *book = f->codebooks + b; - if (!stbv_residue_decode(f, book, target, offset, n, rtype)) + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) goto done; } } @@ -2270,13 +2274,13 @@ static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, } } done: - STBV_CHECK(f); + CHECK(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - stbv_temp_free(f,part_classdata); + temp_free(f,part_classdata); #else - stbv_temp_free(f,classifications); + temp_free(f,classifications); #endif - stbv_temp_alloc_restore(f,temp_alloc_point); + temp_alloc_restore(f,temp_alloc_point); } @@ -2305,7 +2309,7 @@ void inverse_mdct_slow(float *buffer, int n) } #elif 0 // same as above, but just barely able to run in real time on modern machines -void inverse_mdct_slow(float *buffer, int n, stbv_vorb *f, int blocktype) +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { float mcos[16384]; int i,j; @@ -2343,7 +2347,7 @@ void dct_iv_slow(float *buffer, int n) } } -void inverse_mdct_slow(float *buffer, int n, stbv_vorb *f, int blocktype) +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; float temp[4096]; @@ -2364,11 +2368,11 @@ void inverse_mdct_slow(float *buffer, int n, stbv_vorb *f, int blocktype) #if LIBVORBIS_MDCT // directly call the vorbis MDCT using an interface documented // by Jeff Roberts... useful for performance comparison -typedef struct +typedef struct { int n; int log2n; - + float *trig; int *bitrev; @@ -2381,13 +2385,13 @@ extern void mdct_backward(mdct_lookup *init, float *in, float *out); mdct_lookup M1,M2; -void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { mdct_lookup *M; if (M1.n == n) M = &M1; else if (M2.n == n) M = &M2; else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } - else { + else { if (M2.n) __asm int 3; mdct_init(&M2, n); M = &M2; @@ -2401,7 +2405,7 @@ void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) // the following were split out into separate functions while optimizing; // they could be pushed back up but eh. __forceinline showed no change; // they're probably already being inlined. -static void stbv_imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) { float *ee0 = e + i_off; float *ee2 = ee0 + k_off; @@ -2446,7 +2450,7 @@ static void stbv_imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, f } } -static void stbv_imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) { int i; float k00_20, k01_21; @@ -2496,7 +2500,7 @@ static void stbv_imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, } } -static void stbv_imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) { int i; float A0 = A[0]; @@ -2547,7 +2551,7 @@ static void stbv_imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, } } -static __forceinline void stbv_iter_54(float *z) +static __forceinline void iter_54(float *z) { float k00,k11,k22,k33; float y0,y1,y2,y3; @@ -2579,7 +2583,7 @@ static __forceinline void stbv_iter_54(float *z) z[-7] = k11 + k22; // z1 - z5 - z2 + z6 } -static void stbv_imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) { int a_off = base_n >> 3; float A2 = A[0+a_off]; @@ -2588,48 +2592,47 @@ static void stbv_imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, floa while (z > base) { float k00,k11; + float l00,l11; - k00 = z[-0] - z[-8]; - k11 = z[-1] - z[-9]; - z[-0] = z[-0] + z[-8]; - z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11 ; + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; - k00 = z[ -2] - z[-10]; - k11 = z[ -3] - z[-11]; - z[ -2] = z[ -2] + z[-10]; - z[ -3] = z[ -3] + z[-11]; - z[-10] = (k00+k11) * A2; - z[-11] = (k11-k00) * A2; - - k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k00 = z[ -4] - z[-12]; k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; z[ -4] = z[ -4] + z[-12]; z[ -5] = z[ -5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation - k11 = z[ -7] - z[-15]; z[ -6] = z[ -6] + z[-14]; z[ -7] = z[ -7] + z[-15]; - z[-14] = (k00+k11) * A2; - z[-15] = (k00-k11) * A2; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; - stbv_iter_54(z); - stbv_iter_54(z-8); + iter_54(z); + iter_54(z-8); z -= 16; } } -static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; int ld; // @OPTIMIZE: reduce register pressure by using fewer variables? - int save_point = stbv_temp_alloc_save(f); - float *buf2 = (float *) stbv_temp_alloc(f, n2 * sizeof(*buf2)); + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); float *u=NULL,*v=NULL; // twiddle factors float *A = f->A[blocktype]; @@ -2650,7 +2653,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) // once I combined the passes. // so there's a missing 'times 2' here (for adding X to itself). - // this propogates through linearly to the end, where the numbers + // this propagates through linearly to the end, where the numbers // are 1/2 too small, and need to be compensated for. { @@ -2724,7 +2727,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) } // step 3 - ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions // optimized step 3: @@ -2734,14 +2737,14 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) // switch between them halfway. // this is iteration 0 of step 3 - stbv_imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); - stbv_imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); // this is iteration 1 of step 3 - stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); - stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); - stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); - stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); l=2; for (; l < (ld-3)>>1; ++l) { @@ -2749,7 +2752,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) int lim = 1 << (l+1); int i; for (i=0; i < lim; ++i) - stbv_imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); } for (; l < ld-6; ++l) { @@ -2760,7 +2763,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) float *A0 = A; i_off = n2-1; for (r=rlim; r > 0; --r) { - stbv_imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); A0 += k1*4; i_off -= 8; } @@ -2771,14 +2774,14 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) // the big win comes from getting rid of needless flops // due to the constants on pass 5 & 4 being all 1 and 0; // combining them to be simultaneous to improve cache made little difference - stbv_imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); // output is u // step 4, 5, and 6 // cannot be in-place because of step 5 { - stbv_uint16 *bitrev = f->stbv_bit_reverse[blocktype]; + uint16 *bitrev = f->bit_reverse[blocktype]; // weirdly, I'd have thought reading sequentially and writing // erratically would have been better than vice-versa, but in // fact that's not what my testing showed. (That is, with @@ -2800,7 +2803,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) d1[0] = u[k4+1]; d0[1] = u[k4+2]; d0[0] = u[k4+3]; - + d0 -= 4; d1 -= 4; bitrev += 2; @@ -2881,7 +2884,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) float p0,p1,p2,p3; p3 = e[6]*B[7] - e[7]*B[6]; - p2 = -e[6]*B[6] - e[7]*B[7]; + p2 = -e[6]*B[6] - e[7]*B[7]; d0[0] = p3; d1[3] = - p3; @@ -2889,7 +2892,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) d3[3] = p2; p1 = e[4]*B[5] - e[5]*B[4]; - p0 = -e[4]*B[4] - e[5]*B[5]; + p0 = -e[4]*B[4] - e[5]*B[5]; d0[1] = p1; d1[2] = - p1; @@ -2897,7 +2900,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) d3[2] = p0; p3 = e[2]*B[3] - e[3]*B[2]; - p2 = -e[2]*B[2] - e[3]*B[3]; + p2 = -e[2]*B[2] - e[3]*B[3]; d0[2] = p3; d1[1] = - p3; @@ -2905,7 +2908,7 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) d3[1] = p2; p1 = e[0]*B[1] - e[1]*B[0]; - p0 = -e[0]*B[0] - e[1]*B[1]; + p0 = -e[0]*B[0] - e[1]*B[1]; d0[3] = p1; d1[0] = - p1; @@ -2921,8 +2924,8 @@ static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) } } - stbv_temp_free(f,buf2); - stbv_temp_alloc_restore(f,save_point); + temp_free(f,buf2); + temp_alloc_restore(f,save_point); } #if 0 @@ -2973,7 +2976,7 @@ void inverse_mdct_naive(float *buffer, int n) w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; } // step 3 - ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions for (l=0; l < ld-3; ++l) { int k0 = n >> (l+2), k1 = 1 << (l+3); int rlim = n >> (l+4), r4, r; @@ -2996,7 +2999,7 @@ void inverse_mdct_naive(float *buffer, int n) // step 4 for (i=0; i < n8; ++i) { - int j = stbv_bit_reverse(i) >> (32-ld+3); + int j = bit_reverse(i) >> (32-ld+3); assert(j < n8); if (i == j) { // paper bug: original code probably swapped in place; if copying, @@ -3053,34 +3056,34 @@ void inverse_mdct_naive(float *buffer, int n) } #endif -static float *stbv_get_window(stbv_vorb *f, int len) +static float *get_window(vorb *f, int len) { len <<= 1; if (len == f->blocksize_0) return f->window[0]; if (len == f->blocksize_1) return f->window[1]; - assert(0); return NULL; } #ifndef STB_VORBIS_NO_DEFER_FLOOR -typedef stbv_int16 STBV_YTYPE; +typedef int16 YTYPE; #else -typedef int STBV_YTYPE; +typedef int YTYPE; #endif -static int stbv_do_floor(stbv_vorb *f, StbvMapping *map, int i, int n, float *target, STBV_YTYPE *finalY, stbv_uint8 *step2_flag) +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) { int n2 = n >> 1; int s = map->chan[i].mux, floor; floor = map->submap_floor[s]; if (f->floor_types[floor] == 0) { - return stbv_error(f, VORBIS_invalid_stream); + return error(f, VORBIS_invalid_stream); } else { - StbvFloor1 *g = &f->floor_config[floor].floor1; + Floor1 *g = &f->floor_config[floor].floor1; int j,q; int lx = 0, ly = finalY[0] * g->floor1_multiplier; for (q=1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); if (finalY[j] >= 0) #else if (step2_flag[j]) @@ -3089,16 +3092,16 @@ static int stbv_do_floor(stbv_vorb *f, StbvMapping *map, int i, int n, float *ta int hy = finalY[j] * g->floor1_multiplier; int hx = g->Xlist[j]; if (lx != hx) - stbv_draw_line(target, lx,ly, hx,hy, n2); - STBV_CHECK(f); + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); lx = hx, ly = hy; } } if (lx < n2) { - // optimization of: stbv_draw_line(target, lx,ly, n,ly, n2); + // optimization of: draw_line(target, lx,ly, n,ly, n2); for (j=lx; j < n2; ++j) - STBV_LINE_OP(target[j], stbv_inverse_db_table[ly]); - STBV_CHECK(f); + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); } } return TRUE; @@ -3118,36 +3121,36 @@ static int stbv_do_floor(stbv_vorb *f, StbvMapping *map, int i, int n, float *ta // has to be the same as frame N+1's left_end-left_start (which they are by // construction) -static int stbv_vorbis_decode_initial(stbv_vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { - StbvMode *m; + Mode *m; int i, n, prev, next, window_center; f->channel_buffer_start = f->channel_buffer_end = 0; retry: if (f->eof) return FALSE; - if (!stbv_maybe_start_packet(f)) + if (!maybe_start_packet(f)) return FALSE; // check packet type - if (stbv_get_bits(f,1) != 0) { - if (STBV_IS_PUSH_MODE(f)) - return stbv_error(f,VORBIS_bad_packet_type); - while (STBV_EOP != stbv_get8_packet(f)); + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); goto retry; } if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - i = stbv_get_bits(f, stbv_ilog(f->mode_count-1)); - if (i == STBV_EOP) return FALSE; + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; if (i >= f->mode_count) return FALSE; *mode = i; m = f->mode_config + i; if (m->blockflag) { n = f->blocksize_1; - prev = stbv_get_bits(f,1); - next = stbv_get_bits(f,1); + prev = get_bits(f,1); + next = get_bits(f,1); } else { prev = next = 0; n = f->blocksize_0; @@ -3174,40 +3177,41 @@ static int stbv_vorbis_decode_initial(stbv_vorb *f, int *p_left_start, int *p_le return TRUE; } -static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) { - StbvMapping *map; + Mapping *map; int i,j,k,n,n2; int zero_channel[256]; int really_zero_channel[256]; // WINDOWING + STBV_NOTUSED(left_end); n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; // FLOORS n2 = n >> 1; - STBV_CHECK(f); + CHECK(f); for (i=0; i < f->channels; ++i) { int s = map->chan[i].mux, floor; zero_channel[i] = FALSE; floor = map->submap_floor[s]; if (f->floor_types[floor] == 0) { - return stbv_error(f, VORBIS_invalid_stream); + return error(f, VORBIS_invalid_stream); } else { - StbvFloor1 *g = &f->floor_config[floor].floor1; - if (stbv_get_bits(f, 1)) { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { short *finalY; - stbv_uint8 step2_flag[256]; + uint8 step2_flag[256]; static int range_list[4] = { 256, 128, 86, 64 }; int range = range_list[g->floor1_multiplier-1]; int offset = 2; finalY = f->finalY[i]; - finalY[0] = stbv_get_bits(f, stbv_ilog(range)-1); - finalY[1] = stbv_get_bits(f, stbv_ilog(range)-1); + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); for (j=0; j < g->partitions; ++j) { int pclass = g->partition_class_list[j]; int cdim = g->class_dimensions[pclass]; @@ -3215,29 +3219,29 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i int csub = (1 << cbits)-1; int cval = 0; if (cbits) { - StbvCodebook *c = f->codebooks + g->class_masterbooks[pclass]; - STBV_DECODE(cval,f,c); + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); } for (k=0; k < cdim; ++k) { int book = g->subclass_books[pclass][cval & csub]; cval = cval >> cbits; if (book >= 0) { int temp; - StbvCodebook *c = f->codebooks + book; - STBV_DECODE(temp,f,c); + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); finalY[offset++] = temp; } else finalY[offset++] = 0; } } - if (f->valid_bits == STBV_INVALID_BITS) goto error; // behavior according to spec + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec step2_flag[0] = step2_flag[1] = 1; for (j=2; j < g->values; ++j) { int low, high, pred, highroom, lowroom, room, val; - low = g->stbv_neighbors[j][0]; - high = g->stbv_neighbors[j][1]; - //stbv_neighbors(g->Xlist, j, &low, &high); - pred = stbv_predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); val = finalY[j]; highroom = range - pred; lowroom = pred; @@ -3265,7 +3269,7 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i } #ifdef STB_VORBIS_NO_DEFER_FLOOR - stbv_do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); #else // defer final floor computation until _after_ residue for (j=0; j < g->values; ++j) { @@ -3282,7 +3286,7 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i // at this point we've decoded the floor into buffer } } - STBV_CHECK(f); + CHECK(f); // at this point we've decoded all floors if (f->alloc.alloc_buffer) @@ -3295,12 +3299,12 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; } - STBV_CHECK(f); -// RESIDUE STBV_DECODE + CHECK(f); +// RESIDUE DECODE for (i=0; i < map->submaps; ++i) { float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; int r; - stbv_uint8 do_not_decode[256]; + uint8 do_not_decode[256]; int ch = 0; for (j=0; j < f->channels; ++j) { if (map->chan[j].mux == i) { @@ -3315,12 +3319,12 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i } } r = map->submap_residue[i]; - stbv_decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); } if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - STBV_CHECK(f); + CHECK(f); // INVERSE COUPLING for (i = map->coupling_steps-1; i >= 0; --i) { @@ -3343,7 +3347,7 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i a[j] = a2; } } - STBV_CHECK(f); + CHECK(f); // finish decoding the floors #ifndef STB_VORBIS_NO_DEFER_FLOOR @@ -3351,7 +3355,7 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i if (really_zero_channel[i]) { memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); } else { - stbv_do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); } } #else @@ -3366,21 +3370,21 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i #endif // INVERSE MDCT - STBV_CHECK(f); + CHECK(f); for (i=0; i < f->channels; ++i) - stbv_inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); - STBV_CHECK(f); + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); // this shouldn't be necessary, unless we exited on an error // and want to flush to get to the next packet - stbv_flush_packet(f); + flush_packet(f); if (f->first_decode) { // assume we start so first non-discarded sample is sample 0 // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; @@ -3408,8 +3412,8 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i // check if we have ogg information about the sample # for this packet if (f->last_seg_which == f->end_seg_with_known_loc) { // if we have a valid current loc, and this is final: - if (f->current_loc_valid && (f->page_flag & STBV_PAGEFLAG_last_page)) { - stbv_uint32 current_end = f->known_loc_for_packet; + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; // then let's infer the size of the (probably) short final frame if (current_end < f->current_loc + (right_end-left_start)) { if (current_end < f->current_loc) { @@ -3437,19 +3441,19 @@ static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, i if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); *len = right_end; // ignore samples after the window goes to 0 - STBV_CHECK(f); + CHECK(f); return TRUE; } -static int stbv_vorbis_decode_packet(stbv_vorb *f, int *len, int *p_left, int *p_right) +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) { int mode, left_end, right_end; - if (!stbv_vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; - return stbv_vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); } -static int stbv_vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) { int prev,i,j; // we use right&left (the start of the right- and left-window sin()-regions) @@ -3463,7 +3467,8 @@ static int stbv_vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) // mixin from previous window if (f->previous_length) { int i,j, n = f->previous_length; - float *w = stbv_get_window(f, n); + float *w = get_window(f, n); + if (w == NULL) return 0; for (i=0; i < f->channels; ++i) { for (j=0; j < n; ++j) f->channel_buffers[i][left+j] = @@ -3501,28 +3506,28 @@ static int stbv_vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) return right - left; } -static int stbv_vorbis_pump_first_frame(stb_vorbis *f) +static int vorbis_pump_first_frame(stb_vorbis *f) { int len, right, left, res; - res = stbv_vorbis_decode_packet(f, &len, &left, &right); + res = vorbis_decode_packet(f, &len, &left, &right); if (res) - stbv_vorbis_finish_frame(f, len, left, right); + vorbis_finish_frame(f, len, left, right); return res; } #ifndef STB_VORBIS_NO_PUSHDATA_API -static int stbv_is_whole_packet_present(stb_vorbis *f, int end_page) +static int is_whole_packet_present(stb_vorbis *f) { // make sure that we have the packet available before continuing... // this requires a full ogg parse, but we know we can fetch from f->stream // instead of coding this out explicitly, we could save the current read state, - // read the next packet with stbv_get8() until end-of-packet, check f->eof, then + // read the next packet with get8() until end-of-packet, check f->eof, then // reset the state? but that would be slower, esp. since we'd have over 256 bytes // of state to restore (primarily the page segment table) int s = f->next_seg, first = TRUE; - stbv_uint8 *p = f->stream; + uint8 *p = f->stream; if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag for (; s < f->segment_count; ++s) { @@ -3531,112 +3536,166 @@ static int stbv_is_whole_packet_present(stb_vorbis *f, int end_page) break; } // either this continues, or it ends it... - if (end_page) - if (s < f->segment_count-1) return stbv_error(f, VORBIS_invalid_stream); if (s == f->segment_count) s = -1; // set 'crosses page' flag - if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } for (; s == -1;) { - stbv_uint8 *q; + uint8 *q; int n; // check that we have the page header ready - if (p + 26 >= f->stream_end) return stbv_error(f, VORBIS_need_more_data); + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); // validate the page - if (memcmp(p, stbv_ogg_page_header, 4)) return stbv_error(f, VORBIS_invalid_stream); - if (p[4] != 0) return stbv_error(f, VORBIS_invalid_stream); + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); if (first) { // the first segment must NOT have 'continued_packet', later ones MUST if (f->previous_length) - if ((p[5] & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_invalid_stream); + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); // if no previous length, we're resynching, so we can come in on a continued-packet, // which we'll just drop } else { - if (!(p[5] & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_invalid_stream); + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); } n = p[26]; // segment counts q = p+27; // q points to segment table p = q + n; // advance past header // make sure we've read the segment table - if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + if (p > f->stream_end) return error(f, VORBIS_need_more_data); for (s=0; s < n; ++s) { p += q[s]; if (q[s] < 255) break; } - if (end_page) - if (s < n-1) return stbv_error(f, VORBIS_invalid_stream); if (s == n) s = -1; // set 'crosses page' flag - if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } return TRUE; } #endif // !STB_VORBIS_NO_PUSHDATA_API -static int stbv_start_decoder(stbv_vorb *f) +static int start_decoder(vorb *f) { - stbv_uint8 header[6], x,y; + uint8 header[6], x,y; int len,i,j,k, max_submaps = 0; int longest_floorlist=0; // first page, first packet + f->first_decode = TRUE; - if (!stbv_start_page(f)) return FALSE; + if (!start_page(f)) return FALSE; // validate page flag - if (!(f->page_flag & STBV_PAGEFLAG_first_page)) return stbv_error(f, VORBIS_invalid_first_page); - if (f->page_flag & STBV_PAGEFLAG_last_page) return stbv_error(f, VORBIS_invalid_first_page); - if (f->page_flag & STBV_PAGEFLAG_continued_packet) return stbv_error(f, VORBIS_invalid_first_page); + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); // check for expected packet length - if (f->segment_count != 1) return stbv_error(f, VORBIS_invalid_first_page); - if (f->segments[0] != 30) return stbv_error(f, VORBIS_invalid_first_page); + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + // read packet // check packet header - if (stbv_get8(f) != STBV_VORBIS_packet_id) return stbv_error(f, VORBIS_invalid_first_page); - if (!stbv_getn(f, header, 6)) return stbv_error(f, VORBIS_unexpected_eof); - if (!stbv_vorbis_validate(header)) return stbv_error(f, VORBIS_invalid_first_page); + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); // vorbis_version - if (stbv_get32(f) != 0) return stbv_error(f, VORBIS_invalid_first_page); - f->channels = stbv_get8(f); if (!f->channels) return stbv_error(f, VORBIS_invalid_first_page); - if (f->channels > STB_VORBIS_MAX_CHANNELS) return stbv_error(f, VORBIS_too_many_channels); - f->sample_rate = stbv_get32(f); if (!f->sample_rate) return stbv_error(f, VORBIS_invalid_first_page); - stbv_get32(f); // bitrate_maximum - stbv_get32(f); // bitrate_nominal - stbv_get32(f); // bitrate_minimum - x = stbv_get8(f); + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); { int log0,log1; log0 = x & 15; log1 = x >> 4; f->blocksize_0 = 1 << log0; f->blocksize_1 = 1 << log1; - if (log0 < 6 || log0 > 13) return stbv_error(f, VORBIS_invalid_setup); - if (log1 < 6 || log1 > 13) return stbv_error(f, VORBIS_invalid_setup); - if (log0 > log1) return stbv_error(f, VORBIS_invalid_setup); + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); } // framing_flag - x = stbv_get8(f); - if (!(x & 1)) return stbv_error(f, VORBIS_invalid_first_page); + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); // second packet! - if (!stbv_start_page(f)) return FALSE; + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + //file vendor + len = get32_packet(f); + f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for(i=0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + //user comments + f->comment_list_length = get32_packet(f); + f->comment_list = NULL; + if (f->comment_list_length > 0) + { + f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } + + for(i=0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for(j=0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; - if (!stbv_start_packet(f)) return FALSE; do { - len = stbv_next_segment(f); - stbv_skip(f, len); + len = next_segment(f); + skip(f, len); f->bytes_in_seg = 0; } while (len); // third packet! - if (!stbv_start_packet(f)) return FALSE; + if (!start_packet(f)) return FALSE; #ifndef STB_VORBIS_NO_PUSHDATA_API - if (STBV_IS_PUSH_MODE(f)) { - if (!stbv_is_whole_packet_present(f, TRUE)) { + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f)) { // convert error in ogg header to write type if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; @@ -3645,64 +3704,65 @@ static int stbv_start_decoder(stbv_vorb *f) } #endif - stbv_crc32_init(); // always init it, to avoid multithread race conditions + crc32_init(); // always init it, to avoid multithread race conditions - if (stbv_get8_packet(f) != STBV_VORBIS_packet_setup) return stbv_error(f, VORBIS_invalid_setup); - for (i=0; i < 6; ++i) header[i] = stbv_get8_packet(f); - if (!stbv_vorbis_validate(header)) return stbv_error(f, VORBIS_invalid_setup); + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); // codebooks - f->codebook_count = stbv_get_bits(f,8) + 1; - f->codebooks = (StbvCodebook *) stbv_setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); - if (f->codebooks == NULL) return stbv_error(f, VORBIS_outofmem); + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); for (i=0; i < f->codebook_count; ++i) { - stbv_uint32 *values; + uint32 *values; int ordered, sorted_count; int total=0; - stbv_uint8 *lengths; - StbvCodebook *c = f->codebooks+i; - STBV_CHECK(f); - x = stbv_get_bits(f, 8); if (x != 0x42) return stbv_error(f, VORBIS_invalid_setup); - x = stbv_get_bits(f, 8); if (x != 0x43) return stbv_error(f, VORBIS_invalid_setup); - x = stbv_get_bits(f, 8); if (x != 0x56) return stbv_error(f, VORBIS_invalid_setup); - x = stbv_get_bits(f, 8); - c->dimensions = (stbv_get_bits(f, 8)<<8) + x; - x = stbv_get_bits(f, 8); - y = stbv_get_bits(f, 8); - c->entries = (stbv_get_bits(f, 8)<<16) + (y<<8) + x; - ordered = stbv_get_bits(f,1); - c->sparse = ordered ? 0 : stbv_get_bits(f,1); + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); - if (c->dimensions == 0 && c->entries != 0) return stbv_error(f, VORBIS_invalid_setup); + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); if (c->sparse) - lengths = (stbv_uint8 *) stbv_setup_temp_malloc(f, c->entries); + lengths = (uint8 *) setup_temp_malloc(f, c->entries); else - lengths = c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->entries); + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); - if (!lengths) return stbv_error(f, VORBIS_outofmem); + if (!lengths) return error(f, VORBIS_outofmem); if (ordered) { int current_entry = 0; - int current_length = stbv_get_bits(f,5) + 1; + int current_length = get_bits(f,5) + 1; while (current_entry < c->entries) { int limit = c->entries - current_entry; - int n = stbv_get_bits(f, stbv_ilog(limit)); - if (current_entry + n > (int) c->entries) { return stbv_error(f, VORBIS_invalid_setup); } + int n = get_bits(f, ilog(limit)); + if (current_length >= 32) return error(f, VORBIS_invalid_setup); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } memset(lengths + current_entry, current_length, n); current_entry += n; ++current_length; } } else { for (j=0; j < c->entries; ++j) { - int present = c->sparse ? stbv_get_bits(f,1) : 1; + int present = c->sparse ? get_bits(f,1) : 1; if (present) { - lengths[j] = stbv_get_bits(f, 5) + 1; + lengths[j] = get_bits(f, 5) + 1; ++total; if (lengths[j] == 32) - return stbv_error(f, VORBIS_invalid_setup); + return error(f, VORBIS_invalid_setup); } else { lengths[j] = NO_CODE; } @@ -3714,10 +3774,10 @@ static int stbv_start_decoder(stbv_vorb *f) if (c->entries > (int) f->setup_temp_memory_required) f->setup_temp_memory_required = c->entries; - c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->entries); - if (c->codeword_lengths == NULL) return stbv_error(f, VORBIS_outofmem); + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); memcpy(c->codeword_lengths, lengths, c->entries); - stbv_setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! lengths = c->codeword_lengths; c->sparse = 0; } @@ -3737,72 +3797,74 @@ static int stbv_start_decoder(stbv_vorb *f) c->sorted_entries = sorted_count; values = NULL; - STBV_CHECK(f); + CHECK(f); if (!c->sparse) { - c->codewords = (stbv_uint32 *) stbv_setup_malloc(f, sizeof(c->codewords[0]) * c->entries); - if (!c->codewords) return stbv_error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); } else { unsigned int size; if (c->sorted_entries) { - c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->sorted_entries); - if (!c->codeword_lengths) return stbv_error(f, VORBIS_outofmem); - c->codewords = (stbv_uint32 *) stbv_setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); - if (!c->codewords) return stbv_error(f, VORBIS_outofmem); - values = (stbv_uint32 *) stbv_setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); - if (!values) return stbv_error(f, VORBIS_outofmem); + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); } size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; if (size > f->setup_temp_memory_required) f->setup_temp_memory_required = size; } - if (!stbv_compute_codewords(c, lengths, c->entries, values)) { - if (c->sparse) stbv_setup_temp_free(f, values, 0); - return stbv_error(f, VORBIS_invalid_setup); + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); } if (c->sorted_entries) { // allocate an extra slot for sentinels - c->sorted_codewords = (stbv_uint32 *) stbv_setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); - if (c->sorted_codewords == NULL) return stbv_error(f, VORBIS_outofmem); + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); // allocate an extra slot at the front so that c->sorted_values[-1] is defined // so that we can catch that case without an extra if - c->sorted_values = ( int *) stbv_setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); - if (c->sorted_values == NULL) return stbv_error(f, VORBIS_outofmem); + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); ++c->sorted_values; c->sorted_values[-1] = -1; - stbv_compute_sorted_huffman(c, lengths, values); + compute_sorted_huffman(c, lengths, values); } if (c->sparse) { - stbv_setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); - stbv_setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); - stbv_setup_temp_free(f, lengths, c->entries); + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); c->codewords = NULL; } - stbv_compute_accelerated_huffman(c); + compute_accelerated_huffman(c); - STBV_CHECK(f); - c->lookup_type = stbv_get_bits(f, 4); - if (c->lookup_type > 2) return stbv_error(f, VORBIS_invalid_setup); + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); if (c->lookup_type > 0) { - stbv_uint16 *mults; - c->minimum_value = stbv_float32_unpack(stbv_get_bits(f, 32)); - c->delta_value = stbv_float32_unpack(stbv_get_bits(f, 32)); - c->value_bits = stbv_get_bits(f, 4)+1; - c->sequence_p = stbv_get_bits(f,1); + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); if (c->lookup_type == 1) { - c->lookup_values = stbv_lookup1_values(c->entries, c->dimensions); + int values = lookup1_values(c->entries, c->dimensions); + if (values < 0) return error(f, VORBIS_invalid_setup); + c->lookup_values = (uint32) values; } else { c->lookup_values = c->entries * c->dimensions; } - if (c->lookup_values == 0) return stbv_error(f, VORBIS_invalid_setup); - mults = (stbv_uint16 *) stbv_setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); - if (mults == NULL) return stbv_error(f, VORBIS_outofmem); + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); for (j=0; j < (int) c->lookup_values; ++j) { - int q = stbv_get_bits(f, c->value_bits); - if (q == STBV_EOP) { stbv_setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_invalid_setup); } + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } mults[j] = q; } @@ -3812,26 +3874,25 @@ static int stbv_start_decoder(stbv_vorb *f) float last=0; // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop if (sparse) { - if (c->sorted_entries == 0) goto stbv_skip; - c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); } else - c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); - if (c->multiplicands == NULL) { stbv_setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_outofmem); } + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } len = sparse ? c->sorted_entries : c->entries; for (j=0; j < len; ++j) { unsigned int z = sparse ? c->sorted_values[j] : j; unsigned int div=1; for (k=0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off]*c->delta_value + c->minimum_value + last; + float val = mults[off]*c->delta_value + c->minimum_value + last; c->multiplicands[j*c->dimensions + k] = val; if (c->sequence_p) last = val; if (k+1 < c->dimensions) { if (div > UINT_MAX / (unsigned int) c->lookup_values) { - stbv_setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); - return stbv_error(f, VORBIS_invalid_setup); + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); } div *= c->lookup_values; } @@ -3843,9 +3904,9 @@ static int stbv_start_decoder(stbv_vorb *f) #endif { float last=0; - STBV_CHECK(f); - c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); - if (c->multiplicands == NULL) { stbv_setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_outofmem); } + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } for (j=0; j < (int) c->lookup_values; ++j) { float val = mults[j] * c->delta_value + c->minimum_value + last; c->multiplicands[j] = val; @@ -3854,72 +3915,72 @@ static int stbv_start_decoder(stbv_vorb *f) } } #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - stbv_skip:; + skip:; #endif - stbv_setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); - STBV_CHECK(f); + CHECK(f); } - STBV_CHECK(f); + CHECK(f); } // time domain transfers (notused) - x = stbv_get_bits(f, 6) + 1; + x = get_bits(f, 6) + 1; for (i=0; i < x; ++i) { - stbv_uint32 z = stbv_get_bits(f, 16); - if (z != 0) return stbv_error(f, VORBIS_invalid_setup); + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); } // Floors - f->floor_count = stbv_get_bits(f, 6)+1; - f->floor_config = (StbvFloor *) stbv_setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); - if (f->floor_config == NULL) return stbv_error(f, VORBIS_outofmem); + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); for (i=0; i < f->floor_count; ++i) { - f->floor_types[i] = stbv_get_bits(f, 16); - if (f->floor_types[i] > 1) return stbv_error(f, VORBIS_invalid_setup); + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); if (f->floor_types[i] == 0) { - StbvFloor0 *g = &f->floor_config[i].floor0; - g->order = stbv_get_bits(f,8); - g->rate = stbv_get_bits(f,16); - g->bark_map_size = stbv_get_bits(f,16); - g->amplitude_bits = stbv_get_bits(f,6); - g->amplitude_offset = stbv_get_bits(f,8); - g->number_of_books = stbv_get_bits(f,4) + 1; + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; for (j=0; j < g->number_of_books; ++j) - g->book_list[j] = stbv_get_bits(f,8); - return stbv_error(f, VORBIS_feature_not_supported); + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); } else { - stbv_floor_ordering p[31*8+2]; - StbvFloor1 *g = &f->floor_config[i].floor1; - int max_class = -1; - g->partitions = stbv_get_bits(f, 5); + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); for (j=0; j < g->partitions; ++j) { - g->partition_class_list[j] = stbv_get_bits(f, 4); + g->partition_class_list[j] = get_bits(f, 4); if (g->partition_class_list[j] > max_class) max_class = g->partition_class_list[j]; } for (j=0; j <= max_class; ++j) { - g->class_dimensions[j] = stbv_get_bits(f, 3)+1; - g->class_subclasses[j] = stbv_get_bits(f, 2); + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); if (g->class_subclasses[j]) { - g->class_masterbooks[j] = stbv_get_bits(f, 8); - if (g->class_masterbooks[j] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } for (k=0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = stbv_get_bits(f,8)-1; - if (g->subclass_books[j][k] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } } - g->floor1_multiplier = stbv_get_bits(f,2)+1; - g->rangebits = stbv_get_bits(f,4); + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); g->Xlist[0] = 0; g->Xlist[1] = 1 << g->rangebits; g->values = 2; for (j=0; j < g->partitions; ++j) { int c = g->partition_class_list[j]; for (k=0; k < g->class_dimensions[c]; ++k) { - g->Xlist[g->values] = stbv_get_bits(f, g->rangebits); + g->Xlist[g->values] = get_bits(f, g->rangebits); ++g->values; } } @@ -3928,15 +3989,18 @@ static int stbv_start_decoder(stbv_vorb *f) p[j].x = g->Xlist[j]; p[j].id = j; } - qsort(p, g->values, sizeof(p[0]), stbv_point_compare); + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values-1; ++j) + if (p[j].x == p[j+1].x) + return error(f, VORBIS_invalid_setup); for (j=0; j < g->values; ++j) - g->sorted_order[j] = (stbv_uint8) p[j].id; - // precompute the stbv_neighbors + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors for (j=2; j < g->values; ++j) { - int low,hi; - stbv_neighbors(g->Xlist, j, &low,&hi); - g->stbv_neighbors[j][0] = low; - g->stbv_neighbors[j][1] = hi; + int low = 0,hi = 0; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; } if (g->values > longest_floorlist) @@ -3944,37 +4008,37 @@ static int stbv_start_decoder(stbv_vorb *f) } } - // StbvResidue - f->residue_count = stbv_get_bits(f, 6)+1; - f->residue_config = (StbvResidue *) stbv_setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); - if (f->residue_config == NULL) return stbv_error(f, VORBIS_outofmem); + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); for (i=0; i < f->residue_count; ++i) { - stbv_uint8 residue_cascade[64]; - StbvResidue *r = f->residue_config+i; - f->residue_types[i] = stbv_get_bits(f, 16); - if (f->residue_types[i] > 2) return stbv_error(f, VORBIS_invalid_setup); - r->begin = stbv_get_bits(f, 24); - r->end = stbv_get_bits(f, 24); - if (r->end < r->begin) return stbv_error(f, VORBIS_invalid_setup); - r->part_size = stbv_get_bits(f,24)+1; - r->classifications = stbv_get_bits(f,6)+1; - r->classbook = stbv_get_bits(f,8); - if (r->classbook >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); for (j=0; j < r->classifications; ++j) { - stbv_uint8 high_bits=0; - stbv_uint8 low_bits=stbv_get_bits(f,3); - if (stbv_get_bits(f,1)) - high_bits = stbv_get_bits(f,5); + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); residue_cascade[j] = high_bits*8 + low_bits; } - r->residue_books = (short (*)[8]) stbv_setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); - if (r->residue_books == NULL) return stbv_error(f, VORBIS_outofmem); + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); for (j=0; j < r->classifications; ++j) { for (k=0; k < 8; ++k) { if (residue_cascade[j] & (1 << k)) { - r->residue_books[j][k] = stbv_get_bits(f, 8); - if (r->residue_books[j][k] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } else { r->residue_books[j][k] = -1; } @@ -3982,14 +4046,14 @@ static int stbv_start_decoder(stbv_vorb *f) } // precompute the classifications[] array to avoid inner-loop mod/divide // call it 'classdata' since we already have r->classifications - r->classdata = (stbv_uint8 **) stbv_setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); - if (!r->classdata) return stbv_error(f, VORBIS_outofmem); + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); for (j=0; j < f->codebooks[r->classbook].entries; ++j) { int classwords = f->codebooks[r->classbook].dimensions; int temp = j; - r->classdata[j] = (stbv_uint8 *) stbv_setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); - if (r->classdata[j] == NULL) return stbv_error(f, VORBIS_outofmem); + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); for (k=classwords-1; k >= 0; --k) { r->classdata[j][k] = temp % r->classifications; temp /= r->classifications; @@ -3997,40 +4061,41 @@ static int stbv_start_decoder(stbv_vorb *f) } } - f->mapping_count = stbv_get_bits(f,6)+1; - f->mapping = (StbvMapping *) stbv_setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); - if (f->mapping == NULL) return stbv_error(f, VORBIS_outofmem); + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); for (i=0; i < f->mapping_count; ++i) { - StbvMapping *m = f->mapping + i; - int mapping_type = stbv_get_bits(f,16); - if (mapping_type != 0) return stbv_error(f, VORBIS_invalid_setup); - m->chan = (StbvMappingChannel *) stbv_setup_malloc(f, f->channels * sizeof(*m->chan)); - if (m->chan == NULL) return stbv_error(f, VORBIS_outofmem); - if (stbv_get_bits(f,1)) - m->submaps = stbv_get_bits(f,4)+1; + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; else m->submaps = 1; if (m->submaps > max_submaps) max_submaps = m->submaps; - if (stbv_get_bits(f,1)) { - m->coupling_steps = stbv_get_bits(f,8)+1; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); for (k=0; k < m->coupling_steps; ++k) { - m->chan[k].magnitude = stbv_get_bits(f, stbv_ilog(f->channels-1)); - m->chan[k].angle = stbv_get_bits(f, stbv_ilog(f->channels-1)); - if (m->chan[k].magnitude >= f->channels) return stbv_error(f, VORBIS_invalid_setup); - if (m->chan[k].angle >= f->channels) return stbv_error(f, VORBIS_invalid_setup); - if (m->chan[k].magnitude == m->chan[k].angle) return stbv_error(f, VORBIS_invalid_setup); + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); } } else m->coupling_steps = 0; // reserved field - if (stbv_get_bits(f,2)) return stbv_error(f, VORBIS_invalid_setup); + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); if (m->submaps > 1) { for (j=0; j < f->channels; ++j) { - m->chan[j].mux = stbv_get_bits(f, 4); - if (m->chan[j].mux >= m->submaps) return stbv_error(f, VORBIS_invalid_setup); + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); } } else // @SPECIFICATION: this case is missing from the spec @@ -4038,64 +4103,64 @@ static int stbv_start_decoder(stbv_vorb *f) m->chan[j].mux = 0; for (j=0; j < m->submaps; ++j) { - stbv_get_bits(f,8); // discard - m->submap_floor[j] = stbv_get_bits(f,8); - m->submap_residue[j] = stbv_get_bits(f,8); - if (m->submap_floor[j] >= f->floor_count) return stbv_error(f, VORBIS_invalid_setup); - if (m->submap_residue[j] >= f->residue_count) return stbv_error(f, VORBIS_invalid_setup); + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); } } // Modes - f->mode_count = stbv_get_bits(f, 6)+1; + f->mode_count = get_bits(f, 6)+1; for (i=0; i < f->mode_count; ++i) { - StbvMode *m = f->mode_config+i; - m->blockflag = stbv_get_bits(f,1); - m->windowtype = stbv_get_bits(f,16); - m->transformtype = stbv_get_bits(f,16); - m->mapping = stbv_get_bits(f,8); - if (m->windowtype != 0) return stbv_error(f, VORBIS_invalid_setup); - if (m->transformtype != 0) return stbv_error(f, VORBIS_invalid_setup); - if (m->mapping >= f->mapping_count) return stbv_error(f, VORBIS_invalid_setup); + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); } - stbv_flush_packet(f); + flush_packet(f); f->previous_length = 0; for (i=0; i < f->channels; ++i) { - f->channel_buffers[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1); - f->previous_window[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1/2); - f->finalY[i] = (stbv_int16 *) stbv_setup_malloc(f, sizeof(stbv_int16) * longest_floorlist); - if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return stbv_error(f, VORBIS_outofmem); + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); #ifdef STB_VORBIS_NO_DEFER_FLOOR - f->floor_buffers[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1/2); - if (f->floor_buffers[i] == NULL) return stbv_error(f, VORBIS_outofmem); + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); #endif } - if (!stbv_init_blocksize(f, 0, f->blocksize_0)) return FALSE; - if (!stbv_init_blocksize(f, 1, f->blocksize_1)) return FALSE; + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; f->blocksize[0] = f->blocksize_0; f->blocksize[1] = f->blocksize_1; #ifdef STB_VORBIS_DIVIDE_TABLE - if (stbv_integer_divide_table[1][1]==0) - for (i=0; i < STBV_DIVTAB_NUMER; ++i) - for (j=1; j < STBV_DIVTAB_DENOM; ++j) - stbv_integer_divide_table[i][j] = i / j; + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; #endif // compute how much temporary memory is needed // 1. { - stbv_uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); - stbv_uint32 classify_mem; + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; int i,max_part_read=0; for (i=0; i < f->residue_count; ++i) { - StbvResidue *r = f->residue_config + i; + Residue *r = f->residue_config + i; unsigned int actual_size = f->blocksize_1 / 2; unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; @@ -4105,7 +4170,7 @@ static int stbv_start_decoder(stbv_vorb *f) max_part_read = part_read; } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(stbv_uint8 *)); + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); #else classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); #endif @@ -4117,89 +4182,105 @@ static int stbv_start_decoder(stbv_vorb *f) f->temp_memory_required = imdct_mem; } - f->first_decode = TRUE; if (f->alloc.alloc_buffer) { assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); // check if there's enough temp memory so we don't error later if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) - return stbv_error(f, VORBIS_outofmem); + return error(f, VORBIS_outofmem); } - f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } return TRUE; } -static void stbv_vorbis_deinit(stb_vorbis *p) +static void vorbis_deinit(stb_vorbis *p) { int i,j; + + setup_free(p, p->vendor); + for (i=0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); + if (p->residue_config) { for (i=0; i < p->residue_count; ++i) { - StbvResidue *r = p->residue_config+i; + Residue *r = p->residue_config+i; if (r->classdata) { for (j=0; j < p->codebooks[r->classbook].entries; ++j) - stbv_setup_free(p, r->classdata[j]); - stbv_setup_free(p, r->classdata); + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); } - stbv_setup_free(p, r->residue_books); + setup_free(p, r->residue_books); } } if (p->codebooks) { - STBV_CHECK(p); + CHECK(p); for (i=0; i < p->codebook_count; ++i) { - StbvCodebook *c = p->codebooks + i; - stbv_setup_free(p, c->codeword_lengths); - stbv_setup_free(p, c->multiplicands); - stbv_setup_free(p, c->codewords); - stbv_setup_free(p, c->sorted_codewords); + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); // c->sorted_values[-1] is the first entry in the array - stbv_setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); } - stbv_setup_free(p, p->codebooks); + setup_free(p, p->codebooks); } - stbv_setup_free(p, p->floor_config); - stbv_setup_free(p, p->residue_config); + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); if (p->mapping) { for (i=0; i < p->mapping_count; ++i) - stbv_setup_free(p, p->mapping[i].chan); - stbv_setup_free(p, p->mapping); + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); } - STBV_CHECK(p); + CHECK(p); for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { - stbv_setup_free(p, p->channel_buffers[i]); - stbv_setup_free(p, p->previous_window[i]); + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); #ifdef STB_VORBIS_NO_DEFER_FLOOR - stbv_setup_free(p, p->floor_buffers[i]); + setup_free(p, p->floor_buffers[i]); #endif - stbv_setup_free(p, p->finalY[i]); + setup_free(p, p->finalY[i]); } for (i=0; i < 2; ++i) { - stbv_setup_free(p, p->A[i]); - stbv_setup_free(p, p->B[i]); - stbv_setup_free(p, p->C[i]); - stbv_setup_free(p, p->window[i]); - stbv_setup_free(p, p->stbv_bit_reverse[i]); + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); } #ifndef STB_VORBIS_NO_STDIO if (p->close_on_free) fclose(p->f); #endif } -STBVDEF void stb_vorbis_close(stb_vorbis *p) +void stb_vorbis_close(stb_vorbis *p) { if (p == NULL) return; - stbv_vorbis_deinit(p); - stbv_setup_free(p,p); + vorbis_deinit(p); + setup_free(p,p); } -static void stbv_vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) { memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start if (z) { p->alloc = *z; - p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->alloc.alloc_buffer_length_in_bytes &= ~7; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; @@ -4213,7 +4294,7 @@ static void stbv_vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) #endif } -STBVDEF int stb_vorbis_get_sample_offset(stb_vorbis *f) +int stb_vorbis_get_sample_offset(stb_vorbis *f) { if (f->current_loc_valid) return f->current_loc; @@ -4221,7 +4302,7 @@ STBVDEF int stb_vorbis_get_sample_offset(stb_vorbis *f) return -1; } -STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) { stb_vorbis_info d; d.channels = f->channels; @@ -4233,22 +4314,31 @@ STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) return d; } -STBVDEF int stb_vorbis_get_error(stb_vorbis *f) +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) +{ + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) { int e = f->error; f->error = VORBIS__no_error; return e; } -static stb_vorbis * stbv_vorbis_alloc(stb_vorbis *f) +static stb_vorbis * vorbis_alloc(stb_vorbis *f) { - stb_vorbis *p = (stb_vorbis *) stbv_setup_malloc(f, sizeof(*p)); + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); return p; } #ifndef STB_VORBIS_NO_PUSHDATA_API -STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f) +void stb_vorbis_flush_pushdata(stb_vorbis *f) { f->previous_length = 0; f->page_crc_tests = 0; @@ -4260,7 +4350,7 @@ STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f) f->channel_buffer_end = 0; } -static int stbv_vorbis_search_for_page_pushdata(stbv_vorb *f, stbv_uint8 *data, int data_len) +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) { int i,n; for (i=0; i < f->page_crc_tests; ++i) @@ -4274,9 +4364,9 @@ static int stbv_vorbis_search_for_page_pushdata(stbv_vorb *f, stbv_uint8 *data, // one that straddles a boundary for (i=0; i < data_len; ++i) { if (data[i] == 0x4f) { - if (0==memcmp(data+i, stbv_ogg_page_header, 4)) { + if (0==memcmp(data+i, ogg_page_header, 4)) { int j,len; - stbv_uint32 crc; + uint32 crc; // make sure we have the whole page header if (i+26 >= data_len || i+27+data[i+26] >= data_len) { // only read up to this page start, so hopefully we'll @@ -4291,10 +4381,10 @@ static int stbv_vorbis_search_for_page_pushdata(stbv_vorb *f, stbv_uint8 *data, // scan everything up to the embedded crc (which we must 0) crc = 0; for (j=0; j < 22; ++j) - crc = stbv_crc32_update(crc, data[i+j]); + crc = crc32_update(crc, data[i+j]); // now process 4 0-bytes for ( ; j < 26; ++j) - crc = stbv_crc32_update(crc, 0); + crc = crc32_update(crc, 0); // len is the total number of bytes we need to scan n = f->page_crc_tests++; f->scan[n].bytes_left = len-j; @@ -4316,7 +4406,7 @@ static int stbv_vorbis_search_for_page_pushdata(stbv_vorb *f, stbv_uint8 *data, } for (i=0; i < f->page_crc_tests;) { - stbv_uint32 crc; + uint32 crc; int j; int n = f->scan[i].bytes_done; int m = f->scan[i].bytes_left; @@ -4324,7 +4414,7 @@ static int stbv_vorbis_search_for_page_pushdata(stbv_vorb *f, stbv_uint8 *data, // m is the bytes to scan in the current chunk crc = f->scan[i].crc_so_far; for (j=0; j < m; ++j) - crc = stbv_crc32_update(crc, data[n+j]); + crc = crc32_update(crc, data[n+j]); f->scan[i].bytes_left -= m; f->scan[i].crc_so_far = crc; if (f->scan[i].bytes_left == 0) { @@ -4351,9 +4441,9 @@ static int stbv_vorbis_search_for_page_pushdata(stbv_vorb *f, stbv_uint8 *data, } // return value: number of bytes we used -STBVDEF int stb_vorbis_decode_frame_pushdata( +int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, // the file we're decoding - const stbv_uint8 *data, int data_len, // the memory available for decoding + const uint8 *data, int data_len, // the memory available for decoding int *channels, // place to write number of float * buffers float ***output, // place to write float ** array of float * buffers int *samples // place to write number of output samples @@ -4362,30 +4452,30 @@ STBVDEF int stb_vorbis_decode_frame_pushdata( int i; int len,right,left; - if (!STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (f->page_crc_tests >= 0) { *samples = 0; - return stbv_vorbis_search_for_page_pushdata(f, (stbv_uint8 *) data, data_len); + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); } - f->stream = (stbv_uint8 *) data; - f->stream_end = (stbv_uint8 *) data + data_len; + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; f->error = VORBIS__no_error; // check that we have the entire packet in memory - if (!stbv_is_whole_packet_present(f, FALSE)) { + if (!is_whole_packet_present(f)) { *samples = 0; return 0; } - if (!stbv_vorbis_decode_packet(f, &len, &left, &right)) { + if (!vorbis_decode_packet(f, &len, &left, &right)) { // save the actual error we encountered enum STBVorbisError error = f->error; if (error == VORBIS_bad_packet_type) { // flush and resynch f->error = VORBIS__no_error; - while (stbv_get8_packet(f) != STBV_EOP) + while (get8_packet(f) != EOP) if (f->eof) break; *samples = 0; return (int) (f->stream - data); @@ -4395,7 +4485,7 @@ STBVDEF int stb_vorbis_decode_frame_pushdata( // we may be resynching, in which case it's ok to hit one // of these; just discard the packet f->error = VORBIS__no_error; - while (stbv_get8_packet(f) != STBV_EOP) + while (get8_packet(f) != EOP) if (f->eof) break; *samples = 0; return (int) (f->stream - data); @@ -4411,7 +4501,7 @@ STBVDEF int stb_vorbis_decode_frame_pushdata( } // success! - len = stbv_vorbis_finish_frame(f, len, left, right); + len = vorbis_finish_frame(f, len, left, right); for (i=0; i < f->channels; ++i) f->outputs[i] = f->channel_buffers[i] + left; @@ -4421,42 +4511,43 @@ STBVDEF int stb_vorbis_decode_frame_pushdata( return (int) (f->stream - data); } -STBVDEF stb_vorbis *stb_vorbis_open_pushdata( +stb_vorbis *stb_vorbis_open_pushdata( const unsigned char *data, int data_len, // the memory available for decoding int *data_used, // only defined if result is not NULL int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; - stbv_vorbis_init(&p, alloc); - p.stream = (stbv_uint8 *) data; - p.stream_end = (stbv_uint8 *) data + data_len; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; p.push_mode = TRUE; - if (!stbv_start_decoder(&p)) { + if (!start_decoder(&p)) { if (p.eof) *error = VORBIS_need_more_data; else *error = p.error; + vorbis_deinit(&p); return NULL; } - f = stbv_vorbis_alloc(&p); + f = vorbis_alloc(&p); if (f) { *f = p; *data_used = (int) (f->stream - data); *error = 0; return f; } else { - stbv_vorbis_deinit(&p); + vorbis_deinit(&p); return NULL; } } #endif // STB_VORBIS_NO_PUSHDATA_API -STBVDEF unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (f->push_mode) return 0; #endif - if (STBV_USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); #ifndef STB_VORBIS_NO_STDIO return (unsigned int) (ftell(f->f) - f->f_start); #endif @@ -4467,12 +4558,12 @@ STBVDEF unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) // DATA-PULLING API // -static stbv_uint32 stbv_vorbis_find_page(stb_vorbis *f, stbv_uint32 *end, stbv_uint32 *last) +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) { for(;;) { int n; if (f->eof) return 0; - n = stbv_get8(f); + n = get8(f); if (n == 0x4f) { // page header candidate unsigned int retry_loc = stb_vorbis_get_file_offset(f); int i; @@ -4481,33 +4572,33 @@ static stbv_uint32 stbv_vorbis_find_page(stb_vorbis *f, stbv_uint32 *end, stbv_u return 0; // check the rest of the header for (i=1; i < 4; ++i) - if (stbv_get8(f) != stbv_ogg_page_header[i]) + if (get8(f) != ogg_page_header[i]) break; if (f->eof) return 0; if (i == 4) { - stbv_uint8 header[27]; - stbv_uint32 i, crc, goal, len; + uint8 header[27]; + uint32 i, crc, goal, len; for (i=0; i < 4; ++i) - header[i] = stbv_ogg_page_header[i]; + header[i] = ogg_page_header[i]; for (; i < 27; ++i) - header[i] = stbv_get8(f); + header[i] = get8(f); if (f->eof) return 0; if (header[4] != 0) goto invalid; - goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); for (i=22; i < 26; ++i) header[i] = 0; crc = 0; for (i=0; i < 27; ++i) - crc = stbv_crc32_update(crc, header[i]); + crc = crc32_update(crc, header[i]); len = 0; for (i=0; i < header[26]; ++i) { - int s = stbv_get8(f); - crc = stbv_crc32_update(crc, s); + int s = get8(f); + crc = crc32_update(crc, s); len += s; } if (len && f->eof) return 0; for (i=0; i < len; ++i) - crc = stbv_crc32_update(crc, stbv_get8(f)); + crc = crc32_update(crc, get8(f)); // finished parsing probable page if (crc == goal) { // we could now check that it's either got the last @@ -4526,19 +4617,19 @@ static stbv_uint32 stbv_vorbis_find_page(stb_vorbis *f, stbv_uint32 *end, stbv_u else *last = 0; } - stbv_set_file_offset(f, retry_loc-1); + set_file_offset(f, retry_loc-1); return 1; } } invalid: // not a valid page, so rewind and look for next one - stbv_set_file_offset(f, retry_loc); + set_file_offset(f, retry_loc); } } } -#define STBV_SAMPLE_unknown 0xffffffff +#define SAMPLE_unknown 0xffffffff // seeking is implemented with a binary search, which narrows down the range to // 64K, before using a linear search (because finding the synchronization @@ -4549,19 +4640,19 @@ static stbv_uint32 stbv_vorbis_find_page(stb_vorbis *f, stbv_uint32 *end, stbv_u // to try to bound either side of the binary search sensibly, while still // working in O(log n) time if they fail. -static int stbv_get_seek_page_info(stb_vorbis *f, StbvProbedPage *z) +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) { - stbv_uint8 header[27], lacing[255]; + uint8 header[27], lacing[255]; int i,len; // record where the page starts z->page_start = stb_vorbis_get_file_offset(f); // parse the header - stbv_getn(f, header, 27); + getn(f, header, 27); if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') return 0; - stbv_getn(f, lacing, header[26]); + getn(f, lacing, header[26]); // determine the length of the payload len = 0; @@ -4575,13 +4666,13 @@ static int stbv_get_seek_page_info(stb_vorbis *f, StbvProbedPage *z) z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); // restore file state to where we were - stbv_set_file_offset(f, z->page_start); + set_file_offset(f, z->page_start); return 1; } -// rarely used function to seek back to the preceeding page while finding the +// rarely used function to seek back to the preceding page while finding the // start of a packet -static int stbv_go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { unsigned int previous_safe, end; @@ -4591,12 +4682,12 @@ static int stbv_go_to_page_before(stb_vorbis *f, unsigned int limit_offset) else previous_safe = f->first_audio_page_offset; - stbv_set_file_offset(f, previous_safe); + set_file_offset(f, previous_safe); - while (stbv_vorbis_find_page(f, &end, NULL)) { + while (vorbis_find_page(f, &end, NULL)) { if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) return 1; - stbv_set_file_offset(f, end); + set_file_offset(f, end); } return 0; @@ -4606,42 +4697,45 @@ static int stbv_go_to_page_before(stb_vorbis *f, unsigned int limit_offset) // the function succeeds, current_loc_valid will be true and current_loc will // be less than or equal to the provided sample number (the closer the // better). -static int stbv_seek_to_sample_coarse(stb_vorbis *f, stbv_uint32 sample_number) +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { - StbvProbedPage left, right, mid; + ProbedPage left, right, mid; int i, start_seg_with_known_loc, end_pos, page_start; - stbv_uint32 delta, stream_length, padding; - double offset, bytes_per_sample; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; int probe = 0; // find the last page and validate the target sample stream_length = stb_vorbis_stream_length_in_samples(f); - if (stream_length == 0) return stbv_error(f, VORBIS_seek_without_length); - if (sample_number > stream_length) return stbv_error(f, VORBIS_seek_invalid); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); // this is the maximum difference between the window-center (which is the // actual granule position value), and the right-start (which the spec // indicates should be the granule position (give or take one)). padding = ((f->blocksize_1 - f->blocksize_0) >> 2); if (sample_number < padding) - sample_number = 0; + last_sample_limit = 0; else - sample_number -= padding; + last_sample_limit = sample_number - padding; left = f->p_first; while (left.last_decoded_sample == ~0U) { // (untested) the first page does not have a 'last_decoded_sample' - stbv_set_file_offset(f, left.page_end); - if (!stbv_get_seek_page_info(f, &left)) goto error; + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; } right = f->p_last; assert(right.last_decoded_sample != ~0U); // starting from the start is handled differently - if (sample_number <= left.last_decoded_sample) { - if (stb_vorbis_seek_start(f)) + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); return 1; + } return 0; } @@ -4651,17 +4745,17 @@ static int stbv_seek_to_sample_coarse(stb_vorbis *f, stbv_uint32 sample_number) delta = right.page_start - left.page_end; if (delta <= 65536) { // there's only 64K left to search - handle it linearly - stbv_set_file_offset(f, left.page_end); + set_file_offset(f, left.page_end); } else { if (probe < 2) { if (probe == 0) { // first probe (interpolate) double data_bytes = right.page_end - left.page_start; bytes_per_sample = data_bytes / right.last_decoded_sample; - offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); } else { // second probe (try to bound the other side) - double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; if (error >= 0 && error < 8000) error = 8000; if (error < 0 && error > -8000) error = -8000; offset += error * 2; @@ -4673,41 +4767,43 @@ static int stbv_seek_to_sample_coarse(stb_vorbis *f, stbv_uint32 sample_number) if (offset > right.page_start - 65536) offset = right.page_start - 65536; - stbv_set_file_offset(f, (unsigned int) offset); + set_file_offset(f, (unsigned int) offset); } else { // binary search for large ranges (offset by 32K to ensure // we don't hit the right page) - stbv_set_file_offset(f, left.page_end + (delta / 2) - 32768); + set_file_offset(f, left.page_end + (delta / 2) - 32768); } - if (!stbv_vorbis_find_page(f, NULL, NULL)) goto error; + if (!vorbis_find_page(f, NULL, NULL)) goto error; } for (;;) { - if (!stbv_get_seek_page_info(f, &mid)) goto error; + if (!get_seek_page_info(f, &mid)) goto error; if (mid.last_decoded_sample != ~0U) break; // (untested) no frames end on this page - stbv_set_file_offset(f, mid.page_end); + set_file_offset(f, mid.page_end); assert(mid.page_start < right.page_start); } // if we've just found the last page again then we're in a tricky file, - // and we're close enough. - if (mid.page_start == right.page_start) - break; - - if (sample_number < mid.last_decoded_sample) - right = mid; - else - left = mid; + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } ++probe; } // seek back to start of the last packet page_start = left.page_start; - stbv_set_file_offset(f, page_start); - if (!stbv_start_page(f)) return stbv_error(f, VORBIS_seek_failed); + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); end_pos = f->end_seg_with_known_loc; assert(end_pos >= 0); @@ -4718,15 +4814,15 @@ static int stbv_seek_to_sample_coarse(stb_vorbis *f, stbv_uint32 sample_number) start_seg_with_known_loc = i; - if (start_seg_with_known_loc > 0 || !(f->page_flag & STBV_PAGEFLAG_continued_packet)) + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) break; // (untested) the final packet begins on an earlier page - if (!stbv_go_to_page_before(f, page_start)) + if (!go_to_page_before(f, page_start)) goto error; page_start = stb_vorbis_get_file_offset(f); - if (!stbv_start_page(f)) goto error; + if (!start_page(f)) goto error; end_pos = f->segment_count - 1; } @@ -4740,38 +4836,38 @@ static int stbv_seek_to_sample_coarse(stb_vorbis *f, stbv_uint32 sample_number) f->next_seg = start_seg_with_known_loc; for (i = 0; i < start_seg_with_known_loc; i++) - stbv_skip(f, f->segments[i]); + skip(f, f->segments[i]); // start decoding (optimizable - this frame is generally discarded) - if (!stbv_vorbis_pump_first_frame(f)) + if (!vorbis_pump_first_frame(f)) return 0; if (f->current_loc > sample_number) - return stbv_error(f, VORBIS_seek_failed); + return error(f, VORBIS_seek_failed); return 1; error: // try to restore the file to a valid state stb_vorbis_seek_start(f); - return stbv_error(f, VORBIS_seek_failed); + return error(f, VORBIS_seek_failed); } -// the same as stbv_vorbis_decode_initial, but without advancing -static int stbv_peek_decode_initial(stbv_vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { int bits_read, bytes_read; - if (!stbv_vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) return 0; // either 1 or 2 bytes were read, figure out which so we can rewind - bits_read = 1 + stbv_ilog(f->mode_count-1); + bits_read = 1 + ilog(f->mode_count-1); if (f->mode_config[*mode].blockflag) bits_read += 2; bytes_read = (bits_read + 7) / 8; f->bytes_in_seg += bytes_read; f->packet_bytes -= bytes_read; - stbv_skip(f, -bytes_read); + skip(f, -bytes_read); if (f->next_seg == -1) f->next_seg = f->segment_count - 1; else @@ -4781,14 +4877,14 @@ static int stbv_peek_decode_initial(stbv_vorb *f, int *p_left_start, int *p_left return 1; } -STBVDEF int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) { - stbv_uint32 max_frame_samples; + uint32 max_frame_samples; - if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); // fast page-level search - if (!stbv_seek_to_sample_coarse(f, sample_number)) + if (!seek_to_sample_coarse(f, sample_number)) return 0; assert(f->current_loc_valid); @@ -4798,36 +4894,36 @@ STBVDEF int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; while (f->current_loc < sample_number) { int left_start, left_end, right_start, right_end, mode, frame_samples; - if (!stbv_peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) - return stbv_error(f, VORBIS_seek_failed); + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); // calculate the number of samples returned by the next frame frame_samples = right_start - left_start; if (f->current_loc + frame_samples > sample_number) { return 1; // the next frame will contain the sample } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { // there's a chance the frame after this could contain the sample - stbv_vorbis_pump_first_frame(f); + vorbis_pump_first_frame(f); } else { // this frame is too early to be relevant f->current_loc += frame_samples; f->previous_length = 0; - stbv_maybe_start_packet(f); - stbv_flush_packet(f); + maybe_start_packet(f); + flush_packet(f); } } - // the next frame will start with the sample - assert(f->current_loc == sample_number); + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); return 1; } -STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) { if (!stb_vorbis_seek_frame(f, sample_number)) return 0; if (sample_number != f->current_loc) { int n; - stbv_uint32 frame_start = f->current_loc; + uint32 frame_start = f->current_loc; stb_vorbis_get_frame_float(f, &n, NULL); assert(sample_number > frame_start); assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); @@ -4837,25 +4933,25 @@ STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) return 1; } -STBVDEF int stb_vorbis_seek_start(stb_vorbis *f) +int stb_vorbis_seek_start(stb_vorbis *f) { - if (STBV_IS_PUSH_MODE(f)) { return stbv_error(f, VORBIS_invalid_api_mixing); } - stbv_set_file_offset(f, f->first_audio_page_offset); + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); f->previous_length = 0; f->first_decode = TRUE; f->next_seg = -1; - return stbv_vorbis_pump_first_frame(f); + return vorbis_pump_first_frame(f); } -STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) { unsigned int restore_offset, previous_safe; unsigned int end, last_page_loc; - if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!f->total_samples) { unsigned int last; - stbv_uint32 lo,hi; + uint32 lo,hi; char header[6]; // first, store the current decode position so we can restore it @@ -4868,11 +4964,11 @@ STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) else previous_safe = f->first_audio_page_offset; - stbv_set_file_offset(f, previous_safe); + set_file_offset(f, previous_safe); // previous_safe is now our candidate 'earliest known place that seeking // to will lead to the final page' - if (!stbv_vorbis_find_page(f, &end, &last)) { + if (!vorbis_find_page(f, &end, &last)) { // if we can't find a page, we're hosed! f->error = VORBIS_cant_find_last_page; f->total_samples = 0xffffffff; @@ -4886,26 +4982,26 @@ STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) // this allows us to stop short of a 'file_section' end without // explicitly checking the length of the section while (!last) { - stbv_set_file_offset(f, end); - if (!stbv_vorbis_find_page(f, &end, &last)) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { // the last page we found didn't have the 'last page' flag // set. whoops! break; } - previous_safe = last_page_loc+1; + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging last_page_loc = stb_vorbis_get_file_offset(f); } - stbv_set_file_offset(f, last_page_loc); + set_file_offset(f, last_page_loc); // parse the header - stbv_getn(f, (unsigned char *)header, 6); + getn(f, (unsigned char *)header, 6); // extract the absolute granule position - lo = stbv_get32(f); - hi = stbv_get32(f); + lo = get32(f); + hi = get32(f); if (lo == 0xffffffff && hi == 0xffffffff) { f->error = VORBIS_cant_find_last_page; - f->total_samples = STBV_SAMPLE_unknown; + f->total_samples = SAMPLE_unknown; goto done; } if (hi) @@ -4917,29 +5013,29 @@ STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) f->p_last.last_decoded_sample = lo; done: - stbv_set_file_offset(f, restore_offset); + set_file_offset(f, restore_offset); } - return f->total_samples == STBV_SAMPLE_unknown ? 0 : f->total_samples; + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; } -STBVDEF float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) { return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; } -STBVDEF int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) { int len, right,left,i; - if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - if (!stbv_vorbis_decode_packet(f, &len, &left, &right)) { + if (!vorbis_decode_packet(f, &len, &left, &right)) { f->channel_buffer_start = f->channel_buffer_end = 0; return 0; } - len = stbv_vorbis_finish_frame(f, len, left, right); + len = vorbis_finish_frame(f, len, left, right); for (i=0; i < f->channels; ++i) f->outputs[i] = f->channel_buffers[i] + left; @@ -4953,28 +5049,28 @@ STBVDEF int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***ou #ifndef STB_VORBIS_NO_STDIO -STBVDEF stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) { stb_vorbis *f, p; - stbv_vorbis_init(&p, alloc); + vorbis_init(&p, alloc); p.f = file; - p.f_start = (stbv_uint32) ftell(file); + p.f_start = (uint32) ftell(file); p.stream_len = length; p.close_on_free = close_on_free; - if (stbv_start_decoder(&p)) { - f = stbv_vorbis_alloc(&p); + if (start_decoder(&p)) { + f = vorbis_alloc(&p); if (f) { *f = p; - stbv_vorbis_pump_first_frame(f); + vorbis_pump_first_frame(f); return f; } } if (error) *error = p.error; - stbv_vorbis_deinit(&p); + vorbis_deinit(&p); return NULL; } -STBVDEF stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) { unsigned int len, start; start = (unsigned int) ftell(file); @@ -4984,58 +5080,67 @@ STBVDEF stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *er return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); } -STBVDEF stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) { - FILE *f = fopen(filename, "rb"); - if (f) + FILE *f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; +#else + f = fopen(filename, "rb"); +#endif + if (f) return stb_vorbis_open_file(f, TRUE, error, alloc); if (error) *error = VORBIS_file_open_failure; return NULL; } #endif // STB_VORBIS_NO_STDIO -STBVDEF stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; - if (data == NULL) return NULL; - stbv_vorbis_init(&p, alloc); - p.stream = (stbv_uint8 *) data; - p.stream_end = (stbv_uint8 *) data + len; - p.stream_start = (stbv_uint8 *) p.stream; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; p.stream_len = len; p.push_mode = FALSE; - if (stbv_start_decoder(&p)) { - f = stbv_vorbis_alloc(&p); + if (start_decoder(&p)) { + f = vorbis_alloc(&p); if (f) { *f = p; - stbv_vorbis_pump_first_frame(f); + vorbis_pump_first_frame(f); if (error) *error = VORBIS__no_error; return f; } } if (error) *error = p.error; - stbv_vorbis_deinit(&p); + vorbis_deinit(&p); return NULL; } #ifndef STB_VORBIS_NO_INTEGER_CONVERSION -#define STBV_PLAYBACK_MONO 1 -#define STBV_PLAYBACK_LEFT 2 -#define STBV_PLAYBACK_RIGHT 4 +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 -#define STBV_L (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_MONO) -#define STBV_C (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT | STBV_PLAYBACK_MONO) -#define STBV_R (STBV_PLAYBACK_RIGHT | STBV_PLAYBACK_MONO) +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) -static stbv_int8 stbv_channel_position[7][6] = +static int8 channel_position[7][6] = { { 0 }, - { STBV_C }, - { STBV_L, STBV_R }, - { STBV_L, STBV_C, STBV_R }, - { STBV_L, STBV_R, STBV_L, STBV_R }, - { STBV_L, STBV_C, STBV_R, STBV_L, STBV_R }, - { STBV_L, STBV_C, STBV_R, STBV_L, STBV_R, STBV_C }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, }; @@ -5043,139 +5148,141 @@ static stbv_int8 stbv_channel_position[7][6] = typedef union { float f; int i; - } stbv_float_conv; + } float_conv; typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; - #define STBV_FASTDEF(x) stbv_float_conv x + #define FASTDEF(x) float_conv x // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round - #define STBV_MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) - #define STBV_ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) - #define STBV_FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + STBV_MAGIC(s), temp.i - STBV_ADDEND(s)) - #define stbv_check_endianness() + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() #else - #define STBV_FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) - #define stbv_check_endianness() - #define STBV_FASTDEF(x) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) #endif -static void stbv_copy_samples(short *dest, float *src, int len) +static void copy_samples(short *dest, float *src, int len) { int i; - stbv_check_endianness(); + check_endianness(); for (i=0; i < len; ++i) { - STBV_FASTDEF(temp); - int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; dest[i] = v; } } -static void stbv_compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE; - stbv_check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { - if (stbv_channel_position[num_c][j] & mask) { + if (channel_position[num_c][j] & mask) { for (i=0; i < n; ++i) buffer[i] += data[j][d_offset+o+i]; } } for (i=0; i < n; ++i) { - STBV_FASTDEF(temp); - int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o+i] = v; } } + #undef STB_BUFFER_SIZE } -static void stbv_compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE >> 1; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data - stbv_check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { - int m = stbv_channel_position[num_c][j] & (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT); - if (m == (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT)) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { for (i=0; i < n; ++i) { buffer[i*2+0] += data[j][d_offset+o+i]; buffer[i*2+1] += data[j][d_offset+o+i]; } - } else if (m == STBV_PLAYBACK_LEFT) { + } else if (m == PLAYBACK_LEFT) { for (i=0; i < n; ++i) { buffer[i*2+0] += data[j][d_offset+o+i]; } - } else if (m == STBV_PLAYBACK_RIGHT) { + } else if (m == PLAYBACK_RIGHT) { for (i=0; i < n; ++i) { buffer[i*2+1] += data[j][d_offset+o+i]; } } } for (i=0; i < (n<<1); ++i) { - STBV_FASTDEF(temp); - int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o2+i] = v; } } + #undef STB_BUFFER_SIZE } -static void stbv_convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) { int i; if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { - static int channel_selector[3][2] = { {0}, {STBV_PLAYBACK_MONO}, {STBV_PLAYBACK_LEFT, STBV_PLAYBACK_RIGHT} }; + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; for (i=0; i < buf_c; ++i) - stbv_compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); } else { int limit = buf_c < data_c ? buf_c : data_c; for (i=0; i < limit; ++i) - stbv_copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); for ( ; i < buf_c; ++i) memset(buffer[i]+b_offset, 0, sizeof(short) * samples); } } -STBVDEF int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { - float **output; + float **output = NULL; int len = stb_vorbis_get_frame_float(f, NULL, &output); if (len > num_samples) len = num_samples; if (len) - stbv_convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); return len; } -static void stbv_convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) { int i; - stbv_check_endianness(); + check_endianness(); if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { assert(buf_c == 2); for (i=0; i < buf_c; ++i) - stbv_compute_stereo_samples(buffer, data_c, data, d_offset, len); + compute_stereo_samples(buffer, data_c, data, d_offset, len); } else { int limit = buf_c < data_c ? buf_c : data_c; int j; for (j=0; j < len; ++j) { for (i=0; i < limit; ++i) { - STBV_FASTDEF(temp); + FASTDEF(temp); float f = data[i][d_offset+j]; - int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; *buffer++ = v; @@ -5186,7 +5293,7 @@ static void stbv_convert_channels_short_interleaved(int buf_c, short *buffer, in } } -STBVDEF int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) { float **output; int len; @@ -5194,23 +5301,21 @@ STBVDEF int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, sho len = stb_vorbis_get_frame_float(f, NULL, &output); if (len) { if (len*num_c > num_shorts) len = num_shorts / num_c; - stbv_convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); } return len; } -STBVDEF int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) { float **outputs; int len = num_shorts / channels; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; if (k) - stbv_convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); buffer += k*channels; n += k; f->channel_buffer_start += k; @@ -5220,17 +5325,15 @@ STBVDEF int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels return n; } -STBVDEF int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) { float **outputs; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; if (k) - stbv_convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); n += k; f->channel_buffer_start += k; if (n == len) break; @@ -5240,7 +5343,7 @@ STBVDEF int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **bu } #ifndef STB_VORBIS_NO_STDIO -STBVDEF int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; @@ -5280,7 +5383,7 @@ STBVDEF int stb_vorbis_decode_filename(const char *filename, int *channels, int } #endif // NO_STDIO -STBVDEF int stb_vorbis_decode_memory(const stbv_uint8 *mem, int len, int *channels, int *sample_rate, short **output) +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; @@ -5320,7 +5423,7 @@ STBVDEF int stb_vorbis_decode_memory(const stbv_uint8 *mem, int len, int *channe } #endif // STB_VORBIS_NO_INTEGER_CONVERSION -STBVDEF int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) { float **outputs; int len = num_floats / channels; @@ -5347,7 +5450,7 @@ STBVDEF int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels return n; } -STBVDEF int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) { float **outputs; int n=0; @@ -5375,14 +5478,20 @@ STBVDEF int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **bu #endif // STB_VORBIS_NO_PULLDATA_API /* Version history + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 + found with Mayhem by ForAllSecure + 1.16 - 2019-03-04 - fix warnings + 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found + 1.14 - 2018-02-11 - delete bogus dealloca usage + 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files - 1.11 - 2017-07-23 - fix MinGW compilation - 1.10 - 2017-03-03 - more robust seeking; fix negative stbv_ilog(); clear error in open_memory + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; avoid discarding last frame of audio data 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API - some more crash fixes when out of memory or with corrupt files + some more crash fixes when out of memory or with corrupt files 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) some crash fixes when out of memory or with corrupt files 1.05 - 2015-04-19 - don't define __forceinline if it's redundant @@ -5429,7 +5538,7 @@ STBVDEF int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **bu 0.90 - first public release */ -#endif // STB_VORBIS_IMPLEMENTATION +#endif // STB_VORBIS_HEADER_ONLY /* @@ -5438,38 +5547,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -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 +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 +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 +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, +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 +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 +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/src/raudio.c b/src/raudio.c index 46d8223d9..591d6f69f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -198,8 +198,7 @@ typedef struct tagBITMAPINFOHEADER { #if defined(SUPPORT_FILEFORMAT_OGG) // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE - #define STB_VORBIS_IMPLEMENTATION - #include "external/stb_vorbis.h" // OGG loading functions + #include "external/stb_vorbis.c" // OGG loading functions #endif #if defined(SUPPORT_FILEFORMAT_XM) From 212f331583c8847162d5950183bed9c551fbf552 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Wed, 1 Feb 2023 16:13:25 +0200 Subject: [PATCH 0245/1710] Update core_input_gamepad.c (#2903) --- examples/core/core_input_gamepad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index 411472b34..cd0c867d7 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -174,7 +174,7 @@ int main(void) DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(0, i)), 20, 70 + 20*i, 10, DARKGRAY); } - if (GetGamepadButtonPressed() != -1) DrawText(TextFormat("DETECTED BUTTON: %i", GetGamepadButtonPressed()), 10, 430, 10, RED); + 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 From b9e49cdca6a6b8fb65f3cc7679911c7fff41cf57 Mon Sep 17 00:00:00 2001 From: Ian McFarlane <70479099+the-argus@users.noreply.github.com> Date: Fri, 3 Feb 2023 13:52:15 +0000 Subject: [PATCH 0246/1710] rcamera: move y clamp to before 3d projection (#2905) --- src/rcamera.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 1b77e19a9..70221b347 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -405,6 +405,9 @@ void UpdateCamera(Camera *camera) } break; case CAMERA_FIRST_PERSON: // Camera moves as in a first-person game, controls are configurable { + // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position' + camera->position.y = CAMERA.playerEyesPosition; + camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + @@ -479,10 +482,6 @@ void UpdateCamera(Camera *camera) camera->target.y = camera->position.y - matTransform.m13; camera->target.z = camera->position.z - matTransform.m14; - // Camera position update - // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position' - camera->position.y = CAMERA.playerEyesPosition; - // Camera swinging (y-movement), only when walking (some key pressed) for (int i = 0; i < 6; i++) if (direction[i]) { swingCounter += GetFrameTime(); break; } camera->position.y -= sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_SWINGING_DELTA; From 7914332c40ee811ad08a90e4eb7d40c2d1e81f4e Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Feb 2023 14:54:28 +0100 Subject: [PATCH 0247/1710] Update qoi.h --- src/external/qoi.h | 50 +++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/src/external/qoi.h b/src/external/qoi.h index 988f9edcb..6734ac46e 100644 --- a/src/external/qoi.h +++ b/src/external/qoi.h @@ -1,31 +1,11 @@ /* +Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org +SPDX-License-Identifier: MIT + + QOI - The "Quite OK Image" format for fast, lossless image compression -Dominic Szablewski - https://phoboslab.org - - --- LICENSE: The MIT License(MIT) - -Copyright(c) 2021 Dominic Szablewski - -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. - - -- About QOI encodes and decodes images in a lossless format. Compared to stb_image and @@ -424,13 +404,12 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { channels = desc->channels; for (px_pos = 0; px_pos < px_len; px_pos += channels) { + px.rgba.r = pixels[px_pos + 0]; + px.rgba.g = pixels[px_pos + 1]; + px.rgba.b = pixels[px_pos + 2]; + if (channels == 4) { - px = *(qoi_rgba_t *)(pixels + px_pos); - } - else { - px.rgba.r = pixels[px_pos + 0]; - px.rgba.g = pixels[px_pos + 1]; - px.rgba.b = pixels[px_pos + 2]; + px.rgba.a = pixels[px_pos + 3]; } if (px.v == px_prev.v) { @@ -598,13 +577,12 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { index[QOI_COLOR_HASH(px) % 64] = px; } + pixels[px_pos + 0] = px.rgba.r; + pixels[px_pos + 1] = px.rgba.g; + pixels[px_pos + 2] = px.rgba.b; + if (channels == 4) { - *(qoi_rgba_t*)(pixels + px_pos) = px; - } - else { - pixels[px_pos + 0] = px.rgba.r; - pixels[px_pos + 1] = px.rgba.g; - pixels[px_pos + 2] = px.rgba.b; + pixels[px_pos + 3] = px.rgba.a; } } From 43e45cbb810b58a5c78e3d6fe708260043083b91 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Feb 2023 20:19:51 +0100 Subject: [PATCH 0248/1710] Replace TABS by 4 spaces --- src/rmodels.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index b5d8578f6..3c9384d2d 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1875,34 +1875,34 @@ bool ExportMesh(Mesh mesh, const char *fileName) // Process obj materials static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount) { - // Init model materials - for (int m = 0; m < materialCount; m++) - { - // Init material to default - // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE - rayMaterials[m] = LoadMaterialDefault(); + // Init model materials + for (int m = 0; m < materialCount; m++) + { + // Init material to default + // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE + rayMaterials[m] = LoadMaterialDefault(); - // Get default texture, in case no texture is defined - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + // Get default texture, in case no texture is defined + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd + if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; + rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; + rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; - if (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks - rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3]; - rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; + if (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks + rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3]; + rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; - if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump - rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; - rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess; + if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump + rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; + rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess; - rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3]; + rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3]; - if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp - } + if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp + } } #endif @@ -4041,7 +4041,7 @@ static Model LoadOBJ(const char *fileName) } // Init model materials - ProcessMaterialsOBJ(model.materials, materials, materialCount); + ProcessMaterialsOBJ(model.materials, materials, materialCount); tinyobj_attrib_free(&attrib); tinyobj_shapes_free(meshes, meshCount); From 901c4553d2b9c23337f10083f6726cdd71927cf5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Feb 2023 20:20:21 +0100 Subject: [PATCH 0249/1710] ADDED: QOA audio format support -WIP- --- src/config.h | 5 +- src/external/qoa.h | 658 +++++++++++++++++++++++++++++++++++++++++++++ src/raudio.c | 316 ++++++++++++++-------- 3 files changed, 866 insertions(+), 113 deletions(-) create mode 100644 src/external/qoa.h diff --git a/src/config.h b/src/config.h index f67ea3cb0..be24c0190 100644 --- a/src/config.h +++ b/src/config.h @@ -211,10 +211,11 @@ // Desired audio fileformats to be supported for loading #define SUPPORT_FILEFORMAT_WAV 1 #define SUPPORT_FILEFORMAT_OGG 1 +#define SUPPORT_FILEFORMAT_MP3 1 +//#define SUPPORT_FILEFORMAT_QOA 1 +//#define SUPPORT_FILEFORMAT_FLAC 1 #define SUPPORT_FILEFORMAT_XM 1 #define SUPPORT_FILEFORMAT_MOD 1 -#define SUPPORT_FILEFORMAT_MP3 1 -//#define SUPPORT_FILEFORMAT_FLAC 1 // raudio: Configuration values //------------------------------------------------------------------------------------ diff --git a/src/external/qoa.h b/src/external/qoa.h new file mode 100644 index 000000000..aae575512 --- /dev/null +++ b/src/external/qoa.h @@ -0,0 +1,658 @@ +/* + +Copyright (c) 2023, Dominic Szablewski - https://phoboslab.org +SPDX-License-Identifier: MIT + +QOA - The "Quite OK Audio" format for fast, lossy audio compression + + +-- Data Format + +A QOA file has an 8 byte file header, followed by a number of frames. Each frame +consists of an 8 byte frame header, the current 8 byte en-/decoder state per +channel and 256 slices per channel. Each slice is 8 bytes wide and encodes 20 +samples of audio data. + +Note that the last frame of a file may contain less than 256 slices per channel. +The last slice (per channel) in the last frame may contain less 20 samples, but +the slice will still be 8 bytes wide, with the unused samples zeroed out. + +The samplerate and number of channels is only stated in the frame headers, but +not in the file header. A decoder may peek into the first frame of the file to +find these values. + +In a valid QOA file all frames have the same number of channels and the same +samplerate. These restriction may be releaxed for streaming. This remains to +be decided. + +All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file, +including the headers, is 64 bit aligned, so it's possible to read files with +just a read_u64() that does the byte swapping if neccessary. + +In pseudocode, the file layout is as follows: + +struct { + struct { + char magic[4]; // magic bytes 'qoaf' + uint32_t samples; // number of samples per channel in this file + } file_header; // = 64 bits + + struct { + struct { + uint8_t num_channels; // number of channels + uint24_t samplerate; // samplerate in hz + uint16_t fsamples; // sample count per channel in this frame + uint16_t fsize; // frame size (including the frame header) + } frame_header; // = 64 bits + + struct { + int16_t history[4]; // = 64 bits + int16_t weights[4]; // = 64 bits + } lms_state[num_channels]; + + qoa_slice_t slices[256][num_channels]; // = 64 bits each + } frames[samples * channels / qoa_max_framesize()]; +} qoa_file; + +Wheras the 64bit qoa_slice_t is defined as follows: + +.- QOA_SLICE -- 64 bits, 20 samples --------------------------/ /------------. +| Byte[0] | Byte[1] | Byte[2] \ \ Byte[7] | +| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 / / 2 1 0 | +|------------+--------+--------+--------+---------+---------+-\ \--+---------| +| sf_index | r00 | r01 | r02 | r03 | r04 | / / | r19 | +`-------------------------------------------------------------\ \------------` + +`sf_index` defines the scalefactor to use for this slice as an index into the +qoa_scalefactor_tab[16] + +`r00`--`r19` are the residuals for the individiual samples, divided by the +scalefactor and quantized by the qoa_quant_tab[]. + +In the decoder, a prediction of the next sample is computed by multiplying the +state (the last four output samples) with the predictor. The residual from the +slice is then dequantized using the qoa_dequant_tab[] and added to the +prediction. The result is clamped to int16 to form the final output sample. + +*/ + + + +/* ----------------------------------------------------------------------------- + Header - Public functions */ + +#ifndef QOA_H +#define QOA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define QOA_MIN_FILESIZE 16 +#define QOA_MAX_CHANNELS 8 + +#define QOA_SLICE_LEN 20 +#define QOA_SLICES_PER_FRAME 256 +#define QOA_FRAME_LEN (QOA_SLICES_PER_FRAME * QOA_SLICE_LEN) +#define QOA_LMS_LEN 4 +#define QOA_MAGIC 0x716f6166 /* 'qoaf' */ + +#define QOA_FRAME_SIZE(channels, slices) \ + (8 + QOA_LMS_LEN * 4 * channels + 8 * slices * channels) + +typedef struct { + int history[QOA_LMS_LEN]; + int weights[QOA_LMS_LEN]; +} qoa_lms_t; + +typedef struct { + unsigned int channels; + unsigned int samplerate; + unsigned int samples; + qoa_lms_t lms[QOA_MAX_CHANNELS]; + #ifdef QOA_RECORD_TOTAL_ERROR + double error; + #endif +} qoa_desc; + +unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes); +unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes); +void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len); + +unsigned int qoa_max_frame_size(qoa_desc *qoa); +unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa); +unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len); +short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *file); + +#ifndef QOA_NO_STDIO + +int qoa_write(const char *filename, const short *sample_data, qoa_desc *qoa); +void *qoa_read(const char *filename, qoa_desc *qoa); + +#endif /* QOA_NO_STDIO */ + + +#ifdef __cplusplus +} +#endif +#endif /* QOA_H */ + + +/* ----------------------------------------------------------------------------- + Implementation */ + +#ifdef QOA_IMPLEMENTATION +#include + +#ifndef QOA_MALLOC + #define QOA_MALLOC(sz) malloc(sz) + #define QOA_FREE(p) free(p) +#endif + +typedef unsigned long long qoa_uint64_t; + + +/* The quant_tab provides an index into the dequant_tab for residuals in the +range of -8 .. 8. It maps this range to just 3bits and becommes less accurate at +the higher end. Note that the residual zero is identical to the lowest positive +value. This is mostly fine, since the qoa_div() function always rounds away +from zero. */ + +static int qoa_quant_tab[17] = { + 7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */ + 0, /* 0 */ + 0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */ +}; + + +/* We have 16 different scalefactors. Like the quantized residuals these become +less accurate at the higher end. In theory, the highest scalefactor that we +would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we +rely on the LMS filter to predict samples accurately enough that a maximum +residual of one quarter of the 16 bit range is high sufficent. I.e. with the +scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14. + +The scalefactor values are computed as: +scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */ + +static int qoa_scalefactor_tab[16] = { + 1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048 +}; + + +/* The reciprocal_tab maps each of the 16 scalefactors to their rounded +reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in +the encoder with just one multiplication instead of an expensive division. We +do this in .16 fixed point with integers, instead of floats. + +The reciprocal_tab is computed as: +reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */ + +static int qoa_reciprocal_tab[16] = { + 65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32 +}; + + +/* The dequant_tab maps each of the scalefactors and quantized residuals to +their unscaled & dequantized version. + +Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4 +instead of 1. The dequant_tab assumes the following dequantized values for each +of the quant_tab indices and is computed as: +float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7}; +dequant_tab[s][q] <- round(scalefactor_tab[s] * dqt[q]) */ + +static int qoa_dequant_tab[16][8] = { + { 1, -1, 3, -3, 5, -5, 7, -7}, + { 5, -5, 18, -18, 32, -32, 49, -49}, + { 16, -16, 53, -53, 95, -95, 147, -147}, + { 34, -34, 113, -113, 203, -203, 315, -315}, + { 63, -63, 210, -210, 378, -378, 588, -588}, + { 104, -104, 345, -345, 621, -621, 966, -966}, + { 158, -158, 528, -528, 950, -950, 1477, -1477}, + { 228, -228, 760, -760, 1368, -1368, 2128, -2128}, + { 316, -316, 1053, -1053, 1895, -1895, 2947, -2947}, + { 422, -422, 1405, -1405, 2529, -2529, 3934, -3934}, + { 548, -548, 1828, -1828, 3290, -3290, 5117, -5117}, + { 696, -696, 2320, -2320, 4176, -4176, 6496, -6496}, + { 868, -868, 2893, -2893, 5207, -5207, 8099, -8099}, + {1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933}, + {1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005}, + {1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336}, +}; + + +/* The Least Mean Squares Filter is the heart of QOA. It predicts the next +sample based on the previous 4 reconstructed samples. It does so by continuously +adjusting 4 weights based on the residual of the previous prediction. + +The next sample is predicted as the sum of (weight[i] * history[i]). + +The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or +subtracts the residual to each weight, based on the corresponding sample from +the history. This, suprisingly, is sufficent to get worthwhile predictions. + +This is all done with fixed point integers. Hence the right-shifts when updating +the weights and calculating the prediction. */ + +static int qoa_lms_predict(qoa_lms_t *lms) { + int prediction = 0; + for (int i = 0; i < QOA_LMS_LEN; i++) { + prediction += lms->weights[i] * lms->history[i]; + } + return prediction >> 13; +} + +static void qoa_lms_update(qoa_lms_t *lms, int sample, int residual) { + int delta = residual >> 4; + for (int i = 0; i < QOA_LMS_LEN; i++) { + lms->weights[i] += lms->history[i] < 0 ? -delta : delta; + } + + for (int i = 0; i < QOA_LMS_LEN-1; i++) { + lms->history[i] = lms->history[i+1]; + } + lms->history[QOA_LMS_LEN-1] = sample; +} + + +/* qoa_div() implements a rounding division, but avoids rounding to zero for +small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still +returns as 0, which is handled in the qoa_quant_tab[]. +qoa_div() takes an index into the .16 fixed point qoa_reciprocal_tab as an +argument, so it can do the division with a cheaper integer multiplication. */ + +static inline int qoa_div(int v, int scalefactor) { + int reciprocal = qoa_reciprocal_tab[scalefactor]; + int n = (v * reciprocal + (1 << 15)) >> 16; + n = n + ((v > 0) - (v < 0)) - ((n > 0) - (n < 0)); /* round away from 0 */ + return n; +} + +static inline int qoa_clamp(int v, int min, int max) { + return (v < min) ? min : (v > max) ? max : v; +} + +static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) { + qoa_uint64_t v = + (qoa_uint64_t)bytes[(*p)+0] << 56 | (qoa_uint64_t)bytes[(*p)+1] << 48 | + (qoa_uint64_t)bytes[(*p)+2] << 40 | (qoa_uint64_t)bytes[(*p)+3] << 32 | + (qoa_uint64_t)bytes[(*p)+4] << 24 | (qoa_uint64_t)bytes[(*p)+5] << 16 | + (qoa_uint64_t)bytes[(*p)+6] << 8 | (qoa_uint64_t)bytes[(*p)+7]; + *p += 8; + return v; +} + +static inline void qoa_write_u64(qoa_uint64_t v, unsigned char *bytes, unsigned int *p) { + bytes[(*p)++] = (v >> 56) & 0xff; + bytes[(*p)++] = (v >> 48) & 0xff; + bytes[(*p)++] = (v >> 40) & 0xff; + bytes[(*p)++] = (v >> 32) & 0xff; + bytes[(*p)++] = (v >> 24) & 0xff; + bytes[(*p)++] = (v >> 16) & 0xff; + bytes[(*p)++] = (v >> 8) & 0xff; + bytes[(*p)++] = (v >> 0) & 0xff; +} + + +/* ----------------------------------------------------------------------------- + Encoder */ + +unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes) { + unsigned int p = 0; + qoa_write_u64(((qoa_uint64_t)QOA_MAGIC << 32) | qoa->samples, bytes, &p); + return p; +} + +unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes) { + unsigned int channels = qoa->channels; + + unsigned int p = 0; + unsigned int slices = (frame_len + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN; + unsigned int frame_size = QOA_FRAME_SIZE(channels, slices); + + /* Write the frame header */ + qoa_write_u64(( + (qoa_uint64_t)qoa->channels << 56 | + (qoa_uint64_t)qoa->samplerate << 32 | + (qoa_uint64_t)frame_len << 16 | + (qoa_uint64_t)frame_size + ), bytes, &p); + + /* Write the current LMS state */ + for (int c = 0; c < channels; c++) { + qoa_uint64_t weights = 0; + qoa_uint64_t history = 0; + for (int i = 0; i < QOA_LMS_LEN; i++) { + history = (history << 16) | (qoa->lms[c].history[i] & 0xffff); + weights = (weights << 16) | (qoa->lms[c].weights[i] & 0xffff); + } + qoa_write_u64(history, bytes, &p); + qoa_write_u64(weights, bytes, &p); + } + + /* 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 (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; + + /* 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_lms_t best_lms; + + for (int scalefactor = 0; scalefactor < 16; scalefactor++) { + + /* We have to reset the LMS state to the last known good one + before trying each scalefactor, as each pass updates the LMS + state when encoding. */ + qoa_lms_t lms = qoa->lms[c]; + qoa_uint64_t slice = scalefactor; + qoa_uint64_t current_error = 0; + + for (int si = slice_start; si < slice_end; si += channels) { + int sample = sample_data[si]; + int predicted = qoa_lms_predict(&lms); + + int residual = sample - predicted; + int scaled = qoa_div(residual, scalefactor); + int clamped = qoa_clamp(scaled, -8, 8); + int quantized = qoa_quant_tab[clamped + 8]; + int dequantized = qoa_dequant_tab[scalefactor][quantized]; + int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); + + int error = (sample - reconstructed); + current_error += error * error; + if (current_error > best_error) { + break; + } + + qoa_lms_update(&lms, reconstructed, dequantized); + slice = (slice << 3) | quantized; + } + + if (current_error < best_error) { + best_error = current_error; + best_slice = slice; + best_lms = lms; + } + } + + qoa->lms[c] = best_lms; + #ifdef QOA_RECORD_TOTAL_ERROR + qoa->error += best_error; + #endif + + /* If this slice was shorter than QOA_SLICE_LEN, we have to left- + shift all encoded data, to ensure the rightmost bits are the empty + ones. This should only happen in the last frame of a file as all + slices are completely filled otherwise. */ + best_slice <<= (QOA_SLICE_LEN - slice_len) * 3; + qoa_write_u64(best_slice, bytes, &p); + } + } + + return p; +} + +void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) { + if ( + qoa->samples == 0 || + qoa->samplerate == 0 || qoa->samplerate > 0xffffff || + qoa->channels == 0 || qoa->channels > QOA_MAX_CHANNELS + ) { + return NULL; + } + + /* Calculate the encoded size and allocate */ + unsigned int num_frames = (qoa->samples + QOA_FRAME_LEN-1) / QOA_FRAME_LEN; + unsigned int num_slices = (qoa->samples + QOA_SLICE_LEN-1) / QOA_SLICE_LEN; + unsigned int encoded_size = 8 + /* 8 byte file header */ + num_frames * 8 + /* 8 byte frame headers */ + 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); + + for (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; + qoa->lms[c].weights[1] = 0; + qoa->lms[c].weights[2] = -(1<<13); + qoa->lms[c].weights[3] = (1<<14); + + /* Explicitly set the history samples to 0, as we might have some + garbage in there. */ + for (int i = 0; i < QOA_LMS_LEN; i++) { + qoa->lms[c].history[i] = 0; + } + } + + + /* Encode the header and go through all frames */ + unsigned int p = qoa_encode_header(qoa, bytes); + #ifdef QOA_RECORD_TOTAL_ERROR + qoa->error = 0; + #endif + + int frame_len = QOA_FRAME_LEN; + for (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); + p += frame_size; + } + + *out_len = p; + return bytes; +} + + + +/* ----------------------------------------------------------------------------- + Decoder */ + +unsigned int qoa_max_frame_size(qoa_desc *qoa) { + return QOA_FRAME_SIZE(qoa->channels, QOA_SLICES_PER_FRAME); +} + +unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa) { + unsigned int p = 0; + if (size < QOA_MIN_FILESIZE) { + return 0; + } + + + /* Read the file header, verify the magic number ('qoaf') and read the + total number of samples. */ + qoa_uint64_t file_header = qoa_read_u64(bytes, &p); + + if ((file_header >> 32) != QOA_MAGIC) { + return 0; + } + + qoa->samples = file_header & 0xffffffff; + if (!qoa->samples) { + return 0; + } + + /* Peek into the first frame header to get the number of channels and + the samplerate. */ + qoa_uint64_t frame_header = qoa_read_u64(bytes, &p); + qoa->channels = (frame_header >> 56) & 0x0000ff; + qoa->samplerate = (frame_header >> 32) & 0xffffff; + + if (qoa->channels == 0 || qoa->samples == 0 || qoa->samplerate == 0) { + return 0; + } + + return 8; +} + +unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len) { + unsigned int p = 0; + *frame_len = 0; + + if (size < 8 + QOA_LMS_LEN * 4 * qoa->channels) { + return 0; + } + + /* 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; + + 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; + + if ( + channels != qoa->channels || + samplerate != qoa->samplerate || + frame_size > size || + samples * channels > max_total_samples + ) { + return 0; + } + + + /* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */ + for (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++) { + qoa->lms[c].history[i] = ((signed short)(history >> 48)); + history <<= 16; + qoa->lms[c].weights[i] = ((signed short)(weights >> 48)); + weights <<= 16; + } + } + + + /* 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++) { + qoa_uint64_t slice = qoa_read_u64(bytes, &p); + + int scalefactor = (slice >> 60) & 0xf; + 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 dequantized = qoa_dequant_tab[scalefactor][quantized]; + int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); + + sample_data[si] = reconstructed; + slice <<= 3; + + qoa_lms_update(&qoa->lms[c], reconstructed, dequantized); + } + } + } + + *frame_len = samples; + return p; +} + +short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) { + unsigned int p = qoa_decode_header(bytes, size, qoa); + if (!p) { + return NULL; + } + + /* 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)); + + unsigned int sample_index = 0; + unsigned int frame_len; + unsigned int frame_size; + + /* Decode all frames */ + do { + short *sample_ptr = sample_data + sample_index * qoa->channels; + frame_size = qoa_decode_frame(bytes + p, size - p, qoa, sample_ptr, &frame_len); + + p += frame_size; + sample_index += frame_len; + } while (frame_size && sample_index < qoa->samples); + + qoa->samples = sample_index; + return sample_data; +} + + + +/* ----------------------------------------------------------------------------- + File read/write convenience functions */ + +#ifndef QOA_NO_STDIO +#include + +int qoa_write(const char *filename, const short *sample_data, qoa_desc *qoa) { + FILE *f = fopen(filename, "wb"); + unsigned int size; + void *encoded; + + if (!f) { + return 0; + } + + encoded = qoa_encode(sample_data, qoa, &size); + if (!encoded) { + fclose(f); + return 0; + } + + fwrite(encoded, 1, size, f); + fclose(f); + + QOA_FREE(encoded); + return size; +} + +void *qoa_read(const char *filename, qoa_desc *qoa) { + FILE *f = fopen(filename, "rb"); + int size, bytes_read; + void *data; + short *sample_data; + + if (!f) { + return NULL; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + if (size <= 0) { + fclose(f); + return NULL; + } + fseek(f, 0, SEEK_SET); + + data = QOA_MALLOC(size); + if (!data) { + fclose(f); + return NULL; + } + + bytes_read = fread(data, 1, size, f); + fclose(f); + + sample_data = qoa_decode(data, bytes_read, qoa); + QOA_FREE(data); + return sample_data; +} + +#endif /* QOA_NO_STDIO */ +#endif /* QOA_IMPLEMENTATION */ diff --git a/src/raudio.c b/src/raudio.c index 591d6f69f..90de7fe9e 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -21,10 +21,11 @@ * * #define SUPPORT_FILEFORMAT_WAV * #define SUPPORT_FILEFORMAT_OGG +* #define SUPPORT_FILEFORMAT_MP3 +* #define SUPPORT_FILEFORMAT_QOA +* #define SUPPORT_FILEFORMAT_FLAC * #define SUPPORT_FILEFORMAT_XM * #define SUPPORT_FILEFORMAT_MOD -* #define SUPPORT_FILEFORMAT_FLAC -* #define SUPPORT_FILEFORMAT_MP3 * Selected desired fileformats to be supported for loading. Some of those formats are * supported by default, to remove support, just comment unrequired #define in this module * @@ -196,37 +197,6 @@ typedef struct tagBITMAPINFOHEADER { #endif #endif -#if defined(SUPPORT_FILEFORMAT_OGG) - // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE - #include "external/stb_vorbis.c" // OGG loading functions -#endif - -#if defined(SUPPORT_FILEFORMAT_XM) - #define JARXM_MALLOC RL_MALLOC - #define JARXM_FREE RL_FREE - -#if defined(_MSC_VER ) // jar xm has warnings on windows, so disable them just for this file -#pragma warning( push ) -#pragma warning( disable : 4244) -#endif - - #define JAR_XM_IMPLEMENTATION - #include "external/jar_xm.h" // XM loading functions - -#if defined(_MSC_VER ) -#pragma warning( pop ) -#endif - -#endif - -#if defined(SUPPORT_FILEFORMAT_MOD) - #define JARMOD_MALLOC RL_MALLOC - #define JARMOD_FREE RL_FREE - - #define JAR_MOD_IMPLEMENTATION - #include "external/jar_mod.h" // MOD loading functions -#endif - #if defined(SUPPORT_FILEFORMAT_WAV) #define DRWAV_MALLOC RL_MALLOC #define DRWAV_REALLOC RL_REALLOC @@ -236,6 +206,11 @@ typedef struct tagBITMAPINFOHEADER { #include "external/dr_wav.h" // WAV loading functions #endif +#if defined(SUPPORT_FILEFORMAT_OGG) + // TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE + #include "external/stb_vorbis.c" // OGG loading functions +#endif + #if defined(SUPPORT_FILEFORMAT_MP3) #define DRMP3_MALLOC RL_MALLOC #define DRMP3_REALLOC RL_REALLOC @@ -245,6 +220,14 @@ typedef struct tagBITMAPINFOHEADER { #include "external/dr_mp3.h" // MP3 loading functions #endif +#if defined(SUPPORT_FILEFORMAT_QOA) + #define QOA_MALLOC RL_MALLOC + #define QOA_FREE RL_FREE + + #define QOA_IMPLEMENTATION + #include "external/qoa.h" // QOA loading and saving functions +#endif + #if defined(SUPPORT_FILEFORMAT_FLAC) #define DRFLAC_MALLOC RL_MALLOC #define DRFLAC_REALLOC RL_REALLOC @@ -255,6 +238,31 @@ typedef struct tagBITMAPINFOHEADER { #include "external/dr_flac.h" // FLAC loading functions #endif +#if defined(SUPPORT_FILEFORMAT_XM) + #define JARXM_MALLOC RL_MALLOC + #define JARXM_FREE RL_FREE + + #if defined(_MSC_VER ) // jar_xm has warnings on windows, so disable them just for this file + #pragma warning( push ) + #pragma warning( disable : 4244) + #endif + + #define JAR_XM_IMPLEMENTATION + #include "external/jar_xm.h" // XM loading functions + + #if defined(_MSC_VER ) + #pragma warning( pop ) + #endif +#endif + +#if defined(SUPPORT_FILEFORMAT_MOD) + #define JARMOD_MALLOC RL_MALLOC + #define JARMOD_FREE RL_FREE + + #define JAR_MOD_IMPLEMENTATION + #include "external/jar_mod.h" // MOD loading functions +#endif + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -285,6 +293,7 @@ typedef enum { MUSIC_AUDIO_OGG, // OGG audio context MUSIC_AUDIO_FLAC, // FLAC audio context MUSIC_AUDIO_MP3, // MP3 audio context + MUSIC_AUDIO_QOA, // QOA audio context MUSIC_MODULE_XM, // XM module audio context MUSIC_MODULE_MOD // MOD module audio context } MusicContextType; @@ -795,19 +804,6 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int else TRACELOG(LOG_WARNING, "WAVE: Failed to load OGG data"); } #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) - else if (strcmp(fileType, ".flac") == 0) - { - unsigned long long int totalFrameCount = 0; - - // NOTE: We are forcing conversion to 16bit sample size on reading - wave.data = drflac_open_memory_and_read_pcm_frames_s16(fileData, dataSize, &wave.channels, &wave.sampleRate, &totalFrameCount, NULL); - wave.sampleSize = 16; - - if (wave.data != NULL) wave.frameCount = (unsigned int)totalFrameCount; - else TRACELOG(LOG_WARNING, "WAVE: Failed to load FLAC data"); - } -#endif #if defined(SUPPORT_FILEFORMAT_MP3) else if (strcmp(fileType, ".mp3") == 0) { @@ -827,6 +823,38 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int else TRACELOG(LOG_WARNING, "WAVE: Failed to load MP3 data"); } +#endif +#if defined(SUPPORT_FILEFORMAT_QOA) + else if (strcmp(fileType, ".qoa") == 0) + { + qoa_desc qoa = { 0 }; + + // NOTE: Returned sample data is always 16 bit? + wave.data = qoa_decode(fileData, dataSize, &qoa); + wave.sampleSize = 16; + + if (wave.data != NULL) + { + wave.channels = qoa.channels; + wave.sampleRate = qoa.samplerate; + wave.frameCount = qoa.samples; + } + else TRACELOG(LOG_WARNING, "WAVE: Failed to load QOA data"); + + } +#endif +#if defined(SUPPORT_FILEFORMAT_FLAC) + else if (strcmp(fileType, ".flac") == 0) + { + unsigned long long int totalFrameCount = 0; + + // NOTE: We are forcing conversion to 16bit sample size on reading + wave.data = drflac_open_memory_and_read_pcm_frames_s16(fileData, dataSize, &wave.channels, &wave.sampleRate, &totalFrameCount, NULL); + wave.sampleSize = 16; + + if (wave.data != NULL) wave.frameCount = (unsigned int)totalFrameCount; + else TRACELOG(LOG_WARNING, "WAVE: Failed to load FLAC data"); + } #endif else TRACELOG(LOG_WARNING, "WAVE: Data format not supported"); @@ -1316,23 +1344,6 @@ Music LoadMusicStream(const char *fileName) } } #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) - else if (IsFileExtension(fileName, ".flac")) - { - music.ctxType = MUSIC_AUDIO_FLAC; - music.ctxData = drflac_open_file(fileName, NULL); - - if (music.ctxData != NULL) - { - drflac *ctxFlac = (drflac *)music.ctxData; - - music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); - music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; - music.looping = true; // Looping enabled by default - musicLoaded = true; - } - } -#endif #if defined(SUPPORT_FILEFORMAT_MP3) else if (IsFileExtension(fileName, ".mp3")) { @@ -1351,6 +1362,45 @@ Music LoadMusicStream(const char *fileName) } } #endif +#if defined(SUPPORT_FILEFORMAT_QOA) + else if (IsFileExtension(fileName, ".qoa")) + { + qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); + + // TODO: QOA stream support: Init context from file + + music.ctxType = MUSIC_AUDIO_QOA; + music.ctxData = ctxQoa; + + if (result > 0) + { + music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels); + + // TODO: Read next frame(s) from QOA stream + //music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len); + + music.looping = true; // Looping enabled by default + musicLoaded = true; + } + } +#endif +#if defined(SUPPORT_FILEFORMAT_FLAC) + else if (IsFileExtension(fileName, ".flac")) + { + music.ctxType = MUSIC_AUDIO_FLAC; + music.ctxData = drflac_open_file(fileName, NULL); + + if (music.ctxData != NULL) + { + drflac *ctxFlac = (drflac *)music.ctxData; + + music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); + music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; + music.looping = true; // Looping enabled by default + musicLoaded = true; + } + } +#endif #if defined(SUPPORT_FILEFORMAT_XM) else if (IsFileExtension(fileName, ".xm")) { @@ -1408,12 +1458,15 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_OGG) else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); #endif - #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); - #endif #if defined(SUPPORT_FILEFORMAT_MP3) else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif + #if defined(SUPPORT_FILEFORMAT_QOA) + else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context data*/ RL_FREE(music.ctxData); } + #endif + #if defined(SUPPORT_FILEFORMAT_FLAC) + else if (music.ctxType == MUSIC_AUDIO_FLAC) 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); #endif @@ -1467,18 +1520,23 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } } #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) - else if (strcmp(fileType, ".flac") == 0) +#if defined(SUPPORT_FILEFORMAT_OGG) + else if (strcmp(fileType, ".ogg") == 0) { - music.ctxType = MUSIC_AUDIO_FLAC; - music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL); + // Open ogg audio stream + music.ctxType = MUSIC_AUDIO_OGG; + //music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL); + music.ctxData = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL); if (music.ctxData != NULL) { - drflac *ctxFlac = (drflac *)music.ctxData; + stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info - music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); - music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; + // OGG bit rate defaults to 16 bit, it's enough for compressed format + music.stream = LoadAudioStream(info.sample_rate, 16, info.channels); + + // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels + music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData); music.looping = true; // Looping enabled by default musicLoaded = true; } @@ -1502,23 +1560,40 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } } #endif -#if defined(SUPPORT_FILEFORMAT_OGG) - else if (strcmp(fileType, ".ogg") == 0) +#if defined(SUPPORT_FILEFORMAT_QOA) + else if (strcmp(fileType, ".qoa") == 0) { - // Open ogg audio stream - music.ctxType = MUSIC_AUDIO_OGG; - //music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL); - music.ctxData = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL); + qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); + + // TODO: Init QOA context data + + music.ctxType = MUSIC_AUDIO_QOA; + music.ctxData = ctxQoa; + + if (success) + { + music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels); + + // TODO: Read next frame(s) from QOA stream + //music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len); + + music.looping = true; // Looping enabled by default + musicLoaded = true; + } + } +#endif +#if defined(SUPPORT_FILEFORMAT_FLAC) + else if (strcmp(fileType, ".flac") == 0) + { + music.ctxType = MUSIC_AUDIO_FLAC; + music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL); if (music.ctxData != NULL) { - stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info + drflac *ctxFlac = (drflac *)music.ctxData; - // OGG bit rate defaults to 16 bit, it's enough for compressed format - music.stream = LoadAudioStream(info.sample_rate, 16, info.channels); - - // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels - music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData); + music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); + music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; music.looping = true; // Looping enabled by default musicLoaded = true; } @@ -1593,14 +1668,17 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, #if defined(SUPPORT_FILEFORMAT_WAV) else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); #endif - #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); + #if defined(SUPPORT_FILEFORMAT_OGG) + else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); #endif #if defined(SUPPORT_FILEFORMAT_MP3) else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif - #if defined(SUPPORT_FILEFORMAT_OGG) - else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); + #if defined(SUPPORT_FILEFORMAT_QOA) + else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context*/ RL_FREE(music.ctxData); } + #endif + #if defined(SUPPORT_FILEFORMAT_FLAC) + else if (music.ctxType == MUSIC_AUDIO_FLAC) 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); @@ -1645,12 +1723,15 @@ void UnloadMusicStream(Music music) #if defined(SUPPORT_FILEFORMAT_OGG) else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); -#endif #if defined(SUPPORT_FILEFORMAT_MP3) else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif +#if defined(SUPPORT_FILEFORMAT_QOA) + else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context*/ RL_FREE(music.ctxData); } +#endif +#if defined(SUPPORT_FILEFORMAT_FLAC) + else if (music.ctxType == MUSIC_AUDIO_FLAC) 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); #endif @@ -1700,12 +1781,15 @@ void StopMusicStream(Music music) #if defined(SUPPORT_FILEFORMAT_OGG) case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break; #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) - case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break; -#endif #if defined(SUPPORT_FILEFORMAT_MP3) case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break; #endif +#if defined(SUPPORT_FILEFORMAT_QOA) + case MUSIC_AUDIO_QOA: /*TODO: Restart QOA context to beginning*/ break; +#endif +#if defined(SUPPORT_FILEFORMAT_FLAC) + case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break; +#endif #if defined(SUPPORT_FILEFORMAT_XM) case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break; #endif @@ -1732,11 +1816,14 @@ void SeekMusicStream(Music music, float position) #if defined(SUPPORT_FILEFORMAT_OGG) case MUSIC_AUDIO_OGG: stb_vorbis_seek_frame((stb_vorbis *)music.ctxData, positionInFrames); break; #endif -#if defined(SUPPORT_FILEFORMAT_FLAC) - case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; -#endif #if defined(SUPPORT_FILEFORMAT_MP3) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; +#endif +#if defined(SUPPORT_FILEFORMAT_QOA) + case MUSIC_AUDIO_QOA: /*TODO: Seek to specific QOA frame*/ break; +#endif +#if defined(SUPPORT_FILEFORMAT_FLAC) + case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; #endif default: break; } @@ -1754,6 +1841,7 @@ void UpdateMusicStream(Music music) // On first call of this function we lazily pre-allocated a temp buffer to read audio files/memory data in int frameSize = music.stream.channels*music.stream.sampleSize/8; unsigned int pcmSize = subBufferSizeInFrames*frameSize; + if (AUDIO.System.pcmBufferSize < pcmSize) { RL_FREE(AUDIO.System.pcmBuffer); @@ -1815,19 +1903,6 @@ void UpdateMusicStream(Music music) } } break; #endif - #if defined(SUPPORT_FILEFORMAT_FLAC) - case MUSIC_AUDIO_FLAC: - { - while (true) - { - int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); - frameCountRedTotal += frameCountRed; - frameCountStillNeeded -= frameCountRed; - if (frameCountStillNeeded == 0) break; - else drflac__seek_to_first_frame((drflac *)music.ctxData); - } - } break; - #endif #if defined(SUPPORT_FILEFORMAT_MP3) case MUSIC_AUDIO_MP3: { @@ -1841,6 +1916,25 @@ void UpdateMusicStream(Music music) } } break; #endif + #if defined(SUPPORT_FILEFORMAT_QOA) + case MUSIC_AUDIO_QOA: + { + // TODO: Read QOA required framecount to fill buffer to keep music playing + } break; + #endif + #if defined(SUPPORT_FILEFORMAT_FLAC) + case MUSIC_AUDIO_FLAC: + { + while (true) + { + int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); + frameCountRedTotal += frameCountRed; + frameCountStillNeeded -= frameCountRed; + if (frameCountStillNeeded == 0) break; + else drflac__seek_to_first_frame((drflac *)music.ctxData); + } + } break; + #endif #if defined(SUPPORT_FILEFORMAT_XM) case MUSIC_MODULE_XM: { From 589892af07e8ee7392f9ab7b8c9bb0f8263c4e48 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Feb 2023 20:27:47 +0100 Subject: [PATCH 0250/1710] Support QOA audio format on `ExportWave()` -WIP- --- src/raudio.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index 90de7fe9e..c66a99c19 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -985,6 +985,18 @@ bool ExportWave(Wave wave, const char *fileName) drwav_free(fileData, NULL); } +#endif +#if defined(SUPPORT_FILEFORMAT_QOA) + else if (IsFileExtension(fileName, ".qoa")) + { + qoa_desc qoa = { 0 }; + qoa.channels = wave.channels; + qoa.samplerate = wave.sampleRate; + qoa.samples = wave.frameCount; + + // TODO: Review wave.data format required for export + success = qoa_write(fileName, wave.data, &qoa); + } #endif else if (IsFileExtension(fileName, ".raw")) { From 8c50da167de3854c4be2f0690a3e801e7723f980 Mon Sep 17 00:00:00 2001 From: HKrogstie Date: Sun, 5 Feb 2023 11:04:30 +0100 Subject: [PATCH 0251/1710] fix DrawMesh using SHADER_LOC_COLOR_SPECULAR as a material map (#2908) (#2909) --- src/rmodels.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 3c9384d2d..3976b92e5 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1373,10 +1373,10 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) 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 + (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); From 60d0ab418a3011fe5df663ac4be37ee53bcc5393 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Feb 2023 11:44:12 +0100 Subject: [PATCH 0252/1710] some typos review --- src/raymath.h | 4 ++-- src/rcore.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index 96c241843..86134d090 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -912,7 +912,7 @@ RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) { Vector3 result = { 0 }; - // Calculate unproject matrix (multiply view matrix by projection matrix) and invert it + // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it Matrix matViewProj = { // MatrixMultiply(view, projection); view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, @@ -975,7 +975,7 @@ RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) // Create quaternion from source point Quaternion quat = { source.x, source.y, source.z, 1.0f }; - // Multiply quat point by unproject matrix + // Multiply quat point by unprojecte matrix Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, diff --git a/src/rcore.c b/src/rcore.c index f2f0cbbe0..ee88727d5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3032,7 +3032,7 @@ const char *GetDirectoryPath(const char *filePath) if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/') { // For security, we set starting path to current directory, - // obtained path will be concated to this + // obtained path will be concatenated to this dirPath[0] = '.'; dirPath[1] = '/'; } From ff70a04bf5e2aea84fe9d71cede790e1a031f810 Mon Sep 17 00:00:00 2001 From: star-tek-mb Date: Sun, 5 Feb 2023 17:03:03 +0500 Subject: [PATCH 0253/1710] update zig build to latest master (#2910) also, adds package manager support --- build.zig | 18 ++++++++++++++++++ examples/build.zig | 34 ++++++++++++++++++---------------- src/build.zig | 15 +++++++++++---- 3 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 build.zig diff --git a/build.zig b/build.zig new file mode 100644 index 000000000..9959d8e0c --- /dev/null +++ b/build.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const raylib = @import("src/build.zig"); + +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const lib = raylib.addRaylib(b, target, optimize); + lib.installHeader("src/raylib.h", "raylib.h"); + lib.install(); +} \ No newline at end of file diff --git a/examples/build.zig b/examples/build.zig index eb040fd2f..c0479910f 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,15 +1,11 @@ const std = @import("std"); const builtin = @import("builtin"); -fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zig.CrossTarget) !*std.build.Step { +fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); } - // Standard release options allow the person running `zig build` to select - // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. - const mode = b.standardReleaseOptions(); - const all = b.step(module, "All " ++ module ++ " examples"); const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); @@ -22,10 +18,12 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi // zig's mingw headers do not include pthread.h if (std.mem.eql(u8, "core_loading_thread", name) and target.getOsTag() == .windows) continue; - const exe = b.addExecutable(name, null); + const exe = b.addExecutable(.{ + .name = name, + .target = target, + .optimize = optimize, + }); exe.addCSourceFile(path, &[_][]const u8{}); - exe.setTarget(target); - exe.setBuildMode(mode); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { .windows => "../src/raylib.lib", @@ -89,15 +87,19 @@ pub fn build(b: *std.build.Builder) !void { // means any target is allowed, and the default is native. Other options // for restricting supported target set are available. const target = b.standardTargetOptions(.{}); + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); const all = b.getInstallStep(); - all.dependOn(try add_module("audio", b, target)); - all.dependOn(try add_module("core", b, target)); - all.dependOn(try add_module("models", b, target)); - all.dependOn(try add_module("others", b, target)); - all.dependOn(try add_module("shaders", b, target)); - all.dependOn(try add_module("shapes", b, target)); - all.dependOn(try add_module("text", b, target)); - all.dependOn(try add_module("textures", b, target)); + all.dependOn(try add_module("audio", b, target, optimize)); + all.dependOn(try add_module("core", b, target, optimize)); + all.dependOn(try add_module("models", b, target, optimize)); + all.dependOn(try add_module("others", b, target, optimize)); + all.dependOn(try add_module("shaders", b, target, optimize)); + all.dependOn(try add_module("shapes", b, target, optimize)); + all.dependOn(try add_module("text", b, target, optimize)); + all.dependOn(try add_module("textures", b, target, optimize)); } diff --git a/src/build.zig b/src/build.zig index 919db3e9f..adf7fdc9d 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.LibExeObjStep { +pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.build.LibExeObjStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", "-D_GNU_SOURCE", @@ -8,8 +8,11 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; - const raylib = b.addStaticLibrary("raylib", null); - raylib.setTarget(target); + const raylib = b.addStaticLibrary(.{ + .name = "raylib", + .target = target, + .optimize = optimize, + }); raylib.linkLibC(); raylib.addIncludePath(srcdir ++ "/external/glfw/include"); @@ -106,8 +109,12 @@ pub fn build(b: *std.build.Builder) void { // means any target is allowed, and the default is native. Other options // for restricting supported target set are available. const target = b.standardTargetOptions(.{}); + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); - const lib = addRaylib(b, target); + const lib = addRaylib(b, target, optimize); lib.setOutputDir(srcdir); lib.install(); } From c91190fc6e9355f58c15a0a17990ca9b450aaf5d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Feb 2023 16:28:04 +0100 Subject: [PATCH 0254/1710] Review QOA sound loading -WIP- --- src/raudio.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index c66a99c19..524aaef43 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -829,18 +829,26 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int { qoa_desc qoa = { 0 }; - // NOTE: Returned sample data is always 16 bit? - wave.data = qoa_decode(fileData, dataSize, &qoa); - wave.sampleSize = 16; - - if (wave.data != NULL) + unsigned int result = qoa_decode_header(fileData, dataSize, &qoa); + + if (result > 0) { - wave.channels = qoa.channels; - wave.sampleRate = qoa.samplerate; + + // Calculate the total audio frame count wave.frameCount = qoa.samples; + + // NOTE: Returned sample data is always 16 bit + wave.data = qoa_decode(fileData, dataSize, &qoa); + wave.sampleSize = 16; + + if (wave.data != NULL) + { + wave.channels = qoa.channels; + wave.sampleRate = qoa.samplerate; + } + else TRACELOG(LOG_WARNING, "WAVE: Failed to load QOA data"); } else TRACELOG(LOG_WARNING, "WAVE: Failed to load QOA data"); - } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) @@ -1380,6 +1388,7 @@ Music LoadMusicStream(const char *fileName) qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); // TODO: QOA stream support: Init context from file + int result = 0; music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; @@ -1578,11 +1587,12 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); // TODO: Init QOA context data + int result = 0; music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; - if (success) + if (result > 0) { music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels); From 1fea26647214308774c4630d10883f42d608fa9d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Feb 2023 16:30:23 +0100 Subject: [PATCH 0255/1710] Clean trailing spaces --- src/raudio.c | 42 ++++------ src/raymath.h | 6 +- src/rcore.c | 16 ++-- src/rglfw.c | 10 +-- src/rlgl.h | 46 +++++------ src/rmodels.c | 214 ++++++++++++++++++++++++------------------------ src/rshapes.c | 8 +- src/rtext.c | 10 +-- src/rtextures.c | 16 ++-- 9 files changed, 179 insertions(+), 189 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 524aaef43..04ae35c5a 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -528,7 +528,7 @@ void CloseAudioDevice(void) RL_FREE(AUDIO.System.pcmBuffer); AUDIO.System.pcmBuffer = NULL; AUDIO.System.pcmBufferSize = 0; - + TRACELOG(LOG_INFO, "AUDIO: Device closed successfully"); } else TRACELOG(LOG_WARNING, "AUDIO: Device could not be closed, not currently initialized"); @@ -829,26 +829,18 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int { qoa_desc qoa = { 0 }; - unsigned int result = qoa_decode_header(fileData, dataSize, &qoa); + // NOTE: Returned sample data is always 16 bit? + wave.data = qoa_decode(fileData, dataSize, &qoa); + wave.sampleSize = 16; - if (result > 0) + if (wave.data != NULL) { - - // Calculate the total audio frame count + wave.channels = qoa.channels; + wave.sampleRate = qoa.samplerate; wave.frameCount = qoa.samples; - - // NOTE: Returned sample data is always 16 bit - wave.data = qoa_decode(fileData, dataSize, &qoa); - wave.sampleSize = 16; - - if (wave.data != NULL) - { - wave.channels = qoa.channels; - wave.sampleRate = qoa.samplerate; - } - else TRACELOG(LOG_WARNING, "WAVE: Failed to load QOA data"); } else TRACELOG(LOG_WARNING, "WAVE: Failed to load QOA data"); + } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) @@ -1001,7 +993,7 @@ bool ExportWave(Wave wave, const char *fileName) qoa.channels = wave.channels; qoa.samplerate = wave.sampleRate; qoa.samples = wave.frameCount; - + // TODO: Review wave.data format required for export success = qoa_write(fileName, wave.data, &qoa); } @@ -1388,7 +1380,6 @@ Music LoadMusicStream(const char *fileName) qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); // TODO: QOA stream support: Init context from file - int result = 0; music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; @@ -1399,7 +1390,7 @@ Music LoadMusicStream(const char *fileName) // TODO: Read next frame(s) from QOA stream //music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len); - + music.looping = true; // Looping enabled by default musicLoaded = true; } @@ -1585,20 +1576,19 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, else if (strcmp(fileType, ".qoa") == 0) { qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); - + // TODO: Init QOA context data - int result = 0; - + music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; - if (result > 0) + if (success) { music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels); - + // TODO: Read next frame(s) from QOA stream //music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len); - + music.looping = true; // Looping enabled by default musicLoaded = true; } @@ -1863,7 +1853,7 @@ void UpdateMusicStream(Music music) // On first call of this function we lazily pre-allocated a temp buffer to read audio files/memory data in int frameSize = music.stream.channels*music.stream.sampleSize/8; unsigned int pcmSize = subBufferSizeInFrames*frameSize; - + if (AUDIO.System.pcmBufferSize < pcmSize) { RL_FREE(AUDIO.System.pcmBuffer); diff --git a/src/raymath.h b/src/raymath.h index 86134d090..422a42eee 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -311,7 +311,7 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { float result = atan2f(v2.y - v1.y, v2.x - v1.x); - + return result; } @@ -321,14 +321,14 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - + float dot = start.x*end.x + start.y*end.y; // Dot product float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp if (dotClamp > 1.0f) dotClamp = 1.0f; result = acosf(dotClamp); - + // Alternative implementation, more costly //float v1Length = sqrtf((start.x*start.x) + (start.y*start.y)); //float v2Length = sqrtf((end.x*end.x) + (end.y*end.y)); diff --git a/src/rcore.c b/src/rcore.c index ee88727d5..98800a790 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1955,10 +1955,10 @@ const char *GetClipboardText(void) .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 @@ -2478,7 +2478,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) // vertex texcoord2 location = 5 // NOTE: If any location is not found, loc point becomes -1 - + shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); // All locations reseted to -1 (no location) @@ -2521,7 +2521,7 @@ void UnloadShader(Shader shader) if (shader.id != rlGetShaderIdDefault()) { rlUnloadShaderProgram(shader.id); - + // NOTE: If shader loading failed, it should be 0 RL_FREE(shader.locs); } @@ -2846,7 +2846,7 @@ void TakeScreenshot(const char *fileName) // Get a random value between min and max (both included) // WARNING: Ranges higher than RAND_MAX will return invalid results -// More specifically, if (max - min) > INT_MAX there will be an overflow, +// 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 int GetRandomValue(int min, int max) { @@ -2856,7 +2856,7 @@ int GetRandomValue(int min, int max) max = min; min = tmp; } - + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); @@ -5756,7 +5756,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) CORE.Input.Gamepad.ready[0] = true; GamepadButton button = AndroidTranslateGamepadButton(keycode); - + if (button == GAMEPAD_BUTTON_UNKNOWN) return 1; if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) @@ -5764,7 +5764,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) CORE.Input.Gamepad.currentButtonState[0][button] = 1; } else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up - + return 1; // Handled gamepad button } diff --git a/src/rglfw.c b/src/rglfw.c index 940257ab1..794e0d2e4 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -75,7 +75,7 @@ #include "external/glfw/src/win32_time.c" #include "external/glfw/src/win32_thread.c" #include "external/glfw/src/wgl_context.c" - + #include "external/glfw/src/egl_context.c" #include "external/glfw/src/osmesa_context.c" #endif @@ -87,10 +87,10 @@ #include "external/glfw/src/posix_poll.c" #include "external/glfw/src/linux_joystick.c" #include "external/glfw/src/xkb_unicode.c" - + #include "external/glfw/src/egl_context.c" #include "external/glfw/src/osmesa_context.c" - + #if defined(_GLFW_WAYLAND) #include "external/glfw/src/wl_init.c" #include "external/glfw/src/wl_monitor.c" @@ -110,7 +110,7 @@ #include "external/glfw/src/posix_time.c" #include "external/glfw/src/null_joystick.c" #include "external/glfw/src/xkb_unicode.c" - + #include "external/glfw/src/x11_init.c" #include "external/glfw/src/x11_monitor.c" #include "external/glfw/src/x11_window.c" @@ -129,7 +129,7 @@ #include "external/glfw/src/cocoa_window.m" #include "external/glfw/src/cocoa_time.c" #include "external/glfw/src/nsgl_context.m" - + #include "external/glfw/src/egl_context.c" #include "external/glfw/src/osmesa_context.c" #endif diff --git a/src/rlgl.h b/src/rlgl.h index 2b982a9a1..ebd0f4de6 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -281,33 +281,33 @@ // GL blending factors #define RL_ZERO 0 // GL_ZERO -#define RL_ONE 1 // GL_ONE -#define RL_SRC_COLOR 0x0300 // GL_SRC_COLOR -#define RL_ONE_MINUS_SRC_COLOR 0x0301 // GL_ONE_MINUS_SRC_COLOR -#define RL_SRC_ALPHA 0x0302 // GL_SRC_ALPHA -#define RL_ONE_MINUS_SRC_ALPHA 0x0303 // GL_ONE_MINUS_SRC_ALPHA -#define RL_DST_ALPHA 0x0304 // GL_DST_ALPHA -#define RL_ONE_MINUS_DST_ALPHA 0x0305 // GL_ONE_MINUS_DST_ALPHA -#define RL_DST_COLOR 0x0306 // GL_DST_COLOR -#define RL_ONE_MINUS_DST_COLOR 0x0307 // GL_ONE_MINUS_DST_COLOR -#define RL_SRC_ALPHA_SATURATE 0x0308 // GL_SRC_ALPHA_SATURATE -#define RL_CONSTANT_COLOR 0x8001 // GL_CONSTANT_COLOR +#define RL_ONE 1 // GL_ONE +#define RL_SRC_COLOR 0x0300 // GL_SRC_COLOR +#define RL_ONE_MINUS_SRC_COLOR 0x0301 // GL_ONE_MINUS_SRC_COLOR +#define RL_SRC_ALPHA 0x0302 // GL_SRC_ALPHA +#define RL_ONE_MINUS_SRC_ALPHA 0x0303 // GL_ONE_MINUS_SRC_ALPHA +#define RL_DST_ALPHA 0x0304 // GL_DST_ALPHA +#define RL_ONE_MINUS_DST_ALPHA 0x0305 // GL_ONE_MINUS_DST_ALPHA +#define RL_DST_COLOR 0x0306 // GL_DST_COLOR +#define RL_ONE_MINUS_DST_COLOR 0x0307 // GL_ONE_MINUS_DST_COLOR +#define RL_SRC_ALPHA_SATURATE 0x0308 // GL_SRC_ALPHA_SATURATE +#define RL_CONSTANT_COLOR 0x8001 // GL_CONSTANT_COLOR #define RL_ONE_MINUS_CONSTANT_COLOR 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR -#define RL_CONSTANT_ALPHA 0x8003 // GL_CONSTANT_ALPHA +#define RL_CONSTANT_ALPHA 0x8003 // GL_CONSTANT_ALPHA #define RL_ONE_MINUS_CONSTANT_ALPHA 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA // GL blending functions/equations -#define RL_FUNC_ADD 0x8006 // GL_FUNC_ADD -#define RL_FUNC_SUBTRACT 0x800A // GL_FUNC_SUBTRACT +#define RL_FUNC_ADD 0x8006 // GL_FUNC_ADD +#define RL_FUNC_SUBTRACT 0x800A // GL_FUNC_SUBTRACT #define RL_FUNC_REVERSE_SUBTRACT 0x800B // GL_FUNC_REVERSE_SUBTRACT -#define RL_BLEND_EQUATION 0x8009 // GL_BLEND_EQUATION +#define RL_BLEND_EQUATION 0x8009 // GL_BLEND_EQUATION #define RL_BLEND_EQUATION_RGB 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION) -#define RL_BLEND_EQUATION_ALPHA 0x883D // GL_BLEND_EQUATION_ALPHA -#define RL_BLEND_DST_RGB 0x80C8 // GL_BLEND_DST_RGB -#define RL_BLEND_SRC_RGB 0x80C9 // GL_BLEND_SRC_RGB -#define RL_BLEND_DST_ALPHA 0x80CA // GL_BLEND_DST_ALPHA -#define RL_BLEND_SRC_ALPHA 0x80CB // GL_BLEND_SRC_ALPHA -#define RL_BLEND_COLOR 0x8005 // GL_BLEND_COLOR +#define RL_BLEND_EQUATION_ALPHA 0x883D // GL_BLEND_EQUATION_ALPHA +#define RL_BLEND_DST_RGB 0x80C8 // GL_BLEND_DST_RGB +#define RL_BLEND_SRC_RGB 0x80C9 // GL_BLEND_SRC_RGB +#define RL_BLEND_DST_ALPHA 0x80CA // GL_BLEND_DST_ALPHA +#define RL_BLEND_SRC_ALPHA 0x80CB // GL_BLEND_SRC_ALPHA +#define RL_BLEND_COLOR 0x8005 // GL_BLEND_COLOR //---------------------------------------------------------------------------------- @@ -790,7 +790,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, - // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 + // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 #if defined(PLATFORM_DESKTOP) #define GLAD_GLES2_IMPLEMENTATION #include "external/glad_gles2.h" @@ -3042,7 +3042,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // In case depth textures not supported, we force renderbuffer usage if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true; - + // NOTE: We let the implementation to choose the best bit-depth // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F unsigned int glInternalFormat = GL_DEPTH_COMPONENT; diff --git a/src/rmodels.c b/src/rmodels.c index 3976b92e5..4a8261795 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -112,7 +112,7 @@ #include "external/par_shapes.h" // Shapes 3d parametric generation #if defined(_MSC_VER ) // disable MSVC warning suppression for par shapes -#pragma warning( pop ) +#pragma warning( pop ) #endif #endif @@ -692,7 +692,7 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int 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}; @@ -704,7 +704,7 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int Vector3 capCenter = endPos; float baseSliceAngle = (2.0f*PI)/slices; - float baseRingAngle = PI * 0.5f / rings; + float baseRingAngle = PI * 0.5f / rings; rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -714,7 +714,7 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int { for (int i = 0; i < rings; i++) { - for (int j = 0; j < slices; j++) + for (int j = 0; j < slices; j++) { // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier @@ -725,32 +725,32 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int // 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){ + 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.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 + 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 + 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 + 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 @@ -759,10 +759,10 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int 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); + + rlVertex3f(w2.x, w2.y, w2.z); + rlVertex3f(w4.x, w4.y, w4.z); + rlVertex3f(w3.x, w3.y, w3.z); } else { @@ -770,9 +770,9 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int 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); + rlVertex3f(w2.x, w2.y, w2.z); + rlVertex3f(w3.x, w3.y, w3.z); + rlVertex3f(w4.x, w4.y, w4.z); } } } @@ -782,37 +782,37 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int // render middle if (!sphereCase) { - for (int j = 0; j < slices; j++) + 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 = { + 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 + 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 + 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 + 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 + 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 / @@ -847,7 +847,7 @@ void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices Vector3 capCenter = endPos; float baseSliceAngle = (2.0f*PI)/slices; - float baseRingAngle = PI * 0.5f / rings; + float baseRingAngle = PI * 0.5f / rings; rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -857,7 +857,7 @@ void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices { for (int i = 0; i < rings; i++) { - for (int j = 0; j < slices; j++) + for (int j = 0; j < slices; j++) { // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier @@ -868,32 +868,32 @@ void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices // 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){ + 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.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 + 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 + 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 + 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); @@ -904,12 +904,12 @@ void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices 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(w4.x, w4.y, w4.z); rlVertex3f(w3.x, w3.y, w3.z); - rlVertex3f(w4.x, w4.y, w4.z); + rlVertex3f(w4.x, w4.y, w4.z); } } capCenter = startPos; @@ -918,46 +918,46 @@ void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices // render middle if (!sphereCase) { - for (int j = 0; j < slices; j++) + 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 = { + 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 + 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 + 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 + 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 + 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(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(w4.x, w4.y, w4.z); - rlVertex3f(w2.x, w2.y, w2.z); + rlVertex3f(w2.x, w2.y, w2.z); rlVertex3f(w3.x, w3.y, w3.z); } } @@ -1373,10 +1373,10 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) 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 + (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); @@ -2024,7 +2024,7 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) 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); @@ -2065,7 +2065,7 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) 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; @@ -4748,7 +4748,7 @@ static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount) // Find parent bone index unsigned int parentIndex = -1; - + for (unsigned int j = 0; j < skin.joints_count; j++) { if (skin.joints[j] == node.parent) @@ -5238,12 +5238,12 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo float tstart = 0.0f; float tend = 0.0f; int keyframe = 0; // Defaults to first pose - + for (int i = 0; i < 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; @@ -5278,11 +5278,11 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo cgltf_accessor_read_float(output, keyframe+1, tmp, 4); Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]}; Vector4 *r = data; - + // Only v4 is for rotations, so we know it's a quat *r = QuaternionSlerp(v1, v2, t); } - + return true; } @@ -5295,12 +5295,12 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in unsigned char *fileData = LoadFileData(fileName, &dataSize); ModelAnimation *animations = NULL; - + // glTF data loading cgltf_options options = { 0 }; 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); @@ -5318,7 +5318,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in 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); @@ -5333,12 +5333,12 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in 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]) @@ -5372,12 +5372,12 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in { 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] Only linear interpolation curves are supported 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); @@ -5394,13 +5394,13 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in { 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 = {0, 0, 0}; Quaternion rotation = {0, 0, 0, 1}; Vector3 scale = {1, 1, 1}; - + if (boneChannels[k].translate) { if (!GetPoseAtTimeGLTF(boneChannels[k].translate->sampler->input, boneChannels[k].translate->sampler->output, time, &translation)) @@ -5438,7 +5438,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s (%d frames, %fs)", fileName, animData.name, animations[i].frameCount, animDuration); RL_FREE(boneChannels); } - } + } else TRACELOG(LOG_ERROR, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count); cgltf_free(data); @@ -5632,7 +5632,7 @@ static Model LoadM3D(const char *fileName) 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 color map is provided, we allocate storage for vertex colors if (m3d->cmap != NULL) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); @@ -5643,7 +5643,7 @@ static Model LoadM3D(const char *fileName) 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; } @@ -5658,7 +5658,7 @@ static Model LoadM3D(const char *fileName) 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) { @@ -5809,7 +5809,7 @@ static Model LoadM3D(const char *fileName) 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 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; @@ -5918,7 +5918,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY); - + if (pose != NULL) { for (j = 0; j < (int)m3d->numbone; j++) diff --git a/src/rshapes.c b/src/rshapes.c index 86e014e2a..4c5072459 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -300,7 +300,7 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP float dy = current.y-previous.y; float dx = current.x-previous.x; float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - + if (i==1) { points[0].x = previous.x+dy*size; @@ -1639,19 +1639,19 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 bool CheckCollisionPointPoly(Vector2 point, Vector2 *points, int pointCount) { bool collision = false; - + if (pointCount > 2) { for (int i = 0; i < pointCount - 1; i++) { Vector2 vc = points[i]; Vector2 vn = points[i + 1]; - + if ((((vc.y >= point.y) && (vn.y < point.y)) || ((vc.y < point.y) && (vn.y >= point.y))) && (point.x < ((vn.x - vc.x)*(point.y - vc.y)/(vn.y - vc.y) + vc.x))) collision = !collision; } } - + return collision; } diff --git a/src/rtext.c b/src/rtext.c index 9908b44df..52fc615df 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1869,7 +1869,7 @@ int GetCodepointNext(const char *text, int *codepointSize) const char *ptr = text; int codepoint = 0x3f; // Codepoint (defaults to '?') *codepointSize = 0; - + // Get current codepoint and bytes processed if (0xf0 == (0xf8 & ptr[0])) { @@ -1882,20 +1882,20 @@ int GetCodepointNext(const char *text, int *codepointSize) // 3 byte UTF-8 codepoint */ 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 codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); *codepointSize = 2; - } + } else { // 1 byte UTF-8 codepoint codepoint = ptr[0]; *codepointSize = 1; } - + return codepoint; } @@ -1910,7 +1910,7 @@ int GetCodepointPrevious(const char *text, int *codepointSize) // Move to previous codepoint do ptr--; while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) == 0x80)); - + codepoint = GetCodepointNext(ptr, &cpSize); if (codepoint != 0) *codepointSize = cpSize; diff --git a/src/rtextures.c b/src/rtextures.c index 89a91b051..c3744f640 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -923,7 +923,7 @@ Image GenImageCellular(int width, int height, int tileSize) Image GenImageText(int width, int height, const char *text) { Image image = { 0 }; - + int textLength = TextLength(text); int imageViewSize = width*height; @@ -1551,7 +1551,7 @@ void ImageBlurGaussian(Image *image, int blurSize) { float avgAlpha = 0.0f; int convolutionSize = blurSize+1; - for (int i = 0; i < blurSize+1; i++) + for (int i = 0; i < blurSize+1; i++) { avgR += pixelsCopy1[row*image->width + i].x; avgG += pixelsCopy1[row*image->width + i].y; @@ -1600,7 +1600,7 @@ void ImageBlurGaussian(Image *image, int blurSize) { float avgAlpha = 0.0f; int convolutionSize = blurSize+1; - for (int i = 0; i < blurSize+1; i++) + for (int i = 0; i < blurSize+1; i++) { avgR += pixelsCopy2[i*image->width + col].x; avgG += pixelsCopy2[i*image->width + col].y; @@ -2931,7 +2931,7 @@ void ImageDrawCircle(Image* dst, int centerX, int centerY, int radius, Color col { y--; decesionParameter = decesionParameter + 4*(x - y) + 10; - } + } else decesionParameter = decesionParameter + 4*x + 6; } } @@ -3998,10 +3998,10 @@ Color ColorTint(Color color, Color tint) Color ColorBrightness(Color color, float factor) { Color result = color; - + if (factor > 1.0f) factor = 1.0f; else if (factor < -1.0f) factor = -1.0f; - + float red = (float)color.r; float green = (float)color.g; float blue = (float)color.b; @@ -4019,7 +4019,7 @@ Color ColorBrightness(Color color, float factor) green = (255 - green)*factor + green; blue = (255 - blue)*factor + blue; } - + result.r = (unsigned char)red; result.g = (unsigned char)green; result.b = (unsigned char)blue; @@ -4032,7 +4032,7 @@ Color ColorBrightness(Color color, float factor) Color ColorContrast(Color color, float contrast) { Color result = color; - + if (contrast < -1.0f) contrast = -1.0f; else if (contrast > 1.0f) contrast = 1.0f; From 04ab76b88927981844aedcdeaebd8f1866a32406 Mon Sep 17 00:00:00 2001 From: star-tek-mb Date: Mon, 6 Feb 2023 14:28:24 +0500 Subject: [PATCH 0256/1710] zig build: do not use deprecated functions (#2913) --- examples/build.zig | 4 ++-- src/build.zig | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/build.zig b/examples/build.zig index c0479910f..65586f12f 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.build.Step { +fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); } @@ -81,7 +81,7 @@ fn add_module(comptime module: []const u8, b: *std.build.Builder, target: std.zi return all; } -pub fn build(b: *std.build.Builder) !void { +pub fn build(b: *std.Build) !void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which // means any target is allowed, and the default is native. Other options diff --git a/src/build.zig b/src/build.zig index adf7fdc9d..84684fef8 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.build.LibExeObjStep { +pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", "-D_GNU_SOURCE", @@ -103,7 +103,7 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget, optimize: s return raylib; } -pub fn build(b: *std.build.Builder) void { +pub fn build(b: *std.Build) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which // means any target is allowed, and the default is native. Other options From e187b693eaa4873a0d44d6578f44c269fd751315 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Feb 2023 11:02:46 +0100 Subject: [PATCH 0257/1710] Update raudio.c --- src/raudio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 04ae35c5a..4705ca582 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1380,6 +1380,7 @@ Music LoadMusicStream(const char *fileName) qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); // TODO: QOA stream support: Init context from file + int result = 0; music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; @@ -1578,11 +1579,12 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); // TODO: Init QOA context data + int result = 0; music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; - if (success) + if (result > 0) { music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels); From f784961b9ce8f516c60b11d73444803db363ca0d Mon Sep 17 00:00:00 2001 From: Nikolas Date: Tue, 7 Feb 2023 22:18:14 +0100 Subject: [PATCH 0258/1710] Enable GetWindowHandle() on macOS (#2915) --- src/rcore.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 98800a790..7af655167 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -242,6 +242,7 @@ #include // Required for: usleep() //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition + void *glfwGetCocoaWindow(GLFWwindow* handle); #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() #endif @@ -1677,7 +1678,7 @@ void *GetWindowHandle(void) #endif #if defined(__APPLE__) // NOTE: Returned handle is: (objc_object *) - return NULL; // TODO: return (void *)glfwGetCocoaWindow(window); + return (void *)glfwGetCocoaWindow(CORE.Window.handle); #endif return NULL; From 5c6a756014fa27e6ec534e6bafa4cf4e1fac890c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sasquatch=20=E3=83=BD=28=C2=B4=E3=83=BC=60=20=29=E2=94=8C?= <74960320+foreignsasquatch@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:15:34 +0530 Subject: [PATCH 0259/1710] Update raylib-hx bindings to 4.2 in BINDINGS.md (#2916) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 1bc2c57ac..f703296a7 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -27,7 +27,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | | h-raylib | 4.5-dev | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | -| raylib-hx | 4.0 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | +| raylib-hx | **4.2** | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | | raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | From 6ae21d6581e6030dbb218a932d3ae280e26481ff Mon Sep 17 00:00:00 2001 From: "Julio C. Galindo" <54072664+stickM4N@users.noreply.github.com> Date: Thu, 9 Feb 2023 07:17:47 -0500 Subject: [PATCH 0260/1710] Fixed some grammar mistakes and typos. (#2914) * Fixed some grammar mistakes. * Fixed some typos. --- src/config.h | 18 ++++++++-------- src/minshell.html | 2 +- src/raudio.c | 20 +++++++++--------- src/raylib.h | 16 +++++++-------- src/rcamera.h | 2 +- src/rcore.c | 52 +++++++++++++++++++++++------------------------ src/rgestures.h | 10 ++++----- src/rlgl.h | 2 +- src/rmodels.c | 36 ++++++++++++++++---------------- src/rshapes.c | 6 +++--- src/rtext.c | 22 ++++++++++---------- src/rtextures.c | 26 ++++++++++++------------ src/shell.html | 2 +- src/utils.c | 8 ++++---- 14 files changed, 111 insertions(+), 111 deletions(-) diff --git a/src/config.h b/src/config.h index be24c0190..48f66aba4 100644 --- a/src/config.h +++ b/src/config.h @@ -50,7 +50,7 @@ // 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 setup and used +// 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 @@ -65,7 +65,7 @@ // Support automatic generated events, loading and recording of those events when required //#define SUPPORT_EVENTS_AUTOMATION 1 // Support custom frame control, only for advance users -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents() +// 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 @@ -110,12 +110,12 @@ // 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 -#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Binded by default to shader location: 0 -#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Binded by default to shader location: 1 -#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Binded by default to shader location: 2 -#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Binded by default to shader location: 3 -#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Binded by default to shader location: 4 -#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Binded by default to shader location: 5 +#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 +#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 +#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 +#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 +#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 +#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5 #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix @@ -139,7 +139,7 @@ //------------------------------------------------------------------------------------ // Module: rtextures - Configuration Flags //------------------------------------------------------------------------------------ -// Selecte desired fileformats to be supported for image data loading +// Select the desired fileformats to be supported for image data loading #define SUPPORT_FILEFORMAT_PNG 1 //#define SUPPORT_FILEFORMAT_BMP 1 //#define SUPPORT_FILEFORMAT_TGA 1 diff --git a/src/minshell.html b/src/minshell.html index 4f0d6ade0..2aa75578f 100644 --- a/src/minshell.html +++ b/src/minshell.html @@ -1,5 +1,5 @@ - + diff --git a/src/raudio.c b/src/raudio.c index 4705ca582..443fb924e 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -314,7 +314,7 @@ typedef enum { #endif // NOTE: Different logic is used when feeding data to the playback device -// depending on whether or not data is streamed (Music vs Sound) +// depending on whether data is streamed (Music vs Sound) typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM @@ -474,7 +474,7 @@ void InitAudioDevice(void) return; } - // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may + // Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS) { @@ -1089,7 +1089,7 @@ void PlaySoundMulti(Sound sound) unsigned int oldAge = 0; int oldIndex = -1; - // find the first non playing pool entry + // find the first non-playing pool entry for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) { if (AUDIO.MultiChannel.channels[i] > oldAge) @@ -1105,7 +1105,7 @@ void PlaySoundMulti(Sound sound) } } - // If no none playing pool members can be index choose the oldest + // If no none playing pool members can be indexed choose the oldest if (index == -1) { TRACELOG(LOG_WARNING, "SOUND: Buffer pool is already full, count: %i", AUDIO.MultiChannel.poolCounter); @@ -1113,7 +1113,7 @@ void PlaySoundMulti(Sound sound) if (oldIndex == -1) { // Shouldn't be able to get here... but just in case something odd happens! - TRACELOG(LOG_WARNING, "SOUND: Buffer pool could not determine oldest buffer not playing sound"); + TRACELOG(LOG_WARNING, "SOUND: Buffer pool could not determine the oldest buffer not playing sound"); return; } @@ -2107,8 +2107,8 @@ void UnloadAudioStream(AudioStream stream) } // Update audio stream buffers with data -// NOTE 1: Only updates one buffer of the stream source: unqueue -> update -> queue -// NOTE 2: To unqueue a buffer it needs to be processed: IsAudioStreamProcessed() +// NOTE 1: Only updates one buffer of the stream source: dequeue -> update -> queue +// NOTE 2: To dequeue a buffer it needs to be processed: IsAudioStreamProcessed() void UpdateAudioStream(AudioStream stream, const void *data, int frameCount) { if (stream.buffer != NULL) @@ -2305,7 +2305,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, if (currentSubBufferIndex > 1) return 0; - // Another thread can update the processed state of buffers so + // Another thread can update the processed state of buffers, so // we just take a copy here to try and avoid potential synchronization problems bool isSubBufferProcessed[2] = { 0 }; isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; @@ -2319,7 +2319,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, { // We break from this loop differently depending on the buffer's usage // - For static buffers, we simply fill as much data as we can - // - For streaming buffers we only fill the halves of the buffer that are processed + // - For streaming buffers we only fill half of the buffer that are processed // Unprocessed halves must keep their audio data in-tact if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) { @@ -2376,7 +2376,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, // For static buffers we can fill the remaining frames with silence for safety, but we don't want // to report those frames as "read". The reason for this is that the caller uses the return value - // to know whether or not a non-looping sound has finished playback. + // to know whether a non-looping sound has finished playback. if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining; } diff --git a/src/raylib.h b/src/raylib.h index e0b7a3159..e31a514a6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -216,7 +216,7 @@ typedef struct Vector4 { // Quaternion, 4 components (Vector4 alias) typedef Vector4 Quaternion; -// Matrix, 4x4 components, column major, OpenGL style, right handed +// Matrix, 4x4 components, column major, OpenGL style, right-handed typedef struct Matrix { float m0, m4, m8, m12; // Matrix first row (4 components) float m1, m5, m9, m13; // Matrix second row (4 components) @@ -413,8 +413,8 @@ typedef struct Ray { // RayCollision, ray hit information typedef struct RayCollision { bool hit; // Did the ray hit something? - float distance; // Distance to nearest hit - Vector3 point; // Point of nearest hit + float distance; // Distance to the nearest hit + Vector3 point; // Point of the nearest hit Vector3 normal; // Surface normal of hit } RayCollision; @@ -681,7 +681,7 @@ typedef enum { MOUSE_CURSOR_RESIZE_NS = 6, // Vertical resize/move arrow shape MOUSE_CURSOR_RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape MOUSE_CURSOR_RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape - MOUSE_CURSOR_RESIZE_ALL = 9, // The omni-directional resize/move cursor shape + MOUSE_CURSOR_RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape MOUSE_CURSOR_NOT_ALLOWED = 10 // The operation-not-allowed shape } MouseCursor; @@ -839,10 +839,10 @@ typedef enum { typedef enum { CUBEMAP_LAYOUT_AUTO_DETECT = 0, // Automatically detect layout type CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces - CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by an horizontal 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 (equirectangular map) + CUBEMAP_LAYOUT_PANORAMA // Layout is defined by a panorama image (equirrectangular map) } CubemapLayout; // Font type, defines generation method @@ -903,7 +903,7 @@ typedef enum { } NPatchLayout; // Callbacks to hook some internal functions -// WARNING: This callbacks are intended for advance users +// WARNING: These callbacks are intended for advance users typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data @@ -1228,7 +1228,7 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); //------------------------------------------------------------------------------------ // Image loading functions -// NOTE: This functions do not require GPU access +// 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 LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) diff --git a/src/rcamera.h b/src/rcamera.h index 70221b347..0caad666d 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -150,7 +150,7 @@ void SetCameraMoveControls(int keyFront, int keyBack, #endif // Camera mouse movement sensitivity -#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.5f // TODO: it should be independant of framerate +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.5f // TODO: it should be independent of framerate #define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f // FREE_CAMERA diff --git a/src/rcore.c b/src/rcore.c index 7af655167..83a70632e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -865,7 +865,7 @@ void InitWindow(int width, int height, const char *title) LoadFontDefault(); #if defined(SUPPORT_MODULE_RSHAPES) Rectangle rec = GetFontDefault().recs[95]; - // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes #endif #else @@ -1318,7 +1318,7 @@ void MaximizeWindow(void) void MinimizeWindow(void) { #if defined(PLATFORM_DESKTOP) - // NOTE: Following function launches callback that sets appropiate flag! + // NOTE: Following function launches callback that sets appropriate flag! glfwIconifyWindow(CORE.Window.handle); #endif } @@ -1417,13 +1417,13 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_TRANSPARENT if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) { - TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization"); + 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)) { - TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization"); + TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); } // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH @@ -1436,13 +1436,13 @@ void SetWindowState(unsigned int flags) // 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)) { - TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization"); + 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)) { - TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization"); + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); } #endif } @@ -1525,13 +1525,13 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_TRANSPARENT if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) { - TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization"); + 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)) { - TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization"); + TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); } // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH @@ -1544,13 +1544,13 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_MSAA_4X_HINT if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0)) { - TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization"); + 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)) { - TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization"); + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); } #endif } @@ -2482,10 +2482,10 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); - // All locations reseted to -1 (no location) + // 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 attibute locations + // 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); @@ -2553,7 +2553,7 @@ void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniform { rlEnableShader(shader.id); rlSetUniform(locIndex, value, uniformType, count); - //rlDisableShader(); // Avoid reseting current shader program, in case other uniforms are set + //rlDisableShader(); // Avoid resetting current shader program, in case other uniforms are set } } @@ -2618,7 +2618,7 @@ Ray GetMouseRay(Vector2 mouse, Camera camera) 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 perspect doesn't have a + // 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); @@ -2808,7 +2808,7 @@ double GetTime(void) // Setup window configuration flags (view FLAGS) // NOTE: This function is expected to be called before window creation, -// because it setups some flags for the window creation process. +// 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) { @@ -3929,7 +3929,7 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scalings) + // ...in top-down or left-right to match display aspect ratio (no weird scaling) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSetErrorCallback(ErrorCallback); @@ -4100,8 +4100,8 @@ static bool InitGraphicsDevice(int width, int height) // 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 dislpay, we can't calclulate the window pos for toggling fullscreened/windowed. - // Toggling fullscreened/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; } @@ -4138,7 +4138,7 @@ static bool InitGraphicsDevice(int width, int height) // 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 appropiate fullscreen framebuffer for the requested screenWidth/screenHeight + // 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... @@ -4205,7 +4205,7 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: V-Sync can be enabled by graphic driver configuration if (CORE.Window.flags & FLAG_VSYNC_HINT) { - // WARNING: It seems to hits a critical render path in Intel HD Graphics + // WARNING: It seems to hit a critical render path in Intel HD Graphics glfwSwapInterval(1); TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); } @@ -4216,12 +4216,12 @@ static bool InitGraphicsDevice(int width, int height) #if defined(PLATFORM_DESKTOP) if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) { - // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling + // 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__) glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight); - // Screen scaling matrix is required in case desired screen area is different than display area + // 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); // Mouse input scaling for the new screen size @@ -4819,7 +4819,7 @@ static void InitTimer(void) // 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 timming on Win32! +// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! void WaitTime(double seconds) { #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) @@ -5414,7 +5414,7 @@ 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 - // do not detects action events, those should be managed by user... + // 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 @@ -5457,7 +5457,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int gestureEvent.position[0].x /= (float)GetScreenWidth(); gestureEvent.position[0].y /= (float)GetScreenHeight(); - // Gesture data is sent to gestures system for processing + // Gesture data is sent to gestures-system for processing ProcessGestureEvent(gestureEvent); #endif } @@ -5488,7 +5488,7 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) gestureEvent.position[0].x /= (float)GetScreenWidth(); gestureEvent.position[0].y /= (float)GetScreenHeight(); - // Gesture data is sent to gestures system for processing + // Gesture data is sent to gestures-system for processing ProcessGestureEvent(gestureEvent); #endif } diff --git a/src/rgestures.h b/src/rgestures.h index b63ac9678..a7440fb98 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -249,7 +249,7 @@ static double rgGetCurrentTime(void); // Module Functions Definition //---------------------------------------------------------------------------------- -// Enable only desired getures to be detected +// Enable only desired gestures to be detected void SetGesturesEnabled(unsigned int flags) { GESTURES.enabledFlags = flags; @@ -300,7 +300,7 @@ void ProcessGestureEvent(GestureEvent event) { if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0]; - // NOTE: GESTURES.Drag.intensity dependend on the resolution of the screen + // NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.timeDuration)); @@ -472,7 +472,7 @@ Vector2 GetGestureDragVector(void) } // Get drag angle -// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise +// NOTE: Angle in degrees, horizontal-right is 0, counterclockwise float GetGestureDragAngle(void) { // NOTE: drag angle is calculated on one touch points TOUCH_ACTION_UP @@ -488,8 +488,8 @@ Vector2 GetGesturePinchVector(void) return GESTURES.Pinch.vector; } -// Get angle beween two pinch points -// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise +// Get angle between two pinch points +// NOTE: Angle in degrees, horizontal-right is 0, counterclockwise float GetGesturePinchAngle(void) { // NOTE: pinch angle is calculated on two touch points TOUCH_ACTION_MOVE diff --git a/src/rlgl.h b/src/rlgl.h index ebd0f4de6..f7d73edaf 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -502,7 +502,7 @@ typedef enum { } rlShaderAttributeDataType; // Framebuffer attachment type -// NOTE: By default up to 8 color channels defined but it can be more +// NOTE: By default up to 8 color channels defined, but it can be more typedef enum { RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1 diff --git a/src/rmodels.c b/src/rmodels.c index 4a8261795..893c031ef 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1125,7 +1125,7 @@ void UnloadModel(Model model) // Unload materials maps // NOTE: As the user could be sharing shaders and textures between models, - // we don't unload the material but just free it's maps, + // 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); @@ -1146,7 +1146,7 @@ void UnloadModelKeepMeshes(Model model) { // Unload materials maps // NOTE: As the user could be sharing shaders and textures between models, - // we don't unload the material but just free it's maps, + // 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); @@ -1230,7 +1230,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) rlEnableVertexAttribute(1); // 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 + // is part of current state, and it is maintained even if a different program object is used if (mesh->normals != NULL) { @@ -1383,7 +1383,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) } // Get a copy of current matrices to work with, - // just in case stereo render is required and we need to modify them + // 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() @@ -1396,7 +1396,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) 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); - // Model transformation matrix is send to shader uniform location: SHADER_LOC_MATRIX_MODEL + // 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], transform); // Accumulate several model transformations: @@ -1517,7 +1517,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) else rlDrawVertexArray(0, mesh.vertexCount); } - // Unbind all binded texture maps + // Unbind all bound texture maps for (int i = 0; i < MAX_MATERIAL_MAPS; i++) { if (material.maps[i].texture.id > 0) @@ -1587,7 +1587,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i } // Get a copy of current matrices to work with, - // just in case stereo render is required and we need to modify them + // 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() @@ -1738,7 +1738,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances); } - // Unbind all binded texture maps + // Unbind all bound texture maps for (int i = 0; i < MAX_MATERIAL_MAPS; i++) { if (material.maps[i].texture.id > 0) @@ -2559,7 +2559,7 @@ Mesh GenMeshSphere(float radius, int rings, int slices) return mesh; } -// Generate hemi-sphere mesh (half sphere, no bottom cap) +// Generate hemisphere mesh (half sphere, no bottom cap) Mesh GenMeshHemiSphere(float radius, int rings, int slices) { Mesh mesh = { 0 }; @@ -3242,7 +3242,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) } } - // Move data from mapVertices temp arays to vertices float array + // Move data from mapVertices temp arrays to vertices float array mesh.vertexCount = vCounter; mesh.triangleCount = vCounter/3; @@ -3742,7 +3742,7 @@ RayCollision GetRayCollisionBox(Ray ray, BoundingBox box) // 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 elemets of the vector are now slightly larger than 1.0f (or smaller than -1.0f) + // 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); @@ -3842,7 +3842,7 @@ RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3 // Calculate u parameter and test bound u = Vector3DotProduct(tv, p)*invDet; - // The intersection lies outside of the triangle + // The intersection lies outside the triangle if ((u < 0.0f) || (u > 1.0f)) return collision; // Prepare to test v parameter @@ -3851,7 +3851,7 @@ RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3 // Calculate V parameter and test bound v = Vector3DotProduct(ray.direction, q)*invDet; - // The intersection lies outside of the triangle + // The intersection lies outside the triangle if ((v < 0.0f) || ((u + v) > 1.0f)) return collision; t = Vector3DotProduct(edge2, q)*invDet; @@ -4671,7 +4671,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat { Image image = { 0 }; - if (cgltfImage->uri != NULL) // Check if image data is provided as a uri (base64 or path) + 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') && @@ -4959,7 +4959,7 @@ static Model LoadGLTF(const char *fileName) for (unsigned int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++) { - // Check the different attributes for every pimitive + // Check the different attributes for every primitive if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_position) // POSITION { cgltf_accessor *attribute = data->meshes[i].primitives[p].attributes[j].data; @@ -5110,7 +5110,7 @@ static Model LoadGLTF(const char *fileName) { // 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, + // 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] == data->meshes[i].primitives[p].material) { @@ -5613,7 +5613,7 @@ static Model LoadM3D(const char *fileName) // Materials are grouped together if (mi != m3d->face[i].materialid) { - // there should be only one material switch per material kind, but be bulletproof for unoptimal model files + // 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++; @@ -5838,7 +5838,7 @@ static Model LoadM3D(const char *fileName) } // 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 exists (so not good if they are left empty). + // 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++) diff --git a/src/rshapes.c b/src/rshapes.c index 4c5072459..f7ee8f5cd 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -978,7 +978,7 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co rlSetTexture(texShapes.id); rlBegin(RL_QUADS); - // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner + // 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) // Hope the compiler is smart enough to unroll this loop { float angle = angles[k]; @@ -1207,7 +1207,7 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo rlBegin(RL_QUADS); - // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + // 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 { float angle = angles[k]; @@ -1342,7 +1342,7 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo // Use LINES to draw the outline rlBegin(RL_LINES); - // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner + // 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 { float angle = angles[k]; diff --git a/src/rtext.c b/src/rtext.c index 52fc615df..3e838f85c 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -65,7 +65,7 @@ #include // Required for: vsprintf() #include // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()] #include // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()] -#include // Requried for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] +#include // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] #if defined(SUPPORT_FILEFORMAT_TTF) #define STB_RECT_PACK_IMPLEMENTATION @@ -336,7 +336,7 @@ Font LoadFont(const char *fileName) } else { - SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default we set point filter (best performance) + 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); } @@ -584,7 +584,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz glyphCount = (glyphCount > 0)? glyphCount : 95; // Fill fontChars in case not provided externally - // NOTE: By default we fill glyphCount consecutevely, starting at 32 (Space) + // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) if (fontChars == NULL) { @@ -647,7 +647,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz } } - // Get bounding box for character (may be offset to account for chars that dip above or below the line) + // Get bounding box for character (maybe offset to account for chars that dip above or below the line) /* int chX1, chY1, chX2, chY2; stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2); @@ -777,7 +777,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { - // It return char rectangles in atlas + // It returns char rectangles in atlas recs[i].x = rects[i].x + (float)padding; recs[i].y = rects[i].y + (float)padding; recs[i].width = (float)chars[i].image.width; @@ -1040,7 +1040,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int size = TextLength(text); // Total size in bytes of the text, scanned by codepoints in loop - int textOffsetY = 0; // Offset between lines (on line break '\n') + int textOffsetY = 0; // Offset between lines (on linebreak '\n') float textOffsetX = 0.0f; // Offset X to next character to draw float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor @@ -1053,7 +1053,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f 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 + // but we need to draw all the bad bytes using the '?' symbol moving one byte if (codepoint == 0x3f) codepointByteCount = 1; if (codepoint == '\n') @@ -1119,7 +1119,7 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz // Draw multiple character (codepoints) void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint) { - int textOffsetY = 0; // Offset between lines (on line break '\n') + int textOffsetY = 0; // Offset between lines (on linebreak '\n') float textOffsetX = 0.0f; // Offset X to next character to draw float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor @@ -1195,7 +1195,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing 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 + // but we need to draw all the bad bytes using the '?' symbol so to not skip any we set next = 1 if (letter == 0x3f) next = 1; i += next - 1; @@ -1410,7 +1410,7 @@ char *TextReplace(char *text, const char *replace, const char *by) 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 replace by) + 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 @@ -1756,7 +1756,7 @@ const char *CodepointToUTF8(int codepoint, int *utf8Size) #endif // SUPPORT_TEXT_MANIPULATION // 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 +// When an 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 diff --git a/src/rtextures.c b/src/rtextures.c index c3744f640..9bce9c716 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -751,7 +751,7 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, float factor = (dist - radius*density)/(radius*(1.0f - density)); factor = (float)fmax(factor, 0.0f); - factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check + factor = (float)fmin(factor, 1.f); // dist can be bigger than radius, so we have to check pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor)); @@ -898,7 +898,7 @@ Image GenImageCellular(int width, int height, int tileSize) } } - // I made this up but it seems to give good results at all tile sizes + // I made this up, but it seems to give good results at all tile sizes int intensity = (int)(minDistance*256.0f/tileSize); if (intensity > 255) intensity = 255; @@ -1176,7 +1176,7 @@ void ImageFormat(Image *image, int newFormat) } break; case PIXELFORMAT_UNCOMPRESSED_R32: { - // WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit + // WARNING: Image is converted to GRAYSCALE equivalent 32bit image->data = (float *)RL_MALLOC(image->width*image->height*sizeof(float)); @@ -1214,7 +1214,7 @@ void ImageFormat(Image *image, int newFormat) RL_FREE(pixels); pixels = NULL; - // In case original image had mipmaps, generate mipmaps for formated image + // In case original image had mipmaps, generate mipmaps for formatted image // NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost if (image->mipmaps > 1) { @@ -1269,7 +1269,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co int size = (int)strlen(text); // Get size in bytes of text int textOffsetX = 0; // Image drawing position X - int textOffsetY = 0; // Offset between lines (on line break '\n') + int textOffsetY = 0; // Offset between lines (on linebreak '\n') // NOTE: Text image is generated at font base size, later scaled to desired font size Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); // WARNING: Module required: rtext @@ -1286,7 +1286,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext // 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 + // but we need to draw all the bad bytes using the '?' symbol moving one byte if (codepoint == 0x3f) codepointByteCount = 1; if (codepoint == '\n') @@ -1331,7 +1331,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co } // Crop image depending on alpha value -// NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f +// NOTE: Threshold is defined as a percentage: 0.0f -> 1.0f void ImageAlphaCrop(Image *image, float threshold) { // Security check to avoid program crash @@ -1711,7 +1711,7 @@ void ImageResize(Image *image, int newWidth, int newHeight) Color *pixels = LoadImageColors(*image); Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); - // NOTE: Color data is casted to (unsigned char *), there shouldn't been any problem... + // NOTE: Color data is cast to (unsigned char *), there shouldn't been any problem... stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4); int format = image->format; @@ -1891,7 +1891,7 @@ void ImageMipmaps(Image *image) } // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) -// NOTE: In case selected bpp do not represent an known 16bit format, +// NOTE: In case selected bpp do not represent a known 16bit format, // dithered data is stored in the LSB part of the unsigned short void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) { @@ -2531,7 +2531,7 @@ void UnloadImagePalette(Color *colors) } // Get image alpha border rectangle -// NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f +// NOTE: Threshold is defined as a percentage: 0.0f -> 1.0f Rectangle GetImageAlphaBorder(Image image, float threshold) { Rectangle crop = { 0 }; @@ -3049,7 +3049,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color if ((srcRec.y + srcRec.height) > src.height) srcRec.height = src.height - srcRec.y; // Check if source rectangle needs to be resized to destination rectangle - // In that case, we make a copy of source and we apply all required transform + // In that case, we make a copy of source, and we apply all required transform if (((int)srcRec.width != (int)dstRec.width) || ((int)srcRec.height != (int)dstRec.height)) { srcMod = ImageFromImage(src, srcRec); // Create image from another image @@ -3273,7 +3273,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout) faces = GenImageColor(size, size*6, MAGENTA); ImageFormat(&faces, image.format); - // NOTE: Image formating does not work with compressed textures + // NOTE: Image formatting does not work with compressed textures for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE); } @@ -3606,7 +3606,7 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 // 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 pourposes, + // I leave here the old implementation for educational purposes, // just in case someone wants to do some performance test /* rlSetTexture(texture.id); diff --git a/src/shell.html b/src/shell.html index 3ab8c3a30..b373394bb 100644 --- a/src/shell.html +++ b/src/shell.html @@ -1,5 +1,5 @@ - + diff --git a/src/utils.c b/src/utils.c index 030e59734..6843bd946 100644 --- a/src/utils.c +++ b/src/utils.c @@ -63,10 +63,10 @@ static int logTypeLevel = LOG_INFO; // Minimum log type level static TraceLogCallback traceLog = NULL; // TraceLog callback function pointer -static LoadFileDataCallback loadFileData = NULL; // LoadFileData callback funtion pointer -static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback funtion pointer -static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback funtion pointer -static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback funtion 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 From 4ae0a416f44195317d6b20af5b357c88c294faf1 Mon Sep 17 00:00:00 2001 From: BugraAlptekinSari <94199723+BugraAlptekinSari@users.noreply.github.com> Date: Sat, 11 Feb 2023 15:37:50 +0300 Subject: [PATCH 0261/1710] Added raymarch example and thumbnail for write depth (#2919) --- examples/Makefile | 9 +- .../shaders/glsl100/hybrid_raster.fs | 16 + .../shaders/glsl100/hybrid_raymarch.fs | 288 ++++++++++++++++++ .../shaders/glsl330/hybrid_raster.fs | 14 + .../shaders/glsl330/hybrid_raymarch.fs | 284 +++++++++++++++++ examples/shaders/shaders_hybrid_render.c | 212 +++++++++++++ examples/shaders/shaders_hybrid_render.png | Bin 0 -> 111135 bytes examples/shaders/shaders_write_depth.png | Bin 0 -> 11174 bytes 8 files changed, 819 insertions(+), 4 deletions(-) create mode 100644 examples/shaders/resources/shaders/glsl100/hybrid_raster.fs create mode 100644 examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs create mode 100644 examples/shaders/resources/shaders/glsl330/hybrid_raster.fs create mode 100644 examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs create mode 100644 examples/shaders/shaders_hybrid_render.c create mode 100644 examples/shaders/shaders_hybrid_render.png create mode 100644 examples/shaders/shaders_write_depth.png diff --git a/examples/Makefile b/examples/Makefile index dce02218a..574975d23 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -523,7 +523,8 @@ SHADERS = \ shaders/shaders_hot_reloading \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ - shaders/shaders_write_depth + shaders/shaders_write_depth \ + shaders/shaders_hybrid_render AUDIO = \ audio/audio_module_playing \ @@ -581,10 +582,10 @@ ifeq ($(PLATFORM),PLATFORM_DRM) rm -fv *.o endif ifeq ($(PLATFORM),PLATFORM_WEB) - ifeq ($(PLATFORM_OS),WINDOWS) + ifeq ($(PLATFORM_OS),WINDOWS) del *.wasm *.html *.js *.data - else + else rm -f */*.wasm */*.html */*.js */*.data - endif + endif endif @echo Cleaning done diff --git a/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs new file mode 100644 index 000000000..28188a454 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs @@ -0,0 +1,16 @@ +#version 100 +#extension GL_EXT_frag_depth : enable // Extension required for writing depth +precision mediump float; // Precision required for OpenGL ES2 (WebGL) + +varying vec2 fragTexCoord; +varying vec4 fragColor; + +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +void main() +{ + vec4 texelColor = texture2D(texture0, fragTexCoord); + gl_FragColor = texelColor*colDiffuse*fragColor; + gl_FragDepthEXT = gl_FragCoord.z; +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs new file mode 100644 index 000000000..6241186de --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs @@ -0,0 +1,288 @@ +#version 100 +#extension GL_EXT_frag_depth : enable //Extension required for writing depth +#extension GL_OES_standard_derivatives : enable //Extension used for fwidth() +precision mediump float; // Precision required for OpenGL ES2 (WebGL) + + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; +varying vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// Custom Input Uniform +uniform vec3 camPos; +uniform vec3 camDir; +uniform vec2 screenCenter; + +#define ZERO 0 + +// https://learnopengl.com/Advanced-OpenGL/Depth-testing +float CalcDepth(in vec3 rd, in float Idist){ + float local_z = dot(normalize(camDir),rd)*Idist; + return (1.0/(local_z) - 1.0/0.01)/(1.0/1000.0 -1.0/0.01); +} + +// https://iquilezles.org/articles/distfunctions/ +float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w ) +{ + p.x = abs(p.x); + float l = length(p.xy); + p.xy = mat2(-c.x, c.y, + c.y, c.x)*p.xy; + p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x), + (p.x>0.0)?p.y:l ); + p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0); + + vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z); + vec2 d = abs(q) - w; + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +// r = sphere's radius +// h = cutting's plane's position +// t = thickness +float sdSixWayCutHollowSphere( vec3 p, float r, float h, float t ) +{ + // Six way symetry Transformation + vec3 ap = abs(p); + if(ap.x < max(ap.y, ap.z)){ + if(ap.y < ap.z) ap.xz = ap.zx; + else ap.xy = ap.yx; + } + + vec2 q = vec2( length(ap.yz), ap.x ); + + float w = sqrt(r*r-h*h); + + return ((h*q.x0.0 ) + { + tmax = min( tmax, tp1 ); + res = vec2( tp1, 1.0 ); + } + + float t = tmin; + for( int i=0; i<70 ; i++ ) + { + if(t>tmax) break; + vec2 h = map( ro+rd*t ); + if( abs(h.x)<(0.0001*t) ) + { + res = vec2(t,h.y); + break; + } + t += h.x; + } + + return res; +} + + +// https://iquilezles.org/articles/rmshadows +float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax ) +{ + // bounding volume + float tp = (0.8-ro.y)/rd.y; if( tp>0.0 ) tmax = min( tmax, tp ); + + float res = 1.0; + float t = mint; + for( int i=ZERO; i<24; i++ ) + { + float h = map( ro + rd*t ).x; + float s = clamp(8.0*h/t,0.0,1.0); + res = min( res, s ); + t += clamp( h, 0.01, 0.2 ); + if( res<0.004 || t>tmax ) break; + } + res = clamp( res, 0.0, 1.0 ); + return res*res*(3.0-2.0*res); +} + + +// https://iquilezles.org/articles/normalsSDF +vec3 calcNormal( in vec3 pos ) +{ + vec2 e = vec2(1.0,-1.0)*0.5773*0.0005; + return normalize( e.xyy*map( pos + e.xyy ).x + + e.yyx*map( pos + e.yyx ).x + + e.yxy*map( pos + e.yxy ).x + + e.xxx*map( pos + e.xxx ).x ); +} + +// https://iquilezles.org/articles/nvscene2008/rwwtt.pdf +float calcAO( in vec3 pos, in vec3 nor ) +{ + float occ = 0.0; + float sca = 1.0; + for( int i=ZERO; i<5; i++ ) + { + float h = 0.01 + 0.12*float(i)/4.0; + float d = map( pos + h*nor ).x; + occ += (h-d)*sca; + sca *= 0.95; + if( occ>0.35 ) break; + } + return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y); +} + +// https://iquilezles.org/articles/checkerfiltering +float checkersGradBox( in vec2 p ) +{ + // filter kernel + vec2 w = fwidth(p) + 0.001; + // analytical integral (box filter) + vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w; + // xor pattern + return 0.5 - 0.5*i.x*i.y; +} + +// https://www.shadertoy.com/view/tdS3DG +vec4 render( in vec3 ro, in vec3 rd) +{ + // background + vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3; + + // raycast scene + vec2 res = raycast(ro,rd); + float t = res.x; + float m = res.y; + if( m>-0.5 ) + { + vec3 pos = ro + t*rd; + vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos ); + vec3 ref = reflect( rd, nor ); + + // material + col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) ); + float ks = 1.0; + + if( m<1.5 ) + { + float f = checkersGradBox( 3.0*pos.xz); + col = 0.15 + f*vec3(0.05); + ks = 0.4; + } + + // lighting + float occ = calcAO( pos, nor ); + + vec3 lin = vec3(0.0); + + // sun + { + vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) ); + vec3 hal = normalize( lig-rd ); + float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); + //if( dif>0.0001 ) + dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); + float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0); + //spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0); + lin += col*2.20*dif*vec3(1.30,1.00,0.70); + lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks; + } + // sky + { + float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 )); + dif *= occ; + float spe = smoothstep( -0.2, 0.2, ref.y ); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 ); + //if( spe>0.001 ) + spe *= calcSoftshadow( pos, ref, 0.02, 2.5 ); + lin += col*0.60*dif*vec3(0.40,0.60,1.15); + lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks; + } + // back + { + float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); + dif *= occ; + lin += col*0.55*dif*vec3(0.25,0.25,0.25); + } + // sss + { + float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0); + dif *= occ; + lin += col*0.25*dif*vec3(1.00,1.00,1.00); + } + + col = lin; + + col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) ); + } + + return vec4(vec3( clamp(col,0.0,1.0) ),t); +} + +vec3 CalcRayDir(vec2 nCoord){ + vec3 horizontal = normalize(cross(camDir,vec3(.0 , 1.0, .0))); + vec3 vertical = normalize(cross(horizontal,camDir)); + return normalize(camDir + horizontal*nCoord.x + vertical*nCoord.y); +} + +mat3 setCamera() +{ + vec3 cw = normalize(camDir); + vec3 cp = vec3(0.0, 1.0 ,0.0); + vec3 cu = normalize( cross(cw,cp) ); + vec3 cv = ( cross(cu,cw) ); + return mat3( cu, cv, cw ); +} + +void main() +{ + vec2 nCoord = (gl_FragCoord.xy - screenCenter.xy)/screenCenter.y; + mat3 ca = setCamera(); + + // focal length + float fl = length(camDir); + vec3 rd = ca * normalize( vec3(nCoord,fl) ); + vec3 color = vec3(nCoord/2.0 + 0.5, 0.0); + float depth = gl_FragCoord.z; + { + vec4 res = render( camPos - vec3(0.0, 0.0, 0.0) , rd ); + color = res.xyz; + depth = CalcDepth(rd,res.w); + } + gl_FragColor = vec4(color , 1.0); + gl_FragDepthEXT = depth; +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs new file mode 100644 index 000000000..85ef492c0 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs @@ -0,0 +1,14 @@ +#version 330 + +in vec2 fragTexCoord; +in vec4 fragColor; + +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +void main() +{ + vec4 texelColor = texture2D(texture0, fragTexCoord); + gl_FragColor = texelColor*colDiffuse*fragColor; + gl_FragDepth = gl_FragCoord.z; +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs new file mode 100644 index 000000000..5b66dd71c --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs @@ -0,0 +1,284 @@ +# version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// Custom Input Uniform +uniform vec3 camPos; +uniform vec3 camDir; +uniform vec2 screenCenter; + +#define ZERO 0 + +// https://learnopengl.com/Advanced-OpenGL/Depth-testing +float CalcDepth(in vec3 rd, in float Idist){ + float local_z = dot(normalize(camDir),rd)*Idist; + return (1.0/(local_z) - 1.0/0.01)/(1.0/1000.0 -1.0/0.01); +} + +// https://iquilezles.org/articles/distfunctions/ +float sdHorseshoe( in vec3 p, in vec2 c, in float r, in float le, vec2 w ) +{ + p.x = abs(p.x); + float l = length(p.xy); + p.xy = mat2(-c.x, c.y, + c.y, c.x)*p.xy; + p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x), + (p.x>0.0)?p.y:l ); + p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0); + + vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z); + vec2 d = abs(q) - w; + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +// r = sphere's radius +// h = cutting's plane's position +// t = thickness +float sdSixWayCutHollowSphere( vec3 p, float r, float h, float t ) +{ + // Six way symetry Transformation + vec3 ap = abs(p); + if(ap.x < max(ap.y, ap.z)){ + if(ap.y < ap.z) ap.xz = ap.zx; + else ap.xy = ap.yx; + } + + vec2 q = vec2( length(ap.yz), ap.x ); + + float w = sqrt(r*r-h*h); + + return ((h*q.x0.0 ) + { + tmax = min( tmax, tp1 ); + res = vec2( tp1, 1.0 ); + } + + float t = tmin; + for( int i=0; i<70 ; i++ ) + { + if(t>tmax) break; + vec2 h = map( ro+rd*t ); + if( abs(h.x)<(0.0001*t) ) + { + res = vec2(t,h.y); + break; + } + t += h.x; + } + + return res; +} + + +// https://iquilezles.org/articles/rmshadows +float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax ) +{ + // bounding volume + float tp = (0.8-ro.y)/rd.y; if( tp>0.0 ) tmax = min( tmax, tp ); + + float res = 1.0; + float t = mint; + for( int i=ZERO; i<24; i++ ) + { + float h = map( ro + rd*t ).x; + float s = clamp(8.0*h/t,0.0,1.0); + res = min( res, s ); + t += clamp( h, 0.01, 0.2 ); + if( res<0.004 || t>tmax ) break; + } + res = clamp( res, 0.0, 1.0 ); + return res*res*(3.0-2.0*res); +} + + +// https://iquilezles.org/articles/normalsSDF +vec3 calcNormal( in vec3 pos ) +{ + vec2 e = vec2(1.0,-1.0)*0.5773*0.0005; + return normalize( e.xyy*map( pos + e.xyy ).x + + e.yyx*map( pos + e.yyx ).x + + e.yxy*map( pos + e.yxy ).x + + e.xxx*map( pos + e.xxx ).x ); +} + +// https://iquilezles.org/articles/nvscene2008/rwwtt.pdf +float calcAO( in vec3 pos, in vec3 nor ) +{ + float occ = 0.0; + float sca = 1.0; + for( int i=ZERO; i<5; i++ ) + { + float h = 0.01 + 0.12*float(i)/4.0; + float d = map( pos + h*nor ).x; + occ += (h-d)*sca; + sca *= 0.95; + if( occ>0.35 ) break; + } + return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y); +} + +// https://iquilezles.org/articles/checkerfiltering +float checkersGradBox( in vec2 p ) +{ + // filter kernel + vec2 w = fwidth(p) + 0.001; + // analytical integral (box filter) + vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w; + // xor pattern + return 0.5 - 0.5*i.x*i.y; +} + +// https://www.shadertoy.com/view/tdS3DG +vec4 render( in vec3 ro, in vec3 rd) +{ + // background + vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3; + + // raycast scene + vec2 res = raycast(ro,rd); + float t = res.x; + float m = res.y; + if( m>-0.5 ) + { + vec3 pos = ro + t*rd; + vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos ); + vec3 ref = reflect( rd, nor ); + + // material + col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) ); + float ks = 1.0; + + if( m<1.5 ) + { + float f = checkersGradBox( 3.0*pos.xz); + col = 0.15 + f*vec3(0.05); + ks = 0.4; + } + + // lighting + float occ = calcAO( pos, nor ); + + vec3 lin = vec3(0.0); + + // sun + { + vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) ); + vec3 hal = normalize( lig-rd ); + float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); + //if( dif>0.0001 ) + dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); + float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0); + //spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0); + lin += col*2.20*dif*vec3(1.30,1.00,0.70); + lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks; + } + // sky + { + float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 )); + dif *= occ; + float spe = smoothstep( -0.2, 0.2, ref.y ); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 ); + //if( spe>0.001 ) + spe *= calcSoftshadow( pos, ref, 0.02, 2.5 ); + lin += col*0.60*dif*vec3(0.40,0.60,1.15); + lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks; + } + // back + { + float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); + dif *= occ; + lin += col*0.55*dif*vec3(0.25,0.25,0.25); + } + // sss + { + float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0); + dif *= occ; + lin += col*0.25*dif*vec3(1.00,1.00,1.00); + } + + col = lin; + + col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) ); + } + + return vec4(vec3( clamp(col,0.0,1.0) ),t); +} + +vec3 CalcRayDir(vec2 nCoord){ + vec3 horizontal = normalize(cross(camDir,vec3(.0 , 1.0, .0))); + vec3 vertical = normalize(cross(horizontal,camDir)); + return normalize(camDir + horizontal*nCoord.x + vertical*nCoord.y); +} + +mat3 setCamera() +{ + vec3 cw = normalize(camDir); + vec3 cp = vec3(0.0, 1.0 ,0.0); + vec3 cu = normalize( cross(cw,cp) ); + vec3 cv = ( cross(cu,cw) ); + return mat3( cu, cv, cw ); +} + +void main() +{ + vec2 nCoord = (gl_FragCoord.xy - screenCenter.xy)/screenCenter.y; + mat3 ca = setCamera(); + + // focal length + float fl = length(camDir); + vec3 rd = ca * normalize( vec3(nCoord,fl) ); + vec3 color = vec3(nCoord/2.0 + 0.5, 0.0); + float depth = gl_FragCoord.z; + { + vec4 res = render( camPos - vec3(0.0, 0.0, 0.0) , rd ); + color = res.xyz; + depth = CalcDepth(rd,res.w); + } + gl_FragColor = vec4(color , 1.0); + gl_FragDepth = depth; +} \ No newline at end of file diff --git a/examples/shaders/shaders_hybrid_render.c b/examples/shaders/shaders_hybrid_render.c new file mode 100644 index 000000000..86ba6397b --- /dev/null +++ b/examples/shaders/shaders_hybrid_render.c @@ -0,0 +1,212 @@ +/******************************************************************************************* +* +* 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-2023 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_RPI, 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 raymarch_shader = 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 raster_shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raster.fs", GLSL_VERSION)); + + // Declare Struct used to store camera locs. + RayLocs march_locs = {0}; + + // Fill the struct with shader locs. + march_locs.camPos = GetShaderLocation(raymarch_shader, "camPos"); + march_locs.camDir = GetShaderLocation(raymarch_shader, "camDir"); + march_locs.screenCenter = GetShaderLocation(raymarch_shader, "screenCenter"); + + { // Transfer screenCenter position to shader. Which is used to calculate ray direction. + Vector2 screenCenter = {.x = screenWidth/2.0, .y = screenHeight/2.0}; + SetShaderValue(raymarch_shader, march_locs.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 mode type + }; + + // Camera FOV is pre-calculated in the camera Distance. + double camDist = 1.0/(tan(camera.fovy*0.5*DEG2RAD)); + + SetCameraMode(camera, CAMERA_FIRST_PERSON); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera); + + //Update Camera Postion in the ray march shader. + SetShaderValue(raymarch_shader, march_locs.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(raymarch_shader, march_locs.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(raymarch_shader); + DrawRectangleRec((Rectangle){0,0,screenWidth,screenHeight},WHITE); + EndShaderMode(); + + // Raserize Scene + BeginMode3D(camera); + BeginShaderMode(raster_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, screenWidth, -screenHeight }, (Vector2) { 0, 0 }, WHITE); + DrawFPS(10, 10); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTextureDepthTex(target); + UnloadShader(raymarch_shader); + UnloadShader(raster_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(width, height); // 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); + } +} \ No newline at end of file diff --git a/examples/shaders/shaders_hybrid_render.png b/examples/shaders/shaders_hybrid_render.png new file mode 100644 index 0000000000000000000000000000000000000000..0a63d19c20c026c2355bd80cefd38cbefd0e8a5b GIT binary patch literal 111135 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

    2PTj|lepQ;T_#AXy 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 literal 0 HcmV?d00001 diff --git a/examples/shaders/shaders_write_depth.png b/examples/shaders/shaders_write_depth.png new file mode 100644 index 0000000000000000000000000000000000000000..5a56d7d25a084c61a1fff502c3c55291a0f91fea GIT binary patch literal 11174 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 literal 0 HcmV?d00001 From 4647441ad81848334ff2827015a9e337a6a0393a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Feb 2023 01:10:54 +0100 Subject: [PATCH 0262/1710] Update rl_gputex.h --- src/external/rl_gputex.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 6d2e97b27..c20bdc67e 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -477,10 +477,10 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo // Calculate file data_size required int data_size = sizeof(ktx_header); - for (int i = 0, width = width, height = height; i < mipmaps; i++) + for (int i = 0, w = width, h = height; i < mipmaps; i++) { - data_size += get_pixel_data_size(width, height, format); - width /= 2; height /= 2; + data_size += get_pixel_data_size(w, h, format); + w /= 2; h /= 2; } unsigned char *file_data = RL_CALLOC(data_size, 1); From 21961a786df76a86302942f3da8f7c08b7f31863 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Feb 2023 12:10:01 +0100 Subject: [PATCH 0263/1710] REVIEWED: Vertex colors support on M3D loading #2878 --- src/rmodels.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 893c031ef..2fd2d5a4a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5633,8 +5633,9 @@ static Model LoadM3D(const char *fileName) 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 color map is provided, we allocate storage for vertex colors - if (m3d->cmap != NULL) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); + // If no map is provided, we allocate storage for vertex colors + // M3D specs only consider vertex colors if no material is provided + if (mi != M3D_UNDEF) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); if (m3d->numbone && m3d->numskin) { From 96a9b4e1f25819bef8198ad0a0412ac84025d179 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Feb 2023 13:28:35 +0100 Subject: [PATCH 0264/1710] REVIEWED: config.h format and inconsistencies --- src/config.h | 148 ++++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/src/config.h b/src/config.h index 48f66aba4..d5f8e486c 100644 --- a/src/config.h +++ b/src/config.h @@ -24,66 +24,68 @@ * 3. This notice may not be removed or altered from any source distribution. * **********************************************************************************************/ + #ifndef CONFIG_H #define CONFIG_H + //------------------------------------------------------------------------------------ // Module selection - Some modules could be avoided // Mandatory modules: rcore, rlgl, utils //------------------------------------------------------------------------------------ -#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 +#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 //------------------------------------------------------------------------------------ // Module: rcore - Configuration Flags //------------------------------------------------------------------------------------ // Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital -#define SUPPORT_CAMERA_SYSTEM 1 +#define SUPPORT_CAMERA_SYSTEM 1 // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag -#define SUPPORT_GESTURES_SYSTEM 1 +#define SUPPORT_GESTURES_SYSTEM 1 // Mouse gestures are directly mapped like touches and processed by gestures system -#define SUPPORT_MOUSE_GESTURES 1 +#define SUPPORT_MOUSE_GESTURES 1 // Reconfigure standard input to receive key inputs, works with SSH connection. -#define SUPPORT_SSH_KEYBOARD_RPI 1 +#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. -#define SUPPORT_WINMM_HIGHRES_TIMER 1 +#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 +//#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 // Wait for events passively (sleeping while no events) instead of polling them actively every frame -//#define SUPPORT_EVENTS_WAITING 1 +//#define SUPPORT_EVENTS_WAITING 1 // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() -#define SUPPORT_SCREEN_CAPTURE 1 +#define SUPPORT_SCREEN_CAPTURE 1 // Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() -#define SUPPORT_GIF_RECORDING 1 +#define SUPPORT_GIF_RECORDING 1 // Support CompressData() and DecompressData() functions -#define SUPPORT_COMPRESSION_API 1 +#define SUPPORT_COMPRESSION_API 1 // Support automatic generated events, loading and recording of those events when required -//#define SUPPORT_EVENTS_AUTOMATION 1 +//#define SUPPORT_EVENTS_AUTOMATION 1 // Support custom frame control, only for advance users // 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 +//#define SUPPORT_CUSTOM_FRAME_CONTROL 1 // rcore: Configuration values //------------------------------------------------------------------------------------ -#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_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_BUTTONS 32 // Maximum number of buttons supported (per gamepad) -#define MAX_TOUCH_POINTS 8 // 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 +#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_BUTTONS 32 // Maximum number of buttons supported (per gamepad) +#define MAX_TOUCH_POINTS 8 // 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 -#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB +#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB //------------------------------------------------------------------------------------ @@ -133,36 +135,36 @@ //------------------------------------------------------------------------------------ // Use QUADS instead of TRIANGLES for drawing when possible // Some lines-based shapes could still use lines -#define SUPPORT_QUADS_DRAW_MODE 1 +#define SUPPORT_QUADS_DRAW_MODE 1 //------------------------------------------------------------------------------------ // Module: rtextures - Configuration Flags //------------------------------------------------------------------------------------ // Select the desired fileformats to be supported for image data loading -#define SUPPORT_FILEFORMAT_PNG 1 -//#define SUPPORT_FILEFORMAT_BMP 1 -//#define SUPPORT_FILEFORMAT_TGA 1 -//#define SUPPORT_FILEFORMAT_JPG 1 -#define SUPPORT_FILEFORMAT_GIF 1 -#define SUPPORT_FILEFORMAT_QOI 1 -//#define SUPPORT_FILEFORMAT_PSD 1 -#define SUPPORT_FILEFORMAT_DDS 1 -#define SUPPORT_FILEFORMAT_HDR 1 -//#define SUPPORT_FILEFORMAT_PIC 1 -//#define SUPPORT_FILEFORMAT_PNM 1 -//#define SUPPORT_FILEFORMAT_KTX 1 -//#define SUPPORT_FILEFORMAT_ASTC 1 -//#define SUPPORT_FILEFORMAT_PKM 1 -//#define SUPPORT_FILEFORMAT_PVR 1 +#define SUPPORT_FILEFORMAT_PNG 1 +//#define SUPPORT_FILEFORMAT_BMP 1 +//#define SUPPORT_FILEFORMAT_TGA 1 +//#define SUPPORT_FILEFORMAT_JPG 1 +#define SUPPORT_FILEFORMAT_GIF 1 +#define SUPPORT_FILEFORMAT_QOI 1 +//#define SUPPORT_FILEFORMAT_PSD 1 +#define SUPPORT_FILEFORMAT_DDS 1 +#define SUPPORT_FILEFORMAT_HDR 1 +//#define SUPPORT_FILEFORMAT_PIC 1 +//#define SUPPORT_FILEFORMAT_PNM 1 +//#define SUPPORT_FILEFORMAT_KTX 1 +//#define SUPPORT_FILEFORMAT_ASTC 1 +//#define SUPPORT_FILEFORMAT_PKM 1 +//#define SUPPORT_FILEFORMAT_PVR 1 // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi) -#define SUPPORT_IMAGE_EXPORT 1 +#define SUPPORT_IMAGE_EXPORT 1 // Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) -#define SUPPORT_IMAGE_GENERATION 1 +#define SUPPORT_IMAGE_GENERATION 1 // 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 +#define SUPPORT_IMAGE_MANIPULATION 1 //------------------------------------------------------------------------------------ @@ -170,52 +172,52 @@ //------------------------------------------------------------------------------------ // 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 +#define SUPPORT_DEFAULT_FONT 1 // Selected desired font fileformats to be supported for loading -#define SUPPORT_FILEFORMAT_FNT 1 -#define SUPPORT_FILEFORMAT_TTF 1 +#define SUPPORT_FILEFORMAT_FNT 1 +#define SUPPORT_FILEFORMAT_TTF 1 // Support text management functions // If not defined, still some functions are supported: TextLength(), TextFormat() -#define SUPPORT_TEXT_MANIPULATION 1 +#define SUPPORT_TEXT_MANIPULATION 1 // rtext: Configuration values //------------------------------------------------------------------------------------ -#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: +#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() +#define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit() //------------------------------------------------------------------------------------ // Module: rmodels - Configuration Flags //------------------------------------------------------------------------------------ // Selected desired model fileformats to be supported for loading -#define SUPPORT_FILEFORMAT_OBJ 1 -#define SUPPORT_FILEFORMAT_MTL 1 -#define SUPPORT_FILEFORMAT_IQM 1 -#define SUPPORT_FILEFORMAT_GLTF 1 -#define SUPPORT_FILEFORMAT_VOX 1 -#define SUPPORT_FILEFORMAT_M3D 1 +#define SUPPORT_FILEFORMAT_OBJ 1 +#define SUPPORT_FILEFORMAT_MTL 1 +#define SUPPORT_FILEFORMAT_IQM 1 +#define SUPPORT_FILEFORMAT_GLTF 1 +#define SUPPORT_FILEFORMAT_VOX 1 +#define SUPPORT_FILEFORMAT_M3D 1 // 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 +#define SUPPORT_MESH_GENERATION 1 // rmodels: Configuration values //------------------------------------------------------------------------------------ -#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported -#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh +#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported +#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh //------------------------------------------------------------------------------------ // Module: raudio - Configuration Flags //------------------------------------------------------------------------------------ // Desired audio fileformats to be supported for loading -#define SUPPORT_FILEFORMAT_WAV 1 -#define SUPPORT_FILEFORMAT_OGG 1 -#define SUPPORT_FILEFORMAT_MP3 1 -//#define SUPPORT_FILEFORMAT_QOA 1 -//#define SUPPORT_FILEFORMAT_FLAC 1 -#define SUPPORT_FILEFORMAT_XM 1 -#define SUPPORT_FILEFORMAT_MOD 1 +#define SUPPORT_FILEFORMAT_WAV 1 +#define SUPPORT_FILEFORMAT_OGG 1 +#define SUPPORT_FILEFORMAT_MP3 1 +//#define SUPPORT_FILEFORMAT_QOA 1 +//#define SUPPORT_FILEFORMAT_FLAC 1 +#define SUPPORT_FILEFORMAT_XM 1 +#define SUPPORT_FILEFORMAT_MOD 1 // raudio: Configuration values //------------------------------------------------------------------------------------ @@ -229,14 +231,14 @@ // Module: utils - Configuration Flags //------------------------------------------------------------------------------------ // Standard file io library (stdio.h) included -#define SUPPORT_STANDARD_FILEIO +#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 +#define SUPPORT_TRACELOG 1 +//#define SUPPORT_TRACELOG_DEBUG 1 // utils: Configuration values //------------------------------------------------------------------------------------ -#define MAX_TRACELOG_MSG_LENGTH 128 // Max length of one trace-log message +#define MAX_TRACELOG_MSG_LENGTH 128 // Max length of one trace-log message #endif // CONFIG_H From 22287a78c5f4b34b0d57d060dd7f68b51e70d363 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Feb 2023 13:29:39 +0100 Subject: [PATCH 0265/1710] Update qoa.h --- src/external/qoa.h | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/external/qoa.h b/src/external/qoa.h index aae575512..65752d1b7 100644 --- a/src/external/qoa.h +++ b/src/external/qoa.h @@ -274,24 +274,26 @@ static inline int qoa_clamp(int v, int min, int max) { } static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) { - qoa_uint64_t v = - (qoa_uint64_t)bytes[(*p)+0] << 56 | (qoa_uint64_t)bytes[(*p)+1] << 48 | - (qoa_uint64_t)bytes[(*p)+2] << 40 | (qoa_uint64_t)bytes[(*p)+3] << 32 | - (qoa_uint64_t)bytes[(*p)+4] << 24 | (qoa_uint64_t)bytes[(*p)+5] << 16 | - (qoa_uint64_t)bytes[(*p)+6] << 8 | (qoa_uint64_t)bytes[(*p)+7]; + bytes += *p; *p += 8; - return v; + return + ((qoa_uint64_t)(bytes[0]) << 56) | ((qoa_uint64_t)(bytes[1]) << 48) | + ((qoa_uint64_t)(bytes[2]) << 40) | ((qoa_uint64_t)(bytes[3]) << 32) | + ((qoa_uint64_t)(bytes[4]) << 24) | ((qoa_uint64_t)(bytes[5]) << 16) | + ((qoa_uint64_t)(bytes[6]) << 8) | ((qoa_uint64_t)(bytes[7]) << 0); } static inline void qoa_write_u64(qoa_uint64_t v, unsigned char *bytes, unsigned int *p) { - bytes[(*p)++] = (v >> 56) & 0xff; - bytes[(*p)++] = (v >> 48) & 0xff; - bytes[(*p)++] = (v >> 40) & 0xff; - bytes[(*p)++] = (v >> 32) & 0xff; - bytes[(*p)++] = (v >> 24) & 0xff; - bytes[(*p)++] = (v >> 16) & 0xff; - bytes[(*p)++] = (v >> 8) & 0xff; - bytes[(*p)++] = (v >> 0) & 0xff; + bytes += *p; + *p += 8; + bytes[0] = (v >> 56) & 0xff; + bytes[1] = (v >> 48) & 0xff; + bytes[2] = (v >> 40) & 0xff; + bytes[3] = (v >> 32) & 0xff; + bytes[4] = (v >> 24) & 0xff; + bytes[5] = (v >> 16) & 0xff; + bytes[6] = (v >> 8) & 0xff; + bytes[7] = (v >> 0) & 0xff; } From a9c28d75837f98f9aef083af495a7be25ae9e0d6 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Feb 2023 13:46:26 +0100 Subject: [PATCH 0266/1710] Update CHANGELOG --- CHANGELOG | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 602e9a4e0..f9bb27ec9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -43,6 +43,7 @@ Detailed changes: [core] REVIEWED: GetClipboardText() on PLATFORM_WEB, permissions issues [core] REVIEWED: Initial window position for display-sized fullscreen (#2742) by @daipom [core] REVIEWED: Sticky touches input (#2857) by @ImazighenGhost +[core] REVIEWED: Enable GetWindowHandle() on macOS (#2915) by @Not-Nik [rlgl] ADDED: OpenGL ES 2.0 support on PLATFORM_DESKTOP (#2840) by @wtnbgo [rlgl] ADDED: Separate blending modes for color and alpha, BLEND_CUSTOM_SEPARATE (#2741) [rlgl] ADDED: rlSetBlendFactorsSeparate and custom blend mode modification checks (#2741) by @pure01fx @@ -58,7 +59,8 @@ Detailed changes: [rlgl] REVIEWED: rlMultMatrixf(), use const pointer (#2807) by @planetis-m [rlgl] REVIEWED: Expose OpenGL blending mode factors and functions/equations [rlgl] REVIEWED: rLoadTextureDepth(), issue with depth textures on WebGL (#2824) -[raymath] REVIEWED: Vector2Angle() (#2829, #2832) by @AlxHnr and @planetis-m +[raymath] ADDED: Vector2LineAngle() (#2887) +[raymath] REVIEWED: Vector2Angle() (#2829, #2832) by @AlxHnr, @planetis-m [shapes] ADDED: CheckCollisionPointPoly() (#2685) by @acejacek [shapes] REVIEWED: DrawPixel*(), use RL_QUADS/RL_TRIANGLES (#2750) by @hatkidchan [shapes] REVIEWED: DrawLineBezier*(), fix bezier line breaking (#2735, #2767) by @nobytesgiven @@ -88,21 +90,29 @@ Detailed changes: [models] ADDED: GLTF animation support (#2844) by @charles-l [models] ADDED: DrawCapsule() and DrawCapsuleWires() (#2761) by @IanBand [models] ADDED: LoadMaterials(), MTL files loading, same code as OBJ loader (#2872) by @JeffM2501 +[models] REVIEWED: DrawMesh(), using SHADER_LOC_COLOR_SPECULAR as a material map (#2908) by @haved +[models] REVIEWED: LoadM3D() vertex color support (#2878) by @GithubPrankster, @bztsrc [models] REVIEWED: GenMeshHeightmap() (#2716) [models] REVIEWED: LoadIQM() (#2676) [models] REVIEWED: Simplify .vox signature check (#2752) by @CrezyDud [models] REVIEWED: LoadIQM(), support bone names loading if available (#2882) by @PencilAmazing [models] `WARNING`: REMOVED: DrawCubeTexture(), DrawCubeTextureRec(), functions moved to new example: `models_draw_cube_texture` +[audio] ADDED: IsWaveReady()`, IsSoundReady(), IsMusicReady() (#2892) by @RobLoach [audio] REVIEWED: Clear PCM buffer state when closing audio device (#2736) by @veins1 [audio] REVIEWED: Android backend selected (#2118, #2875) by @planetis-m +[multi] ADDED: IsShaderReady(), IsImageReady(), IsFontReady() (#2892) by @RobLoach +[multi] ADDED: IsModelReady(), IsMaterialReady(), IsTextureReady(), IsRenderTextureReady() (#2895) by @RobLoach [multi] REVIEWED: Multiple code/comment typos by @sDos280 +[multi] REVIEWED: Grammar mistakes and typos (#2914) by @stickM4N [multi] REVIEWED: Use TRACELOG() macro instead of TraceLog() in internal modules (#2881) by @RobLoach [examples] ADDED: textures_textured_curve (#2821) by @JeffM2501 [examples] ADDED: shaders_write_depth (#2836) by @BugraAlptekinSari +[examples] ADDED: shaders_hybrid_render (#2919) by @BugraAlptekinSari [examples] RENAMED: Several shaders for naming consistency (#2707) [examples] RENAMED: lighting_instanced.fs to lighting_instancing.fs (glsl100) (#2805) by @gtrxAC [examples] REVIEWED: core_custom_logging.c (#2692) by @hartmannathan [examples] REVIEWED: core_camera_2d_platformer (#2687) by @skylar779 +[examples] REVIEWED: core_input_gamepad.c (#2903) by @planetis-m [examples] REVIEWED: core_custom_frame_control [examples] REVIEWED: text_rectangle_bounds (#2746) by @SzieberthAdam [examples] REVIEWED: textures_image_processing, added gaussian blurring (#2775) by @nobytesgiven @@ -114,6 +124,9 @@ Detailed changes: [build] ADDED: Packaging for distros with deb-based and rpm-based packages (#2877) by @KOLANICH [build] ADDED: Linkage library -latomic on Linux (only required for ARM32) [build] ADDED: Required frameworks on macOS (#2793) by @SpexGuy +[build] ADDED: WASM support for Zig build (#2901) by @Not-Nik +[build] REVIEWED: config.h format and inconsistencies +[build] REVIEWED: Zig build to latest master, avoid deprecated functions (#2910) by @star-tek-mb [build] REVIEWED: CMake project template to easily target raylib version (#2700) by @RobLoach [build] REVIEWED: PATH for PLATFORM_WEB target (#2647) by @futureapricot [build] REVIEWED: build.zig to let user decide how to set build mode and linker fixes by @InKryption @@ -141,7 +154,7 @@ Detailed changes: [bindings] ADDED: TurboRaylib (Object Pascal) by @turborium [bindings] ADDED: Kaylib (Kotlin/Native) by @Its-Kenta [bindings] ADDED: Raylib-Nelua (Nelua) by @Its-Kenta -[misc] REVIEWED: Update some external libraries to latest versions +[misc] REVIEWED: Update external libraries to latest versions ------------------------------------------------------------------------- Release: raylib 4.2 (11 August 2022) From f1bcabcc3799df741d54593980ac06afe2e0f081 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Feb 2023 13:48:11 +0100 Subject: [PATCH 0267/1710] Update CHANGELOG --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f9bb27ec9..8cbbaa2b1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,14 +9,13 @@ Release: raylib 4.5 (xx February 2023) -WIP- KEY CHANGES: - ADDED: M3D model format support with animations - ADDED: GLTF animation support + - ADDED: QOA audio format support (import/export) -WIP- - rlgl redesign to avoid render batch triangles limits pre-check: rlCheckRenderBatchLimit() - rshapes simplification to minimize the requirement of rlgl functionality, now it only depends on 6 functions - rl_gputex.h: Compressed textures loading, required by rtextures module, has been moved to a separate self-contained library - raygui 3.5: New version of the immediate-mode gui system for tools development with raylib - Detailed changes: - [core] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach [core] ADDED: Basic gamepad support for Android (#2709) by @deniska [core] ADDED: Support CAPS/NUM lock keys registering if locked From 73989a49817225f11f547d270598e93745bf7df0 Mon Sep 17 00:00:00 2001 From: Crydsch Cube Date: Tue, 14 Feb 2023 17:47:21 +0100 Subject: [PATCH 0268/1710] WIP rcamera redesign vector (#2563) * core functionality CAMERA_FREE * fix example * add remaining camera modes * add view bobbing * view bobbing * catch curser in SetCameraMode * adjust examples * fix compilation on linux * fix example text_draw_3d * actually fix text_draw_3d * Updated camera API * Improve Vector3RotateByAxisAngle() function * remove camera.mode dependency from low-level functions * remove camera.mode from struct * fixes after rebase * adjust examples for new UpdateCamera function * adjust example models_loading_m3d --------- Co-authored-by: Ray --- examples/core/core_3d_camera_first_person.c | 92 ++- examples/core/core_3d_camera_free.c | 4 +- examples/core/core_3d_picking.c | 15 +- examples/core/core_vr_simulator.c | 6 +- examples/core/core_world_screen.c | 7 +- examples/models/models_animation.c | 5 +- examples/models/models_billboard.c | 6 +- examples/models/models_cubicmap.c | 5 +- examples/models/models_first_person_maze.c | 5 +- examples/models/models_heightmap.c | 5 +- examples/models/models_loading.c | 5 +- examples/models/models_loading_gltf.c | 6 +- examples/models/models_loading_m3d.c | 5 +- examples/models/models_loading_vox.c | 5 +- examples/models/models_mesh_generation.c | 5 +- examples/models/models_mesh_picking.c | 14 +- examples/models/models_rlgl_solar_system.c | 5 +- examples/models/models_skybox.c | 5 +- examples/shaders/shaders_basic_lighting.c | 5 +- examples/shaders/shaders_custom_uniform.c | 6 +- examples/shaders/shaders_fog.c | 5 +- examples/shaders/shaders_mesh_instancing.c | 6 +- examples/shaders/shaders_model_shader.c | 5 +- examples/shaders/shaders_postprocessing.c | 6 +- examples/shaders/shaders_raymarching.c | 5 +- examples/shaders/shaders_simple_mask.c | 3 +- examples/text/text_draw_3d.c | 9 +- src/raylib.h | 8 +- src/rcamera.h | 648 ++++++++------------ src/rcamera_old.h | 567 +++++++++++++++++ 30 files changed, 996 insertions(+), 477 deletions(-) create mode 100644 src/rcamera_old.h diff --git a/examples/core/core_3d_camera_first_person.c b/examples/core/core_3d_camera_first_person.c index a7aa5f49a..d98a002fd 100644 --- a/examples/core/core_3d_camera_first_person.c +++ b/examples/core/core_3d_camera_first_person.c @@ -12,6 +12,7 @@ ********************************************************************************************/ #include "raylib.h" +#include "rcamera.h" #define MAX_COLUMNS 20 @@ -29,11 +30,14 @@ int main(void) // Define the camera to look into our 3d world (position, target, up vector) Camera camera = { 0 }; - camera.position = (Vector3){ 4.0f, 2.0f, 4.0f }; - camera.target = (Vector3){ 0.0f, 1.8f, 0.0f }; + camera.position = (Vector3){ 0.0f, 2.0f, 4.0f }; + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; camera.fovy = 60.0f; camera.projection = CAMERA_PERSPECTIVE; + camera.swingCounter = 1; // Enable view bobbing + + int cameraMode = CAMERA_FIRST_PERSON; // Generates some random columns float heights[MAX_COLUMNS] = { 0 }; @@ -47,8 +51,7 @@ int main(void) colors[i] = (Color){ GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255 }; } - SetCameraMode(camera, CAMERA_FIRST_PERSON); // Set a first person camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -57,7 +60,51 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + // Switch camera mode + if (IsKeyPressed(KEY_ONE)) { + cameraMode = CAMERA_FREE; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll + } + if (IsKeyPressed(KEY_TWO)) { + cameraMode = CAMERA_FIRST_PERSON; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll + } + if (IsKeyPressed(KEY_THREE)) { + cameraMode = CAMERA_THIRD_PERSON; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll + } + if (IsKeyPressed(KEY_FOUR)) { + cameraMode = CAMERA_ORBITAL; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll + } + + // Switch camera projection + if (IsKeyPressed(KEY_P)) { + if (camera.projection == CAMERA_PERSPECTIVE) { + // Create isometric view + cameraMode = CAMERA_THIRD_PERSON; + // Note: The target distance is related to the render distance in the orthographic projection + camera.position = (Vector3){ 0.0f, 2.0f, -100.0f }; + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; + 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); + } + else if (camera.projection == CAMERA_ORTHOGRAPHIC) + { + // Reset to default view + cameraMode = CAMERA_THIRD_PERSON; + camera.position = (Vector3){ 0.0f, 2.0f, 10.0f }; + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.projection = CAMERA_PERSPECTIVE; + camera.fovy = 60.0f; + } + } + + UpdateCamera(&camera, cameraMode); // Update camera //---------------------------------------------------------------------------------- // Draw @@ -80,14 +127,39 @@ int main(void) DrawCubeWires(positions[i], 2.0f, heights[i], 2.0f, MAROON); } + // Draw player cube + if (cameraMode == CAMERA_THIRD_PERSON) + { + DrawCube(camera.target, 0.5f, 0.5f, 0.5f, PURPLE); + DrawCubeWires(camera.target, 0.5f, 0.5f, 0.5f, DARKPURPLE); + } + EndMode3D(); - DrawRectangle( 10, 10, 220, 70, Fade(SKYBLUE, 0.5f)); - DrawRectangleLines( 10, 10, 220, 70, BLUE); + // Draw info boxes + DrawRectangle(5, 5, 330, 100, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(5, 5, 330, 100, BLUE); - DrawText("First person camera default controls:", 20, 20, 10, BLACK); - DrawText("- Move with keys: W, A, S, D", 40, 40, 10, DARKGRAY); - DrawText("- Mouse move to look around", 40, 60, 10, DARKGRAY); + 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("- Camera mode keys: 1, 2, 3, 4", 15, 60, 10, BLACK); + DrawText("- Zoom keys: num-plus, num-minus or mouse scroll", 15, 75, 10, BLACK); + DrawText("- Camera projection key: P", 15, 90, 10, BLACK); + + DrawRectangle(600, 5, 195, 100, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(600, 5, 195, 100, BLUE); + + DrawText("Camera status:", 610, 15, 10, BLACK); + DrawText(TextFormat("- Mode: %s", (cameraMode == CAMERA_FREE) ? "FREE" : + (cameraMode == CAMERA_FIRST_PERSON) ? "FIRST_PERSON" : + (cameraMode == CAMERA_THIRD_PERSON) ? "THIRD_PERSON" : + (cameraMode == CAMERA_ORBITAL) ? "ORBITAL" : "CUSTOM"), 610, 30, 10, BLACK); + DrawText(TextFormat("- Projection: %s", (camera.projection == CAMERA_PERSPECTIVE) ? "PERSPECTIVE" : + (camera.projection == CAMERA_ORTHOGRAPHIC) ? "ORTHOGRAPHIC" : "CUSTOM"), 610, 45, 10, BLACK); + DrawText(TextFormat("- Position: (%06.3f, %06.3f, %06.3f)", camera.position.x, camera.position.y, camera.position.z), 610, 60, 10, BLACK); + DrawText(TextFormat("- Target: (%06.3f, %06.3f, %06.3f)", camera.target.x, camera.target.y, camera.target.z), 610, 75, 10, BLACK); + DrawText(TextFormat("- Up: (%06.3f, %06.3f, %06.3f)", camera.up.x, camera.up.y, camera.up.z), 610, 90, 10, BLACK); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index af7ab8d72..887a1df33 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -35,8 +35,6 @@ int main(void) Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -45,7 +43,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FREE); if (IsKeyDown('Z')) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //---------------------------------------------------------------------------------- diff --git a/examples/core/core_3d_picking.c b/examples/core/core_3d_picking.c index 8c0df9663..0cf56f5fe 100644 --- a/examples/core/core_3d_picking.c +++ b/examples/core/core_3d_picking.c @@ -40,7 +40,7 @@ int main(void) RayCollision collision = { 0 }; - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode + EnableCursor(); // Disable camera controls SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -50,7 +50,14 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + if (IsCursorHidden()) UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Update camera + + // Toggle camera controls + if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) + { + if (IsCursorHidden()) EnableCursor(); + else DisableCursor(); + } if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { @@ -93,10 +100,12 @@ int main(void) EndMode3D(); - DrawText("Try selecting the box with mouse!", 240, 10, 20, DARKGRAY); + 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); + DrawText("Right click mouse to toggle camera controls", 10, 430, 10, GRAY); + DrawFPS(10, 10); EndDrawing(); diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 5914897b0..3024b7853 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -96,11 +96,11 @@ int main(void) camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector camera.fovy = 60.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera type + camera.swingCounter = 1; // Enable view bobbing Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; - SetCameraMode(camera, CAMERA_FIRST_PERSON); // Set first person camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(90); // Set our game to run at 90 frames-per-second //-------------------------------------------------------------------------------------- @@ -109,7 +109,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/core/core_world_screen.c b/examples/core/core_world_screen.c index d86adf877..f96690ba5 100644 --- a/examples/core/core_world_screen.c +++ b/examples/core/core_world_screen.c @@ -23,7 +23,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - core world screen"); // Define the camera to look into our 3d world Camera camera = { 0 }; @@ -36,8 +36,7 @@ int main(void) Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; Vector2 cubeScreenPosition = { 0.0f, 0.0f }; - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -46,7 +45,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_THIRD_PERSON); // Calculate cube screen space position (with a little offset to be in top) cubeScreenPosition = GetWorldToScreen((Vector3){cubePosition.x, cubePosition.y + 2.5f, cubePosition.z}, camera); diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c index b9216b21f..a36f3fe5b 100644 --- a/examples/models/models_animation.c +++ b/examples/models/models_animation.c @@ -54,8 +54,7 @@ int main(void) ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount); int animFrameCounter = 0; - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -64,7 +63,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Play animation when spacebar is held down if (IsKeyDown(KEY_SPACE)) diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index ce313949b..6d16cf01f 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -48,14 +48,13 @@ int main(void) // Here we choose to rotate around the image center // NOTE: (-1, 1) is the range where origin.x, origin.y is inside the texture Vector2 rotateOrigin = { 0.0f }; - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode // 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 //-------------------------------------------------------------------------------------- @@ -64,7 +63,8 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); + rotation += 0.4f; distanceStatic = Vector3Distance(camera.position, billPositionStatic); distanceRotating = Vector3Distance(camera.position, billPositionRotating); diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index 4953a99fc..83bfab68b 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -42,8 +42,7 @@ int main(void) UnloadImage(image); // Unload cubesmap image from RAM, already uploaded to VRAM - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -52,7 +51,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_first_person_maze.c b/examples/models/models_first_person_maze.c index b5e35b585..3f6a935c3 100644 --- a/examples/models/models_first_person_maze.c +++ b/examples/models/models_first_person_maze.c @@ -45,8 +45,7 @@ int main(void) Vector3 mapPosition = { -16.0f, 0.0f, -8.0f }; // Set model position - SetCameraMode(camera, CAMERA_FIRST_PERSON); // Set camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -57,7 +56,7 @@ int main(void) //---------------------------------------------------------------------------------- Vector3 oldCamPos = camera.position; // Store old camera position - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Check player collision (we simplify to 2D collision detection) Vector2 playerPos = { camera.position.x, camera.position.z }; diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index a5d17b02e..25dc004d5 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -39,8 +39,7 @@ int main(void) UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -49,7 +48,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c index 7bdf1772f..56dad4084 100644 --- a/examples/models/models_loading.c +++ b/examples/models/models_loading.c @@ -57,10 +57,9 @@ int main(void) // NOTE: bounds are calculated from the original size of the model, // if model is scaled on drawing, bounds must be also scaled - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - bool selected = false; // Selected object flag + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -69,7 +68,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Load new models/textures on drag&drop if (IsFileDropped()) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index 92b517a1c..0c9070713 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -51,8 +51,6 @@ int main(void) Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -61,6 +59,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_THIRD_PERSON); // Select current animation if (IsKeyPressed(KEY_UP)) animIndex = (animIndex + 1)%animsCount; else if (IsKeyPressed(KEY_DOWN)) animIndex = (animIndex + animsCount - 1)%animsCount; @@ -69,9 +68,6 @@ int main(void) ModelAnimation anim = modelAnimations[animIndex]; animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; UpdateModelAnimation(model, anim, animCurrentFrame); - - // Update camera - UpdateCamera(&camera); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index 52dc95226..c1a4af07d 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -53,8 +53,7 @@ int main(void) int animFrameCounter = 0, animId = 0; ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data - SetCameraMode(camera, CAMERA_FREE); // Set free camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -63,7 +62,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); if (animsCount) { diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c index 49477cd9c..6a38fe89a 100644 --- a/examples/models/models_loading_vox.c +++ b/examples/models/models_loading_vox.c @@ -69,8 +69,7 @@ int main(void) int currentModel = 0; - SetCameraMode(camera, CAMERA_ORBITAL); // Set a orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -79,7 +78,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); // Cycle between models on mouse click if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1)%MAX_VOX_FILES; diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index b4ff018cc..8e9fdf900 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -68,8 +68,7 @@ int main(void) int currentModel = 0; - SetCameraMode(camera, CAMERA_ORBITAL); // Set a orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -78,7 +77,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 52f9974d3..1118168cb 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -64,8 +64,7 @@ int main(void) Vector3 sp = (Vector3){ -30.0f, 5.0f, 5.0f }; float sr = 4.0f; - SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode - + EnableCursor(); // Disable camera controls SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -73,7 +72,14 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + if (IsCursorHidden()) UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Update camera + + // Toggle camera controls + if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) + { + if (IsCursorHidden()) EnableCursor(); + else DisableCursor(); + } // Display information about closest hit RayCollision collision = { 0 }; @@ -219,7 +225,7 @@ int main(void) DrawText(TextFormat("Barycenter: %3.2f %3.2f %3.2f", bary.x, bary.y, bary.z), 10, ypos + 45, 10, BLACK); } - DrawText("Use Mouse to Move Camera", 10, 430, 10, GRAY); + DrawText("Right click mouse to toggle camera controls", 10, 430, 10, GRAY); DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, GRAY); diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c index 903752f76..24f8b9a54 100644 --- a/examples/models/models_rlgl_solar_system.c +++ b/examples/models/models_rlgl_solar_system.c @@ -49,8 +49,6 @@ int main(void) camera.fovy = 45.0f; camera.projection = CAMERA_PERSPECTIVE; - SetCameraMode(camera, CAMERA_FREE); - float rotationSpeed = 0.2f; // General system rotation speed float earthRotation = 0.0f; // Rotation of earth around itself (days) in degrees @@ -58,6 +56,7 @@ int main(void) float moonRotation = 0.0f; // Rotation of moon around itself float moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -66,7 +65,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); earthRotation += (5.0f*rotationSpeed); earthOrbitRotation += (365/360.0f*(5.0f*rotationSpeed)*rotationSpeed); diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 1b9923334..d12cc557d 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -87,8 +87,7 @@ int main(void) UnloadImage(img); } - SetCameraMode(camera, CAMERA_FIRST_PERSON); // Set a first person camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -97,7 +96,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Load new cubemap texture on drag&drop if (IsFileDropped()) diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 1b29b8522..702177f16 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -80,8 +80,7 @@ int main(void) lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, shader); lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, shader); - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -90,7 +89,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + 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 }; diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index 75f6112e5..c617a6d1f 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -67,9 +67,7 @@ int main(void) // Create a RenderTexture2D to be used for render to texture RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - // Setup orbital camera - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -78,7 +76,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); Vector2 mousePosition = GetMousePosition(); diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index ac84a0180..c4b619edc 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -84,8 +84,7 @@ int main(void) // Using just 1 point lights CreateLight(LIGHT_POINT, (Vector3){ 0, 2, 6 }, Vector3Zero(), WHITE, shader); - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -94,7 +93,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); if (IsKeyDown(KEY_UP)) { diff --git a/examples/shaders/shaders_mesh_instancing.c b/examples/shaders/shaders_mesh_instancing.c index f4e6b40b0..0c2f9e1d9 100644 --- a/examples/shaders/shaders_mesh_instancing.c +++ b/examples/shaders/shaders_mesh_instancing.c @@ -94,9 +94,7 @@ int main(void) Material matDefault = LoadMaterialDefault(); matDefault.maps[MATERIAL_MAP_DIFFUSE].color = BLUE; - // Set an orbital camera mode - SetCameraMode(camera, CAMERA_ORBITAL); - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -105,7 +103,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); // Update the light shader with the camera view position float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index de3cb5f8d..069d0ee25 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -60,8 +60,7 @@ int main(void) Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - SetCameraMode(camera, CAMERA_FREE); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -70,7 +69,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); //---------------------------------------------------------------------------------- // Draw diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index d0555f219..688ca9092 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -107,9 +107,7 @@ int main(void) // Create a RenderTexture2D to be used for render to texture RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - // Setup orbital camera - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -118,7 +116,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); if (IsKeyPressed(KEY_RIGHT)) currentShader++; else if (IsKeyPressed(KEY_LEFT)) currentShader--; diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index 9b8bc30cd..5cc9af050 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -41,8 +41,6 @@ int main(void) camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 65.0f; // Camera field-of-view Y - SetCameraMode(camera, CAMERA_FREE); // Set camera mode - // 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)); @@ -58,6 +56,7 @@ int main(void) float runTime = 0.0f; + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -66,7 +65,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + 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 }; diff --git a/examples/shaders/shaders_simple_mask.c b/examples/shaders/shaders_simple_mask.c index ce3fc8bb9..49470294b 100644 --- a/examples/shaders/shaders_simple_mask.c +++ b/examples/shaders/shaders_simple_mask.c @@ -85,6 +85,7 @@ int main(void) int framesCounter = 0; Vector3 rotation = { 0 }; // Model rotation angles + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -93,7 +94,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_FIRST_PERSON); framesCounter++; rotation.x += 0.01f; diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index 127eb7c6a..c90ea6658 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -98,11 +98,12 @@ int main(void) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera mode type - SetCameraMode(camera, CAMERA_ORBITAL); + int camera_mode = CAMERA_ORBITAL; Vector3 cubePosition = { 0.0f, 1.0f, 0.0f }; Vector3 cubeSize = { 2.0f, 2.0f, 2.0f }; + DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second // Use the default font @@ -141,7 +142,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, camera_mode); // Handle font files dropped if (IsFileDropped()) @@ -181,12 +182,12 @@ int main(void) if (spin) { camera.position = (Vector3){ -10.0f, 15.0f, -10.0f }; // Camera position - SetCameraMode(camera, CAMERA_ORBITAL); + camera_mode = CAMERA_ORBITAL; } else { camera.position = (Vector3){ 10.0f, 10.0f, -10.0f }; // Camera position - SetCameraMode(camera, CAMERA_FREE); + camera_mode = CAMERA_FREE; } } diff --git a/src/raylib.h b/src/raylib.h index e31a514a6..650b5b29c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1156,13 +1156,7 @@ RLAPI float GetGesturePinchAngle(void); // Get gesture pinch ang //------------------------------------------------------------------------------------ // Camera System Functions (Module: rcamera) //------------------------------------------------------------------------------------ -RLAPI void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) -RLAPI void UpdateCamera(Camera *camera); // Update camera position for selected mode - -RLAPI void SetCameraPanControl(int keyPan); // Set camera pan key to combine with mouse movement (free camera) -RLAPI void SetCameraAltControl(int keyAlt); // Set camera alt key to combine with mouse movement (free camera) -RLAPI void SetCameraSmoothZoomControl(int keySmoothZoom); // Set camera smooth zoom key to combine with mouse (free camera) -RLAPI void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int keyLeft, int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) +RLAPI void UpdateCamera(Camera3D *camera, int mode); // Update camera position for selected mode //------------------------------------------------------------------------------------ // Basic Shapes Drawing Functions (Module: shapes) diff --git a/src/rcamera.h b/src/rcamera.h index 0caad666d..60ac642a5 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -2,8 +2,6 @@ * * rcamera - Basic camera system for multiple camera modes * -* NOTE: Memory footprint of this library is aproximately 52 bytes (global variables) -* * CONFIGURATION: * * #define CAMERA_IMPLEMENTATION @@ -17,6 +15,7 @@ * * CONTRIBUTORS: * Ramon Santamaria: Supervision, review, update and maintenance +* Christoph Wagner: Redesign (2022) * Marc Palau: Initial implementation (2014) * * @@ -44,15 +43,26 @@ #ifndef RCAMERA_H #define RCAMERA_H +// The only dependency // TODO review standalone mode +#include "raymath.h" + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- //... +#if defined(CAMERA_STANDALONE) +#define CAMERA_CULL_DISTANCE_NEAR 0.01 +#define CAMERA_CULL_DISTANCE_FAR 1000.0 +#else +#define CAMERA_CULL_DISTANCE_NEAR RL_CULL_DISTANCE_NEAR +#define CAMERA_CULL_DISTANCE_FAR RL_CULL_DISTANCE_FAR +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition // NOTE: Below types are required for CAMERA_STANDALONE usage //---------------------------------------------------------------------------------- +// TODO review #if defined(CAMERA_STANDALONE) // Vector2 type typedef struct Vector2 { @@ -76,8 +86,6 @@ int projection; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; - typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D - // Camera system modes typedef enum { CAMERA_CUSTOM = 0, @@ -97,7 +105,7 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -//... + //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -107,17 +115,19 @@ extern "C" { // Prevents name mangling of functions #endif -#if defined(CAMERA_STANDALONE) -void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) -void UpdateCamera(Camera *camera); // Update camera position for selected mode - -void SetCameraPanControl(int keyPan); // Set camera pan key to combine with mouse movement (free camera) -void SetCameraAltControl(int keyAlt); // Set camera alt key to combine with mouse movement (free camera) -void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera) -void SetCameraMoveControls(int keyFront, int keyBack, - int keyRight, int keyLeft, - int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) -#endif +Vector3 GetCameraForward(Camera3D* camera); +Vector3 GetCameraUp(Camera3D* camera); +Vector3 GetCameraRight(Camera3D* camera); +void CameraMoveForward(Camera3D* camera, float distance, bool moveInWorldPlane); +void CameraMoveUp(Camera3D* camera, float distance); +void CameraMoveRight(Camera3D* camera, float distance, bool moveInWorldPlane); +void CameraZoom(Camera3D* camera, float delta); +void CameraYaw(Camera3D* camera, float angle, bool rotateAroundTarget); +void CameraPitch(Camera3D* camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp); +void CameraRoll(Camera3D* camera, float angle); +void CameraViewBobbing(Camera3D* camera); +Matrix GetCameraViewMatrix(Camera3D* camera); +Matrix GetCameraProjectionMatrix(Camera3D* camera, float aspect); #if defined(__cplusplus) } @@ -134,424 +144,310 @@ void SetCameraMoveControls(int keyFront, int keyBack, #if defined(CAMERA_IMPLEMENTATION) -#include // Required for: sinf(), cosf(), sqrtf() - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846 -#endif -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif + +#define CAMERA_MOVE_SPEED 0.09f +#define CAMERA_ROTATION_SPEED 0.03f // Camera mouse movement sensitivity -#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.5f // TODO: it should be independent of framerate +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate #define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f -// FREE_CAMERA -#define CAMERA_FREE_MOUSE_SENSITIVITY 0.01f -#define CAMERA_FREE_DISTANCE_MIN_CLAMP 0.3f -#define CAMERA_FREE_DISTANCE_MAX_CLAMP 120.0f -#define CAMERA_FREE_MIN_CLAMP 85.0f -#define CAMERA_FREE_MAX_CLAMP -85.0f -#define CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY 0.05f -#define CAMERA_FREE_PANNING_DIVIDER 5.1f +#define CAMERA_ORBITAL_SPEED 0.01f // Radians per frame -// ORBITAL_CAMERA -#define CAMERA_ORBITAL_SPEED 0.5f // Radians per second -// FIRST_PERSON -//#define CAMERA_FIRST_PERSON_MOUSE_SENSITIVITY 0.003f -#define CAMERA_FIRST_PERSON_FOCUS_DISTANCE 25.0f -#define CAMERA_FIRST_PERSON_MIN_CLAMP 89.0f -#define CAMERA_FIRST_PERSON_MAX_CLAMP -89.0f - -// When walking, y-position of the player moves up-down at step frequency (swinging) but -// also the body slightly tilts left-right on every step, when all the body weight is left over one foot (tilting) -#define CAMERA_FIRST_PERSON_STEP_FREQUENCY 1.8f // Step frequency when walking (steps per second) -#define CAMERA_FIRST_PERSON_SWINGING_DELTA 0.03f // Maximum up-down swinging distance when walking -#define CAMERA_FIRST_PERSON_TILTING_DELTA 0.005f // Maximum left-right tilting distance when walking - -// THIRD_PERSON -//#define CAMERA_THIRD_PERSON_MOUSE_SENSITIVITY 0.003f -#define CAMERA_THIRD_PERSON_DISTANCE_CLAMP 1.2f -#define CAMERA_THIRD_PERSON_MIN_CLAMP 5.0f -#define CAMERA_THIRD_PERSON_MAX_CLAMP -85.0f -#define CAMERA_THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f } +#define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 8.0f +#define CAMERA_FIRST_PERSON_STEP_DIVIDER 30.0f +#define CAMERA_FIRST_PERSON_WAVING_DIVIDER 200.0f // PLAYER (used by camera) -#define PLAYER_MOVEMENT_SENSITIVITY 2.0f +#define PLAYER_MOVEMENT_SENSITIVITY 20.0f //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -// Camera move modes (first person and third person cameras) -typedef enum { - MOVE_FRONT = 0, - MOVE_BACK, - MOVE_RIGHT, - MOVE_LEFT, - MOVE_UP, - MOVE_DOWN -} CameraMove; -// Camera global state context data [56 bytes] -typedef struct { - unsigned int mode; // Current camera mode - float targetDistance; // Camera distance from position to target - float playerEyesPosition; // Player eyes position from ground (in meters) - Vector2 angle; // Camera angle in plane XZ - - // Camera movement control keys - int moveControl[6]; // Move controls (CAMERA_FIRST_PERSON) - int smoothZoomControl; // Smooth zoom control key - int altControl; // Alternative control key - int panControl; // Pan view control key -} CameraData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static CameraData CAMERA = { // Global CAMERA state context - .mode = 0, - .targetDistance = 0, - .playerEyesPosition = 1.85f, - .angle = { 0 }, - .moveControl = { 'W', 'S', 'D', 'A', 'E', 'Q' }, - .smoothZoomControl = 341, // raylib: KEY_LEFT_CONTROL - .altControl = 342, // raylib: KEY_LEFT_ALT - .panControl = 2 // raylib: MOUSE_BUTTON_MIDDLE -}; + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -#if defined(CAMERA_STANDALONE) -// NOTE: Camera controls depend on some raylib input functions -static void EnableCursor() {} // Unlock cursor -static void DisableCursor() {} // Lock cursor -static int IsKeyDown(int key) { return 0; } - -static int IsMouseButtonDown(int button) { return 0;} -static float GetMouseWheelMove() { return 0.0f; } -static Vector2 GetMousePosition() { return (Vector2){ 0.0f, 0.0f }; } -#endif //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- -// Select camera mode (multiple camera modes available) -void SetCameraMode(Camera camera, int mode) +// Returns the cameras forward vector (normalized) +Vector3 GetCameraForward(Camera3D *camera) { - Vector3 v1 = camera.position; - Vector3 v2 = camera.target; - - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - - CAMERA.targetDistance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance to target - - // Camera angle calculation - CAMERA.angle.x = atan2f(dx, dz); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) - CAMERA.angle.y = atan2f(dy, sqrtf(dx*dx + dz*dz)); // Camera angle in plane XY (0 aligned with X, move positive CW) - - CAMERA.playerEyesPosition = camera.position.y; // Init player eyes position to camera Y position - - // Lock cursor for first person and third person cameras - if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor(); - else EnableCursor(); - - CAMERA.mode = mode; + return Vector3Normalize(Vector3Subtract(camera->target, camera->position)); } -// Update camera depending on selected mode -// NOTE: Camera controls depend on some raylib functions: -// System: EnableCursor(), DisableCursor() -// Mouse: IsMouseButtonDown(), GetMousePosition(), GetMouseWheelMove() -// Keys: IsKeyDown() -void UpdateCamera(Camera *camera) +// Returns the cameras up vector (normalized) +// Note: The up vector might not be perpendicular to the forward vector +Vector3 GetCameraUp(Camera3D *camera) { - static float swingCounter = 0.0f; // Used for 1st person swinging movement + return Vector3Normalize(camera->up); +} - // TODO: Compute CAMERA.targetDistance and CAMERA.angle here (?) +// Returns the cameras right vector (normalized) +Vector3 GetCameraRight(Camera3D *camera) +{ + Vector3 forward = GetCameraForward(camera); + Vector3 up = GetCameraUp(camera); + return Vector3CrossProduct(forward, up); +} - // Mouse movement detection - Vector2 mousePositionDelta = GetMouseDelta(); - float mouseWheelMove = GetMouseWheelMove(); +// Moves the camera in its forward direction +void CameraMoveForward(Camera3D *camera, float distance, bool moveInWorldPlane) +{ + Vector3 forward = GetCameraForward(camera); - // Keys input detection - // TODO: Input detection is raylib-dependant, it could be moved outside the module - bool keyPan = IsMouseButtonDown(CAMERA.panControl); - bool keyAlt = IsKeyDown(CAMERA.altControl); - bool szoomKey = IsKeyDown(CAMERA.smoothZoomControl); - bool direction[6] = { IsKeyDown(CAMERA.moveControl[MOVE_FRONT]), - IsKeyDown(CAMERA.moveControl[MOVE_BACK]), - IsKeyDown(CAMERA.moveControl[MOVE_RIGHT]), - IsKeyDown(CAMERA.moveControl[MOVE_LEFT]), - IsKeyDown(CAMERA.moveControl[MOVE_UP]), - IsKeyDown(CAMERA.moveControl[MOVE_DOWN]) }; - - // Support for multiple automatic camera modes - // NOTE: In case of CAMERA_CUSTOM nothing happens here, user must update it manually - switch (CAMERA.mode) + if (moveInWorldPlane) { - case CAMERA_FREE: // Camera free controls, using standard 3d-content-creation scheme - { - // Camera zoom - if ((CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP; - } + // Project vector onto world plane + forward.y = 0; + forward = Vector3Normalize(forward); + } - // Camera looking down - else if ((camera->position.y > camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - } - else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + // Scale by distance + forward = Vector3Scale(forward, distance); - // if (camera->target.y < 0) camera->target.y = -0.001; - } - else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; - } - // Camera looking up - else if ((camera->position.y < camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - } - else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + // Move position and target + camera->position = Vector3Add(camera->position, forward); + camera->target = Vector3Add(camera->target, forward); +} - // if (camera->target.y > 0) camera->target.y = 0.001; - } - else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; - } +// Moves the camera in its up direction +void CameraMoveUp(Camera3D *camera, float distance) +{ + Vector3 up = GetCameraUp(camera); + + // Scale by distance + up = Vector3Scale(up, distance); - // Input keys checks - if (keyPan) - { - if (keyAlt) // Alternative key behaviour - { - if (szoomKey) - { - // Camera smooth zoom - CAMERA.targetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY); - } - else - { - // Camera rotation - CAMERA.angle.x += mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY; - CAMERA.angle.y += mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY; + // Move position and target + camera->position = Vector3Add(camera->position, up); + camera->target = Vector3Add(camera->target, up); +} - // Angle clamp - if (CAMERA.angle.y > CAMERA_FREE_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_FREE_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MAX_CLAMP*DEG2RAD; - } - } - else - { - // Camera panning - camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - } - } +// Moves the camera target in its current right direction +void CameraMoveRight(Camera3D *camera, float distance, bool moveInWorldPlane) +{ + Vector3 right = GetCameraRight(camera); - // Update camera position with changes - camera->position.x = -sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance + camera->target.y; - camera->position.z = -cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; + if (moveInWorldPlane) + { + // Project vector onto world plane + right.y = 0; + right = Vector3Normalize(right); + } - } break; - case CAMERA_ORBITAL: // Camera just orbits around target, only zoom allowed - { - CAMERA.angle.x += CAMERA_ORBITAL_SPEED*GetFrameTime(); // Camera orbit angle - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom + // Scale by distance + right = Vector3Scale(right, distance); - // Camera distance clamp - if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; + // Move position and target + camera->position = Vector3Add(camera->position, right); + camera->target = Vector3Add(camera->target, right); +} - // Update camera position with changes - camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - camera->position.y = ((CAMERA.angle.y <= 0.0f)? 1 : -1)*sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; +// Moves the camera position closer/farther to/from the camera target +void CameraZoom(Camera3D *camera, float delta) +{ + float distance = Vector3Distance(camera->position, camera->target); - } break; - case CAMERA_FIRST_PERSON: // Camera moves as in a first-person game, controls are configurable - { - // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position' - camera->position.y = CAMERA.playerEyesPosition; + // Apply delta + distance += delta; - camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - - cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + - cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); + // Distance must be greater than 0 + if (distance < 0) distance = 0.001f; - camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - - sinf(CAMERA.angle.y)*direction[MOVE_BACK] + - 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); + // Set new distance by moving the position along the forward vector + Vector3 forward = GetCameraForward(camera); + camera->position = Vector3Add(camera->target, Vector3Scale(forward, -distance)); +} - camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - - cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + - sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - - sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); +// Rotates the camera around its up vector +// Yaw is "looking left and right" +// If rotateAroundTarget is false, the camera rotates around its position +// Note: angle must be provided in radians +void CameraYaw(Camera3D *camera, float angle, bool rotateAroundTarget) +{ + // Rotation axis + Vector3 up = GetCameraUp(camera); - // Camera orientation calculation - CAMERA.angle.x -= mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY*GetFrameTime(); - CAMERA.angle.y -= mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY*GetFrameTime(); + // View vector + Vector3 target_position = Vector3Subtract(camera->target, camera->position); - // Angle clamp - if (CAMERA.angle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD; + // Rotate view vector around up axis + target_position = Vector3RotateByAxisAngle(target_position, up, angle); - // Calculate translation matrix - Matrix matTranslation = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, (CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER), - 0.0f, 0.0f, 0.0f, 1.0f }; - - // Calculate rotation matrix - Matrix matRotation = { 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 }; - - float cosz = cosf(0.0f); - float sinz = sinf(0.0f); - float cosy = cosf(-(PI*2 - CAMERA.angle.x)); - float siny = sinf(-(PI*2 - CAMERA.angle.x)); - float cosx = cosf(-(PI*2 - CAMERA.angle.y)); - float sinx = sinf(-(PI*2 - CAMERA.angle.y)); - - matRotation.m0 = cosz*cosy; - matRotation.m4 = (cosz*siny*sinx) - (sinz*cosx); - matRotation.m8 = (cosz*siny*cosx) + (sinz*sinx); - matRotation.m1 = sinz*cosy; - matRotation.m5 = (sinz*siny*sinx) + (cosz*cosx); - matRotation.m9 = (sinz*siny*cosx) - (cosz*sinx); - matRotation.m2 = -siny; - matRotation.m6 = cosy*sinx; - matRotation.m10= cosy*cosx; - - // Multiply translation and rotation matrices - Matrix matTransform = { 0 }; - matTransform.m0 = matTranslation.m0*matRotation.m0 + matTranslation.m1*matRotation.m4 + matTranslation.m2*matRotation.m8 + matTranslation.m3*matRotation.m12; - matTransform.m1 = matTranslation.m0*matRotation.m1 + matTranslation.m1*matRotation.m5 + matTranslation.m2*matRotation.m9 + matTranslation.m3*matRotation.m13; - matTransform.m2 = matTranslation.m0*matRotation.m2 + matTranslation.m1*matRotation.m6 + matTranslation.m2*matRotation.m10 + matTranslation.m3*matRotation.m14; - matTransform.m3 = matTranslation.m0*matRotation.m3 + matTranslation.m1*matRotation.m7 + matTranslation.m2*matRotation.m11 + matTranslation.m3*matRotation.m15; - matTransform.m4 = matTranslation.m4*matRotation.m0 + matTranslation.m5*matRotation.m4 + matTranslation.m6*matRotation.m8 + matTranslation.m7*matRotation.m12; - matTransform.m5 = matTranslation.m4*matRotation.m1 + matTranslation.m5*matRotation.m5 + matTranslation.m6*matRotation.m9 + matTranslation.m7*matRotation.m13; - matTransform.m6 = matTranslation.m4*matRotation.m2 + matTranslation.m5*matRotation.m6 + matTranslation.m6*matRotation.m10 + matTranslation.m7*matRotation.m14; - matTransform.m7 = matTranslation.m4*matRotation.m3 + matTranslation.m5*matRotation.m7 + matTranslation.m6*matRotation.m11 + matTranslation.m7*matRotation.m15; - matTransform.m8 = matTranslation.m8*matRotation.m0 + matTranslation.m9*matRotation.m4 + matTranslation.m10*matRotation.m8 + matTranslation.m11*matRotation.m12; - matTransform.m9 = matTranslation.m8*matRotation.m1 + matTranslation.m9*matRotation.m5 + matTranslation.m10*matRotation.m9 + matTranslation.m11*matRotation.m13; - matTransform.m10 = matTranslation.m8*matRotation.m2 + matTranslation.m9*matRotation.m6 + matTranslation.m10*matRotation.m10 + matTranslation.m11*matRotation.m14; - matTransform.m11 = matTranslation.m8*matRotation.m3 + matTranslation.m9*matRotation.m7 + matTranslation.m10*matRotation.m11 + matTranslation.m11*matRotation.m15; - matTransform.m12 = matTranslation.m12*matRotation.m0 + matTranslation.m13*matRotation.m4 + matTranslation.m14*matRotation.m8 + matTranslation.m15*matRotation.m12; - matTransform.m13 = matTranslation.m12*matRotation.m1 + matTranslation.m13*matRotation.m5 + matTranslation.m14*matRotation.m9 + matTranslation.m15*matRotation.m13; - matTransform.m14 = matTranslation.m12*matRotation.m2 + matTranslation.m13*matRotation.m6 + matTranslation.m14*matRotation.m10 + matTranslation.m15*matRotation.m14; - matTransform.m15 = matTranslation.m12*matRotation.m3 + matTranslation.m13*matRotation.m7 + matTranslation.m14*matRotation.m11 + matTranslation.m15*matRotation.m15; - - camera->target.x = camera->position.x - matTransform.m12; - camera->target.y = camera->position.y - matTransform.m13; - camera->target.z = camera->position.z - matTransform.m14; - - // Camera swinging (y-movement), only when walking (some key pressed) - for (int i = 0; i < 6; i++) if (direction[i]) { swingCounter += GetFrameTime(); break; } - camera->position.y -= sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_SWINGING_DELTA; - - // Camera waiving (xz-movement), only when walking (some key pressed) - camera->up.x = sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_TILTING_DELTA; - camera->up.z = -sinf(2*PI*CAMERA_FIRST_PERSON_STEP_FREQUENCY*swingCounter)*CAMERA_FIRST_PERSON_TILTING_DELTA; - - } break; - case CAMERA_THIRD_PERSON: // Camera moves as in a third-person game, following target at a distance, controls are configurable - { - camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - - cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + - cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); - - camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - - sinf(CAMERA.angle.y)*direction[MOVE_BACK] + - 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); - - camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - - cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + - sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - - sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])*PLAYER_MOVEMENT_SENSITIVITY*GetFrameTime(); - - // Camera orientation calculation - CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); - CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); - - // Angle clamp - if (CAMERA.angle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD; - - // Camera zoom - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - - // Camera distance clamp - if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; - - camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - - if (CAMERA.angle.y <= 0.0f) camera->position.y = sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - else camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - - camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; - - } break; - case CAMERA_CUSTOM: break; - default: break; + if (rotateAroundTarget) + { + // Move position relative to target + camera->position = Vector3Subtract(camera->target, target_position); + } + else // rotate around camera.position + { + // Move target relative to position + camera->target = Vector3Add(camera->position, target_position); } } -// Set camera pan key to combine with mouse movement (free camera) -void SetCameraPanControl(int keyPan) { CAMERA.panControl = keyPan; } - -// Set camera alt key to combine with mouse movement (free camera) -void SetCameraAltControl(int keyAlt) { CAMERA.altControl = keyAlt; } - -// Set camera smooth zoom key to combine with mouse (free camera) -void SetCameraSmoothZoomControl(int szoomKey) { CAMERA.smoothZoomControl = szoomKey; } - -// Set camera move controls (1st person and 3rd person cameras) -void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int keyLeft, int keyUp, int keyDown) +// Rotates the camera around its right vector +// Pitch is "looking up and down" +// lockView prevents camera overrotation (aka "somersaults") +// If rotateAroundTarget is false, the camera rotates around its position +// rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE) +// Note: angle must be provided in radians +void CameraPitch(Camera3D *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp) { - CAMERA.moveControl[MOVE_FRONT] = keyFront; - CAMERA.moveControl[MOVE_BACK] = keyBack; - CAMERA.moveControl[MOVE_RIGHT] = keyRight; - CAMERA.moveControl[MOVE_LEFT] = keyLeft; - CAMERA.moveControl[MOVE_UP] = keyUp; - CAMERA.moveControl[MOVE_DOWN] = keyDown; + // Up direction + Vector3 up = GetCameraUp(camera); + + // View vector + Vector3 target_position = Vector3Subtract(camera->target, camera->position); + + if (lockView) + { + // In these camera modes we clamp the Pitch angle + // to allow only viewing straight up or down. + + // Clamp view up + float max_angle_up = Vector3Angle(up, target_position); + max_angle_up -= 0.001f; // avoid numerical errors + if (angle > max_angle_up) angle = max_angle_up; + + // Clamp view down + float max_angle_down = Vector3Angle(Vector3Negate(up), target_position); + max_angle_down *= -1.0f; // downwards angle is negative + max_angle_down += 0.001f; // avoid numerical errors + if (angle < max_angle_down) angle = max_angle_down; + } + + // Rotation axis + Vector3 right = GetCameraRight(camera); + + // Rotate view vector around right axis + target_position = Vector3RotateByAxisAngle(target_position, right, angle); + + if (rotateAroundTarget) + { + // Move position relative to target + camera->position = Vector3Subtract(camera->target, target_position); + } + else // rotate around camera.position + { + // Move target relative to position + camera->target = Vector3Add(camera->position, target_position); + } + + if (rotateUp) + { + // Rotate up direction around right axis + camera->up = Vector3RotateByAxisAngle(camera->up, right, angle); + } } +// Rotates the camera around its forward vector +// Roll is "turning your head sideways to the left or right" +// Note: angle must be provided in radians +void CameraRoll(Camera3D *camera, float angle) +{ + // Rotation axis + Vector3 forward = GetCameraForward(camera); + + // Rotate up direction around forward axis + camera->up = Vector3RotateByAxisAngle(camera->up, forward, angle); +} + +// Moves camera slightly to simulate a walking motion +// Note: Only active if camera->swingCounter > 0 +void CameraViewBobbing(Camera3D *camera) +{ + if (camera->swingCounter > 0) + { + // NOTE: We delay the target movement relative to the position movement to create a little pitch with each step. + camera->position.y = camera->position.y - 0.25f * sinf((camera->swingCounter + 1) / CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / CAMERA_FIRST_PERSON_STEP_DIVIDER; + camera->target.y = camera->target.y - 0.25f * sinf((camera->swingCounter - 1) / CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / CAMERA_FIRST_PERSON_STEP_DIVIDER; + + // Update counter for next frame + camera->swingCounter %= 2147483647 /* INT_MAX */; // Counter must be positive + camera->swingCounter++; + } +} + +// Returns the camera view matrix +Matrix GetCameraViewMatrix(Camera3D *camera) +{ + return MatrixLookAt(camera->position, camera->target, camera->up); +} + +// Returns the camera projection matrix +Matrix GetCameraProjectionMatrix(Camera3D *camera, float aspect) +{ + if (camera->projection == CAMERA_PERSPECTIVE) + { + return MatrixPerspective(camera->fovy * DEG2RAD, aspect, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR); + } + else if (camera->projection == CAMERA_ORTHOGRAPHIC) + { + double top = camera->fovy / 2.0; + double right = top * aspect; + return MatrixOrtho(-right, right, -top, top, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR); + } + + return MatrixIdentity(); +} + + +#ifndef CAMERA_STANDALONE +// Update camera position for selected mode +// Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM +void UpdateCamera(Camera3D *camera, int mode) +{ + Vector2 mousePositionDelta = GetMouseDelta(); + + bool moveInWorldPlane = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON; + bool rotateAroundTarget = mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; + bool lockView = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; + bool rotateUp = mode == CAMERA_FREE; + + // Camera movement + if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); + if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + + // Camera rotation + if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); + if (IsKeyDown(KEY_UP)) CameraPitch(camera, CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); + if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -CAMERA_ROTATION_SPEED, rotateAroundTarget); + if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, CAMERA_ROTATION_SPEED, rotateAroundTarget); + if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); + if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); + + CameraYaw(camera, mousePositionDelta.x * -CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, mousePositionDelta.y * -CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + + // Zoom target distance + CameraZoom(camera, -GetMouseWheelMove()); + if (IsKeyPressed(KEY_KP_SUBTRACT)) CameraZoom(camera, 2.0f); + if (IsKeyPressed(KEY_KP_ADD)) CameraZoom(camera, -2.0f); + + + // Apply view bobbing when moving around (per default only active in CAMERA_FIRST_PERSON) + if (mode == CAMERA_FIRST_PERSON && (IsKeyDown(KEY_W) || IsKeyDown(KEY_A) || IsKeyDown(KEY_S) || IsKeyDown(KEY_D))) CameraViewBobbing(camera); +} +#endif // !CAMERA_STANDALONE + #endif // CAMERA_IMPLEMENTATION diff --git a/src/rcamera_old.h b/src/rcamera_old.h new file mode 100644 index 000000000..08c376690 --- /dev/null +++ b/src/rcamera_old.h @@ -0,0 +1,567 @@ +/******************************************************************************************* +* +* rcamera - Basic camera system for multiple camera modes +* +* NOTE: Memory footprint of this library is aproximately 52 bytes (global variables) +* +* CONFIGURATION: +* +* #define CAMERA_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 CAMERA_STANDALONE +* If defined, the library can be used as standalone as a camera system but some +* functions must be redefined to manage inputs accordingly. +* +* CONTRIBUTORS: +* Ramon Santamaria: Supervision, review, update and maintenance +* Marc Palau: Initial implementation (2014) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2015-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 RCAMERA_H +#define RCAMERA_H + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Below types are required for CAMERA_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(CAMERA_STANDALONE) + // Vector2 type + typedef struct Vector2 { + float x; + float y; + } Vector2; + + // Vector3 type + typedef struct Vector3 { + float x; + float y; + float z; + } Vector3; + + // Camera type, defines a camera position/orientation in 3d space + typedef struct Camera3D { + 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 apperture in Y (degrees) in perspective, used as near plane width in orthographic + int type; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC + } Camera3D; + + typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D + + // Camera system modes + typedef enum { + CAMERA_CUSTOM = 0, + CAMERA_FREE, + CAMERA_ORBITAL, + CAMERA_FIRST_PERSON, + CAMERA_THIRD_PERSON + } CameraMode; + + // Camera projection modes + typedef enum { + CAMERA_PERSPECTIVE = 0, + CAMERA_ORTHOGRAPHIC + } CameraProjection; +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +#if defined(CAMERA_STANDALONE) +void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) +void UpdateCamera(Camera *camera); // Update camera position for selected mode + +void SetCameraPanControl(int keyPan); // Set camera pan key to combine with mouse movement (free camera) +void SetCameraAltControl(int keyAlt); // Set camera alt key to combine with mouse movement (free camera) +void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera) +void SetCameraMoveControls(int keyFront, int keyBack, + int keyRight, int keyLeft, + int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) +#endif + +#ifdef __cplusplus +} +#endif + +#endif // CAMERA_H + + +/*********************************************************************************** +* +* CAMERA IMPLEMENTATION +* +************************************************************************************/ + +#if defined(CAMERA_IMPLEMENTATION) + +#include // Required for: sinf(), cosf(), sqrtf() + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846 +#endif +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +// Camera mouse movement sensitivity +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f +#define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f + +// FREE_CAMERA +#define CAMERA_FREE_MOUSE_SENSITIVITY 0.01f +#define CAMERA_FREE_DISTANCE_MIN_CLAMP 0.3f +#define CAMERA_FREE_DISTANCE_MAX_CLAMP 120.0f +#define CAMERA_FREE_MIN_CLAMP 85.0f +#define CAMERA_FREE_MAX_CLAMP -85.0f +#define CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY 0.05f +#define CAMERA_FREE_PANNING_DIVIDER 5.1f + +// ORBITAL_CAMERA +#define CAMERA_ORBITAL_SPEED 0.01f // Radians per frame + +// FIRST_PERSON +//#define CAMERA_FIRST_PERSON_MOUSE_SENSITIVITY 0.003f +#define CAMERA_FIRST_PERSON_FOCUS_DISTANCE 25.0f +#define CAMERA_FIRST_PERSON_MIN_CLAMP 89.0f +#define CAMERA_FIRST_PERSON_MAX_CLAMP -89.0f + +#define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 8.0f +#define CAMERA_FIRST_PERSON_STEP_DIVIDER 30.0f +#define CAMERA_FIRST_PERSON_WAVING_DIVIDER 200.0f + +// THIRD_PERSON +//#define CAMERA_THIRD_PERSON_MOUSE_SENSITIVITY 0.003f +#define CAMERA_THIRD_PERSON_DISTANCE_CLAMP 1.2f +#define CAMERA_THIRD_PERSON_MIN_CLAMP 5.0f +#define CAMERA_THIRD_PERSON_MAX_CLAMP -85.0f +#define CAMERA_THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f } + +// PLAYER (used by camera) +#define PLAYER_MOVEMENT_SENSITIVITY 20.0f + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Camera move modes (first person and third person cameras) +typedef enum { + MOVE_FRONT = 0, + MOVE_BACK, + MOVE_RIGHT, + MOVE_LEFT, + MOVE_UP, + MOVE_DOWN +} CameraMove; + +// Camera global state context data [56 bytes] +typedef struct { + unsigned int mode; // Current camera mode + float targetDistance; // Camera distance from position to target + float playerEyesPosition; // Player eyes position from ground (in meters) + Vector2 angle; // Camera angle in plane XZ + Vector2 previousMousePosition; // Previous mouse position + + // Camera movement control keys + int moveControl[6]; // Move controls (CAMERA_FIRST_PERSON) + int smoothZoomControl; // Smooth zoom control key + int altControl; // Alternative control key + int panControl; // Pan view control key +} CameraData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static CameraData CAMERA = { // Global CAMERA state context + .mode = 0, + .targetDistance = 0, + .playerEyesPosition = 1.85f, + .angle = { 0 }, + .previousMousePosition = { 0 }, + .moveControl = { 'W', 'S', 'D', 'A', 'E', 'Q' }, + .smoothZoomControl = 341, // raylib: KEY_LEFT_CONTROL + .altControl = 342, // raylib: KEY_LEFT_ALT + .panControl = 2 // raylib: MOUSE_BUTTON_MIDDLE +}; + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +#if defined(CAMERA_STANDALONE) +// NOTE: Camera controls depend on some raylib input functions +static void EnableCursor() {} // Unlock cursor +static void DisableCursor() {} // Lock cursor + +static int IsKeyDown(int key) { return 0; } + +static int IsMouseButtonDown(int button) { return 0;} +static float GetMouseWheelMove() { return 0.0f; } +static Vector2 GetMousePosition() { return (Vector2){ 0.0f, 0.0f }; } +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Select camera mode (multiple camera modes available) +void SetCameraMode(Camera camera, int mode) +{ + Vector3 v1 = camera.position; + Vector3 v2 = camera.target; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + float dz = v2.z - v1.z; + + CAMERA.targetDistance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance to target + + // Camera angle calculation + CAMERA.angle.x = atan2f(dx, dz); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) + CAMERA.angle.y = atan2f(dy, sqrtf(dx*dx + dz*dz)); // Camera angle in plane XY (0 aligned with X, move positive CW) + + CAMERA.playerEyesPosition = camera.position.y; // Init player eyes position to camera Y position + + CAMERA.previousMousePosition = GetMousePosition(); // Init mouse position + + // Lock cursor for first person and third person cameras + if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor(); + else EnableCursor(); + + CAMERA.mode = mode; +} + +// Update camera depending on selected mode +// NOTE: Camera controls depend on some raylib functions: +// System: EnableCursor(), DisableCursor() +// Mouse: IsMouseButtonDown(), GetMousePosition(), GetMouseWheelMove() +// Keys: IsKeyDown() +void UpdateCamera(Camera *camera) +{ + static int swingCounter = 0; // Used for 1st person swinging movement + + // TODO: Compute CAMERA.targetDistance and CAMERA.angle here (?) + + // Mouse movement detection + Vector2 mousePositionDelta = { 0.0f, 0.0f }; + Vector2 mousePosition = GetMousePosition(); + float mouseWheelMove = GetMouseWheelMove(); + + // Keys input detection + // TODO: Input detection is raylib-dependant, it could be moved outside the module + bool keyPan = IsMouseButtonDown(CAMERA.panControl); + bool keyAlt = IsKeyDown(CAMERA.altControl); + bool szoomKey = IsKeyDown(CAMERA.smoothZoomControl); + bool direction[6] = { IsKeyDown(CAMERA.moveControl[MOVE_FRONT]), + IsKeyDown(CAMERA.moveControl[MOVE_BACK]), + IsKeyDown(CAMERA.moveControl[MOVE_RIGHT]), + IsKeyDown(CAMERA.moveControl[MOVE_LEFT]), + IsKeyDown(CAMERA.moveControl[MOVE_UP]), + IsKeyDown(CAMERA.moveControl[MOVE_DOWN]) }; + + if (CAMERA.mode != CAMERA_CUSTOM) + { + mousePositionDelta.x = mousePosition.x - CAMERA.previousMousePosition.x; + mousePositionDelta.y = mousePosition.y - CAMERA.previousMousePosition.y; + + CAMERA.previousMousePosition = mousePosition; + } + + // Support for multiple automatic camera modes + // NOTE: In case of CAMERA_CUSTOM nothing happens here, user must update it manually + switch (CAMERA.mode) + { + case CAMERA_FREE: // Camera free controls, using standard 3d-content-creation scheme + { + // Camera zoom + if ((CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) + { + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); + if (CAMERA.targetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP; + } + + // Camera looking down + else if ((camera->position.y > camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) + { + camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + } + else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0)) + { + camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + + // if (camera->target.y < 0) camera->target.y = -0.001; + } + else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0)) + { + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); + if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; + } + // Camera looking up + else if ((camera->position.y < camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) + { + camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + } + else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0)) + { + camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; + + // if (camera->target.y > 0) camera->target.y = 0.001; + } + else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0)) + { + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); + if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; + } + + // Input keys checks + if (keyPan) + { + if (keyAlt) // Alternative key behaviour + { + if (szoomKey) + { + // Camera smooth zoom + CAMERA.targetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY); + } + else + { + // Camera rotation + CAMERA.angle.x += mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY; + CAMERA.angle.y += mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY; + + // Angle clamp + if (CAMERA.angle.y > CAMERA_FREE_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MIN_CLAMP*DEG2RAD; + else if (CAMERA.angle.y < CAMERA_FREE_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MAX_CLAMP*DEG2RAD; + } + } + else + { + // Camera panning + camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); + camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); + camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); + } + } + + // Update camera position with changes + camera->position.x = -sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; + camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance + camera->target.y; + camera->position.z = -cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; + + } break; + case CAMERA_ORBITAL: // Camera just orbits around target, only zoom allowed + { + CAMERA.angle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom + + // Camera distance clamp + if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; + + // Update camera position with changes + camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; + camera->position.y = ((CAMERA.angle.y <= 0.0f)? 1 : -1)*sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; + camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; + + } break; + case CAMERA_FIRST_PERSON: // Camera moves as in a first-person game, controls are configurable + { + camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - + sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - + cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + + cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; + + camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - + sinf(CAMERA.angle.y)*direction[MOVE_BACK] + + 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; + + camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - + cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + + sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - + sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; + + // Camera orientation calculation + CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); + CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); + + // Angle clamp + if (CAMERA.angle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; + else if (CAMERA.angle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD; + + // Calculate translation matrix + Matrix matTranslation = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, (CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER), + 0.0f, 0.0f, 0.0f, 1.0f }; + + // Calculate rotation matrix + Matrix matRotation = { 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 }; + + float cosz = cosf(0.0f); + float sinz = sinf(0.0f); + float cosy = cosf(-(PI*2 - CAMERA.angle.x)); + float siny = sinf(-(PI*2 - CAMERA.angle.x)); + float cosx = cosf(-(PI*2 - CAMERA.angle.y)); + float sinx = sinf(-(PI*2 - CAMERA.angle.y)); + + matRotation.m0 = cosz*cosy; + matRotation.m4 = (cosz*siny*sinx) - (sinz*cosx); + matRotation.m8 = (cosz*siny*cosx) + (sinz*sinx); + matRotation.m1 = sinz*cosy; + matRotation.m5 = (sinz*siny*sinx) + (cosz*cosx); + matRotation.m9 = (sinz*siny*cosx) - (cosz*sinx); + matRotation.m2 = -siny; + matRotation.m6 = cosy*sinx; + matRotation.m10= cosy*cosx; + + // Multiply translation and rotation matrices + Matrix matTransform = { 0 }; + matTransform.m0 = matTranslation.m0*matRotation.m0 + matTranslation.m1*matRotation.m4 + matTranslation.m2*matRotation.m8 + matTranslation.m3*matRotation.m12; + matTransform.m1 = matTranslation.m0*matRotation.m1 + matTranslation.m1*matRotation.m5 + matTranslation.m2*matRotation.m9 + matTranslation.m3*matRotation.m13; + matTransform.m2 = matTranslation.m0*matRotation.m2 + matTranslation.m1*matRotation.m6 + matTranslation.m2*matRotation.m10 + matTranslation.m3*matRotation.m14; + matTransform.m3 = matTranslation.m0*matRotation.m3 + matTranslation.m1*matRotation.m7 + matTranslation.m2*matRotation.m11 + matTranslation.m3*matRotation.m15; + matTransform.m4 = matTranslation.m4*matRotation.m0 + matTranslation.m5*matRotation.m4 + matTranslation.m6*matRotation.m8 + matTranslation.m7*matRotation.m12; + matTransform.m5 = matTranslation.m4*matRotation.m1 + matTranslation.m5*matRotation.m5 + matTranslation.m6*matRotation.m9 + matTranslation.m7*matRotation.m13; + matTransform.m6 = matTranslation.m4*matRotation.m2 + matTranslation.m5*matRotation.m6 + matTranslation.m6*matRotation.m10 + matTranslation.m7*matRotation.m14; + matTransform.m7 = matTranslation.m4*matRotation.m3 + matTranslation.m5*matRotation.m7 + matTranslation.m6*matRotation.m11 + matTranslation.m7*matRotation.m15; + matTransform.m8 = matTranslation.m8*matRotation.m0 + matTranslation.m9*matRotation.m4 + matTranslation.m10*matRotation.m8 + matTranslation.m11*matRotation.m12; + matTransform.m9 = matTranslation.m8*matRotation.m1 + matTranslation.m9*matRotation.m5 + matTranslation.m10*matRotation.m9 + matTranslation.m11*matRotation.m13; + matTransform.m10 = matTranslation.m8*matRotation.m2 + matTranslation.m9*matRotation.m6 + matTranslation.m10*matRotation.m10 + matTranslation.m11*matRotation.m14; + matTransform.m11 = matTranslation.m8*matRotation.m3 + matTranslation.m9*matRotation.m7 + matTranslation.m10*matRotation.m11 + matTranslation.m11*matRotation.m15; + matTransform.m12 = matTranslation.m12*matRotation.m0 + matTranslation.m13*matRotation.m4 + matTranslation.m14*matRotation.m8 + matTranslation.m15*matRotation.m12; + matTransform.m13 = matTranslation.m12*matRotation.m1 + matTranslation.m13*matRotation.m5 + matTranslation.m14*matRotation.m9 + matTranslation.m15*matRotation.m13; + matTransform.m14 = matTranslation.m12*matRotation.m2 + matTranslation.m13*matRotation.m6 + matTranslation.m14*matRotation.m10 + matTranslation.m15*matRotation.m14; + matTransform.m15 = matTranslation.m12*matRotation.m3 + matTranslation.m13*matRotation.m7 + matTranslation.m14*matRotation.m11 + matTranslation.m15*matRotation.m15; + + camera->target.x = camera->position.x - matTransform.m12; + camera->target.y = camera->position.y - matTransform.m13; + camera->target.z = camera->position.z - matTransform.m14; + + // If movement detected (some key pressed), increase swinging + for (int i = 0; i < 6; i++) if (direction[i]) { swingCounter++; break; } + + // Camera position update + // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position' + camera->position.y = CAMERA.playerEyesPosition - sinf(swingCounter/CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER)/CAMERA_FIRST_PERSON_STEP_DIVIDER; + + camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; + camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; + + } break; + case CAMERA_THIRD_PERSON: // Camera moves as in a third-person game, following target at a distance, controls are configurable + { + camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - + sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - + cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + + cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; + + camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - + sinf(CAMERA.angle.y)*direction[MOVE_BACK] + + 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; + + camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - + cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + + sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - + sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; + + // Camera orientation calculation + CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); + CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); + + // Angle clamp + if (CAMERA.angle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD; + else if (CAMERA.angle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD; + + // Camera zoom + CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); + + // Camera distance clamp + if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; + + camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; + + if (CAMERA.angle.y <= 0.0f) camera->position.y = sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; + else camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; + + camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; + + } break; + case CAMERA_CUSTOM: break; + default: break; + } +} + +// Set camera pan key to combine with mouse movement (free camera) +void SetCameraPanControl(int keyPan) { CAMERA.panControl = keyPan; } + +// Set camera alt key to combine with mouse movement (free camera) +void SetCameraAltControl(int keyAlt) { CAMERA.altControl = keyAlt; } + +// Set camera smooth zoom key to combine with mouse (free camera) +void SetCameraSmoothZoomControl(int szoomKey) { CAMERA.smoothZoomControl = szoomKey; } + +// Set camera move controls (1st person and 3rd person cameras) +void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int keyLeft, int keyUp, int keyDown) +{ + CAMERA.moveControl[MOVE_FRONT] = keyFront; + CAMERA.moveControl[MOVE_BACK] = keyBack; + CAMERA.moveControl[MOVE_RIGHT] = keyRight; + CAMERA.moveControl[MOVE_LEFT] = keyLeft; + CAMERA.moveControl[MOVE_UP] = keyUp; + CAMERA.moveControl[MOVE_DOWN] = keyDown; +} + +#endif // CAMERA_IMPLEMENTATION From ea590c44a967075b3f6b420fa76e6387075ecf1d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Feb 2023 20:00:51 +0100 Subject: [PATCH 0269/1710] REVIEWED: Camera redesign PR --- examples/core/core_3d_camera_first_person.c | 41 +++++--- examples/core/core_3d_camera_free.c | 4 +- examples/core/core_3d_picking.c | 9 +- examples/core/core_vr_simulator.c | 6 +- examples/core/core_world_screen.c | 17 ++-- examples/models/models_animation.c | 14 +-- examples/models/models_billboard.c | 22 ++-- examples/models/models_cubicmap.c | 16 ++- examples/models/models_first_person_maze.c | 13 ++- examples/models/models_heightmap.c | 28 ++++-- examples/models/models_loading.c | 3 +- examples/models/models_loading_gltf.c | 10 +- examples/models/models_loading_m3d.c | 12 ++- examples/models/models_loading_vox.c | 5 +- examples/models/models_mesh_generation.c | 3 +- examples/models/models_mesh_picking.c | 3 +- examples/models/models_rlgl_solar_system.c | 13 +-- examples/models/models_skybox.c | 14 ++- examples/models/models_waving_cubes.c | 10 +- examples/shaders/shaders_basic_lighting.c | 8 +- examples/shaders/shaders_custom_uniform.c | 11 +- examples/shaders/shaders_fog.c | 17 ++-- examples/shaders/shaders_mesh_instancing.c | 16 +-- examples/shaders/shaders_model_shader.c | 16 +-- examples/shaders/shaders_postprocessing.c | 17 ++-- examples/shaders/shaders_raymarching.c | 7 +- examples/shaders/shaders_simple_mask.c | 26 ++--- examples/shaders/shaders_spotlight.c | 79 +++++++-------- examples/text/text_draw_3d.c | 9 +- projects/VS2022/raylib.sln | 19 ++++ src/raylib.h | 2 +- src/rcamera.h | 106 ++++++++------------ src/{rcamera_old.h => rcamera.old.h} | 0 src/rcore.c | 13 ++- 34 files changed, 321 insertions(+), 268 deletions(-) rename src/{rcamera_old.h => rcamera.old.h} (100%) diff --git a/examples/core/core_3d_camera_first_person.c b/examples/core/core_3d_camera_first_person.c index d98a002fd..0fc784fa6 100644 --- a/examples/core/core_3d_camera_first_person.c +++ b/examples/core/core_3d_camera_first_person.c @@ -30,13 +30,12 @@ int main(void) // Define the camera to look into our 3d world (position, target, up vector) Camera camera = { 0 }; - camera.position = (Vector3){ 0.0f, 2.0f, 4.0f }; - camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.fovy = 60.0f; - camera.projection = CAMERA_PERSPECTIVE; - camera.swingCounter = 1; // Enable view bobbing - + camera.position = (Vector3){ 0.0f, 2.0f, 4.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 = 60.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + int cameraMode = CAMERA_FIRST_PERSON; // Generates some random columns @@ -51,36 +50,46 @@ int main(void) colors[i] = (Color){ GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255 }; } - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- // Switch camera mode - if (IsKeyPressed(KEY_ONE)) { + if (IsKeyPressed(KEY_ONE)) + { cameraMode = CAMERA_FREE; camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll } - if (IsKeyPressed(KEY_TWO)) { + + if (IsKeyPressed(KEY_TWO)) + { cameraMode = CAMERA_FIRST_PERSON; camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll } - if (IsKeyPressed(KEY_THREE)) { + + if (IsKeyPressed(KEY_THREE)) + { cameraMode = CAMERA_THIRD_PERSON; camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll } - if (IsKeyPressed(KEY_FOUR)) { + + if (IsKeyPressed(KEY_FOUR)) + { cameraMode = CAMERA_ORBITAL; camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Reset roll } // Switch camera projection - if (IsKeyPressed(KEY_P)) { - if (camera.projection == CAMERA_PERSPECTIVE) { + if (IsKeyPressed(KEY_P)) + { + if (camera.projection == CAMERA_PERSPECTIVE) + { // Create isometric view cameraMode = CAMERA_THIRD_PERSON; // Note: The target distance is related to the render distance in the orthographic projection diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index 887a1df33..59bd158a2 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -31,10 +31,12 @@ int main(void) 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 + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/core/core_3d_picking.c b/examples/core/core_3d_picking.c index 0cf56f5fe..c7bf94663 100644 --- a/examples/core/core_3d_picking.c +++ b/examples/core/core_3d_picking.c @@ -31,16 +31,13 @@ int main(void) 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 + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type Vector3 cubePosition = { 0.0f, 1.0f, 0.0f }; Vector3 cubeSize = { 2.0f, 2.0f, 2.0f }; Ray ray = { 0 }; // Picking line ray - - RayCollision collision = { 0 }; - - EnableCursor(); // Disable camera controls + RayCollision collision = { 0 }; // Ray collision hit info SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -50,7 +47,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if (IsCursorHidden()) UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Update camera + if (IsCursorHidden()) UpdateCamera(&camera, CAMERA_FIRST_PERSON); // Toggle camera controls if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index 3024b7853..bc69cc698 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -95,12 +95,12 @@ int main(void) 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 camera.fovy = 60.0f; // Camera field-of-view Y - camera.projection = CAMERA_PERSPECTIVE; // Camera type - camera.swingCounter = 1; // Enable view bobbing + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; - DisableCursor(); // Catch cursor + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(90); // Set our game to run at 90 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/core/core_world_screen.c b/examples/core/core_world_screen.c index f96690ba5..6c811a509 100644 --- a/examples/core/core_world_screen.c +++ b/examples/core/core_world_screen.c @@ -27,16 +27,17 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 10.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; + 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 Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; Vector2 cubeScreenPosition = { 0.0f, 0.0f }; - DisableCursor(); // Catch cursor + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -67,7 +68,9 @@ int main(void) EndMode3D(); DrawText("Enemy: 100 / 100", (int)cubeScreenPosition.x - MeasureText("Enemy: 100/100", 20)/2, (int)cubeScreenPosition.y, 20, BLACK); - DrawText("Text is always on top of the cube", (screenWidth - MeasureText("Text is always on top of the cube", 20))/2, 25, 20, GRAY); + + 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); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/models/models_animation.c b/examples/models/models_animation.c index a36f3fe5b..ffe2d0124 100644 --- a/examples/models/models_animation.c +++ b/examples/models/models_animation.c @@ -21,8 +21,6 @@ #include "raylib.h" -#include - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -102,15 +100,11 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); // Unload texture + UnloadTexture(texture); // Unload texture + UnloadModelAnimations(anims, animsCount); // Unload model animations data + UnloadModel(model); // Unload model - // Unload model animations data - for (unsigned int i = 0; i < animsCount; i++) UnloadModelAnimation(anims[i]); - RL_FREE(anims); - - UnloadModel(model); // Unload model - - CloseWindow(); // Close window and OpenGL context + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 6d16cf01f..596a09d3b 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -28,15 +28,15 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 5.0f, 4.0f, 5.0f }; - camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.fovy = 45.0f; - camera.projection = CAMERA_PERSPECTIVE; + 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 billboard - Vector3 billPositionRotating = { 1.0f, 2.0f, 1.0f }; + 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 }; @@ -55,11 +55,13 @@ int main(void) float distanceRotating; float rotation = 0.0f; - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index 83bfab68b..714918e41 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -26,7 +26,12 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - cubesmap loading and drawing"); // Define the camera to look into our 3d world - Camera camera = { { 16.0f, 14.0f, 16.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + 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) @@ -36,18 +41,19 @@ int main(void) // 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 + 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 - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/models/models_first_person_maze.c b/examples/models/models_first_person_maze.c index 3f6a935c3..6a4345a8b 100644 --- a/examples/models/models_first_person_maze.c +++ b/examples/models/models_first_person_maze.c @@ -28,7 +28,13 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze"); // Define the camera to look into our 3d world - Camera camera = { { 0.2f, 0.4f, 0.2f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { 0 }; + camera.position = (Vector3){ 0.2f, 0.4f, 0.2f }; // Camera position + camera.target = (Vector3){ 0.185f, 0.4f, 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 + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position Image imMap = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM) Texture2D cubicmap = LoadTextureFromImage(imMap); // Convert image to texture to display (VRAM) @@ -37,7 +43,7 @@ int main(void) // 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 + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture // Get map image data to be used for collision detection Color *mapPixels = LoadImageColors(imMap); @@ -45,7 +51,8 @@ int main(void) Vector3 mapPosition = { -16.0f, 0.0f, -8.0f }; // Set model position - DisableCursor(); // Catch cursor + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index 25dc004d5..8f32de9fe 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -26,25 +26,31 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing"); // Define our custom camera to look into our 3d world - Camera camera = { { 18.0f, 18.0f, 18.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + 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) + 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 + 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 + 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 + UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c index 56dad4084..4bce2a79e 100644 --- a/examples/models/models_loading.c +++ b/examples/models/models_loading.c @@ -59,7 +59,8 @@ int main(void) bool selected = false; // Selected object flag - DisableCursor(); // Catch cursor + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index 0c9070713..d8b34efed 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -34,11 +34,11 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position + 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 mode type + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type // Load gltf model Model model = LoadModel("resources/models/gltf/robot.glb"); @@ -51,11 +51,13 @@ int main(void) Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index c1a4af07d..42d3416de 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -33,11 +33,12 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 1.5f, 1.5f, 1.5f }; // Camera position + camera.position = (Vector3){ 1.5f, 1.5f, 1.5f }; // Camera position camera.target = (Vector3){ 0.0f, 0.4f, 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 + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position char modelFileName[128] = "resources/models/m3d/CesiumMan.m3d"; @@ -53,12 +54,13 @@ int main(void) int animFrameCounter = 0, animId = 0; ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c index 6a38fe89a..d17fd6ee6 100644 --- a/examples/models/models_loading_vox.c +++ b/examples/models/models_loading_vox.c @@ -43,7 +43,7 @@ int main(void) 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 + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type // Load MagicaVoxel files Model models[MAX_VOX_FILES] = { 0 }; @@ -69,7 +69,8 @@ int main(void) int currentModel = 0; - DisableCursor(); // Catch cursor + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index 8e9fdf900..6189fb46d 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -68,7 +68,8 @@ int main(void) int currentModel = 0; - DisableCursor(); // Catch cursor + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_mesh_picking.c b/examples/models/models_mesh_picking.c index 1118168cb..69d98aa18 100644 --- a/examples/models/models_mesh_picking.c +++ b/examples/models/models_mesh_picking.c @@ -36,7 +36,7 @@ int main(void) camera.target = (Vector3){ 0.0f, 8.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 mode type + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type Ray ray = { 0 }; // Picking ray @@ -64,7 +64,6 @@ int main(void) Vector3 sp = (Vector3){ -30.0f, 5.0f, 5.0f }; float sr = 4.0f; - EnableCursor(); // Disable camera controls SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c index 24f8b9a54..fc2a1f020 100644 --- a/examples/models/models_rlgl_solar_system.c +++ b/examples/models/models_rlgl_solar_system.c @@ -43,11 +43,11 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 16.0f, 16.0f, 16.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; + camera.position = (Vector3){ 16.0f, 16.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 float rotationSpeed = 0.2f; // General system rotation speed @@ -56,7 +56,8 @@ int main(void) float moonRotation = 0.0f; // Rotation of moon around itself float moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees - DisableCursor(); // Catch cursor + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index d12cc557d..34616de59 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -38,7 +38,12 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox loading and drawing"); // Define the camera to look into our 3d world - Camera camera = { { 1.0f, 1.0f, 1.0f }, { 4.0f, 1.0f, 4.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + 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); @@ -87,12 +92,13 @@ int main(void) UnloadImage(img); } - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/models/models_waving_cubes.c b/examples/models/models_waving_cubes.c index 31ed4ff41..d382c998d 100644 --- a/examples/models/models_waving_cubes.c +++ b/examples/models/models_waving_cubes.c @@ -31,11 +31,11 @@ int main() // Initialize the camera Camera3D camera = { 0 }; - camera.position = (Vector3){ 30.0f, 20.0f, 30.0f }; - camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.fovy = 70.0f; - camera.projection = CAMERA_PERSPECTIVE; + camera.position = (Vector3){ 30.0f, 20.0f, 30.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 = 70.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type // Specify the amount of blocks in each direction const int numBlocks = 15; diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 702177f16..49dc1e7d4 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -50,7 +50,7 @@ int main(void) 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 mode type + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type // Load plane model from a generated mesh Model model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3)); @@ -80,12 +80,12 @@ int main(void) lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, shader); lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, shader); - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index c617a6d1f..eaeca5e51 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -42,11 +42,11 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; - camera.target = (Vector3){ 0.0f, 1.5f, 0.0f }; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.fovy = 45.0f; - camera.projection = CAMERA_PERSPECTIVE; + camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 1.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 Model model = LoadModel("resources/models/barracks.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/barracks_diffuse.png"); // Load model texture (diffuse map) @@ -67,7 +67,6 @@ int main(void) // Create a RenderTexture2D to be used for render to texture RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - DisableCursor(); // Catch cursor SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index c4b619edc..ddd721d46 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -45,11 +45,12 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog"); // Define the camera to look into our 3d world - Camera camera = { - (Vector3){ 2.0f, 2.0f, 6.0f }, // position - (Vector3){ 0.0f, 0.5f, 0.0f }, // target - (Vector3){ 0.0f, 1.0f, 0.0f }, // up - 45.0f, CAMERA_PERSPECTIVE }; // fov, type + 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)); @@ -84,12 +85,12 @@ int main(void) // Using just 1 point lights CreateLight(LIGHT_POINT, (Vector3){ 0, 2, 6 }, Vector3Zero(), WHITE, shader); - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_mesh_instancing.c b/examples/shaders/shaders_mesh_instancing.c index 0c2f9e1d9..02a7ee8f0 100644 --- a/examples/shaders/shaders_mesh_instancing.c +++ b/examples/shaders/shaders_mesh_instancing.c @@ -44,11 +44,11 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ -125.0f, 125.0f, -125.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; + camera.position = (Vector3){ -125.0f, 125.0f, -125.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 // Define mesh to be instanced Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); @@ -94,12 +94,12 @@ int main(void) Material matDefault = LoadMaterialDefault(); matDefault.maps[MATERIAL_MAP_DIFFUSE].color = BLUE; - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index 069d0ee25..e84ecbfbe 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -42,11 +42,11 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; - camera.target = (Vector3){ 0.0f, 1.0f, -1.0f }; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.fovy = 45.0f; - camera.projection = CAMERA_PERSPECTIVE; + camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 1.0f, -1.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 Model model = LoadModel("resources/models/watermill.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/watermill_diffuse.png"); // Load model texture @@ -60,12 +60,12 @@ int main(void) Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index 688ca9092..991e9839b 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -75,13 +75,18 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - postprocessing shader"); // Define the camera to look into our 3d world - Camera camera = { { 2.0f, 3.0f, 2.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = { 0 }; + camera.position = (Vector3){ 2.0f, 3.0f, 2.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 = 45.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type Model model = LoadModel("resources/models/church.obj"); // Load OBJ model Texture2D texture = LoadTexture("resources/models/church_diffuse.png"); // Load model texture (diffuse map) - model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set model diffuse texture + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set model diffuse texture - Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position // Load all postpro shaders // NOTE 1: All postpro shader use the base vertex shader (DEFAULT_VERTEX_SHADER) @@ -107,12 +112,12 @@ int main(void) // Create a RenderTexture2D to be used for render to texture RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index 5cc9af050..7b34a5233 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -40,6 +40,7 @@ int main(void) 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 @@ -56,12 +57,12 @@ int main(void) float runTime = 0.0f; - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_simple_mask.c b/examples/shaders/shaders_simple_mask.c index 49470294b..6283ccbcc 100644 --- a/examples/shaders/shaders_simple_mask.c +++ b/examples/shaders/shaders_simple_mask.c @@ -39,15 +39,15 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib - simple shader mask"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - simple shader mask"); // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 0.0f, 1.0f, 2.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; + camera.position = (Vector3){ 0.0f, 1.0f, 2.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 // Define our three models to show the shader on Mesh torus = GenMeshTorus(0.3f, 1, 16, 32); @@ -83,14 +83,14 @@ int main(void) model2.materials[0].shader = shader; int framesCounter = 0; - Vector3 rotation = { 0 }; // Model rotation angles + Vector3 rotation = { 0 }; // Model rotation angles - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set to run at 60 frames-per-second + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- @@ -116,9 +116,9 @@ int main(void) BeginMode3D(camera); - DrawModel(model1, (Vector3){0.5,0,0}, 1, WHITE); - DrawModelEx(model2, (Vector3){-.5,0,0}, (Vector3){1,1,0}, 50, (Vector3){1,1,1}, WHITE); - DrawModel(model3,(Vector3){0,0,-1.5}, 1, WHITE); + DrawModel(model1, (Vector3){ 0.5f, 0.0f, 0.0f }, 1, WHITE); + DrawModelEx(model2, (Vector3){ -0.5f, 0.0f, 0.0f }, (Vector3){ 1.0f, 1.0f, 0.0f }, 50, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE); + DrawModel(model3,(Vector3){ 0.0f, 0.0f, -1.5f }, 1, WHITE); DrawGrid(10, 1.0f); // Draw a grid EndMode3D(); diff --git a/examples/shaders/shaders_spotlight.c b/examples/shaders/shaders_spotlight.c index a21e3c82a..c96c983d0 100644 --- a/examples/shaders/shaders_spotlight.c +++ b/examples/shaders/shaders_spotlight.c @@ -29,10 +29,8 @@ ********************************************************************************************/ #include "raylib.h" -#include "raymath.h" -#include -#include +#include "raymath.h" #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 @@ -44,26 +42,26 @@ #define MAX_STARS 400 // Spot data -typedef struct { - Vector2 pos; - Vector2 vel; +typedef struct Spot { + Vector2 position; + Vector2 speed; float inner; float radius; // Shader locations - unsigned int posLoc; + unsigned int positionLoc; unsigned int innerLoc; unsigned int radiusLoc; } Spot; // Stars in the star field have a position and velocity typedef struct Star { - Vector2 pos; - Vector2 vel; + Vector2 position; + Vector2 speed; } Star; -void UpdateStar(Star *s); -void ResetStar(Star *s); +static void UpdateStar(Star *s); +static void ResetStar(Star *s); //------------------------------------------------------------------------------------ // Program main entry point @@ -75,7 +73,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib - shader spotlight"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shader spotlight"); HideCursor(); Texture texRay = LoadTexture("resources/raysan.png"); @@ -108,7 +106,7 @@ int main(void) innerName[6] = '0' + i; radiusName[6] = '0' + i; - spots[i].posLoc = GetShaderLocation(shdrSpot, posName); + spots[i].positionLoc = GetShaderLocation(shdrSpot, posName); spots[i].innerLoc = GetShaderLocation(shdrSpot, innerName); spots[i].radiusLoc = GetShaderLocation(shdrSpot, radiusName); @@ -124,20 +122,20 @@ int main(void) // and initialize the shader locations for (int i = 0; i < MAX_SPOTS; i++) { - spots[i].pos.x = (float)GetRandomValue(64, screenWidth - 64); - spots[i].pos.y = (float)GetRandomValue(64, screenHeight - 64); - spots[i].vel = (Vector2){ 0, 0 }; + 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].vel.x) + fabs(spots[i].vel.y)) < 2) + while ((fabs(spots[i].speed.x) + fabs(spots[i].speed.y)) < 2) { - spots[i].vel.x = GetRandomValue(-400, 40) / 10.0f; - spots[i].vel.y = GetRandomValue(-400, 40) / 10.0f; + 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].posLoc, &spots[i].pos.x, SHADER_UNIFORM_VEC2); + 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); } @@ -161,21 +159,21 @@ int main(void) if (i == 0) { Vector2 mp = GetMousePosition(); - spots[i].pos.x = mp.x; - spots[i].pos.y = screenHeight - mp.y; + spots[i].position.x = mp.x; + spots[i].position.y = screenHeight - mp.y; } else { - spots[i].pos.x += spots[i].vel.x; - spots[i].pos.y += spots[i].vel.y; + spots[i].position.x += spots[i].speed.x; + spots[i].position.y += spots[i].speed.y; - if (spots[i].pos.x < 64) spots[i].vel.x = -spots[i].vel.x; - if (spots[i].pos.x > (screenWidth - 64)) spots[i].vel.x = -spots[i].vel.x; - if (spots[i].pos.y < 64) spots[i].vel.y = -spots[i].vel.y; - if (spots[i].pos.y > (screenHeight - 64)) spots[i].vel.y = -spots[i].vel.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].posLoc, &spots[i].pos.x, SHADER_UNIFORM_VEC2); + SetShaderValue(shdrSpot, spots[i].positionLoc, &spots[i].position.x, SHADER_UNIFORM_VEC2); } // Draw @@ -188,7 +186,7 @@ int main(void) for (int n = 0; n < MAX_STARS; n++) { // Single pixel is just too small these days! - DrawRectangle((int)stars[n].pos.x, (int)stars[n].pos.y, 2, 2, WHITE); + DrawRectangle((int)stars[n].position.x, (int)stars[n].position.y, 2, 2, WHITE); } for (int i = 0; i < 16; i++) @@ -213,7 +211,6 @@ int main(void) DrawText("Pitch Black", (int)(screenWidth*0.2f), screenHeight/2, 20, GREEN); DrawText("Dark", (int)(screenWidth*.66f), screenHeight/2, 20, GREEN); - EndDrawing(); //---------------------------------------------------------------------------------- } @@ -230,26 +227,26 @@ int main(void) } -void ResetStar(Star *s) +static void ResetStar(Star *s) { - s->pos = (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }; + s->position = (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }; do { - s->vel.x = (float)GetRandomValue(-1000, 1000)/100.0f; - s->vel.y = (float)GetRandomValue(-1000, 1000)/100.0f; + s->speed.x = (float)GetRandomValue(-1000, 1000)/100.0f; + s->speed.y = (float)GetRandomValue(-1000, 1000)/100.0f; - } while (!(fabs(s->vel.x) + (fabs(s->vel.y) > 1))); + } while (!(fabs(s->speed.x) + (fabs(s->speed.y) > 1))); - s->pos = Vector2Add(s->pos, Vector2Multiply(s->vel, (Vector2){ 8.0f, 8.0f })); + s->position = Vector2Add(s->position, Vector2Multiply(s->speed, (Vector2){ 8.0f, 8.0f })); } -void UpdateStar(Star *s) +static void UpdateStar(Star *s) { - s->pos = Vector2Add(s->pos, s->vel); + s->position = Vector2Add(s->position, s->speed); - if ((s->pos.x < 0) || (s->pos.x > GetScreenWidth()) || - (s->pos.y < 0) || (s->pos.y > GetScreenHeight())) + if ((s->position.x < 0) || (s->position.x > GetScreenWidth()) || + (s->position.y < 0) || (s->position.y > GetScreenHeight())) { ResetStar(s); } diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index c90ea6658..097e4fc67 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -96,16 +96,13 @@ int main(void) 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 + 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 }; - DisableCursor(); // Catch cursor - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - // Use the default font Font font = GetFontDefault(); float fontSize = 8.0f; @@ -135,6 +132,10 @@ int main(void) // 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 diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 13d0df6de..5033ca124 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -265,6 +265,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_stream_effects", "exa EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_textured_curve", "examples\textures_textured_curve.vcxproj", "{769FF0C1-4424-4FA3-BC44-D7A7DA312A06}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_m3d", "examples\models_loading_m3d.vcxproj", "{6D9E00D8-2893-45E4-9363-3F7F61D416BD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -2225,6 +2227,22 @@ Global {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x64.Build.0 = Release|x64 {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x86.ActiveCfg = Release|Win32 {769FF0C1-4424-4FA3-BC44-D7A7DA312A06}.Release|x86.Build.0 = Release|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x64.ActiveCfg = Debug|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x64.Build.0 = Debug|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x86.ActiveCfg = Debug|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Debug|x86.Build.0 = Debug|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x64.ActiveCfg = Release|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x64.Build.0 = Release|x64 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x86.ActiveCfg = Release|Win32 + {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2359,6 +2377,7 @@ Global {1DE84812-E143-4C4B-A61D-9267AAD55401} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {4A87569C-4BD3-4113-B4B9-573D65B3D3F8} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {769FF0C1-4424-4FA3-BC44-D7A7DA312A06} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {6D9E00D8-2893-45E4-9363-3F7F61D416BD} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/src/raylib.h b/src/raylib.h index 650b5b29c..c1b85abdf 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1156,7 +1156,7 @@ RLAPI float GetGesturePinchAngle(void); // Get gesture pinch ang //------------------------------------------------------------------------------------ // Camera System Functions (Module: rcamera) //------------------------------------------------------------------------------------ -RLAPI void UpdateCamera(Camera3D *camera, int mode); // Update camera position for selected mode +RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode //------------------------------------------------------------------------------------ // Basic Shapes Drawing Functions (Module: shapes) diff --git a/src/rcamera.h b/src/rcamera.h index 60ac642a5..d1871cdf2 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -1,6 +1,6 @@ /******************************************************************************************* * -* rcamera - Basic camera system for multiple camera modes +* rcamera - Basic camera system with support for multiple camera modes * * CONFIGURATION: * @@ -15,13 +15,13 @@ * * CONTRIBUTORS: * Ramon Santamaria: Supervision, review, update and maintenance -* Christoph Wagner: Redesign (2022) +* Christoph Wagner: Complete redesign, using raymath (2022) * Marc Palau: Initial implementation (2014) * * * LICENSE: zlib/libpng * -* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2023 Christoph Wagner (@Crydsch) & 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. @@ -43,9 +43,6 @@ #ifndef RCAMERA_H #define RCAMERA_H -// The only dependency // TODO review standalone mode -#include "raymath.h" - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -86,6 +83,8 @@ int projection; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; + typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D + // Camera system modes typedef enum { CAMERA_CUSTOM = 0, @@ -115,19 +114,21 @@ extern "C" { // Prevents name mangling of functions #endif -Vector3 GetCameraForward(Camera3D* camera); -Vector3 GetCameraUp(Camera3D* camera); -Vector3 GetCameraRight(Camera3D* camera); -void CameraMoveForward(Camera3D* camera, float distance, bool moveInWorldPlane); -void CameraMoveUp(Camera3D* camera, float distance); -void CameraMoveRight(Camera3D* camera, float distance, bool moveInWorldPlane); -void CameraZoom(Camera3D* camera, float delta); -void CameraYaw(Camera3D* camera, float angle, bool rotateAroundTarget); -void CameraPitch(Camera3D* camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp); -void CameraRoll(Camera3D* camera, float angle); -void CameraViewBobbing(Camera3D* camera); -Matrix GetCameraViewMatrix(Camera3D* camera); -Matrix GetCameraProjectionMatrix(Camera3D* camera, float aspect); +Vector3 GetCameraForward(Camera *camera); +Vector3 GetCameraUp(Camera *camera); +Vector3 GetCameraRight(Camera *camera); + +void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane); +void CameraMoveUp(Camera *camera, float distance); +void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane); +void CameraMoveToTarget(Camera *camera, float delta); + +void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget); +void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp); +void CameraRoll(Camera *camera, float angle); + +Matrix GetCameraViewMatrix(Camera *camera); +Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); #if defined(__cplusplus) } @@ -144,10 +145,12 @@ Matrix GetCameraProjectionMatrix(Camera3D* camera, float aspect); #if defined(CAMERA_IMPLEMENTATION) + +#include "raymath.h" // Required for some vector maths + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- - #define CAMERA_MOVE_SPEED 0.09f #define CAMERA_ROTATION_SPEED 0.03f @@ -185,20 +188,20 @@ Matrix GetCameraProjectionMatrix(Camera3D* camera, float aspect); //---------------------------------------------------------------------------------- // Returns the cameras forward vector (normalized) -Vector3 GetCameraForward(Camera3D *camera) +Vector3 GetCameraForward(Camera *camera) { return Vector3Normalize(Vector3Subtract(camera->target, camera->position)); } // Returns the cameras up vector (normalized) // Note: The up vector might not be perpendicular to the forward vector -Vector3 GetCameraUp(Camera3D *camera) +Vector3 GetCameraUp(Camera *camera) { return Vector3Normalize(camera->up); } // Returns the cameras right vector (normalized) -Vector3 GetCameraRight(Camera3D *camera) +Vector3 GetCameraRight(Camera *camera) { Vector3 forward = GetCameraForward(camera); Vector3 up = GetCameraUp(camera); @@ -206,7 +209,7 @@ Vector3 GetCameraRight(Camera3D *camera) } // Moves the camera in its forward direction -void CameraMoveForward(Camera3D *camera, float distance, bool moveInWorldPlane) +void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane) { Vector3 forward = GetCameraForward(camera); @@ -226,7 +229,7 @@ void CameraMoveForward(Camera3D *camera, float distance, bool moveInWorldPlane) } // Moves the camera in its up direction -void CameraMoveUp(Camera3D *camera, float distance) +void CameraMoveUp(Camera *camera, float distance) { Vector3 up = GetCameraUp(camera); @@ -239,7 +242,7 @@ void CameraMoveUp(Camera3D *camera, float distance) } // Moves the camera target in its current right direction -void CameraMoveRight(Camera3D *camera, float distance, bool moveInWorldPlane) +void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane) { Vector3 right = GetCameraRight(camera); @@ -259,7 +262,7 @@ void CameraMoveRight(Camera3D *camera, float distance, bool moveInWorldPlane) } // Moves the camera position closer/farther to/from the camera target -void CameraZoom(Camera3D *camera, float delta) +void CameraMoveToTarget(Camera *camera, float delta) { float distance = Vector3Distance(camera->position, camera->target); @@ -278,7 +281,7 @@ void CameraZoom(Camera3D *camera, float delta) // Yaw is "looking left and right" // If rotateAroundTarget is false, the camera rotates around its position // Note: angle must be provided in radians -void CameraYaw(Camera3D *camera, float angle, bool rotateAroundTarget) +void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget) { // Rotation axis Vector3 up = GetCameraUp(camera); @@ -307,7 +310,7 @@ void CameraYaw(Camera3D *camera, float angle, bool rotateAroundTarget) // If rotateAroundTarget is false, the camera rotates around its position // rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE) // Note: angle must be provided in radians -void CameraPitch(Camera3D *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp) +void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp) { // Up direction Vector3 up = GetCameraUp(camera); @@ -359,7 +362,7 @@ void CameraPitch(Camera3D *camera, float angle, bool lockView, bool rotateAround // Rotates the camera around its forward vector // Roll is "turning your head sideways to the left or right" // Note: angle must be provided in radians -void CameraRoll(Camera3D *camera, float angle) +void CameraRoll(Camera *camera, float angle) { // Rotation axis Vector3 forward = GetCameraForward(camera); @@ -368,30 +371,14 @@ void CameraRoll(Camera3D *camera, float angle) camera->up = Vector3RotateByAxisAngle(camera->up, forward, angle); } -// Moves camera slightly to simulate a walking motion -// Note: Only active if camera->swingCounter > 0 -void CameraViewBobbing(Camera3D *camera) -{ - if (camera->swingCounter > 0) - { - // NOTE: We delay the target movement relative to the position movement to create a little pitch with each step. - camera->position.y = camera->position.y - 0.25f * sinf((camera->swingCounter + 1) / CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / CAMERA_FIRST_PERSON_STEP_DIVIDER; - camera->target.y = camera->target.y - 0.25f * sinf((camera->swingCounter - 1) / CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER) / CAMERA_FIRST_PERSON_STEP_DIVIDER; - - // Update counter for next frame - camera->swingCounter %= 2147483647 /* INT_MAX */; // Counter must be positive - camera->swingCounter++; - } -} - // Returns the camera view matrix -Matrix GetCameraViewMatrix(Camera3D *camera) +Matrix GetCameraViewMatrix(Camera *camera) { return MatrixLookAt(camera->position, camera->target, camera->up); } // Returns the camera projection matrix -Matrix GetCameraProjectionMatrix(Camera3D *camera, float aspect) +Matrix GetCameraProjectionMatrix(Camera *camera, float aspect) { if (camera->projection == CAMERA_PERSPECTIVE) { @@ -407,11 +394,10 @@ Matrix GetCameraProjectionMatrix(Camera3D *camera, float aspect) return MatrixIdentity(); } - #ifndef CAMERA_STANDALONE // Update camera position for selected mode // Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM -void UpdateCamera(Camera3D *camera, int mode) +void UpdateCamera(Camera *camera, int mode) { Vector2 mousePositionDelta = GetMouseDelta(); @@ -422,11 +408,11 @@ void UpdateCamera(Camera3D *camera, int mode) // Camera movement if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); - if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); + //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); // Camera rotation if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); @@ -436,17 +422,13 @@ void UpdateCamera(Camera3D *camera, int mode) if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); - CameraYaw(camera, mousePositionDelta.x * -CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, mousePositionDelta.y * -CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); // Zoom target distance - CameraZoom(camera, -GetMouseWheelMove()); - if (IsKeyPressed(KEY_KP_SUBTRACT)) CameraZoom(camera, 2.0f); - if (IsKeyPressed(KEY_KP_ADD)) CameraZoom(camera, -2.0f); - - - // Apply view bobbing when moving around (per default only active in CAMERA_FIRST_PERSON) - if (mode == CAMERA_FIRST_PERSON && (IsKeyDown(KEY_W) || IsKeyDown(KEY_A) || IsKeyDown(KEY_S) || IsKeyDown(KEY_D))) CameraViewBobbing(camera); + CameraMoveToTarget(camera, -GetMouseWheelMove()); + if (IsKeyPressed(KEY_KP_SUBTRACT)) CameraMoveToTarget(camera, 2.0f); + if (IsKeyPressed(KEY_KP_ADD)) CameraMoveToTarget(camera, -2.0f); } #endif // !CAMERA_STANDALONE diff --git a/src/rcamera_old.h b/src/rcamera.old.h similarity index 100% rename from src/rcamera_old.h rename to src/rcamera.old.h diff --git a/src/rcore.c b/src/rcore.c index 83a70632e..f81d23186 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -925,6 +925,8 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.currentPosition.x = (float)CORE.Window.screen.width/2.0f; CORE.Input.Mouse.currentPosition.y = (float)CORE.Window.screen.height/2.0f; + + SetMousePosition((int)CORE.Input.Mouse.currentPosition.x, (int)CORE.Input.Mouse.currentPosition.x); #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent)); @@ -2024,6 +2026,13 @@ void DisableCursor(void) { #if defined(PLATFORM_DESKTOP) glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // Set cursor position in the middle of screen and update delta accordingly + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/2; + CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.width/2; + CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x; + CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.currentPosition.y; #endif #if defined(PLATFORM_WEB) emscripten_request_pointerlock("#canvas", 1); @@ -2189,7 +2198,7 @@ void EndMode2D(void) } // Initializes 3D mode with custom camera (3D) -void BeginMode3D(Camera3D camera) +void BeginMode3D(Camera camera) { rlDrawRenderBatchActive(); // Update and draw internal render batch @@ -3788,7 +3797,7 @@ Vector2 GetMousePosition(void) // Get mouse delta between frames Vector2 GetMouseDelta(void) { - Vector2 delta = {0}; + 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; From 50d17393a5a3ece1e4290c86655c65b70ff390a4 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Feb 2023 20:00:56 +0100 Subject: [PATCH 0270/1710] Create models_loading_m3d.vcxproj --- .../examples/models_loading_m3d.vcxproj | 387 ++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 projects/VS2022/examples/models_loading_m3d.vcxproj diff --git a/projects/VS2022/examples/models_loading_m3d.vcxproj b/projects/VS2022/examples/models_loading_m3d.vcxproj new file mode 100644 index 000000000..f08b1ebb2 --- /dev/null +++ b/projects/VS2022/examples/models_loading_m3d.vcxproj @@ -0,0 +1,387 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {6D9E00D8-2893-45E4-9363-3F7F61D416BD} + Win32Proj + models_loading_m3d + 10.0 + models_loading_m3d + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file From 547819766faca80482371902acd908c0c4c3f7e0 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Feb 2023 20:12:53 +0100 Subject: [PATCH 0271/1710] ADDED: Example project to VS2022 solution --- examples/shaders/shaders_write_depth.c | 14 +- .../examples/shaders_write_depth.vcxproj | 387 ++++++++++++++++++ projects/VS2022/raylib.sln | 19 + 3 files changed, 413 insertions(+), 7 deletions(-) create mode 100644 projects/VS2022/examples/shaders_write_depth.vcxproj diff --git a/examples/shaders/shaders_write_depth.c b/examples/shaders/shaders_write_depth.c index 317f6ac75..b7f69ddbf 100644 --- a/examples/shaders/shaders_write_depth.c +++ b/examples/shaders/shaders_write_depth.c @@ -14,6 +14,7 @@ ********************************************************************************************/ #include "raylib.h" + #include "rlgl.h" #if defined(PLATFORM_DESKTOP) @@ -27,10 +28,10 @@ //------------------------------------------------------------------------------------ // 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 //------------------------------------------------------------------------------------ @@ -55,20 +56,19 @@ int main(void) .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 mode type + .projection = CAMERA_PERSPECTIVE // Camera projection type }; - SetCameraMode(camera, CAMERA_ORBITAL); - - SetTargetFPS(60); + 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 + while (!WindowShouldClose()) // Detect window close button or ESC key { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); //---------------------------------------------------------------------------------- // Draw diff --git a/projects/VS2022/examples/shaders_write_depth.vcxproj b/projects/VS2022/examples/shaders_write_depth.vcxproj new file mode 100644 index 000000000..18b1d8797 --- /dev/null +++ b/projects/VS2022/examples/shaders_write_depth.vcxproj @@ -0,0 +1,387 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {70B35F59-AFC2-4D8F-8833-5314D2047A81} + Win32Proj + shaders_write_depth + 10.0 + shaders_write_depth + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 5033ca124..153628063 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -267,6 +267,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_textured_curve", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_m3d", "examples\models_loading_m3d.vcxproj", "{6D9E00D8-2893-45E4-9363-3F7F61D416BD}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_write_depth", "examples\shaders_write_depth.vcxproj", "{70B35F59-AFC2-4D8F-8833-5314D2047A81}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -2243,6 +2245,22 @@ Global {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x64.Build.0 = Release|x64 {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x86.ActiveCfg = Release|Win32 {6D9E00D8-2893-45E4-9363-3F7F61D416BD}.Release|x86.Build.0 = Release|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x64.ActiveCfg = Debug|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x64.Build.0 = Debug|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x86.ActiveCfg = Debug|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Debug|x86.Build.0 = Debug|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x64.ActiveCfg = Release|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x64.Build.0 = Release|x64 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x86.ActiveCfg = Release|Win32 + {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2378,6 +2396,7 @@ Global {4A87569C-4BD3-4113-B4B9-573D65B3D3F8} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {769FF0C1-4424-4FA3-BC44-D7A7DA312A06} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {6D9E00D8-2893-45E4-9363-3F7F61D416BD} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} + {70B35F59-AFC2-4D8F-8833-5314D2047A81} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From e843be7ea53a9ec3b460ccecb9587f26721e4b96 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Feb 2023 20:13:13 +0100 Subject: [PATCH 0272/1710] ADDED: rcamera dependencies details --- src/rcamera.h | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index d1871cdf2..7770071e8 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -48,11 +48,11 @@ //---------------------------------------------------------------------------------- //... #if defined(CAMERA_STANDALONE) -#define CAMERA_CULL_DISTANCE_NEAR 0.01 -#define CAMERA_CULL_DISTANCE_FAR 1000.0 +#define CAMERA_CULL_DISTANCE_NEAR 0.01 +#define CAMERA_CULL_DISTANCE_FAR 1000.0 #else -#define CAMERA_CULL_DISTANCE_NEAR RL_CULL_DISTANCE_NEAR -#define CAMERA_CULL_DISTANCE_FAR RL_CULL_DISTANCE_FAR +#define CAMERA_CULL_DISTANCE_NEAR RL_CULL_DISTANCE_NEAR +#define CAMERA_CULL_DISTANCE_FAR RL_CULL_DISTANCE_FAR #endif //---------------------------------------------------------------------------------- @@ -145,8 +145,22 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); #if defined(CAMERA_IMPLEMENTATION) +#include "raymath.h" // Required for vector maths: + // Vector3Add() + // Vector3Subtract() + // Vector3Scale() + // Vector3Normalize() + // Vector3Distance() + // Vector3CrossProduct() + // Vector3RotateByAxisAngle() + // Vector3Angle() + // Vector3Negate() + // MatrixLookAt() + // MatrixPerspective() + // MatrixOrtho() + // MatrixIdentity() -#include "raymath.h" // Required for some vector maths +// raylib input functionality required: GetMouseDelta(), GetMouseWheelMove(), IsKeyDown(), IsKeyPressed() //---------------------------------------------------------------------------------- // Defines and Macros @@ -171,12 +185,12 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- - +//... //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- - +//... //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -186,7 +200,6 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- - // Returns the cameras forward vector (normalized) Vector3 GetCameraForward(Camera *camera) { From 1cfb48410013399348c56be39135dbc922ccd792 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Feb 2023 20:31:42 +0100 Subject: [PATCH 0273/1710] ADDED: Example project to VS --- examples/shaders/shaders_hybrid_render.c | 55 ++- .../examples/shaders_hybrid_render.vcxproj | 387 ++++++++++++++++++ projects/VS2022/raylib.sln | 19 + 3 files changed, 432 insertions(+), 29 deletions(-) create mode 100644 projects/VS2022/examples/shaders_hybrid_render.vcxproj diff --git a/examples/shaders/shaders_hybrid_render.c b/examples/shaders/shaders_hybrid_render.c index 86ba6397b..0f30680c8 100644 --- a/examples/shaders/shaders_hybrid_render.c +++ b/examples/shaders/shaders_hybrid_render.c @@ -52,24 +52,24 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - write depth buffer"); - // This Shader calculates pixel depth and color using raymarch. - Shader raymarch_shader = 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 raster_shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raster.fs", GLSL_VERSION)); + // 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 march_locs = {0}; + RayLocs marchLocs = {0}; // Fill the struct with shader locs. - march_locs.camPos = GetShaderLocation(raymarch_shader, "camPos"); - march_locs.camDir = GetShaderLocation(raymarch_shader, "camDir"); - march_locs.screenCenter = GetShaderLocation(raymarch_shader, "screenCenter"); - - { // Transfer screenCenter position to shader. Which is used to calculate ray direction. - Vector2 screenCenter = {.x = screenWidth/2.0, .y = screenHeight/2.0}; - SetShaderValue(raymarch_shader, march_locs.screenCenter , &screenCenter , SHADER_UNIFORM_VEC2); - } + 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.0, .y = screenHeight/2.0}; + SetShaderValue(shdrRaymarch, marchLocs.screenCenter , &screenCenter , SHADER_UNIFORM_VEC2); // Use Customized function to create writable depth texture buffer RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight); @@ -80,15 +80,14 @@ int main(void) .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 mode type + .projection = CAMERA_PERSPECTIVE // Camera projection type }; // Camera FOV is pre-calculated in the camera Distance. double camDist = 1.0/(tan(camera.fovy*0.5*DEG2RAD)); - - SetCameraMode(camera, CAMERA_FIRST_PERSON); - SetTargetFPS(60); + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -96,33 +95,31 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); - //Update Camera Postion in the ray march shader. - SetShaderValue(raymarch_shader, march_locs.camPos, &(camera.position), RL_SHADER_UNIFORM_VEC3); + // 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(raymarch_shader, march_locs.camDir, &(camDir), 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(raymarch_shader); + BeginShaderMode(shdrRaymarch); DrawRectangleRec((Rectangle){0,0,screenWidth,screenHeight},WHITE); EndShaderMode(); // Raserize Scene BeginMode3D(camera); - BeginShaderMode(raster_shader); + 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); @@ -145,8 +142,8 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- UnloadRenderTextureDepthTex(target); - UnloadShader(raymarch_shader); - UnloadShader(raster_shader); + UnloadShader(shdrRaymarch); + UnloadShader(shdrRaster); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/projects/VS2022/examples/shaders_hybrid_render.vcxproj b/projects/VS2022/examples/shaders_hybrid_render.vcxproj new file mode 100644 index 000000000..ba8cdc58b --- /dev/null +++ b/projects/VS2022/examples/shaders_hybrid_render.vcxproj @@ -0,0 +1,387 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} + Win32Proj + shaders_hybrid_render + 10.0 + shaders_hybrid_render + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shaders + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 153628063..06166cdd0 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -269,6 +269,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_m3d", "examp EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_write_depth", "examples\shaders_write_depth.vcxproj", "{70B35F59-AFC2-4D8F-8833-5314D2047A81}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hybrid_render", "examples\shaders_hybrid_render.vcxproj", "{3755E9F4-CB48-4EC3-B561-3B85964EBDEF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -2261,6 +2263,22 @@ Global {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x64.Build.0 = Release|x64 {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x86.ActiveCfg = Release|Win32 {70B35F59-AFC2-4D8F-8833-5314D2047A81}.Release|x86.Build.0 = Release|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x64.ActiveCfg = Debug|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x64.Build.0 = Debug|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x86.ActiveCfg = Debug|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Debug|x86.Build.0 = Debug|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x64.ActiveCfg = Release|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x64.Build.0 = Release|x64 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.ActiveCfg = Release|Win32 + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2397,6 +2415,7 @@ Global {769FF0C1-4424-4FA3-BC44-D7A7DA312A06} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {6D9E00D8-2893-45E4-9363-3F7F61D416BD} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {70B35F59-AFC2-4D8F-8833-5314D2047A81} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From ec95aa2e04e5c4df49dbe25a64e9239db66d0fc2 Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Wed, 15 Feb 2023 02:29:32 +0500 Subject: [PATCH 0274/1710] Move camera after rotation (#2923) Moving camera after rotation is set to avoid 1 frame delay in movement direction --- src/rcamera.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 7770071e8..8184e625c 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -418,14 +418,6 @@ void UpdateCamera(Camera *camera, int mode) bool rotateAroundTarget = mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; bool lockView = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; bool rotateUp = mode == CAMERA_FREE; - - // Camera movement - if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); - //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); // Camera rotation if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); @@ -438,6 +430,14 @@ void UpdateCamera(Camera *camera, int mode) CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + // Camera movement + if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); + //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + // Zoom target distance CameraMoveToTarget(camera, -GetMouseWheelMove()); if (IsKeyPressed(KEY_KP_SUBTRACT)) CameraMoveToTarget(camera, 2.0f); From 5dedb27ce01fc649b4b81b582418b31f4435da98 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Feb 2023 22:51:34 +0100 Subject: [PATCH 0275/1710] REVIEWED: Issue with camera jump on first frame --- src/rcore.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index f81d23186..d47aaab07 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2017,6 +2017,8 @@ void EnableCursor(void) #if defined(PLATFORM_WEB) emscripten_exit_pointerlock(); #endif + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); CORE.Input.Mouse.cursorHidden = false; } @@ -2026,17 +2028,12 @@ void DisableCursor(void) { #if defined(PLATFORM_DESKTOP) glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - - // Set cursor position in the middle of screen and update delta accordingly - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); - CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/2; - CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.width/2; - CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x; - CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.currentPosition.y; #endif #if defined(PLATFORM_WEB) emscripten_request_pointerlock("#canvas", 1); #endif + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); CORE.Input.Mouse.cursorHidden = true; } @@ -3809,6 +3806,8 @@ Vector2 GetMouseDelta(void) void SetMousePosition(int x, int y) { CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // NOTE: emscripten not implemented glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); From 903d85e3233c9a8b1908bc2b9322e1251d100bbe Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Feb 2023 12:53:41 +0100 Subject: [PATCH 0276/1710] Make sure window position always inits in the middle of the current monitor --- src/rcore.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index d47aaab07..0c4691eba 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -849,6 +849,7 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); return; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); @@ -923,10 +924,7 @@ void InitWindow(int width, int height, const char *title) emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); #endif - CORE.Input.Mouse.currentPosition.x = (float)CORE.Window.screen.width/2.0f; - CORE.Input.Mouse.currentPosition.y = (float)CORE.Window.screen.height/2.0f; - - SetMousePosition((int)CORE.Input.Mouse.currentPosition.x, (int)CORE.Input.Mouse.currentPosition.x); + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent)); From beb44f1dacce8aef0df3740e341582ca79e040e1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Feb 2023 13:11:26 +0100 Subject: [PATCH 0277/1710] Avoid centering mouse when program launches --- src/rcore.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 0c4691eba..3de0f3f3c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -924,8 +924,6 @@ void InitWindow(int width, int height, const char *title) emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); #endif - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); - #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; From 9eaed07b77bd6c9004175c9a641a573671cadb29 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 15 Feb 2023 08:17:00 -0800 Subject: [PATCH 0278/1710] Make the oribital camera work like it used to (it is not just a copy of third person) (#2926) --- examples/models/models_billboard.c | 2 - examples/models/models_cubicmap.c | 2 - examples/models/models_heightmap.c | 2 - examples/models/models_loading_vox.c | 2 - examples/models/models_mesh_generation.c | 2 - examples/models/models_rlgl_solar_system.c | 2 - examples/shaders/shaders_basic_lighting.c | 1 - examples/shaders/shaders_fog.c | 1 - examples/shaders/shaders_hybrid_render.c | 1 - examples/shaders/shaders_mesh_instancing.c | 1 - examples/shaders/shaders_postprocessing.c | 1 - examples/shaders/shaders_write_depth.c | 1 - src/rcamera.h | 62 +++++++++++++--------- 13 files changed, 38 insertions(+), 42 deletions(-) diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 596a09d3b..9b02a8af1 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -55,8 +55,6 @@ int main(void) float distanceRotating; float rotation = 0.0f; - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_cubicmap.c b/examples/models/models_cubicmap.c index 714918e41..e0a2b3089 100644 --- a/examples/models/models_cubicmap.c +++ b/examples/models/models_cubicmap.c @@ -47,8 +47,6 @@ int main(void) UnloadImage(image); // Unload cubesmap image from RAM, already uploaded to VRAM - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_heightmap.c b/examples/models/models_heightmap.c index 8f32de9fe..e1c3e4fb6 100644 --- a/examples/models/models_heightmap.c +++ b/examples/models/models_heightmap.c @@ -44,8 +44,6 @@ int main(void) UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c index d17fd6ee6..e148fd91a 100644 --- a/examples/models/models_loading_vox.c +++ b/examples/models/models_loading_vox.c @@ -69,8 +69,6 @@ int main(void) int currentModel = 0; - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index 6189fb46d..d17a20a12 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -68,8 +68,6 @@ int main(void) int currentModel = 0; - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c index fc2a1f020..e458036fe 100644 --- a/examples/models/models_rlgl_solar_system.c +++ b/examples/models/models_rlgl_solar_system.c @@ -56,8 +56,6 @@ int main(void) float moonRotation = 0.0f; // Rotation of moon around itself float moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 49dc1e7d4..61cec2e80 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -80,7 +80,6 @@ int main(void) lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, shader); lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, shader); - DisableCursor(); // Limit cursor to relative movement inside the window SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index ddd721d46..24a1c1068 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -85,7 +85,6 @@ int main(void) // Using just 1 point lights CreateLight(LIGHT_POINT, (Vector3){ 0, 2, 6 }, Vector3Zero(), WHITE, shader); - DisableCursor(); // Limit cursor to relative movement inside the window SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_hybrid_render.c b/examples/shaders/shaders_hybrid_render.c index 0f30680c8..f07917fb0 100644 --- a/examples/shaders/shaders_hybrid_render.c +++ b/examples/shaders/shaders_hybrid_render.c @@ -86,7 +86,6 @@ int main(void) // Camera FOV is pre-calculated in the camera Distance. double camDist = 1.0/(tan(camera.fovy*0.5*DEG2RAD)); - DisableCursor(); // Limit cursor to relative movement inside the window SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_mesh_instancing.c b/examples/shaders/shaders_mesh_instancing.c index 02a7ee8f0..7789f2cb0 100644 --- a/examples/shaders/shaders_mesh_instancing.c +++ b/examples/shaders/shaders_mesh_instancing.c @@ -94,7 +94,6 @@ int main(void) Material matDefault = LoadMaterialDefault(); matDefault.maps[MATERIAL_MAP_DIFFUSE].color = BLUE; - DisableCursor(); // Limit cursor to relative movement inside the window SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index 991e9839b..7f6bd0092 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -112,7 +112,6 @@ int main(void) // Create a RenderTexture2D to be used for render to texture RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - DisableCursor(); // Limit cursor to relative movement inside the window SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_write_depth.c b/examples/shaders/shaders_write_depth.c index b7f69ddbf..048e297ad 100644 --- a/examples/shaders/shaders_write_depth.c +++ b/examples/shaders/shaders_write_depth.c @@ -59,7 +59,6 @@ int main(void) .projection = CAMERA_PERSPECTIVE // Camera projection type }; - DisableCursor(); // Limit cursor to relative movement inside the window SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/src/rcamera.h b/src/rcamera.h index 8184e625c..c0c28209d 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -172,7 +172,7 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); #define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate #define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f -#define CAMERA_ORBITAL_SPEED 0.01f // Radians per frame +#define CAMERA_ORBITAL_SPEED 0.5f // Radians per second #define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 8.0f @@ -418,30 +418,44 @@ void UpdateCamera(Camera *camera, int mode) bool rotateAroundTarget = mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; bool lockView = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; bool rotateUp = mode == CAMERA_FREE; - - // Camera rotation - if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); - if (IsKeyDown(KEY_UP)) CameraPitch(camera, CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); - if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -CAMERA_ROTATION_SPEED, rotateAroundTarget); - if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, CAMERA_ROTATION_SPEED, rotateAroundTarget); - if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); - if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); - - CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); - - // Camera movement - if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); - //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); - // Zoom target distance - CameraMoveToTarget(camera, -GetMouseWheelMove()); - if (IsKeyPressed(KEY_KP_SUBTRACT)) CameraMoveToTarget(camera, 2.0f); - if (IsKeyPressed(KEY_KP_ADD)) CameraMoveToTarget(camera, -2.0f); + if (mode == CAMERA_ORBITAL) + { + // Obital can just orbit + Matrix rotatation = MatrixRotate(GetCameraUp(camera), CAMERA_ORBITAL_SPEED * GetFrameTime()); + Vector3 viewVector = Vector3Subtract(camera->position, camera->target); + viewVector = Vector3Transform(viewVector, rotatation); + camera->position = Vector3Add(camera->target, viewVector); + } + else + { + // Camera rotation + if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); + if (IsKeyDown(KEY_UP)) CameraPitch(camera, CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); + if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -CAMERA_ROTATION_SPEED, rotateAroundTarget); + if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, CAMERA_ROTATION_SPEED, rotateAroundTarget); + if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); + if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); + + CameraYaw(camera, -mousePositionDelta.x * CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -mousePositionDelta.y * CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + + // Camera movement + if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); + //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + } + + if (mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL) + { + // Zoom target distance + CameraMoveToTarget(camera, -GetMouseWheelMove()); + if (IsKeyPressed(KEY_KP_SUBTRACT)) CameraMoveToTarget(camera, 2.0f); + if (IsKeyPressed(KEY_KP_ADD)) CameraMoveToTarget(camera, -2.0f); + } } #endif // !CAMERA_STANDALONE From 2766835ed432a2ad575635ce338aa1389078cb12 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Feb 2023 17:36:31 +0100 Subject: [PATCH 0279/1710] REPLACE: TABS by 4 spaces --- examples/textures/textures_textured_curve.c | 216 ++++++++++---------- parser/raylib_parser.c | 4 +- src/rcamera.h | 39 ++-- 3 files changed, 130 insertions(+), 129 deletions(-) diff --git a/examples/textures/textures_textured_curve.c b/examples/textures/textures_textured_curve.c index 201b01fec..7945d8e32 100644 --- a/examples/textures/textures_textured_curve.c +++ b/examples/textures/textures_textured_curve.c @@ -59,19 +59,19 @@ int main() const int screenWidth = 800; const int screenHeight = 450; - SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "raylib [textures] examples - textured curve"); + SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT); + InitWindow(screenWidth, screenHeight, "raylib [textures] examples - textured curve"); - // Load the road texture - texRoad = LoadTexture("resources/road.png"); + // Load the road texture + texRoad = LoadTexture("resources/road.png"); SetTextureFilter(texRoad, TEXTURE_FILTER_BILINEAR); - // Setup the curve - curveStartPosition = (Vector2){ 80, 100 }; - curveStartPositionTangent = (Vector2){ 100, 300 }; + // Setup the curve + curveStartPosition = (Vector2){ 80, 100 }; + curveStartPositionTangent = (Vector2){ 100, 300 }; - curveEndPosition = (Vector2){ 700, 350 }; - curveEndPositionTangent = (Vector2){ 600, 100 }; + curveEndPosition = (Vector2){ 700, 350 }; + curveEndPositionTangent = (Vector2){ 600, 100 }; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -81,8 +81,8 @@ int main() { // Update //---------------------------------------------------------------------------------- - UpdateCurve(); - UpdateOptions(); + UpdateCurve(); + UpdateOptions(); //---------------------------------------------------------------------------------- @@ -99,13 +99,13 @@ int main() 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(); + EndDrawing(); //---------------------------------------------------------------------------------- } // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texRoad); + UnloadTexture(texRoad); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- @@ -118,142 +118,142 @@ int main() //---------------------------------------------------------------------------------- static void DrawCurve(void) { - if (showCurve) DrawLineBezierCubic(curveStartPosition, curveEndPosition, curveStartPositionTangent, curveEndPositionTangent, 2, BLUE); + if (showCurve) DrawLineBezierCubic(curveStartPosition, curveEndPosition, curveStartPositionTangent, curveEndPositionTangent, 2, BLUE); - // Draw the various control points and highlight where the mouse is - DrawLineV(curveStartPosition, curveStartPositionTangent, SKYBLUE); - DrawLineV(curveEndPosition, curveEndPositionTangent, PURPLE); - Vector2 mouse = GetMousePosition(); + // Draw the various control points and highlight where the mouse is + DrawLineV(curveStartPosition, curveStartPositionTangent, SKYBLUE); + DrawLineV(curveEndPosition, curveEndPositionTangent, PURPLE); + Vector2 mouse = GetMousePosition(); - if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) DrawCircleV(curveStartPosition, 7, YELLOW); - DrawCircleV(curveStartPosition, 5, RED); + if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) DrawCircleV(curveStartPosition, 7, YELLOW); + DrawCircleV(curveStartPosition, 5, RED); - if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) DrawCircleV(curveStartPositionTangent, 7, YELLOW); - DrawCircleV(curveStartPositionTangent, 5, MAROON); + if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) DrawCircleV(curveStartPositionTangent, 7, YELLOW); + DrawCircleV(curveStartPositionTangent, 5, MAROON); - if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) DrawCircleV(curveEndPosition, 7, YELLOW); - DrawCircleV(curveEndPosition, 5, GREEN); + if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) DrawCircleV(curveEndPosition, 7, YELLOW); + DrawCircleV(curveEndPosition, 5, GREEN); - if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) DrawCircleV(curveEndPositionTangent, 7, YELLOW); - DrawCircleV(curveEndPositionTangent, 5, DARKGREEN); + if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) DrawCircleV(curveEndPositionTangent, 7, YELLOW); + DrawCircleV(curveEndPositionTangent, 5, DARKGREEN); } static void UpdateCurve(void) { - // If the mouse is not down, we are not editing the curve so clear the selection - if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON)) - { - curveSelectedPoint = NULL; - return; - } + // If the mouse is not down, we are not editing the curve so clear the selection + if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + curveSelectedPoint = NULL; + return; + } - // If a point was selected, move it - if (curveSelectedPoint) - { - *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta()); - return; - } + // If a point was selected, move it + if (curveSelectedPoint) + { + *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta()); + return; + } - // The mouse is down, and nothing was selected, so see if anything was picked - Vector2 mouse = GetMousePosition(); + // The mouse is down, and nothing was selected, so see if anything was picked + Vector2 mouse = GetMousePosition(); - if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) curveSelectedPoint = &curveStartPosition; - else if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) curveSelectedPoint = &curveStartPositionTangent; - else if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) curveSelectedPoint = &curveEndPosition; - else if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) curveSelectedPoint = &curveEndPositionTangent; + if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) curveSelectedPoint = &curveStartPosition; + else if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) curveSelectedPoint = &curveStartPositionTangent; + else if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) curveSelectedPoint = &curveEndPosition; + else if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) curveSelectedPoint = &curveEndPositionTangent; } static void DrawTexturedCurve(void) { - const float step = 1.0f/curveSegments; + const float step = 1.0f/curveSegments; - Vector2 previous = curveStartPosition; - Vector2 previousTangent = { 0 }; - float previousV = 0; + Vector2 previous = curveStartPosition; + Vector2 previousTangent = { 0 }; + float previousV = 0; - // We can't compute a tangent for the first point, so we need to reuse the tangent from the first segment - bool tangentSet = false; + // We can't compute a tangent for the first point, so we need to reuse the tangent from the first segment + bool tangentSet = false; - Vector2 current = { 0 }; - float t = 0.0f; + Vector2 current = { 0 }; + float t = 0.0f; - for (int i = 1; i <= curveSegments; i++) - { - // Segment the curve - t = step*i; - float a = powf(1 - t, 3); - float b = 3*powf(1 - t, 2)*t; - float c = 3*(1 - t)*powf(t, 2); - float d = powf(t, 3); + for (int i = 1; i <= curveSegments; i++) + { + // Segment the curve + t = step*i; + float a = powf(1 - t, 3); + float b = 3*powf(1 - t, 2)*t; + float c = 3*(1 - t)*powf(t, 2); + float d = powf(t, 3); - // Compute the endpoint for this segment - current.y = a*curveStartPosition.y + b*curveStartPositionTangent.y + c*curveEndPositionTangent.y + d*curveEndPosition.y; - current.x = a*curveStartPosition.x + b*curveStartPositionTangent.x + c*curveEndPositionTangent.x + d*curveEndPosition.x; + // Compute the endpoint for this segment + current.y = a*curveStartPosition.y + b*curveStartPositionTangent.y + c*curveEndPositionTangent.y + d*curveEndPosition.y; + current.x = a*curveStartPosition.x + b*curveStartPositionTangent.x + c*curveEndPositionTangent.x + d*curveEndPosition.x; - // Vector from previous to current - Vector2 delta = { current.x - previous.x, current.y - previous.y }; + // Vector from previous to current + Vector2 delta = { current.x - previous.x, current.y - previous.y }; - // The right hand normal to the delta vector - Vector2 normal = Vector2Normalize((Vector2){ -delta.y, delta.x }); + // The right hand normal to the delta vector + 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); + // The v texture coordinate of the segment (add up the length of all the segments so far) + float v = previousV + Vector2Length(delta); - // Make sure the start point has a normal - if (!tangentSet) - { - previousTangent = normal; - tangentSet = true; - } + // Make sure the start point has a normal + if (!tangentSet) + { + previousTangent = normal; + tangentSet = true; + } - // Extend out the normals from the previous and current points to get the quad for this segment - Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, curveWidth)); - Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -curveWidth)); + // Extend out the normals from the previous and current points to get the quad for this segment + Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, curveWidth)); + Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -curveWidth)); - Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, curveWidth)); - Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -curveWidth)); + Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, curveWidth)); + Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -curveWidth)); - // Draw the segment as a quad - rlSetTexture(texRoad.id); - rlBegin(RL_QUADS); + // Draw the segment as a quad + rlSetTexture(texRoad.id); + rlBegin(RL_QUADS); - rlColor4ub(255,255,255,255); - rlNormal3f(0.0f, 0.0f, 1.0f); + rlColor4ub(255,255,255,255); + rlNormal3f(0.0f, 0.0f, 1.0f); - rlTexCoord2f(0, previousV); - rlVertex2f(prevNegNormal.x, prevNegNormal.y); + rlTexCoord2f(0, previousV); + rlVertex2f(prevNegNormal.x, prevNegNormal.y); - rlTexCoord2f(1, previousV); - rlVertex2f(prevPosNormal.x, prevPosNormal.y); + rlTexCoord2f(1, previousV); + rlVertex2f(prevPosNormal.x, prevPosNormal.y); - rlTexCoord2f(1, v); - rlVertex2f(currentPosNormal.x, currentPosNormal.y); + rlTexCoord2f(1, v); + rlVertex2f(currentPosNormal.x, currentPosNormal.y); - rlTexCoord2f(0, v); - rlVertex2f(currentNegNormal.x, currentNegNormal.y); + rlTexCoord2f(0, v); + rlVertex2f(currentNegNormal.x, currentNegNormal.y); - rlEnd(); + rlEnd(); - // The current step is the start of the next step - previous = current; - previousTangent = normal; - previousV = v; - } + // The current step is the start of the next step + previous = current; + previousTangent = normal; + previousV = v; + } } static void UpdateOptions(void) { - if (IsKeyPressed(KEY_SPACE)) showCurve = !showCurve; + if (IsKeyPressed(KEY_SPACE)) showCurve = !showCurve; - // Update with - if (IsKeyPressed(KEY_EQUAL)) curveWidth += 2; - if (IsKeyPressed(KEY_MINUS)) curveWidth -= 2; + // Update with + if (IsKeyPressed(KEY_EQUAL)) curveWidth += 2; + if (IsKeyPressed(KEY_MINUS)) curveWidth -= 2; - if (curveWidth < 2) curveWidth = 2; + if (curveWidth < 2) curveWidth = 2; - // Update segments - if (IsKeyPressed(KEY_LEFT)) curveSegments -= 2; - if (IsKeyPressed(KEY_RIGHT)) curveSegments += 2; + // Update segments + if (IsKeyPressed(KEY_LEFT)) curveSegments -= 2; + if (IsKeyPressed(KEY_RIGHT)) curveSegments += 2; - if (curveSegments < 2) curveSegments = 2; + if (curveSegments < 2) curveSegments = 2; } diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 7bb8cf7fa..94ff95626 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -209,8 +209,8 @@ int main(int argc, char* argv[]) if (buffer == NULL) { - printf("Could not read input file: %s\n", inFileName); - return 1; + printf("Could not read input file: %s\n", inFileName); + return 1; } // Preprocess buffer to get separate lines diff --git a/src/rcamera.h b/src/rcamera.h index c0c28209d..edcd5ee30 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -160,7 +160,7 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); // MatrixOrtho() // MatrixIdentity() -// raylib input functionality required: GetMouseDelta(), GetMouseWheelMove(), IsKeyDown(), IsKeyPressed() +// raylib required functionality: GetMouseDelta(), GetMouseWheelMove(), IsKeyDown(), IsKeyPressed(), GetFrameTime() //---------------------------------------------------------------------------------- // Defines and Macros @@ -195,7 +195,7 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- - +//... //---------------------------------------------------------------------------------- // Module Functions Definition @@ -218,6 +218,7 @@ Vector3 GetCameraRight(Camera *camera) { Vector3 forward = GetCameraForward(camera); Vector3 up = GetCameraUp(camera); + return Vector3CrossProduct(forward, up); } @@ -419,26 +420,26 @@ void UpdateCamera(Camera *camera, int mode) bool lockView = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; bool rotateUp = mode == CAMERA_FREE; - if (mode == CAMERA_ORBITAL) - { - // Obital can just orbit - Matrix rotatation = MatrixRotate(GetCameraUp(camera), CAMERA_ORBITAL_SPEED * GetFrameTime()); - Vector3 viewVector = Vector3Subtract(camera->position, camera->target); - viewVector = Vector3Transform(viewVector, rotatation); - camera->position = Vector3Add(camera->target, viewVector); - } + if (mode == CAMERA_ORBITAL) + { + // Orbital can just orbit + Matrix rotatation = MatrixRotate(GetCameraUp(camera), CAMERA_ORBITAL_SPEED*GetFrameTime()); + Vector3 viewVector = Vector3Subtract(camera->position, camera->target); + viewVector = Vector3Transform(viewVector, rotatation); + camera->position = Vector3Add(camera->target, viewVector); + } else { - // Camera rotation - if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); - if (IsKeyDown(KEY_UP)) CameraPitch(camera, CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); - if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -CAMERA_ROTATION_SPEED, rotateAroundTarget); - if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, CAMERA_ROTATION_SPEED, rotateAroundTarget); - if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); - if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); + // Camera rotation + if (IsKeyDown(KEY_DOWN)) CameraPitch(camera, -CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); + if (IsKeyDown(KEY_UP)) CameraPitch(camera, CAMERA_ROTATION_SPEED, lockView, rotateAroundTarget, rotateUp); + if (IsKeyDown(KEY_RIGHT)) CameraYaw(camera, -CAMERA_ROTATION_SPEED, rotateAroundTarget); + if (IsKeyDown(KEY_LEFT)) CameraYaw(camera, CAMERA_ROTATION_SPEED, rotateAroundTarget); + if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); + if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); - CameraYaw(camera, -mousePositionDelta.x * CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, -mousePositionDelta.y * CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + CameraYaw(camera, -mousePositionDelta.x * CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -mousePositionDelta.y * CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); // Camera movement if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); From f594f14510984c43f38e4296d854a0c88633ad63 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Feb 2023 18:01:42 +0100 Subject: [PATCH 0280/1710] Reviewed typo and formating --- src/rcamera.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index edcd5ee30..9aab3a348 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -423,10 +423,10 @@ void UpdateCamera(Camera *camera, int mode) if (mode == CAMERA_ORBITAL) { // Orbital can just orbit - Matrix rotatation = MatrixRotate(GetCameraUp(camera), CAMERA_ORBITAL_SPEED*GetFrameTime()); - Vector3 viewVector = Vector3Subtract(camera->position, camera->target); - viewVector = Vector3Transform(viewVector, rotatation); - camera->position = Vector3Add(camera->target, viewVector); + Matrix rotation = MatrixRotate(GetCameraUp(camera), CAMERA_ORBITAL_SPEED*GetFrameTime()); + Vector3 view = Vector3Subtract(camera->position, camera->target); + view = Vector3Transform(view, rotation); + camera->position = Vector3Add(camera->target, view); } else { @@ -438,8 +438,8 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); - CameraYaw(camera, -mousePositionDelta.x * CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, -mousePositionDelta.y * CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); // Camera movement if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); From 47dd842e8163d43587c7f6bb91bbf88dbdbb24b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Feb 2023 18:11:46 +0100 Subject: [PATCH 0281/1710] Minor format tweaks --- src/rcamera.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 9aab3a348..6aee26f38 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -160,7 +160,12 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); // MatrixOrtho() // MatrixIdentity() -// raylib required functionality: GetMouseDelta(), GetMouseWheelMove(), IsKeyDown(), IsKeyPressed(), GetFrameTime() +// raylib required functionality: + // GetMouseDelta() + // GetMouseWheelMove() + // IsKeyDown() + // IsKeyPressed() + // GetFrameTime() //---------------------------------------------------------------------------------- // Defines and Macros @@ -396,12 +401,13 @@ Matrix GetCameraProjectionMatrix(Camera *camera, float aspect) { if (camera->projection == CAMERA_PERSPECTIVE) { - return MatrixPerspective(camera->fovy * DEG2RAD, aspect, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR); + return MatrixPerspective(camera->fovy*DEG2RAD, aspect, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR); } else if (camera->projection == CAMERA_ORTHOGRAPHIC) { - double top = camera->fovy / 2.0; - double right = top * aspect; + double top = camera->fovy/2.0; + double right = top*aspect; + return MatrixOrtho(-right, right, -top, top, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR); } @@ -415,10 +421,10 @@ void UpdateCamera(Camera *camera, int mode) { Vector2 mousePositionDelta = GetMouseDelta(); - bool moveInWorldPlane = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON; - bool rotateAroundTarget = mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; - bool lockView = mode == CAMERA_FIRST_PERSON || mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL; - bool rotateUp = mode == CAMERA_FREE; + bool moveInWorldPlane = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)); + bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); + bool lockView = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); + bool rotateUp = (mode == CAMERA_FREE); if (mode == CAMERA_ORBITAL) { @@ -450,7 +456,7 @@ void UpdateCamera(Camera *camera, int mode) //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); } - if (mode == CAMERA_THIRD_PERSON || mode == CAMERA_ORBITAL) + if ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)) { // Zoom target distance CameraMoveToTarget(camera, -GetMouseWheelMove()); From d26a56d4e1d8168912e3670a2ff3122b125fd94c Mon Sep 17 00:00:00 2001 From: hkc Date: Mon, 20 Feb 2023 13:13:24 +0300 Subject: [PATCH 0282/1710] Added mixed audio processor (#2929) * Use RL_QUADS/RL_TRIANGLES for single-pixel drawing Addresses problem mentioned in https://github.com/raysan5/raylib/issues/2744#issuecomment-1273568263 (in short: when drawing pixels using DrawPixel{,V} in camera mode, upscaled pixel becomes a line instead of bigger pixel) * [rtextures] Fixed scaling down in ImageTextEx Closes #2755 * Added global audio processor * Renamed struct member to follow naming conventions * Added example for AttachAudioMixedProcessor --- examples/audio/audio_mixed_processor.c | 123 +++++++++++++++++++++++ examples/audio/audio_mixed_processor.png | Bin 0 -> 8708 bytes src/raudio.c | 65 +++++++++++- src/raylib.h | 3 + 4 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 examples/audio/audio_mixed_processor.c create mode 100644 examples/audio/audio_mixed_processor.png diff --git a/examples/audio/audio_mixed_processor.c b/examples/audio/audio_mixed_processor.c new file mode 100644 index 000000000..3a008f3e2 --- /dev/null +++ b/examples/audio/audio_mixed_processor.c @@ -0,0 +1,123 @@ +/******************************************************************************************* +* +* raylib [audio] example - Mixed audio processing +* +* 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) +* +* 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) +* +********************************************************************************************/ +#include "raylib.h" +#include + +static float exponent = 1.0f; // Audio exponentiation value +static float averageVolume[400] = { 0.0f }; // Average volume history + +//------------------------------------------------------------------------------------ +// Audio processing function +//------------------------------------------------------------------------------------ +void ProcessAudio(void *buffer, unsigned int frames) +{ + float *samples = (float *)buffer; // Samples internally stored as s + float average = 0.0f; // Temporary average volume + + for (unsigned int frame = 0; frame < frames; frame++) + { + 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 ); + + average += fabsf(*left) / frames; // accumulating average volume + average += fabsf(*right) / frames; + } + + // Moving history to the left + for (int i = 0; i < 399; i++) averageVolume[i] = averageVolume[i + 1]; + + averageVolume[399] = average; // Adding last average value +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - processing mixed output"); + + InitAudioDevice(); // Initialize audio device + + AttachAudioMixedProcessor(ProcessAudio); + + Music music = LoadMusicStream("resources/country.mp3"); + Sound sound = LoadSound("resources/coin.wav"); + + PlayMusicStream(music); + + 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 + //---------------------------------------------------------------------------------- + UpdateMusicStream(music); // Update music buffer with new stream data + + // Modify processing variables + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_LEFT)) exponent -= 0.05f; + if (IsKeyPressed(KEY_RIGHT)) exponent += 0.05f; + + if (exponent <= 0.5f) exponent = 0.5f; + if (exponent >= 3.0f) exponent = 3.0f; + + if (IsKeyPressed(KEY_SPACE)) PlaySound(sound); + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, LIGHTGRAY); + + DrawText(TextFormat("EXPONENT = %.2f", exponent), 215, 180, 20, LIGHTGRAY); + + DrawRectangle(199, 199, 402, 34, LIGHTGRAY); + for (int i = 0; i < 400; i++) + { + DrawLine(201 + i, 232 - averageVolume[i] * 32, 201 + i, 232, MAROON); + } + DrawRectangleLines(199, 199, 402, 34, GRAY); + + DrawText("PRESS SPACE TO PLAY OTHER SOUND", 200, 250, 20, LIGHTGRAY); + DrawText("USE LEFT AND RIGHT ARROWS TO ALTER DISTORTION", 140, 280, 20, LIGHTGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadMusicStream(music); // Unload music stream buffers from RAM + + DetachAudioMixedProcessor(ProcessAudio); // Disconnect audio processor + + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/audio/audio_mixed_processor.png b/examples/audio/audio_mixed_processor.png new file mode 100644 index 0000000000000000000000000000000000000000..8575a836df6c39d1a35c82f10c0837262eb7f368 GIT binary patch literal 8708 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 z%p+%>9dklHUpnT?process = process; + + rAudioProcessor *last = AUDIO.mixedProcessor; + + while (last && last->next) + { + last = last->next; + } + if (last) + { + processor->prev = last; + last->next = processor; + } + else AUDIO.mixedProcessor = processor; + + ma_mutex_unlock(&AUDIO.System.lock); +} + +void DetachAudioMixedProcessor(AudioCallback process) +{ + ma_mutex_lock(&AUDIO.System.lock); + + rAudioProcessor *processor = AUDIO.mixedProcessor; + + while (processor) + { + rAudioProcessor *next = processor->next; + rAudioProcessor *prev = processor->prev; + + if (processor->process == process) + { + if (AUDIO.mixedProcessor == processor) AUDIO.mixedProcessor = next; + if (prev) prev->next = next; + if (next) next->prev = prev; + + RL_FREE(processor); + } + + processor = next; + } + + ma_mutex_unlock(&AUDIO.System.lock); +} + + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -2519,6 +2575,13 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const } } + rAudioProcessor *processor = AUDIO.mixedProcessor; + while (processor) + { + processor->process(pFramesOut, frameCount); + processor = processor->next; + } + ma_mutex_unlock(&AUDIO.System.lock); } diff --git a/src/raylib.h b/src/raylib.h index c1b85abdf..73c6d2242 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1580,6 +1580,9 @@ RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream 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 +RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline + #if defined(__cplusplus) } #endif From 1347a155397621d789f23a4bd1f49e3652cb125d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Feb 2023 13:14:51 +0100 Subject: [PATCH 0283/1710] ADDED: `SetWindowIcons()` to set multiple icon image sizes --- src/raylib.h | 3 ++- src/rcore.c | 70 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 73c6d2242..6bc81fe75 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -941,7 +941,8 @@ RLAPI void ToggleFullscreen(void); // Toggle wind 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 (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) RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) diff --git a/src/rcore.c b/src/rcore.c index 3de0f3f3c..df5f193c5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1554,23 +1554,69 @@ void ClearWindowState(unsigned int flags) } // Set icon for window (only PLATFORM_DESKTOP) -// NOTE: Image must be in RGBA format, 8bit per channel +// NOTE 1: Image must be in RGBA format, 8bit per channel +// NOTE 2: Image is scaled by the OS for all required sizes void SetWindowIcon(Image image) { #if defined(PLATFORM_DESKTOP) - if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + if (image.data == NULL) { - GLFWimage icon[1] = { 0 }; - - icon[0].width = image.width; - icon[0].height = image.height; - icon[0].pixels = (unsigned char *)image.data; - - // NOTE 1: We only support one image icon - // NOTE 2: The specified image data is copied before this function returns - glfwSetWindowIcon(CORE.Window.handle, 1, icon); + // Revert to the default window icon, pass in an empty image array + glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + } + else + { + if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + GLFWimage icon[1] = { 0 }; + + icon[0].width = image.width; + icon[0].height = image.height; + icon[0].pixels = (unsigned char *)image.data; + + // NOTE 1: We only support one image icon + // NOTE 2: The specified image data is copied before this function returns + glfwSetWindowIcon(CORE.Window.handle, 1, icon); + } + else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); + } +#endif +} + +// Set icon for window (multiple images, only PLATFORM_DESKTOP) +// NOTE 1: Images must be in RGBA format, 8bit per channel +// NOTE 2: The multiple images are used depending on provided sizes +// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 +void SetWindowIcons(Image *images, int count) +{ +#if defined(PLATFORM_DESKTOP) + if ((images == NULL) || (count <= 0)) + { + // Revert to the default window icon, pass in an empty image array + glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + } + else + { + int valid = 0; + GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); + + for (int i = 0; i < count; i++) + { + if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + icons[valid].width = images[i].width; + icons[valid].height = images[i].height; + icons[valid].pixels = (unsigned char *)images[i].data; + + valid++; + } + else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); + } + // NOTE: Images data is copied internally before this function returns + glfwSetWindowIcon(CORE.Window.handle, valid, icons); + + RL_FREE(icons); } - else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); #endif } From bcae065c8851eacb1ab1e60cf085dd7edcf2a8f7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Feb 2023 13:15:11 +0100 Subject: [PATCH 0284/1710] REVIEWED: `IsShaderReady()` --- src/rcore.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index df5f193c5..6fdeb94e6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2561,7 +2561,33 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) // Check if a shader is ready bool IsShaderReady(Shader shader) { - return shader.locs != NULL; + return ((shader.id > 0) && // Validate shader id (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) From 3cade2a1a033d8c1f5ab62cfaa24e5f65d424576 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Feb 2023 13:20:04 +0100 Subject: [PATCH 0285/1710] REVIEWED: `IsImageReady()` and `IsTexureReady()` Reordered some functions to avoid config.h issues when disabling some features. --- src/rtextures.c | 358 +++++++++++++++++++++++++----------------------- 1 file changed, 183 insertions(+), 175 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 9bce9c716..c56365637 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -30,7 +30,7 @@ * * #define SUPPORT_IMAGE_MANIPULATION * Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... -* If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() +* If not defined only some image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageResize*() * * #define SUPPORT_IMAGE_GENERATION * Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) @@ -190,13 +190,10 @@ #include "external/stb_perlin.h" // Required for: stb_perlin_fbm_noise3 #endif -#if defined(SUPPORT_IMAGE_MANIPULATION) - #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size)) - #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr)) - - #define STB_IMAGE_RESIZE_IMPLEMENTATION - #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()] -#endif +#define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size)) +#define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr)) +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()] //---------------------------------------------------------------------------------- // Defines and Macros @@ -505,7 +502,11 @@ Image LoadImageFromScreen(void) // Check if an image is ready bool IsImageReady(Image image) { - return image.data != NULL && image.width > 0 && image.height > 0 && image.format > 0; + return ((image.data != NULL) && // Validate pixel data available + (image.width > 0) && + (image.height > 0) && // Validate image size + (image.format > 0) && // Validate image format + (image.mipmaps > 0)); // Validate image mipmaps (at least 1 for basic mipmap level) } // Unload image from CPU memory (RAM) @@ -1228,23 +1229,6 @@ void ImageFormat(Image *image, int newFormat) } } -// Convert image to POT (power-of-two) -// NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5) -void ImageToPOT(Image *image, Color fill) -{ - // Security check to avoid program crash - if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; - - // Calculate next power-of-two values - // NOTE: Just add the required amount of pixels at the right and bottom sides of image... - int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2))); - int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2))); - - // Check if POT texture generation is required (if texture is not already POT) - if ((potWidth != image->width) || (potHeight != image->height)) ImageResizeCanvas(image, potWidth, potHeight, 0, 0, fill); -} - -#if defined(SUPPORT_IMAGE_MANIPULATION) // Create an image from text (default font) Image ImageText(const char *text, int fontSize, Color color) { @@ -1330,6 +1314,172 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co return imText; } +// Resize and image to new size using Nearest-Neighbor scaling algorithm +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; + + Color *pixels = LoadImageColors(*image); + Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); + + // EDIT: added +1 to account for an early rounding problem + int xRatio = (int)((image->width << 16)/newWidth) + 1; + int yRatio = (int)((image->height << 16)/newHeight) + 1; + + int x2, y2; + for (int y = 0; y < newHeight; y++) + { + for (int x = 0; x < newWidth; x++) + { + x2 = ((x*xRatio) >> 16); + y2 = ((y*yRatio) >> 16); + + output[(y*newWidth) + x] = pixels[(y2*image->width) + x2] ; + } + } + + int format = image->format; + + RL_FREE(image->data); + + image->data = output; + image->width = newWidth; + image->height = newHeight; + image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + ImageFormat(image, format); // Reformat 32bit RGBA image to original format + + UnloadImageColors(pixels); +} + + +// Resize and image to new size +// NOTE: Uses stb default scaling filters (both bicubic): +// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +// STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL (high-quality Catmull-Rom) +void ImageResize(Image *image, int newWidth, int newHeight) +{ + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + + // Check if we can use a fast path on image scaling + // It can be for 8 bit per channel images with 1 to 4 channels per pixel + if ((image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || + (image->format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) || + (image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) || + (image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)) + { + int bytesPerPixel = GetPixelDataSize(1, 1, image->format); + unsigned char *output = (unsigned char *)RL_MALLOC(newWidth*newHeight*bytesPerPixel); + + switch (image->format) + { + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 1); break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 2); break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 3); break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 4); break; + default: break; + } + + RL_FREE(image->data); + image->data = output; + image->width = newWidth; + image->height = newHeight; + } + else + { + // Get data as Color pixels array to work with it + Color *pixels = LoadImageColors(*image); + Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); + + // NOTE: Color data is cast to (unsigned char *), there shouldn't been any problem... + stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4); + + int format = image->format; + + UnloadImageColors(pixels); + RL_FREE(image->data); + + image->data = output; + image->width = newWidth; + image->height = newHeight; + image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + ImageFormat(image, format); // Reformat 32bit RGBA image to original format + } +} + +// Resize canvas and fill with color +// NOTE: Resize offset is relative to the top-left corner of the original image +void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill) +{ + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + + if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level"); + if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats"); + else if ((newWidth != image->width) || (newHeight != image->height)) + { + Rectangle srcRec = { 0, 0, (float)image->width, (float)image->height }; + Vector2 dstPos = { (float)offsetX, (float)offsetY }; + + if (offsetX < 0) + { + srcRec.x = (float)-offsetX; + srcRec.width += (float)offsetX; + dstPos.x = 0; + } + else if ((offsetX + image->width) > newWidth) srcRec.width = (float)(newWidth - offsetX); + + if (offsetY < 0) + { + srcRec.y = (float)-offsetY; + srcRec.height += (float)offsetY; + dstPos.y = 0; + } + else if ((offsetY + image->height) > newHeight) srcRec.height = (float)(newHeight - offsetY); + + if (newWidth < srcRec.width) srcRec.width = (float)newWidth; + if (newHeight < srcRec.height) srcRec.height = (float)newHeight; + + int bytesPerPixel = GetPixelDataSize(1, 1, image->format); + unsigned char *resizedData = (unsigned char *)RL_CALLOC(newWidth*newHeight*bytesPerPixel, 1); + + // TODO: Fill resized canvas with fill color (must be formatted to image->format) + + int dstOffsetSize = ((int)dstPos.y*newWidth + (int)dstPos.x)*bytesPerPixel; + + for (int y = 0; y < (int)srcRec.height; y++) + { + memcpy(resizedData + dstOffsetSize, ((unsigned char *)image->data) + ((y + (int)srcRec.y)*image->width + (int)srcRec.x)*bytesPerPixel, (int)srcRec.width*bytesPerPixel); + dstOffsetSize += (newWidth*bytesPerPixel); + } + + RL_FREE(image->data); + image->data = resizedData; + image->width = newWidth; + image->height = newHeight; + } +} + +#if defined(SUPPORT_IMAGE_MANIPULATION) +// Convert image to POT (power-of-two) +// NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5) +void ImageToPOT(Image *image, Color fill) +{ + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + + // Calculate next power-of-two values + // NOTE: Just add the required amount of pixels at the right and bottom sides of image... + int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2))); + int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2))); + + // Check if POT texture generation is required (if texture is not already POT) + if ((potWidth != image->width) || (potHeight != image->height)) ImageResizeCanvas(image, potWidth, potHeight, 0, 0, fill); +} + // Crop image depending on alpha value // NOTE: Threshold is defined as a percentage: 0.0f -> 1.0f void ImageAlphaCrop(Image *image, float threshold) @@ -1672,154 +1822,6 @@ void ImageBlurGaussian(Image *image, int blurSize) { ImageFormat(image, format); } -// Resize and image to new size -// NOTE: Uses stb default scaling filters (both bicubic): -// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM -// STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL (high-quality Catmull-Rom) -void ImageResize(Image *image, int newWidth, int newHeight) -{ - // Security check to avoid program crash - if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; - - // Check if we can use a fast path on image scaling - // It can be for 8 bit per channel images with 1 to 4 channels per pixel - if ((image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || - (image->format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) || - (image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) || - (image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)) - { - int bytesPerPixel = GetPixelDataSize(1, 1, image->format); - unsigned char *output = (unsigned char *)RL_MALLOC(newWidth*newHeight*bytesPerPixel); - - switch (image->format) - { - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 1); break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 2); break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 3); break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 4); break; - default: break; - } - - RL_FREE(image->data); - image->data = output; - image->width = newWidth; - image->height = newHeight; - } - else - { - // Get data as Color pixels array to work with it - Color *pixels = LoadImageColors(*image); - Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); - - // NOTE: Color data is cast to (unsigned char *), there shouldn't been any problem... - stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4); - - int format = image->format; - - UnloadImageColors(pixels); - RL_FREE(image->data); - - image->data = output; - image->width = newWidth; - image->height = newHeight; - image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - - ImageFormat(image, format); // Reformat 32bit RGBA image to original format - } -} - -// Resize and image to new size using Nearest-Neighbor scaling algorithm -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; - - Color *pixels = LoadImageColors(*image); - Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); - - // EDIT: added +1 to account for an early rounding problem - int xRatio = (int)((image->width << 16)/newWidth) + 1; - int yRatio = (int)((image->height << 16)/newHeight) + 1; - - int x2, y2; - for (int y = 0; y < newHeight; y++) - { - for (int x = 0; x < newWidth; x++) - { - x2 = ((x*xRatio) >> 16); - y2 = ((y*yRatio) >> 16); - - output[(y*newWidth) + x] = pixels[(y2*image->width) + x2] ; - } - } - - int format = image->format; - - RL_FREE(image->data); - - image->data = output; - image->width = newWidth; - image->height = newHeight; - image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - - ImageFormat(image, format); // Reformat 32bit RGBA image to original format - - UnloadImageColors(pixels); -} - -// Resize canvas and fill with color -// NOTE: Resize offset is relative to the top-left corner of the original image -void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill) -{ - // Security check to avoid program crash - if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; - - if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level"); - if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats"); - else if ((newWidth != image->width) || (newHeight != image->height)) - { - Rectangle srcRec = { 0, 0, (float)image->width, (float)image->height }; - Vector2 dstPos = { (float)offsetX, (float)offsetY }; - - if (offsetX < 0) - { - srcRec.x = (float)-offsetX; - srcRec.width += (float)offsetX; - dstPos.x = 0; - } - else if ((offsetX + image->width) > newWidth) srcRec.width = (float)(newWidth - offsetX); - - if (offsetY < 0) - { - srcRec.y = (float)-offsetY; - srcRec.height += (float)offsetY; - dstPos.y = 0; - } - else if ((offsetY + image->height) > newHeight) srcRec.height = (float)(newHeight - offsetY); - - if (newWidth < srcRec.width) srcRec.width = (float)newWidth; - if (newHeight < srcRec.height) srcRec.height = (float)newHeight; - - int bytesPerPixel = GetPixelDataSize(1, 1, image->format); - unsigned char *resizedData = (unsigned char *)RL_CALLOC(newWidth*newHeight*bytesPerPixel, 1); - - // TODO: Fill resized canvas with fill color (must be formatted to image->format) - - int dstOffsetSize = ((int)dstPos.y*newWidth + (int)dstPos.x)*bytesPerPixel; - - for (int y = 0; y < (int)srcRec.height; y++) - { - memcpy(resizedData + dstOffsetSize, ((unsigned char *)image->data) + ((y + (int)srcRec.y)*image->width + (int)srcRec.x)*bytesPerPixel, (int)srcRec.width*bytesPerPixel); - dstOffsetSize += (newWidth*bytesPerPixel); - } - - RL_FREE(image->data); - image->data = resizedData; - image->width = newWidth; - image->height = newHeight; - } -} - // Generate all mipmap levels for a provided image // NOTE 1: Supports POT and NPOT images // NOTE 2: image.data is scaled to include mipmap levels @@ -3333,7 +3335,13 @@ RenderTexture2D LoadRenderTexture(int width, int height) // Check if a texture is ready bool IsTextureReady(Texture2D texture) { - return texture.id > 0 && texture.width > 0 && texture.height > 0 && texture.format > 0; + // TODO: Validate maximum texture size supported by GPU? + + return ((texture.id > 0) && // Validate OpenGL id + (texture.width > 0) && + (texture.height > 0) && // Validate texture size + (texture.format > 0) && // Validate texture pixel format + (texture.mipmap > 0)); // Validate texture mipmaps (at least 1 for basic mipmap level) } // Unload texture from GPU memory (VRAM) From b2926b2d28accb1616db72489f5ee123c41c01d2 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Feb 2023 13:20:52 +0100 Subject: [PATCH 0286/1710] Update rtextures.c --- src/rtextures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index c56365637..7ae96c67f 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3341,7 +3341,7 @@ bool IsTextureReady(Texture2D texture) (texture.width > 0) && (texture.height > 0) && // Validate texture size (texture.format > 0) && // Validate texture pixel format - (texture.mipmap > 0)); // Validate texture mipmaps (at least 1 for basic mipmap level) + (texture.mipmaps > 0)); // Validate texture mipmaps (at least 1 for basic mipmap level) } // Unload texture from GPU memory (VRAM) From 8169d0eab249c0a639bca16c721f8bfe1b3ef4b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Feb 2023 23:01:23 +0100 Subject: [PATCH 0287/1710] REVIEWED: `IsWindowFocused()` to consider Android App state #2935 --- src/rcore.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 6fdeb94e6..327921fec 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1141,9 +1141,8 @@ bool IsWindowMinimized(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); -#else - return false; #endif + return false; } // Check if window has been maximized (only PLATFORM_DESKTOP) @@ -1151,9 +1150,8 @@ bool IsWindowMaximized(void) { #if defined(PLATFORM_DESKTOP) return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); -#else - return false; #endif + return false; } // Check if window has the focus @@ -1161,9 +1159,11 @@ bool IsWindowFocused(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); -#else - return true; #endif +#if defined(PLATFORM_ANDROID) + return CORE.Android.appEnabled; +#endif + return true; } // Check if window has been resizedLastFrame From b4d824d6fcdbfb64023664ebd1c9b258d021a0cc Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Feb 2023 23:10:03 +0100 Subject: [PATCH 0288/1710] REVIEWED: `GetMonitorWidth()` and `GetMonitorHeight()` #2934 --- src/rcore.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 327921fec..807d58f91 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1831,6 +1831,12 @@ int GetMonitorWidth(int monitor) else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +#endif +#if defined(PLATFORM_ANDROID) + if (CORE.Android.app->window != NULL) + { + return ANativeWindow_getWidth(CORE.Android.app->window); + } #endif return 0; } @@ -1850,6 +1856,12 @@ int GetMonitorHeight(int monitor) else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +#endif +#if defined(PLATFORM_ANDROID) + if (CORE.Android.app->window != NULL) + { + return ANativeWindow_getHeight(CORE.Android.app->window); + } #endif return 0; } From 153470d60503e840d5b0a15abe3647a4316e988c Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Feb 2023 23:55:55 +0100 Subject: [PATCH 0289/1710] REVIEWED: `GenMeshTangents()`, avoid crash on missing texcoords data #2927 --- src/rmodels.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 2fd2d5a4a..f8d6ec135 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3326,9 +3326,15 @@ BoundingBox GetMeshBoundingBox(Mesh mesh) // Compute mesh tangents // NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates -// Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html +// 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 { From d652b95fbfc06b7a996386c9d212e9fe2d669ed8 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Feb 2023 17:28:25 +0100 Subject: [PATCH 0290/1710] ADDED: Security checks --- src/rtext.c | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 3e838f85c..7cce1eb87 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1568,17 +1568,20 @@ const char *TextToUpper(const char *text) static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); - for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + if (text != NULL) { - if (text[i] != '\0') + for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) { - buffer[i] = (char)toupper(text[i]); - //if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32; + if (text[i] != '\0') + { + buffer[i] = (char)toupper(text[i]); + //if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32; - // TODO: Support UTF-8 diacritics to upper-case - //if ((text[i] >= 'à') && (text[i] <= 'ý')) buffer[i] = text[i] - 32; + // TODO: Support UTF-8 diacritics to upper-case + //if ((text[i] >= 'à') && (text[i] <= 'ý')) buffer[i] = text[i] - 32; + } + else { buffer[i] = '\0'; break; } } - else { buffer[i] = '\0'; break; } } return buffer; @@ -1591,14 +1594,17 @@ const char *TextToLower(const char *text) static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); - for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + if (text != NULL) { - if (text[i] != '\0') + for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) { - buffer[i] = (char)tolower(text[i]); - //if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32; + if (text[i] != '\0') + { + buffer[i] = (char)tolower(text[i]); + //if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32; + } + else { buffer[i] = '\0'; break; } } - else { buffer[i] = '\0'; break; } } return buffer; @@ -1611,20 +1617,23 @@ const char *TextToPascal(const char *text) static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); - buffer[0] = (char)toupper(text[0]); - - for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++) + if (text != NULL) { - if (text[j] != '\0') + buffer[0] = (char)toupper(text[0]); + + for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++) { - if (text[j] != '_') buffer[i] = text[j]; - else + if (text[j] != '\0') { - j++; - buffer[i] = (char)toupper(text[j]); + if (text[j] != '_') buffer[i] = text[j]; + else + { + j++; + buffer[i] = (char)toupper(text[j]); + } } + else { buffer[i] = '\0'; break; } } - else { buffer[i] = '\0'; break; } } return buffer; From d5a31168ce768ef9c1e11fe4734285770a33aba4 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 23 Feb 2023 18:08:15 +0100 Subject: [PATCH 0291/1710] REVIEWED: Data validation --- src/raudio.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 5f5332153..04386167d 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -868,7 +868,11 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int // Checks if wave data is ready bool IsWaveReady(Wave wave) { - return wave.data != NULL; + return ((wave.data != NULL) && // Validate wave data available + (wave.frameCount > 0) && // Validate frame count + (wave.sampleRate > 0) && // Validate sample rate is supported + (wave.sampleSize > 0) && // Validate sample size is supported + (wave.channels > 0)); // Validate number of channels supported } // Load sound from file @@ -930,7 +934,11 @@ Sound LoadSoundFromWave(Wave wave) // Checks if a sound is ready bool IsSoundReady(Sound sound) { - return sound.stream.buffer != NULL; + return ((sound.frameCount > 0) && // Validate frame count + (sound.stream.buffer != NULL) && // Validate stream buffer + (sound.stream.sampleRate > 0) && // Validate sample rate is supported + (sound.stream.sampleSize > 0) && // Validate sample size is supported + (sound.stream.channels > 0)); // Validate number of channels supported } // Unload wave data @@ -1722,7 +1730,11 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, // Checks if a music stream is ready bool IsMusicReady(Music music) { - return music.ctxData != NULL; + return ((music.ctxData != NULL) && // Validate context loaded + (music.frameCount > 0) && // Validate audio frame count + (music.stream.sampleRate > 0) && // Validate sample rate is supported + (music.stream.sampleSize > 0) && // Validate sample size is supported + (music.stream.channels > 0)); // Validate number of channels supported } // Unload music stream @@ -2097,7 +2109,10 @@ AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // Checks if an audio stream is ready RLAPI bool IsAudioStreamReady(AudioStream stream) { - return stream.buffer != NULL; + return ((stream.buffer != NULL) && // Validate stream buffer + (stream.sampleRate > 0) && // Validate sample rate is supported + (stream.sampleSize > 0) && // Validate sample size is supported + (stream.channels > 0)); // Validate number of channels supported } // Unload audio stream and free memory From 110eab56a701bbd65f8762cbf6d11ca4e6925fbd Mon Sep 17 00:00:00 2001 From: fubark Date: Thu, 23 Feb 2023 15:48:28 -0800 Subject: [PATCH 0292/1710] Add Cyber to BINDINGS.md (#2939) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index f703296a7..e232b1b6c 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -14,6 +14,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | claylib/wrap | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | | chez-raylib | auto | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | +| ray-cyber | **4.2** | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | From ca98a84d68ea6f70112de57a68866b4bb55edbe5 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 24 Feb 2023 15:27:25 +0100 Subject: [PATCH 0293/1710] Update rtext.c --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 7cce1eb87..f07261121 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1339,7 +1339,7 @@ int TextCopy(char *dst, const char *src) { int bytes = 0; - if (dst != NULL) + if ((src != NULL) && (dst != NULL)) { while (*src != '\0') { From cb2ba3675c3928a2937f84e4c6503a659375ca0a Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 24 Feb 2023 16:19:25 +0100 Subject: [PATCH 0294/1710] REVIEWED: `rlUnloadFramebuffer()` #2937 --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index f7d73edaf..7e9fee68a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3473,7 +3473,7 @@ void rlUnloadFramebuffer(unsigned int id) unsigned int depthIdU = (unsigned int)depthId; if (depthType == GL_RENDERBUFFER) glDeleteRenderbuffers(1, &depthIdU); - else if (depthType == GL_RENDERBUFFER) glDeleteTextures(1, &depthIdU); + 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. From 215eb967e84c4bd9e193e242b586e7f1e67010a5 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 24 Feb 2023 18:31:16 +0100 Subject: [PATCH 0295/1710] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 807d58f91..8e7adda97 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6996,7 +6996,7 @@ static void ExportAutomationEvents(const char *fileName) // Save as binary /* FILE *repFile = fopen(fileName, "wb"); - fwrite(fileId, 4, 1, repFile); + fwrite(fileId, sizeof(unsigned char), 4, repFile); fwrite(&eventCount, sizeof(int), 1, repFile); fwrite(events, sizeof(AutomationEvent), eventCount, repFile); fclose(repFile); From 7fd2bf1a32c3ed7c932fe7c020858ecadb3b2056 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 25 Feb 2023 01:07:52 +0100 Subject: [PATCH 0296/1710] Minimal tweak --- src/rcore.c | 2 +- src/rmodels.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8e7adda97..443ef8e3a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6944,7 +6944,7 @@ static void LoadAutomationEvents(const char *fileName) // Load binary /* FILE *repFile = fopen(fileName, "rb"); - fread(fileId, 4, 1, repFile); + fread(fileId, 1, 4, repFile); if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) { diff --git a/src/rmodels.c b/src/rmodels.c index f8d6ec135..b28e14dc1 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4222,11 +4222,11 @@ static Model LoadIQM(const char *fileName) 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, 1, iqmFile); + //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, 1, iqmFile); + //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(); @@ -4254,7 +4254,7 @@ static Model LoadIQM(const char *fileName) // Triangles data processing tri = RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle)); //fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET); - //fread(tri, iqmHeader->num_triangles*sizeof(IQMTriangle), 1, iqmFile); + //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++) @@ -4276,7 +4276,7 @@ static Model LoadIQM(const char *fileName) // Vertex arrays data processing va = RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); //fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET); - //fread(va, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray), 1, iqmFile); + //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++) @@ -4395,7 +4395,7 @@ static Model LoadIQM(const char *fileName) // Bones (joints) data processing ijoint = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); //fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET); - //fread(ijoint, iqmHeader->num_joints*sizeof(IQMJoint), 1, iqmFile); + //fread(ijoint, sizeof(IQMJoint), iqmHeader->num_joints, iqmFile); memcpy(ijoint, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint)); model.boneCount = iqmHeader->num_joints; @@ -4407,7 +4407,7 @@ static Model LoadIQM(const char *fileName) // Bones model.bones[i].parent = ijoint[i].parent; //fseek(iqmFile, iqmHeader->ofs_text + ijoint[i].name, SEEK_SET); - //fread(model.bones[i].name, BONE_NAME_LENGTH*sizeof(char), 1, iqmFile); + //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) @@ -4511,14 +4511,14 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int // Get bones data IQMPose *poses = RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose)); //fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET); - //fread(poses, iqmHeader->num_poses*sizeof(IQMPose), 1, iqmFile); + //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, iqmHeader->num_anims*sizeof(IQMAnim), 1, iqmFile); + //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)); @@ -4526,7 +4526,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int // frameposes unsigned short *framedata = RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short)); //fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET); - //fread(framedata, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short), 1, iqmFile); + //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 From 73c9f72c5229808fc564c878bd876d0f693f757d Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 25 Feb 2023 22:32:50 +0100 Subject: [PATCH 0297/1710] Update rtextures.c --- src/rtextures.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 7ae96c67f..f9b5c862d 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -296,7 +296,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int Image LoadImageAnim(const char *fileName, int *frames) { Image image = { 0 }; - int frameCount = 1; + int frameCount = 0; #if defined(SUPPORT_FILEFORMAT_GIF) if (IsFileExtension(fileName, ".gif")) @@ -320,7 +320,11 @@ Image LoadImageAnim(const char *fileName, int *frames) #else if (false) { } #endif - else image = LoadImage(fileName); + else + { + image = LoadImage(fileName); + frameCount = 1; + } // TODO: Support APNG animated images From 394b31faffa0a1557f4860c31bc1168be0fc3022 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sun, 26 Feb 2023 13:01:42 +0200 Subject: [PATCH 0298/1710] Add GL_MIN GL_MAX blending equations (#2945) --- src/rlgl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index 7e9fee68a..66a349645 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -298,6 +298,8 @@ // GL blending functions/equations #define RL_FUNC_ADD 0x8006 // GL_FUNC_ADD +#define RL_MIN 0x8007 // GL_MIN +#define RL_MAX 0x8008 // GL_MAX #define RL_FUNC_SUBTRACT 0x800A // GL_FUNC_SUBTRACT #define RL_FUNC_REVERSE_SUBTRACT 0x800B // GL_FUNC_REVERSE_SUBTRACT #define RL_BLEND_EQUATION 0x8009 // GL_BLEND_EQUATION From 6897b4359922ba5862eaaba159b24e734ff655ee Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Mar 2023 20:13:35 +0100 Subject: [PATCH 0299/1710] REVIEWED: `core_drop_files` #2943 --- examples/core/core_drop_files.c | 44 ++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/examples/core/core_drop_files.c b/examples/core/core_drop_files.c index cfe03e196..b591cdea7 100644 --- a/examples/core/core_drop_files.c +++ b/examples/core/core_drop_files.c @@ -15,6 +15,11 @@ #include "raylib.h" +#include // Required for: calloc(), free() + +#define MAX_FILEPATH_RECORDED 4096 +#define MAX_FILEPATH_SIZE 2048 + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -27,7 +32,14 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - drop files"); - FilePathList droppedFiles = { 0 }; + int filePathCounter = 0; + char *filePaths[MAX_FILEPATH_RECORDED] = { 0 }; // We will register a maximum of filepaths + + // Allocate space for the required file paths + for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) + { + filePaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_SIZE, 1); + } SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -39,11 +51,18 @@ int main(void) //---------------------------------------------------------------------------------- if (IsFileDropped()) { - // Is some files have been previously loaded, unload them - if (droppedFiles.count > 0) UnloadDroppedFiles(droppedFiles); - - // Load new dropped files - droppedFiles = LoadDroppedFiles(); + FilePathList droppedFiles = LoadDroppedFiles(); + + for (int i = 0, offset = filePathCounter; i < droppedFiles.count; i++) + { + if (filePathCounter < (MAX_FILEPATH_RECORDED - 1)) + { + TextCopy(filePaths[offset + i], droppedFiles.paths[i]); + filePathCounter++; + } + } + + UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory } //---------------------------------------------------------------------------------- @@ -53,20 +72,20 @@ int main(void) ClearBackground(RAYWHITE); - if (droppedFiles.count == 0) DrawText("Drop your files to this window!", 100, 40, 20, DARKGRAY); + if (filePathCounter == 0) DrawText("Drop your files to this window!", 100, 40, 20, DARKGRAY); else { DrawText("Dropped files:", 100, 40, 20, DARKGRAY); - for (unsigned int i = 0; i < droppedFiles.count; i++) + for (unsigned int i = 0; i < filePathCounter; i++) { if (i%2 == 0) DrawRectangle(0, 85 + 40*i, screenWidth, 40, Fade(LIGHTGRAY, 0.5f)); else DrawRectangle(0, 85 + 40*i, screenWidth, 40, Fade(LIGHTGRAY, 0.3f)); - DrawText(droppedFiles.paths[i], 120, 100 + 40*i, 10, GRAY); + DrawText(filePaths[i], 120, 100 + 40*i, 10, GRAY); } - DrawText("Drop new files...", 100, 110 + 40*droppedFiles.count, 20, DARKGRAY); + DrawText("Drop new files...", 100, 110 + 40*filePathCounter, 20, DARKGRAY); } EndDrawing(); @@ -75,7 +94,10 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadDroppedFiles(droppedFiles); // Unload files memory + for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) + { + RL_FREE(filePaths[i]); // Free allocated memory for all filepaths + } CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From 1611cd54e7f9a17bab14902ebe37bd8b2945449d Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 4 Mar 2023 18:56:04 +0100 Subject: [PATCH 0300/1710] REVIEWED: `GetWindowHandle()` #2938 --- src/rcore.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 443ef8e3a..6f4fa2951 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1718,7 +1718,8 @@ void *GetWindowHandle(void) // typedef unsigned long XID; // typedef XID Window; //unsigned long id = (unsigned long)glfwGetX11Window(window); - return NULL; // TODO: Find a way to return value... cast to void *? + //return NULL; // TODO: Find a way to return value... cast to void *? + return (void *)CORE.Window.handle; #endif #if defined(__APPLE__) // NOTE: Returned handle is: (objc_object *) From 5492f52adccbde94db221c1a5c7351deea1dfeec Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 00:09:56 +0100 Subject: [PATCH 0301/1710] Change default threading model for COM objects It shouldn't matter much but it could avoid some conflicts with other libraries in the future (like `tinyfiledialogs`). --- src/raudio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index 04386167d..b4b8b64e3 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -168,6 +168,10 @@ typedef struct tagBITMAPINFOHEADER { #define MA_NO_WAV #define MA_NO_FLAC #define MA_NO_MP3 + +// Threading model: Default: [0] COINIT_MULTITHREADED: COM calls objects on any thread (free threading) +#define MA_COINIT_VALUE 2 // [2] COINIT_APARTMENTTHREADED: Each object has its own thread (apartment model) + #define MINIAUDIO_IMPLEMENTATION //#define MA_DEBUG_OUTPUT #include "external/miniaudio.h" // Audio device initialization and management From 0eeb499288b7c8f948588695e5927bfd07eabe07 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 11:17:47 +0100 Subject: [PATCH 0302/1710] Update qoa.h --- src/external/qoa.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/external/qoa.h b/src/external/qoa.h index 65752d1b7..59d90aded 100644 --- a/src/external/qoa.h +++ b/src/external/qoa.h @@ -22,12 +22,12 @@ not in the file header. A decoder may peek into the first frame of the file to find these values. In a valid QOA file all frames have the same number of channels and the same -samplerate. These restriction may be releaxed for streaming. This remains to +samplerate. These restrictions may be relaxed for streaming. This remains to be decided. All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file, including the headers, is 64 bit aligned, so it's possible to read files with -just a read_u64() that does the byte swapping if neccessary. +just a read_u64() that does the byte swapping if necessary. In pseudocode, the file layout is as follows: @@ -66,7 +66,7 @@ Wheras the 64bit qoa_slice_t is defined as follows: `sf_index` defines the scalefactor to use for this slice as an index into the qoa_scalefactor_tab[16] -`r00`--`r19` are the residuals for the individiual samples, divided by the +`r00`--`r19` are the residuals for the individual samples, divided by the scalefactor and quantized by the qoa_quant_tab[]. In the decoder, a prediction of the next sample is computed by multiplying the @@ -153,7 +153,7 @@ typedef unsigned long long qoa_uint64_t; /* The quant_tab provides an index into the dequant_tab for residuals in the -range of -8 .. 8. It maps this range to just 3bits and becommes less accurate at +range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at the higher end. Note that the residual zero is identical to the lowest positive value. This is mostly fine, since the qoa_div() function always rounds away from zero. */ @@ -169,7 +169,7 @@ static int qoa_quant_tab[17] = { less accurate at the higher end. In theory, the highest scalefactor that we would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we rely on the LMS filter to predict samples accurately enough that a maximum -residual of one quarter of the 16 bit range is high sufficent. I.e. with the +residual of one quarter of the 16 bit range is high sufficient. I.e. with the scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14. The scalefactor values are computed as: @@ -230,7 +230,7 @@ The next sample is predicted as the sum of (weight[i] * history[i]). The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or subtracts the residual to each weight, based on the corresponding sample from -the history. This, suprisingly, is sufficent to get worthwhile predictions. +the history. This, surprisingly, is sufficient to get worthwhile predictions. This is all done with fixed point integers. Hence the right-shifts when updating the weights and calculating the prediction. */ @@ -369,7 +369,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int dequantized = qoa_dequant_tab[scalefactor][quantized]; int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); - int error = (sample - reconstructed); + long long error = (sample - reconstructed); current_error += error * error; if (current_error > best_error) { break; From ab14ad5d754f7bbccbf8845b8062c8ece018b3e0 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 11:26:37 +0100 Subject: [PATCH 0303/1710] Close issue #2949 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 6f4fa2951..4efb20179 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1713,7 +1713,7 @@ void *GetWindowHandle(void) // NOTE: Returned handle is: void *HWND (windows.h) return glfwGetWin32Window(CORE.Window.handle); #endif -#if defined(__linux__) +#if defined(__linux__) && !defined(PLATFORM_DRM) // NOTE: Returned handle is: unsigned long Window (X.h) // typedef unsigned long XID; // typedef XID Window; From 9614d3353b81b95adcfd01392c62326a8c512aee Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 13:49:18 +0100 Subject: [PATCH 0304/1710] REVIEWED: QOA audio file export --- src/raudio.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index b4b8b64e3..207c0c490 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1003,13 +1003,17 @@ bool ExportWave(Wave wave, const char *fileName) #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { - qoa_desc qoa = { 0 }; - qoa.channels = wave.channels; - qoa.samplerate = wave.sampleRate; - qoa.samples = wave.frameCount; + if (wave.sampleSize == 16) + { + qoa_desc qoa = { 0 }; + qoa.channels = wave.channels; + qoa.samplerate = wave.sampleRate; + qoa.samples = wave.frameCount; - // TODO: Review wave.data format required for export - success = qoa_write(fileName, wave.data, &qoa); + int bytesWritten = qoa_write(fileName, 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"); } #endif else if (IsFileExtension(fileName, ".raw")) From f4f6e25340080e149deea5f640976dd8f7452d33 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 14:07:02 +0100 Subject: [PATCH 0305/1710] Support QOA audio file format by default --- examples/audio/resources/target.qoa | Bin 0 -> 35648 bytes src/config.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 examples/audio/resources/target.qoa diff --git a/examples/audio/resources/target.qoa b/examples/audio/resources/target.qoa new file mode 100644 index 0000000000000000000000000000000000000000..d48c4f9f057083be25e82f944e6b08f5c6ffb5e6 GIT binary patch literal 35648 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 Oa$vL^7%d0LF9!e{3+BQA literal 0 HcmV?d00001 diff --git a/src/config.h b/src/config.h index d5f8e486c..de9f4641a 100644 --- a/src/config.h +++ b/src/config.h @@ -214,7 +214,7 @@ #define SUPPORT_FILEFORMAT_WAV 1 #define SUPPORT_FILEFORMAT_OGG 1 #define SUPPORT_FILEFORMAT_MP3 1 -//#define SUPPORT_FILEFORMAT_QOA 1 +#define SUPPORT_FILEFORMAT_QOA 1 //#define SUPPORT_FILEFORMAT_FLAC 1 #define SUPPORT_FILEFORMAT_XM 1 #define SUPPORT_FILEFORMAT_MOD 1 From d3f5bc40436ef45aee2cc353f985d4ad5fc57bbd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Mar 2023 20:06:45 +0100 Subject: [PATCH 0306/1710] REVIEWED: `GetWindowHandle()` #2950 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 4efb20179..4f3d98b0f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1713,7 +1713,7 @@ void *GetWindowHandle(void) // NOTE: Returned handle is: void *HWND (windows.h) return glfwGetWin32Window(CORE.Window.handle); #endif -#if defined(__linux__) && !defined(PLATFORM_DRM) +#if defined(PLATFORM_DESKTOP) && defined(__linux__) // NOTE: Returned handle is: unsigned long Window (X.h) // typedef unsigned long XID; // typedef XID Window; From 68ee0bb8dd43074574dab08d836eaeba58bd0351 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 12:33:49 +0100 Subject: [PATCH 0307/1710] ADDED: QOA music streaming (with auxiliar lib) Some format tweaks --- src/external/qoaplay.c | 278 +++++++++++++++++++++++++++++++++++++++++ src/raudio.c | 157 +++++++++++------------ 2 files changed, 357 insertions(+), 78 deletions(-) create mode 100644 src/external/qoaplay.c diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c new file mode 100644 index 000000000..b4dc09c7e --- /dev/null +++ b/src/external/qoaplay.c @@ -0,0 +1,278 @@ +/******************************************************************************************* +* +* qoaplay - QOA stream playing helper functions +* +* qoaplay is a tiny abstraction to read and decode a QOA file "on the fly". +* It reads and decodes one frame at a time with minimal memory requirements. +* qoaplay also provides some functions to seek to a specific frame. +* +* LICENSE: MIT License +* +* Copyright (c) 2023 Dominic Szablewski (@phoboslab), 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. +* +**********************************************************************************************/ + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// QOA streaming data descriptor +typedef struct { + qoa_desc info; // QOA descriptor data + + FILE *file; // QOA file to read, if NULL, using memory buffer -> file_data + unsigned char *file_data; // QOA file data on memory + unsigned int file_data_size; // QOA file data on memory size + unsigned int file_data_offset; // QOA file data on memory offset for next read + + unsigned int first_frame_pos; // First frame position (after QOA header, required for offset) + 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 + unsigned int sample_data_pos; // Sample data decoded position + +} qoaplay_desc; + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +qoaplay_desc *qoaplay_open(char *path); +qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size); +void qoaplay_close(qoaplay_desc *qoa_ctx); + +void qoaplay_rewind(qoaplay_desc *qoa_ctx); +void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame); +unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples); +unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx); +double qoaplay_get_duration(qoaplay_desc *qoa_ctx); +double qoaplay_get_time(qoaplay_desc *qoa_ctx); +int qoaplay_get_frame(qoaplay_desc *qoa_ctx); + +#if defined(__cplusplus) +} // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Open QOA file, keep FILE pointer to keep reading from file +qoaplay_desc *qoaplay_open(char *path) +{ + FILE *file = fopen(path, "rb"); + if (!file) return NULL; + + // Read and decode the file header + unsigned char header[QOA_MIN_FILESIZE]; + int read = fread(header, QOA_MIN_FILESIZE, 1, file); + if (!read) return NULL; + + qoa_desc qoa; + unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa); + if (!first_frame_pos) return NULL; + + // Rewind the file back to beginning of the first frame + fseek(file, first_frame_pos, SEEK_SET); + + // 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); + 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->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->info.channels = qoa.channels; + qoa_ctx->info.samplerate = qoa.samplerate; + qoa_ctx->info.samples = qoa.samples; + + return qoa_ctx; +} + +// 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 (!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); + 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); + memcpy(qoa_ctx->file_data, data, data_size); + qoa_ctx->file_data_size = data_size; + qoa_ctx->file_data_offset = 0; + 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->info.channels = qoa.channels; + qoa_ctx->info.samplerate = qoa.samplerate; + qoa_ctx->info.samples = qoa.samples; + + return qoa_ctx; +} + +// Close QOA file (if open) and free internal memory +void qoaplay_close(qoaplay_desc *qoa_ctx) +{ + 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_FREE(qoa_ctx); +} + +// 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); + 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; + } + + unsigned int frame_len; + qoa_decode_frame(qoa_ctx->buffer, qoa_ctx->buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len); + qoa_ctx->sample_data_pos = 0; + qoa_ctx->sample_data_len = frame_len; + + return frame_len; +} + +// Rewind QOA file or memory pointer to beginning +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; + + qoa_ctx->sample_position = 0; + qoa_ctx->sample_data_len = 0; + qoa_ctx->sample_data_pos = 0; +} + +// Decode required QOA frames +unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples) +{ + int src_index = qoa_ctx->sample_data_pos*qoa_ctx->info.channels; + int dst_index = 0; + + for (int i = 0; i < num_samples; i++) + { + // Do we have to decode more samples? + if (qoa_ctx->sample_data_len - qoa_ctx->sample_data_pos == 0) + { + if (!qoaplay_decode_frame(qoa_ctx)) + { + // Loop to the beginning + qoaplay_rewind(qoa_ctx); + qoaplay_decode_frame(qoa_ctx); + } + + src_index = 0; + } + + // Normalize to -1..1 floats and write to dest + for (int c = 0; c < qoa_ctx->info.channels; c++) + { + sample_data[dst_index++] = qoa_ctx->sample_data[src_index++]/32768.0; + } + + qoa_ctx->sample_data_pos++; + qoa_ctx->sample_position++; + } + + return num_samples; +} + +// Get QOA total time duration in seconds +double qoaplay_get_duration(qoaplay_desc *qoa_ctx) +{ + return (double)qoa_ctx->info.samples/(double)qoa_ctx->info.samplerate; +} + +// Get QOA current time position in seconds +double qoaplay_get_time(qoaplay_desc *qoa_ctx) +{ + return (double)qoa_ctx->sample_position/(double)qoa_ctx->info.samplerate; +} + +// Get QOA current audio frame +int qoaplay_get_frame(qoaplay_desc *qoa_ctx) +{ + return qoa_ctx->sample_position/QOA_FRAME_LEN; +} + +// Seek QOA audio frame +void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame) +{ + if (frame < 0) frame = 0; + + if (frame > qoa_ctx->info.samples/QOA_FRAME_LEN) frame = qoa_ctx->info.samples/QOA_FRAME_LEN; + + qoa_ctx->sample_position = frame*QOA_FRAME_LEN; + qoa_ctx->sample_data_len = 0; + qoa_ctx->sample_data_pos = 0; + + unsigned int offset = qoa_ctx->first_frame_pos + frame*qoa_max_frame_size(&qoa_ctx->info); + + if (qoa_ctx->file) fseek(qoa_ctx->file, offset, SEEK_SET); + else qoa_ctx->file_data_offset = offset; +} diff --git a/src/raudio.c b/src/raudio.c index 207c0c490..03e4bcbaa 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -230,6 +230,7 @@ typedef struct tagBITMAPINFOHEADER { #define QOA_IMPLEMENTATION #include "external/qoa.h" // QOA loading and saving functions + #include "external/qoaplay.c" // QOA stream playing helper functions #endif #if defined(SUPPORT_FILEFORMAT_FLAC) @@ -287,21 +288,6 @@ typedef struct tagBITMAPINFOHEADER { //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- - -// Music context type -// NOTE: Depends on data structure provided by the library -// in charge of reading the different file types -typedef enum { - MUSIC_AUDIO_NONE = 0, // No audio context loaded - MUSIC_AUDIO_WAV, // WAV audio context - MUSIC_AUDIO_OGG, // OGG audio context - MUSIC_AUDIO_FLAC, // FLAC audio context - MUSIC_AUDIO_MP3, // MP3 audio context - MUSIC_AUDIO_QOA, // QOA audio context - MUSIC_MODULE_XM, // XM module audio context - MUSIC_MODULE_MOD // MOD module audio context -} MusicContextType; - #if defined(RAUDIO_STANDALONE) // Trace log level // NOTE: Organized by priority level @@ -317,6 +303,20 @@ typedef enum { } TraceLogLevel; #endif +// Music context type +// NOTE: Depends on data structure provided by the library +// in charge of reading the different file types +typedef enum { + MUSIC_AUDIO_NONE = 0, // No audio context loaded + MUSIC_AUDIO_WAV, // WAV audio context + MUSIC_AUDIO_OGG, // OGG audio context + MUSIC_AUDIO_FLAC, // FLAC audio context + MUSIC_AUDIO_MP3, // MP3 audio context + MUSIC_AUDIO_QOA, // QOA audio context + MUSIC_MODULE_XM, // XM module audio context + MUSIC_MODULE_MOD // MOD module audio context +} MusicContextType; + // NOTE: Different logic is used when feeding data to the playback device // depending on whether data is streamed (Music vs Sound) typedef enum { @@ -1322,7 +1322,7 @@ void UnloadWaveSamples(float *samples) } //---------------------------------------------------------------------------------- -// Module Functions Definition - Music loading and stream playing (.OGG) +// Module Functions Definition - Music loading and stream playing //---------------------------------------------------------------------------------- // Load music stream from file @@ -1395,21 +1395,16 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { - qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); - - // TODO: QOA stream support: Init context from file - int result = 0; - + qoaplay_desc *ctxQoa = qoaplay_open(fileName); music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; - if (result > 0) + if (ctxQoa->file != NULL) { - music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels); - - // TODO: Read next frame(s) from QOA stream - //music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len); - + // NOTE: We are loading samples are 32bit float normalized data, so, + // we configure the output audio stream to also use float 32bit + music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); + music.frameCount = ctxQoa->info.samples; music.looping = true; // Looping enabled by default musicLoaded = true; } @@ -1594,21 +1589,16 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, #if defined(SUPPORT_FILEFORMAT_QOA) else if (strcmp(fileType, ".qoa") == 0) { - qoa_desc *ctxQoa = RL_CALLOC(1, sizeof(qoa_desc)); - - // TODO: Init QOA context data - int result = 0; - + qoaplay_desc *ctxQoa = qoaplay_open_memory(data, dataSize); music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; - if (result > 0) + if ((ctxQoa->file_data != NULL) && (ctxQoa->file_data_size != 0)) { - music.stream = LoadAudioStream(ctxQoa->samplerate, 16, ctxQoa->channels); - - // TODO: Read next frame(s) from QOA stream - //music.frameCount = qoa_decode_frame(const unsigned char *bytes, unsigned int size, ctxQoa, short *sample_data, unsigned int *frame_len); - + // NOTE: We are loading samples are 32bit float normalized data, so, + // we configure the output audio stream to also use float 32bit + music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); + music.frameCount = ctxQoa->info.samples; music.looping = true; // Looping enabled by default musicLoaded = true; } @@ -1697,27 +1687,27 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, if (!musicLoaded) { if (false) { } - #if defined(SUPPORT_FILEFORMAT_WAV) +#if defined(SUPPORT_FILEFORMAT_WAV) else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); - #endif - #if defined(SUPPORT_FILEFORMAT_OGG) +#endif +#if defined(SUPPORT_FILEFORMAT_OGG) else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); - #endif - #if defined(SUPPORT_FILEFORMAT_MP3) +#endif +#if defined(SUPPORT_FILEFORMAT_MP3) else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } - #endif - #if defined(SUPPORT_FILEFORMAT_QOA) - else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context*/ RL_FREE(music.ctxData); } - #endif - #if defined(SUPPORT_FILEFORMAT_FLAC) +#endif +#if defined(SUPPORT_FILEFORMAT_QOA) + 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); - #endif - #if defined(SUPPORT_FILEFORMAT_XM) +#endif +#if defined(SUPPORT_FILEFORMAT_XM) else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); - #endif - #if defined(SUPPORT_FILEFORMAT_MOD) +#endif +#if defined(SUPPORT_FILEFORMAT_MOD) else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } - #endif +#endif music.ctxData = NULL; TRACELOG(LOG_WARNING, "FILEIO: Music data could not be loaded"); @@ -1763,7 +1753,7 @@ void UnloadMusicStream(Music music) else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif #if defined(SUPPORT_FILEFORMAT_QOA) - else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context*/ RL_FREE(music.ctxData); } + 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); @@ -1821,7 +1811,7 @@ void StopMusicStream(Music music) case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break; #endif #if defined(SUPPORT_FILEFORMAT_QOA) - case MUSIC_AUDIO_QOA: /*TODO: Restart QOA context to beginning*/ break; + case MUSIC_AUDIO_QOA: qoaplay_rewind((qoaplay_desc *)music.ctxData); break; #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break; @@ -1856,7 +1846,7 @@ void SeekMusicStream(Music music, float position) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; #endif #if defined(SUPPORT_FILEFORMAT_QOA) - case MUSIC_AUDIO_QOA: /*TODO: Seek to specific QOA frame*/ break; + case MUSIC_AUDIO_QOA: qoaplay_seek_frame((qoaplay_desc *)music.ctxData, positionInFrames); break; #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; @@ -1892,11 +1882,13 @@ void UpdateMusicStream(Music music) 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; int frameCountStillNeeded = framesToStream; - int frameCountRedTotal = 0; + int frameCountReadTotal = 0; + switch (music.ctxType) { #if defined(SUPPORT_FILEFORMAT_WAV) @@ -1906,8 +1898,8 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); - frameCountRedTotal += frameCountRed; + int frameCountRed = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); + frameCountReadTotal += frameCountRed; frameCountStillNeeded -= frameCountRed; if (frameCountStillNeeded == 0) break; else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); @@ -1917,8 +1909,8 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); - frameCountRedTotal += frameCountRed; + int frameCountRed = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); + frameCountReadTotal += frameCountRed; frameCountStillNeeded -= frameCountRed; if (frameCountStillNeeded == 0) break; else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); @@ -1931,8 +1923,8 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize), frameCountStillNeeded*music.stream.channels); - frameCountRedTotal += frameCountRed; + int frameCountRed = stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize), frameCountStillNeeded*music.stream.channels); + frameCountReadTotal += frameCountRed; frameCountStillNeeded -= frameCountRed; if (frameCountStillNeeded == 0) break; else stb_vorbis_seek_start((stb_vorbis *)music.ctxData); @@ -1944,9 +1936,9 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = (int)drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); - frameCountRedTotal += frameCountRed; - frameCountStillNeeded -= frameCountRed; + int frameCountRead = (int)drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); + frameCountReadTotal += frameCountRead; + frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; else drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); } @@ -1955,7 +1947,18 @@ void UpdateMusicStream(Music music) #if defined(SUPPORT_FILEFORMAT_QOA) case MUSIC_AUDIO_QOA: { - // TODO: Read QOA required framecount to fill buffer to keep music playing + unsigned int frameCountRead = qoaplay_decode((qoaplay_desc *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream); + frameCountReadTotal += frameCountRead; + /* + while (true) + { + int frameCountRead = (int)qoaplay_decode((qoaplay_desc *)music.ctxData, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize), frameCountStillNeeded); + frameCountReadTotal += frameCountRead; + frameCountStillNeeded -= frameCountRead; + if (frameCountStillNeeded == 0) break; + else qoaplay_rewind((qoaplay_desc *)music.ctxData); + } + */ } break; #endif #if defined(SUPPORT_FILEFORMAT_FLAC) @@ -1964,8 +1967,8 @@ void UpdateMusicStream(Music music) while (true) { int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); - frameCountRedTotal += frameCountRed; - frameCountStillNeeded -= frameCountRed; + frameCountReadTotal += frameCountRead; + frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; else drflac__seek_to_first_frame((drflac *)music.ctxData); } @@ -1978,7 +1981,6 @@ void UpdateMusicStream(Music music) if (AUDIO_DEVICE_FORMAT == ma_format_f32) jar_xm_generate_samples((jar_xm_context_t *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream); else if (AUDIO_DEVICE_FORMAT == ma_format_s16) jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream); else if (AUDIO_DEVICE_FORMAT == ma_format_u8) jar_xm_generate_samples_8bit((jar_xm_context_t *)music.ctxData, (char *)AUDIO.System.pcmBuffer, framesToStream); - //jar_xm_reset((jar_xm_context_t *)music.ctxData); } break; @@ -1988,7 +1990,6 @@ void UpdateMusicStream(Music music) { // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2 jar_mod_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream, 0); - //jar_mod_seek_start((jar_mod_context_t *)music.ctxData); } break; @@ -2056,7 +2057,7 @@ float GetMusicTimePlayed(Music music) float secondsPlayed = 0.0f; if (music.stream.buffer != NULL) { - #if defined(SUPPORT_FILEFORMAT_XM) +#if defined(SUPPORT_FILEFORMAT_XM) if (music.ctxType == MUSIC_MODULE_XM) { uint64_t framesPlayed = 0; @@ -2065,7 +2066,7 @@ float GetMusicTimePlayed(Music music) secondsPlayed = (float)framesPlayed/music.stream.sampleRate; } else - #endif +#endif { //ma_uint32 frameSizeInBytes = ma_get_bytes_per_sample(music.stream.buffer->dsp.formatConverterIn.config.formatIn)*music.stream.buffer->dsp.formatConverterIn.config.channels; int framesProcessed = (int)music.stream.buffer->framesProcessed; @@ -2115,7 +2116,7 @@ AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, un } // Checks if an audio stream is ready -RLAPI bool IsAudioStreamReady(AudioStream stream) +bool IsAudioStreamReady(AudioStream stream) { return ((stream.buffer != NULL) && // Validate stream buffer (stream.sampleRate > 0) && // Validate sample rate is supported @@ -2277,6 +2278,7 @@ void AttachAudioStreamProcessor(AudioStream stream, AudioCallback process) ma_mutex_unlock(&AUDIO.System.lock); } +// Remove processor from audio stream void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process) { ma_mutex_lock(&AUDIO.System.lock); @@ -2304,9 +2306,8 @@ void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process) } // Add processor to audio pipeline. Order of processors is important -// Works the same way as {Attach,Detach}AudioStreamProcessor functions, except -// these two work on the already mixed output just before sending it to the -// sound hardware. +// Works the same way as {Attach,Detach}AudioStreamProcessor() functions, except +// these two work on the already mixed output just before sending it to the sound hardware void AttachAudioMixedProcessor(AudioCallback process) { ma_mutex_lock(&AUDIO.System.lock); @@ -2330,6 +2331,7 @@ void AttachAudioMixedProcessor(AudioCallback process) ma_mutex_unlock(&AUDIO.System.lock); } +// Remove processor from audio pipeline void DetachAudioMixedProcessor(AudioCallback process) { ma_mutex_lock(&AUDIO.System.lock); @@ -2508,7 +2510,6 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f return totalOutputFramesProcessed; } - // Sending audio data to device callback function // This function will be called when miniaudio needs more data // NOTE: All the mixing takes place here From aae7ab64c7a6b4e7e040b21736710843086b35a0 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 13:00:30 +0100 Subject: [PATCH 0308/1710] Update raudio.c --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 03e4bcbaa..8bd9052c2 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1966,7 +1966,7 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountRedTotal*frameSize)); + int frameCountRead = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); frameCountReadTotal += frameCountRead; frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; From cf1ebada0e0c18a02d48f091091418ce7b020b1e Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 13:15:13 +0100 Subject: [PATCH 0309/1710] Tweak `WindowDropCallback()` #2943 --- src/rcore.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 4f3d98b0f..071915cf7 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5598,25 +5598,28 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) // GLFW3 Window Drop Callback, runs when drop files into window static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) { - // In case previous dropped filepaths have not been freed, we free them - if (CORE.Window.dropFileCount > 0) + if (count > 0) { - for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); + // 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); + RL_FREE(CORE.Window.dropFilepaths); - CORE.Window.dropFileCount = 0; - CORE.Window.dropFilepaths = NULL; - } + 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 *)); + // 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)); - strcpy(CORE.Window.dropFilepaths[i], paths[i]); + 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]); + } } } #endif From 614e0518a7740bb900539e8c92517acaf052232c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 14:58:58 +0100 Subject: [PATCH 0310/1710] Remove trailing spaces --- examples/Makefile | 10 +++++----- examples/core/core_drop_files.c | 4 ++-- src/external/qoaplay.c | 32 ++++++++++++++++---------------- src/rcamera.h | 12 ++++++------ src/rcore.c | 6 +++--- src/rtextures.c | 10 +++++----- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 574975d23..0b524015f 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -297,17 +297,17 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # --preload-file resources # specify a resources folder for data compilation # --source-map-base # allow debugging in browser with source map LDFLAGS += -s USE_GLFW=3 -s TOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -s FORCE_FILESYSTEM=1 - + # Build using asyncify ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) LDFLAGS += -s ASYNCIFY endif - + # Add resources building if required ifeq ($(BUILD_WEB_RESOURCES),TRUE) LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) endif - + # Add debug mode flags if required ifeq ($(BUILD_MODE),DEBUG) LDFLAGS += -s ASSERTIONS=1 --profiling @@ -316,7 +316,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # Define a custom shell .html and output extension LDFLAGS += --shell-file $(BUILD_WEB_SHELL) EXT = .html - + # NOTE: Simple raylib examples are compiled to be interpreter with asyncify, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger # projects, code needs to be refactored to avoid a blocking while() loop, moving Update and Draw @@ -354,7 +354,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(RAYLIB_LIBTYPE),SHARED) LDLIBS += -lc endif - + # NOTE: On ARM 32bit arch, miniaudio requires atomics library LDLIBS += -latomic endif diff --git a/examples/core/core_drop_files.c b/examples/core/core_drop_files.c index b591cdea7..9f6220393 100644 --- a/examples/core/core_drop_files.c +++ b/examples/core/core_drop_files.c @@ -34,7 +34,7 @@ int main(void) int filePathCounter = 0; char *filePaths[MAX_FILEPATH_RECORDED] = { 0 }; // We will register a maximum of filepaths - + // Allocate space for the required file paths for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) { @@ -94,7 +94,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) + for (int i = 0; i < MAX_FILEPATH_RECORDED; i++) { RL_FREE(filePaths[i]); // Free allocated memory for all filepaths } diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index b4dc09c7e..466d13319 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -7,19 +7,19 @@ * qoaplay also provides some functions to seek to a specific frame. * * LICENSE: MIT License -* +* * Copyright (c) 2023 Dominic Szablewski (@phoboslab), 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 @@ -121,7 +121,7 @@ qoaplay_desc *qoaplay_open(char *path) qoa_ctx->info.channels = qoa.channels; qoa_ctx->info.samplerate = qoa.samplerate; qoa_ctx->info.samples = qoa.samples; - + return qoa_ctx; } @@ -174,15 +174,15 @@ void qoaplay_close(qoaplay_desc *qoa_ctx) QOA_FREE(qoa_ctx->file_data); qoa_ctx->file_data_size = 0; } - + QOA_FREE(qoa_ctx); } // 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); - else + 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); @@ -193,14 +193,14 @@ unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx) qoa_decode_frame(qoa_ctx->buffer, qoa_ctx->buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len); qoa_ctx->sample_data_pos = 0; qoa_ctx->sample_data_len = frame_len; - + return frame_len; } // Rewind QOA file or memory pointer to beginning void qoaplay_rewind(qoaplay_desc *qoa_ctx) { - if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET); + if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET); else qoa_ctx->file_data_offset = 0; qoa_ctx->sample_position = 0; @@ -213,7 +213,7 @@ unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_s { int src_index = qoa_ctx->sample_data_pos*qoa_ctx->info.channels; int dst_index = 0; - + for (int i = 0; i < num_samples; i++) { // Do we have to decode more samples? @@ -225,20 +225,20 @@ unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_s qoaplay_rewind(qoa_ctx); qoaplay_decode_frame(qoa_ctx); } - + src_index = 0; } // Normalize to -1..1 floats and write to dest - for (int c = 0; c < qoa_ctx->info.channels; c++) + for (int c = 0; c < qoa_ctx->info.channels; c++) { sample_data[dst_index++] = qoa_ctx->sample_data[src_index++]/32768.0; } - + qoa_ctx->sample_data_pos++; qoa_ctx->sample_position++; } - + return num_samples; } @@ -272,7 +272,7 @@ void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame) qoa_ctx->sample_data_pos = 0; unsigned int offset = qoa_ctx->first_frame_pos + frame*qoa_max_frame_size(&qoa_ctx->info); - + if (qoa_ctx->file) fseek(qoa_ctx->file, offset, SEEK_SET); else qoa_ctx->file_data_offset = offset; } diff --git a/src/rcamera.h b/src/rcamera.h index 6aee26f38..c3a101f0b 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -160,7 +160,7 @@ Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); // MatrixOrtho() // MatrixIdentity() -// raylib required functionality: +// raylib required functionality: // GetMouseDelta() // GetMouseWheelMove() // IsKeyDown() @@ -223,7 +223,7 @@ Vector3 GetCameraRight(Camera *camera) { Vector3 forward = GetCameraForward(camera); Vector3 up = GetCameraUp(camera); - + return Vector3CrossProduct(forward, up); } @@ -251,7 +251,7 @@ void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane) void CameraMoveUp(Camera *camera, float distance) { Vector3 up = GetCameraUp(camera); - + // Scale by distance up = Vector3Scale(up, distance); @@ -410,7 +410,7 @@ Matrix GetCameraProjectionMatrix(Camera *camera, float aspect) return MatrixOrtho(-right, right, -top, top, CAMERA_CULL_DISTANCE_NEAR, CAMERA_CULL_DISTANCE_FAR); } - + return MatrixIdentity(); } @@ -425,7 +425,7 @@ void UpdateCamera(Camera *camera, int mode) bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); bool lockView = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); bool rotateUp = (mode == CAMERA_FREE); - + if (mode == CAMERA_ORBITAL) { // Orbital can just orbit @@ -446,7 +446,7 @@ void UpdateCamera(Camera *camera, int mode) CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); - + // Camera movement if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); diff --git a/src/rcore.c b/src/rcore.c index 071915cf7..f24ae012c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1555,7 +1555,7 @@ void ClearWindowState(unsigned int flags) // Set icon for window (only PLATFORM_DESKTOP) // NOTE 1: Image must be in RGBA format, 8bit per channel -// NOTE 2: Image is scaled by the OS for all required sizes +// NOTE 2: Image is scaled by the OS for all required sizes void SetWindowIcon(Image image) { #if defined(PLATFORM_DESKTOP) @@ -1586,7 +1586,7 @@ void SetWindowIcon(Image image) // Set icon for window (multiple images, only PLATFORM_DESKTOP) // NOTE 1: Images must be in RGBA format, 8bit per channel // NOTE 2: The multiple images are used depending on provided sizes -// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 +// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 void SetWindowIcons(Image *images, int count) { #if defined(PLATFORM_DESKTOP) @@ -2580,7 +2580,7 @@ bool IsShaderReady(Shader shader) // 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 diff --git a/src/rtextures.c b/src/rtextures.c index f9b5c862d..56b200ce8 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -320,7 +320,7 @@ Image LoadImageAnim(const char *fileName, int *frames) #else if (false) { } #endif - else + else { image = LoadImage(fileName); frameCount = 1; @@ -507,7 +507,7 @@ Image LoadImageFromScreen(void) bool IsImageReady(Image image) { return ((image.data != NULL) && // Validate pixel data available - (image.width > 0) && + (image.width > 0) && (image.height > 0) && // Validate image size (image.format > 0) && // Validate image format (image.mipmaps > 0)); // Validate image mipmaps (at least 1 for basic mipmap level) @@ -3340,10 +3340,10 @@ RenderTexture2D LoadRenderTexture(int width, int height) bool IsTextureReady(Texture2D texture) { // TODO: Validate maximum texture size supported by GPU? - + return ((texture.id > 0) && // Validate OpenGL id - (texture.width > 0) && - (texture.height > 0) && // Validate texture size + (texture.width > 0) && + (texture.height > 0) && // Validate texture size (texture.format > 0) && // Validate texture pixel format (texture.mipmaps > 0)); // Validate texture mipmaps (at least 1 for basic mipmap level) } From 8f7e2cd1791414f55e8442186b1b32557704f1b5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 6 Mar 2023 15:05:57 +0100 Subject: [PATCH 0311/1710] Replace TABS by 4 spaces --- src/external/qoaplay.c | 222 ++++++++++++++++++++--------------------- 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index 466d13319..549e9f778 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -35,22 +35,22 @@ //---------------------------------------------------------------------------------- // QOA streaming data descriptor typedef struct { - qoa_desc info; // QOA descriptor data - + qoa_desc info; // QOA descriptor data + FILE *file; // QOA file to read, if NULL, using memory buffer -> file_data unsigned char *file_data; // QOA file data on memory unsigned int file_data_size; // QOA file data on memory size unsigned int file_data_offset; // QOA file data on memory offset for next read - unsigned int first_frame_pos; // First frame position (after QOA header, required for offset) - unsigned int sample_position; // Current streaming sample position + unsigned int first_frame_pos; // First frame position (after QOA header, required for offset) + 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 + 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 - unsigned int sample_data_pos; // Sample data decoded position + short *sample_data; // Sample data decoded + unsigned int sample_data_len; // Sample data decoded length + unsigned int sample_data_pos; // Sample data decoded position } qoaplay_desc; @@ -85,97 +85,97 @@ int qoaplay_get_frame(qoaplay_desc *qoa_ctx); // Open QOA file, keep FILE pointer to keep reading from file qoaplay_desc *qoaplay_open(char *path) { - FILE *file = fopen(path, "rb"); - if (!file) return NULL; + FILE *file = fopen(path, "rb"); + if (!file) return NULL; - // Read and decode the file header - unsigned char header[QOA_MIN_FILESIZE]; - int read = fread(header, QOA_MIN_FILESIZE, 1, file); - if (!read) return NULL; + // Read and decode the file header + unsigned char header[QOA_MIN_FILESIZE]; + int read = fread(header, QOA_MIN_FILESIZE, 1, file); + if (!read) return NULL; - qoa_desc qoa; - unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa); - if (!first_frame_pos) return NULL; + qoa_desc qoa; + unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa); + if (!first_frame_pos) return NULL; - // Rewind the file back to beginning of the first frame - fseek(file, first_frame_pos, SEEK_SET); + // Rewind the file back to beginning of the first frame + fseek(file, first_frame_pos, SEEK_SET); - // Allocate one chunk of memory for the qoaplay_desc struct + // 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); - 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->first_frame_pos = first_frame_pos; + 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); + 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->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); + // 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->info.channels = qoa.channels; - qoa_ctx->info.samplerate = qoa.samplerate; - qoa_ctx->info.samples = qoa.samples; + qoa_ctx->info.channels = qoa.channels; + qoa_ctx->info.samplerate = qoa.samplerate; + qoa_ctx->info.samples = qoa.samples; - return qoa_ctx; + return qoa_ctx; } // 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); + // 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 (!first_frame_pos) return NULL; + qoa_desc qoa; + unsigned int first_frame_pos = qoa_decode_header(header, 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); - memset(qoa_ctx, 0, sizeof(qoaplay_desc)); + // 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); + memset(qoa_ctx, 0, sizeof(qoaplay_desc)); - qoa_ctx->file = NULL; + 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); - memcpy(qoa_ctx->file_data, data, data_size); - qoa_ctx->file_data_size = data_size; - qoa_ctx->file_data_offset = 0; - qoa_ctx->first_frame_pos = first_frame_pos; + // Keep a copy of file data provided to be managed internally + qoa_ctx->file_data = (unsigned char *)QOA_MALLOC(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->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); + // 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->info.channels = qoa.channels; - qoa_ctx->info.samplerate = qoa.samplerate; - qoa_ctx->info.samples = qoa.samples; + qoa_ctx->info.channels = qoa.channels; + qoa_ctx->info.samplerate = qoa.samplerate; + qoa_ctx->info.samples = qoa.samples; - return qoa_ctx; + return qoa_ctx; } // Close QOA file (if open) and free internal memory void qoaplay_close(qoaplay_desc *qoa_ctx) { - if (qoa_ctx->file) fclose(qoa_ctx->file); + 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; - } + if ((qoa_ctx->file_data) && (qoa_ctx->file_data_size > 0)) + { + QOA_FREE(qoa_ctx->file_data); + qoa_ctx->file_data_size = 0; + } - QOA_FREE(qoa_ctx); + QOA_FREE(qoa_ctx); } // Decode one frame from QOA data @@ -189,90 +189,90 @@ unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx) qoa_ctx->file_data_offset += qoa_ctx->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_ctx->sample_data_pos = 0; - qoa_ctx->sample_data_len = frame_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_ctx->sample_data_pos = 0; + qoa_ctx->sample_data_len = frame_len; - return frame_len; + return frame_len; } // Rewind QOA file or memory pointer to beginning void qoaplay_rewind(qoaplay_desc *qoa_ctx) { - if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET); + if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET); else qoa_ctx->file_data_offset = 0; - qoa_ctx->sample_position = 0; - qoa_ctx->sample_data_len = 0; - qoa_ctx->sample_data_pos = 0; + qoa_ctx->sample_position = 0; + qoa_ctx->sample_data_len = 0; + qoa_ctx->sample_data_pos = 0; } // Decode required QOA frames unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples) { - int src_index = qoa_ctx->sample_data_pos*qoa_ctx->info.channels; - int dst_index = 0; + int src_index = qoa_ctx->sample_data_pos*qoa_ctx->info.channels; + int dst_index = 0; - for (int i = 0; i < num_samples; i++) + for (int i = 0; i < num_samples; i++) { - // Do we have to decode more samples? - if (qoa_ctx->sample_data_len - qoa_ctx->sample_data_pos == 0) + // Do we have to decode more samples? + if (qoa_ctx->sample_data_len - qoa_ctx->sample_data_pos == 0) { - if (!qoaplay_decode_frame(qoa_ctx)) + if (!qoaplay_decode_frame(qoa_ctx)) { - // Loop to the beginning - qoaplay_rewind(qoa_ctx); - qoaplay_decode_frame(qoa_ctx); - } + // Loop to the beginning + qoaplay_rewind(qoa_ctx); + qoaplay_decode_frame(qoa_ctx); + } - src_index = 0; - } + src_index = 0; + } - // Normalize to -1..1 floats and write to dest - for (int c = 0; c < qoa_ctx->info.channels; c++) + // Normalize to -1..1 floats and write to dest + for (int c = 0; c < qoa_ctx->info.channels; c++) { - sample_data[dst_index++] = qoa_ctx->sample_data[src_index++]/32768.0; - } + sample_data[dst_index++] = qoa_ctx->sample_data[src_index++]/32768.0; + } - qoa_ctx->sample_data_pos++; - qoa_ctx->sample_position++; - } + qoa_ctx->sample_data_pos++; + qoa_ctx->sample_position++; + } - return num_samples; + return num_samples; } // Get QOA total time duration in seconds double qoaplay_get_duration(qoaplay_desc *qoa_ctx) { - return (double)qoa_ctx->info.samples/(double)qoa_ctx->info.samplerate; + return (double)qoa_ctx->info.samples/(double)qoa_ctx->info.samplerate; } // Get QOA current time position in seconds double qoaplay_get_time(qoaplay_desc *qoa_ctx) { - return (double)qoa_ctx->sample_position/(double)qoa_ctx->info.samplerate; + return (double)qoa_ctx->sample_position/(double)qoa_ctx->info.samplerate; } // Get QOA current audio frame int qoaplay_get_frame(qoaplay_desc *qoa_ctx) { - return qoa_ctx->sample_position/QOA_FRAME_LEN; + return qoa_ctx->sample_position/QOA_FRAME_LEN; } // Seek QOA audio frame void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame) { - if (frame < 0) frame = 0; + if (frame < 0) frame = 0; - if (frame > qoa_ctx->info.samples/QOA_FRAME_LEN) frame = qoa_ctx->info.samples/QOA_FRAME_LEN; + if (frame > qoa_ctx->info.samples/QOA_FRAME_LEN) frame = qoa_ctx->info.samples/QOA_FRAME_LEN; - qoa_ctx->sample_position = frame*QOA_FRAME_LEN; - qoa_ctx->sample_data_len = 0; - qoa_ctx->sample_data_pos = 0; + qoa_ctx->sample_position = frame*QOA_FRAME_LEN; + qoa_ctx->sample_data_len = 0; + qoa_ctx->sample_data_pos = 0; - unsigned int offset = qoa_ctx->first_frame_pos + frame*qoa_max_frame_size(&qoa_ctx->info); + unsigned int offset = qoa_ctx->first_frame_pos + frame*qoa_max_frame_size(&qoa_ctx->info); - if (qoa_ctx->file) fseek(qoa_ctx->file, offset, SEEK_SET); + if (qoa_ctx->file) fseek(qoa_ctx->file, offset, SEEK_SET); else qoa_ctx->file_data_offset = offset; } From 0b18882a949fb763f03e87d8139d702107223ee0 Mon Sep 17 00:00:00 2001 From: Sid K <58383260+Skaytacium@users.noreply.github.com> Date: Tue, 7 Mar 2023 01:40:18 +0530 Subject: [PATCH 0312/1710] fix(build): Fix DLL build on Windows (#2951) Changes the DLL export condition to apply to platform WIN32 instead of compiler MSVC --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 690f970a4..9087f8eea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,7 +62,7 @@ if (NOT BUILD_SHARED_LIBS) add_library(raylib_static ALIAS raylib) else() MESSAGE(STATUS "Building raylib shared library") - if (MSVC) + if (WIN32) target_compile_definitions(raylib PRIVATE $ INTERFACE $ From 3a332d241c1c675c8b581fe3a29e328983642fe4 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 Mar 2023 11:32:37 +0100 Subject: [PATCH 0313/1710] Update CHANGELOG --- CHANGELOG | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8cbbaa2b1..091425582 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,10 +9,11 @@ Release: raylib 4.5 (xx February 2023) -WIP- KEY CHANGES: - ADDED: M3D model format support with animations - ADDED: GLTF animation support - - ADDED: QOA audio format support (import/export) -WIP- - - rlgl redesign to avoid render batch triangles limits pre-check: rlCheckRenderBatchLimit() - - rshapes simplification to minimize the requirement of rlgl functionality, now it only depends on 6 functions - - rl_gputex.h: Compressed textures loading, required by rtextures module, has been moved to a separate self-contained library + - ADDED: QOA audio format support (import/export) + - REDESIGNED: rcamera module, new implementation from scratch, simpler and more extendable + - REDESIGNED: rlgl module to avoid render batch triangles limits pre-check: rlCheckRenderBatchLimit() + - REDESIGNED: rshapes module to minimize the rlgl dependency, now only 6 low-level functions required! + - ADDED: rl_gputex self-contained module for compressed textures loading, used by rtextures module - raygui 3.5: New version of the immediate-mode gui system for tools development with raylib Detailed changes: @@ -20,6 +21,7 @@ Detailed changes: [core] ADDED: Basic gamepad support for Android (#2709) by @deniska [core] ADDED: Support CAPS/NUM lock keys registering if locked [core] ADDED: _GNU_SOURCE define on Linux (#2729) +[core] ADDED: SetWindowIcons() to set multiple icon image sizes [core] `WARNING`: RENAMED: Exported raylib version symbol to raylib_version #2671 [core] REMOVED: Touch points on touch up events on Android (#2711) by @deniska [core] REVIEWED: Window position setup on InitWindow() (#2732) by @RandomErrorMessage @@ -43,6 +45,14 @@ Detailed changes: [core] REVIEWED: Initial window position for display-sized fullscreen (#2742) by @daipom [core] REVIEWED: Sticky touches input (#2857) by @ImazighenGhost [core] REVIEWED: Enable GetWindowHandle() on macOS (#2915) by @Not-Nik +[core] REVIEWED: Window position always inits centered in current monitor +[core] REVIEWED: IsWindowFocused() to consider Android App state (#2935) +[core] REVIEWED: GetMonitorWidth() and GetMonitorHeight() (#2934) +[core] REVIEWED: GetWindowHandle() to return Linux window (#2938) +[core] REVIEWED: WindowDropCallback(), additional security check (#2943) +[rcamera] REDESIGNED: New implementation from scratch (#2563) by @Crydsch +[rcamera] REVIEWED: Make orbital camera work as expected (#2926) by @JeffM2501 +[rcamera] REVIEWED: Multiple reviews on the new implementation [rlgl] ADDED: OpenGL ES 2.0 support on PLATFORM_DESKTOP (#2840) by @wtnbgo [rlgl] ADDED: Separate blending modes for color and alpha, BLEND_CUSTOM_SEPARATE (#2741) [rlgl] ADDED: rlSetBlendFactorsSeparate and custom blend mode modification checks (#2741) by @pure01fx @@ -58,6 +68,7 @@ Detailed changes: [rlgl] REVIEWED: rlMultMatrixf(), use const pointer (#2807) by @planetis-m [rlgl] REVIEWED: Expose OpenGL blending mode factors and functions/equations [rlgl] REVIEWED: rLoadTextureDepth(), issue with depth textures on WebGL (#2824) +[rlgl] REVIEWED: rlUnloadFramebuffer() (#2937) [raymath] ADDED: Vector2LineAngle() (#2887) [raymath] REVIEWED: Vector2Angle() (#2829, #2832) by @AlxHnr, @planetis-m [shapes] ADDED: CheckCollisionPointPoly() (#2685) by @acejacek @@ -95,10 +106,14 @@ Detailed changes: [models] REVIEWED: LoadIQM() (#2676) [models] REVIEWED: Simplify .vox signature check (#2752) by @CrezyDud [models] REVIEWED: LoadIQM(), support bone names loading if available (#2882) by @PencilAmazing +[models] REVIEWED: GenMeshTangents(), avoid crash on missing texcoords data (#2927) [models] `WARNING`: REMOVED: DrawCubeTexture(), DrawCubeTextureRec(), functions moved to new example: `models_draw_cube_texture` +[audio] ADDED: Full support for QOA audio file format +[audio] ADDED: Mixed audio processor (#2929) by @hatkidchan [audio] ADDED: IsWaveReady()`, IsSoundReady(), IsMusicReady() (#2892) by @RobLoach [audio] REVIEWED: Clear PCM buffer state when closing audio device (#2736) by @veins1 [audio] REVIEWED: Android backend selected (#2118, #2875) by @planetis-m +[audio] REVIEWED: Change default threading model for COM objects in miniaudio [multi] ADDED: IsShaderReady(), IsImageReady(), IsFontReady() (#2892) by @RobLoach [multi] ADDED: IsModelReady(), IsMaterialReady(), IsTextureReady(), IsRenderTextureReady() (#2895) by @RobLoach [multi] REVIEWED: Multiple code/comment typos by @sDos280 @@ -113,6 +128,7 @@ Detailed changes: [examples] REVIEWED: core_camera_2d_platformer (#2687) by @skylar779 [examples] REVIEWED: core_input_gamepad.c (#2903) by @planetis-m [examples] REVIEWED: core_custom_frame_control +[examples] REVIEWED: core_drop_files (#2943) [examples] REVIEWED: text_rectangle_bounds (#2746) by @SzieberthAdam [examples] REVIEWED: textures_image_processing, added gaussian blurring (#2775) by @nobytesgiven [examples] REVIEWED: models_billboard, highlighting rotation and draw order (#2779) by @nobytesgiven @@ -124,6 +140,7 @@ Detailed changes: [build] ADDED: Linkage library -latomic on Linux (only required for ARM32) [build] ADDED: Required frameworks on macOS (#2793) by @SpexGuy [build] ADDED: WASM support for Zig build (#2901) by @Not-Nik +[build] ADDED: New raylib examples as VS2022 project (to raylib solution) [build] REVIEWED: config.h format and inconsistencies [build] REVIEWED: Zig build to latest master, avoid deprecated functions (#2910) by @star-tek-mb [build] REVIEWED: CMake project template to easily target raylib version (#2700) by @RobLoach @@ -137,6 +154,7 @@ Detailed changes: [build] REVIEWED: Several compilation warnings (for strict rules) [build] REVIEWED: All github workflows using deprecated actions [build] REVIEWED: CMake when compiling for web (#2820) by @object71 +[build] REVIEWED: DLL build on Windows (#2951) by @Skaytacium [build] REVIEWED: Avoid MSVC warnings in raylib project (#2871) by @JeffM2501 [build] REVIEWED: Paths in .bat files to build examples (#2870) by @masoudd [build] REVIEWED: CMake, use GLVND for old cmake versions (#2826) by @simendsjo @@ -153,6 +171,7 @@ Detailed changes: [bindings] ADDED: TurboRaylib (Object Pascal) by @turborium [bindings] ADDED: Kaylib (Kotlin/Native) by @Its-Kenta [bindings] ADDED: Raylib-Nelua (Nelua) by @Its-Kenta +[bindings] ADDED: Cyber binding by @fubark [misc] REVIEWED: Update external libraries to latest versions ------------------------------------------------------------------------- From 1b873b028f18486d31bb5c75f6b04a4de4c8d9fa Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 Mar 2023 19:48:47 +0100 Subject: [PATCH 0314/1710] WARNING: REMOVED: Multichannel audio support API It was quite restrictive and hacky implementation, just load multiple types same sound to play multiple instances. --- examples/Makefile | 1 - examples/Makefile.Web | 6 - examples/README.md | 1 - examples/audio/audio_multichannel_sound.c | 78 ---- examples/audio/audio_multichannel_sound.png | Bin 16088 -> 0 bytes .../examples/audio_multichannel_sound.vcxproj | 390 ------------------ projects/VS2022/raylib.sln | 2 - src/raudio.c | 87 ---- src/raylib.h | 3 - 9 files changed, 568 deletions(-) delete mode 100644 examples/audio/audio_multichannel_sound.c delete mode 100644 examples/audio/audio_multichannel_sound.png delete mode 100644 projects/VS2022/examples/audio_multichannel_sound.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 0b524015f..6b0b4a848 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -531,7 +531,6 @@ AUDIO = \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ - audio/audio_multichannel_sound \ audio/audio_stream_effects CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index a55a76b8c..063530fb3 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -503,7 +503,6 @@ AUDIO = \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ - audio/audio_multichannel_sound \ audio/audio_stream_effects CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) @@ -1006,11 +1005,6 @@ audio/audio_sound_loading: audio/audio_sound_loading.c --preload-file audio/resources/sound.wav@resources/sound.wav \ --preload-file audio/resources/target.ogg@resources/target.ogg -audio/audio_multichannel_sound: audio/audio_multichannel_sound.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file audio/resources/sound.wav@resources/sound.wav \ - --preload-file audio/resources/target.ogg@resources/target.ogg - audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 diff --git a/examples/README.md b/examples/README.md index 7a3e72f87..8399d677d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -186,7 +186,6 @@ Examples using raylib audio functionality, including sound/music loading and pla | 118 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | | 119 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | | 120 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | -| 121 | [audio_multichannel_sound](audio/audio_multichannel_sound.c) | audio_multichannel_sound | ⭐️☆☆☆ | 3.0 | 3.5 | [Chris Camacho](https://github.com/codifies) | ### category: others diff --git a/examples/audio/audio_multichannel_sound.c b/examples/audio/audio_multichannel_sound.c deleted file mode 100644 index 1d549a055..000000000 --- a/examples/audio/audio_multichannel_sound.c +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************************* -* -* raylib [audio] example - Multichannel sound playing -* -* Example originally created with raylib 3.0, last time updated with raylib 3.5 -* -* 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-2023 Chris Camacho (@chriscamacho) and 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 [audio] example - Multichannel sound playing"); - - InitAudioDevice(); // Initialize audio device - - Sound fxWav = LoadSound("resources/sound.wav"); // Load WAV audio file - Sound fxOgg = LoadSound("resources/target.ogg"); // Load OGG audio file - - SetSoundVolume(fxWav, 0.2f); - - 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_ENTER)) PlaySoundMulti(fxWav); // Play a new wav sound instance - if (IsKeyPressed(KEY_SPACE)) PlaySoundMulti(fxOgg); // Play a new ogg sound instance - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - DrawText("MULTICHANNEL SOUND PLAYING", 20, 20, 20, GRAY); - DrawText("Press SPACE to play new ogg instance!", 200, 120, 20, LIGHTGRAY); - DrawText("Press ENTER to play new wav instance!", 200, 180, 20, LIGHTGRAY); - - DrawText(TextFormat("CONCURRENT SOUNDS PLAYING: %02i", GetSoundsPlaying()), 220, 280, 20, RED); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - StopSoundMulti(); // We must stop the buffer pool before unloading - - UnloadSound(fxWav); // Unload sound data - UnloadSound(fxOgg); // Unload sound data - - CloseAudioDevice(); // Close audio device - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} diff --git a/examples/audio/audio_multichannel_sound.png b/examples/audio/audio_multichannel_sound.png deleted file mode 100644 index 4b98b26e0a05f517b5f157d7cf50ba2424866446..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16088 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 B{|Ep8 diff --git a/projects/VS2022/examples/audio_multichannel_sound.vcxproj b/projects/VS2022/examples/audio_multichannel_sound.vcxproj deleted file mode 100644 index 91ab9095d..000000000 --- a/projects/VS2022/examples/audio_multichannel_sound.vcxproj +++ /dev/null @@ -1,390 +0,0 @@ - - - - - Debug.DLL - Win32 - - - Debug.DLL - x64 - - - Debug - Win32 - - - Debug - x64 - - - Release.DLL - Win32 - - - Release.DLL - x64 - - - Release - Win32 - - - Release - x64 - - - - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F} - Win32Proj - audio_multichannel_sound - 10.0 - audio_multichannel_sound - - - - Application - true - $(DefaultPlatformToolset) - Unicode - - - Application - true - $(DefaultPlatformToolset) - Unicode - - - Application - true - $(DefaultPlatformToolset) - Unicode - - - Application - true - $(DefaultPlatformToolset) - Unicode - - - Application - false - $(DefaultPlatformToolset) - true - Unicode - - - Application - false - $(DefaultPlatformToolset) - true - Unicode - - - Application - false - $(DefaultPlatformToolset) - true - Unicode - - - Application - false - $(DefaultPlatformToolset) - true - Unicode - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ - - - false - $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ - - - false - $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ - - - false - $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ - - - false - $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\..\examples\audio - WindowsLocalDebugger - - - $(SolutionDir)..\..\examples\audio - WindowsLocalDebugger - - - $(SolutionDir)..\..\examples\audio - WindowsLocalDebugger - - - $(SolutionDir)..\..\examples\audio - WindowsLocalDebugger - - - $(SolutionDir)..\..\examples\audio - WindowsLocalDebugger - - - $(SolutionDir)..\..\examples\audio - WindowsLocalDebugger - - - $(SolutionDir)..\..\examples\audio - WindowsLocalDebugger - - - $(SolutionDir)..\..\examples\audio - WindowsLocalDebugger - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) - CompileAsC - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - - - Console - true - $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ - raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) - CompileAsC - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - /FS %(AdditionalOptions) - - - Console - true - $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ - raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) - CompileAsC - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - - - Console - true - $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ - raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" - Copy Debug DLL to output directory - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) - CompileAsC - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - - - Console - true - $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ - raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" - Copy Debug DLL to output directory - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - CompileAsC - true - - - Console - true - true - true - raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - CompileAsC - true - - - Console - true - true - true - raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - CompileAsC - true - - - Console - true - true - true - raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ - - - xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" - - - Copy Release DLL to output directory - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) - CompileAsC - true - - - Console - true - true - true - raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ - - - xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" - - - Copy Release DLL to output directory - - - - - - - - - - - {e89d61ac-55de-4482-afd4-df7242ebc859} - - - - - - \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 06166cdd0..673ccb4b3 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -17,8 +17,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_image_drawing", "e EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_module_playing", "examples\audio_module_playing.vcxproj", "{E6784F91-4E4E-4956-A079-73FAB1AC7BE6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_multichannel_sound", "examples\audio_multichannel_sound.vcxproj", "{9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_music_stream", "examples\audio_music_stream.vcxproj", "{BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_raw_stream", "examples\audio_raw_stream.vcxproj", "{93A1F656-0D29-4C5E-B140-11F23FF5D6AB}" diff --git a/src/raudio.c b/src/raudio.c index 8bd9052c2..c86ea0bb4 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -377,11 +377,6 @@ typedef struct AudioData { int defaultSize; // Default audio buffer size for audio streams } Buffer; rAudioProcessor *mixedProcessor; - struct { - unsigned int poolCounter; // AudioBuffer pointers pool counter - AudioBuffer *pool[MAX_AUDIO_BUFFER_POOL_CHANNELS]; // Multichannel AudioBuffer pointers pool - unsigned int channels[MAX_AUDIO_BUFFER_POOL_CHANNELS]; // AudioBuffer pool channels - } MultiChannel; } AudioData; //---------------------------------------------------------------------------------- @@ -1100,88 +1095,6 @@ void PlaySound(Sound sound) PlayAudioBuffer(sound.stream.buffer); } -// Play a sound in the multichannel buffer pool -void PlaySoundMulti(Sound sound) -{ - int index = -1; - unsigned int oldAge = 0; - int oldIndex = -1; - - // find the first non-playing pool entry - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) - { - if (AUDIO.MultiChannel.channels[i] > oldAge) - { - oldAge = AUDIO.MultiChannel.channels[i]; - oldIndex = i; - } - - if (!IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i])) - { - index = i; - break; - } - } - - // If no none playing pool members can be indexed choose the oldest - if (index == -1) - { - TRACELOG(LOG_WARNING, "SOUND: Buffer pool is already full, count: %i", AUDIO.MultiChannel.poolCounter); - - if (oldIndex == -1) - { - // Shouldn't be able to get here... but just in case something odd happens! - TRACELOG(LOG_WARNING, "SOUND: Buffer pool could not determine the oldest buffer not playing sound"); - return; - } - - index = oldIndex; - - // Just in case... - StopAudioBuffer(AUDIO.MultiChannel.pool[index]); - } - - // Experimentally mutex lock doesn't seem to be needed this makes sense - // as pool[index] isn't playing and the only stuff we're copying - // shouldn't be changing... - - AUDIO.MultiChannel.channels[index] = AUDIO.MultiChannel.poolCounter; - AUDIO.MultiChannel.poolCounter++; - - SetAudioBufferVolume(AUDIO.MultiChannel.pool[index], sound.stream.buffer->volume); - SetAudioBufferPitch(AUDIO.MultiChannel.pool[index], sound.stream.buffer->pitch); - SetAudioBufferPan(AUDIO.MultiChannel.pool[index], sound.stream.buffer->pan); - - AUDIO.MultiChannel.pool[index]->looping = sound.stream.buffer->looping; - AUDIO.MultiChannel.pool[index]->usage = sound.stream.buffer->usage; - AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[0] = false; - AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[1] = false; - AUDIO.MultiChannel.pool[index]->sizeInFrames = sound.stream.buffer->sizeInFrames; - - AUDIO.MultiChannel.pool[index]->data = sound.stream.buffer->data; // Fill dummy track with data for playing - - PlayAudioBuffer(AUDIO.MultiChannel.pool[index]); -} - -// Stop any sound played with PlaySoundMulti() -void StopSoundMulti(void) -{ - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) StopAudioBuffer(AUDIO.MultiChannel.pool[i]); -} - -// Get number of sounds playing in the multichannel buffer pool -int GetSoundsPlaying(void) -{ - int counter = 0; - - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) - { - if (IsAudioBufferPlaying(AUDIO.MultiChannel.pool[i])) counter++; - } - - return counter; -} - // Pause a sound void PauseSound(Sound sound) { diff --git a/src/raylib.h b/src/raylib.h index 6bc81fe75..9fcece6f0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1530,9 +1530,6 @@ RLAPI void PlaySound(Sound sound); // Play a RLAPI void StopSound(Sound sound); // Stop playing a sound RLAPI void PauseSound(Sound sound); // Pause a sound RLAPI void ResumeSound(Sound sound); // Resume a paused sound -RLAPI void PlaySoundMulti(Sound sound); // Play a sound (using multichannel buffer pool) -RLAPI void StopSoundMulti(void); // Stop any sound playing (using multichannel buffer pool) -RLAPI int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel 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) From 57f0015a08144aa215789f2536f46e67c426a00e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 Mar 2023 19:51:17 +0100 Subject: [PATCH 0315/1710] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 091425582..b7abe8b68 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -111,6 +111,7 @@ Detailed changes: [audio] ADDED: Full support for QOA audio file format [audio] ADDED: Mixed audio processor (#2929) by @hatkidchan [audio] ADDED: IsWaveReady()`, IsSoundReady(), IsMusicReady() (#2892) by @RobLoach +[audio] `WARNING`: REMOVED: Multichannel audio API: PlaySoundMulti(), StopSoundMulti() [audio] REVIEWED: Clear PCM buffer state when closing audio device (#2736) by @veins1 [audio] REVIEWED: Android backend selected (#2118, #2875) by @planetis-m [audio] REVIEWED: Change default threading model for COM objects in miniaudio From ae53600d31c9f8502524a2c9d51bdd4068e7ebf8 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 Mar 2023 19:54:53 +0100 Subject: [PATCH 0316/1710] WARNING: REMOVED: Multichannel audio support API --- src/raudio.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index c86ea0bb4..8ac09b2f3 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -485,13 +485,6 @@ void InitAudioDevice(void) return; } - // Init dummy audio buffers pool for multichannel sound playing - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) - { - // WARNING: An empty audio buffer is created (data = 0) and added to list, AudioBuffer data is filled on PlaySoundMulti() - AUDIO.MultiChannel.pool[i] = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); - } - TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully"); TRACELOG(LOG_INFO, " > Backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend)); TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat)); @@ -507,20 +500,6 @@ void CloseAudioDevice(void) { if (AUDIO.System.isReady) { - // Unload dummy audio buffers pool - // WARNING: They can be pointing to already unloaded data - for (int i = 0; i < MAX_AUDIO_BUFFER_POOL_CHANNELS; i++) - { - //UnloadAudioBuffer(AUDIO.MultiChannel.pool[i]); - if (AUDIO.MultiChannel.pool[i] != NULL) - { - ma_data_converter_uninit(&AUDIO.MultiChannel.pool[i]->converter, NULL); - UntrackAudioBuffer(AUDIO.MultiChannel.pool[i]); - //RL_FREE(buffer->data); // Already unloaded by UnloadSound() - RL_FREE(AUDIO.MultiChannel.pool[i]); - } - } - ma_mutex_uninit(&AUDIO.System.lock); ma_device_uninit(&AUDIO.System.device); ma_context_uninit(&AUDIO.System.context); From 15716e59dc1fddbf6efce18a359448fe6d53a908 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 Mar 2023 20:07:54 +0100 Subject: [PATCH 0317/1710] Update CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index b7abe8b68..dfd6b2863 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ changelog Current Release: raylib 4.2.0 (11 August 2022) ------------------------------------------------------------------------- -Release: raylib 4.5 (xx February 2023) -WIP- +Release: raylib 4.5 (xx March 2023) -WIP- ------------------------------------------------------------------------- KEY CHANGES: - ADDED: M3D model format support with animations From ab1e246367973c4d2ef74843a5f9588010889466 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 Mar 2023 20:33:45 +0100 Subject: [PATCH 0318/1710] REVIEWED: Data types validation --- src/raudio.c | 10 +++++----- src/rmodels.c | 11 +++++++++-- src/rtext.c | 9 ++++++++- src/rtextures.c | 4 +++- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 8ac09b2f3..4bdbede9e 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -912,11 +912,11 @@ Sound LoadSoundFromWave(Wave wave) // Checks if a sound is ready bool IsSoundReady(Sound sound) { - return ((sound.frameCount > 0) && // Validate frame count - (sound.stream.buffer != NULL) && // Validate stream buffer - (sound.stream.sampleRate > 0) && // Validate sample rate is supported - (sound.stream.sampleSize > 0) && // Validate sample size is supported - (sound.stream.channels > 0)); // Validate number of channels supported + return ((sound.frameCount > 0) && // Validate frame count + (sound.stream.buffer != NULL) && // Validate stream buffer + (sound.stream.sampleRate > 0) && // Validate sample rate is supported + (sound.stream.sampleSize > 0) && // Validate sample size is supported + (sound.stream.channels > 0)); // Validate number of channels supported } // Unload wave data diff --git a/src/rmodels.c b/src/rmodels.c index b28e14dc1..a2b5dd888 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1112,7 +1112,13 @@ Model LoadModelFromMesh(Mesh mesh) // Check if a model is ready bool IsModelReady(Model model) { - return model.meshes != NULL && model.materials != NULL && model.meshMaterial != NULL && model.meshCount > 0 && model.materialCount > 0; + return ((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)); // Validate material count + + // NOTE: This is a very general model validation, many elements could be validated from a model... } // Unload model (meshes/materials) from memory (RAM and/or VRAM) @@ -1959,7 +1965,8 @@ Material LoadMaterialDefault(void) // Check if a material is ready bool IsMaterialReady(Material material) { - return material.maps != NULL; + return ((material.maps != NULL) && // Validate material contain some map + (material.shader.id > 0)); // Validate material shader is valid } // Unload material from memory diff --git a/src/rtext.c b/src/rtext.c index f07261121..2d47bdf6c 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -538,7 +538,14 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int // Check if a font is ready bool IsFontReady(Font font) { - return font.glyphs != NULL; + return ((font.texture.id > 0) && // Validate OpenGL id fot font texture atlas + (font.baseSize > 0) && // Validate font size + (font.glyphCount > 0) && // Validate font contains some glyph + (font.recs != NULL) && // Validate font recs defining glyphs on texture atlas + (font.glyphs != NULL)); // Validate glyph data is loaded + + // NOTE: Further validations could be done to verify if recs count and glyphs count + // match glyphCount and to verify that data contained is valid (glyphs values, metrics...) } // Load font data for further use diff --git a/src/rtextures.c b/src/rtextures.c index 56b200ce8..12bca8a8e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3362,7 +3362,9 @@ void UnloadTexture(Texture2D texture) // Check if a render texture is ready bool IsRenderTextureReady(RenderTexture2D target) { - return target.id > 0 && IsTextureReady(target.depth) && IsTextureReady(target.texture); + return ((target.id > 0) && // Validate OpenGL id + IsTextureReady(target.depth) && // Validate FBO depth texture/renderbuffer + IsTextureReady(target.texture)); // Validate FBO texture } // Unload render texture from GPU memory (VRAM) From 4f7b5ff59f821dfcc62e73fc75753093200d7e97 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 Mar 2023 20:40:45 +0100 Subject: [PATCH 0319/1710] `WARNING`: REMOVED: `UnloadModelKeepMeshes()` --- src/raylib.h | 1 - src/rmodels.c | 21 --------------------- 2 files changed, 22 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 9fcece6f0..a6c1a49d6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1440,7 +1440,6 @@ RLAPI Model LoadModel(const char *fileName); RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material) RLAPI bool IsModelReady(Model model); // Check if a model is ready RLAPI void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM) -RLAPI void UnloadModelKeepMeshes(Model model); // Unload model (but not meshes) from memory (RAM and/or VRAM) RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) // Model drawing functions diff --git a/src/rmodels.c b/src/rmodels.c index a2b5dd888..8ab923421 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1147,27 +1147,6 @@ void UnloadModel(Model model) TRACELOG(LOG_INFO, "MODEL: Unloaded model (and meshes) from RAM and VRAM"); } -// Unload model (but not meshes) from memory (RAM and/or VRAM) -void UnloadModelKeepMeshes(Model model) -{ - // 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 (but not meshes) from RAM and VRAM"); -} - // Compute model bounding box limits (considers all meshes) BoundingBox GetModelBoundingBox(Model model) { From 4fd1e04e706bdf290377b7b29d2706fa5954afb1 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 Mar 2023 21:03:44 +0100 Subject: [PATCH 0320/1710] Update CHANGELOG --- CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index dfd6b2863..76fd13ce5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -100,6 +100,8 @@ Detailed changes: [models] ADDED: GLTF animation support (#2844) by @charles-l [models] ADDED: DrawCapsule() and DrawCapsuleWires() (#2761) by @IanBand [models] ADDED: LoadMaterials(), MTL files loading, same code as OBJ loader (#2872) by @JeffM2501 +[models] `WARNING`: REMOVED: UnloadModelKeepMeshes() +[models] `WARNING`: REMOVED: DrawCubeTexture(), DrawCubeTextureRec(), functions moved to new example: `models_draw_cube_texture` [models] REVIEWED: DrawMesh(), using SHADER_LOC_COLOR_SPECULAR as a material map (#2908) by @haved [models] REVIEWED: LoadM3D() vertex color support (#2878) by @GithubPrankster, @bztsrc [models] REVIEWED: GenMeshHeightmap() (#2716) @@ -107,7 +109,6 @@ Detailed changes: [models] REVIEWED: Simplify .vox signature check (#2752) by @CrezyDud [models] REVIEWED: LoadIQM(), support bone names loading if available (#2882) by @PencilAmazing [models] REVIEWED: GenMeshTangents(), avoid crash on missing texcoords data (#2927) -[models] `WARNING`: REMOVED: DrawCubeTexture(), DrawCubeTextureRec(), functions moved to new example: `models_draw_cube_texture` [audio] ADDED: Full support for QOA audio file format [audio] ADDED: Mixed audio processor (#2929) by @hatkidchan [audio] ADDED: IsWaveReady()`, IsSoundReady(), IsMusicReady() (#2892) by @RobLoach From ee3e40c663e138473fffb9abc58214f6be130f03 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 13:47:02 +0100 Subject: [PATCH 0321/1710] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index f24ae012c..d6c8325ea 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1717,7 +1717,7 @@ void *GetWindowHandle(void) // NOTE: Returned handle is: unsigned long Window (X.h) // typedef unsigned long XID; // typedef XID Window; - //unsigned long id = (unsigned long)glfwGetX11Window(window); + //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle); //return NULL; // TODO: Find a way to return value... cast to void *? return (void *)CORE.Window.handle; #endif From b436c8d7e5346a241b00511a11585936895d959d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 13:48:11 +0100 Subject: [PATCH 0322/1710] ADDED: Security check for `emscripten_run_script()` #2954 --- src/rcore.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index d6c8325ea..eae49515f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1994,7 +1994,9 @@ void SetClipboardText(const char *text) glfwSetClipboardString(CORE.Window.handle, text); #endif #if defined(PLATFORM_WEB) - emscripten_run_script(TextFormat("navigator.clipboard.writeText('%s')", 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 emscripten_run_script(TextFormat("navigator.clipboard.writeText('%s')", text)); #endif } @@ -2006,6 +2008,7 @@ const char *GetClipboardText(void) return glfwGetClipboardString(CORE.Window.handle); #endif #if defined(PLATFORM_WEB) +/* // 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 @@ -2019,7 +2022,7 @@ const char *GetClipboardText(void) // 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; #endif return NULL; @@ -2910,6 +2913,9 @@ void SetConfigFlags(unsigned int flags) void TakeScreenshot(const char *fileName) { #if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_WEB + 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 }; @@ -3536,12 +3542,8 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Small security check trying to avoid (partially) malicious code... - // sorry for the inconvenience when you hit this point... - if (strchr(url, '\'') != NULL) - { - TRACELOG(LOG_WARNING, "SYSTEM: Provided URL is not valid"); - } + // Security check to (aprtially) avoid malicious code on PLATFORM_WEB + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { #if defined(PLATFORM_DESKTOP) From f9c4cc2040aa014292b02fcf10918ca930341e3b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 19:28:42 +0100 Subject: [PATCH 0323/1710] ADDED: `UpdateCameraPro()` -Experimental- REVIEWED: rcamera module formating REVIEWED: `core_3d_camera_example` --- examples/core/core_3d_camera_first_person.c | 19 +++ src/rcamera.h | 144 ++++++++++++-------- 2 files changed, 104 insertions(+), 59 deletions(-) diff --git a/examples/core/core_3d_camera_first_person.c b/examples/core/core_3d_camera_first_person.c index 0fc784fa6..de0a43c33 100644 --- a/examples/core/core_3d_camera_first_person.c +++ b/examples/core/core_3d_camera_first_person.c @@ -113,7 +113,26 @@ int main(void) } } + // 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 UpdateCamera(&camera, cameraMode); // Update camera + +/* + // Camera PRO usage example (EXPERIMENTAL) + // This new camera function allows custom movement/rotation values to be directly provided + // as input parameters, with this approach, rcamera module is internally independent of raylib inputs + UpdateCameraPro(&camera, + (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 + GetMouseWheelMove()*2.0f, // Move to target (zoom) + GetMouseDelta().x*0.05f, // Rotation: yaw + GetMouseDelta().y*0.05f, // Rotation: pitch + 0.0f); // Rotation: roll +*/ //---------------------------------------------------------------------------------- // Draw diff --git a/src/rcamera.h b/src/rcamera.h index c3a101f0b..172237588 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -46,32 +46,35 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -//... +// Function specifiers definition +#ifndef RLAPI + #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + #if defined(CAMERA_STANDALONE) -#define CAMERA_CULL_DISTANCE_NEAR 0.01 -#define CAMERA_CULL_DISTANCE_FAR 1000.0 + #define CAMERA_CULL_DISTANCE_NEAR 0.01 + #define CAMERA_CULL_DISTANCE_FAR 1000.0 #else -#define CAMERA_CULL_DISTANCE_NEAR RL_CULL_DISTANCE_NEAR -#define CAMERA_CULL_DISTANCE_FAR RL_CULL_DISTANCE_FAR + #define CAMERA_CULL_DISTANCE_NEAR RL_CULL_DISTANCE_NEAR + #define CAMERA_CULL_DISTANCE_FAR RL_CULL_DISTANCE_FAR #endif //---------------------------------------------------------------------------------- // Types and Structures Definition // NOTE: Below types are required for CAMERA_STANDALONE usage //---------------------------------------------------------------------------------- -// TODO review #if defined(CAMERA_STANDALONE) - // Vector2 type + // Vector2, 2 components typedef struct Vector2 { - float x; - float y; + float x; // Vector x component + float y; // Vector y component } Vector2; - // Vector3 type + // Vector3, 3 components typedef struct Vector3 { - float x; - float y; - float z; + float x; // Vector x component + float y; // Vector y component + float z; // Vector z component } Vector3; // Camera type, defines a camera position/orientation in 3d space @@ -80,31 +83,31 @@ Vector3 target; // Camera target it looks-at Vector3 up; // Camera up vector (rotation over its axis) float fovy; // Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic - int projection; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC + int projection; // Camera projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D + // Camera projection + typedef enum { + CAMERA_PERSPECTIVE = 0, // Perspective projection + CAMERA_ORTHOGRAPHIC // Orthographic projection + } CameraProjection; + // Camera system modes typedef enum { - CAMERA_CUSTOM = 0, - CAMERA_FREE, - CAMERA_ORBITAL, - CAMERA_FIRST_PERSON, - CAMERA_THIRD_PERSON + CAMERA_CUSTOM = 0, // Camera custom, controlled by user (UpdateCamera() does nothing) + CAMERA_FREE, // Camera free mode + CAMERA_ORBITAL, // Camera orbital, around target, zoom supported + CAMERA_FIRST_PERSON, // Camera first person + CAMERA_THIRD_PERSON // Camera third person } CameraMode; - - // Camera projection modes - typedef enum { - CAMERA_PERSPECTIVE = 0, - CAMERA_ORTHOGRAPHIC - } CameraProjection; #endif //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- - +//... //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -114,21 +117,23 @@ extern "C" { // Prevents name mangling of functions #endif -Vector3 GetCameraForward(Camera *camera); -Vector3 GetCameraUp(Camera *camera); -Vector3 GetCameraRight(Camera *camera); +RLAPI Vector3 GetCameraForward(Camera *camera); +RLAPI Vector3 GetCameraUp(Camera *camera); +RLAPI Vector3 GetCameraRight(Camera *camera); -void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane); -void CameraMoveUp(Camera *camera, float distance); -void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane); -void CameraMoveToTarget(Camera *camera, float delta); +// Camera movement +RLAPI void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane); +RLAPI void CameraMoveUp(Camera *camera, float distance); +RLAPI void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane); +RLAPI void CameraMoveToTarget(Camera *camera, float delta); -void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget); -void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp); -void CameraRoll(Camera *camera, float angle); +// Camera rotation +RLAPI void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget); +RLAPI void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp); +RLAPI void CameraRoll(Camera *camera, float angle); -Matrix GetCameraViewMatrix(Camera *camera); -Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); +RLAPI Matrix GetCameraViewMatrix(Camera *camera); +RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); #if defined(__cplusplus) } @@ -306,36 +311,35 @@ void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget) Vector3 up = GetCameraUp(camera); // View vector - Vector3 target_position = Vector3Subtract(camera->target, camera->position); + Vector3 targetPosition = Vector3Subtract(camera->target, camera->position); // Rotate view vector around up axis - target_position = Vector3RotateByAxisAngle(target_position, up, angle); + targetPosition = Vector3RotateByAxisAngle(targetPosition, up, angle); if (rotateAroundTarget) { // Move position relative to target - camera->position = Vector3Subtract(camera->target, target_position); + camera->position = Vector3Subtract(camera->target, targetPosition); } else // rotate around camera.position { // Move target relative to position - camera->target = Vector3Add(camera->position, target_position); + camera->target = Vector3Add(camera->position, targetPosition); } } -// Rotates the camera around its right vector -// Pitch is "looking up and down" -// lockView prevents camera overrotation (aka "somersaults") -// If rotateAroundTarget is false, the camera rotates around its position -// rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE) -// Note: angle must be provided in radians +// Rotates the camera around its right vector, pitch is "looking up and down" +// - 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 void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp) { // Up direction Vector3 up = GetCameraUp(camera); // View vector - Vector3 target_position = Vector3Subtract(camera->target, camera->position); + Vector3 targetPosition = Vector3Subtract(camera->target, camera->position); if (lockView) { @@ -343,32 +347,32 @@ void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTa // to allow only viewing straight up or down. // Clamp view up - float max_angle_up = Vector3Angle(up, target_position); - max_angle_up -= 0.001f; // avoid numerical errors - if (angle > max_angle_up) angle = max_angle_up; + float maxAngleUp = Vector3Angle(up, targetPosition); + maxAngleUp -= 0.001f; // avoid numerical errors + if (angle > maxAngleUp) angle = maxAngleUp; // Clamp view down - float max_angle_down = Vector3Angle(Vector3Negate(up), target_position); - max_angle_down *= -1.0f; // downwards angle is negative - max_angle_down += 0.001f; // avoid numerical errors - if (angle < max_angle_down) angle = max_angle_down; + float maxAngleDown = Vector3Angle(Vector3Negate(up), targetPosition); + maxAngleDown *= -1.0f; // downwards angle is negative + maxAngleDown += 0.001f; // avoid numerical errors + if (angle < maxAngleDown) angle = maxAngleDown; } // Rotation axis Vector3 right = GetCameraRight(camera); // Rotate view vector around right axis - target_position = Vector3RotateByAxisAngle(target_position, right, angle); + targetPosition = Vector3RotateByAxisAngle(targetPosition, right, angle); if (rotateAroundTarget) { // Move position relative to target - camera->position = Vector3Subtract(camera->target, target_position); + camera->position = Vector3Subtract(camera->target, targetPosition); } else // rotate around camera.position { // Move target relative to position - camera->target = Vector3Add(camera->position, target_position); + camera->target = Vector3Add(camera->position, targetPosition); } if (rotateUp) @@ -466,4 +470,26 @@ void UpdateCamera(Camera *camera, int mode) } #endif // !CAMERA_STANDALONE +// Update camera movement, movement/rotation values should be provided by user +void UpdateCameraPro(Camera *camera, float moveForward, float moveRightLeft, float moveUpDown, float moveToTarget, float yaw, float pitch, float roll) +{ + bool lockView = true; + bool rotateAroundTarget = false; + bool rotateUp = false; + bool moveInWorldPlane = true; + + // Camera rotation + CameraPitch(camera, -pitch*DEG2RAD, lockView, rotateAroundTarget, rotateUp); + CameraYaw(camera, -yaw*DEG2RAD, rotateAroundTarget); + CameraRoll(camera, roll*DEG2RAD); + + // Camera movement + CameraMoveForward(camera, moveForward, moveInWorldPlane); + CameraMoveRight(camera, moveRightLeft, moveInWorldPlane); + CameraMoveUp(camera, moveUpDown); + + // Zoom target distance + CameraMoveToTarget(camera, moveToTarget); +} + #endif // CAMERA_IMPLEMENTATION From 8ca32127013be61110b72eddc19dda16520ac704 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 19:40:43 +0100 Subject: [PATCH 0324/1710] REVIEWED: `UpdateCameraPro()` to use `Vector3` --- examples/core/core_3d_camera_first_person.c | 22 ++++++++++-------- src/rcamera.h | 25 ++++++++++++++------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/examples/core/core_3d_camera_first_person.c b/examples/core/core_3d_camera_first_person.c index de0a43c33..6a2a115a7 100644 --- a/examples/core/core_3d_camera_first_person.c +++ b/examples/core/core_3d_camera_first_person.c @@ -123,15 +123,19 @@ int main(void) // This new camera function allows custom movement/rotation values to be directly provided // as input parameters, with this approach, rcamera module is internally independent of raylib inputs UpdateCameraPro(&camera, - (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 - GetMouseWheelMove()*2.0f, // Move to target (zoom) - GetMouseDelta().x*0.05f, // Rotation: yaw - GetMouseDelta().y*0.05f, // Rotation: pitch - 0.0f); // Rotation: roll + (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 + }, + (Vector3){ + GetMouseDelta().x*0.05f, // Rotation: yaw + GetMouseDelta().y*0.05f, // Rotation: pitch + 0.0f // Rotation: roll + }, + GetMouseWheelMove()*2.0f); // Move to target (zoom) */ //---------------------------------------------------------------------------------- diff --git a/src/rcamera.h b/src/rcamera.h index 172237588..8b37ff7ba 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -471,25 +471,34 @@ void UpdateCamera(Camera *camera, int mode) #endif // !CAMERA_STANDALONE // Update camera movement, movement/rotation values should be provided by user -void UpdateCameraPro(Camera *camera, float moveForward, float moveRightLeft, float moveUpDown, float moveToTarget, float yaw, float pitch, float roll) +void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom) { + // Required values + // movement.x - Move forward/backward + // movement.y - Move right/left + // movement.z - Move up/down + // rotation.x - yaw + // rotation.y - pitch + // rotation.z - roll + // zoom - Move towards target + bool lockView = true; bool rotateAroundTarget = false; bool rotateUp = false; bool moveInWorldPlane = true; // Camera rotation - CameraPitch(camera, -pitch*DEG2RAD, lockView, rotateAroundTarget, rotateUp); - CameraYaw(camera, -yaw*DEG2RAD, rotateAroundTarget); - CameraRoll(camera, roll*DEG2RAD); + CameraPitch(camera, -rotation.y*DEG2RAD, lockView, rotateAroundTarget, rotateUp); + CameraYaw(camera, -rotation.x*DEG2RAD, rotateAroundTarget); + CameraRoll(camera, rotation.z*DEG2RAD); // Camera movement - CameraMoveForward(camera, moveForward, moveInWorldPlane); - CameraMoveRight(camera, moveRightLeft, moveInWorldPlane); - CameraMoveUp(camera, moveUpDown); + CameraMoveForward(camera, movement.x, moveInWorldPlane); + CameraMoveRight(camera, movement.y, moveInWorldPlane); + CameraMoveUp(camera, movement.z); // Zoom target distance - CameraMoveToTarget(camera, moveToTarget); + CameraMoveToTarget(camera, zoom); } #endif // CAMERA_IMPLEMENTATION From 520ea94de8fdf85af987d9b291fb4bc24259494a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 19:52:24 +0100 Subject: [PATCH 0325/1710] Update version to `raylib 4.5` --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index a6c1a49d6..9c68807b9 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib v4.5-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: * - NO external dependencies, all required libraries included with raylib @@ -84,7 +84,7 @@ #define RAYLIB_VERSION_MAJOR 4 #define RAYLIB_VERSION_MINOR 5 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "4.5-dev" +#define RAYLIB_VERSION "4.5" // 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 From 4f38830058ab65a86bc23e30efeb1f5730acbd56 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 20:14:32 +0100 Subject: [PATCH 0326/1710] Some tweaks --- examples/models/models_loading_m3d.c | 2 +- examples/textures/textures_textured_curve.c | 2 +- src/raylib.dll.rc.data | Bin 11246 -> 11246 bytes src/raylib.h | 1 + src/raylib.rc.data | Bin 11182 -> 11182 bytes 5 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index 42d3416de..18c8e1afc 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -2,7 +2,7 @@ * * raylib [models] example - Load models M3D * -* Example originally created with raylib 4.5-dev, last time updated with raylib 4.5-dev +* 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) * diff --git a/examples/textures/textures_textured_curve.c b/examples/textures/textures_textured_curve.c index 7945d8e32..3c2f0606a 100644 --- a/examples/textures/textures_textured_curve.c +++ b/examples/textures/textures_textured_curve.c @@ -2,7 +2,7 @@ * * raylib [textures] example - Draw a texture along a segmented curve * -* Example originally created with raylib 4.5-dev +* 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) * diff --git a/src/raylib.dll.rc.data b/src/raylib.dll.rc.data index 729c45ddfb017ca51ecd873c24d166c84cd494ea..d506b8d28651d7d7e17519bcc0a382a161b688f1 100644 GIT binary patch delta 48 zcmaDC{w{pO9Sv?)1{MYo0Me5`YKTwL@wN|X+) delta 48 ycmZ1%zAk*j9Sv?K1{MYo0Me5`YKTwL Date: Sun, 12 Mar 2023 20:48:49 +0100 Subject: [PATCH 0327/1710] Update CHANGELOG --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 76fd13ce5..d7207d708 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -50,9 +50,11 @@ Detailed changes: [core] REVIEWED: GetMonitorWidth() and GetMonitorHeight() (#2934) [core] REVIEWED: GetWindowHandle() to return Linux window (#2938) [core] REVIEWED: WindowDropCallback(), additional security check (#2943) +[core] REVIEWED: Security checks for emscripten_run_script() (#2954) [rcamera] REDESIGNED: New implementation from scratch (#2563) by @Crydsch [rcamera] REVIEWED: Make orbital camera work as expected (#2926) by @JeffM2501 [rcamera] REVIEWED: Multiple reviews on the new implementation +[rcamera] ADDED: UpdateCameraPro(), supporting custom user inputs [rlgl] ADDED: OpenGL ES 2.0 support on PLATFORM_DESKTOP (#2840) by @wtnbgo [rlgl] ADDED: Separate blending modes for color and alpha, BLEND_CUSTOM_SEPARATE (#2741) [rlgl] ADDED: rlSetBlendFactorsSeparate and custom blend mode modification checks (#2741) by @pure01fx From 54f01e151cebca517ef31870d6d5f03c4c68b64a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 20:50:19 +0100 Subject: [PATCH 0328/1710] Update CHANGELOG --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d7207d708..118d57821 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ changelog Current Release: raylib 4.2.0 (11 August 2022) ------------------------------------------------------------------------- -Release: raylib 4.5 (xx March 2023) -WIP- +Release: raylib 4.5 (16 March 2023) ------------------------------------------------------------------------- KEY CHANGES: - ADDED: M3D model format support with animations @@ -14,7 +14,6 @@ KEY CHANGES: - REDESIGNED: rlgl module to avoid render batch triangles limits pre-check: rlCheckRenderBatchLimit() - REDESIGNED: rshapes module to minimize the rlgl dependency, now only 6 low-level functions required! - ADDED: rl_gputex self-contained module for compressed textures loading, used by rtextures module - - raygui 3.5: New version of the immediate-mode gui system for tools development with raylib Detailed changes: [core] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach From e4bc2c643ee3fa3fa3360d46afe8b03bb21deadb Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 20:52:24 +0100 Subject: [PATCH 0329/1710] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c66eed75e..05ba5ccf0 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,10 @@ features -------- - **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external) - Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!** - - Written in plain C code (C99) in PascalCase/camelCase notation + - Written in plain C code (C99) using PascalCase/camelCase notation - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3 or ES 2.0**) - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - - Multiple **Fonts** formats supported (TTF, XNA fonts, AngelCode fonts) + - Multiple **Fonts** formats supported (TTF, Image fonts, AngelCode fonts) - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - Flexible Materials system, supporting classic maps and **PBR maps** @@ -51,7 +51,7 @@ features - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - Huge examples collection with [+120 code examples](https://github.com/raysan5/raylib/tree/master/examples)! - - Bindings to [+50 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! + - Bindings to [+60 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! - **Free and open source**. basic example From ce28dfbdeb8c00d3ee389eb2204db9c43480d1f4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 21:03:21 +0100 Subject: [PATCH 0330/1710] Update CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 118d57821..3a5c43759 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -93,7 +93,6 @@ Detailed changes: [text] `WARNING`: RENAMED: TextCodepointsToUTF8() to LoadUTF8() [text] `WARNING`: RENAMED: GetCodepoint() -> GetCodepointNext() [text] REDESIGNED: GetCodepointNext() -[text] REVIEWED text_codepoints_loading.c [text] REVIEWED: MeasureTextEx(), avoid crash on bad data [text] REVIEWED: UnloadFontData(), avoid crash on invalid font data [models] ADDED: Support M3D model file format (meshes and materials) (#2648) by @bztsrc @@ -138,6 +137,7 @@ Detailed changes: [examples] REVIEWED: core_loading_thread, join thread on completion (#2845) by @planetis-m [examples] REVIEWED: models_loading_gltf [examples] REVIEWED: Shader lighting.fs for GLSL120 (#2651) +[examples] REVIEWED: text_codepoints_loading.c [parser] REVIEWED: raylib-parser Makefile (#2765) by @Peter0x44 [build] ADDED: Packaging for distros with deb-based and rpm-based packages (#2877) by @KOLANICH [build] ADDED: Linkage library -latomic on Linux (only required for ARM32) From 8873aca268fdd6ecea657b91a9ea0e2910b3430c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 12 Mar 2023 21:09:50 +0100 Subject: [PATCH 0331/1710] Update CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3a5c43759..36c8bd282 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ changelog --------- -Current Release: raylib 4.2.0 (11 August 2022) +Current Release: raylib 4.5.0 (16 March 2023) ------------------------------------------------------------------------- Release: raylib 4.5 (16 March 2023) From 90e36d188386181e9bb0bf5de7ef087d4033c66b Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 10:54:06 +0100 Subject: [PATCH 0332/1710] Update ROADMAP.md --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index f25f83459..0d126c86f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -9,7 +9,7 @@ Here it is a wishlist with features and ideas to improve the library. Note that _Current version of raylib is complete and functional but there is always room for improvements._ **raylib 4.x** - - [ ] Redesign camera module (more flexible) ([#1143](https://github.com/raysan5/raylib/issues/1143), https://github.com/raysan5/raylib/discussions/2507) + - [x] Redesign camera module (more flexible) ([#1143](https://github.com/raysan5/raylib/issues/1143), https://github.com/raysan5/raylib/discussions/2507) - [ ] Redesign gestures system, improve touch inputs management - [ ] Redesign raudio module, implement miniaudio high-level provided features - [x] Better documentation and improved examples From dad0e64aa81c6f53773d3a8d52394ae1147dd386 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 11:44:02 +0100 Subject: [PATCH 0333/1710] Update HISTORY.md --- HISTORY.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index cebac5362..0b722b1a3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -395,3 +395,18 @@ Highlights for `raylib 4.2`: As always, there are more improvements than the key features listed, make sure to check raylib [CHANGELOG](CHANGELOG) for the detailed list of changes; for this release a `WARNING` flag has been added to all the changes that could affect bindings or productivity code. **raylib keeps improving one more version** and a special focus on maintainability has been put on the library for the future. Specific/advance functionality will be provided through **raylib-extras** repos and raylib main repo devlelopment will be focused on what made raylib popular: being a simple and easy-to-use library to **enjoy videogames programming**. **Enjoy gamedev/tools/graphics programming!** :) + +notes on raylib 4.5 +------------------- + +It's been **7 months** since latest raylib release. As usual, **many parts of the library have been reviewed and improved** along those months. Many issues have been closed, staying under 10 open issues at the moment of this writting and also many PRs from contributors have been received, reviewed and merged into raylib library. Some new functions have been added and some others have been removed to improve library coherence and avoid moving too high level, giving the users the tools to implement advance functionality themselfs over raylib. Again, this is a big release with a considerable amount of changes and improvements. Here it is a small summary highlighting this new **rayib 4.5**. + +Some numbers for this release: + + - **+100** closed issues (for a TOTAL of **+1340**!) + - **+350** commits since previous RELEASE (for a TOTAL of **+6350**!) + - **+25** functions ADDED to raylib API (for a TOTAL of **516**!) + - **+40** functions REVIEWED/REDESIGNED + - **+40** new contributors (for a TOTAL of **405**!) + +-WIP- From a3b7bd0891a7c4a211d75745e51908cc90de7c4a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 11:44:49 +0100 Subject: [PATCH 0334/1710] Update webassembly.yml --- .github/workflows/webassembly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index 198e88802..b50cd8c46 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -29,7 +29,7 @@ jobs: - name: Setup emsdk uses: mymindstorm/setup-emsdk@v11 with: - version: 3.0.0 + version: 3.1.30 actions-cache-folder: 'emsdk-cache' - name: Setup Release Version From 9a115106b4692031a99222cefcea372261d2ab75 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 11:48:34 +0100 Subject: [PATCH 0335/1710] Update miniaudio.h --- src/external/miniaudio.h | 3362 +++++++++++++++++++++++++++----------- 1 file changed, 2381 insertions(+), 981 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index ad3651503..74d584153 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.11 - 2022-11-04 +miniaudio - v0.11.12 - TBD David Reid - mackron@gmail.com @@ -38,7 +38,7 @@ A config/init pattern is used throughout the entire library. The idea is that yo object and pass that into the initialization routine. The advantage to this system is that the config object can be initialized with logical defaults and new properties added to it without breaking the API. The config object can be allocated on the stack and does not need to be -maintained after initialization of the corresponding object. +maintained after initialization of the corresponding object. 1.1. Low Level API @@ -363,7 +363,7 @@ initialized. The easiest but least flexible way of playing a sound is like so: This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the internal sound up for recycling. The last parameter is used to specify which sound group the sound should be associated with which will be explained later. This particular way of playing a sound is -simple, but lacks flexibility and features. A more flexible way of playing a sound is to first +simple, but lacks flexibility and features. A more flexible way of playing a sound is to first initialize a sound: ```c @@ -460,6 +460,8 @@ is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled wit miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details. +Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. + 2.1. Windows ------------ @@ -489,9 +491,10 @@ notarization process. To fix this there are two options. The first is to use the #include "miniaudio.h" ``` -This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. -Alternatively, if you would rather keep using runtime linking you can add the following to your -entitlements.xcent file: +This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. +If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when +using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can +add the following to your entitlements.xcent file: ``` com.apple.security.cs.allow-dyld-environment-variables @@ -748,7 +751,7 @@ To read data from a data source: ma_result result; ma_uint64 framesRead; - result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop); + result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead); if (result != MA_SUCCESS) { return result; // Failed to read data from the data source. } @@ -768,7 +771,7 @@ you could plug in a decoder like so: ma_uint64 framesRead; ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. - result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop); + result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead); if (result != MA_SUCCESS) { return result; // Failed to read data from the decoder. } @@ -822,7 +825,7 @@ retrieved like so: ma_uint32 channels; ma_uint32 sampleRate; ma_channel channelMap[MA_MAX_CHANNELS]; - + result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); if (result != MA_SUCCESS) { return result; // Failed to retrieve data format. @@ -842,7 +845,9 @@ read data within a certain range of the underlying data. To do this you can use ``` This is useful if you have a sound bank where many sounds are stored in the same file and you want -the data source to only play one of those sub-sounds. +the data source to only play one of those sub-sounds. Note that once the range is set, everything +that takes a position, such as cursors and loop points, should always be relatvie to the start of +the range. When the range is set, any previously defined loop point will be reset. Custom loop points can also be used with data sources. By default, data sources will loop after they reach the end of the data source, but if you need to loop at a specific location, you can do @@ -871,19 +876,19 @@ To do this, you can use chaining: return result; // Failed to set the next data source. } - result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE); + result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead); if (result != MA_SUCCESS) { return result; // Failed to read from the decoder. } ``` In the example above we're using decoders. When reading from a chain, you always want to read from -the top level data source in the chain. In the example above, `decoder1` is the top level data +the top level data source in the chain. In the example above, `decoder1` is the top level data source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any gaps. -Note that the `loop` parameter is set to false in the example above. When this is set to true, only -the current data source will be looped. You can loop the entire chain by linking in a loop like so: +Note that when looping is enabled, only the current data source will be looped. You can loop the +entire chain by linking in a loop like so: ```c ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 @@ -894,9 +899,9 @@ Note that setting up chaining is not thread safe, so care needs to be taken if y changing links while the audio thread is in the middle of reading. Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple -instances of the same sound simultaneously. Instead, initialize multiple data sources for each -instance. This can be extremely inefficient depending on the data source and can result in -glitching due to subtle changes to the state of internal filters. +instances of the same sound simultaneously. This can be extremely inefficient depending on the type +of data source and can result in glitching due to subtle changes to the state of internal filters. +Instead, initialize multiple data sources for each instance. 4.1. Custom Data Sources @@ -971,7 +976,7 @@ base object (`ma_data_source_base`): void my_data_source_uninit(my_data_source* pMyDataSource) { // ... do the uninitialization of your custom data source here ... - + // You must uninitialize the base data source. ma_data_source_uninit(&pMyDataSource->base); } @@ -1020,7 +1025,7 @@ configure the engine with an engine config: ma_engine_config engineConfig; engineConfig = ma_engine_config_init(); - engineConfig.pPlaybackDevice = &myDevice; + engineConfig.pDevice = &myDevice; result = ma_engine_init(&engineConfig, &engine); if (result != MA_SUCCESS) { @@ -1061,7 +1066,7 @@ Note that when you're not using a device, you must set the channel count and sam config or else miniaudio won't know what to use (miniaudio will use the device to determine this normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio data from the engine. This kind of setup is useful if you want to do something like offline -processing. +processing or want to use a different audio system for playback such as SDL. When a sound is loaded it goes through a resource manager. By default the engine will initialize a resource manager internally, but you can also specify a pre-initialized resource manager: @@ -1226,7 +1231,7 @@ might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_ By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously -by specificying the `MA_SOUND_FLAG_ASYNC` flag: +by specifying the `MA_SOUND_FLAG_ASYNC` flag: ```c ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); @@ -1247,7 +1252,7 @@ counter hit's zero. You can specify a fence like so: ma_sound sounds[4]; result = ma_fence_init(&fence); - if (result != MA_SUCCES) { + if (result != MA_SUCCESS) { return result; } @@ -2028,14 +2033,14 @@ data from the graph: ``` When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from it's input attachments, which in turn recusively pull in data from their inputs, and so +data from it's input attachments, which in turn recursively pull in data from their inputs, and so on. At the start of the graph there will be some kind of data source node which will have zero inputs and will instead read directly from a data source. The base nodes don't literally need to read from a `ma_data_source` object, but they will always have some kind of underlying object that sources some kind of audio. The `ma_data_source_node` node can be used to read from a `ma_data_source`. Data is always in floating-point format and in the number of channels you specified when the graph was initialized. The sample rate is defined by the underlying data sources. -It's up to you to ensure they use a consistent and appropraite sample rate. +It's up to you to ensure they use a consistent and appropriate sample rate. The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but miniaudio includes a few stock nodes for common functionality. This is how you would initialize a @@ -2076,7 +2081,7 @@ another, you do not need to detach first. You can just call `ma_node_attach_outp deal with it for you. Less frequently you may want to create a specialized node. This will be a node where you implement -your own processing callback to apply a custom effect of some kind. This is similar to initalizing +your own processing callback to apply a custom effect of some kind. This is similar to initializing one of the stock node types, only this time you need to specify a pointer to a vtable containing a pointer to the processing function and the number of input and output buses. Example: @@ -2115,7 +2120,7 @@ pointer to the processing function and the number of input and output buses. Exa // Each bus needs to have a channel count specified. To do this you need to specify the channel // counts in an array and then pass that into the node config. ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. - ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specicied in the vtable. + ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable. inputChannels[0] = channelsIn; inputChannels[1] = channelsIn; @@ -2199,10 +2204,19 @@ and include the following: +-----------------------------------------+---------------------------------------------------+ | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | | | when no data is available to be read from input | - | | attachments. This is useful for effects like | + | | attachments. When a node has at least one input | + | | bus, but there are no inputs attached or the | + | | inputs do not deliver any data, the node's | + | | processing callback will not get fired. This flag | + | | will make it so the callback is always fired | + | | regardless of whether or not any input data is | + | | received. This is useful for effects like | | | echos where there will be a tail of audio data | | | that still needs to be processed even when the | - | | original data sources have reached their ends. | + | | original data sources have reached their ends. It | + | | may also be useful for nodes that must always | + | | have their processing callback fired when there | + | | are no inputs attached. | +-----------------------------------------+---------------------------------------------------+ | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | @@ -2393,7 +2407,7 @@ bus and input bus is locked. This locking is specifically for attaching and deta different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when considering that iterating over attachments must not break as a result of attaching or detaching a -node while iteration is occuring. +node while iteration is occurring. Attaching and detaching are both quite simple. When an output bus of a node is attached to an input bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where @@ -2459,7 +2473,7 @@ implementation: #define MA_NO_FLAC ``` -Disabling built-in decoding libraries is useful if you use these libraries independantly of the +Disabling built-in decoding libraries is useful if you use these libraries independently of the `ma_decoder` API. A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with @@ -2561,7 +2575,7 @@ The `ma_decoding_backend_vtable` vtable has the following functions: ``` onInit - onInitFile + onInitFile onInitFileW onInitMemory onUninit @@ -2573,11 +2587,11 @@ these are not specified, miniaudio will deal with it for you via a generic imple When you initialize a custom data source (by implementing the `onInit` function in the vtable) you will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the -section about data sources for details on how to implemen this. Alternatively, see the +section about data sources for details on how to implement this. Alternatively, see the "custom_decoders" example in the miniaudio repository. The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data -from some abitrary source. You'll use these functions to read from the raw data and perform the +from some arbitrary source. You'll use these functions to read from the raw data and perform the decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant parameter. @@ -2728,7 +2742,7 @@ To perform the conversion simply call `ma_channel_converter_process_pcm_frames() } ``` -It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM +It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM frames. Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. @@ -3174,7 +3188,7 @@ you can chain first and second order filters together. If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample -rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the +rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the format or channel count after initialization is invalid and will result in an error. The `ma_lpf` object supports a configurable order, but if you only need a first order filter you @@ -3347,8 +3361,8 @@ The noise API uses simple LCG random number generation. It supports a custom see for things like automated testing requiring reproducibility. Setting the seed to zero will default to `MA_DEFAULT_LCG_SEED`. -The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, -`ma_noise_set_seed()`, and `ma_noise_set_type()` respectively. +The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and +`ma_noise_set_seed()` respectively. By default, the noise API will use different values for different channels. So, for example, the left side in a stereo stream will be different to the right side. To instead have each channel use @@ -3496,7 +3510,7 @@ you will want to use. To initialize a ring buffer, do something like the followi ``` The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because -it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you +it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. @@ -3555,7 +3569,7 @@ example, ALSA, which is specific to Linux, will not be included in the Windows b +-------------+-----------------------+--------------------------------------------------------+ | WASAPI | ma_backend_wasapi | Windows Vista+ | | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | + | WinMM | ma_backend_winmm | Windows 95+ | | Core Audio | ma_backend_coreaudio | macOS, iOS | | sndio | ma_backend_sndio | OpenBSD | | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | @@ -3601,6 +3615,12 @@ Some backends have some nuance details you may want to be aware of. miniaudio's built-in resampler is to take advantage of any potential device-specific optimizations the driver may implement. +BSD +--- +- The sndio backend is currently only enabled on OpenBSD builds. +- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can + use it. + 15.4. UWP --------- - UWP only supports default playback and capture devices. @@ -3631,14 +3651,28 @@ Some backends have some nuance details you may want to be aware of. 16. Optimization Tips ===================== +See below for some tips on improving performance. -16.1. High Level API +16.1. Low Level API +------------------- +- In the data callback, if your data is already clipped prior to copying it into the output buffer, + set the `noClip` config option in the device config to true. This will disable miniaudio's built + in clipping function. +- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you + will always write valid data to the output buffer you can disable pre-silencing by setting the + `noPreSilence` config option in the device config to true. + +16.2. High Level API -------------------- - If a sound does not require doppler or pitch shifting, consider disabling pitching by initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. -- If a sound does not require spatialization, disable it by initialzing the sound with the - `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be renabled again post-initialization with +- If a sound does not require spatialization, disable it by initializing the sound with the + `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with `ma_sound_set_spatialization_enabled()`. +- If you know all of your sounds will always be the same sample rate, set the engine's sample + rate to match that of the sounds. Likewise, if you're using a self-managed resource manager, + consider setting the decoded sample rate to match your sounds. By configuring everything to + use a consistent sample rate, sample rate conversion can be avoided. @@ -3647,17 +3681,6 @@ Some backends have some nuance details you may want to be aware of. - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though not all have been tested. -- The contents of the output buffer passed into the data callback will always be pre-initialized to - silence unless the `noPreSilencedOutputBuffer` config variable in `ma_device_config` is set to - true, in which case it'll be undefined which will require you to write something to the entire - buffer. -- By default miniaudio will automatically clip samples. This only applies when the playback sample - format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this - overhead by setting `noClip` to true in the device config. -- Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations. -- The sndio backend is currently only enabled on OpenBSD builds. -- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can - use it. - When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This is due to 64-bit file APIs not being available. */ @@ -3674,7 +3697,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 11 +#define MA_VERSION_REVISION 12 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3689,7 +3712,7 @@ extern "C" { #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ #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__) @@ -3748,6 +3771,10 @@ typedef ma_uint32 ma_bool32; #define MA_TRUE 1 #define MA_FALSE 0 +/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */ +typedef float ma_float; +typedef double ma_double; + typedef void* ma_handle; typedef void* ma_ptr; typedef void (* ma_proc)(void); @@ -3801,7 +3828,11 @@ typedef ma_uint16 wchar_t; #ifdef __unix__ #define MA_UNIX - #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #ifdef __ORBIS__ + #define MA_ORBIS + #elif defined(__PROSPERO__) + #define MA_PROSPERO + #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define MA_BSD #endif #endif @@ -3817,8 +3848,24 @@ typedef ma_uint16 wchar_t; #ifdef __EMSCRIPTEN__ #define MA_EMSCRIPTEN #endif + #if defined(__NX__) + #define MA_NX + #endif #endif +#if defined(__has_c_attribute) + #if __has_c_attribute(fallthrough) + #define MA_FALLTHROUGH [[fallthrough]] + #endif +#endif +#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) + #if __has_attribute(fallthrough) + #define MA_FALLTHROUGH __attribute__((fallthrough)) + #endif +#endif +#if !defined(MA_FALLTHROUGH) + #define MA_FALLTHROUGH ((void)0) +#endif #ifdef _MSC_VER #define MA_INLINE __forceinline @@ -4068,6 +4115,7 @@ typedef enum MA_API_NOT_FOUND = -105, MA_INVALID_DEVICE_CONFIG = -106, MA_LOOP = -107, + MA_BACKEND_NOT_ENABLED = -108, /* State errors. */ MA_DEVICE_NOT_INITIALIZED = -200, @@ -4084,7 +4132,7 @@ typedef enum #define MA_MIN_CHANNELS 1 -#ifndef MA_MAX_CHANNELS +#ifndef MA_MAX_CHANNELS #define MA_MAX_CHANNELS 254 #endif @@ -4195,6 +4243,63 @@ typedef struct } ma_lcg; +/* +Atomics. + +These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too +easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By +using a struct we can enforce the use of atomics at compile time. + +These types are declared in the header section because we need to reference them in structs below, but functions for +using them are only exposed in the implementation section. I do not want these to be part of the public API. + +There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are +some macros to help with the declarations. They will be named like so: + + ma_atomic_uint32 - atomic ma_uint32 + ma_atomic_int32 - atomic ma_int32 + ma_atomic_uint64 - atomic ma_uint64 + ma_atomic_float - atomic float + ma_atomic_bool32 - atomic ma_bool32 + +The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific +type of pointer you need to make atomic. For example, an atomic ma_node* will look like this: + + MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node) + +Which will declare a type struct that's named like so: + + ma_atomic_ptr_node + +Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with +the name of the struct. For example: + + ma_atomic_uint32_set() - Atomic store of ma_uint32 + ma_atomic_uint32_get() - Atomic load of ma_uint32 + etc. + +For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in +return you get type safety and enforcement of atomic operations. +*/ +#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \ + typedef struct \ + { \ + MA_ATOMIC(typeSize, ma_##type) value; \ + } ma_atomic_##type; \ + +#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \ + typedef struct \ + { \ + MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \ + } ma_atomic_ptr_##type; \ + +MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32) +MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32) +MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64) +MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float) +MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) + + /* Spinlocks are 32-bit for compatibility reasons. */ typedef ma_uint32 ma_spinlock; @@ -4281,7 +4386,7 @@ Logging #endif #endif #ifndef MA_ATTRIBUTE_FORMAT -#define MA_ATTRIBUTE_FORMAT(fmt,va) +#define MA_ATTRIBUTE_FORMAT(fmt, va) #endif #ifndef MA_MAX_LOG_CALLBACKS @@ -4312,11 +4417,6 @@ logLevel (in) pMessage (in) The log message. - - -Remarks -------- -Do not modify the state of the device from inside the callback. */ typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); @@ -4811,6 +4911,7 @@ typedef struct { ma_gainer_config config; ma_uint32 t; + float masterVolume; float* pOldGains; float* pNewGains; @@ -4826,6 +4927,8 @@ MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); +MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume); +MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume); @@ -4887,7 +4990,7 @@ MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); -MA_API float ma_fader_get_current_volume(ma_fader* pFader); +MA_API float ma_fader_get_current_volume(const ma_fader* pFader); @@ -4899,6 +5002,12 @@ typedef struct float z; } ma_vec3f; +typedef struct +{ + ma_vec3f v; + ma_spinlock lock; +} ma_atomic_vec3f; + typedef enum { ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ @@ -4938,9 +5047,9 @@ MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uin typedef struct { ma_spatializer_listener_config config; - ma_vec3f position; /* The absolute position of the listener. */ - ma_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ - ma_vec3f velocity; + ma_atomic_vec3f position; /* The absolute position of the listener. */ + ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ + ma_atomic_vec3f velocity; ma_bool32 isEnabled; /* Memory management. */ @@ -5012,9 +5121,9 @@ typedef struct float dopplerFactor; /* Set to 0 to disable doppler effect. */ float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ - ma_vec3f position; - ma_vec3f direction; - ma_vec3f velocity; /* For doppler effect. */ + ma_atomic_vec3f position; + ma_atomic_vec3f direction; + ma_atomic_vec3f velocity; /* For doppler effect. */ float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ ma_gainer gainer; /* For smooth gain transitions. */ float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ @@ -5029,6 +5138,8 @@ MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* p MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume); +MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume); MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); @@ -5225,7 +5336,7 @@ MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const /* -Sets the input and output sample sample rate. +Sets the input and output sample rate. */ MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); @@ -6151,7 +6262,7 @@ This section contains the APIs for device playback and capture. Here is where yo #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ #endif #endif -#if defined(MA_UNIX) +#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) #if defined(MA_LINUX) #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */ #define MA_SUPPORT_ALSA @@ -6246,6 +6357,9 @@ typedef enum ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ } ma_device_state; +MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state) + + #ifdef MA_SUPPORT_WASAPI /* We need a IMMNotificationClient object for WASAPI. */ typedef struct @@ -6438,7 +6552,7 @@ DEPRECATED. Use ma_device_notification_proc instead. The callback for when the device has been stopped. This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces -such as being unplugged or an internal error occuring. +such as being unplugged or an internal error occurring. Parameters @@ -6567,6 +6681,13 @@ typedef enum ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ } ma_aaudio_input_preset; +typedef enum +{ + ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ + ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ + ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ + ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ +} ma_aaudio_allowed_capture_policy; typedef union { @@ -6694,13 +6815,16 @@ struct ma_device_config { ma_opensl_stream_type streamType; ma_opensl_recording_preset recordingPreset; + ma_bool32 enableCompatibilityWorkarounds; } opensl; struct { ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; + ma_aaudio_allowed_capture_policy allowedCapturePolicy; ma_bool32 noAutoStartAfterReroute; + ma_bool32 enableCompatibilityWorkarounds; } aaudio; }; @@ -6801,7 +6925,7 @@ If the backend requires absolute flexibility with it's data delivery, it can opt which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been -encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. +encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated @@ -7214,6 +7338,7 @@ struct ma_context ma_proc AAudioStreamBuilder_setUsage; ma_proc AAudioStreamBuilder_setContentType; ma_proc AAudioStreamBuilder_setInputPreset; + ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; ma_proc AAudioStreamBuilder_openStream; ma_proc AAudioStream_close; ma_proc AAudioStream_getState; @@ -7263,6 +7388,7 @@ struct ma_context struct { /*HMODULE*/ ma_handle hOle32DLL; + ma_proc CoInitialize; ma_proc CoInitializeEx; ma_proc CoUninitialize; ma_proc CoCreateInstance; @@ -7283,22 +7409,7 @@ struct ma_context #ifdef MA_POSIX struct { - ma_handle pthreadSO; - ma_proc pthread_create; - ma_proc pthread_join; - ma_proc pthread_mutex_init; - ma_proc pthread_mutex_destroy; - ma_proc pthread_mutex_lock; - ma_proc pthread_mutex_unlock; - ma_proc pthread_cond_init; - ma_proc pthread_cond_destroy; - ma_proc pthread_cond_wait; - ma_proc pthread_cond_signal; - ma_proc pthread_attr_init; - ma_proc pthread_attr_destroy; - ma_proc pthread_attr_setschedpolicy; - ma_proc pthread_attr_getschedparam; - ma_proc pthread_attr_setschedparam; + int _unused; } posix; #endif int _unused; @@ -7310,7 +7421,7 @@ struct ma_device ma_context* pContext; ma_device_type type; ma_uint32 sampleRate; - MA_ATOMIC(4, ma_device_state) state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ + ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ @@ -7326,7 +7437,7 @@ struct ma_device ma_bool8 noClip; ma_bool8 noDisableDenormals; ma_bool8 noFixedSizedCallback; - MA_ATOMIC(4, float) masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ + ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ struct { @@ -7414,8 +7525,8 @@ struct ma_device void* pMappedBufferPlayback; ma_uint32 mappedBufferPlaybackCap; ma_uint32 mappedBufferPlaybackLen; - MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ ma_uint32 loopbackProcessID; ma_bool8 loopbackProcessExclude; ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ @@ -7426,7 +7537,8 @@ struct ma_device ma_bool8 isDetachedPlayback; ma_bool8 isDetachedCapture; ma_wasapi_usage usage; - void *hAvrtHandle; + void* hAvrtHandle; + ma_mutex rerouteLock; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND @@ -7544,6 +7656,7 @@ struct ma_device ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; + ma_aaudio_allowed_capture_policy allowedCapturePolicy; ma_bool32 noAutoStartAfterReroute; } aaudio; #endif @@ -7569,6 +7682,20 @@ struct ma_device #ifdef MA_SUPPORT_WEBAUDIO struct { + /* AudioWorklets path. */ + /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextPlayback; + /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextCapture; + /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodePlayback; + /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodeCapture; + size_t intermediaryBufferSizeInFramesPlayback; + size_t intermediaryBufferSizeInFramesCapture; + float* pIntermediaryBufferPlayback; + float* pIntermediaryBufferCapture; + void* pStackBufferPlayback; + void* pStackBufferCapture; + ma_bool32 isInitialized; + + /* ScriptProcessorNode path. */ int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */ int indexCapture; } webaudio; @@ -7588,7 +7715,7 @@ struct ma_device ma_uint32 currentPeriodFramesRemainingCapture; ma_uint64 lastProcessedFramePlayback; ma_uint64 lastProcessedFrameCapture; - MA_ATOMIC(4, ma_bool32) isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ + ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ } null_device; #endif }; @@ -8252,7 +8379,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c A pointer that will passed to callbacks in pBackendVTable. resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher + The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. @@ -9170,6 +9297,11 @@ Retrieves a friendly name for a backend. */ MA_API const char* ma_get_backend_name(ma_backend backend); +/* +Retrieves the backend enum from the given name. +*/ +MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend); + /* Determines whether or not the given backend is available by the compilation environment. */ @@ -9259,7 +9391,7 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); /************************************************************************************************************************************************************ -Utiltities +Utilities ************************************************************************************************************************************************************/ @@ -9361,6 +9493,12 @@ Helper for converting gain in decibels to a linear factor. MA_API float ma_volume_db_to_linear(float gain); +/* +Mixes the specified number of frames in floating point format with a volume factor. + +This will run on an optimized path when the volume is equal to 1. +*/ +MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); /************************************************************************************************** @@ -10118,7 +10256,7 @@ struct ma_resource_manager_data_buffer ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ - ma_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ + ma_atomic_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ union { ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ @@ -10384,7 +10522,7 @@ struct ma_node_output_bus ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ - MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */ + ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */ MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ @@ -10408,7 +10546,7 @@ struct ma_node_input_bus MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ /* Set once at startup. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ + ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ }; @@ -10416,7 +10554,7 @@ typedef struct ma_node_base ma_node_base; struct ma_node_base { /* These variables are set once at startup. */ - ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ + ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ const ma_node_vtable* vtable; float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ @@ -10518,7 +10656,7 @@ MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourc MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); -/* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ +/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ typedef struct { ma_node_config nodeConfig; @@ -10791,7 +10929,7 @@ typedef struct ma_uint32 channelsOut; ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ ma_mono_expansion_mode monoExpansionMode; - ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ + ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ } ma_engine_node_config; @@ -10847,7 +10985,8 @@ typedef struct ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; ma_bool32 isLooping; - ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */ + ma_resource_manager_pipeline_notifications initNotifications; + ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ } ma_sound_config; MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ @@ -11032,7 +11171,7 @@ MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound); +MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); @@ -11155,6 +11294,10 @@ IMPLEMENTATION #include #endif +#ifdef MA_NX +#include /* For nanosleep() */ +#endif + #include /* For fstat(), etc. */ #ifdef MA_EMSCRIPTEN @@ -11436,23 +11579,6 @@ static MA_INLINE ma_bool32 ma_has_neon(void) #endif } -#define MA_SIMD_NONE 0 -#define MA_SIMD_SSE2 1 -#define MA_SIMD_AVX2 2 -#define MA_SIMD_NEON 3 - -#ifndef MA_PREFERRED_SIMD - # if defined(MA_SUPPORT_SSE2) && defined(MA_PREFER_SSE2) - #define MA_PREFERRED_SIMD MA_SIMD_SSE2 - #elif defined(MA_SUPPORT_AVX2) && defined(MA_PREFER_AVX2) - #define MA_PREFERRED_SIMD MA_SIMD_AVX2 - #elif defined(MA_SUPPORT_NEON) && defined(MA_PREFER_NEON) - #define MA_PREFERRED_SIMD MA_SIMD_NEON - #else - #define MA_PREFERRED_SIMD MA_SIMD_NONE - #endif -#endif - #if defined(__has_builtin) #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) #else @@ -11566,7 +11692,7 @@ static void ma_sleep__posix(ma_uint32 milliseconds) (void)milliseconds; MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ #else - #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L + #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) struct timespec ts; ts.tv_sec = milliseconds / 1000; ts.tv_nsec = milliseconds % 1000 * 1000000; @@ -11714,6 +11840,20 @@ static MA_INLINE void ma_restore_denormals(unsigned int prevState) } +#ifdef MA_ANDROID +#include + +int ma_android_sdk_version() +{ + char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; + if (__system_property_get("ro.build.version.sdk", sdkVersion)) { + return atoi(sdkVersion); + } + + return 0; +} +#endif + #ifndef MA_COINIT_VALUE #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ @@ -11867,6 +12007,14 @@ MA_API const char* ma_version_string(void) Standard Library Stuff ******************************************************************************/ +#ifndef MA_ASSERT +#ifdef MA_WIN32 +#define MA_ASSERT(condition) assert(condition) +#else +#define MA_ASSERT(condition) assert(condition) +#endif +#endif + #ifndef MA_MALLOC #ifdef MA_WIN32 #define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz)) @@ -11893,6 +12041,11 @@ Standard Library Stuff static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) { + if (p == NULL) { + MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */ + return; + } + #ifdef MA_WIN32 ZeroMemory(p, sz); #else @@ -11922,14 +12075,6 @@ static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) #endif #endif -#ifndef MA_ASSERT -#ifdef MA_WIN32 -#define MA_ASSERT(condition) assert(condition) -#else -#define MA_ASSERT(condition) assert(condition) -#endif -#endif - #define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) #define ma_countof(x) (sizeof(x) / sizeof(x[0])) @@ -11974,6 +12119,40 @@ static MA_INLINE double ma_sqrtd(double x) } +static MA_INLINE float ma_rsqrtf(float x) +{ + #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)) + { + /* + For SSE we can use RSQRTSS. + + This Stack Overflow post suggests that compilers don't necessarily generate optimal code + when using intrinsics: + + https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper + + I'm going to do something similar here, but a bit simpler. + */ + #if defined(__GNUC__) || defined(__clang__) + { + float result; + __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x)); + return result; + } + #else + { + return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x))); + } + #endif + } + #else + { + return 1 / (float)ma_sqrtd(x); + } + #endif +} + + static MA_INLINE float ma_sinf(float x) { return (float)ma_sind((float)x); @@ -13069,6 +13248,9 @@ MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) } #if defined(MA_DEBUG_OUTPUT) +#if defined(MA_ANDROID) + #include +#endif /* Customize this to use a specific tag in __android_log_print() for debug output messages. */ #ifndef MA_ANDROID_LOG_TAG @@ -13733,11 +13915,17 @@ typedef unsigned char c89atomic_bool; #define C89ATOMIC_32BIT #endif #endif +#if defined(__arm__) || defined(_M_ARM) +#define C89ATOMIC_ARM32 +#endif +#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#define C89ATOMIC_ARM64 +#endif #if defined(__x86_64__) || defined(_M_X64) #define C89ATOMIC_X64 #elif defined(__i386) || defined(_M_IX86) #define C89ATOMIC_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#elif defined(C89ATOMIC_ARM32) || defined(C89ATOMIC_ARM64) #define C89ATOMIC_ARM #endif #if defined(_MSC_VER) @@ -13758,6 +13946,56 @@ typedef unsigned char c89atomic_bool; #define C89ATOMIC_HAS_32 #define C89ATOMIC_HAS_64 #if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) + #define C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, c89atomicType, msvcType) \ + c89atomicType result; \ + switch (order) \ + { \ + case c89atomic_memory_order_relaxed: \ + { \ + result = (c89atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case c89atomic_memory_order_consume: \ + case c89atomic_memory_order_acquire: \ + { \ + result = (c89atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case c89atomic_memory_order_release: \ + { \ + result = (c89atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case c89atomic_memory_order_acq_rel: \ + case c89atomic_memory_order_seq_cst: \ + default: \ + { \ + result = (c89atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + } \ + return result; + #define C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, c89atomicType, msvcType) \ + c89atomicType result; \ + switch (order) \ + { \ + case c89atomic_memory_order_relaxed: \ + { \ + result = (c89atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case c89atomic_memory_order_consume: \ + case c89atomic_memory_order_acquire: \ + { \ + result = (c89atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case c89atomic_memory_order_release: \ + { \ + result = (c89atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case c89atomic_memory_order_acq_rel: \ + case c89atomic_memory_order_seq_cst: \ + default: \ + { \ + result = (c89atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + } \ + return result; #define c89atomic_memory_order_relaxed 0 #define c89atomic_memory_order_consume 1 #define c89atomic_memory_order_acquire 2 @@ -13896,29 +14134,45 @@ typedef unsigned char c89atomic_bool; #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, c89atomic_uint8, char); + #else (void)order; return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); + #endif } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, c89atomic_uint16, short); + #else (void)order; return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); + #endif } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, c89atomic_uint32, long); + #else (void)order; return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src); + #endif } #endif #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, c89atomic_uint64, long long); + #else (void)order; return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); + #endif } #else #endif @@ -13981,29 +14235,45 @@ typedef unsigned char c89atomic_bool; #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, c89atomic_uint8, char); + #else (void)order; return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); + #endif } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, c89atomic_uint16, short); + #else (void)order; return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); + #endif } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, c89atomic_uint32, long); + #else (void)order; return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); + #endif } #endif #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, c89atomic_uint64, long long); + #else (void)order; return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + #endif } #else #endif @@ -14032,6 +14302,8 @@ typedef unsigned char c89atomic_bool; #else #if defined(C89ATOMIC_X64) #define c89atomic_thread_fence(order) __faststorefence(), (void)order + #elif defined(C89ATOMIC_ARM64) + #define c89atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order #else static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order) { @@ -14045,29 +14317,45 @@ typedef unsigned char c89atomic_bool; #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, c89atomic_uint8, char); + #else (void)order; return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0); + #endif } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, c89atomic_uint16, short); + #else (void)order; return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0); + #endif } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, c89atomic_uint32, long); + #else (void)order; return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0); + #endif } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, c89atomic_uint64, long long); + #else (void)order; return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0); + #endif } #endif #if defined(C89ATOMIC_HAS_8) @@ -14137,6 +14425,9 @@ typedef unsigned char c89atomic_bool; #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, c89atomic_uint8, char); + #else c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { @@ -14145,11 +14436,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, c89atomic_uint16, short); + #else c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { @@ -14158,11 +14453,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, c89atomic_uint32, long); + #else c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { @@ -14171,11 +14470,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, c89atomic_uint64, long long); + #else c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { @@ -14184,11 +14487,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, c89atomic_uint8, char); + #else c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { @@ -14197,11 +14504,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, c89atomic_uint16, short); + #else c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { @@ -14210,11 +14521,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, c89atomic_uint32, long); + #else c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { @@ -14223,11 +14538,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, c89atomic_uint64, long long); + #else c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { @@ -14236,11 +14555,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, c89atomic_uint8, char); + #else c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { @@ -14249,11 +14572,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, c89atomic_uint16, short); + #else c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { @@ -14262,11 +14589,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, c89atomic_uint32, long); + #else c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { @@ -14275,11 +14606,15 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, c89atomic_uint64, long long); + #else c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { @@ -14288,6 +14623,7 @@ typedef unsigned char c89atomic_bool; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; + #endif } #endif #if defined(C89ATOMIC_HAS_8) @@ -15140,10 +15476,10 @@ typedef unsigned char c89atomic_bool; #define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order) #define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) #define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_store_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_store_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_store_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_store_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_store_explicit_i8( dst, src, order) c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_store_explicit_i16(dst, src, order) c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_store_explicit_i32(dst, src, order) c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_store_explicit_i64(dst, src, order) c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) #define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order) #define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order) #define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order) @@ -15284,6 +15620,110 @@ static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); return r.f; } +static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +{ + c89atomic_if32 d; + d.f = desired; + return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder); +} +static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +{ + c89atomic_if64 d; + d.f = desired; + return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder); +} +static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +{ + c89atomic_if32 d; + d.f = desired; + return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder); +} +static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +{ + c89atomic_if64 d; + d.f = desired; + return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder); +} +static C89ATOMIC_INLINE float c89atomic_fetch_add_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +{ + c89atomic_if32 r; + c89atomic_if32 x; + x.f = src; + r.i = c89atomic_fetch_add_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE double c89atomic_fetch_add_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +{ + c89atomic_if64 r; + c89atomic_if64 x; + x.f = src; + r.i = c89atomic_fetch_add_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE float c89atomic_fetch_sub_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +{ + c89atomic_if32 r; + c89atomic_if32 x; + x.f = src; + r.i = c89atomic_fetch_sub_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE double c89atomic_fetch_sub_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +{ + c89atomic_if64 r; + c89atomic_if64 x; + x.f = src; + r.i = c89atomic_fetch_sub_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE float c89atomic_fetch_or_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +{ + c89atomic_if32 r; + c89atomic_if32 x; + x.f = src; + r.i = c89atomic_fetch_or_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE double c89atomic_fetch_or_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +{ + c89atomic_if64 r; + c89atomic_if64 x; + x.f = src; + r.i = c89atomic_fetch_or_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE float c89atomic_fetch_xor_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +{ + c89atomic_if32 r; + c89atomic_if32 x; + x.f = src; + r.i = c89atomic_fetch_xor_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE double c89atomic_fetch_xor_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +{ + c89atomic_if64 r; + c89atomic_if64 x; + x.f = src; + r.i = c89atomic_fetch_xor_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE float c89atomic_fetch_and_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +{ + c89atomic_if32 r; + c89atomic_if32 x; + x.f = src; + r.i = c89atomic_fetch_and_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + return r.f; +} +static C89ATOMIC_INLINE double c89atomic_fetch_and_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +{ + c89atomic_if64 r; + c89atomic_if64 x; + x.f = src; + r.i = c89atomic_fetch_and_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + return r.f; +} #define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) @@ -15292,6 +15732,38 @@ static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* #define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_f32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_f64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_f32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_f64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_f32(dst, src) c89atomic_fetch_add_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_f64(dst, src) c89atomic_fetch_add_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_f32(dst, src) c89atomic_fetch_sub_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_f64(dst, src) c89atomic_fetch_sub_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_f32(dst, src) c89atomic_fetch_or_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_f64(dst, src) c89atomic_fetch_or_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_f32(dst, src) c89atomic_fetch_xor_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_f64(dst, src) c89atomic_fetch_xor_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_f32(dst, src) c89atomic_fetch_and_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_f64(dst, src) c89atomic_fetch_and_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +static C89ATOMIC_INLINE float c89atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) +{ + c89atomic_if32 r; + c89atomic_if32 e, d; + e.f = expected; + d.f = desired; + r.i = c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, e.i, d.i); + return r.f; +} +static C89ATOMIC_INLINE double c89atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) +{ + c89atomic_if64 r; + c89atomic_if64 e, d; + e.f = expected; + d.f = desired; + r.i = c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, e.i, d.i); + return r.f; +} typedef c89atomic_flag c89atomic_spinlock; static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock) { @@ -15313,6 +15785,76 @@ static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlo #endif /* c89atomic.h end */ +#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ + static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ + { \ + return (ma_##type)c89atomic_load_##c89TypeExtension(&x->value); \ + } \ + static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ + { \ + c89atomic_store_##c89TypeExtension(&x->value, value); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ + { \ + return (ma_##type)c89atomic_exchange_##c89TypeExtension(&x->value, value); \ + } \ + static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ + { \ + return c89atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)c89atomic_fetch_add_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)c89atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)c89atomic_fetch_or_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)c89atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)c89atomic_fetch_and_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ + { \ + return (ma_##type)c89atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ + } \ + +#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ + { \ + return c89atomic_load_ptr((void**)&x->value); \ + } \ + static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ + { \ + c89atomic_store_ptr((void**)&x->value, (void*)value); \ + } \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ + { \ + return c89atomic_exchange_ptr((void**)&x->value, (void*)value); \ + } \ + static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ + { \ + return c89atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + } \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ + { \ + return (ma_##type*)c89atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + } \ + +MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) +MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) +MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) +MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) +MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) +MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) @@ -15446,7 +15988,9 @@ static int ma_thread_priority_to_win32(ma_thread_priority priority) static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) { - *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, NULL); + DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ + + *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); if (*pThread == NULL) { return ma_result_from_GetLastError(GetLastError()); } @@ -15465,7 +16009,7 @@ static void ma_thread_wait__win32(ma_thread* pThread) static ma_result ma_mutex_init__win32(ma_mutex* pMutex) { - *pMutex = CreateEventW(NULL, FALSE, TRUE, NULL); + *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); if (*pMutex == NULL) { return ma_result_from_GetLastError(GetLastError()); } @@ -15491,7 +16035,7 @@ static void ma_mutex_unlock__win32(ma_mutex* pMutex) static ma_result ma_event_init__win32(ma_event* pEvent) { - *pEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); if (*pEvent == NULL) { return ma_result_from_GetLastError(GetLastError()); } @@ -15581,6 +16125,10 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority pthread_attr_t attr; if (pthread_attr_init(&attr) == 0) { int scheduler = -1; + + /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ + pAttr = &attr; + if (priority == ma_thread_priority_idle) { #ifdef SCHED_IDLE if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { @@ -15624,9 +16172,8 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority } } - if (pthread_attr_setschedparam(&attr, &sched) == 0) { - pAttr = &attr; - } + /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */ + pthread_attr_setschedparam(&attr, &sched); } } } @@ -16748,7 +17295,7 @@ MA_API ma_result ma_job_process(ma_job* pJob) return MA_INVALID_ARGS; } - if (pJob->toc.breakup.code > MA_JOB_TYPE_COUNT) { + if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) { return MA_INVALID_OPERATION; } @@ -17141,6 +17688,14 @@ DEVICE I/O ************************************************************************************************************************************************************* ************************************************************************************************************************************************************/ + +/* Disable run-time linking on certain backends and platforms. */ +#ifndef MA_NO_RUNTIME_LINKING + #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) + #define MA_NO_RUNTIME_LINKING + #endif +#endif + #ifndef MA_NO_DEVICE_IO #ifdef MA_WIN32 #include @@ -17152,33 +17707,18 @@ DEVICE I/O #include /* For mach_absolute_time() */ #endif -#ifdef MA_ANDROID - #include -#endif - #ifdef MA_POSIX #include #include - #include -#endif -/* -Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When -using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing -compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply -disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am -not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere. -*/ -/*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/ - -/* Disable run-time linking on certain backends. */ -#ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_EMSCRIPTEN) - #define MA_NO_RUNTIME_LINKING + /* No need for dlfcn.h if we're not using runtime linking. */ + #ifndef MA_NO_RUNTIME_LINKING + #include #endif #endif + MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) { if (pDeviceInfo == NULL) { @@ -17195,27 +17735,60 @@ MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, m } +typedef struct +{ + ma_backend backend; + const char* pName; +} ma_backend_info; + +static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */ +{ + {ma_backend_wasapi, "WASAPI"}, + {ma_backend_dsound, "DirectSound"}, + {ma_backend_winmm, "WinMM"}, + {ma_backend_coreaudio, "Core Audio"}, + {ma_backend_sndio, "sndio"}, + {ma_backend_audio4, "audio(4)"}, + {ma_backend_oss, "OSS"}, + {ma_backend_pulseaudio, "PulseAudio"}, + {ma_backend_alsa, "ALSA"}, + {ma_backend_jack, "JACK"}, + {ma_backend_aaudio, "AAudio"}, + {ma_backend_opensl, "OpenSL|ES"}, + {ma_backend_webaudio, "Web Audio"}, + {ma_backend_custom, "Custom"}, + {ma_backend_null, "Null"} +}; + MA_API const char* ma_get_backend_name(ma_backend backend) { - switch (backend) - { - case ma_backend_wasapi: return "WASAPI"; - case ma_backend_dsound: return "DirectSound"; - case ma_backend_winmm: return "WinMM"; - case ma_backend_coreaudio: return "Core Audio"; - case ma_backend_sndio: return "sndio"; - case ma_backend_audio4: return "audio(4)"; - case ma_backend_oss: return "OSS"; - case ma_backend_pulseaudio: return "PulseAudio"; - case ma_backend_alsa: return "ALSA"; - case ma_backend_jack: return "JACK"; - case ma_backend_aaudio: return "AAudio"; - case ma_backend_opensl: return "OpenSL|ES"; - case ma_backend_webaudio: return "Web Audio"; - case ma_backend_custom: return "Custom"; - case ma_backend_null: return "Null"; - default: return "Unknown"; + if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) { + return "Unknown"; } + + return gBackendInfo[backend].pName; +} + +MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend) +{ + size_t iBackend; + + if (pBackendName == NULL) { + return MA_INVALID_ARGS; + } + + for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) { + if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) { + if (pBackend != NULL) { + *pBackend = gBackendInfo[iBackend].backend; + } + + return MA_SUCCESS; + } + } + + /* Getting here means the backend name is unknown. */ + return MA_INVALID_ARGS; } MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) @@ -17290,16 +17863,7 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) #if defined(MA_HAS_AAUDIO) #if defined(MA_ANDROID) { - char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; - if (__system_property_get("ro.build.version.sdk", sdkVersion)) { - if (atoi(sdkVersion) >= 26) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } + return ma_android_sdk_version() >= 26; } #else return MA_FALSE; @@ -17311,16 +17875,7 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) #if defined(MA_HAS_OPENSL) #if defined(MA_ANDROID) { - char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; - if (__system_property_get("ro.build.version.sdk", sdkVersion)) { - if (atoi(sdkVersion) >= 9) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } + return ma_android_sdk_version() >= 9; } #else return MA_TRUE; @@ -17570,6 +18125,7 @@ static ma_result ma_result_from_HRESULT(HRESULT hr) } } +typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(LPVOID pvReserved); typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); typedef void (WINAPI * MA_PFN_CoUninitialize)(void); typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); @@ -17713,6 +18269,7 @@ Dynamic Linking *******************************************************************************/ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) { +#ifndef MA_NO_RUNTIME_LINKING ma_handle handle; ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); @@ -17744,10 +18301,17 @@ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) (void)pContext; /* It's possible for pContext to be unused. */ return handle; +#else + /* Runtime linking is disabled. */ + (void)pContext; + (void)filename; + return NULL; +#endif } MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) { +#ifndef MA_NO_RUNTIME_LINKING #ifdef _WIN32 FreeLibrary((HMODULE)handle); #else @@ -17755,10 +18319,16 @@ MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) #endif (void)pContext; +#else + /* Runtime linking is disabled. */ + (void)pContext; + (void)handle; +#endif } MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol) { +#ifndef MA_NO_RUNTIME_LINKING ma_proc proc; ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); @@ -17782,6 +18352,13 @@ MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symb (void)pContext; /* It's possible for pContext to be unused. */ return proc; +#else + /* Runtime linking is disabled. */ + (void)pContext; + (void)handle; + (void)symbol; + return NULL; +#endif } @@ -17986,7 +18563,7 @@ static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* /* The intermediary buffer has just been filled. */ pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; } - } + } } /* If we're in duplex mode we might need to do a refill of the data. */ @@ -18338,7 +18915,7 @@ static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, /* A helper for changing the state of the device. */ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) { - c89atomic_exchange_i32((ma_int32*)&pDevice->state, (ma_int32)newState); + ma_atomic_device_state_set(&pDevice->state, newState); } @@ -18849,7 +19426,7 @@ static ma_result ma_device_start__null(ma_device* pDevice) ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); - c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE); + ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); return MA_SUCCESS; } @@ -18859,10 +19436,17 @@ static ma_result ma_device_stop__null(ma_device* pDevice) ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); - c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE); + ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); return MA_SUCCESS; } +static ma_bool32 ma_device_is_started__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + return ma_atomic_bool32_get(&pDevice->null_device.isStarted); +} + static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { ma_result result = MA_SUCCESS; @@ -18873,7 +19457,7 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame *pFramesWritten = 0; } - wasStartedOnEntry = c89atomic_load_32(&pDevice->null_device.isStarted); + wasStartedOnEntry = ma_device_is_started__null(pDevice); /* Keep going until everything has been read. */ totalPCMFramesProcessed = 0; @@ -18899,7 +19483,7 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; - if (!c89atomic_load_32(&pDevice->null_device.isStarted) && !wasStartedOnEntry) { + if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { result = ma_device_start__null(pDevice); if (result != MA_SUCCESS) { break; @@ -18919,7 +19503,7 @@ static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrame ma_uint64 currentFrame; /* Stop waiting if the device has been stopped. */ - if (!c89atomic_load_32(&pDevice->null_device.isStarted)) { + if (!ma_device_is_started__null(pDevice)) { break; } @@ -18990,7 +19574,7 @@ static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_u ma_uint64 currentFrame; /* Stop waiting if the device has been stopped. */ - if (!c89atomic_load_32(&pDevice->null_device.isStarted)) { + if (!ma_device_is_started__null(pDevice)) { break; } @@ -19056,7 +19640,7 @@ WIN32 COMMON *******************************************************************************/ #if defined(MA_WIN32) #if defined(MA_WIN32_DESKTOP) - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) + #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) @@ -19917,7 +20501,7 @@ static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHand pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; pHandler->counter = 1; - pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); if (pHandler->hEvent == NULL) { return ma_result_from_GetLastError(GetLastError()); } @@ -20093,12 +20677,18 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged } /* We only care about devices with the same data flow and role as the current device. */ - if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || - (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) { + if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || + (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || + (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); return S_OK; } + /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ + if (pThis->pDevice->type == ma_device_type_loopback) { + dataFlow = ma_eCapture; + } + /* Don't do automatic stream routing if we're not allowed. */ if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { @@ -20119,7 +20709,6 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged - /* Second attempt at device rerouting. We're going to retrieve the device's state at the time of the route change. We're then going to stop the device, reinitialize the device, and then start @@ -20129,37 +20718,49 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged ma_uint32 previousState = ma_device_get_state(pThis->pDevice); ma_bool8 restartDevice = MA_FALSE; + if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n"); + return S_OK; + } + if (previousState == ma_device_state_started) { ma_device_stop(pThis->pDevice); restartDevice = MA_TRUE; } if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ - if (dataFlow == ma_eRender) { - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); + ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); + { + if (dataFlow == ma_eRender) { + ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; + if (pThis->pDevice->wasapi.isDetachedPlayback) { + pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { - restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ - } else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ + if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { + restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ + } + else { + restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ + } } } - } else { - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); + else { + ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; + if (pThis->pDevice->wasapi.isDetachedCapture) { + pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { - restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ - } else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ + if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { + restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ + } + else { + restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ + } } } } + ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); if (restartDevice) { ma_device_start(pThis->pDevice); @@ -21797,7 +22398,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, however, because we want to block until we actually have something for the first call to ma_device_read(). */ - pDevice->wasapi.hEventCapture = CreateEventW(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ + pDevice->wasapi.hEventCapture = CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ if (pDevice->wasapi.hEventCapture == NULL) { result = ma_result_from_GetLastError(GetLastError()); @@ -21879,7 +22480,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able to get passed WaitForMultipleObjects(). */ - pDevice->wasapi.hEventPlayback = CreateEventW(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ + pDevice->wasapi.hEventPlayback = CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ if (pDevice->wasapi.hEventPlayback == NULL) { result = ma_result_from_GetLastError(GetLastError()); @@ -21933,7 +22534,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { - if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) { + if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; } if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { @@ -21941,6 +22542,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf } } + ma_mutex_init(&pDevice->wasapi.rerouteLock); + hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_device_uninit__wasapi(pDevice); @@ -21961,8 +22564,8 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf } #endif - c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE); - c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE); + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); return MA_SUCCESS; } @@ -22040,18 +22643,17 @@ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type de } ma_device__post_init_setup(pDevice, deviceType); - ma_device__on_notification_rerouted(pDevice); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); + return MA_SUCCESS; } -static ma_result ma_device_start__wasapi(ma_device* pDevice) +static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) { HRESULT hr; - MA_ASSERT(pDevice != NULL); - if (pDevice->pContext->wasapi.hAvrt) { LPCWSTR pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); if (pTaskName) { @@ -22067,7 +22669,7 @@ static ma_result ma_device_start__wasapi(ma_device* pDevice) return ma_result_from_HRESULT(hr); } - c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE); + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { @@ -22077,13 +22679,29 @@ static ma_result ma_device_start__wasapi(ma_device* pDevice) return ma_result_from_HRESULT(hr); } - c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE); + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); } return MA_SUCCESS; } -static ma_result ma_device_stop__wasapi(ma_device* pDevice) +static ma_result ma_device_start__wasapi(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* Wait for any rerouting to finish before attempting to start the device. */ + ma_mutex_lock(&pDevice->wasapi.rerouteLock); + { + result = ma_device_start__wasapi_nolock(pDevice); + } + ma_mutex_unlock(&pDevice->wasapi.rerouteLock); + + return result; +} + +static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) { ma_result result; HRESULT hr; @@ -22112,12 +22730,12 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) /* If we have a mapped buffer we need to release it. */ if (pDevice->wasapi.pMappedBufferCapture != NULL) { ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.pMappedBufferCapture = NULL; pDevice->wasapi.mappedBufferCaptureCap = 0; pDevice->wasapi.mappedBufferCaptureLen = 0; } - c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE); + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { @@ -22125,13 +22743,14 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. */ - if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { + if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; if (pDevice->playback.shareMode == ma_share_mode_exclusive) { WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); - } else { + } + else { ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; ma_uint32 framesAvailablePlayback; for (;;) { @@ -22174,17 +22793,33 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) if (pDevice->wasapi.pMappedBufferPlayback != NULL) { ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.pMappedBufferPlayback = NULL; pDevice->wasapi.mappedBufferPlaybackCap = 0; pDevice->wasapi.mappedBufferPlaybackLen = 0; } - c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE); + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); } return MA_SUCCESS; } +static ma_result ma_device_stop__wasapi(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* Wait for any rerouting to finish before attempting to stop the device. */ + ma_mutex_lock(&pDevice->wasapi.rerouteLock); + { + result = ma_device_stop__wasapi_nolock(pDevice); + } + ma_mutex_unlock(&pDevice->wasapi.rerouteLock); + + return result; +} + #ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS #define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 @@ -22291,7 +22926,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui for (i = 0; i < iterationCount; i += 1) { hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %d.\n", hr); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); break; } @@ -22316,7 +22951,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui } if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %d.\n", hr); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr); } break; @@ -23708,6 +24343,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf ma_uint32 periodSizeInFrames; ma_uint32 periodCount; MA_DSBUFFERDESC descDS; + WORD nativeChannelCount; + DWORD nativeChannelMask = 0; result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); if (result != MA_SUCCESS) { @@ -23741,21 +24378,25 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf return ma_result_from_HRESULT(hr); } - if (pDescriptorPlayback->channels == 0) { - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - DWORD speakerConfig; + if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { + DWORD speakerConfig; - /* It supports at least stereo, but could support more. */ - wf.Format.nChannels = 2; + /* It supports at least stereo, but could support more. */ + nativeChannelCount = 2; - /* Look at the speaker configuration to get a better idea on the channel count. */ - if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - wf.Format.nChannels = 1; + /* Look at the speaker configuration to get a better idea on the channel count. */ + if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { + ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); } + } else { + /* It does not support stereo, which means we are stuck with mono. */ + nativeChannelCount = 1; + nativeChannelMask = 0x00000001; + } + + if (pDescriptorPlayback->channels == 0) { + wf.Format.nChannels = nativeChannelCount; + wf.dwChannelMask = nativeChannelMask; } if (pDescriptorPlayback->sampleRate == 0) { @@ -23777,11 +24418,28 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer and compare the result with the format that was requested with the SetFormat method. */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf); + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, &wf.Format); if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); - return ma_result_from_HRESULT(hr); + /* + If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have + observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a + sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will + use 44100 for the sample rate. + */ + wf.Format.cbSize = sizeof(wf.Format); + wf.Format.wFormatTag = WAVE_FORMAT_PCM; + wf.Format.wBitsPerSample = 16; + wf.Format.nChannels = nativeChannelCount; + wf.Format.nSamplesPerSec = 44100; + wf.Format.nBlockAlign = wf.Format.nChannels * (wf.Format.wBitsPerSample / 8); + wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; + + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, &wf.Format); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); + return ma_result_from_HRESULT(hr); + } } /* Get the _actual_ properties of the buffer. */ @@ -23828,7 +24486,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf descDS.dwSize = sizeof(descDS); descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); - descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; + descDS.lpwfxFormat = &pActualFormat->Format; hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); @@ -24385,6 +25043,18 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_ pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); + /* + We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too + well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient + place to just disable the DirectSound backend for Windows 95. + */ + if (pContext->dsound.DirectSoundCreate == NULL || + pContext->dsound.DirectSoundEnumerateA == NULL || + pContext->dsound.DirectSoundCaptureCreate == NULL || + pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { + return MA_API_NOT_FOUND; + } + pCallbacks->onContextInit = ma_context_init__dsound; pCallbacks->onContextUninit = ma_context_uninit__dsound; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; @@ -24934,7 +25604,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventCapture = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL); + pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventCapture == NULL) { errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; @@ -24972,7 +25642,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventPlayback = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL); + pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventPlayback == NULL) { errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; @@ -26617,7 +27287,7 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic isUsingMMap = MA_FALSE; #if 0 /* NOTE: MMAP mode temporarily disabled. */ if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ - if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) { + if (!pConfig->alsa.noMMap) { if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { pDevice->alsa.isUsingMMap = MA_TRUE; } @@ -29300,7 +29970,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi sampleRate = pDescriptorCapture->sampleRate; } - + result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); if (result != MA_SUCCESS) { @@ -31092,15 +31762,15 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* { pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - } /* Intentional fallthrough. */ + } MA_FALLTHROUGH; /* Intentional fallthrough. */ case kAudioChannelLayoutTag_Hexagonal: { pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - } /* Intentional fallthrough. */ + } MA_FALLTHROUGH; /* Intentional fallthrough. */ case kAudioChannelLayoutTag_Pentagonal: { pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - } /* Intentional fallghrough. */ + } MA_FALLTHROUGH; /* Intentional fallthrough. */ case kAudioChannelLayoutTag_Quadraphonic: { pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; @@ -32438,7 +33108,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); return noErr; } - + pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; MA_ASSERT(pRenderedBufferList); @@ -32876,7 +33546,7 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) */ ma_device__on_notification_interruption_began(m_pDevice); } break; - + case AVAudioSessionInterruptionTypeEnded: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); @@ -32930,7 +33600,7 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) } ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); - + /* Let the application know about the route change. */ ma_device__on_notification_rerouted(m_pDevice); } @@ -33303,7 +33973,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev @autoreleasepool { AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; MA_ASSERT(pAudioSession != NULL); - + [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); } @@ -33544,7 +34214,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c #if defined(MA_APPLE_DESKTOP) ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - + /* If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly switch the device in the background. @@ -33608,7 +34278,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c #if defined(MA_APPLE_DESKTOP) ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - + /* If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly switch the device in the background. @@ -36255,6 +36925,9 @@ static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_con #endif /* OSS */ + + + /****************************************************************************** AAudio Backend @@ -36273,6 +36946,7 @@ typedef int32_t ma_aaudio_performance_mo typedef int32_t ma_aaudio_usage_t; typedef int32_t ma_aaudio_content_type_t; typedef int32_t ma_aaudio_input_preset_t; +typedef int32_t ma_aaudio_allowed_capture_policy_t; typedef int32_t ma_aaudio_data_callback_result_t; typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; typedef struct ma_AAudioStream_t* ma_AAudioStream; @@ -36347,6 +37021,11 @@ typedef struct ma_AAudioStream_t* ma_AAudioStream; #define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 #define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 +/* Allowed Capture Policies */ +#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1 +#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2 +#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3 + /* Callback results. */ #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1 @@ -36371,6 +37050,7 @@ typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMod typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); +typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); @@ -36448,8 +37128,22 @@ static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_prese return MA_AAUDIO_INPUT_PRESET_GENERIC; } +static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy) +{ + switch (allowedCapturePolicy) { + case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; + case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; + case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE; + default: break; + } + + return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; +} + static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) { + ma_result result; + ma_job job; ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); @@ -36458,26 +37152,24 @@ static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUs ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); /* - From the documentation for AAudio, when a device is disconnected all we can do is stop it. However, we cannot stop it from the callback - we need - to do it from another thread. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely. + When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, + we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this + cleanly and safely. */ - if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { - /* We need to post a job to the job thread for processing. This will reroute the device by reinitializing the stream. */ - ma_result result; - ma_job job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); - job.data.device.aaudio.reroute.pDevice = pDevice; + job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); + job.data.device.aaudio.reroute.pDevice = pDevice; - if (pStream == pDevice->aaudio.pStreamCapture) { - job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; - } else { - job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; - } + if (pStream == pDevice->aaudio.pStreamCapture) { + job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; + } + else { + job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; + } - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); - return; - } + result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); + return; } } @@ -36507,7 +37199,6 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* { ma_AAudioStreamBuilder* pBuilder; ma_aaudio_result_t resultAA; - ma_uint32 bufferCapacityInFrames; /* Safety. */ *ppBuilder = NULL; @@ -36549,17 +37240,26 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* } } + /* - AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you - retrieve the actual sample rate until after you've opened the stream. But you need to configure - the buffer capacity before you open the stream... :/ - - To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. + There have been reports where setting the frames per data callback results in an error + later on from Android. To address this, I'm experimenting with simply not setting it on + anything from Android 11 and earlier. Suggestions welcome on how we might be able to make + this more targetted. */ - bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; + if (pConfig->aaudio.enableCompatibilityWorkarounds && ma_android_sdk_version() > 30) { + /* + AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you + retrieve the actual sample rate until after you've opened the stream. But you need to configure + the buffer capacity before you open the stream... :/ - ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); - ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); + To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. + */ + ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; + + ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); + ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); + } if (deviceType == ma_device_type_capture) { if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { @@ -36576,6 +37276,10 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); } + if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { + ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); + } + ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); } @@ -36843,6 +37547,7 @@ static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_conf pDevice->aaudio.usage = pConfig->aaudio.usage; pDevice->aaudio.contentType = pConfig->aaudio.contentType; pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; + pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { @@ -37019,6 +37724,7 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev deviceConfig.aaudio.usage = pDevice->aaudio.usage; deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; + deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; deviceConfig.periods = 1; @@ -37154,6 +37860,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); + pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close"); pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState"); @@ -37196,7 +37903,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ return result; } } - + (void)pConfig; return MA_SUCCESS; @@ -38495,6 +39202,29 @@ Web Audio Backend #ifdef MA_HAS_WEBAUDIO #include +#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) + #include + #define MA_SUPPORT_AUDIO_WORKLETS +#endif + +/* +TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS. +*/ +#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS) + #define MA_USE_AUDIO_WORKLETS +#endif + +/* The thread stack size must be a multiple of 16. */ +#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE +#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384 +#endif + +#if defined(MA_USE_AUDIO_WORKLETS) +#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced" +#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive" +#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" +#endif + static ma_bool32 ma_is_capture_supported__webaudio() { return EM_ASM_INT({ @@ -38505,6 +39235,16 @@ static ma_bool32 ma_is_capture_supported__webaudio() #ifdef __cplusplus extern "C" { #endif +void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_malloc(sz, pAllocationCallbacks); +} + +void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_free(p, pAllocationCallbacks); +} + void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) { ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); @@ -38595,13 +39335,14 @@ static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_d return MA_SUCCESS; } - +#if !defined(MA_USE_AUDIO_WORKLETS) static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex) { MA_ASSERT(pDevice != NULL); EM_ASM({ var device = miniaudio.get_device_by_index($0); + var pAllocationCallbacks = $3; /* Make sure all nodes are disconnected and marked for collection. */ if (device.scriptNode !== undefined) { @@ -38623,7 +39364,7 @@ static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_ty /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */ if (device.intermediaryBuffer !== undefined) { - Module._free(device.intermediaryBuffer); + _ma_free_emscripten(device.intermediaryBuffer, pAllocationCallbacks); device.intermediaryBuffer = undefined; device.intermediaryBufferView = undefined; device.intermediaryBufferSizeInBytes = undefined; @@ -38631,7 +39372,32 @@ static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_ty /* Make sure the device is untracked so the slot can be reused later. */ miniaudio.untrack_device_by_index($0); - }, deviceIndex, deviceType); + }, deviceIndex, deviceType, &pDevice->pContext->allocationCallbacks); +} +#endif + +static void ma_device_uninit_by_type__webaudio(ma_device* pDevice, ma_device_type deviceType) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); + +#if defined(MA_USE_AUDIO_WORKLETS) + if (deviceType == ma_device_type_capture) { + ma_free(pDevice->webaudio.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->webaudio.pStackBufferCapture, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContextCapture); + } else { + ma_free(pDevice->webaudio.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->webaudio.pStackBufferPlayback, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContextPlayback); + } +#else + if (deviceType == ma_device_type_capture) { + ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); + } else { + ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); + } +#endif } static ma_result ma_device_uninit__webaudio(ma_device* pDevice) @@ -38639,11 +39405,11 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice) MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); + ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); + ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_playback); } return MA_SUCCESS; @@ -38651,10 +39417,16 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice) static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { +#if defined(MA_USE_AUDIO_WORKLETS) + (void)pDescriptor; + (void)nativeSampleRate; + (void)performanceProfile; + + return 256; +#else /* - There have been reports of the default buffer size being too small on some browsers. There have been reports of the default buffer - size being too small on some browsers. If we're using default buffer size, we'll make sure the period size is a big biffer than our - standard defaults. + There have been reports of the default buffer size being too small on some browsers. If we're using + the default buffer size, we'll make sure the period size is bigger than our standard defaults. */ ma_uint32 periodSizeInFrames; @@ -38682,11 +39454,177 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(co } return periodSizeInFrames; +#endif } + +#if defined(MA_USE_AUDIO_WORKLETS) +typedef struct +{ + ma_device* pDevice; + const ma_device_config* pConfig; + ma_device_descriptor* pDescriptor; + ma_device_type deviceType; + ma_uint32 channels; +} ma_audio_worklet_thread_initialized_data; + +static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_uint32 frameCount; + ma_uint32 framesProcessed; + + (void)paramCount; + (void)pParams; + + /* + The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels + like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer + to variables instead of a hard coded number. In any case, will follow along for the time being. + + Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio + for further processing. + */ + frameCount = 128; + + /* Run the conversion logic in a loop for robustness. */ + framesProcessed = 0; + while (framesProcessed < frameCount) { + ma_uint32 framesToProcessThisIteration = frameCount - framesProcessed; + + if (inputCount > 0) { + if (framesToProcessThisIteration > pDevice->webaudio.intermediaryBufferSizeInFramesPlayback) { + framesToProcessThisIteration = pDevice->webaudio.intermediaryBufferSizeInFramesPlayback; + } + + /* Input data needs to be interleaved before we hand it to the client. */ + for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) { + for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { + pDevice->webaudio.pIntermediaryBufferCapture[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + framesProcessed + iFrame]; + } + } + + ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferCapture); + } + + if (outputCount > 0) { + ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferPlayback); + + /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ + for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) { + for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { + pOutputs[0].data[frameCount*iChannel + framesProcessed + iFrame] = pDevice->webaudio.pIntermediaryBufferPlayback[iFrame*pDevice->playback.internalChannels + iChannel]; + } + } + } + + framesProcessed += framesToProcessThisIteration; + } + + return EM_TRUE; +} + + +static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) +{ + ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; + EmscriptenAudioWorkletNodeCreateOptions workletNodeOptions; + EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletNode; + int outputChannelCount = 0; + + if (success == EM_FALSE) { + pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + return; + } + + MA_ZERO_OBJECT(&workletNodeOptions); + + if (pParameters->deviceType == ma_device_type_capture) { + workletNodeOptions.numberOfInputs = 1; + } else { + outputChannelCount = (int)pParameters->channels; /* Safe cast. */ + + workletNodeOptions.numberOfOutputs = 1; + workletNodeOptions.outputChannelCounts = &outputChannelCount; + } + + /* Here is where we create the node that will do our processing. */ + workletNode = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &workletNodeOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); + + if (pParameters->deviceType == ma_device_type_capture) { + pParameters->pDevice->webaudio.workletNodeCapture = workletNode; + } else { + pParameters->pDevice->webaudio.workletNodePlayback = workletNode; + } + + /* + With the worklet node created we can now attach it to the graph. This is done differently depending on whether or not + it's capture or playback mode. + */ + if (pParameters->deviceType == ma_device_type_capture) { + EM_ASM({ + var workletNode = emscriptenGetAudioObject($0); + var audioContext = emscriptenGetAudioObject($1); + + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + audioContext.streamNode = audioContext.createMediaStreamSource(stream); + audioContext.streamNode.connect(workletNode); + + /* + Now that the worklet node has been connected, do we need to inspect workletNode.channelCount + to check the actual channel count, or is it safe to assume it's always 2? + */ + }) + .catch(function(error) { + + }); + }, workletNode, audioContext); + } else { + EM_ASM({ + var workletNode = emscriptenGetAudioObject($0); + var audioContext = emscriptenGetAudioObject($1); + workletNode.connect(audioContext.destination); + }, workletNode, audioContext); + } + + pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", workletNode); + + /* Our parameter data is no longer needed. */ + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); +} + + + +static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) +{ + ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; + WebAudioWorkletProcessorCreateOptions workletProcessorOptions; + + MA_ASSERT(pParameters != NULL); + + if (success == EM_FALSE) { + pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + return; + } + + MA_ZERO_OBJECT(&workletProcessorOptions); + workletProcessorOptions.name = "miniaudio"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */ + + emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters); +} +#endif + static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { - int deviceIndex; +#if defined(MA_USE_AUDIO_WORKLETS) + EMSCRIPTEN_WEBAUDIO_T audioContext; + void* pStackBuffer; + size_t intermediaryBufferSizeInFrames; + float* pIntermediaryBuffer; +#endif ma_uint32 channels; ma_uint32 sampleRate; ma_uint32 periodSizeInFrames; @@ -38706,13 +39644,99 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames); +#if defined(MA_USE_AUDIO_WORKLETS) + { + ma_audio_worklet_thread_initialized_data* pInitParameters; + EmscriptenWebAudioCreateAttributes audioContextAttributes; + + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; + audioContextAttributes.sampleRate = sampleRate; + + /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ + audioContext = emscripten_create_audio_context(&audioContextAttributes); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: AUDIO CONTEXT CREATED\n"); + + /* + We now need to create a worker thread. This is a bit weird because we need to allocate our + own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to + allocate this on the heap to keep it simple. + */ + pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); + if (pStackBuffer == NULL) { + emscripten_destroy_audio_context(audioContext); + return MA_OUT_OF_MEMORY; + } + + /* + We need an intermediary buffer for data conversion. WebAudio reports data in uninterleaved + format whereas we require it to be interleaved. We'll do this in chunks of 128 frames. + */ + intermediaryBufferSizeInFrames = 128; + pIntermediaryBuffer = ma_malloc(intermediaryBufferSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); + if (pIntermediaryBuffer == NULL) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(audioContext); + return MA_OUT_OF_MEMORY; + } + + pInitParameters = ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); + if (pInitParameters == NULL) { + ma_free(pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(audioContext); + return MA_OUT_OF_MEMORY; + } + + pInitParameters->pDevice = pDevice; + pInitParameters->pConfig = pConfig; + pInitParameters->pDescriptor = pDescriptor; + pInitParameters->deviceType = deviceType; + pInitParameters->channels = channels; + + /* + We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of + the Emscripten WebAudio stuff is asynchronous. + */ + pDevice->webaudio.isInitialized = MA_FALSE; + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: CREATING WORKLET\n"); + + emscripten_start_wasm_audio_worklet_thread_async(audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); + + /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ + while (pDevice->webaudio.isInitialized == MA_FALSE) { + emscripten_sleep(1); + } + + /* + Now that initialization is finished we can go ahead and extract our channel count so that + miniaudio can set up a data converter at a higher level. + */ + if (deviceType == ma_device_type_capture) { + /* + For capture we won't actually know what the channel count is. Everything I've seen seems + to indicate that the default channel count is 2, so I'm sticking with that. + */ + channels = 2; + } else { + /* Get the channel count from the audio context. */ + channels = (ma_uint32)EM_ASM_INT({ + return emscriptenGetAudioObject($0).destination.channelCount; + }, audioContext); + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: INITIALIZED. channels = %u\n", channels); + } +#else /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ - deviceIndex = EM_ASM_INT({ - var channels = $0; - var sampleRate = $1; - var bufferSize = $2; /* In PCM frames. */ - var isCapture = $3; - var pDevice = $4; + int deviceIndex = EM_ASM_INT({ + var channels = $0; + var sampleRate = $1; + var bufferSize = $2; /* In PCM frames. */ + var isCapture = $3; + var pDevice = $4; + var pAllocationCallbacks = $5; if (typeof(window.miniaudio) === 'undefined') { return -1; /* Context not initialized. */ @@ -38725,12 +39749,9 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d device.webaudio.suspend(); device.state = 1; /* ma_device_state_stopped */ - /* - We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between - JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free(). - */ + /* We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. */ device.intermediaryBufferSizeInBytes = channels * bufferSize * 4; - device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes); + device.intermediaryBuffer = _ma_malloc_emscripten(device.intermediaryBufferSizeInBytes, pAllocationCallbacks); device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); /* @@ -38871,25 +39892,45 @@ static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_d } return miniaudio.track_device(device); - }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice); + }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice, &pDevice->pContext->allocationCallbacks); if (deviceIndex < 0) { return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } +#endif +#if defined(MA_USE_AUDIO_WORKLETS) + if (deviceType == ma_device_type_capture) { + pDevice->webaudio.audioContextCapture = audioContext; + pDevice->webaudio.pStackBufferCapture = pStackBuffer; + pDevice->webaudio.intermediaryBufferSizeInFramesCapture = intermediaryBufferSizeInFrames; + pDevice->webaudio.pIntermediaryBufferCapture = pIntermediaryBuffer; + } else { + pDevice->webaudio.audioContextPlayback = audioContext; + pDevice->webaudio.pStackBufferPlayback = pStackBuffer; + pDevice->webaudio.intermediaryBufferSizeInFramesPlayback = intermediaryBufferSizeInFrames; + pDevice->webaudio.pIntermediaryBufferPlayback = pIntermediaryBuffer; + } +#else if (deviceType == ma_device_type_capture) { pDevice->webaudio.indexCapture = deviceIndex; } else { pDevice->webaudio.indexPlayback = deviceIndex; } +#endif pDescriptor->format = ma_format_f32; pDescriptor->channels = channels; ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); pDescriptor->periodSizeInFrames = periodSizeInFrames; pDescriptor->periodCount = 1; +#if defined(MA_USE_AUDIO_WORKLETS) + pDescriptor->sampleRate = sampleRate; /* Is this good enough to be used in the general case? */ +#else + pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); +#endif + return MA_SUCCESS; } @@ -38918,7 +39959,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { if (pConfig->deviceType == ma_device_type_duplex) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); + ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture); } return result; } @@ -38931,6 +39972,15 @@ static ma_result ma_device_start__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); +#if defined(MA_USE_AUDIO_WORKLETS) + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextPlayback); + } +#else if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { EM_ASM({ var device = miniaudio.get_device_by_index($0); @@ -38946,6 +39996,7 @@ static ma_result ma_device_start__webaudio(ma_device* pDevice) device.state = 2; /* ma_device_state_started */ }, pDevice->webaudio.indexPlayback); } +#endif return MA_SUCCESS; } @@ -38964,6 +40015,20 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) do any kind of explicit draining. */ +#if defined(MA_USE_AUDIO_WORKLETS) + /* I can't seem to find a way to suspend an AudioContext via the C Emscripten API. Is this an oversight? */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + EM_ASM({ + emscriptenGetAudioObject($0).suspend(); + }, pDevice->webaudio.audioContextCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + EM_ASM({ + emscriptenGetAudioObject($0).suspend(); + }, pDevice->webaudio.audioContextPlayback); + } +#else if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { EM_ASM({ var device = miniaudio.get_device_by_index($0); @@ -38979,6 +40044,7 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) device.state = 1; /* ma_device_state_stopped */ }, pDevice->webaudio.indexPlayback); } +#endif ma_device__on_notification_stopped(pDevice); @@ -39138,6 +40204,22 @@ static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint } +static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + + if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { + if (pContext->callbacks.onDeviceDataLoop == NULL) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } +} + + static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) { ma_result result; @@ -39257,8 +40339,23 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d /* - In playback mode, if the data converter does not support retrieval of the required number of - input frames given a number of output frames, we need to fall back to a heap-allocated cache. + If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's + a couple of situations where we'll need a heap allocated cache. + + The first is a duplex device for backends that use a callback for data delivery. The reason + this is needed is that the input stage needs to have a buffer to place the input data while it + waits for the playback stage, after which the miniaudio data callback will get fired. This is + not needed for backends that use a blocking API because miniaudio manages temporary buffers on + the stack to achieve this. + + The other situation is when the data converter does not have the ability to query the number + of input frames that are required in order to process a given number of output frames. When + performing data conversion, it's useful if miniaudio know exactly how many frames it needs + from the client in order to generate a given number of output frames. This way, only exactly + the number of frames are needed to be read from the client which means no cache is necessary. + On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read + in fixed sized chunks and then cache any residual unused input frames, those of which will be + processed at a later stage. */ if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_uint64 unused; @@ -39266,7 +40363,9 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d pDevice->playback.inputCacheConsumed = 0; pDevice->playback.inputCacheRemaining = 0; - if (deviceType == ma_device_type_duplex || ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) { + if ((pDevice->type == ma_device_type_duplex && ma_context_is_backend_asynchronous(pDevice->pContext)) || /* Duplex with asynchronous backend. */ + ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ + { /* We need a heap allocated cache. We want to size this based on the period size. */ void* pNewInputCache; ma_uint64 newInputCacheCap; @@ -39282,7 +40381,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ } - pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); + pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); if (pNewInputCache == NULL) { ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); pDevice->playback.pInputCache = NULL; @@ -39523,6 +40622,7 @@ static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) return MA_FAILED_TO_INIT_BACKEND; } + pContext->win32.CoInitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitialize"); pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx"); pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize"); pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance"); @@ -39560,71 +40660,14 @@ static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) #else static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) { -#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING) - ma_dlclose(pContext, pContext->posix.pthreadSO); -#else (void)pContext; -#endif return MA_SUCCESS; } static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) { - /* pthread */ -#if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING) - const char* libpthreadFileNames[] = { - "libpthread.so", - "libpthread.so.0", - "libpthread.dylib" - }; - size_t i; - - for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) { - pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]); - if (pContext->posix.pthreadSO != NULL) { - break; - } - } - - if (pContext->posix.pthreadSO == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create"); - pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join"); - pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init"); - pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy"); - pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock"); - pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock"); - pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init"); - pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy"); - pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait"); - pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal"); - pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init"); - pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy"); - pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy"); - pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam"); - pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam"); -#else - pContext->posix.pthread_create = (ma_proc)pthread_create; - pContext->posix.pthread_join = (ma_proc)pthread_join; - pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init; - pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy; - pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock; - pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock; - pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init; - pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy; - pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait; - pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal; - pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init; - pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy; -#if !defined(__EMSCRIPTEN__) - pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy; - pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam; - pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam; -#endif -#endif + (void)pContext; return MA_SUCCESS; } @@ -39655,22 +40698,6 @@ static ma_result ma_context_uninit_backend_apis(ma_context* pContext) } -static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { - if (pContext->callbacks.onDeviceDataLoop == NULL) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } -} - - /* The default capacity doesn't need to be too big. */ #ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY #define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 @@ -39730,7 +40757,7 @@ MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pC /* Initialize the job queue before the thread to ensure it's in a valid state. */ - jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); + jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); if (result != MA_SUCCESS) { @@ -39983,7 +41010,16 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); } else { - result = MA_NO_BACKEND; + /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */ + if (backend != ma_backend_custom) { + result = MA_BACKEND_NOT_ENABLED; + } else { + #if !defined(MA_HAS_CUSTOM) + result = MA_BACKEND_NOT_ENABLED; + #else + result = MA_NO_BACKEND; + #endif + } } /* If this iteration was successful, return. */ @@ -40007,7 +41043,11 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC pContext->backend = backend; return result; } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); + if (result == MA_BACKEND_NOT_ENABLED) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); + } } } @@ -40303,7 +41343,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC pDevice->noClip = pConfig->noClip; pDevice->noDisableDenormals = pConfig->noDisableDenormals; pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; - pDevice->masterVolumeFactor = 1; + ma_atomic_float_set(&pDevice->masterVolumeFactor, 1); pDevice->type = pConfig->deviceType; pDevice->sampleRate = pConfig->sampleRate; @@ -40525,7 +41565,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_uint64 intermediaryBufferSizeInBytes; - + pDevice->playback.intermediaryBufferLen = 0; if (pConfig->deviceType == ma_device_type_duplex) { pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ @@ -40537,7 +41577,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - + pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); if (pDevice->playback.pIntermediaryBuffer == NULL) { ma_device_uninit(pDevice); @@ -40663,7 +41703,6 @@ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backen allocationCallbacks = ma_allocation_callbacks_init_default(); } - pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); if (pContext == NULL) { return MA_OUT_OF_MEMORY; @@ -41011,7 +42050,7 @@ MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) return ma_device_state_uninitialized; } - return (ma_device_state)c89atomic_load_i32((ma_int32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ + return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ } MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) @@ -41024,7 +42063,7 @@ MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) return MA_INVALID_ARGS; } - c89atomic_exchange_f32(&pDevice->masterVolumeFactor, volume); + ma_atomic_float_set(&pDevice->masterVolumeFactor, volume); return MA_SUCCESS; } @@ -41040,7 +42079,7 @@ MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) return MA_INVALID_ARGS; } - *pVolume = c89atomic_load_f32(&pDevice->masterVolumeFactor); + *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor); return MA_SUCCESS; } @@ -41635,6 +42674,35 @@ MA_API float ma_volume_db_to_linear(float gain) } +MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) +{ + ma_uint64 iSample; + ma_uint64 sampleCount; + + if (pDst == NULL || pSrc == NULL || channels == 0) { + return MA_INVALID_ARGS; + } + + if (volume == 0) { + return MA_SUCCESS; /* No changes if the volume is 0. */ + } + + sampleCount = frameCount * channels; + + if (volume == 1) { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pDst[iSample] += pSrc[iSample]; + } + } else { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); + } + } + + return MA_SUCCESS; +} + + /************************************************************************************************************************************************************** @@ -41700,12 +42768,6 @@ static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -41718,15 +42780,11 @@ MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dit #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); } else @@ -41767,12 +42825,6 @@ static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -41785,15 +42837,11 @@ MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dit #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); } else @@ -41832,12 +42880,6 @@ static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -41850,15 +42892,11 @@ MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dit #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); } else @@ -41898,12 +42936,6 @@ static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -41916,15 +42948,11 @@ MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dit #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); } else @@ -42060,12 +43088,6 @@ static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42078,15 +43100,11 @@ MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dit #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); } else @@ -42131,12 +43149,6 @@ static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uin ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42149,15 +43161,11 @@ MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); } else @@ -42193,12 +43201,6 @@ static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uin ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42211,15 +43213,11 @@ MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); } else @@ -42267,12 +43265,6 @@ static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uin ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42285,15 +43277,11 @@ MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); } else @@ -42405,12 +43393,6 @@ static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42423,15 +43405,11 @@ MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dit #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); } else @@ -42485,12 +43463,6 @@ static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uin ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42503,15 +43475,11 @@ MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); } else @@ -42555,12 +43523,6 @@ static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uin ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42573,15 +43535,11 @@ MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); } else @@ -42629,12 +43587,6 @@ static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uin ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42647,15 +43599,11 @@ MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); } else @@ -42775,12 +43723,6 @@ static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42793,15 +43735,11 @@ MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dit #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); } else @@ -42855,12 +43793,6 @@ static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uin ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42873,15 +43805,11 @@ MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); } else @@ -42920,12 +43848,6 @@ static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uin ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -42938,15 +43860,11 @@ MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); } else @@ -43000,12 +43918,6 @@ static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uin ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -43018,15 +43930,11 @@ MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); } else @@ -43133,12 +44041,6 @@ static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -43151,15 +44053,11 @@ MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dit #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); } else @@ -43363,129 +44261,6 @@ static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uin } #endif /* SSE2 */ -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i16; - ma_uint64 count16; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - /* Both the input and output buffers need to be aligned to 32 bytes. */ - if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */ - count16 = count >> 4; - for (i16 = 0; i16 < count16; i16 += 1) { - __m256 d0; - __m256 d1; - __m256 x0; - __m256 x1; - __m256i i0; - __m256i i1; - __m256i p0; - __m256i p1; - __m256i r; - - if (ditherMode == ma_dither_mode_none) { - d0 = _mm256_set1_ps(0); - d1 = _mm256_set1_ps(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - d0 = _mm256_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - d1 = _mm256_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - } else { - d0 = _mm256_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - d1 = _mm256_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - } - - x0 = *((__m256*)(src_f32 + i) + 0); - x1 = *((__m256*)(src_f32 + i) + 1); - - x0 = _mm256_add_ps(x0, d0); - x1 = _mm256_add_ps(x1, d1); - - x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f)); - x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f)); - - /* Computing the final result is a little more complicated for AVX2 than SSE2. */ - i0 = _mm256_cvttps_epi32(x0); - i1 = _mm256_cvttps_epi32(x1); - p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32); - p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48); - r = _mm256_packs_epi32(p0, p1); - - _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r); - - i += 16; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* AVX2 */ - #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -43498,7 +44273,8 @@ static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uin float ditherMax; if (!ma_has_neon()) { - return ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; } /* Both the input and output buffers need to be aligned to 16 bytes. */ @@ -43597,15 +44373,11 @@ MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); } else @@ -43658,12 +44430,6 @@ static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uin ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -43676,15 +44442,11 @@ MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); } else @@ -43733,12 +44495,6 @@ static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uin ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); } #endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { @@ -43751,15 +44507,11 @@ MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_di #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); #else - # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 - if (ma_has_avx2()) { - ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode); - } else - #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 + # if defined(MA_SUPPORT_SSE2) if (ma_has_sse2()) { ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); } else - #elif MA_PREFERRED_SIMD == MA_SIMD_NEON + #elif defined(MA_SUPPORT_NEON) if (ma_has_neon()) { ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); } else @@ -45134,7 +45886,7 @@ static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, cons MA_ASSERT(pLPF->format == ma_format_f32); - MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); + MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); @@ -45152,7 +45904,7 @@ static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, c MA_ASSERT(pLPF->format == ma_format_s16); - MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); + MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); @@ -47491,6 +48243,7 @@ MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, vo pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); + pGainer->masterVolume = 1; pGainer->config = *pConfig; pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ @@ -47550,20 +48303,256 @@ static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint3 return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); } -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint32 iChannel; - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; + ma_uint64 interpolatedFrameCount; - if (pGainer == NULL) { - return MA_INVALID_ARGS; + MA_ASSERT(pGainer != NULL); + + /* + We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When + linear interpolation is not needed we can do a simple volume adjustment which will be more + efficient than a lerp with an alpha value of 1. + + To do this, all we need to do is determine how many frames need to have a lerp applied. Then we + just process that number of frames with linear interpolation. After that we run on an optimized + path which just applies the new gains without a lerp. + */ + if (pGainer->t >= pGainer->config.smoothTimeInFrames) { + interpolatedFrameCount = 0; + } else { + interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames; + if (interpolatedFrameCount > frameCount) { + interpolatedFrameCount = frameCount; + } } + /* + Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers + so that the fast path can work naturally without consideration of the interpolated path. + */ + if (interpolatedFrameCount > 0) { + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + /* + All we're really doing here is moving the old gains towards the new gains. We don't want to + be modifying the gains inside the ma_gainer object because that will break things. Instead + we can make a copy here on the stack. For extreme channel counts we can fall back to a slower + implementation which just uses a standard lerp. + */ + float* pFramesOutF32 = (float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + float d = 1.0f / pGainer->config.smoothTimeInFrames; + + if (pGainer->config.channels <= 32) { + float pRunningGain[32]; + float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */ + + /* Initialize the running gain. */ + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + float t = (pGainer->pOldGains[iChannel] - pGainer->pNewGains[iChannel]) * pGainer->masterVolume; + pRunningGainDelta[iChannel] = t * d; + pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); + } + + iFrame = 0; + + /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */ + if (pGainer->config.channels == 2) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean SIMD loop below. */ + __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]); + __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]); + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0)); + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + } + + iFrame = unrolledLoopCount << 1; + } else + #endif + { + /* + Two different scalar implementations here. Clang (and I assume GCC) will vectorize + both of these, but the bottom version results in a nicer vectorization with less + instructions emitted. The problem, however, is that the bottom version runs slower + when compiled with MSVC. The top version will be partially vectorized by MSVC. + */ + #if defined(_MSC_VER) && !defined(__clang__) + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */ + pRunningGainDelta[2] = pRunningGainDelta[0]; + pRunningGainDelta[3] = pRunningGainDelta[1]; + pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0]; + pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1]; + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0]; + pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1]; + pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2]; + pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3]; + + /* Move the running gain forward towards the new gain. */ + pRunningGain[0] += pRunningGainDelta[0]; + pRunningGain[1] += pRunningGainDelta[1]; + pRunningGain[2] += pRunningGainDelta[2]; + pRunningGain[3] += pRunningGainDelta[3]; + } + + iFrame = unrolledLoopCount << 1; + #else + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 2; iChannel += 1) { + pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel]; + } + + for (iChannel = 0; iChannel < 2; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + #endif + } + } else if (pGainer->config.channels == 6) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* + For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames + at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays + so we can do clean 4x SIMD operations. + */ + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean SIMD loop below. */ + __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]); + __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]); + __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]); + + __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]); + __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]); + __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]); + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0)); + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1)); + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2)); + + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); + runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2); + } + + iFrame = unrolledLoopCount << 1; + } else + #endif + { + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 6; iChannel += 1) { + pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel]; + } + + /* Move the running gain forward towards the new gain. */ + for (iChannel = 0; iChannel < 6; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } + } else if (pGainer->config.channels == 8) { + /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */ + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]); + __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]); + __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]); + __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]); + + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0)); + _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1)); + + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); + } + } else + #endif + { + /* This is crafted so that it auto-vectorizes when compiled with Clang. */ + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 8; iChannel += 1) { + pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel]; + } + + /* Move the running gain forward towards the new gain. */ + for (iChannel = 0; iChannel < 8; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } + } + + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel]; + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } else { + /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */ + for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; + } + + a += d; + } + } + } + + /* Make sure the timer is updated. */ + pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames); + + /* Adjust our arguments so the next part can work normally. */ + frameCount -= interpolatedFrameCount; + pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float)); + pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float)); + } + + /* All we need to do here is apply the new gains using an optimized path. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + if (pGainer->config.channels <= 32) { + float gains[32]; + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume; + } + + ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains); + } else { + /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume; + } + } + } + } + + /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount); + } + +#if 0 if (pGainer->t >= pGainer->config.smoothTimeInFrames) { /* Fast path. No gain calculation required. */ ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); + ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume); /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ if (pGainer->t == (ma_uint32)-1) { @@ -47580,7 +48569,7 @@ MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesO for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channelCount; iChannel += 1) { - pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a); + pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; } pFramesOutF32 += channelCount; @@ -47600,7 +48589,7 @@ MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesO /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ if (pFramesOut != NULL && pFramesIn != NULL) { for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel); + pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume; } } @@ -47609,10 +48598,24 @@ MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesO } #endif } +#endif return MA_SUCCESS; } +MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + /* + ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which + helps with auto-vectorization. + */ + return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount); +} + static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) { pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); @@ -47664,6 +48667,28 @@ MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) return MA_SUCCESS; } +MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume) +{ + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + pGainer->masterVolume = volume; + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume) +{ + if (pGainer == NULL || pVolume == NULL) { + return MA_INVALID_ARGS; + } + + *pVolume = pGainer->masterVolume; + + return MA_SUCCESS; +} + MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) { @@ -48013,7 +49038,7 @@ MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd pFader->cursorInFrames = 0; /* Reset cursor. */ } -MA_API float ma_fader_get_current_volume(ma_fader* pFader) +MA_API float ma_fader_get_current_volume(const ma_fader* pFader) { if (pFader == NULL) { return 0.0f; @@ -48078,6 +49103,8 @@ MA_API float ma_vec3f_len(ma_vec3f v) return (float)ma_sqrtd(ma_vec3f_len2(v)); } + + MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) { return ma_vec3f_len(ma_vec3f_sub(a, b)); @@ -48085,16 +49112,16 @@ MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) { - float f; - float l = ma_vec3f_len(v); - if (l == 0) { + float invLen; + float len2 = ma_vec3f_len2(v); + if (len2 == 0) { return ma_vec3f_init_3f(0, 0, 0); } - f = 1 / l; - v.x *= f; - v.y *= f; - v.z *= f; + invLen = ma_rsqrtf(len2); + v.x *= invLen; + v.y *= invLen; + v.z *= invLen; return v; } @@ -48109,6 +49136,35 @@ MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) } +MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value) +{ + v->v = value; + v->lock = 0; /* Important this is initialized to 0. */ +} + +MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value) +{ + ma_spinlock_lock(&v->lock); + { + v->v = value; + } + ma_spinlock_unlock(&v->lock); +} + +MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v) +{ + ma_vec3f r; + + ma_spinlock_lock(&v->lock); + { + r = v->v; + } + ma_spinlock_unlock(&v->lock); + + return r; +} + + static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); @@ -48359,14 +49415,15 @@ MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_ MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pListener->config = *pConfig; - pListener->position = ma_vec3f_init_3f(0, 0, 0); - pListener->direction = ma_vec3f_init_3f(0, 0, -1); - pListener->velocity = ma_vec3f_init_3f(0, 0, 0); + ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0)); + ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1)); + ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0)); pListener->isEnabled = MA_TRUE; /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ if (pListener->config.handedness == ma_handedness_left) { - pListener->direction = ma_vec3f_neg(pListener->direction); + ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener)); + ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z); } @@ -48469,7 +49526,7 @@ MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListe return; } - pListener->position = ma_vec3f_init_3f(x, y, z); + ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z)); } MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) @@ -48478,7 +49535,7 @@ MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listen return ma_vec3f_init_3f(0, 0, 0); } - return pListener->position; + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ } MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) @@ -48487,7 +49544,7 @@ MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pList return; } - pListener->direction = ma_vec3f_init_3f(x, y, z); + ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z)); } MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) @@ -48496,7 +49553,7 @@ MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_liste return ma_vec3f_init_3f(0, 0, -1); } - return pListener->direction; + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ } MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) @@ -48505,7 +49562,7 @@ MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListe return; } - pListener->velocity = ma_vec3f_init_3f(x, y, z); + ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z)); } MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) @@ -48514,7 +49571,7 @@ MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listen return ma_vec3f_init_3f(0, 0, 0); } - return pListener->velocity; + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ } MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) @@ -48737,14 +49794,15 @@ MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* p pSpatializer->dopplerFactor = pConfig->dopplerFactor; pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; - pSpatializer->position = ma_vec3f_init_3f(0, 0, 0); - pSpatializer->direction = ma_vec3f_init_3f(0, 0, -1); - pSpatializer->velocity = ma_vec3f_init_3f(0, 0, 0); + ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); + ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1)); + ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0)); pSpatializer->dopplerPitch = 1; /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ if (pSpatializer->handedness == ma_handedness_left) { - pSpatializer->direction = ma_vec3f_neg(pSpatializer->direction); + ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer)); + ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z); } /* Channel map. This will be on the heap. */ @@ -48909,7 +49967,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, defined by the listener, so we'll grab that here too. */ if (pListener != NULL) { - listenerVel = pListener->velocity; + listenerVel = ma_spatializer_listener_get_velocity(pListener); speedOfSound = pListener->config.speedOfSound; } else { listenerVel = ma_vec3f_init_3f(0, 0, 0); @@ -48918,8 +49976,8 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { /* There's no listener or we're using relative positioning. */ - relativePos = pSpatializer->position; - relativeDir = pSpatializer->direction; + relativePos = ma_spatializer_get_position(pSpatializer); + relativeDir = ma_spatializer_get_direction(pSpatializer); } else { /* We've found a listener and we're using absolute positioning. We need to transform the @@ -49079,7 +50137,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, 0, panning will be most extreme and any sounds that are positioned on the opposite side of the speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it doesn't even remotely represent the real world at all because sounds that come from your right side - are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at + are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at all, which is also not ideal. By setting it to something greater than 0, the spatialization effect becomes much less dramatic and a lot more bearable. @@ -49148,7 +50206,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, source. */ if (dopplerFactor > 0) { - pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(pListener->position, pSpatializer->position), pSpatializer->velocity, listenerVel, speedOfSound, dopplerFactor); + pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor); } else { pSpatializer->dopplerPitch = 1; } @@ -49157,6 +50215,24 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, return MA_SUCCESS; } +MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume) +{ + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_gainer_set_master_volume(&pSpatializer->gainer, volume); +} + +MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume) +{ + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume); +} + MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { @@ -49373,7 +50449,7 @@ MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, f return; } - pSpatializer->position = ma_vec3f_init_3f(x, y, z); + ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z)); } MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) @@ -49382,7 +50458,7 @@ MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) return ma_vec3f_init_3f(0, 0, 0); } - return pSpatializer->position; + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ } MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) @@ -49391,7 +50467,7 @@ MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, return; } - pSpatializer->direction = ma_vec3f_init_3f(x, y, z); + ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z)); } MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) @@ -49400,7 +50476,7 @@ MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) return ma_vec3f_init_3f(0, 0, -1); } - return pSpatializer->direction; + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ } MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) @@ -49409,7 +50485,7 @@ MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, f return; } - pSpatializer->velocity = ma_vec3f_init_3f(x, y, z); + ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z)); } MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) @@ -49418,7 +50494,7 @@ MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) return ma_vec3f_init_3f(0, 0, 0); } - return pSpatializer->velocity; + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ } MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) @@ -49442,23 +50518,32 @@ MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatiali if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { /* There's no listener or we're using relative positioning. */ if (pRelativePos != NULL) { - *pRelativePos = pSpatializer->position; + *pRelativePos = ma_spatializer_get_position(pSpatializer); } if (pRelativeDir != NULL) { - *pRelativeDir = pSpatializer->direction; + *pRelativeDir = ma_spatializer_get_direction(pSpatializer); } } else { + ma_vec3f spatializerPosition; + ma_vec3f spatializerDirection; + ma_vec3f listenerPosition; + ma_vec3f listenerDirection; ma_vec3f v; ma_vec3f axisX; ma_vec3f axisY; ma_vec3f axisZ; float m[4][4]; + spatializerPosition = ma_spatializer_get_position(pSpatializer); + spatializerDirection = ma_spatializer_get_direction(pSpatializer); + listenerPosition = ma_spatializer_listener_get_position(pListener); + listenerDirection = ma_spatializer_listener_get_direction(pListener); + /* We need to calcualte the right vector from our forward and up vectors. This is done with a cross product. */ - axisZ = ma_vec3f_normalize(pListener->direction); /* Normalization required here because we can't trust the caller. */ + axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ /* @@ -49483,9 +50568,9 @@ MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatiali } /* Lookat. */ - m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, pListener->position); - m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, pListener->position); - m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), pListener->position); + m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition); + m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition); + m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition); m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; /* @@ -49494,7 +50579,7 @@ MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatiali origin which makes things simpler. */ if (pRelativePos != NULL) { - v = pSpatializer->position; + v = spatializerPosition; pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; @@ -49505,7 +50590,7 @@ MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatiali rotation of the listener. */ if (pRelativeDir != NULL) { - v = pSpatializer->direction; + v = spatializerDirection; pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; @@ -51245,7 +52330,7 @@ static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const floa return MA_SUCCESS; } -static ma_result ma_channel_map_apply_mono_in_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) +static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) { ma_uint64 iFrame; ma_uint32 iChannelOut; @@ -51350,16 +52435,123 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* pFramesOut, const ma_ch { default_handler: { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + if (channelsOut <= MA_MAX_CHANNELS) { + ma_bool32 hasEmptyChannel = MA_FALSE; + ma_channel channelPositions[MA_MAX_CHANNELS]; for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iChannelOut] = pFramesIn[0]; + channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) { + hasEmptyChannel = MA_TRUE; } } - pFramesOut += channelsOut; - pFramesIn += 1; + if (hasEmptyChannel == MA_FALSE) { + /* + Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully + help the compiler with auto-vectorization.m + */ + if (channelsOut == 2) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* We want to do two frames in each iteration. */ + ma_uint64 unrolledFrameCount = frameCount >> 1; + + for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { + __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); + __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); + _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); + } + + /* Tail. */ + iFrame = unrolledFrameCount << 1; + goto generic_on_fastpath; + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) { + pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else if (channelsOut == 6) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */ + ma_uint64 unrolledFrameCount = frameCount >> 1; + + for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { + __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); + __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); + + _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); + } + + /* Tail. */ + iFrame = unrolledFrameCount << 1; + goto generic_on_fastpath; + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) { + pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else if (channelsOut == 8) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + __m128 in = _mm_set1_ps(pFramesIn[iFrame]); + _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in); + _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in); + } + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) { + pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else { + iFrame = 0; + + #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */ + generic_on_fastpath: + #endif + { + for (; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } + } else { + /* Slow path. Need to handle MA_CHANNEL_NONE. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } + } else { + /* Slow path. Too many channels to store on the stack. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } } } } break; @@ -51426,19 +52618,105 @@ static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChann } } - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + iFrame = 0; + + /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */ + if (channelsOut == 8) { + /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */ + if (channelsIn == 2) { + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0]; + accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0]; + accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0]; + accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0]; + accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0]; + accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0]; + accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0]; + accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0]; + + accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1]; + accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1]; + accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1]; + accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1]; + accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1]; + accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1]; + accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1]; + accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1]; + + pFramesOut[iFrame*8 + 0] = accumulation[0]; + pFramesOut[iFrame*8 + 1] = accumulation[1]; + pFramesOut[iFrame*8 + 2] = accumulation[2]; + pFramesOut[iFrame*8 + 3] = accumulation[3]; + pFramesOut[iFrame*8 + 4] = accumulation[4]; + pFramesOut[iFrame*8 + 5] = accumulation[5]; + pFramesOut[iFrame*8 + 6] = accumulation[6]; + pFramesOut[iFrame*8 + 7] = accumulation[7]; + } + } else { + /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */ + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; + accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; + accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; + accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; + accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; + accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; + accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn]; + accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn]; + } + + pFramesOut[iFrame*8 + 0] = accumulation[0]; + pFramesOut[iFrame*8 + 1] = accumulation[1]; + pFramesOut[iFrame*8 + 2] = accumulation[2]; + pFramesOut[iFrame*8 + 3] = accumulation[3]; + pFramesOut[iFrame*8 + 4] = accumulation[4]; + pFramesOut[iFrame*8 + 5] = accumulation[5]; + pFramesOut[iFrame*8 + 6] = accumulation[6]; + pFramesOut[iFrame*8 + 7] = accumulation[7]; + } + } + } else if (channelsOut == 6) { + /* + When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll + expand our weights and do two frames at a time. + */ + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; + accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; + accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; + accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; + accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; + accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; + } + + pFramesOut[iFrame*6 + 0] = accumulation[0]; + pFramesOut[iFrame*6 + 1] = accumulation[1]; + pFramesOut[iFrame*6 + 2] = accumulation[2]; + pFramesOut[iFrame*6 + 3] = accumulation[3]; + pFramesOut[iFrame*6 + 4] = accumulation[4]; + pFramesOut[iFrame*6 + 5] = accumulation[5]; + } + } + + /* Leftover frames. */ + for (; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { float accumulation = 0; for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation += pFramesIn[iChannelIn] * weights[iChannelOut][iChannelIn]; + accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn]; } - pFramesOut[iChannelOut] = accumulation; + pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; } } else { /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ @@ -51449,14 +52727,11 @@ static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChann for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - accumulation += pFramesIn[iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); + accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); } - pFramesOut[iChannelOut] = accumulation; + pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; } } } @@ -51638,7 +52913,7 @@ MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_convert /* We now need to fill out our weights table. This is determined by the mixing mode. */ - + /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); @@ -55284,6 +56559,11 @@ MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) +#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0 +#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0) +#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0 +#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0) + MA_API ma_data_source_config ma_data_source_config_init(void) { ma_data_source_config config; @@ -55309,10 +56589,10 @@ MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_da } pDataSourceBase->vtable = pConfig->vtable; - pDataSourceBase->rangeBegInFrames = 0; - pDataSourceBase->rangeEndInFrames = ~((ma_uint64)0); - pDataSourceBase->loopBegInFrames = 0; - pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); + pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; + pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; + pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; + pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ pDataSourceBase->pNext = NULL; pDataSourceBase->onGetNext = NULL; @@ -55378,18 +56658,23 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { /* Need to clamp to within the range. */ - ma_uint64 cursor; + ma_uint64 relativeCursor; + ma_uint64 absoluteCursor; - result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &cursor); + result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); if (result != MA_SUCCESS) { /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { + ma_uint64 rangeBeg; ma_uint64 rangeEnd; /* We have the cursor. We need to make sure we don't read beyond our range. */ + rangeBeg = pDataSourceBase->rangeBegInFrames; rangeEnd = pDataSourceBase->rangeEndInFrames; + absoluteCursor = rangeBeg + relativeCursor; + /* If looping, make sure we're within range. */ if (loop) { if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { @@ -55397,8 +56682,8 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa } } - if (frameCount > (rangeEnd - cursor) && rangeEnd != ~((ma_uint64)0)) { - frameCount = (rangeEnd - cursor); + if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) { + frameCount = (rangeEnd - absoluteCursor); } /* @@ -55803,9 +57088,9 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_result result; - ma_uint64 cursor; - ma_uint64 loopBegAbsolute; - ma_uint64 loopEndAbsolute; + ma_uint64 relativeCursor; + ma_uint64 absoluteCursor; + ma_bool32 doSeekAdjustment = MA_FALSE; if (pDataSource == NULL) { return MA_INVALID_ARGS; @@ -55816,51 +57101,51 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou } /* - The loop points need to be updated. We'll be storing the loop points relative to the range. We'll update - these so that they maintain their absolute positioning. The loop points will then be clamped to the range. + We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now + so we can calculate it's absolute position before we change the range. */ - loopBegAbsolute = pDataSourceBase->loopBegInFrames + pDataSourceBase->rangeBegInFrames; - loopEndAbsolute = pDataSourceBase->loopEndInFrames + ((pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) ? pDataSourceBase->rangeBegInFrames : 0); + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); + if (result == MA_SUCCESS) { + doSeekAdjustment = MA_TRUE; + absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames; + } else { + /* + We couldn't get the position of the cursor. It probably means the data source has no notion + of a cursor. We'll just leave it at position 0. Don't treat this as an error. + */ + doSeekAdjustment = MA_FALSE; + relativeCursor = 0; + absoluteCursor = 0; + } pDataSourceBase->rangeBegInFrames = rangeBegInFrames; pDataSourceBase->rangeEndInFrames = rangeEndInFrames; - /* Make the loop points relative again, and make sure they're clamped to within the range. */ - if (loopBegAbsolute > pDataSourceBase->rangeBegInFrames) { - pDataSourceBase->loopBegInFrames = loopBegAbsolute - pDataSourceBase->rangeBegInFrames; - } else { - pDataSourceBase->loopBegInFrames = 0; - } + /* + The commented out logic below was intended to maintain loop points in response to a change in the + range. However, this is not useful because it results in the sound breaking when you move the range + outside of the old loop points. I'm simplifying this by simply resetting the loop points. The + caller is expected to update their loop points if they change the range. - if (pDataSourceBase->loopBegInFrames > pDataSourceBase->rangeEndInFrames) { - pDataSourceBase->loopBegInFrames = pDataSourceBase->rangeEndInFrames; - } + In practice this should be mostly a non-issue because the majority of the time the range will be + set once right after initialization. + */ + pDataSourceBase->loopBegInFrames = 0; + pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - /* Only need to update the loop end point if it's not -1. */ - if (loopEndAbsolute != ~((ma_uint64)0)) { - if (loopEndAbsolute > pDataSourceBase->rangeBegInFrames) { - pDataSourceBase->loopEndInFrames = loopEndAbsolute - pDataSourceBase->rangeBegInFrames; - } else { - pDataSourceBase->loopEndInFrames = 0; - } - - if (pDataSourceBase->loopEndInFrames > pDataSourceBase->rangeEndInFrames && pDataSourceBase->loopEndInFrames) { - pDataSourceBase->loopEndInFrames = pDataSourceBase->rangeEndInFrames; - } - } - - - /* If the new range is past the current cursor position we need to seek to it. */ - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); - if (result == MA_SUCCESS) { - /* Seek to within range. Note that our seek positions here are relative to the new range. */ - if (cursor < rangeBegInFrames) { + + /* + Seek to within range. Note that our seek positions here are relative to the new range. We don't want + do do this if we failed to retrieve the cursor earlier on because it probably means the data source + has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but + I'm just not even going to attempt it. + */ + if (doSeekAdjustment) { + if (absoluteCursor < rangeBegInFrames) { ma_data_source_seek_to_pcm_frame(pDataSource, 0); - } else if (cursor > rangeEndInFrames) { + } else if (absoluteCursor > rangeEndInFrames) { ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); } - } else { - /* We failed to get the cursor position. Probably means the data source has no notion of a cursor such a noise data source. Just pretend the seeking worked. */ } return MA_SUCCESS; @@ -57870,7 +59155,7 @@ extern "C" { #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 7 +#define DRWAV_VERSION_REVISION 8 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include typedef signed char drwav_int8; @@ -61292,6 +62577,7 @@ typedef struct ma_uint8* pData; size_t dataSize; size_t dataCapacity; + size_t audioStartOffsetInBytes; ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ float** ppPacketData; @@ -61458,6 +62744,13 @@ MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_ */ dataSize -= (size_t)consumedDataSize; /* Consume the data. */ MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); + + /* + We need to track the start point so we can seek back to the start of the audio + data when seeking. + */ + pVorbis->push.audioStartOffsetInBytes = consumedDataSize; + break; } else { /* Failed to open the decoder. */ @@ -61791,13 +63084,14 @@ MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 TODO: Use seeking logic documented for stb_vorbis_flush_pushdata(). */ - /* Seek to the start of the file to begin with. */ - result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); + /* Seek to the start of the audio data in the file to begin with. */ + result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, pVorbis->push.audioStartOffsetInBytes, ma_seek_origin_start); if (result != MA_SUCCESS) { return result; } stb_vorbis_flush_pushdata(pVorbis->stb); + pVorbis->push.framesConsumed = 0; pVorbis->push.framesRemaining = 0; pVorbis->push.dataSize = 0; @@ -64360,8 +65654,15 @@ MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) return MA_INVALID_ARGS; } - pNoise->config.type = type; - return MA_SUCCESS; + /* + This function should never have been implemented in the first place. Changing the type dynamically is not + supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function + will be removed in version 0.12. + */ + MA_ASSERT(MA_FALSE); + (void)type; + + return MA_INVALID_OPERATION; } static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) @@ -65573,8 +66874,11 @@ MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_co ma_resource_manager_data_source_config config; MA_ZERO_OBJECT(&config); - config.rangeEndInPCMFrames = ~((ma_uint64)0); - config.loopPointEndInPCMFrames = ~((ma_uint64)0); + config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; + config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; + config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; + config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; + config.isLooping = MA_FALSE; return config; } @@ -65623,8 +66927,17 @@ static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourc return MA_SUCCESS; } +static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized); +} + static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) { + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { + return NULL; /* Connector not yet initialized. */ + } + switch (pDataBuffer->pNode->data.type) { case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; @@ -65646,7 +66959,7 @@ static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_mana MA_ASSERT(pDataBuffer != NULL); MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDataBuffer->isConnectorInitialized == MA_FALSE); + MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE); /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); @@ -65696,14 +67009,30 @@ static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_mana */ if (result == MA_SUCCESS) { /* - Make sure the looping state is set before returning in order to handle the case where the - loop state was set on the data buffer before the connector was initialized. - */ - ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); + The resource manager supports the ability to set the range and loop settings via a config at + initialization time. This results in an case where the ranges could be set explicitly via + ma_data_source_set_*() before we get to this point here. If this happens, we'll end up + hitting a case where we just override those settings which results in what feels like a bug. - pDataBuffer->isConnectorInitialized = MA_TRUE; + To address this we only change the relevant properties if they're not equal to defaults. If + they're equal to defaults there's no need to change them anyway. If they're *not* set to the + default values, we can assume the user has set the range and loop settings via the config. If + they're doing their own calls to ma_data_source_set_*() in addition to setting them via the + config, that's entirely on the caller and any synchronization issue becomes their problem. + */ + if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) { + ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + } + + if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) { + ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + } + + if (pConfig->isLooping != MA_FALSE) { + ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); + } + + ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE); if (pInitNotification != NULL) { ma_async_notification_signal(pInitNotification); @@ -65723,6 +67052,8 @@ static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_ma MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBuffer != NULL); + (void)pResourceManager; + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) { case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ @@ -66708,15 +68039,25 @@ MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_man MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); /* If the node is not initialized we need to abort with a busy code. */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { return MA_BUSY; /* Still loading. */ } + /* + If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's + a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If + this happens, we need to keep the seek scheduled and return MA_BUSY. + */ if (pDataBuffer->seekToCursorOnNextRead) { pDataBuffer->seekToCursorOnNextRead = MA_FALSE; result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); if (result != MA_SUCCESS) { + if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) { + pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */ + return MA_BUSY; + } + return result; } } @@ -66789,7 +68130,7 @@ MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_m MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); /* If we haven't yet got a connector we need to abort. */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { pDataBuffer->seekTargetInPCMFrames = frameIndex; pDataBuffer->seekToCursorOnNextRead = MA_TRUE; return MA_BUSY; /* Still loading. */ @@ -67248,6 +68589,14 @@ MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pR ma_async_notification_signal(notifications.init.pNotification); } + /* + If there was an error during initialization make sure we return that result here. We don't want to do this + if we're not waiting because it will most likely be in a busy state. + */ + if (pDataStream->result != MA_SUCCESS) { + return pDataStream->result; + } + /* NOTE: Do not release pInitFence here. That will be done by the job. */ } @@ -67262,7 +68611,7 @@ MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pReso config.pFilePath = pFilePath; config.flags = flags; config.pNotifications = pNotifications; - + return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); } @@ -67274,7 +68623,7 @@ MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pRe config.pFilePathW = pFilePath; config.flags = flags; config.pNotifications = pNotifications; - + return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); } @@ -68354,7 +69703,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob } /* Try initializing the connector if we haven't already. */ - isConnectorInitialized = pDataBuffer->isConnectorInitialized; + isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer); if (isConnectorInitialized == MA_FALSE) { dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); @@ -68387,7 +69736,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob There is a hole between here and the where the data connector is initialized where the data buffer node may have finished initializing. We need to check for this by checking the result of the data buffer node and whether or not we had an unknown data supply type at the time of - trying to initialize the data connector. + trying to initialize the data connector. */ result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { @@ -68410,7 +69759,7 @@ done: If at this point the data buffer has not had it's connector initialized, it means the notification event was never signalled which means we need to signal it here. */ - if (pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) { + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) { if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); } @@ -68736,35 +70085,6 @@ MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 -static ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) -{ - ma_uint64 iSample; - ma_uint64 sampleCount; - - if (pDst == NULL || pSrc == NULL || channels == 0) { - return MA_INVALID_ARGS; - } - - if (volume == 0) { - return MA_SUCCESS; /* No changes if the volume is 0. */ - } - - sampleCount = frameCount * channels; - - if (volume == 1) { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += pSrc[iSample]; - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); - } - } - - return MA_SUCCESS; -} - - MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) { ma_node_graph_config config; @@ -69238,7 +70558,7 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu old input bus has been updated so that pOutputBus will not get iterated again. */ pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ - pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; /* As above. */ + pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; /* Now we need to attach the output bus to the linked list. This involves updating two pointers on @@ -69336,6 +70656,8 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_ ma_uint32 inputChannels; ma_bool32 doesOutputBufferHaveContent = MA_FALSE; + (void)pInputNode; /* Not currently used. */ + /* This will be called from the audio thread which means we can't be doing any locking. Basically, this function will not perfom any locking, whereas attaching and detaching will, but crafted in @@ -69378,6 +70700,7 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_ ma_bool32 isSilentOutput = MA_FALSE; MA_ASSERT(pOutputBus->pNode != NULL); + MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL); isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; @@ -69560,8 +70883,8 @@ static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_ /* Some special rules for passthrough nodes. */ if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - if (pConfig->vtable->inputBusCount != 1 || pConfig->vtable->outputBusCount != 1) { - return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 input bus and 1 output bus. */ + if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) { + return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */ } if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { @@ -70250,6 +71573,15 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ ppFramesOut[0] = pFramesOut; + + /* + If it's a passthrough we won't be expecting the callback to output anything, so we'll + need to pre-silence the output buffer. + */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); + } + ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); totalFramesRead = frameCountOut; } else { @@ -70502,7 +71834,7 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); } } - + /* Apply volume, if necessary. */ ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); @@ -70671,8 +72003,7 @@ static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** pp ma_uint32 channels; MA_ASSERT(pNodeBase != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); - MA_ASSERT(ma_node_get_output_bus_count(pNodeBase) >= 2); + MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ (void)pFrameCountIn; @@ -71702,6 +73033,33 @@ static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_n return inputFrameCount; } +static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume) +{ + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ + ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); + + return MA_SUCCESS; +} + +static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume) +{ + if (pVolume == NULL) { + return MA_INVALID_ARGS; + } + + *pVolume = 0.0f; + + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_spatializer_get_master_volume(&pEngineNode->spatializer, pVolume); +} + static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_uint32 frameCountIn; @@ -71822,18 +73180,23 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { iListener = pEngineNode->pinnedListenerIndex; } else { - iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, pEngineNode->spatializer.position.x, pEngineNode->spatializer.position.y, pEngineNode->spatializer.position.z); + ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer); + iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z); } ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); } else { - /* No spatialization, but we still need to do channel conversion. */ + /* No spatialization, but we still need to do channel conversion and master volume. */ + float volume; + ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */ + if (channelsIn == channelsOut) { /* No channel conversion required. Just copy straight to the output buffer. */ - ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut); + ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); } else { /* Channel conversion required. TODO: Add support for channel maps here. */ ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); + ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); } } @@ -72068,6 +73431,7 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo ma_spatializer_config spatializerConfig; ma_uint32 channelsIn; ma_uint32 channelsOut; + ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ MA_ASSERT(pHeapLayout); @@ -72104,7 +73468,7 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo /* Resmapler. */ resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ resamplerConfig.lpfOrder = 0; - + result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the size of the heap for the resampler. */ @@ -72117,6 +73481,10 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo /* Spatializer. */ spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); + if (spatializerConfig.channelsIn == 2) { + spatializerConfig.pChannelMapIn = defaultStereoChannelMap; + } + result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the size of the heap for the spatializer. */ @@ -72161,6 +73529,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p ma_panner_config pannerConfig; ma_uint32 channelsIn; ma_uint32 channelsOut; + ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ if (pEngineNode == NULL) { return MA_INVALID_ARGS; @@ -72190,10 +73559,17 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); + /* + If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler + is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used. + */ + if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) { + pEngineNode->isPitchDisabled = MA_FALSE; + } + /* Base node. */ baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); @@ -72240,6 +73616,10 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; + if (spatializerConfig.channelsIn == 2) { + spatializerConfig.pChannelMapIn = defaultStereoChannelMap; + } + result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); if (result != MA_SUCCESS) { goto error2; @@ -72331,7 +73711,7 @@ MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) } else { config.monoExpansionMode = ma_mono_expansion_mode_default; } - + config.rangeEndInPCMFrames = ~((ma_uint64)0); config.loopPointEndInPCMFrames = ~((ma_uint64)0); @@ -72439,7 +73819,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng #if !defined(MA_NO_DEVICE_IO) { pEngine->pDevice = engineConfig.pDevice; - + /* If we don't have a device, we need one. */ if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { ma_device_config deviceConfig; @@ -72554,7 +73934,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng Temporarily disabled. There is a subtle bug here where front-left and front-right will be used by the device's channel map, but this is not what we want to use for spatialization. Instead we want to use side-left and side-right. I need to figure - out a better solution for this. For now, disabling the user of device channel maps. + out a better solution for this. For now, disabling the use of device channel maps. */ /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ } @@ -72924,7 +74304,7 @@ MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float iListenerClosest = 0; for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { if (ma_engine_listener_is_enabled(pEngine, iListener)) { - float len2 = ma_vec3f_len2(ma_vec3f_sub(pEngine->listeners[iListener].position, ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); + float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); if (closestLen2 > len2) { closestLen2 = len2; iListenerClosest = iListener; @@ -73306,8 +74686,11 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s return MA_OUT_OF_MEMORY; } - notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pFence = pConfig->pDoneFence; + /* Removed in 0.12. Set pDoneFence on the notifications. */ + notifications = pConfig->initNotifications; + if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) { + notifications.done.pFence = pConfig->pDoneFence; + } /* We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does @@ -73355,21 +74738,35 @@ done: MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) { - ma_sound_config config = ma_sound_config_init_2(pEngine); + ma_sound_config config; + + if (pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_sound_config_init_2(pEngine); config.pFilePath = pFilePath; config.flags = flags; config.pInitialAttachment = pGroup; config.pDoneFence = pDoneFence; + return ma_sound_init_ex(pEngine, &config, pSound); } MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) { - ma_sound_config config = ma_sound_config_init_2(pEngine); + ma_sound_config config; + + if (pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_sound_config_init_2(pEngine); config.pFilePathW = pFilePath; config.flags = flags; config.pInitialAttachment = pGroup; config.pDoneFence = pDoneFence; + return ma_sound_init_ex(pEngine, &config, pSound); } @@ -73552,17 +74949,20 @@ MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) return; } - /* The volume is controlled via the output bus. */ - ma_node_set_output_bus_volume(pSound, 0, volume); + ma_engine_node_set_volume(&pSound->engineNode, volume); } MA_API float ma_sound_get_volume(const ma_sound* pSound) { + float volume = 0; + if (pSound == NULL) { return 0; } - return ma_node_get_output_bus_volume(pSound, 0); + ma_engine_node_get_volume(&pSound->engineNode, &volume); + + return volume; } MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) @@ -73956,7 +75356,7 @@ MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); } -MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound) +MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) { if (pSound == NULL) { return MA_INVALID_ARGS; @@ -76318,7 +77718,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += drwav__write(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); } } break; case drwav_metadata_type_inst: @@ -77235,10 +78635,10 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, (void)pAllocationCallbacks; } #else - #if defined(__DJGPP__) - { - } - #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -77271,7 +78671,7 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, *ppFile = fopen(pFilePathMB, pOpenModeMB); drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } - #endif + #endif if (*ppFile == NULL) { return DRWAV_ERROR; } @@ -85196,10 +86596,10 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons (void)pAllocationCallbacks; } #else - #if defined(__DJGPP__) - { - } - #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -85232,7 +86632,7 @@ static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, cons *ppFile = fopen(pFilePathMB, pOpenModeMB); drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } - #endif + #endif if (*ppFile == NULL) { return DRFLAC_ERROR; } @@ -90532,10 +91932,10 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const (void)pAllocationCallbacks; } #else - #if defined(__DJGPP__) - { - } - #else + #if defined(__DJGPP__) + { + } + #else { mbstate_t mbs; size_t lenMB; @@ -90568,7 +91968,7 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const *ppFile = fopen(pFilePathMB, pOpenModeMB); drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } - #endif + #endif if (*ppFile == NULL) { return DRMP3_ERROR; } From 393b0d1a80cee881323e352164071f7bf6def557 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 12:06:54 +0100 Subject: [PATCH 0336/1710] Delete rcamera.old.h --- src/rcamera.old.h | 567 ---------------------------------------------- 1 file changed, 567 deletions(-) delete mode 100644 src/rcamera.old.h diff --git a/src/rcamera.old.h b/src/rcamera.old.h deleted file mode 100644 index 08c376690..000000000 --- a/src/rcamera.old.h +++ /dev/null @@ -1,567 +0,0 @@ -/******************************************************************************************* -* -* rcamera - Basic camera system for multiple camera modes -* -* NOTE: Memory footprint of this library is aproximately 52 bytes (global variables) -* -* CONFIGURATION: -* -* #define CAMERA_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 CAMERA_STANDALONE -* If defined, the library can be used as standalone as a camera system but some -* functions must be redefined to manage inputs accordingly. -* -* CONTRIBUTORS: -* Ramon Santamaria: Supervision, review, update and maintenance -* Marc Palau: Initial implementation (2014) -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2015-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 RCAMERA_H -#define RCAMERA_H - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -// NOTE: Below types are required for CAMERA_STANDALONE usage -//---------------------------------------------------------------------------------- -#if defined(CAMERA_STANDALONE) - // Vector2 type - typedef struct Vector2 { - float x; - float y; - } Vector2; - - // Vector3 type - typedef struct Vector3 { - float x; - float y; - float z; - } Vector3; - - // Camera type, defines a camera position/orientation in 3d space - typedef struct Camera3D { - 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 apperture in Y (degrees) in perspective, used as near plane width in orthographic - int type; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC - } Camera3D; - - typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D - - // Camera system modes - typedef enum { - CAMERA_CUSTOM = 0, - CAMERA_FREE, - CAMERA_ORBITAL, - CAMERA_FIRST_PERSON, - CAMERA_THIRD_PERSON - } CameraMode; - - // Camera projection modes - typedef enum { - CAMERA_PERSPECTIVE = 0, - CAMERA_ORTHOGRAPHIC - } CameraProjection; -#endif - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - -#if defined(CAMERA_STANDALONE) -void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) -void UpdateCamera(Camera *camera); // Update camera position for selected mode - -void SetCameraPanControl(int keyPan); // Set camera pan key to combine with mouse movement (free camera) -void SetCameraAltControl(int keyAlt); // Set camera alt key to combine with mouse movement (free camera) -void SetCameraSmoothZoomControl(int szoomKey); // Set camera smooth zoom key to combine with mouse (free camera) -void SetCameraMoveControls(int keyFront, int keyBack, - int keyRight, int keyLeft, - int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) -#endif - -#ifdef __cplusplus -} -#endif - -#endif // CAMERA_H - - -/*********************************************************************************** -* -* CAMERA IMPLEMENTATION -* -************************************************************************************/ - -#if defined(CAMERA_IMPLEMENTATION) - -#include // Required for: sinf(), cosf(), sqrtf() - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846 -#endif -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif - -// Camera mouse movement sensitivity -#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f -#define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f - -// FREE_CAMERA -#define CAMERA_FREE_MOUSE_SENSITIVITY 0.01f -#define CAMERA_FREE_DISTANCE_MIN_CLAMP 0.3f -#define CAMERA_FREE_DISTANCE_MAX_CLAMP 120.0f -#define CAMERA_FREE_MIN_CLAMP 85.0f -#define CAMERA_FREE_MAX_CLAMP -85.0f -#define CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY 0.05f -#define CAMERA_FREE_PANNING_DIVIDER 5.1f - -// ORBITAL_CAMERA -#define CAMERA_ORBITAL_SPEED 0.01f // Radians per frame - -// FIRST_PERSON -//#define CAMERA_FIRST_PERSON_MOUSE_SENSITIVITY 0.003f -#define CAMERA_FIRST_PERSON_FOCUS_DISTANCE 25.0f -#define CAMERA_FIRST_PERSON_MIN_CLAMP 89.0f -#define CAMERA_FIRST_PERSON_MAX_CLAMP -89.0f - -#define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 8.0f -#define CAMERA_FIRST_PERSON_STEP_DIVIDER 30.0f -#define CAMERA_FIRST_PERSON_WAVING_DIVIDER 200.0f - -// THIRD_PERSON -//#define CAMERA_THIRD_PERSON_MOUSE_SENSITIVITY 0.003f -#define CAMERA_THIRD_PERSON_DISTANCE_CLAMP 1.2f -#define CAMERA_THIRD_PERSON_MIN_CLAMP 5.0f -#define CAMERA_THIRD_PERSON_MAX_CLAMP -85.0f -#define CAMERA_THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f } - -// PLAYER (used by camera) -#define PLAYER_MOVEMENT_SENSITIVITY 20.0f - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -// Camera move modes (first person and third person cameras) -typedef enum { - MOVE_FRONT = 0, - MOVE_BACK, - MOVE_RIGHT, - MOVE_LEFT, - MOVE_UP, - MOVE_DOWN -} CameraMove; - -// Camera global state context data [56 bytes] -typedef struct { - unsigned int mode; // Current camera mode - float targetDistance; // Camera distance from position to target - float playerEyesPosition; // Player eyes position from ground (in meters) - Vector2 angle; // Camera angle in plane XZ - Vector2 previousMousePosition; // Previous mouse position - - // Camera movement control keys - int moveControl[6]; // Move controls (CAMERA_FIRST_PERSON) - int smoothZoomControl; // Smooth zoom control key - int altControl; // Alternative control key - int panControl; // Pan view control key -} CameraData; - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -static CameraData CAMERA = { // Global CAMERA state context - .mode = 0, - .targetDistance = 0, - .playerEyesPosition = 1.85f, - .angle = { 0 }, - .previousMousePosition = { 0 }, - .moveControl = { 'W', 'S', 'D', 'A', 'E', 'Q' }, - .smoothZoomControl = 341, // raylib: KEY_LEFT_CONTROL - .altControl = 342, // raylib: KEY_LEFT_ALT - .panControl = 2 // raylib: MOUSE_BUTTON_MIDDLE -}; - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -#if defined(CAMERA_STANDALONE) -// NOTE: Camera controls depend on some raylib input functions -static void EnableCursor() {} // Unlock cursor -static void DisableCursor() {} // Lock cursor - -static int IsKeyDown(int key) { return 0; } - -static int IsMouseButtonDown(int button) { return 0;} -static float GetMouseWheelMove() { return 0.0f; } -static Vector2 GetMousePosition() { return (Vector2){ 0.0f, 0.0f }; } -#endif - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- - -// Select camera mode (multiple camera modes available) -void SetCameraMode(Camera camera, int mode) -{ - Vector3 v1 = camera.position; - Vector3 v2 = camera.target; - - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - - CAMERA.targetDistance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance to target - - // Camera angle calculation - CAMERA.angle.x = atan2f(dx, dz); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) - CAMERA.angle.y = atan2f(dy, sqrtf(dx*dx + dz*dz)); // Camera angle in plane XY (0 aligned with X, move positive CW) - - CAMERA.playerEyesPosition = camera.position.y; // Init player eyes position to camera Y position - - CAMERA.previousMousePosition = GetMousePosition(); // Init mouse position - - // Lock cursor for first person and third person cameras - if ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)) DisableCursor(); - else EnableCursor(); - - CAMERA.mode = mode; -} - -// Update camera depending on selected mode -// NOTE: Camera controls depend on some raylib functions: -// System: EnableCursor(), DisableCursor() -// Mouse: IsMouseButtonDown(), GetMousePosition(), GetMouseWheelMove() -// Keys: IsKeyDown() -void UpdateCamera(Camera *camera) -{ - static int swingCounter = 0; // Used for 1st person swinging movement - - // TODO: Compute CAMERA.targetDistance and CAMERA.angle here (?) - - // Mouse movement detection - Vector2 mousePositionDelta = { 0.0f, 0.0f }; - Vector2 mousePosition = GetMousePosition(); - float mouseWheelMove = GetMouseWheelMove(); - - // Keys input detection - // TODO: Input detection is raylib-dependant, it could be moved outside the module - bool keyPan = IsMouseButtonDown(CAMERA.panControl); - bool keyAlt = IsKeyDown(CAMERA.altControl); - bool szoomKey = IsKeyDown(CAMERA.smoothZoomControl); - bool direction[6] = { IsKeyDown(CAMERA.moveControl[MOVE_FRONT]), - IsKeyDown(CAMERA.moveControl[MOVE_BACK]), - IsKeyDown(CAMERA.moveControl[MOVE_RIGHT]), - IsKeyDown(CAMERA.moveControl[MOVE_LEFT]), - IsKeyDown(CAMERA.moveControl[MOVE_UP]), - IsKeyDown(CAMERA.moveControl[MOVE_DOWN]) }; - - if (CAMERA.mode != CAMERA_CUSTOM) - { - mousePositionDelta.x = mousePosition.x - CAMERA.previousMousePosition.x; - mousePositionDelta.y = mousePosition.y - CAMERA.previousMousePosition.y; - - CAMERA.previousMousePosition = mousePosition; - } - - // Support for multiple automatic camera modes - // NOTE: In case of CAMERA_CUSTOM nothing happens here, user must update it manually - switch (CAMERA.mode) - { - case CAMERA_FREE: // Camera free controls, using standard 3d-content-creation scheme - { - // Camera zoom - if ((CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP; - } - - // Camera looking down - else if ((camera->position.y > camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - } - else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - - // if (camera->target.y < 0) camera->target.y = -0.001; - } - else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; - } - // Camera looking up - else if ((camera->position.y < camera->target.y) && (CAMERA.targetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - } - else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0)) - { - camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/CAMERA.targetDistance; - - // if (camera->target.y > 0) camera->target.y = 0.001; - } - else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0)) - { - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - if (CAMERA.targetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) CAMERA.targetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; - } - - // Input keys checks - if (keyPan) - { - if (keyAlt) // Alternative key behaviour - { - if (szoomKey) - { - // Camera smooth zoom - CAMERA.targetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY); - } - else - { - // Camera rotation - CAMERA.angle.x += mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY; - CAMERA.angle.y += mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY; - - // Angle clamp - if (CAMERA.angle.y > CAMERA_FREE_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_FREE_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FREE_MAX_CLAMP*DEG2RAD; - } - } - else - { - // Camera panning - camera->target.x += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - camera->target.z += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(CAMERA.angle.x) + (mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(CAMERA.angle.x)*sinf(CAMERA.angle.y))*(CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER); - } - } - - // Update camera position with changes - camera->position.x = -sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance + camera->target.y; - camera->position.z = -cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; - - } break; - case CAMERA_ORBITAL: // Camera just orbits around target, only zoom allowed - { - CAMERA.angle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom - - // Camera distance clamp - if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; - - // Update camera position with changes - camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - camera->position.y = ((CAMERA.angle.y <= 0.0f)? 1 : -1)*sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; - - } break; - case CAMERA_FIRST_PERSON: // Camera moves as in a first-person game, controls are configurable - { - camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - - cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + - cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - - camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - - sinf(CAMERA.angle.y)*direction[MOVE_BACK] + - 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; - - camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - - cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + - sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - - sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - - // Camera orientation calculation - CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); - CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); - - // Angle clamp - if (CAMERA.angle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD; - - // Calculate translation matrix - Matrix matTranslation = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, (CAMERA.targetDistance/CAMERA_FREE_PANNING_DIVIDER), - 0.0f, 0.0f, 0.0f, 1.0f }; - - // Calculate rotation matrix - Matrix matRotation = { 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 }; - - float cosz = cosf(0.0f); - float sinz = sinf(0.0f); - float cosy = cosf(-(PI*2 - CAMERA.angle.x)); - float siny = sinf(-(PI*2 - CAMERA.angle.x)); - float cosx = cosf(-(PI*2 - CAMERA.angle.y)); - float sinx = sinf(-(PI*2 - CAMERA.angle.y)); - - matRotation.m0 = cosz*cosy; - matRotation.m4 = (cosz*siny*sinx) - (sinz*cosx); - matRotation.m8 = (cosz*siny*cosx) + (sinz*sinx); - matRotation.m1 = sinz*cosy; - matRotation.m5 = (sinz*siny*sinx) + (cosz*cosx); - matRotation.m9 = (sinz*siny*cosx) - (cosz*sinx); - matRotation.m2 = -siny; - matRotation.m6 = cosy*sinx; - matRotation.m10= cosy*cosx; - - // Multiply translation and rotation matrices - Matrix matTransform = { 0 }; - matTransform.m0 = matTranslation.m0*matRotation.m0 + matTranslation.m1*matRotation.m4 + matTranslation.m2*matRotation.m8 + matTranslation.m3*matRotation.m12; - matTransform.m1 = matTranslation.m0*matRotation.m1 + matTranslation.m1*matRotation.m5 + matTranslation.m2*matRotation.m9 + matTranslation.m3*matRotation.m13; - matTransform.m2 = matTranslation.m0*matRotation.m2 + matTranslation.m1*matRotation.m6 + matTranslation.m2*matRotation.m10 + matTranslation.m3*matRotation.m14; - matTransform.m3 = matTranslation.m0*matRotation.m3 + matTranslation.m1*matRotation.m7 + matTranslation.m2*matRotation.m11 + matTranslation.m3*matRotation.m15; - matTransform.m4 = matTranslation.m4*matRotation.m0 + matTranslation.m5*matRotation.m4 + matTranslation.m6*matRotation.m8 + matTranslation.m7*matRotation.m12; - matTransform.m5 = matTranslation.m4*matRotation.m1 + matTranslation.m5*matRotation.m5 + matTranslation.m6*matRotation.m9 + matTranslation.m7*matRotation.m13; - matTransform.m6 = matTranslation.m4*matRotation.m2 + matTranslation.m5*matRotation.m6 + matTranslation.m6*matRotation.m10 + matTranslation.m7*matRotation.m14; - matTransform.m7 = matTranslation.m4*matRotation.m3 + matTranslation.m5*matRotation.m7 + matTranslation.m6*matRotation.m11 + matTranslation.m7*matRotation.m15; - matTransform.m8 = matTranslation.m8*matRotation.m0 + matTranslation.m9*matRotation.m4 + matTranslation.m10*matRotation.m8 + matTranslation.m11*matRotation.m12; - matTransform.m9 = matTranslation.m8*matRotation.m1 + matTranslation.m9*matRotation.m5 + matTranslation.m10*matRotation.m9 + matTranslation.m11*matRotation.m13; - matTransform.m10 = matTranslation.m8*matRotation.m2 + matTranslation.m9*matRotation.m6 + matTranslation.m10*matRotation.m10 + matTranslation.m11*matRotation.m14; - matTransform.m11 = matTranslation.m8*matRotation.m3 + matTranslation.m9*matRotation.m7 + matTranslation.m10*matRotation.m11 + matTranslation.m11*matRotation.m15; - matTransform.m12 = matTranslation.m12*matRotation.m0 + matTranslation.m13*matRotation.m4 + matTranslation.m14*matRotation.m8 + matTranslation.m15*matRotation.m12; - matTransform.m13 = matTranslation.m12*matRotation.m1 + matTranslation.m13*matRotation.m5 + matTranslation.m14*matRotation.m9 + matTranslation.m15*matRotation.m13; - matTransform.m14 = matTranslation.m12*matRotation.m2 + matTranslation.m13*matRotation.m6 + matTranslation.m14*matRotation.m10 + matTranslation.m15*matRotation.m14; - matTransform.m15 = matTranslation.m12*matRotation.m3 + matTranslation.m13*matRotation.m7 + matTranslation.m14*matRotation.m11 + matTranslation.m15*matRotation.m15; - - camera->target.x = camera->position.x - matTransform.m12; - camera->target.y = camera->position.y - matTransform.m13; - camera->target.z = camera->position.z - matTransform.m14; - - // If movement detected (some key pressed), increase swinging - for (int i = 0; i < 6; i++) if (direction[i]) { swingCounter++; break; } - - // Camera position update - // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position' - camera->position.y = CAMERA.playerEyesPosition - sinf(swingCounter/CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER)/CAMERA_FIRST_PERSON_STEP_DIVIDER; - - camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; - camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; - - } break; - case CAMERA_THIRD_PERSON: // Camera moves as in a third-person game, following target at a distance, controls are configurable - { - camera->position.x += (sinf(CAMERA.angle.x)*direction[MOVE_BACK] - - sinf(CAMERA.angle.x)*direction[MOVE_FRONT] - - cosf(CAMERA.angle.x)*direction[MOVE_LEFT] + - cosf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - - camera->position.y += (sinf(CAMERA.angle.y)*direction[MOVE_FRONT] - - sinf(CAMERA.angle.y)*direction[MOVE_BACK] + - 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY; - - camera->position.z += (cosf(CAMERA.angle.x)*direction[MOVE_BACK] - - cosf(CAMERA.angle.x)*direction[MOVE_FRONT] + - sinf(CAMERA.angle.x)*direction[MOVE_LEFT] - - sinf(CAMERA.angle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - - // Camera orientation calculation - CAMERA.angle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY); - CAMERA.angle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY); - - // Angle clamp - if (CAMERA.angle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD; - else if (CAMERA.angle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) CAMERA.angle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD; - - // Camera zoom - CAMERA.targetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); - - // Camera distance clamp - if (CAMERA.targetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) CAMERA.targetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP; - - camera->position.x = sinf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.x; - - if (CAMERA.angle.y <= 0.0f) camera->position.y = sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - else camera->position.y = -sinf(CAMERA.angle.y)*CAMERA.targetDistance*sinf(CAMERA.angle.y) + camera->target.y; - - camera->position.z = cosf(CAMERA.angle.x)*CAMERA.targetDistance*cosf(CAMERA.angle.y) + camera->target.z; - - } break; - case CAMERA_CUSTOM: break; - default: break; - } -} - -// Set camera pan key to combine with mouse movement (free camera) -void SetCameraPanControl(int keyPan) { CAMERA.panControl = keyPan; } - -// Set camera alt key to combine with mouse movement (free camera) -void SetCameraAltControl(int keyAlt) { CAMERA.altControl = keyAlt; } - -// Set camera smooth zoom key to combine with mouse (free camera) -void SetCameraSmoothZoomControl(int szoomKey) { CAMERA.smoothZoomControl = szoomKey; } - -// Set camera move controls (1st person and 3rd person cameras) -void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int keyLeft, int keyUp, int keyDown) -{ - CAMERA.moveControl[MOVE_FRONT] = keyFront; - CAMERA.moveControl[MOVE_BACK] = keyBack; - CAMERA.moveControl[MOVE_RIGHT] = keyRight; - CAMERA.moveControl[MOVE_LEFT] = keyLeft; - CAMERA.moveControl[MOVE_UP] = keyUp; - CAMERA.moveControl[MOVE_DOWN] = keyDown; -} - -#endif // CAMERA_IMPLEMENTATION From a7f81b06b93a4305186999e407b32bcb2ba78f04 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 12:08:23 +0100 Subject: [PATCH 0337/1710] Remove trailing spaces --- CHANGELOG | 6 +++--- ROADMAP.md | 17 ++++++++++------- src/rmodels.c | 4 ++-- src/rtext.c | 4 ++-- src/rtextures.c | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 36c8bd282..fa89b3ba5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -38,7 +38,7 @@ Detailed changes: [core] REVIEWED: GetKeyPressed(), out of range issue (#2814) by @daipom [core] REVIEWED: GetTime(), renamed variable 'time' to 'nanoSeconds' (#2816) by @jtainer [core] REVIEWED: LoadShaderFromMemory(), issue with shader linkage -[core] REVIEWED: Avoid possible gamepad index as -1 (#2839) +[core] REVIEWED: Avoid possible gamepad index as -1 (#2839) [core] REVIEWED: SetShaderValue*(), avoid setup uniforms for invalid locations [core] REVIEWED: GetClipboardText() on PLATFORM_WEB, permissions issues [core] REVIEWED: Initial window position for display-sized fullscreen (#2742) by @daipom @@ -60,7 +60,7 @@ Detailed changes: [rlgl] ADDED: RL_TEXTURE_MIPMAP_BIAS_RATIO support to `rlTextureParameters()` for OpenGL 3.3 #2674 [rlgl] ADDED: rlCubemapParameters() (#2862) by @GithubPrankster [rlgl] ADDED: rlSetCullFace() (#2797) by @jtainer -[rlgl] REMOVED: Mipmaps software generation for OpenGL 1.1 +[rlgl] REMOVED: Mipmaps software generation for OpenGL 1.1 [rlgl] REVIEWED: Check for extensions before enabling them (#2706) by @Not-Nik [rlgl] REVIEWED: SSBO usage to avoid long long data types [rlgl] REVIEWED: Enable DXT compression on __APPLE__ targets (#2694) by @Not-Nik @@ -85,7 +85,7 @@ Detailed changes: [textures] ADDED: ImageBlurGaussian() (#2770) by @nobytesgiven [textures] REVIEWED: Image fileformat support: PIC, PNM [textures] REVIEWED: ImageTextEx() and ImageDrawTextEx() scaling (#2756) by @hatkidchan -[textures] `WARNING`: REMOVED: DrawTextureQuad() +[textures] `WARNING`: REMOVED: DrawTextureQuad() [textures] `WARNING`: REMOVED: DrawTexturePoly(), function moved to example: `textures_polygon` [textures] `WARNING`: REMOVED: DrawTextureTiled(),function implementation moved to the textures_tiled.c [text] ADDED: GetCodepointPrevious() diff --git a/ROADMAP.md b/ROADMAP.md index 0d126c86f..4c7c965e9 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,8 +3,11 @@ Here it is a wishlist with features and ideas to improve the library. Note that features listed here are usually long term improvements or just describe a route to follow for the library. There are also some additional places to look for raylib improvements and ideas: - [GitHub Issues](https://github.com/raysan5/raylib/issues) has several open issues for possible improvements or bugs to fix. - - [raylib source code](https://github.com/raysan5/raylib/tree/master/src) has multiple *TODO* comments around code with pending things to review or improve. - - raylib wishlists discussions (https://github.com/raysan5/raylib/discussions/1502, https://github.com/raysan5/raylib/discussions/2272) are open to everyone to ask for improvements, feel free to check and comment. + - [raylib source code](https://github.com/raysan5/raylib/tree/master/src) has multiple *TODO* comments around code with pending things to review or improve. + - raylib wishlists discussions are open to everyone to ask for improvements, feel free to check and comment: + - [raylib wishlist 2021](https://github.com/raysan5/raylib/discussions/1502) + - [raylib wishlist 2022](https://github.com/raysan5/raylib/discussions/2272) + - [raylib 5.0 wishlist](https://github.com/raysan5/raylib/discussions/2952) _Current version of raylib is complete and functional but there is always room for improvements._ @@ -23,23 +26,23 @@ _Current version of raylib is complete and functional but there is always room f - Basic CPU/GPU stats system (memory, draws, time...) ([#1295](https://github.com/raysan5/raylib/issues/1295)) - _DISCARDED_ - Software rendering backend (avoiding OpenGL) ([#1370](https://github.com/raysan5/raylib/issues/1370)) - _DISCARDED_ - Network module (UDP): `rnet` ([#753](https://github.com/raysan5/raylib/issues/753)) - _DISCARDED_ - Use [nbnet](https://github.com/nathhB/nbnet). - + **raylib 3.0** - [x] Custom memory allocators support - [x] Global variables moved to global context - [x] Optimize data structures for pass-by-value - [x] Trace log messages redesign ([#1065](https://github.com/raysan5/raylib/issues/1065)) - [x] Continuous Integration using GitHub Actions - + **raylib 2.5** - [x] Support Animated models - [x] Support glTF models file format - [x] Unicode support on text drawing - + **raylib 2.0** - [x] Removed external dependencies (GLFW3 and OpenAL) - [x] Support TCC compiler (32bit and 64bit) - + **raylib 1.8** - [x] Improved Materials system with PBR support - [x] Procedural image generation functions (spot, gradient, noise...) @@ -50,7 +53,7 @@ _Current version of raylib is complete and functional but there is always room f - [x] Support configuration flags - [x] Improved build system for Android - [x] Gamepad support on HTML5 - + **raylib 1.6** - [x] Lua scripting support (raylib Lua wrapper) - [x] Redesigned audio module diff --git a/src/rmodels.c b/src/rmodels.c index 8ab923421..2b1d0f936 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1117,8 +1117,8 @@ bool IsModelReady(Model model) (model.meshMaterial != NULL) && // Validate mesh-material linkage (model.meshCount > 0) && // Validate mesh count (model.materialCount > 0)); // Validate material count - - // NOTE: This is a very general model validation, many elements could be validated from a model... + + // NOTE: This is a very general model validation, many elements could be validated from a model... } // Unload model (meshes/materials) from memory (RAM and/or VRAM) diff --git a/src/rtext.c b/src/rtext.c index 2d47bdf6c..9eec0059f 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -543,8 +543,8 @@ bool IsFontReady(Font font) (font.glyphCount > 0) && // Validate font contains some glyph (font.recs != NULL) && // Validate font recs defining glyphs on texture atlas (font.glyphs != NULL)); // Validate glyph data is loaded - - // NOTE: Further validations could be done to verify if recs count and glyphs count + + // NOTE: Further validations could be done to verify if recs count and glyphs count // match glyphCount and to verify that data contained is valid (glyphs values, metrics...) } diff --git a/src/rtextures.c b/src/rtextures.c index 12bca8a8e..b5f9998a9 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3364,7 +3364,7 @@ bool IsRenderTextureReady(RenderTexture2D target) { return ((target.id > 0) && // Validate OpenGL id IsTextureReady(target.depth) && // Validate FBO depth texture/renderbuffer - IsTextureReady(target.texture)); // Validate FBO texture + IsTextureReady(target.texture)); // Validate FBO texture } // Unload render texture from GPU memory (VRAM) From 90ec0d52e045b4b3be9e2a7cd8d18a742882e457 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 23:12:23 +0100 Subject: [PATCH 0338/1710] Reviewed filename --- examples/models/models_loading_m3d.c | 2 +- .../models/m3d/{CesiumMan.m3d => cesium_man.m3d} | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename examples/models/resources/models/m3d/{CesiumMan.m3d => cesium_man.m3d} (100%) diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index 18c8e1afc..b9674b6f6 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -41,7 +41,7 @@ int main(void) Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - char modelFileName[128] = "resources/models/m3d/CesiumMan.m3d"; + char modelFileName[128] = "resources/models/m3d/cesium_man.m3d"; bool drawMesh = 1; bool drawSkeleton = 1; bool animPlaying = false; // Store anim state, what to draw diff --git a/examples/models/resources/models/m3d/CesiumMan.m3d b/examples/models/resources/models/m3d/cesium_man.m3d similarity index 100% rename from examples/models/resources/models/m3d/CesiumMan.m3d rename to examples/models/resources/models/m3d/cesium_man.m3d From 5a2c49b9541493b446291f2ff6a241522432b478 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 23:13:02 +0100 Subject: [PATCH 0339/1710] Updated Makefiles to include all missing new examples --- examples/Makefile | 10 +++-- examples/Makefile.Web | 90 ++++++++++++++++++++++++++++++------------- src/Makefile | 10 +++-- 3 files changed, 76 insertions(+), 34 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 6b0b4a848..04bd14e65 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,7 +30,7 @@ PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 4.2.0 +RAYLIB_VERSION ?= 4.5.0 RAYLIB_PATH ?= .. # Locations of raylib.h and libraylib.a/libraylib.so @@ -129,7 +129,7 @@ endif RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src ifeq ($(PLATFORM),PLATFORM_WEB) - ifeq ($(PLATFORM_OS),WINDOWS) + ifeq ($(PLATFORM_OS),WINDOWS) # Emscripten required variables EMSDK_PATH ?= C:/emsdk EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten @@ -137,7 +137,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-1_64bit NODE_PATH = $(EMSDK_PATH)/node/14.15.5_64bit/bin export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) - endif + endif endif # Define default C compiler: CC @@ -489,6 +489,7 @@ MODELS = \ models/models_billboard \ models/models_box_collisions \ models/models_cubicmap \ + models/models_draw_cube_texture \ models/models_first_person_maze \ models/models_geometric_shapes \ models/models_mesh_generation \ @@ -531,7 +532,8 @@ AUDIO = \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ - audio/audio_stream_effects + audio/audio_stream_effects \ + audio/audio_mixed_processor CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 063530fb3..6af43758a 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -30,7 +30,7 @@ PLATFORM ?= PLATFORM_WEB # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 4.2.0 +RAYLIB_VERSION ?= 4.5.0 RAYLIB_PATH ?= .. # Locations of raylib.h and libraylib.a/libraylib.so @@ -60,8 +60,8 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif endif -# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +# Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -119,16 +119,18 @@ ifeq ($(PLATFORM),PLATFORM_DRM) endif # Define raylib release directory for compiled library -RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src +RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src ifeq ($(PLATFORM),PLATFORM_WEB) - # Emscripten required variables - EMSDK_PATH ?= C:/emsdk - EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten - CLANG_PATH = $(EMSDK_PATH)/upstream/bin - PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-1_64bit - NODE_PATH = $(EMSDK_PATH)/node/14.15.5_64bit/bin - export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) + ifeq ($(PLATFORM_OS),WINDOWS) + # Emscripten required variables + EMSDK_PATH ?= C:/emsdk + EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten + CLANG_PATH = $(EMSDK_PATH)/upstream/bin + PYTHON_PATH = $(EMSDK_PATH)/python/3.9.2-1_64bit + NODE_PATH = $(EMSDK_PATH)/node/14.15.5_64bit/bin + export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH):$$(PATH) + endif endif # Define default C compiler: CC @@ -195,9 +197,13 @@ ifeq ($(BUILD_MODE),DEBUG) endif else ifeq ($(PLATFORM),PLATFORM_WEB) - CFLAGS += -Os + ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) + CFLAGS += -O3 + else + CFLAGS += -Os + endif else - CFLAGS += -s -O1 + CFLAGS += -s -O2 endif endif @@ -329,7 +335,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(RAYLIB_LIBTYPE),SHARED) LDLIBS += -lc endif - + # NOTE: On ARM 32bit arch, miniaudio requires atomics library LDLIBS += -latomic endif @@ -394,13 +400,13 @@ CORE = \ core/core_scissor_test \ core/core_storage_values \ core/core_vr_simulator \ - core/core_loading_thread \ core/core_window_flags \ core/core_window_letterbox \ core/core_window_should_close \ core/core_split_screen \ core/core_smooth_pixelperfect \ - core/core_custom_frame_control + core/core_custom_frame_control \ + core/core_loading_thread SHAPES = \ shapes/shapes_basic_shapes \ @@ -437,6 +443,7 @@ TEXTURES = \ textures/textures_sprite_anim \ textures/textures_sprite_button \ textures/textures_sprite_explosion \ + textures/textures_textured_curve \ textures/textures_bunnymark \ textures/textures_blend_modes \ textures/textures_draw_tiled \ @@ -463,6 +470,7 @@ MODELS = \ models/models_billboard \ models/models_box_collisions \ models/models_cubicmap \ + models/models_draw_cube_texture \ models/models_first_person_maze \ models/models_geometric_shapes \ models/models_mesh_generation \ @@ -496,14 +504,17 @@ SHADERS = \ shaders/shaders_spotlight \ shaders/shaders_hot_reloading \ shaders/shaders_mesh_instancing \ - shaders/shaders_multi_sample2d + shaders/shaders_multi_sample2d \ + shaders/shaders_write_depth \ + shaders/shaders_hybrid_render AUDIO = \ audio/audio_module_playing \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ - audio/audio_stream_effects + audio/audio_stream_effects \ + audio/audio_mixed_processor CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) @@ -593,13 +604,6 @@ core/core_vr_simulator: core/core_vr_simulator.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file core/resources/distortion100.fs@resources/distortion100.fs -# NOTE: To use multi-threading raylib must be compiled with multi-theading support (-s USE_PTHREADS=1) -# WARNING: For security reasons multi-threading is not supported on browsers, it requires cross-origin isolation (Oct.2021) -# WARNING: It requires raylib to be compiled using -pthread, so atomic operations and thread-local data (if any) -# in its source were transformed to non-atomic operations and non-thread-local data -core/core_loading_thread: core/core_loading_thread.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s USE_PTHREADS=1 - core/core_window_flags: core/core_window_flags.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -614,6 +618,14 @@ core/core_custom_frame_control: core/core_custom_frame_control.c core/core_window_should_close: core/core_window_should_close.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +# NOTE: To use multi-threading raylib must be compiled with multi-theading support (-s USE_PTHREADS=1) +# WARNING: For security reasons multi-threading is not supported on browsers, it requires cross-origin isolation (Oct.2021) +# WARNING: It requires raylib to be compiled using -pthread, so atomic operations and thread-local data (if any) +# in its source were transformed to non-atomic operations and non-thread-local data +core/core_loading_thread: core/core_loading_thread.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s USE_PTHREADS=1 + # Compile SHAPES examples shapes/shapes_basic_shapes: shapes/shapes_basic_shapes.c @@ -733,6 +745,10 @@ textures/textures_sprite_explosion: textures/textures_sprite_explosion.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/explosion.png@resources/explosion.png \ --preload-file textures/resources/boom.wav@resources/boom.wav + +textures/textures_textured_curve: textures/textures_textured_curve.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/resources/road.png@resources/road.png textures/textures_bunnymark: textures/textures_bunnymark.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -839,6 +855,10 @@ models/models_cubicmap: models/models_cubicmap.c --preload-file models/resources/cubicmap.png@resources/cubicmap.png \ --preload-file models/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png +models/models_draw_cube_texture: models/models_draw_cube_texture.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file models/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png + models/models_first_person_maze: models/models_first_person_maze.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/cubicmap.png@resources/cubicmap.png \ @@ -869,6 +889,10 @@ models/models_loading_vox: models/models_loading_vox.c models/models_loading_gltf: models/models_loading_gltf.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb + +models/models_loading_m3d: models/models_loading_m3d.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ + --preload-file models/resources/models/m3d/cesium_man.m3d@resources/models/m3d/cesium_man.m3d models/models_orthographic_projection: models/models_orthographic_projection.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -987,7 +1011,16 @@ shaders/shaders_texture_outline: shaders/shaders_texture_outline.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/outline.fs@resources/shaders/glsl100/outline.fs \ --preload-file shaders/resources/fudesumi.png@resources/fudesumi.png - + +shaders/shaders_write_depth: shaders/shaders_write_depth.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file shaders/resources/shaders/glsl100/write_depth.fs@resources/shaders/glsl100/write_depth.fs + +shaders/shaders_hybrid_render: shaders/shaders_hybrid_render.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file shaders/resources/shaders/glsl100/hybrid_raymarch.fs@resources/shaders/glsl100/hybrid_raymarch.fs \ + --preload-file shaders/resources/shaders/glsl100/hybrid_raster.fs@resources/shaders/glsl100/hybrid_raster.fs + # Compile AUDIO examples audio/audio_module_playing: audio/audio_module_playing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1008,6 +1041,11 @@ audio/audio_sound_loading: audio/audio_sound_loading.c audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 + +audio/audio_mixed_processor: audio/audio_mixed_processor.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ + --preload-file audio/resources/country.mp3@resources/country.mp3 \ + --preload-file audio/resources/coin.wav@resources/coin.wav # Clean everything clean: diff --git a/src/Makefile b/src/Makefile index 4d9c8723b..1667a9744 100644 --- a/src/Makefile +++ b/src/Makefile @@ -45,8 +45,8 @@ PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables -RAYLIB_VERSION = 4.2.0 -RAYLIB_API_VERSION = 420 +RAYLIB_VERSION = 4.5.0 +RAYLIB_API_VERSION = 450 # Define raylib source code path RAYLIB_SRC_PATH ?= ../src @@ -231,8 +231,10 @@ endif ifeq ($(PLATFORM),PLATFORM_DESKTOP) # By default use OpenGL 3.3 on desktop platforms GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 - #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 + #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 + #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 + #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 + #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif ifeq ($(PLATFORM),PLATFORM_RPI) # On RPI OpenGL ES 2.0 must be used From 1a361fdfe2629235a0c14ebe58fc78678e033738 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 13 Mar 2023 23:24:30 +0100 Subject: [PATCH 0340/1710] Update Makefile.Web --- examples/Makefile.Web | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 6af43758a..499eb3eaa 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -974,13 +974,13 @@ shaders/shaders_basic_lighting: shaders/shaders_basic_lighting.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/texel_checker.png@resources/texel_checker.png \ --preload-file shaders/resources/shaders/glsl100/lighting.fs@resources/shaders/glsl100/lighting.fs \ - --preload-file shaders/resources/shaders/glsl100/base_lighting.vs@resources/shaders/glsl100/base_lighting.vs + --preload-file shaders/resources/shaders/glsl100/lighting.vs@resources/shaders/glsl100/lighting.vs shaders/shaders_fog: shaders/shaders_fog.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/texel_checker.png@resources/texel_checker.png \ --preload-file shaders/resources/shaders/glsl100/fog.fs@resources/shaders/glsl100/fog.fs \ - --preload-file shaders/resources/shaders/glsl100/base_lighting.vs@resources/shaders/glsl100/base_lighting.vs + --preload-file shaders/resources/shaders/glsl100/lighting.vs@resources/shaders/glsl100/lighting.vs shaders/shaders_simple_mask: shaders/shaders_simple_mask.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -999,10 +999,9 @@ shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c shaders/shaders_mesh_instancing: shaders/shaders_mesh_instancing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file shaders/resources/shaders/glsl100/base_lighting_instanced.vs@resources/shaders/glsl100/base_lighting_instanced.vs \ + --preload-file shaders/resources/shaders/glsl100/lighting_instancing.vs@resources/shaders/glsl100/lighting_instancing.vs \ --preload-file shaders/resources/shaders/glsl100/lighting.fs@resources/shaders/glsl100/lighting.fs - shaders/shaders_multi_sample2d: shaders/shaders_multi_sample2d.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/color_mix.fs@resources/shaders/glsl100/color_mix.fs From a6300d828ae77c3021655fcd88d26c4bf8f69de1 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Mar 2023 13:05:58 +0100 Subject: [PATCH 0341/1710] Update CHANGELOG --- CHANGELOG | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fa89b3ba5..10c96f7b4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,13 +7,13 @@ Current Release: raylib 4.5.0 (16 March 2023) Release: raylib 4.5 (16 March 2023) ------------------------------------------------------------------------- KEY CHANGES: - - ADDED: M3D model format support with animations - - ADDED: GLTF animation support - - ADDED: QOA audio format support (import/export) - - REDESIGNED: rcamera module, new implementation from scratch, simpler and more extendable - - REDESIGNED: rlgl module to avoid render batch triangles limits pre-check: rlCheckRenderBatchLimit() - - REDESIGNED: rshapes module to minimize the rlgl dependency, now only 6 low-level functions required! - - ADDED: rl_gputex self-contained module for compressed textures loading, used by rtextures module + - ADDED: Improved ANGLE support on Desktop platforms + - ADDED: rcamera module, simpler and more extendable + - ADDED: Support for M3D models and M3D/GLTF animations + - ADDED: Support QOA audio format (import/export) + - ADDED: rl_gputex module for compressed textures loading + - REDESIGNED: rlgl module for automatic render-batch limits checking + - REDESIGNED: rshapes module to minimize the rlgl dependency Detailed changes: [core] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach @@ -122,8 +122,11 @@ Detailed changes: [multi] REVIEWED: Grammar mistakes and typos (#2914) by @stickM4N [multi] REVIEWED: Use TRACELOG() macro instead of TraceLog() in internal modules (#2881) by @RobLoach [examples] ADDED: textures_textured_curve (#2821) by @JeffM2501 +[examples] ADDED: models_draw_cube_texture +[examples] ADDED: models_loading_m3d (#2648) by @bztsrc [examples] ADDED: shaders_write_depth (#2836) by @BugraAlptekinSari [examples] ADDED: shaders_hybrid_render (#2919) by @BugraAlptekinSari +[examples] REMOVED: audio_multichannel_sound [examples] RENAMED: Several shaders for naming consistency (#2707) [examples] RENAMED: lighting_instanced.fs to lighting_instancing.fs (glsl100) (#2805) by @gtrxAC [examples] REVIEWED: core_custom_logging.c (#2692) by @hartmannathan From 975c70d2b790cab053ca2d5fa84e42809a4aeeee Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Mar 2023 13:06:17 +0100 Subject: [PATCH 0342/1710] Update rlgl.h --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 66a349645..86208de4c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rlgl v4.2 - A multi-OpenGL abstraction layer with an immediate-mode style API +* rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API * * An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) * that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) @@ -106,7 +106,7 @@ #ifndef RLGL_H #define RLGL_H -#define RLGL_VERSION "4.2" +#define RLGL_VERSION "4.5" // 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 From 2b9ef7d15c532e89b8a1b15f699b030acfdcda73 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Mar 2023 13:06:22 +0100 Subject: [PATCH 0343/1710] Update ROADMAP.md --- ROADMAP.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 4c7c965e9..61c6b56e3 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -12,10 +12,12 @@ Here it is a wishlist with features and ideas to improve the library. Note that _Current version of raylib is complete and functional but there is always room for improvements._ **raylib 4.x** - - [x] Redesign camera module (more flexible) ([#1143](https://github.com/raysan5/raylib/issues/1143), https://github.com/raysan5/raylib/discussions/2507) + - [ ] Split core module into separate platforms? + - [ ] Basic 2d software renderer, using `Image` provided API - [ ] Redesign gestures system, improve touch inputs management - - [ ] Redesign raudio module, implement miniaudio high-level provided features - - [x] Better documentation and improved examples + - [ ] Redesign audio module, implement miniaudio high-level provided features + - [x] Redesign camera module (more flexible) ([#1143](https://github.com/raysan5/raylib/issues/1143), https://github.com/raysan5/raylib/discussions/2507) + - [x] Better documentation and improved examples, reviewed webpage with examples complexity level - [x] Focus on HTML5 ([raylib 5k gamejam](https://itch.io/jam/raylib-5k-gamejam)) and embedded platforms (RPI and similar SOCs) - [x] Additional support libraries: [raygui](https://github.com/raysan5/raygui), [rres](https://github.com/raysan5/rres) From 7ac2b4a226f2bba358f62dc0f680131aad88ed45 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Mar 2023 13:07:24 +0100 Subject: [PATCH 0344/1710] Update HISTORY.md --- HISTORY.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 0b722b1a3..253a94c90 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -409,4 +409,26 @@ Some numbers for this release: - **+40** functions REVIEWED/REDESIGNED - **+40** new contributors (for a TOTAL of **405**!) --WIP- +Highlights for `raylib 4.5`: + + - **`NEW` Improved ANGLE support on Desktop platforms**: Support for OpenGL ES 2.0 on Desktop platforms (Windows, Linux, macOS) has been reviewed by @wtnbgo GitHub user. Now raylib can be compiled on desktop for OpenGL ES 2.0 and linked against [`ANGLE`](). This _small_ addition open the door to building raylib for all **ANGLE supported backends: Direct3D 11, Vulkan and Metal**. Please note that this new feature is still experimental and requires further testing! + + - **`NEW` Camera module**: A brand new implementation from scratch for `rcamera` module, contributed by @Crydsch GitHub user! **New camera system is simpler, more flexible, more granular and more extendable**. Specific camera math transformations (movement/rotation) have been moved to individual functions, exposing them to users if required. Global state has been removed from the module and standalone usage has been greatly improved; now `rcamera.h` single-file header-only library can be used externally, independently of raylib. A new `UpdateCameraPro()` function has been added to address input-dependency of `UpdateCamera()`, now advance users have **full control over camera inputs and movement/rotation speeds**! + + - **`NEW` Support for M3D models and M3D/GLTF animations**: 3d models animations support has been a limited aspect of raylib for long time, some versions ago IQM animations were supported but raylib 4.5 also adds support for the brand new [M3D file format](), including animations and the long expected support for **GLTF animations**! The new M3D file format is **simple, portable, feature complete, extensible and open source**. It also provides a complete set of tools to export/visualize M3D models from/to Blender! Now raylib supports up to **3 model file-formats with animations**: `IQM`, `GLTF` and `M3D`. + + - **`NEW` Support QOA audio format (import/export)**: Just a couple of months ago the new [QOA file format]() was published, a very simple, portable and open source quite-ok-audio file format. raylib already supports it, added to `raudio` module and including audio loading from file, loading from memory, streaming from file, streaming from memory and **exporting to QOA** audio format. **Because simplicity really matters to raylib!** + + - **`NEW` Module for compressed textures loading**: `rl_gputex.h`, a portable single-file header-only small library to load compressed texture file-formats (DDS, PKM, KTX, PVR, ASTC). Provided functionality is not new to raylib but it was part of the raylib `rtextures` module, now it has been moved into a separate self-contained library, **improving portability**. Note that this module is only intended to **load compressed data from files, ready to be uploaded to GPU**, no compression/decompression functionality is provided. This change is a first step towards a better modularization of raylib library. + + - **Reviewed `rlgl` module for automatic limits checking**: Again, [`rlgl`]() has been reviewed to simplify usage. Now users do not need to worry about reaching the internal render-batch limits when they send their triangles to draw 2d/3d, `rlgl` manages it automatically! This change allows a **great simplification for other modules** like `rshapes`, `rtextures` and `rmodels` that do not need to worry about bufffer overflows and can just define as many vertex as desired! + + - **Reviewed `rshapes` module to minimize the rlgl dependency**: Now `rshapes` 2d shapes drawing functions **only depend on 6 low-level functions**: `rlBegin()`, `rlEnd()`, `rlVertex3f()`, `rlTexCoord2f()`, `rlNormal3f()`, `rlSetTexture()`. With only those pseudo-OpenGl 1.1 minimal functionality, everything can be drawn! This improvement converts `rshapes` module in a **self-contained, portable shapes-drawing library that can be used independently of raylib**, as far as entry points for those 6 functions are provided by the user. It even allows to be used for software rendering, with the proper backend! + + - **Added data structures validation functions**: Multiple functions have been added by @RobLoach GitHub user to ease the validation of raylib data structures: `IsImageReady()`, `IsTextureReady()`, `IsSoundReady()`... Now users have a simple mechanism to **make sure data has been correctly loaded**, instead of checking internal structure values by themselfs. + +As usual, those are only some highlights but there is much more! New image generators, new color transformation functionality, improved blending support for color/alpha, etc... Make sure to check raylib [CHANGELOG](CHANGELOG) for a detailed list of changes! Please, note that all breaking changes have been flagged with a `WARNING` in the CHANGELOG, specially useful for binding creators! + +**raylib keeps improving one more version** with a special focus on maintainability and sustainability. Always working towards making the library more **simple and easy-to-use**. + +Let's keep **enjoying games/tools/graphics programming!** :) From 3d64598e11ecb9dc8f09440fba49ffbfe460c0d3 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Mar 2023 13:14:19 +0100 Subject: [PATCH 0345/1710] Update Makefile --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 04bd14e65..cc9ef2395 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -210,7 +210,7 @@ else CFLAGS += -Os endif else - CFLAGS += -s -O2 + CFLAGS += -O2 endif endif From 54ccb18e87645b6330960f1f88d5473f3171904d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Mar 2023 13:39:33 +0100 Subject: [PATCH 0346/1710] Update write_depth.fs --- .../shaders/resources/shaders/glsl330/write_depth.fs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl330/write_depth.fs b/examples/shaders/resources/shaders/glsl330/write_depth.fs index 88a4113ff..f0e07beec 100644 --- a/examples/shaders/resources/shaders/glsl330/write_depth.fs +++ b/examples/shaders/resources/shaders/glsl330/write_depth.fs @@ -1,15 +1,20 @@ #version 330 +// Input vertex attributes (from vertex shader) in vec2 fragTexCoord; in vec4 fragColor; +// Input uniform values uniform sampler2D texture0; uniform vec4 colDiffuse; +// Output fragment color +out vec4 finalColor; + void main() { - vec4 texelColor = texture2D(texture0, fragTexCoord); + vec4 texelColor = texture(texture0, fragTexCoord); - gl_FragColor = texelColor*colDiffuse*fragColor; - gl_FragDepth = 1.0 - gl_FragCoord.z; + finalColor = texelColor*colDiffuse*fragColor; + gl_FragDepth = 1.0 - finalColor.z; } From 796d96408bfd6dab9f255d45abc47d22373b6d82 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Mar 2023 14:06:37 +0100 Subject: [PATCH 0347/1710] Update HISTORY.md --- HISTORY.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 253a94c90..5ae12c05c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -411,23 +411,23 @@ Some numbers for this release: Highlights for `raylib 4.5`: - - **`NEW` Improved ANGLE support on Desktop platforms**: Support for OpenGL ES 2.0 on Desktop platforms (Windows, Linux, macOS) has been reviewed by @wtnbgo GitHub user. Now raylib can be compiled on desktop for OpenGL ES 2.0 and linked against [`ANGLE`](). This _small_ addition open the door to building raylib for all **ANGLE supported backends: Direct3D 11, Vulkan and Metal**. Please note that this new feature is still experimental and requires further testing! + - **`NEW` Improved ANGLE support on Desktop platforms**: Support for OpenGL ES 2.0 on Desktop platforms (Windows, Linux, macOS) has been reviewed by @wtnbgo GitHub user. Now raylib can be compiled on desktop for OpenGL ES 2.0 and linked against [`ANGLE`](https://github.com/google/angle). This _small_ addition open the door to building raylib for all **ANGLE supported backends: Direct3D 11, Vulkan and Metal**. Please note that this new feature is still experimental and requires further testing! - **`NEW` Camera module**: A brand new implementation from scratch for `rcamera` module, contributed by @Crydsch GitHub user! **New camera system is simpler, more flexible, more granular and more extendable**. Specific camera math transformations (movement/rotation) have been moved to individual functions, exposing them to users if required. Global state has been removed from the module and standalone usage has been greatly improved; now `rcamera.h` single-file header-only library can be used externally, independently of raylib. A new `UpdateCameraPro()` function has been added to address input-dependency of `UpdateCamera()`, now advance users have **full control over camera inputs and movement/rotation speeds**! - - **`NEW` Support for M3D models and M3D/GLTF animations**: 3d models animations support has been a limited aspect of raylib for long time, some versions ago IQM animations were supported but raylib 4.5 also adds support for the brand new [M3D file format](), including animations and the long expected support for **GLTF animations**! The new M3D file format is **simple, portable, feature complete, extensible and open source**. It also provides a complete set of tools to export/visualize M3D models from/to Blender! Now raylib supports up to **3 model file-formats with animations**: `IQM`, `GLTF` and `M3D`. + - **`NEW` Support for M3D models and M3D/GLTF animations**: 3d models animations support has been a limited aspect of raylib for long time, some versions ago IQM animations were supported but raylib 4.5 also adds support for the brand new [M3D file format](https://bztsrc.gitlab.io/model3d/), including animations and the long expected support for **GLTF animations**! The new M3D file format is **simple, portable, feature complete, extensible and open source**. It also provides a complete set of tools to export/visualize M3D models from/to Blender! Now raylib supports up to **3 model file-formats with animations**: `IQM`, `GLTF` and `M3D`. - - **`NEW` Support QOA audio format (import/export)**: Just a couple of months ago the new [QOA file format]() was published, a very simple, portable and open source quite-ok-audio file format. raylib already supports it, added to `raudio` module and including audio loading from file, loading from memory, streaming from file, streaming from memory and **exporting to QOA** audio format. **Because simplicity really matters to raylib!** + - **`NEW` Support QOA audio format (import/export)**: Just a couple of months ago the new [QOA file format](https://qoaformat.org/) was published, a very simple, portable and open source quite-ok-audio file format. raylib already supports it, added to `raudio` module and including audio loading from file, loading from memory, streaming from file, streaming from memory and **exporting to QOA** audio format. **Because simplicity really matters to raylib!** - - **`NEW` Module for compressed textures loading**: `rl_gputex.h`, a portable single-file header-only small library to load compressed texture file-formats (DDS, PKM, KTX, PVR, ASTC). Provided functionality is not new to raylib but it was part of the raylib `rtextures` module, now it has been moved into a separate self-contained library, **improving portability**. Note that this module is only intended to **load compressed data from files, ready to be uploaded to GPU**, no compression/decompression functionality is provided. This change is a first step towards a better modularization of raylib library. + - **`NEW` Module for compressed textures loading**: [`rl_gputex`](https://github.com/raysan5/raylib/blob/master/src/external/rl_gputex.h), a portable single-file header-only small library to load compressed texture file-formats (DDS, PKM, KTX, PVR, ASTC). Provided functionality is not new to raylib but it was part of the raylib `rtextures` module, now it has been moved into a separate self-contained library, **improving portability**. Note that this module is only intended to **load compressed data from files, ready to be uploaded to GPU**, no compression/decompression functionality is provided. This change is a first step towards a better modularization of raylib library. - - **Reviewed `rlgl` module for automatic limits checking**: Again, [`rlgl`]() has been reviewed to simplify usage. Now users do not need to worry about reaching the internal render-batch limits when they send their triangles to draw 2d/3d, `rlgl` manages it automatically! This change allows a **great simplification for other modules** like `rshapes`, `rtextures` and `rmodels` that do not need to worry about bufffer overflows and can just define as many vertex as desired! + - **Reviewed `rlgl` module for automatic limits checking**: Again, [`rlgl`](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) has been reviewed to simplify usage. Now users do not need to worry about reaching the internal render-batch limits when they send their triangles to draw 2d/3d, `rlgl` manages it automatically! This change allows a **great simplification for other modules** like `rshapes`, `rtextures` and `rmodels` that do not need to worry about bufffer overflows and can just define as many vertex as desired! - **Reviewed `rshapes` module to minimize the rlgl dependency**: Now `rshapes` 2d shapes drawing functions **only depend on 6 low-level functions**: `rlBegin()`, `rlEnd()`, `rlVertex3f()`, `rlTexCoord2f()`, `rlNormal3f()`, `rlSetTexture()`. With only those pseudo-OpenGl 1.1 minimal functionality, everything can be drawn! This improvement converts `rshapes` module in a **self-contained, portable shapes-drawing library that can be used independently of raylib**, as far as entry points for those 6 functions are provided by the user. It even allows to be used for software rendering, with the proper backend! - **Added data structures validation functions**: Multiple functions have been added by @RobLoach GitHub user to ease the validation of raylib data structures: `IsImageReady()`, `IsTextureReady()`, `IsSoundReady()`... Now users have a simple mechanism to **make sure data has been correctly loaded**, instead of checking internal structure values by themselfs. -As usual, those are only some highlights but there is much more! New image generators, new color transformation functionality, improved blending support for color/alpha, etc... Make sure to check raylib [CHANGELOG](CHANGELOG) for a detailed list of changes! Please, note that all breaking changes have been flagged with a `WARNING` in the CHANGELOG, specially useful for binding creators! +As usual, those are only some highlights but there is much more! New image generators, new color transformation functionality, improved blending support for color/alpha, etc... Make sure to check raylib [CHANGELOG]([CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG)) for a detailed list of changes! Please, note that all breaking changes have been flagged with a `WARNING` in the CHANGELOG, specially useful for binding creators! **raylib keeps improving one more version** with a special focus on maintainability and sustainability. Always working towards making the library more **simple and easy-to-use**. From cf04425bc2afcf8da998d8c5d24a320cf8a076bb Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Tue, 14 Mar 2023 23:23:17 +0500 Subject: [PATCH 0348/1710] Spelling (#2957) --- src/raudio.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 4bdbede9e..d7ee183a8 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1790,9 +1790,9 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); - frameCountReadTotal += frameCountRed; - frameCountStillNeeded -= frameCountRed; + int frameCountRead = (int)drwav_read_pcm_frames_s16((drwav *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); + frameCountReadTotal += frameCountRead; + frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); } @@ -1801,9 +1801,9 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); - frameCountReadTotal += frameCountRed; - frameCountStillNeeded -= frameCountRed; + int frameCountRead = (int)drwav_read_pcm_frames_f32((drwav *)music.ctxData, frameCountStillNeeded, (float *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); + frameCountReadTotal += frameCountRead; + frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; else drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); } @@ -1815,9 +1815,9 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRed = stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize), frameCountStillNeeded*music.stream.channels); - frameCountReadTotal += frameCountRed; - frameCountStillNeeded -= frameCountRed; + int frameCountRead = stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize), frameCountStillNeeded*music.stream.channels); + frameCountReadTotal += frameCountRead; + frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; else stb_vorbis_seek_start((stb_vorbis *)music.ctxData); } From ad2067340ffd601873b8c0d658a16e1ef3a98624 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Mar 2023 13:03:55 +0100 Subject: [PATCH 0349/1710] REVIEWED: `TraceLog()`, avoid possible buffer overflow --- src/utils.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils.c b/src/utils.c index 6843bd946..771271d35 100644 --- a/src/utils.c +++ b/src/utils.c @@ -54,7 +54,7 @@ // Defines and Macros //---------------------------------------------------------------------------------- #ifndef MAX_TRACELOG_MSG_LENGTH - #define MAX_TRACELOG_MSG_LENGTH 128 // Max length of one trace-log message + #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message #endif //---------------------------------------------------------------------------------- @@ -145,7 +145,8 @@ void TraceLog(int logType, const char *text, ...) default: break; } - strcat(buffer, text); + unsigned int textSize = 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); From 159e6b6f247beb7164e5e3e8c1ea61d52fc7a2f9 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Mar 2023 13:13:48 +0100 Subject: [PATCH 0350/1710] UPDATED: raylib-parser output files (TXT, XML, JSON, LUA) --- parser/output/raylib_api.json | 592 ++++++++------- parser/output/raylib_api.lua | 361 +++++---- parser/output/raylib_api.txt | 1286 +++++++++++++++++---------------- parser/output/raylib_api.xml | 184 ++--- 4 files changed, 1318 insertions(+), 1105 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index d9a548128..62d9f4401 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -6,10 +6,28 @@ "value": "", "description": "" }, + { + "name": "RAYLIB_VERSION_MAJOR", + "type": "INT", + "value": 4, + "description": "" + }, + { + "name": "RAYLIB_VERSION_MINOR", + "type": "INT", + "value": 5, + "description": "" + }, + { + "name": "RAYLIB_VERSION_PATCH", + "type": "INT", + "value": 0, + "description": "" + }, { "name": "RAYLIB_VERSION", "type": "STRING", - "value": "4.5-dev", + "value": "4.5", "description": "" }, { @@ -385,7 +403,7 @@ }, { "name": "Matrix", - "description": "Matrix, 4x4 components, column major, OpenGL style, right handed", + "description": "Matrix, 4x4 components, column major, OpenGL style, right-handed", "fields": [ { "type": "float", @@ -1045,12 +1063,12 @@ { "type": "float", "name": "distance", - "description": "Distance to nearest hit" + "description": "Distance to the nearest hit" }, { "type": "Vector3", "name": "point", - "description": "Point of nearest hit" + "description": "Point of the nearest hit" }, { "type": "Vector3", @@ -2112,7 +2130,7 @@ { "name": "MOUSE_CURSOR_RESIZE_ALL", "value": 9, - "description": "The omni-directional resize/move cursor shape" + "description": "The omnidirectional resize/move cursor shape" }, { "name": "MOUSE_CURSOR_NOT_ALLOWED", @@ -2717,7 +2735,7 @@ { "name": "CUBEMAP_LAYOUT_LINE_HORIZONTAL", "value": 2, - "description": "Layout is defined by an horizontal line with faces" + "description": "Layout is defined by a horizontal line with faces" }, { "name": "CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR", @@ -2732,7 +2750,7 @@ { "name": "CUBEMAP_LAYOUT_PANORAMA", "value": 5, - "description": "Layout is defined by a panorama image (equirectangular map)" + "description": "Layout is defined by a panorama image (equirrectangular map)" } ] }, @@ -2794,12 +2812,12 @@ { "name": "BLEND_CUSTOM", "value": 6, - "description": "Blend textures using custom src/dst factors (use rlSetBlendMode())" + "description": "Blend textures using custom src/dst factors (use rlSetBlendFactors())" }, { "name": "BLEND_CUSTOM_SEPARATE", "value": 7, - "description": "Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendModeSeparate())" + "description": "Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate())" } ] }, @@ -3149,7 +3167,7 @@ }, { "name": "SetWindowIcon", - "description": "Set icon for window (only PLATFORM_DESKTOP)", + "description": "Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP)", "returnType": "void", "params": [ { @@ -3158,6 +3176,21 @@ } ] }, + { + "name": "SetWindowIcons", + "description": "Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP)", + "returnType": "void", + "params": [ + { + "type": "Image *", + "name": "images" + }, + { + "type": "int", + "name": "count" + } + ] + }, { "name": "SetWindowTitle", "description": "Set title for window (only PLATFORM_DESKTOP)", @@ -3632,6 +3665,17 @@ } ] }, + { + "name": "IsShaderReady", + "description": "Check if a shader is ready", + "returnType": "bool", + "params": [ + { + "type": "Shader", + "name": "shader" + } + ] + }, { "name": "GetShaderLocation", "description": "Get shader uniform location", @@ -4124,7 +4168,7 @@ "returnType": "bool", "params": [ { - "type": "const char *", + "type": "const unsigned char *", "name": "data" }, { @@ -4863,12 +4907,12 @@ "returnType": "float" }, { - "name": "SetCameraMode", - "description": "Set camera mode (multiple camera modes available)", + "name": "UpdateCamera", + "description": "Update camera position for selected mode", "returnType": "void", "params": [ { - "type": "Camera", + "type": "Camera *", "name": "camera" }, { @@ -4878,77 +4922,25 @@ ] }, { - "name": "UpdateCamera", - "description": "Update camera position for selected mode", + "name": "UpdateCameraPro", + "description": "Update camera movement/rotation", "returnType": "void", "params": [ { "type": "Camera *", "name": "camera" - } - ] - }, - { - "name": "SetCameraPanControl", - "description": "Set camera pan key to combine with mouse movement (free camera)", - "returnType": "void", - "params": [ - { - "type": "int", - "name": "keyPan" - } - ] - }, - { - "name": "SetCameraAltControl", - "description": "Set camera alt key to combine with mouse movement (free camera)", - "returnType": "void", - "params": [ - { - "type": "int", - "name": "keyAlt" - } - ] - }, - { - "name": "SetCameraSmoothZoomControl", - "description": "Set camera smooth zoom key to combine with mouse (free camera)", - "returnType": "void", - "params": [ - { - "type": "int", - "name": "keySmoothZoom" - } - ] - }, - { - "name": "SetCameraMoveControls", - "description": "Set camera move controls (1st person and 3rd person cameras)", - "returnType": "void", - "params": [ - { - "type": "int", - "name": "keyFront" }, { - "type": "int", - "name": "keyBack" + "type": "Vector3", + "name": "movement" }, { - "type": "int", - "name": "keyRight" + "type": "Vector3", + "name": "rotation" }, { - "type": "int", - "name": "keyLeft" - }, - { - "type": "int", - "name": "keyUp" - }, - { - "type": "int", - "name": "keyDown" + "type": "float", + "name": "zoom" } ] }, @@ -6172,6 +6164,17 @@ "description": "Load image from screen buffer and (screenshot)", "returnType": "Image" }, + { + "name": "IsImageReady", + "description": "Check if an image is ready", + "returnType": "bool", + "params": [ + { + "type": "Image", + "name": "image" + } + ] + }, { "name": "UnloadImage", "description": "Unload image from CPU memory (RAM)", @@ -6401,6 +6404,25 @@ } ] }, + { + "name": "GenImageText", + "description": "Generate image: grayscale image from text data", + "returnType": "Image", + "params": [ + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + }, + { + "type": "const char *", + "name": "text" + } + ] + }, { "name": "ImageCopy", "description": "Create an image duplicate (useful for transformations)", @@ -6578,6 +6600,21 @@ } ] }, + { + "name": "ImageBlurGaussian", + "description": "Apply Gaussian blur using a box blur approximation", + "returnType": "void", + "params": [ + { + "type": "Image *", + "name": "image" + }, + { + "type": "int", + "name": "blurSize" + } + ] + }, { "name": "ImageResize", "description": "Resize image (Bicubic scaling algorithm)", @@ -7353,6 +7390,17 @@ } ] }, + { + "name": "IsTextureReady", + "description": "Check if a texture is ready", + "returnType": "bool", + "params": [ + { + "type": "Texture2D", + "name": "texture" + } + ] + }, { "name": "UnloadTexture", "description": "Unload texture from GPU memory (VRAM)", @@ -7364,6 +7412,17 @@ } ] }, + { + "name": "IsRenderTextureReady", + "description": "Check if a render texture is ready", + "returnType": "bool", + "params": [ + { + "type": "RenderTexture2D", + "name": "target" + } + ] + }, { "name": "UnloadRenderTexture", "description": "Unload render texture from GPU memory (VRAM)", @@ -7542,68 +7601,6 @@ } ] }, - { - "name": "DrawTextureQuad", - "description": "Draw texture quad with tiling and offset parameters", - "returnType": "void", - "params": [ - { - "type": "Texture2D", - "name": "texture" - }, - { - "type": "Vector2", - "name": "tiling" - }, - { - "type": "Vector2", - "name": "offset" - }, - { - "type": "Rectangle", - "name": "quad" - }, - { - "type": "Color", - "name": "tint" - } - ] - }, - { - "name": "DrawTextureTiled", - "description": "Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest.", - "returnType": "void", - "params": [ - { - "type": "Texture2D", - "name": "texture" - }, - { - "type": "Rectangle", - "name": "source" - }, - { - "type": "Rectangle", - "name": "dest" - }, - { - "type": "Vector2", - "name": "origin" - }, - { - "type": "float", - "name": "rotation" - }, - { - "type": "float", - "name": "scale" - }, - { - "type": "Color", - "name": "tint" - } - ] - }, { "name": "DrawTexturePro", "description": "Draw a part of a texture defined by a rectangle with 'pro' parameters", @@ -7666,37 +7663,6 @@ } ] }, - { - "name": "DrawTexturePoly", - "description": "Draw a textured polygon", - "returnType": "void", - "params": [ - { - "type": "Texture2D", - "name": "texture" - }, - { - "type": "Vector2", - "name": "center" - }, - { - "type": "Vector2 *", - "name": "points" - }, - { - "type": "Vector2 *", - "name": "texcoords" - }, - { - "type": "int", - "name": "pointCount" - }, - { - "type": "Color", - "name": "tint" - } - ] - }, { "name": "Fade", "description": "Get color with alpha applied, alpha goes from 0.0f to 1.0f", @@ -7775,6 +7741,51 @@ } ] }, + { + "name": "ColorTint", + "description": "Get color multiplied with another color", + "returnType": "Color", + "params": [ + { + "type": "Color", + "name": "color" + }, + { + "type": "Color", + "name": "tint" + } + ] + }, + { + "name": "ColorBrightness", + "description": "Get color with brightness correction, brightness factor goes from -1.0f to 1.0f", + "returnType": "Color", + "params": [ + { + "type": "Color", + "name": "color" + }, + { + "type": "float", + "name": "factor" + } + ] + }, + { + "name": "ColorContrast", + "description": "Get color with contrast correction, contrast values between -1.0f and 1.0f", + "returnType": "Color", + "params": [ + { + "type": "Color", + "name": "color" + }, + { + "type": "float", + "name": "contrast" + } + ] + }, { "name": "ColorAlpha", "description": "Get color with alpha applied, alpha goes from 0.0f to 1.0f", @@ -7962,6 +7973,17 @@ } ] }, + { + "name": "IsFontReady", + "description": "Check if a font is ready", + "returnType": "bool", + "params": [ + { + "type": "Font", + "name": "font" + } + ] + }, { "name": "LoadFontData", "description": "Load font data for further use", @@ -8869,72 +8891,6 @@ } ] }, - { - "name": "DrawCubeTexture", - "description": "Draw cube textured", - "returnType": "void", - "params": [ - { - "type": "Texture2D", - "name": "texture" - }, - { - "type": "Vector3", - "name": "position" - }, - { - "type": "float", - "name": "width" - }, - { - "type": "float", - "name": "height" - }, - { - "type": "float", - "name": "length" - }, - { - "type": "Color", - "name": "color" - } - ] - }, - { - "name": "DrawCubeTextureRec", - "description": "Draw cube with a region of a texture", - "returnType": "void", - "params": [ - { - "type": "Texture2D", - "name": "texture" - }, - { - "type": "Rectangle", - "name": "source" - }, - { - "type": "Vector3", - "name": "position" - }, - { - "type": "float", - "name": "width" - }, - { - "type": "float", - "name": "height" - }, - { - "type": "float", - "name": "length" - }, - { - "type": "Color", - "name": "color" - } - ] - }, { "name": "DrawSphere", "description": "Draw sphere", @@ -9132,6 +9088,68 @@ } ] }, + { + "name": "DrawCapsule", + "description": "Draw a capsule with the center of its sphere caps at startPos and endPos", + "returnType": "void", + "params": [ + { + "type": "Vector3", + "name": "startPos" + }, + { + "type": "Vector3", + "name": "endPos" + }, + { + "type": "float", + "name": "radius" + }, + { + "type": "int", + "name": "slices" + }, + { + "type": "int", + "name": "rings" + }, + { + "type": "Color", + "name": "color" + } + ] + }, + { + "name": "DrawCapsuleWires", + "description": "Draw capsule wireframe with the center of its sphere caps at startPos and endPos", + "returnType": "void", + "params": [ + { + "type": "Vector3", + "name": "startPos" + }, + { + "type": "Vector3", + "name": "endPos" + }, + { + "type": "float", + "name": "radius" + }, + { + "type": "int", + "name": "slices" + }, + { + "type": "int", + "name": "rings" + }, + { + "type": "Color", + "name": "color" + } + ] + }, { "name": "DrawPlane", "description": "Draw a plane XZ", @@ -9204,9 +9222,9 @@ ] }, { - "name": "UnloadModel", - "description": "Unload model (including meshes) from memory (RAM and/or VRAM)", - "returnType": "void", + "name": "IsModelReady", + "description": "Check if a model is ready", + "returnType": "bool", "params": [ { "type": "Model", @@ -9215,8 +9233,8 @@ ] }, { - "name": "UnloadModelKeepMeshes", - "description": "Unload model (but not meshes) from memory (RAM and/or VRAM)", + "name": "UnloadModel", + "description": "Unload model (including meshes) from memory (RAM and/or VRAM)", "returnType": "void", "params": [ { @@ -9821,6 +9839,17 @@ "description": "Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)", "returnType": "Material" }, + { + "name": "IsMaterialReady", + "description": "Check if a material is ready", + "returnType": "bool", + "params": [ + { + "type": "Material", + "name": "material" + } + ] + }, { "name": "UnloadMaterial", "description": "Unload material from GPU memory (VRAM)", @@ -10161,6 +10190,17 @@ } ] }, + { + "name": "IsWaveReady", + "description": "Checks if wave data is ready", + "returnType": "bool", + "params": [ + { + "type": "Wave", + "name": "wave" + } + ] + }, { "name": "LoadSound", "description": "Load sound from file", @@ -10183,6 +10223,17 @@ } ] }, + { + "name": "IsSoundReady", + "description": "Checks if a sound is ready", + "returnType": "bool", + "params": [ + { + "type": "Sound", + "name": "sound" + } + ] + }, { "name": "UpdateSound", "description": "Update sound buffer with new data", @@ -10298,27 +10349,6 @@ } ] }, - { - "name": "PlaySoundMulti", - "description": "Play a sound (using multichannel buffer pool)", - "returnType": "void", - "params": [ - { - "type": "Sound", - "name": "sound" - } - ] - }, - { - "name": "StopSoundMulti", - "description": "Stop any sound playing (using multichannel buffer pool)", - "returnType": "void" - }, - { - "name": "GetSoundsPlaying", - "description": "Get number of sounds playing in the multichannel", - "returnType": "int" - }, { "name": "IsSoundPlaying", "description": "Check if a sound is currently playing", @@ -10480,6 +10510,17 @@ } ] }, + { + "name": "IsMusicReady", + "description": "Checks if a music stream is ready", + "returnType": "bool", + "params": [ + { + "type": "Music", + "name": "music" + } + ] + }, { "name": "UnloadMusicStream", "description": "Unload music stream", @@ -10658,6 +10699,17 @@ } ] }, + { + "name": "IsAudioStreamReady", + "description": "Checks if an audio stream is ready", + "returnType": "bool", + "params": [ + { + "type": "AudioStream", + "name": "stream" + } + ] + }, { "name": "UnloadAudioStream", "description": "Unload audio stream and free memory", @@ -10854,6 +10906,28 @@ "name": "processor" } ] + }, + { + "name": "AttachAudioMixedProcessor", + "description": "Attach audio stream processor to the entire audio pipeline", + "returnType": "void", + "params": [ + { + "type": "AudioCallback", + "name": "processor" + } + ] + }, + { + "name": "DetachAudioMixedProcessor", + "description": "Detach audio stream processor from the entire audio pipeline", + "returnType": "void", + "params": [ + { + "type": "AudioCallback", + "name": "processor" + } + ] } ] } diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index a7b68f1c3..0a1b95856 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -6,10 +6,28 @@ return { value = "", description = "" }, + { + name = "RAYLIB_VERSION_MAJOR", + type = "INT", + value = 4, + description = "" + }, + { + name = "RAYLIB_VERSION_MINOR", + type = "INT", + value = 5, + description = "" + }, + { + name = "RAYLIB_VERSION_PATCH", + type = "INT", + value = 0, + description = "" + }, { name = "RAYLIB_VERSION", type = "STRING", - value = "4.5-dev", + value = "4.5", description = "" }, { @@ -385,7 +403,7 @@ return { }, { name = "Matrix", - description = "Matrix, 4x4 components, column major, OpenGL style, right handed", + description = "Matrix, 4x4 components, column major, OpenGL style, right-handed", fields = { { type = "float", @@ -1045,12 +1063,12 @@ return { { type = "float", name = "distance", - description = "Distance to nearest hit" + description = "Distance to the nearest hit" }, { type = "Vector3", name = "point", - description = "Point of nearest hit" + description = "Point of the nearest hit" }, { type = "Vector3", @@ -2112,7 +2130,7 @@ return { { name = "MOUSE_CURSOR_RESIZE_ALL", value = 9, - description = "The omni-directional resize/move cursor shape" + description = "The omnidirectional resize/move cursor shape" }, { name = "MOUSE_CURSOR_NOT_ALLOWED", @@ -2717,7 +2735,7 @@ return { { name = "CUBEMAP_LAYOUT_LINE_HORIZONTAL", value = 2, - description = "Layout is defined by an horizontal line with faces" + description = "Layout is defined by a horizontal line with faces" }, { name = "CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR", @@ -2732,7 +2750,7 @@ return { { name = "CUBEMAP_LAYOUT_PANORAMA", value = 5, - description = "Layout is defined by a panorama image (equirectangular map)" + description = "Layout is defined by a panorama image (equirrectangular map)" } } }, @@ -2794,12 +2812,12 @@ return { { name = "BLEND_CUSTOM", value = 6, - description = "Blend textures using custom src/dst factors (use rlSetBlendMode())" + description = "Blend textures using custom src/dst factors (use rlSetBlendFactors())" }, { name = "BLEND_CUSTOM_SEPARATE", value = 7, - description = "Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendModeSeparate())" + description = "Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate())" } } }, @@ -3092,12 +3110,21 @@ return { }, { name = "SetWindowIcon", - description = "Set icon for window (only PLATFORM_DESKTOP)", + description = "Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP)", returnType = "void", params = { {type = "Image", name = "image"} } }, + { + name = "SetWindowIcons", + description = "Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP)", + returnType = "void", + params = { + {type = "Image *", name = "images"}, + {type = "int", name = "count"} + } + }, { name = "SetWindowTitle", description = "Set title for window (only PLATFORM_DESKTOP)", @@ -3467,6 +3494,14 @@ return { {type = "const char *", name = "fsCode"} } }, + { + name = "IsShaderReady", + description = "Check if a shader is ready", + returnType = "bool", + params = { + {type = "Shader", name = "shader"} + } + }, { name = "GetShaderLocation", description = "Get shader uniform location", @@ -3778,7 +3813,7 @@ return { description = "Export data to code (.h), returns true on success", returnType = "bool", params = { - {type = "const char *", name = "data"}, + {type = "const unsigned char *", name = "data"}, {type = "unsigned int", name = "size"}, {type = "const char *", name = "fileName"} } @@ -4304,58 +4339,24 @@ return { description = "Get gesture pinch angle", returnType = "float" }, - { - name = "SetCameraMode", - description = "Set camera mode (multiple camera modes available)", - returnType = "void", - params = { - {type = "Camera", name = "camera"}, - {type = "int", name = "mode"} - } - }, { name = "UpdateCamera", description = "Update camera position for selected mode", returnType = "void", params = { - {type = "Camera *", name = "camera"} + {type = "Camera *", name = "camera"}, + {type = "int", name = "mode"} } }, { - name = "SetCameraPanControl", - description = "Set camera pan key to combine with mouse movement (free camera)", + name = "UpdateCameraPro", + description = "Update camera movement/rotation", returnType = "void", params = { - {type = "int", name = "keyPan"} - } - }, - { - name = "SetCameraAltControl", - description = "Set camera alt key to combine with mouse movement (free camera)", - returnType = "void", - params = { - {type = "int", name = "keyAlt"} - } - }, - { - name = "SetCameraSmoothZoomControl", - description = "Set camera smooth zoom key to combine with mouse (free camera)", - returnType = "void", - params = { - {type = "int", name = "keySmoothZoom"} - } - }, - { - name = "SetCameraMoveControls", - description = "Set camera move controls (1st person and 3rd person cameras)", - returnType = "void", - params = { - {type = "int", name = "keyFront"}, - {type = "int", name = "keyBack"}, - {type = "int", name = "keyRight"}, - {type = "int", name = "keyLeft"}, - {type = "int", name = "keyUp"}, - {type = "int", name = "keyDown"} + {type = "Camera *", name = "camera"}, + {type = "Vector3", name = "movement"}, + {type = "Vector3", name = "rotation"}, + {type = "float", name = "zoom"} } }, { @@ -4945,6 +4946,14 @@ return { description = "Load image from screen buffer and (screenshot)", returnType = "Image" }, + { + name = "IsImageReady", + description = "Check if an image is ready", + returnType = "bool", + params = { + {type = "Image", name = "image"} + } + }, { name = "UnloadImage", description = "Unload image from CPU memory (RAM)", @@ -5060,6 +5069,16 @@ return { {type = "int", name = "tileSize"} } }, + { + name = "GenImageText", + description = "Generate image: grayscale image from text data", + returnType = "Image", + params = { + {type = "int", name = "width"}, + {type = "int", name = "height"}, + {type = "const char *", name = "text"} + } + }, { name = "ImageCopy", description = "Create an image duplicate (useful for transformations)", @@ -5162,6 +5181,15 @@ return { {type = "Image *", name = "image"} } }, + { + name = "ImageBlurGaussian", + description = "Apply Gaussian blur using a box blur approximation", + returnType = "void", + params = { + {type = "Image *", name = "image"}, + {type = "int", name = "blurSize"} + } + }, { name = "ImageResize", description = "Resize image (Bicubic scaling algorithm)", @@ -5571,6 +5599,14 @@ return { {type = "int", name = "height"} } }, + { + name = "IsTextureReady", + description = "Check if a texture is ready", + returnType = "bool", + params = { + {type = "Texture2D", name = "texture"} + } + }, { name = "UnloadTexture", description = "Unload texture from GPU memory (VRAM)", @@ -5579,6 +5615,14 @@ return { {type = "Texture2D", name = "texture"} } }, + { + name = "IsRenderTextureReady", + description = "Check if a render texture is ready", + returnType = "bool", + params = { + {type = "RenderTexture2D", name = "target"} + } + }, { name = "UnloadRenderTexture", description = "Unload render texture from GPU memory (VRAM)", @@ -5676,32 +5720,6 @@ return { {type = "Color", name = "tint"} } }, - { - name = "DrawTextureQuad", - description = "Draw texture quad with tiling and offset parameters", - returnType = "void", - params = { - {type = "Texture2D", name = "texture"}, - {type = "Vector2", name = "tiling"}, - {type = "Vector2", name = "offset"}, - {type = "Rectangle", name = "quad"}, - {type = "Color", name = "tint"} - } - }, - { - name = "DrawTextureTiled", - description = "Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest.", - returnType = "void", - params = { - {type = "Texture2D", name = "texture"}, - {type = "Rectangle", name = "source"}, - {type = "Rectangle", name = "dest"}, - {type = "Vector2", name = "origin"}, - {type = "float", name = "rotation"}, - {type = "float", name = "scale"}, - {type = "Color", name = "tint"} - } - }, { name = "DrawTexturePro", description = "Draw a part of a texture defined by a rectangle with 'pro' parameters", @@ -5728,19 +5746,6 @@ return { {type = "Color", name = "tint"} } }, - { - name = "DrawTexturePoly", - description = "Draw a textured polygon", - returnType = "void", - params = { - {type = "Texture2D", name = "texture"}, - {type = "Vector2", name = "center"}, - {type = "Vector2 *", name = "points"}, - {type = "Vector2 *", name = "texcoords"}, - {type = "int", name = "pointCount"}, - {type = "Color", name = "tint"} - } - }, { name = "Fade", description = "Get color with alpha applied, alpha goes from 0.0f to 1.0f", @@ -5792,6 +5797,33 @@ return { {type = "float", name = "value"} } }, + { + name = "ColorTint", + description = "Get color multiplied with another color", + returnType = "Color", + params = { + {type = "Color", name = "color"}, + {type = "Color", name = "tint"} + } + }, + { + name = "ColorBrightness", + description = "Get color with brightness correction, brightness factor goes from -1.0f to 1.0f", + returnType = "Color", + params = { + {type = "Color", name = "color"}, + {type = "float", name = "factor"} + } + }, + { + name = "ColorContrast", + description = "Get color with contrast correction, contrast values between -1.0f and 1.0f", + returnType = "Color", + params = { + {type = "Color", name = "color"}, + {type = "float", name = "contrast"} + } + }, { name = "ColorAlpha", description = "Get color with alpha applied, alpha goes from 0.0f to 1.0f", @@ -5895,6 +5927,14 @@ return { {type = "int", name = "glyphCount"} } }, + { + name = "IsFontReady", + description = "Check if a font is ready", + returnType = "bool", + params = { + {type = "Font", name = "font"} + } + }, { name = "LoadFontData", description = "Load font data for further use", @@ -6379,33 +6419,6 @@ return { {type = "Color", name = "color"} } }, - { - name = "DrawCubeTexture", - description = "Draw cube textured", - returnType = "void", - params = { - {type = "Texture2D", name = "texture"}, - {type = "Vector3", name = "position"}, - {type = "float", name = "width"}, - {type = "float", name = "height"}, - {type = "float", name = "length"}, - {type = "Color", name = "color"} - } - }, - { - name = "DrawCubeTextureRec", - description = "Draw cube with a region of a texture", - returnType = "void", - params = { - {type = "Texture2D", name = "texture"}, - {type = "Rectangle", name = "source"}, - {type = "Vector3", name = "position"}, - {type = "float", name = "width"}, - {type = "float", name = "height"}, - {type = "float", name = "length"}, - {type = "Color", name = "color"} - } - }, { name = "DrawSphere", description = "Draw sphere", @@ -6492,6 +6505,32 @@ return { {type = "Color", name = "color"} } }, + { + name = "DrawCapsule", + description = "Draw a capsule with the center of its sphere caps at startPos and endPos", + returnType = "void", + params = { + {type = "Vector3", name = "startPos"}, + {type = "Vector3", name = "endPos"}, + {type = "float", name = "radius"}, + {type = "int", name = "slices"}, + {type = "int", name = "rings"}, + {type = "Color", name = "color"} + } + }, + { + name = "DrawCapsuleWires", + description = "Draw capsule wireframe with the center of its sphere caps at startPos and endPos", + returnType = "void", + params = { + {type = "Vector3", name = "startPos"}, + {type = "Vector3", name = "endPos"}, + {type = "float", name = "radius"}, + {type = "int", name = "slices"}, + {type = "int", name = "rings"}, + {type = "Color", name = "color"} + } + }, { name = "DrawPlane", description = "Draw a plane XZ", @@ -6537,16 +6576,16 @@ return { } }, { - name = "UnloadModel", - description = "Unload model (including meshes) from memory (RAM and/or VRAM)", - returnType = "void", + name = "IsModelReady", + description = "Check if a model is ready", + returnType = "bool", params = { {type = "Model", name = "model"} } }, { - name = "UnloadModelKeepMeshes", - description = "Unload model (but not meshes) from memory (RAM and/or VRAM)", + name = "UnloadModel", + description = "Unload model (including meshes) from memory (RAM and/or VRAM)", returnType = "void", params = { {type = "Model", name = "model"} @@ -6857,6 +6896,14 @@ return { description = "Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)", returnType = "Material" }, + { + name = "IsMaterialReady", + description = "Check if a material is ready", + returnType = "bool", + params = { + {type = "Material", name = "material"} + } + }, { name = "UnloadMaterial", description = "Unload material from GPU memory (VRAM)", @@ -7053,6 +7100,14 @@ return { {type = "int", name = "dataSize"} } }, + { + name = "IsWaveReady", + description = "Checks if wave data is ready", + returnType = "bool", + params = { + {type = "Wave", name = "wave"} + } + }, { name = "LoadSound", description = "Load sound from file", @@ -7069,6 +7124,14 @@ return { {type = "Wave", name = "wave"} } }, + { + name = "IsSoundReady", + description = "Checks if a sound is ready", + returnType = "bool", + params = { + {type = "Sound", name = "sound"} + } + }, { name = "UpdateSound", description = "Update sound buffer with new data", @@ -7145,24 +7208,6 @@ return { {type = "Sound", name = "sound"} } }, - { - name = "PlaySoundMulti", - description = "Play a sound (using multichannel buffer pool)", - returnType = "void", - params = { - {type = "Sound", name = "sound"} - } - }, - { - name = "StopSoundMulti", - description = "Stop any sound playing (using multichannel buffer pool)", - returnType = "void" - }, - { - name = "GetSoundsPlaying", - description = "Get number of sounds playing in the multichannel", - returnType = "int" - }, { name = "IsSoundPlaying", description = "Check if a sound is currently playing", @@ -7261,6 +7306,14 @@ return { {type = "int", name = "dataSize"} } }, + { + name = "IsMusicReady", + description = "Checks if a music stream is ready", + returnType = "bool", + params = { + {type = "Music", name = "music"} + } + }, { name = "UnloadMusicStream", description = "Unload music stream", @@ -7379,6 +7432,14 @@ return { {type = "unsigned int", name = "channels"} } }, + { + name = "IsAudioStreamReady", + description = "Checks if an audio stream is ready", + returnType = "bool", + params = { + {type = "AudioStream", name = "stream"} + } + }, { name = "UnloadAudioStream", description = "Unload audio stream and free memory", @@ -7506,6 +7567,22 @@ return { {type = "AudioStream", name = "stream"}, {type = "AudioCallback", name = "processor"} } + }, + { + name = "AttachAudioMixedProcessor", + description = "Attach audio stream processor to the entire audio pipeline", + returnType = "void", + params = { + {type = "AudioCallback", name = "processor"} + } + }, + { + name = "DetachAudioMixedProcessor", + description = "Detach audio stream processor from the entire audio pipeline", + returnType = "void", + params = { + {type = "AudioCallback", name = "processor"} + } } } } diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index c53e292d0..4b13bc3b4 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -1,267 +1,282 @@ -Defines found: 53 +Defines found: 56 Define 001: RAYLIB_H Name: RAYLIB_H Type: GUARD Value: Description: -Define 002: RAYLIB_VERSION +Define 002: RAYLIB_VERSION_MAJOR + Name: RAYLIB_VERSION_MAJOR + Type: INT + Value: 4 + Description: +Define 003: RAYLIB_VERSION_MINOR + Name: RAYLIB_VERSION_MINOR + Type: INT + Value: 5 + Description: +Define 004: RAYLIB_VERSION_PATCH + Name: RAYLIB_VERSION_PATCH + Type: INT + Value: 0 + Description: +Define 005: RAYLIB_VERSION Name: RAYLIB_VERSION Type: STRING - Value: "4.5-dev" + Value: "4.5" Description: -Define 003: __declspec(x) +Define 006: __declspec(x) Name: __declspec(x) Type: MACRO Value: __attribute__((x)) Description: -Define 004: RLAPI +Define 007: RLAPI Name: RLAPI Type: UNKNOWN Value: __declspec(dllexport) Description: We are building the library as a Win32 shared library (.dll) -Define 005: PI +Define 008: PI Name: PI Type: FLOAT Value: 3.14159265358979323846 Description: -Define 006: DEG2RAD +Define 009: DEG2RAD Name: DEG2RAD Type: FLOAT_MATH Value: (PI/180.0f) Description: -Define 007: RAD2DEG +Define 010: RAD2DEG Name: RAD2DEG Type: FLOAT_MATH Value: (180.0f/PI) Description: -Define 008: RL_MALLOC(sz) +Define 011: RL_MALLOC(sz) Name: RL_MALLOC(sz) Type: MACRO Value: malloc(sz) Description: -Define 009: RL_CALLOC(n,sz) +Define 012: RL_CALLOC(n,sz) Name: RL_CALLOC(n,sz) Type: MACRO Value: calloc(n,sz) Description: -Define 010: RL_REALLOC(ptr,sz) +Define 013: RL_REALLOC(ptr,sz) Name: RL_REALLOC(ptr,sz) Type: MACRO Value: realloc(ptr,sz) Description: -Define 011: RL_FREE(ptr) +Define 014: RL_FREE(ptr) Name: RL_FREE(ptr) Type: MACRO Value: free(ptr) Description: -Define 012: CLITERAL(type) +Define 015: CLITERAL(type) Name: CLITERAL(type) Type: MACRO Value: type Description: -Define 013: RL_COLOR_TYPE +Define 016: RL_COLOR_TYPE Name: RL_COLOR_TYPE Type: GUARD Value: Description: -Define 014: RL_RECTANGLE_TYPE +Define 017: RL_RECTANGLE_TYPE Name: RL_RECTANGLE_TYPE Type: GUARD Value: Description: -Define 015: RL_VECTOR2_TYPE +Define 018: RL_VECTOR2_TYPE Name: RL_VECTOR2_TYPE Type: GUARD Value: Description: -Define 016: RL_VECTOR3_TYPE +Define 019: RL_VECTOR3_TYPE Name: RL_VECTOR3_TYPE Type: GUARD Value: Description: -Define 017: RL_VECTOR4_TYPE +Define 020: RL_VECTOR4_TYPE Name: RL_VECTOR4_TYPE Type: GUARD Value: Description: -Define 018: RL_QUATERNION_TYPE +Define 021: RL_QUATERNION_TYPE Name: RL_QUATERNION_TYPE Type: GUARD Value: Description: -Define 019: RL_MATRIX_TYPE +Define 022: RL_MATRIX_TYPE Name: RL_MATRIX_TYPE Type: GUARD Value: Description: -Define 020: LIGHTGRAY +Define 023: LIGHTGRAY Name: LIGHTGRAY Type: COLOR Value: CLITERAL(Color){ 200, 200, 200, 255 } Description: Light Gray -Define 021: GRAY +Define 024: GRAY Name: GRAY Type: COLOR Value: CLITERAL(Color){ 130, 130, 130, 255 } Description: Gray -Define 022: DARKGRAY +Define 025: DARKGRAY Name: DARKGRAY Type: COLOR Value: CLITERAL(Color){ 80, 80, 80, 255 } Description: Dark Gray -Define 023: YELLOW +Define 026: YELLOW Name: YELLOW Type: COLOR Value: CLITERAL(Color){ 253, 249, 0, 255 } Description: Yellow -Define 024: GOLD +Define 027: GOLD Name: GOLD Type: COLOR Value: CLITERAL(Color){ 255, 203, 0, 255 } Description: Gold -Define 025: ORANGE +Define 028: ORANGE Name: ORANGE Type: COLOR Value: CLITERAL(Color){ 255, 161, 0, 255 } Description: Orange -Define 026: PINK +Define 029: PINK Name: PINK Type: COLOR Value: CLITERAL(Color){ 255, 109, 194, 255 } Description: Pink -Define 027: RED +Define 030: RED Name: RED Type: COLOR Value: CLITERAL(Color){ 230, 41, 55, 255 } Description: Red -Define 028: MAROON +Define 031: MAROON Name: MAROON Type: COLOR Value: CLITERAL(Color){ 190, 33, 55, 255 } Description: Maroon -Define 029: GREEN +Define 032: GREEN Name: GREEN Type: COLOR Value: CLITERAL(Color){ 0, 228, 48, 255 } Description: Green -Define 030: LIME +Define 033: LIME Name: LIME Type: COLOR Value: CLITERAL(Color){ 0, 158, 47, 255 } Description: Lime -Define 031: DARKGREEN +Define 034: DARKGREEN Name: DARKGREEN Type: COLOR Value: CLITERAL(Color){ 0, 117, 44, 255 } Description: Dark Green -Define 032: SKYBLUE +Define 035: SKYBLUE Name: SKYBLUE Type: COLOR Value: CLITERAL(Color){ 102, 191, 255, 255 } Description: Sky Blue -Define 033: BLUE +Define 036: BLUE Name: BLUE Type: COLOR Value: CLITERAL(Color){ 0, 121, 241, 255 } Description: Blue -Define 034: DARKBLUE +Define 037: DARKBLUE Name: DARKBLUE Type: COLOR Value: CLITERAL(Color){ 0, 82, 172, 255 } Description: Dark Blue -Define 035: PURPLE +Define 038: PURPLE Name: PURPLE Type: COLOR Value: CLITERAL(Color){ 200, 122, 255, 255 } Description: Purple -Define 036: VIOLET +Define 039: VIOLET Name: VIOLET Type: COLOR Value: CLITERAL(Color){ 135, 60, 190, 255 } Description: Violet -Define 037: DARKPURPLE +Define 040: DARKPURPLE Name: DARKPURPLE Type: COLOR Value: CLITERAL(Color){ 112, 31, 126, 255 } Description: Dark Purple -Define 038: BEIGE +Define 041: BEIGE Name: BEIGE Type: COLOR Value: CLITERAL(Color){ 211, 176, 131, 255 } Description: Beige -Define 039: BROWN +Define 042: BROWN Name: BROWN Type: COLOR Value: CLITERAL(Color){ 127, 106, 79, 255 } Description: Brown -Define 040: DARKBROWN +Define 043: DARKBROWN Name: DARKBROWN Type: COLOR Value: CLITERAL(Color){ 76, 63, 47, 255 } Description: Dark Brown -Define 041: WHITE +Define 044: WHITE Name: WHITE Type: COLOR Value: CLITERAL(Color){ 255, 255, 255, 255 } Description: White -Define 042: BLACK +Define 045: BLACK Name: BLACK Type: COLOR Value: CLITERAL(Color){ 0, 0, 0, 255 } Description: Black -Define 043: BLANK +Define 046: BLANK Name: BLANK Type: COLOR Value: CLITERAL(Color){ 0, 0, 0, 0 } Description: Blank (Transparent) -Define 044: MAGENTA +Define 047: MAGENTA Name: MAGENTA Type: COLOR Value: CLITERAL(Color){ 255, 0, 255, 255 } Description: Magenta -Define 045: RAYWHITE +Define 048: RAYWHITE Name: RAYWHITE Type: COLOR Value: CLITERAL(Color){ 245, 245, 245, 255 } Description: My own White (raylib logo) -Define 046: RL_BOOL_TYPE +Define 049: RL_BOOL_TYPE Name: RL_BOOL_TYPE Type: GUARD Value: Description: -Define 047: MOUSE_LEFT_BUTTON +Define 050: MOUSE_LEFT_BUTTON Name: MOUSE_LEFT_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_LEFT Description: -Define 048: MOUSE_RIGHT_BUTTON +Define 051: MOUSE_RIGHT_BUTTON Name: MOUSE_RIGHT_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_RIGHT Description: -Define 049: MOUSE_MIDDLE_BUTTON +Define 052: MOUSE_MIDDLE_BUTTON Name: MOUSE_MIDDLE_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_MIDDLE Description: -Define 050: MATERIAL_MAP_DIFFUSE +Define 053: MATERIAL_MAP_DIFFUSE Name: MATERIAL_MAP_DIFFUSE Type: UNKNOWN Value: MATERIAL_MAP_ALBEDO Description: -Define 051: MATERIAL_MAP_SPECULAR +Define 054: MATERIAL_MAP_SPECULAR Name: MATERIAL_MAP_SPECULAR Type: UNKNOWN Value: MATERIAL_MAP_METALNESS Description: -Define 052: SHADER_LOC_MAP_DIFFUSE +Define 055: SHADER_LOC_MAP_DIFFUSE Name: SHADER_LOC_MAP_DIFFUSE Type: UNKNOWN Value: SHADER_LOC_MAP_ALBEDO Description: -Define 053: SHADER_LOC_MAP_SPECULAR +Define 056: SHADER_LOC_MAP_SPECULAR Name: SHADER_LOC_MAP_SPECULAR Type: UNKNOWN Value: SHADER_LOC_MAP_METALNESS @@ -289,7 +304,7 @@ Struct 03: Vector4 (4 fields) Field[4]: float w // Vector w component Struct 04: Matrix (16 fields) Name: Matrix - Description: Matrix, 4x4 components, column major, OpenGL style, right handed + Description: Matrix, 4x4 components, column major, OpenGL style, right-handed Field[1]: float m0 // Matrix first row (4 components) Field[2]: float m4 // Matrix first row (4 components) Field[3]: float m8 // Matrix first row (4 components) @@ -457,8 +472,8 @@ Struct 24: RayCollision (4 fields) Name: RayCollision Description: RayCollision, ray hit information Field[1]: bool hit // Did the ray hit something? - Field[2]: float distance // Distance to nearest hit - Field[3]: Vector3 point // Point of nearest hit + Field[2]: float distance // Distance to the nearest hit + Field[3]: Vector3 point // Point of the nearest hit Field[4]: Vector3 normal // Surface normal of hit Struct 25: BoundingBox (2 fields) Name: BoundingBox @@ -948,7 +963,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 509 +Functions found: 517 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -1040,252 +1055,258 @@ Function 017: RestoreWindow() (0 input parameters) Function 018: SetWindowIcon() (1 input parameters) Name: SetWindowIcon Return type: void - Description: Set icon for window (only PLATFORM_DESKTOP) + Description: Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) Param[1]: image (type: Image) -Function 019: SetWindowTitle() (1 input parameters) +Function 019: SetWindowIcons() (2 input parameters) + Name: SetWindowIcons + Return type: void + Description: Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) + Param[1]: images (type: Image *) + Param[2]: count (type: int) +Function 020: SetWindowTitle() (1 input parameters) Name: SetWindowTitle Return type: void Description: Set title for window (only PLATFORM_DESKTOP) Param[1]: title (type: const char *) -Function 020: SetWindowPosition() (2 input parameters) +Function 021: SetWindowPosition() (2 input parameters) Name: SetWindowPosition Return type: void Description: Set window position on screen (only PLATFORM_DESKTOP) Param[1]: x (type: int) Param[2]: y (type: int) -Function 021: SetWindowMonitor() (1 input parameters) +Function 022: SetWindowMonitor() (1 input parameters) Name: SetWindowMonitor Return type: void Description: Set monitor for the current window (fullscreen mode) Param[1]: monitor (type: int) -Function 022: SetWindowMinSize() (2 input parameters) +Function 023: SetWindowMinSize() (2 input parameters) Name: SetWindowMinSize Return type: void Description: Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) Param[1]: width (type: int) Param[2]: height (type: int) -Function 023: SetWindowSize() (2 input parameters) +Function 024: SetWindowSize() (2 input parameters) Name: SetWindowSize Return type: void Description: Set window dimensions Param[1]: width (type: int) Param[2]: height (type: int) -Function 024: SetWindowOpacity() (1 input parameters) +Function 025: SetWindowOpacity() (1 input parameters) Name: SetWindowOpacity Return type: void Description: Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) Param[1]: opacity (type: float) -Function 025: GetWindowHandle() (0 input parameters) +Function 026: GetWindowHandle() (0 input parameters) Name: GetWindowHandle Return type: void * Description: Get native window handle No input parameters -Function 026: GetScreenWidth() (0 input parameters) +Function 027: GetScreenWidth() (0 input parameters) Name: GetScreenWidth Return type: int Description: Get current screen width No input parameters -Function 027: GetScreenHeight() (0 input parameters) +Function 028: GetScreenHeight() (0 input parameters) Name: GetScreenHeight Return type: int Description: Get current screen height No input parameters -Function 028: GetRenderWidth() (0 input parameters) +Function 029: GetRenderWidth() (0 input parameters) Name: GetRenderWidth Return type: int Description: Get current render width (it considers HiDPI) No input parameters -Function 029: GetRenderHeight() (0 input parameters) +Function 030: GetRenderHeight() (0 input parameters) Name: GetRenderHeight Return type: int Description: Get current render height (it considers HiDPI) No input parameters -Function 030: GetMonitorCount() (0 input parameters) +Function 031: GetMonitorCount() (0 input parameters) Name: GetMonitorCount Return type: int Description: Get number of connected monitors No input parameters -Function 031: GetCurrentMonitor() (0 input parameters) +Function 032: GetCurrentMonitor() (0 input parameters) Name: GetCurrentMonitor Return type: int Description: Get current connected monitor No input parameters -Function 032: GetMonitorPosition() (1 input parameters) +Function 033: GetMonitorPosition() (1 input parameters) Name: GetMonitorPosition Return type: Vector2 Description: Get specified monitor position Param[1]: monitor (type: int) -Function 033: GetMonitorWidth() (1 input parameters) +Function 034: GetMonitorWidth() (1 input parameters) Name: GetMonitorWidth Return type: int Description: Get specified monitor width (current video mode used by monitor) Param[1]: monitor (type: int) -Function 034: GetMonitorHeight() (1 input parameters) +Function 035: GetMonitorHeight() (1 input parameters) Name: GetMonitorHeight Return type: int Description: Get specified monitor height (current video mode used by monitor) Param[1]: monitor (type: int) -Function 035: GetMonitorPhysicalWidth() (1 input parameters) +Function 036: GetMonitorPhysicalWidth() (1 input parameters) Name: GetMonitorPhysicalWidth Return type: int Description: Get specified monitor physical width in millimetres Param[1]: monitor (type: int) -Function 036: GetMonitorPhysicalHeight() (1 input parameters) +Function 037: GetMonitorPhysicalHeight() (1 input parameters) Name: GetMonitorPhysicalHeight Return type: int Description: Get specified monitor physical height in millimetres Param[1]: monitor (type: int) -Function 037: GetMonitorRefreshRate() (1 input parameters) +Function 038: GetMonitorRefreshRate() (1 input parameters) Name: GetMonitorRefreshRate Return type: int Description: Get specified monitor refresh rate Param[1]: monitor (type: int) -Function 038: GetWindowPosition() (0 input parameters) +Function 039: GetWindowPosition() (0 input parameters) Name: GetWindowPosition Return type: Vector2 Description: Get window position XY on monitor No input parameters -Function 039: GetWindowScaleDPI() (0 input parameters) +Function 040: GetWindowScaleDPI() (0 input parameters) Name: GetWindowScaleDPI Return type: Vector2 Description: Get window scale DPI factor No input parameters -Function 040: GetMonitorName() (1 input parameters) +Function 041: GetMonitorName() (1 input parameters) Name: GetMonitorName Return type: const char * Description: Get the human-readable, UTF-8 encoded name of the primary monitor Param[1]: monitor (type: int) -Function 041: SetClipboardText() (1 input parameters) +Function 042: SetClipboardText() (1 input parameters) Name: SetClipboardText Return type: void Description: Set clipboard text content Param[1]: text (type: const char *) -Function 042: GetClipboardText() (0 input parameters) +Function 043: GetClipboardText() (0 input parameters) Name: GetClipboardText Return type: const char * Description: Get clipboard text content No input parameters -Function 043: EnableEventWaiting() (0 input parameters) +Function 044: EnableEventWaiting() (0 input parameters) Name: EnableEventWaiting Return type: void Description: Enable waiting for events on EndDrawing(), no automatic event polling No input parameters -Function 044: DisableEventWaiting() (0 input parameters) +Function 045: DisableEventWaiting() (0 input parameters) Name: DisableEventWaiting Return type: void Description: Disable waiting for events on EndDrawing(), automatic events polling No input parameters -Function 045: SwapScreenBuffer() (0 input parameters) +Function 046: SwapScreenBuffer() (0 input parameters) Name: SwapScreenBuffer Return type: void Description: Swap back buffer with front buffer (screen drawing) No input parameters -Function 046: PollInputEvents() (0 input parameters) +Function 047: PollInputEvents() (0 input parameters) Name: PollInputEvents Return type: void Description: Register all input events No input parameters -Function 047: WaitTime() (1 input parameters) +Function 048: WaitTime() (1 input parameters) Name: WaitTime Return type: void Description: Wait for some time (halt program execution) Param[1]: seconds (type: double) -Function 048: ShowCursor() (0 input parameters) +Function 049: ShowCursor() (0 input parameters) Name: ShowCursor Return type: void Description: Shows cursor No input parameters -Function 049: HideCursor() (0 input parameters) +Function 050: HideCursor() (0 input parameters) Name: HideCursor Return type: void Description: Hides cursor No input parameters -Function 050: IsCursorHidden() (0 input parameters) +Function 051: IsCursorHidden() (0 input parameters) Name: IsCursorHidden Return type: bool Description: Check if cursor is not visible No input parameters -Function 051: EnableCursor() (0 input parameters) +Function 052: EnableCursor() (0 input parameters) Name: EnableCursor Return type: void Description: Enables cursor (unlock cursor) No input parameters -Function 052: DisableCursor() (0 input parameters) +Function 053: DisableCursor() (0 input parameters) Name: DisableCursor Return type: void Description: Disables cursor (lock cursor) No input parameters -Function 053: IsCursorOnScreen() (0 input parameters) +Function 054: IsCursorOnScreen() (0 input parameters) Name: IsCursorOnScreen Return type: bool Description: Check if cursor is on the screen No input parameters -Function 054: ClearBackground() (1 input parameters) +Function 055: ClearBackground() (1 input parameters) Name: ClearBackground Return type: void Description: Set background color (framebuffer clear color) Param[1]: color (type: Color) -Function 055: BeginDrawing() (0 input parameters) +Function 056: BeginDrawing() (0 input parameters) Name: BeginDrawing Return type: void Description: Setup canvas (framebuffer) to start drawing No input parameters -Function 056: EndDrawing() (0 input parameters) +Function 057: EndDrawing() (0 input parameters) Name: EndDrawing Return type: void Description: End canvas drawing and swap buffers (double buffering) No input parameters -Function 057: BeginMode2D() (1 input parameters) +Function 058: BeginMode2D() (1 input parameters) Name: BeginMode2D Return type: void Description: Begin 2D mode with custom camera (2D) Param[1]: camera (type: Camera2D) -Function 058: EndMode2D() (0 input parameters) +Function 059: EndMode2D() (0 input parameters) Name: EndMode2D Return type: void Description: Ends 2D mode with custom camera No input parameters -Function 059: BeginMode3D() (1 input parameters) +Function 060: BeginMode3D() (1 input parameters) Name: BeginMode3D Return type: void Description: Begin 3D mode with custom camera (3D) Param[1]: camera (type: Camera3D) -Function 060: EndMode3D() (0 input parameters) +Function 061: EndMode3D() (0 input parameters) Name: EndMode3D Return type: void Description: Ends 3D mode and returns to default 2D orthographic mode No input parameters -Function 061: BeginTextureMode() (1 input parameters) +Function 062: BeginTextureMode() (1 input parameters) Name: BeginTextureMode Return type: void Description: Begin drawing to render texture Param[1]: target (type: RenderTexture2D) -Function 062: EndTextureMode() (0 input parameters) +Function 063: EndTextureMode() (0 input parameters) Name: EndTextureMode Return type: void Description: Ends drawing to render texture No input parameters -Function 063: BeginShaderMode() (1 input parameters) +Function 064: BeginShaderMode() (1 input parameters) Name: BeginShaderMode Return type: void Description: Begin custom shader drawing Param[1]: shader (type: Shader) -Function 064: EndShaderMode() (0 input parameters) +Function 065: EndShaderMode() (0 input parameters) Name: EndShaderMode Return type: void Description: End custom shader drawing (use default shader) No input parameters -Function 065: BeginBlendMode() (1 input parameters) +Function 066: BeginBlendMode() (1 input parameters) Name: BeginBlendMode Return type: void Description: Begin blending mode (alpha, additive, multiplied, subtract, custom) Param[1]: mode (type: int) -Function 066: EndBlendMode() (0 input parameters) +Function 067: EndBlendMode() (0 input parameters) Name: EndBlendMode Return type: void Description: End blending mode (reset to default: alpha blending) No input parameters -Function 067: BeginScissorMode() (4 input parameters) +Function 068: BeginScissorMode() (4 input parameters) Name: BeginScissorMode Return type: void Description: Begin scissor mode (define screen area for following drawing) @@ -1293,56 +1314,61 @@ Function 067: BeginScissorMode() (4 input parameters) Param[2]: y (type: int) Param[3]: width (type: int) Param[4]: height (type: int) -Function 068: EndScissorMode() (0 input parameters) +Function 069: EndScissorMode() (0 input parameters) Name: EndScissorMode Return type: void Description: End scissor mode No input parameters -Function 069: BeginVrStereoMode() (1 input parameters) +Function 070: BeginVrStereoMode() (1 input parameters) Name: BeginVrStereoMode Return type: void Description: Begin stereo rendering (requires VR simulator) Param[1]: config (type: VrStereoConfig) -Function 070: EndVrStereoMode() (0 input parameters) +Function 071: EndVrStereoMode() (0 input parameters) Name: EndVrStereoMode Return type: void Description: End stereo rendering (requires VR simulator) No input parameters -Function 071: LoadVrStereoConfig() (1 input parameters) +Function 072: LoadVrStereoConfig() (1 input parameters) Name: LoadVrStereoConfig Return type: VrStereoConfig Description: Load VR stereo config for VR simulator device parameters Param[1]: device (type: VrDeviceInfo) -Function 072: UnloadVrStereoConfig() (1 input parameters) +Function 073: UnloadVrStereoConfig() (1 input parameters) Name: UnloadVrStereoConfig Return type: void Description: Unload VR stereo config Param[1]: config (type: VrStereoConfig) -Function 073: LoadShader() (2 input parameters) +Function 074: LoadShader() (2 input parameters) Name: LoadShader Return type: Shader Description: Load shader from files and bind default locations Param[1]: vsFileName (type: const char *) Param[2]: fsFileName (type: const char *) -Function 074: LoadShaderFromMemory() (2 input parameters) +Function 075: LoadShaderFromMemory() (2 input parameters) Name: LoadShaderFromMemory Return type: Shader Description: Load shader from code strings and bind default locations Param[1]: vsCode (type: const char *) Param[2]: fsCode (type: const char *) -Function 075: GetShaderLocation() (2 input parameters) +Function 076: IsShaderReady() (1 input parameters) + Name: IsShaderReady + Return type: bool + Description: Check if a shader is ready + Param[1]: shader (type: Shader) +Function 077: GetShaderLocation() (2 input parameters) Name: GetShaderLocation Return type: int Description: Get shader uniform location Param[1]: shader (type: Shader) Param[2]: uniformName (type: const char *) -Function 076: GetShaderLocationAttrib() (2 input parameters) +Function 078: GetShaderLocationAttrib() (2 input parameters) Name: GetShaderLocationAttrib Return type: int Description: Get shader attribute location Param[1]: shader (type: Shader) Param[2]: attribName (type: const char *) -Function 077: SetShaderValue() (4 input parameters) +Function 079: SetShaderValue() (4 input parameters) Name: SetShaderValue Return type: void Description: Set shader uniform value @@ -1350,7 +1376,7 @@ Function 077: SetShaderValue() (4 input parameters) Param[2]: locIndex (type: int) Param[3]: value (type: const void *) Param[4]: uniformType (type: int) -Function 078: SetShaderValueV() (5 input parameters) +Function 080: SetShaderValueV() (5 input parameters) Name: SetShaderValueV Return type: void Description: Set shader uniform value vector @@ -1359,54 +1385,54 @@ Function 078: SetShaderValueV() (5 input parameters) Param[3]: value (type: const void *) Param[4]: uniformType (type: int) Param[5]: count (type: int) -Function 079: SetShaderValueMatrix() (3 input parameters) +Function 081: SetShaderValueMatrix() (3 input parameters) Name: SetShaderValueMatrix Return type: void Description: Set shader uniform value (matrix 4x4) Param[1]: shader (type: Shader) Param[2]: locIndex (type: int) Param[3]: mat (type: Matrix) -Function 080: SetShaderValueTexture() (3 input parameters) +Function 082: SetShaderValueTexture() (3 input parameters) Name: SetShaderValueTexture Return type: void Description: Set shader uniform value for texture (sampler2d) Param[1]: shader (type: Shader) Param[2]: locIndex (type: int) Param[3]: texture (type: Texture2D) -Function 081: UnloadShader() (1 input parameters) +Function 083: UnloadShader() (1 input parameters) Name: UnloadShader Return type: void Description: Unload shader from GPU memory (VRAM) Param[1]: shader (type: Shader) -Function 082: GetMouseRay() (2 input parameters) +Function 084: GetMouseRay() (2 input parameters) Name: GetMouseRay Return type: Ray Description: Get a ray trace from mouse position Param[1]: mousePosition (type: Vector2) Param[2]: camera (type: Camera) -Function 083: GetCameraMatrix() (1 input parameters) +Function 085: GetCameraMatrix() (1 input parameters) Name: GetCameraMatrix Return type: Matrix Description: Get camera transform matrix (view matrix) Param[1]: camera (type: Camera) -Function 084: GetCameraMatrix2D() (1 input parameters) +Function 086: GetCameraMatrix2D() (1 input parameters) Name: GetCameraMatrix2D Return type: Matrix Description: Get camera 2d transform matrix Param[1]: camera (type: Camera2D) -Function 085: GetWorldToScreen() (2 input parameters) +Function 087: GetWorldToScreen() (2 input parameters) Name: GetWorldToScreen Return type: Vector2 Description: Get the screen space position for a 3d world space position Param[1]: position (type: Vector3) Param[2]: camera (type: Camera) -Function 086: GetScreenToWorld2D() (2 input parameters) +Function 088: GetScreenToWorld2D() (2 input parameters) Name: GetScreenToWorld2D Return type: Vector2 Description: Get the world space position for a 2d camera screen space position Param[1]: position (type: Vector2) Param[2]: camera (type: Camera2D) -Function 087: GetWorldToScreenEx() (4 input parameters) +Function 089: GetWorldToScreenEx() (4 input parameters) Name: GetWorldToScreenEx Return type: Vector2 Description: Get size position for a 3d world space position @@ -1414,566 +1440,544 @@ Function 087: GetWorldToScreenEx() (4 input parameters) Param[2]: camera (type: Camera) Param[3]: width (type: int) Param[4]: height (type: int) -Function 088: GetWorldToScreen2D() (2 input parameters) +Function 090: GetWorldToScreen2D() (2 input parameters) Name: GetWorldToScreen2D Return type: Vector2 Description: Get the screen space position for a 2d camera world space position Param[1]: position (type: Vector2) Param[2]: camera (type: Camera2D) -Function 089: SetTargetFPS() (1 input parameters) +Function 091: SetTargetFPS() (1 input parameters) Name: SetTargetFPS Return type: void Description: Set target FPS (maximum) Param[1]: fps (type: int) -Function 090: GetFPS() (0 input parameters) +Function 092: GetFPS() (0 input parameters) Name: GetFPS Return type: int Description: Get current FPS No input parameters -Function 091: GetFrameTime() (0 input parameters) +Function 093: GetFrameTime() (0 input parameters) Name: GetFrameTime Return type: float Description: Get time in seconds for last frame drawn (delta time) No input parameters -Function 092: GetTime() (0 input parameters) +Function 094: GetTime() (0 input parameters) Name: GetTime Return type: double Description: Get elapsed time in seconds since InitWindow() No input parameters -Function 093: GetRandomValue() (2 input parameters) +Function 095: GetRandomValue() (2 input parameters) Name: GetRandomValue Return type: int Description: Get a random value between min and max (both included) Param[1]: min (type: int) Param[2]: max (type: int) -Function 094: SetRandomSeed() (1 input parameters) +Function 096: SetRandomSeed() (1 input parameters) Name: SetRandomSeed Return type: void Description: Set the seed for the random number generator Param[1]: seed (type: unsigned int) -Function 095: TakeScreenshot() (1 input parameters) +Function 097: TakeScreenshot() (1 input parameters) Name: TakeScreenshot Return type: void Description: Takes a screenshot of current screen (filename extension defines format) Param[1]: fileName (type: const char *) -Function 096: SetConfigFlags() (1 input parameters) +Function 098: SetConfigFlags() (1 input parameters) Name: SetConfigFlags Return type: void Description: Setup init configuration flags (view FLAGS) Param[1]: flags (type: unsigned int) -Function 097: TraceLog() (3 input parameters) +Function 099: TraceLog() (3 input parameters) Name: TraceLog Return type: void Description: Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) Param[1]: logLevel (type: int) Param[2]: text (type: const char *) Param[3]: args (type: ...) -Function 098: SetTraceLogLevel() (1 input parameters) +Function 100: SetTraceLogLevel() (1 input parameters) Name: SetTraceLogLevel Return type: void Description: Set the current threshold (minimum) log level Param[1]: logLevel (type: int) -Function 099: MemAlloc() (1 input parameters) +Function 101: MemAlloc() (1 input parameters) Name: MemAlloc Return type: void * Description: Internal memory allocator Param[1]: size (type: unsigned int) -Function 100: MemRealloc() (2 input parameters) +Function 102: MemRealloc() (2 input parameters) Name: MemRealloc Return type: void * Description: Internal memory reallocator Param[1]: ptr (type: void *) Param[2]: size (type: unsigned int) -Function 101: MemFree() (1 input parameters) +Function 103: MemFree() (1 input parameters) Name: MemFree Return type: void Description: Internal memory free Param[1]: ptr (type: void *) -Function 102: OpenURL() (1 input parameters) +Function 104: OpenURL() (1 input parameters) Name: OpenURL Return type: void Description: Open URL with default system browser (if available) Param[1]: url (type: const char *) -Function 103: SetTraceLogCallback() (1 input parameters) +Function 105: SetTraceLogCallback() (1 input parameters) Name: SetTraceLogCallback Return type: void Description: Set custom trace log Param[1]: callback (type: TraceLogCallback) -Function 104: SetLoadFileDataCallback() (1 input parameters) +Function 106: SetLoadFileDataCallback() (1 input parameters) Name: SetLoadFileDataCallback Return type: void Description: Set custom file binary data loader Param[1]: callback (type: LoadFileDataCallback) -Function 105: SetSaveFileDataCallback() (1 input parameters) +Function 107: SetSaveFileDataCallback() (1 input parameters) Name: SetSaveFileDataCallback Return type: void Description: Set custom file binary data saver Param[1]: callback (type: SaveFileDataCallback) -Function 106: SetLoadFileTextCallback() (1 input parameters) +Function 108: SetLoadFileTextCallback() (1 input parameters) Name: SetLoadFileTextCallback Return type: void Description: Set custom file text data loader Param[1]: callback (type: LoadFileTextCallback) -Function 107: SetSaveFileTextCallback() (1 input parameters) +Function 109: SetSaveFileTextCallback() (1 input parameters) Name: SetSaveFileTextCallback Return type: void Description: Set custom file text data saver Param[1]: callback (type: SaveFileTextCallback) -Function 108: LoadFileData() (2 input parameters) +Function 110: LoadFileData() (2 input parameters) Name: LoadFileData Return type: unsigned char * Description: Load file data as byte array (read) Param[1]: fileName (type: const char *) Param[2]: bytesRead (type: unsigned int *) -Function 109: UnloadFileData() (1 input parameters) +Function 111: UnloadFileData() (1 input parameters) Name: UnloadFileData Return type: void Description: Unload file data allocated by LoadFileData() Param[1]: data (type: unsigned char *) -Function 110: SaveFileData() (3 input parameters) +Function 112: SaveFileData() (3 input parameters) Name: SaveFileData Return type: bool Description: Save data to file from byte array (write), returns true on success Param[1]: fileName (type: const char *) Param[2]: data (type: void *) Param[3]: bytesToWrite (type: unsigned int) -Function 111: ExportDataAsCode() (3 input parameters) +Function 113: ExportDataAsCode() (3 input parameters) Name: ExportDataAsCode Return type: bool Description: Export data to code (.h), returns true on success - Param[1]: data (type: const char *) + Param[1]: data (type: const unsigned char *) Param[2]: size (type: unsigned int) Param[3]: fileName (type: const char *) -Function 112: LoadFileText() (1 input parameters) +Function 114: LoadFileText() (1 input parameters) Name: LoadFileText Return type: char * Description: Load text data from file (read), returns a '\0' terminated string Param[1]: fileName (type: const char *) -Function 113: UnloadFileText() (1 input parameters) +Function 115: UnloadFileText() (1 input parameters) Name: UnloadFileText Return type: void Description: Unload file text data allocated by LoadFileText() Param[1]: text (type: char *) -Function 114: SaveFileText() (2 input parameters) +Function 116: SaveFileText() (2 input parameters) Name: SaveFileText Return type: bool Description: Save text data to file (write), string must be '\0' terminated, returns true on success Param[1]: fileName (type: const char *) Param[2]: text (type: char *) -Function 115: FileExists() (1 input parameters) +Function 117: FileExists() (1 input parameters) Name: FileExists Return type: bool Description: Check if file exists Param[1]: fileName (type: const char *) -Function 116: DirectoryExists() (1 input parameters) +Function 118: DirectoryExists() (1 input parameters) Name: DirectoryExists Return type: bool Description: Check if a directory path exists Param[1]: dirPath (type: const char *) -Function 117: IsFileExtension() (2 input parameters) +Function 119: IsFileExtension() (2 input parameters) Name: IsFileExtension Return type: bool Description: Check file extension (including point: .png, .wav) Param[1]: fileName (type: const char *) Param[2]: ext (type: const char *) -Function 118: GetFileLength() (1 input parameters) +Function 120: GetFileLength() (1 input parameters) Name: GetFileLength Return type: int Description: Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) Param[1]: fileName (type: const char *) -Function 119: GetFileExtension() (1 input parameters) +Function 121: GetFileExtension() (1 input parameters) Name: GetFileExtension Return type: const char * Description: Get pointer to extension for a filename string (includes dot: '.png') Param[1]: fileName (type: const char *) -Function 120: GetFileName() (1 input parameters) +Function 122: GetFileName() (1 input parameters) Name: GetFileName Return type: const char * Description: Get pointer to filename for a path string Param[1]: filePath (type: const char *) -Function 121: GetFileNameWithoutExt() (1 input parameters) +Function 123: GetFileNameWithoutExt() (1 input parameters) Name: GetFileNameWithoutExt Return type: const char * Description: Get filename string without extension (uses static string) Param[1]: filePath (type: const char *) -Function 122: GetDirectoryPath() (1 input parameters) +Function 124: GetDirectoryPath() (1 input parameters) Name: GetDirectoryPath Return type: const char * Description: Get full path for a given fileName with path (uses static string) Param[1]: filePath (type: const char *) -Function 123: GetPrevDirectoryPath() (1 input parameters) +Function 125: GetPrevDirectoryPath() (1 input parameters) Name: GetPrevDirectoryPath Return type: const char * Description: Get previous directory path for a given path (uses static string) Param[1]: dirPath (type: const char *) -Function 124: GetWorkingDirectory() (0 input parameters) +Function 126: GetWorkingDirectory() (0 input parameters) Name: GetWorkingDirectory Return type: const char * Description: Get current working directory (uses static string) No input parameters -Function 125: GetApplicationDirectory() (0 input parameters) +Function 127: GetApplicationDirectory() (0 input parameters) Name: GetApplicationDirectory Return type: const char * Description: Get the directory if the running application (uses static string) No input parameters -Function 126: ChangeDirectory() (1 input parameters) +Function 128: ChangeDirectory() (1 input parameters) Name: ChangeDirectory Return type: bool Description: Change working directory, return true on success Param[1]: dir (type: const char *) -Function 127: IsPathFile() (1 input parameters) +Function 129: IsPathFile() (1 input parameters) Name: IsPathFile Return type: bool Description: Check if a given path is a file or a directory Param[1]: path (type: const char *) -Function 128: LoadDirectoryFiles() (1 input parameters) +Function 130: LoadDirectoryFiles() (1 input parameters) Name: LoadDirectoryFiles Return type: FilePathList Description: Load directory filepaths Param[1]: dirPath (type: const char *) -Function 129: LoadDirectoryFilesEx() (3 input parameters) +Function 131: LoadDirectoryFilesEx() (3 input parameters) Name: LoadDirectoryFilesEx Return type: FilePathList Description: Load directory filepaths with extension filtering and recursive directory scan Param[1]: basePath (type: const char *) Param[2]: filter (type: const char *) Param[3]: scanSubdirs (type: bool) -Function 130: UnloadDirectoryFiles() (1 input parameters) +Function 132: UnloadDirectoryFiles() (1 input parameters) Name: UnloadDirectoryFiles Return type: void Description: Unload filepaths Param[1]: files (type: FilePathList) -Function 131: IsFileDropped() (0 input parameters) +Function 133: IsFileDropped() (0 input parameters) Name: IsFileDropped Return type: bool Description: Check if a file has been dropped into window No input parameters -Function 132: LoadDroppedFiles() (0 input parameters) +Function 134: LoadDroppedFiles() (0 input parameters) Name: LoadDroppedFiles Return type: FilePathList Description: Load dropped filepaths No input parameters -Function 133: UnloadDroppedFiles() (1 input parameters) +Function 135: UnloadDroppedFiles() (1 input parameters) Name: UnloadDroppedFiles Return type: void Description: Unload dropped filepaths Param[1]: files (type: FilePathList) -Function 134: GetFileModTime() (1 input parameters) +Function 136: GetFileModTime() (1 input parameters) Name: GetFileModTime Return type: long Description: Get file modification time (last write time) Param[1]: fileName (type: const char *) -Function 135: CompressData() (3 input parameters) +Function 137: CompressData() (3 input parameters) Name: CompressData Return type: unsigned char * Description: Compress data (DEFLATE algorithm), memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: compDataSize (type: int *) -Function 136: DecompressData() (3 input parameters) +Function 138: DecompressData() (3 input parameters) Name: DecompressData Return type: unsigned char * Description: Decompress data (DEFLATE algorithm), memory must be MemFree() Param[1]: compData (type: const unsigned char *) Param[2]: compDataSize (type: int) Param[3]: dataSize (type: int *) -Function 137: EncodeDataBase64() (3 input parameters) +Function 139: EncodeDataBase64() (3 input parameters) Name: EncodeDataBase64 Return type: char * Description: Encode data to Base64 string, memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: outputSize (type: int *) -Function 138: DecodeDataBase64() (2 input parameters) +Function 140: DecodeDataBase64() (2 input parameters) Name: DecodeDataBase64 Return type: unsigned char * Description: Decode Base64 string data, memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: outputSize (type: int *) -Function 139: IsKeyPressed() (1 input parameters) +Function 141: IsKeyPressed() (1 input parameters) Name: IsKeyPressed Return type: bool Description: Check if a key has been pressed once Param[1]: key (type: int) -Function 140: IsKeyDown() (1 input parameters) +Function 142: IsKeyDown() (1 input parameters) Name: IsKeyDown Return type: bool Description: Check if a key is being pressed Param[1]: key (type: int) -Function 141: IsKeyReleased() (1 input parameters) +Function 143: IsKeyReleased() (1 input parameters) Name: IsKeyReleased Return type: bool Description: Check if a key has been released once Param[1]: key (type: int) -Function 142: IsKeyUp() (1 input parameters) +Function 144: IsKeyUp() (1 input parameters) Name: IsKeyUp Return type: bool Description: Check if a key is NOT being pressed Param[1]: key (type: int) -Function 143: SetExitKey() (1 input parameters) +Function 145: SetExitKey() (1 input parameters) Name: SetExitKey Return type: void Description: Set a custom key to exit program (default is ESC) Param[1]: key (type: int) -Function 144: GetKeyPressed() (0 input parameters) +Function 146: GetKeyPressed() (0 input parameters) Name: GetKeyPressed Return type: int Description: Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty No input parameters -Function 145: GetCharPressed() (0 input parameters) +Function 147: GetCharPressed() (0 input parameters) Name: GetCharPressed Return type: int Description: Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty No input parameters -Function 146: IsGamepadAvailable() (1 input parameters) +Function 148: IsGamepadAvailable() (1 input parameters) Name: IsGamepadAvailable Return type: bool Description: Check if a gamepad is available Param[1]: gamepad (type: int) -Function 147: GetGamepadName() (1 input parameters) +Function 149: GetGamepadName() (1 input parameters) Name: GetGamepadName Return type: const char * Description: Get gamepad internal name id Param[1]: gamepad (type: int) -Function 148: IsGamepadButtonPressed() (2 input parameters) +Function 150: IsGamepadButtonPressed() (2 input parameters) Name: IsGamepadButtonPressed Return type: bool Description: Check if a gamepad button has been pressed once Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 149: IsGamepadButtonDown() (2 input parameters) +Function 151: IsGamepadButtonDown() (2 input parameters) Name: IsGamepadButtonDown Return type: bool Description: Check if a gamepad button is being pressed Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 150: IsGamepadButtonReleased() (2 input parameters) +Function 152: IsGamepadButtonReleased() (2 input parameters) Name: IsGamepadButtonReleased Return type: bool Description: Check if a gamepad button has been released once Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 151: IsGamepadButtonUp() (2 input parameters) +Function 153: IsGamepadButtonUp() (2 input parameters) Name: IsGamepadButtonUp Return type: bool Description: Check if a gamepad button is NOT being pressed Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 152: GetGamepadButtonPressed() (0 input parameters) +Function 154: GetGamepadButtonPressed() (0 input parameters) Name: GetGamepadButtonPressed Return type: int Description: Get the last gamepad button pressed No input parameters -Function 153: GetGamepadAxisCount() (1 input parameters) +Function 155: GetGamepadAxisCount() (1 input parameters) Name: GetGamepadAxisCount Return type: int Description: Get gamepad axis count for a gamepad Param[1]: gamepad (type: int) -Function 154: GetGamepadAxisMovement() (2 input parameters) +Function 156: GetGamepadAxisMovement() (2 input parameters) Name: GetGamepadAxisMovement Return type: float Description: Get axis movement value for a gamepad axis Param[1]: gamepad (type: int) Param[2]: axis (type: int) -Function 155: SetGamepadMappings() (1 input parameters) +Function 157: SetGamepadMappings() (1 input parameters) Name: SetGamepadMappings Return type: int Description: Set internal gamepad mappings (SDL_GameControllerDB) Param[1]: mappings (type: const char *) -Function 156: IsMouseButtonPressed() (1 input parameters) +Function 158: IsMouseButtonPressed() (1 input parameters) Name: IsMouseButtonPressed Return type: bool Description: Check if a mouse button has been pressed once Param[1]: button (type: int) -Function 157: IsMouseButtonDown() (1 input parameters) +Function 159: IsMouseButtonDown() (1 input parameters) Name: IsMouseButtonDown Return type: bool Description: Check if a mouse button is being pressed Param[1]: button (type: int) -Function 158: IsMouseButtonReleased() (1 input parameters) +Function 160: IsMouseButtonReleased() (1 input parameters) Name: IsMouseButtonReleased Return type: bool Description: Check if a mouse button has been released once Param[1]: button (type: int) -Function 159: IsMouseButtonUp() (1 input parameters) +Function 161: IsMouseButtonUp() (1 input parameters) Name: IsMouseButtonUp Return type: bool Description: Check if a mouse button is NOT being pressed Param[1]: button (type: int) -Function 160: GetMouseX() (0 input parameters) +Function 162: GetMouseX() (0 input parameters) Name: GetMouseX Return type: int Description: Get mouse position X No input parameters -Function 161: GetMouseY() (0 input parameters) +Function 163: GetMouseY() (0 input parameters) Name: GetMouseY Return type: int Description: Get mouse position Y No input parameters -Function 162: GetMousePosition() (0 input parameters) +Function 164: GetMousePosition() (0 input parameters) Name: GetMousePosition Return type: Vector2 Description: Get mouse position XY No input parameters -Function 163: GetMouseDelta() (0 input parameters) +Function 165: GetMouseDelta() (0 input parameters) Name: GetMouseDelta Return type: Vector2 Description: Get mouse delta between frames No input parameters -Function 164: SetMousePosition() (2 input parameters) +Function 166: SetMousePosition() (2 input parameters) Name: SetMousePosition Return type: void Description: Set mouse position XY Param[1]: x (type: int) Param[2]: y (type: int) -Function 165: SetMouseOffset() (2 input parameters) +Function 167: SetMouseOffset() (2 input parameters) Name: SetMouseOffset Return type: void Description: Set mouse offset Param[1]: offsetX (type: int) Param[2]: offsetY (type: int) -Function 166: SetMouseScale() (2 input parameters) +Function 168: SetMouseScale() (2 input parameters) Name: SetMouseScale Return type: void Description: Set mouse scaling Param[1]: scaleX (type: float) Param[2]: scaleY (type: float) -Function 167: GetMouseWheelMove() (0 input parameters) +Function 169: GetMouseWheelMove() (0 input parameters) Name: GetMouseWheelMove Return type: float Description: Get mouse wheel movement for X or Y, whichever is larger No input parameters -Function 168: GetMouseWheelMoveV() (0 input parameters) +Function 170: GetMouseWheelMoveV() (0 input parameters) Name: GetMouseWheelMoveV Return type: Vector2 Description: Get mouse wheel movement for both X and Y No input parameters -Function 169: SetMouseCursor() (1 input parameters) +Function 171: SetMouseCursor() (1 input parameters) Name: SetMouseCursor Return type: void Description: Set mouse cursor Param[1]: cursor (type: int) -Function 170: GetTouchX() (0 input parameters) +Function 172: GetTouchX() (0 input parameters) Name: GetTouchX Return type: int Description: Get touch position X for touch point 0 (relative to screen size) No input parameters -Function 171: GetTouchY() (0 input parameters) +Function 173: GetTouchY() (0 input parameters) Name: GetTouchY Return type: int Description: Get touch position Y for touch point 0 (relative to screen size) No input parameters -Function 172: GetTouchPosition() (1 input parameters) +Function 174: GetTouchPosition() (1 input parameters) Name: GetTouchPosition Return type: Vector2 Description: Get touch position XY for a touch point index (relative to screen size) Param[1]: index (type: int) -Function 173: GetTouchPointId() (1 input parameters) +Function 175: GetTouchPointId() (1 input parameters) Name: GetTouchPointId Return type: int Description: Get touch point identifier for given index Param[1]: index (type: int) -Function 174: GetTouchPointCount() (0 input parameters) +Function 176: GetTouchPointCount() (0 input parameters) Name: GetTouchPointCount Return type: int Description: Get number of touch points No input parameters -Function 175: SetGesturesEnabled() (1 input parameters) +Function 177: SetGesturesEnabled() (1 input parameters) Name: SetGesturesEnabled Return type: void Description: Enable a set of gestures using flags Param[1]: flags (type: unsigned int) -Function 176: IsGestureDetected() (1 input parameters) +Function 178: IsGestureDetected() (1 input parameters) Name: IsGestureDetected Return type: bool Description: Check if a gesture have been detected Param[1]: gesture (type: int) -Function 177: GetGestureDetected() (0 input parameters) +Function 179: GetGestureDetected() (0 input parameters) Name: GetGestureDetected Return type: int Description: Get latest detected gesture No input parameters -Function 178: GetGestureHoldDuration() (0 input parameters) +Function 180: GetGestureHoldDuration() (0 input parameters) Name: GetGestureHoldDuration Return type: float Description: Get gesture hold time in milliseconds No input parameters -Function 179: GetGestureDragVector() (0 input parameters) +Function 181: GetGestureDragVector() (0 input parameters) Name: GetGestureDragVector Return type: Vector2 Description: Get gesture drag vector No input parameters -Function 180: GetGestureDragAngle() (0 input parameters) +Function 182: GetGestureDragAngle() (0 input parameters) Name: GetGestureDragAngle Return type: float Description: Get gesture drag angle No input parameters -Function 181: GetGesturePinchVector() (0 input parameters) +Function 183: GetGesturePinchVector() (0 input parameters) Name: GetGesturePinchVector Return type: Vector2 Description: Get gesture pinch delta No input parameters -Function 182: GetGesturePinchAngle() (0 input parameters) +Function 184: GetGesturePinchAngle() (0 input parameters) Name: GetGesturePinchAngle Return type: float Description: Get gesture pinch angle No input parameters -Function 183: SetCameraMode() (2 input parameters) - Name: SetCameraMode - Return type: void - Description: Set camera mode (multiple camera modes available) - Param[1]: camera (type: Camera) - Param[2]: mode (type: int) -Function 184: UpdateCamera() (1 input parameters) +Function 185: UpdateCamera() (2 input parameters) Name: UpdateCamera Return type: void Description: Update camera position for selected mode Param[1]: camera (type: Camera *) -Function 185: SetCameraPanControl() (1 input parameters) - Name: SetCameraPanControl + Param[2]: mode (type: int) +Function 186: UpdateCameraPro() (4 input parameters) + Name: UpdateCameraPro Return type: void - Description: Set camera pan key to combine with mouse movement (free camera) - Param[1]: keyPan (type: int) -Function 186: SetCameraAltControl() (1 input parameters) - Name: SetCameraAltControl - Return type: void - Description: Set camera alt key to combine with mouse movement (free camera) - Param[1]: keyAlt (type: int) -Function 187: SetCameraSmoothZoomControl() (1 input parameters) - Name: SetCameraSmoothZoomControl - Return type: void - Description: Set camera smooth zoom key to combine with mouse (free camera) - Param[1]: keySmoothZoom (type: int) -Function 188: SetCameraMoveControls() (6 input parameters) - Name: SetCameraMoveControls - Return type: void - Description: Set camera move controls (1st person and 3rd person cameras) - Param[1]: keyFront (type: int) - Param[2]: keyBack (type: int) - Param[3]: keyRight (type: int) - Param[4]: keyLeft (type: int) - Param[5]: keyUp (type: int) - Param[6]: keyDown (type: int) -Function 189: SetShapesTexture() (2 input parameters) + Description: Update camera movement/rotation + Param[1]: camera (type: Camera *) + Param[2]: movement (type: Vector3) + Param[3]: rotation (type: Vector3) + Param[4]: zoom (type: float) +Function 187: SetShapesTexture() (2 input parameters) Name: SetShapesTexture Return type: void Description: Set texture and rectangle to be used on shapes drawing Param[1]: texture (type: Texture2D) Param[2]: source (type: Rectangle) -Function 190: DrawPixel() (3 input parameters) +Function 188: DrawPixel() (3 input parameters) Name: DrawPixel Return type: void Description: Draw a pixel Param[1]: posX (type: int) Param[2]: posY (type: int) Param[3]: color (type: Color) -Function 191: DrawPixelV() (2 input parameters) +Function 189: DrawPixelV() (2 input parameters) Name: DrawPixelV Return type: void Description: Draw a pixel (Vector version) Param[1]: position (type: Vector2) Param[2]: color (type: Color) -Function 192: DrawLine() (5 input parameters) +Function 190: DrawLine() (5 input parameters) Name: DrawLine Return type: void Description: Draw a line @@ -1982,14 +1986,14 @@ Function 192: DrawLine() (5 input parameters) Param[3]: endPosX (type: int) Param[4]: endPosY (type: int) Param[5]: color (type: Color) -Function 193: DrawLineV() (3 input parameters) +Function 191: DrawLineV() (3 input parameters) Name: DrawLineV Return type: void Description: Draw a line (Vector version) Param[1]: startPos (type: Vector2) Param[2]: endPos (type: Vector2) Param[3]: color (type: Color) -Function 194: DrawLineEx() (4 input parameters) +Function 192: DrawLineEx() (4 input parameters) Name: DrawLineEx Return type: void Description: Draw a line defining thickness @@ -1997,7 +2001,7 @@ Function 194: DrawLineEx() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 195: DrawLineBezier() (4 input parameters) +Function 193: DrawLineBezier() (4 input parameters) Name: DrawLineBezier Return type: void Description: Draw a line using cubic-bezier curves in-out @@ -2005,7 +2009,7 @@ Function 195: DrawLineBezier() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 196: DrawLineBezierQuad() (5 input parameters) +Function 194: DrawLineBezierQuad() (5 input parameters) Name: DrawLineBezierQuad Return type: void Description: Draw line using quadratic bezier curves with a control point @@ -2014,7 +2018,7 @@ Function 196: DrawLineBezierQuad() (5 input parameters) Param[3]: controlPos (type: Vector2) Param[4]: thick (type: float) Param[5]: color (type: Color) -Function 197: DrawLineBezierCubic() (6 input parameters) +Function 195: DrawLineBezierCubic() (6 input parameters) Name: DrawLineBezierCubic Return type: void Description: Draw line using cubic bezier curves with 2 control points @@ -2024,14 +2028,14 @@ Function 197: DrawLineBezierCubic() (6 input parameters) Param[4]: endControlPos (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 198: DrawLineStrip() (3 input parameters) +Function 196: DrawLineStrip() (3 input parameters) Name: DrawLineStrip Return type: void Description: Draw lines sequence Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 199: DrawCircle() (4 input parameters) +Function 197: DrawCircle() (4 input parameters) Name: DrawCircle Return type: void Description: Draw a color-filled circle @@ -2039,7 +2043,7 @@ Function 199: DrawCircle() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 200: DrawCircleSector() (6 input parameters) +Function 198: DrawCircleSector() (6 input parameters) Name: DrawCircleSector Return type: void Description: Draw a piece of a circle @@ -2049,7 +2053,7 @@ Function 200: DrawCircleSector() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 201: DrawCircleSectorLines() (6 input parameters) +Function 199: DrawCircleSectorLines() (6 input parameters) Name: DrawCircleSectorLines Return type: void Description: Draw circle sector outline @@ -2059,7 +2063,7 @@ Function 201: DrawCircleSectorLines() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 202: DrawCircleGradient() (5 input parameters) +Function 200: DrawCircleGradient() (5 input parameters) Name: DrawCircleGradient Return type: void Description: Draw a gradient-filled circle @@ -2068,14 +2072,14 @@ Function 202: DrawCircleGradient() (5 input parameters) Param[3]: radius (type: float) Param[4]: color1 (type: Color) Param[5]: color2 (type: Color) -Function 203: DrawCircleV() (3 input parameters) +Function 201: DrawCircleV() (3 input parameters) Name: DrawCircleV Return type: void Description: Draw a color-filled circle (Vector version) Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 204: DrawCircleLines() (4 input parameters) +Function 202: DrawCircleLines() (4 input parameters) Name: DrawCircleLines Return type: void Description: Draw circle outline @@ -2083,7 +2087,7 @@ Function 204: DrawCircleLines() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 205: DrawEllipse() (5 input parameters) +Function 203: DrawEllipse() (5 input parameters) Name: DrawEllipse Return type: void Description: Draw ellipse @@ -2092,7 +2096,7 @@ Function 205: DrawEllipse() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 206: DrawEllipseLines() (5 input parameters) +Function 204: DrawEllipseLines() (5 input parameters) Name: DrawEllipseLines Return type: void Description: Draw ellipse outline @@ -2101,7 +2105,7 @@ Function 206: DrawEllipseLines() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 207: DrawRing() (7 input parameters) +Function 205: DrawRing() (7 input parameters) Name: DrawRing Return type: void Description: Draw ring @@ -2112,7 +2116,7 @@ Function 207: DrawRing() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 208: DrawRingLines() (7 input parameters) +Function 206: DrawRingLines() (7 input parameters) Name: DrawRingLines Return type: void Description: Draw ring outline @@ -2123,7 +2127,7 @@ Function 208: DrawRingLines() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 209: DrawRectangle() (5 input parameters) +Function 207: DrawRectangle() (5 input parameters) Name: DrawRectangle Return type: void Description: Draw a color-filled rectangle @@ -2132,20 +2136,20 @@ Function 209: DrawRectangle() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 210: DrawRectangleV() (3 input parameters) +Function 208: DrawRectangleV() (3 input parameters) Name: DrawRectangleV Return type: void Description: Draw a color-filled rectangle (Vector version) Param[1]: position (type: Vector2) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 211: DrawRectangleRec() (2 input parameters) +Function 209: DrawRectangleRec() (2 input parameters) Name: DrawRectangleRec Return type: void Description: Draw a color-filled rectangle Param[1]: rec (type: Rectangle) Param[2]: color (type: Color) -Function 212: DrawRectanglePro() (4 input parameters) +Function 210: DrawRectanglePro() (4 input parameters) Name: DrawRectanglePro Return type: void Description: Draw a color-filled rectangle with pro parameters @@ -2153,7 +2157,7 @@ Function 212: DrawRectanglePro() (4 input parameters) Param[2]: origin (type: Vector2) Param[3]: rotation (type: float) Param[4]: color (type: Color) -Function 213: DrawRectangleGradientV() (6 input parameters) +Function 211: DrawRectangleGradientV() (6 input parameters) Name: DrawRectangleGradientV Return type: void Description: Draw a vertical-gradient-filled rectangle @@ -2163,7 +2167,7 @@ Function 213: DrawRectangleGradientV() (6 input parameters) Param[4]: height (type: int) Param[5]: color1 (type: Color) Param[6]: color2 (type: Color) -Function 214: DrawRectangleGradientH() (6 input parameters) +Function 212: DrawRectangleGradientH() (6 input parameters) Name: DrawRectangleGradientH Return type: void Description: Draw a horizontal-gradient-filled rectangle @@ -2173,7 +2177,7 @@ Function 214: DrawRectangleGradientH() (6 input parameters) Param[4]: height (type: int) Param[5]: color1 (type: Color) Param[6]: color2 (type: Color) -Function 215: DrawRectangleGradientEx() (5 input parameters) +Function 213: DrawRectangleGradientEx() (5 input parameters) Name: DrawRectangleGradientEx Return type: void Description: Draw a gradient-filled rectangle with custom vertex colors @@ -2182,7 +2186,7 @@ Function 215: DrawRectangleGradientEx() (5 input parameters) Param[3]: col2 (type: Color) Param[4]: col3 (type: Color) Param[5]: col4 (type: Color) -Function 216: DrawRectangleLines() (5 input parameters) +Function 214: DrawRectangleLines() (5 input parameters) Name: DrawRectangleLines Return type: void Description: Draw rectangle outline @@ -2191,14 +2195,14 @@ Function 216: DrawRectangleLines() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 217: DrawRectangleLinesEx() (3 input parameters) +Function 215: DrawRectangleLinesEx() (3 input parameters) Name: DrawRectangleLinesEx Return type: void Description: Draw rectangle outline with extended parameters Param[1]: rec (type: Rectangle) Param[2]: lineThick (type: float) Param[3]: color (type: Color) -Function 218: DrawRectangleRounded() (4 input parameters) +Function 216: DrawRectangleRounded() (4 input parameters) Name: DrawRectangleRounded Return type: void Description: Draw rectangle with rounded edges @@ -2206,7 +2210,7 @@ Function 218: DrawRectangleRounded() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 219: DrawRectangleRoundedLines() (5 input parameters) +Function 217: DrawRectangleRoundedLines() (5 input parameters) Name: DrawRectangleRoundedLines Return type: void Description: Draw rectangle with rounded edges outline @@ -2215,7 +2219,7 @@ Function 219: DrawRectangleRoundedLines() (5 input parameters) Param[3]: segments (type: int) Param[4]: lineThick (type: float) Param[5]: color (type: Color) -Function 220: DrawTriangle() (4 input parameters) +Function 218: DrawTriangle() (4 input parameters) Name: DrawTriangle Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -2223,7 +2227,7 @@ Function 220: DrawTriangle() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 221: DrawTriangleLines() (4 input parameters) +Function 219: DrawTriangleLines() (4 input parameters) Name: DrawTriangleLines Return type: void Description: Draw triangle outline (vertex in counter-clockwise order!) @@ -2231,21 +2235,21 @@ Function 221: DrawTriangleLines() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 222: DrawTriangleFan() (3 input parameters) +Function 220: DrawTriangleFan() (3 input parameters) Name: DrawTriangleFan Return type: void Description: Draw a triangle fan defined by points (first vertex is the center) Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 223: DrawTriangleStrip() (3 input parameters) +Function 221: DrawTriangleStrip() (3 input parameters) Name: DrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 224: DrawPoly() (5 input parameters) +Function 222: DrawPoly() (5 input parameters) Name: DrawPoly Return type: void Description: Draw a regular polygon (Vector version) @@ -2254,7 +2258,7 @@ Function 224: DrawPoly() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 225: DrawPolyLines() (5 input parameters) +Function 223: DrawPolyLines() (5 input parameters) Name: DrawPolyLines Return type: void Description: Draw a polygon outline of n sides @@ -2263,7 +2267,7 @@ Function 225: DrawPolyLines() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 226: DrawPolyLinesEx() (6 input parameters) +Function 224: DrawPolyLinesEx() (6 input parameters) Name: DrawPolyLinesEx Return type: void Description: Draw a polygon outline of n sides with extended parameters @@ -2273,13 +2277,13 @@ Function 226: DrawPolyLinesEx() (6 input parameters) Param[4]: rotation (type: float) Param[5]: lineThick (type: float) Param[6]: color (type: Color) -Function 227: CheckCollisionRecs() (2 input parameters) +Function 225: CheckCollisionRecs() (2 input parameters) Name: CheckCollisionRecs Return type: bool Description: Check collision between two rectangles Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 228: CheckCollisionCircles() (4 input parameters) +Function 226: CheckCollisionCircles() (4 input parameters) Name: CheckCollisionCircles Return type: bool Description: Check collision between two circles @@ -2287,27 +2291,27 @@ Function 228: CheckCollisionCircles() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector2) Param[4]: radius2 (type: float) -Function 229: CheckCollisionCircleRec() (3 input parameters) +Function 227: CheckCollisionCircleRec() (3 input parameters) Name: CheckCollisionCircleRec Return type: bool Description: Check collision between circle and rectangle Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: rec (type: Rectangle) -Function 230: CheckCollisionPointRec() (2 input parameters) +Function 228: CheckCollisionPointRec() (2 input parameters) Name: CheckCollisionPointRec Return type: bool Description: Check if point is inside rectangle Param[1]: point (type: Vector2) Param[2]: rec (type: Rectangle) -Function 231: CheckCollisionPointCircle() (3 input parameters) +Function 229: CheckCollisionPointCircle() (3 input parameters) Name: CheckCollisionPointCircle Return type: bool Description: Check if point is inside circle Param[1]: point (type: Vector2) Param[2]: center (type: Vector2) Param[3]: radius (type: float) -Function 232: CheckCollisionPointTriangle() (4 input parameters) +Function 230: CheckCollisionPointTriangle() (4 input parameters) Name: CheckCollisionPointTriangle Return type: bool Description: Check if point is inside a triangle @@ -2315,14 +2319,14 @@ Function 232: CheckCollisionPointTriangle() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: p3 (type: Vector2) -Function 233: CheckCollisionPointPoly() (3 input parameters) +Function 231: CheckCollisionPointPoly() (3 input parameters) Name: CheckCollisionPointPoly Return type: bool Description: Check if point is within a polygon described by array of vertices Param[1]: point (type: Vector2) Param[2]: points (type: Vector2 *) Param[3]: pointCount (type: int) -Function 234: CheckCollisionLines() (5 input parameters) +Function 232: CheckCollisionLines() (5 input parameters) Name: CheckCollisionLines Return type: bool Description: Check the collision between two lines defined by two points each, returns collision point by reference @@ -2331,7 +2335,7 @@ Function 234: CheckCollisionLines() (5 input parameters) Param[3]: startPos2 (type: Vector2) Param[4]: endPos2 (type: Vector2) Param[5]: collisionPoint (type: Vector2 *) -Function 235: CheckCollisionPointLine() (4 input parameters) +Function 233: CheckCollisionPointLine() (4 input parameters) Name: CheckCollisionPointLine Return type: bool Description: Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] @@ -2339,18 +2343,18 @@ Function 235: CheckCollisionPointLine() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: threshold (type: int) -Function 236: GetCollisionRec() (2 input parameters) +Function 234: GetCollisionRec() (2 input parameters) Name: GetCollisionRec Return type: Rectangle Description: Get collision rectangle for two rectangles collision Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 237: LoadImage() (1 input parameters) +Function 235: LoadImage() (1 input parameters) Name: LoadImage Return type: Image Description: Load image from file into CPU memory (RAM) Param[1]: fileName (type: const char *) -Function 238: LoadImageRaw() (5 input parameters) +Function 236: LoadImageRaw() (5 input parameters) Name: LoadImageRaw Return type: Image Description: Load image from RAW file data @@ -2359,54 +2363,59 @@ Function 238: LoadImageRaw() (5 input parameters) Param[3]: height (type: int) Param[4]: format (type: int) Param[5]: headerSize (type: int) -Function 239: LoadImageAnim() (2 input parameters) +Function 237: LoadImageAnim() (2 input parameters) Name: LoadImageAnim Return type: Image Description: Load image sequence from file (frames appended to image.data) Param[1]: fileName (type: const char *) Param[2]: frames (type: int *) -Function 240: LoadImageFromMemory() (3 input parameters) +Function 238: LoadImageFromMemory() (3 input parameters) Name: LoadImageFromMemory Return type: Image Description: Load image from memory buffer, fileType refers to extension: i.e. '.png' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 241: LoadImageFromTexture() (1 input parameters) +Function 239: LoadImageFromTexture() (1 input parameters) Name: LoadImageFromTexture Return type: Image Description: Load image from GPU texture data Param[1]: texture (type: Texture2D) -Function 242: LoadImageFromScreen() (0 input parameters) +Function 240: LoadImageFromScreen() (0 input parameters) Name: LoadImageFromScreen Return type: Image Description: Load image from screen buffer and (screenshot) No input parameters -Function 243: UnloadImage() (1 input parameters) +Function 241: IsImageReady() (1 input parameters) + Name: IsImageReady + Return type: bool + Description: Check if an image is ready + Param[1]: image (type: Image) +Function 242: UnloadImage() (1 input parameters) Name: UnloadImage Return type: void Description: Unload image from CPU memory (RAM) Param[1]: image (type: Image) -Function 244: ExportImage() (2 input parameters) +Function 243: ExportImage() (2 input parameters) Name: ExportImage Return type: bool Description: Export image data to file, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 245: ExportImageAsCode() (2 input parameters) +Function 244: ExportImageAsCode() (2 input parameters) Name: ExportImageAsCode Return type: bool Description: Export image as code file defining an array of bytes, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 246: GenImageColor() (3 input parameters) +Function 245: GenImageColor() (3 input parameters) Name: GenImageColor Return type: Image Description: Generate image: plain color Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 247: GenImageGradientV() (4 input parameters) +Function 246: GenImageGradientV() (4 input parameters) Name: GenImageGradientV Return type: Image Description: Generate image: vertical gradient @@ -2414,7 +2423,7 @@ Function 247: GenImageGradientV() (4 input parameters) Param[2]: height (type: int) Param[3]: top (type: Color) Param[4]: bottom (type: Color) -Function 248: GenImageGradientH() (4 input parameters) +Function 247: GenImageGradientH() (4 input parameters) Name: GenImageGradientH Return type: Image Description: Generate image: horizontal gradient @@ -2422,7 +2431,7 @@ Function 248: GenImageGradientH() (4 input parameters) Param[2]: height (type: int) Param[3]: left (type: Color) Param[4]: right (type: Color) -Function 249: GenImageGradientRadial() (5 input parameters) +Function 248: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2431,7 +2440,7 @@ Function 249: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 250: GenImageChecked() (6 input parameters) +Function 249: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2441,14 +2450,14 @@ Function 250: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 251: GenImageWhiteNoise() (3 input parameters) +Function 250: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 252: GenImagePerlinNoise() (5 input parameters) +Function 251: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2457,13 +2466,20 @@ Function 252: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 253: GenImageCellular() (3 input parameters) +Function 252: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) +Function 253: GenImageText() (3 input parameters) + Name: GenImageText + Return type: Image + Description: Generate image: grayscale image from text data + Param[1]: width (type: int) + Param[2]: height (type: int) + Param[3]: text (type: const char *) Function 254: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image @@ -2533,21 +2549,27 @@ Function 264: ImageAlphaPremultiply() (1 input parameters) Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 265: ImageResize() (3 input parameters) +Function 265: ImageBlurGaussian() (2 input parameters) + Name: ImageBlurGaussian + Return type: void + Description: Apply Gaussian blur using a box blur approximation + Param[1]: image (type: Image *) + Param[2]: blurSize (type: int) +Function 266: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 266: ImageResizeNN() (3 input parameters) +Function 267: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 267: ImageResizeCanvas() (6 input parameters) +Function 268: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2557,12 +2579,12 @@ Function 267: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 268: ImageMipmaps() (1 input parameters) +Function 269: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 269: ImageDither() (5 input parameters) +Function 270: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2571,103 +2593,103 @@ Function 269: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 270: ImageFlipVertical() (1 input parameters) +Function 271: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 271: ImageFlipHorizontal() (1 input parameters) +Function 272: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 272: ImageRotateCW() (1 input parameters) +Function 273: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 273: ImageRotateCCW() (1 input parameters) +Function 274: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 274: ImageColorTint() (2 input parameters) +Function 275: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 275: ImageColorInvert() (1 input parameters) +Function 276: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 276: ImageColorGrayscale() (1 input parameters) +Function 277: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 277: ImageColorContrast() (2 input parameters) +Function 278: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 278: ImageColorBrightness() (2 input parameters) +Function 279: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 279: ImageColorReplace() (3 input parameters) +Function 280: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 280: LoadImageColors() (1 input parameters) +Function 281: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 281: LoadImagePalette() (3 input parameters) +Function 282: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 282: UnloadImageColors() (1 input parameters) +Function 283: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 283: UnloadImagePalette() (1 input parameters) +Function 284: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 284: GetImageAlphaBorder() (2 input parameters) +Function 285: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 285: GetImageColor() (3 input parameters) +Function 286: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 286: ImageClearBackground() (2 input parameters) +Function 287: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 287: ImageDrawPixel() (4 input parameters) +Function 288: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2675,14 +2697,14 @@ Function 287: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 288: ImageDrawPixelV() (3 input parameters) +Function 289: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 289: ImageDrawLine() (6 input parameters) +Function 290: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2692,7 +2714,7 @@ Function 289: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 290: ImageDrawLineV() (4 input parameters) +Function 291: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2700,7 +2722,7 @@ Function 290: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 291: ImageDrawCircle() (5 input parameters) +Function 292: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2709,7 +2731,7 @@ Function 291: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 292: ImageDrawCircleV() (4 input parameters) +Function 293: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2717,7 +2739,7 @@ Function 292: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 293: ImageDrawCircleLines() (5 input parameters) +Function 294: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2726,7 +2748,7 @@ Function 293: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 294: ImageDrawCircleLinesV() (4 input parameters) +Function 295: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2734,7 +2756,7 @@ Function 294: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 295: ImageDrawRectangle() (6 input parameters) +Function 296: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2744,7 +2766,7 @@ Function 295: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 296: ImageDrawRectangleV() (4 input parameters) +Function 297: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2752,14 +2774,14 @@ Function 296: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 297: ImageDrawRectangleRec() (3 input parameters) +Function 298: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 298: ImageDrawRectangleLines() (4 input parameters) +Function 299: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2767,7 +2789,7 @@ Function 298: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 299: ImageDraw() (5 input parameters) +Function 300: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2776,7 +2798,7 @@ Function 299: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 300: ImageDrawText() (6 input parameters) +Function 301: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2786,7 +2808,7 @@ Function 300: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 301: ImageDrawTextEx() (7 input parameters) +Function 302: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2797,69 +2819,79 @@ Function 301: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 302: LoadTexture() (1 input parameters) +Function 303: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 303: LoadTextureFromImage() (1 input parameters) +Function 304: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 304: LoadTextureCubemap() (2 input parameters) +Function 305: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 305: LoadRenderTexture() (2 input parameters) +Function 306: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 306: UnloadTexture() (1 input parameters) +Function 307: IsTextureReady() (1 input parameters) + Name: IsTextureReady + Return type: bool + Description: Check if a texture is ready + Param[1]: texture (type: Texture2D) +Function 308: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 307: UnloadRenderTexture() (1 input parameters) +Function 309: IsRenderTextureReady() (1 input parameters) + Name: IsRenderTextureReady + Return type: bool + Description: Check if a render texture is ready + Param[1]: target (type: RenderTexture2D) +Function 310: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 308: UpdateTexture() (2 input parameters) +Function 311: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 309: UpdateTextureRec() (3 input parameters) +Function 312: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 310: GenTextureMipmaps() (1 input parameters) +Function 313: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 311: SetTextureFilter() (2 input parameters) +Function 314: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 312: SetTextureWrap() (2 input parameters) +Function 315: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 313: DrawTexture() (4 input parameters) +Function 316: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2867,14 +2899,14 @@ Function 313: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 314: DrawTextureV() (3 input parameters) +Function 317: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 315: DrawTextureEx() (5 input parameters) +Function 318: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2883,7 +2915,7 @@ Function 315: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 316: DrawTextureRec() (4 input parameters) +Function 319: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2891,27 +2923,7 @@ Function 316: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 317: DrawTextureQuad() (5 input parameters) - Name: DrawTextureQuad - Return type: void - Description: Draw texture quad with tiling and offset parameters - Param[1]: texture (type: Texture2D) - Param[2]: tiling (type: Vector2) - Param[3]: offset (type: Vector2) - Param[4]: quad (type: Rectangle) - Param[5]: tint (type: Color) -Function 318: DrawTextureTiled() (7 input parameters) - Name: DrawTextureTiled - Return type: void - Description: Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. - Param[1]: texture (type: Texture2D) - Param[2]: source (type: Rectangle) - Param[3]: dest (type: Rectangle) - Param[4]: origin (type: Vector2) - Param[5]: rotation (type: float) - Param[6]: scale (type: float) - Param[7]: tint (type: Color) -Function 319: DrawTexturePro() (6 input parameters) +Function 320: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2921,7 +2933,7 @@ Function 319: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 320: DrawTextureNPatch() (6 input parameters) +Function 321: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2931,16 +2943,6 @@ Function 320: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 321: DrawTexturePoly() (6 input parameters) - Name: DrawTexturePoly - Return type: void - Description: Draw a textured polygon - Param[1]: texture (type: Texture2D) - Param[2]: center (type: Vector2) - Param[3]: points (type: Vector2 *) - Param[4]: texcoords (type: Vector2 *) - Param[5]: pointCount (type: int) - Param[6]: tint (type: Color) Function 322: Fade() (2 input parameters) Name: Fade Return type: Color @@ -2974,55 +2976,73 @@ Function 327: ColorFromHSV() (3 input parameters) Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 328: ColorAlpha() (2 input parameters) +Function 328: ColorTint() (2 input parameters) + Name: ColorTint + Return type: Color + Description: Get color multiplied with another color + Param[1]: color (type: Color) + Param[2]: tint (type: Color) +Function 329: ColorBrightness() (2 input parameters) + Name: ColorBrightness + Return type: Color + Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f + Param[1]: color (type: Color) + Param[2]: factor (type: float) +Function 330: ColorContrast() (2 input parameters) + Name: ColorContrast + Return type: Color + Description: Get color with contrast correction, contrast values between -1.0f and 1.0f + Param[1]: color (type: Color) + Param[2]: contrast (type: float) +Function 331: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 329: ColorAlphaBlend() (3 input parameters) +Function 332: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 330: GetColor() (1 input parameters) +Function 333: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 331: GetPixelColor() (2 input parameters) +Function 334: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 332: SetPixelColor() (3 input parameters) +Function 335: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 333: GetPixelDataSize() (3 input parameters) +Function 336: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 334: GetFontDefault() (0 input parameters) +Function 337: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 335: LoadFont() (1 input parameters) +Function 338: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 336: LoadFontEx() (4 input parameters) +Function 339: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set @@ -3030,14 +3050,14 @@ Function 336: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: fontChars (type: int *) Param[4]: glyphCount (type: int) -Function 337: LoadFontFromImage() (3 input parameters) +Function 340: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 338: LoadFontFromMemory() (6 input parameters) +Function 341: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3047,7 +3067,12 @@ Function 338: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: fontChars (type: int *) Param[6]: glyphCount (type: int) -Function 339: LoadFontData() (6 input parameters) +Function 342: IsFontReady() (1 input parameters) + Name: IsFontReady + Return type: bool + Description: Check if a font is ready + Param[1]: font (type: Font) +Function 343: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3057,7 +3082,7 @@ Function 339: LoadFontData() (6 input parameters) Param[4]: fontChars (type: int *) Param[5]: glyphCount (type: int) Param[6]: type (type: int) -Function 340: GenImageFontAtlas() (6 input parameters) +Function 344: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3067,30 +3092,30 @@ Function 340: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 341: UnloadFontData() (2 input parameters) +Function 345: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: chars (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 342: UnloadFont() (1 input parameters) +Function 346: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 343: ExportFontAsCode() (2 input parameters) +Function 347: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 344: DrawFPS() (2 input parameters) +Function 348: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 345: DrawText() (5 input parameters) +Function 349: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3099,7 +3124,7 @@ Function 345: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 346: DrawTextEx() (6 input parameters) +Function 350: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3109,7 +3134,7 @@ Function 346: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 347: DrawTextPro() (8 input parameters) +Function 351: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3121,7 +3146,7 @@ Function 347: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 348: DrawTextCodepoint() (5 input parameters) +Function 352: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3130,7 +3155,7 @@ Function 348: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 349: DrawTextCodepoints() (7 input parameters) +Function 353: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3141,13 +3166,13 @@ Function 349: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 350: MeasureText() (2 input parameters) +Function 354: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 351: MeasureTextEx() (4 input parameters) +Function 355: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3155,180 +3180,180 @@ Function 351: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 352: GetGlyphIndex() (2 input parameters) +Function 356: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 353: GetGlyphInfo() (2 input parameters) +Function 357: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 354: GetGlyphAtlasRec() (2 input parameters) +Function 358: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 355: LoadUTF8() (2 input parameters) +Function 359: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 356: UnloadUTF8() (1 input parameters) +Function 360: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 357: LoadCodepoints() (2 input parameters) +Function 361: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 358: UnloadCodepoints() (1 input parameters) +Function 362: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 359: GetCodepointCount() (1 input parameters) +Function 363: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 360: GetCodepoint() (2 input parameters) +Function 364: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 361: GetCodepointNext() (2 input parameters) +Function 365: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 362: GetCodepointPrevious() (2 input parameters) +Function 366: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 363: CodepointToUTF8() (2 input parameters) +Function 367: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 364: TextCopy() (2 input parameters) +Function 368: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 365: TextIsEqual() (2 input parameters) +Function 369: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 366: TextLength() (1 input parameters) +Function 370: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 367: TextFormat() (2 input parameters) +Function 371: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 368: TextSubtext() (3 input parameters) +Function 372: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 369: TextReplace() (3 input parameters) +Function 373: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 370: TextInsert() (3 input parameters) +Function 374: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 371: TextJoin() (3 input parameters) +Function 375: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 372: TextSplit() (3 input parameters) +Function 376: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 373: TextAppend() (3 input parameters) +Function 377: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 374: TextFindIndex() (2 input parameters) +Function 378: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 375: TextToUpper() (1 input parameters) +Function 379: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 376: TextToLower() (1 input parameters) +Function 380: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 377: TextToPascal() (1 input parameters) +Function 381: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 378: TextToInteger() (1 input parameters) +Function 382: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 379: DrawLine3D() (3 input parameters) +Function 383: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 380: DrawPoint3D() (2 input parameters) +Function 384: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 381: DrawCircle3D() (5 input parameters) +Function 385: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3337,7 +3362,7 @@ Function 381: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 382: DrawTriangle3D() (4 input parameters) +Function 386: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3345,14 +3370,14 @@ Function 382: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 383: DrawTriangleStrip3D() (3 input parameters) +Function 387: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 384: DrawCube() (5 input parameters) +Function 388: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3361,14 +3386,14 @@ Function 384: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 385: DrawCubeV() (3 input parameters) +Function 389: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 386: DrawCubeWires() (5 input parameters) +Function 390: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3377,42 +3402,21 @@ Function 386: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 387: DrawCubeWiresV() (3 input parameters) +Function 391: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 388: DrawCubeTexture() (6 input parameters) - Name: DrawCubeTexture - Return type: void - Description: Draw cube textured - Param[1]: texture (type: Texture2D) - Param[2]: position (type: Vector3) - Param[3]: width (type: float) - Param[4]: height (type: float) - Param[5]: length (type: float) - Param[6]: color (type: Color) -Function 389: DrawCubeTextureRec() (7 input parameters) - Name: DrawCubeTextureRec - Return type: void - Description: Draw cube with a region of a texture - Param[1]: texture (type: Texture2D) - Param[2]: source (type: Rectangle) - Param[3]: position (type: Vector3) - Param[4]: width (type: float) - Param[5]: height (type: float) - Param[6]: length (type: float) - Param[7]: color (type: Color) -Function 390: DrawSphere() (3 input parameters) +Function 392: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 391: DrawSphereEx() (5 input parameters) +Function 393: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3421,7 +3425,7 @@ Function 391: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 392: DrawSphereWires() (5 input parameters) +Function 394: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3430,7 +3434,7 @@ Function 392: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 393: DrawCylinder() (6 input parameters) +Function 395: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3440,7 +3444,7 @@ Function 393: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 394: DrawCylinderEx() (6 input parameters) +Function 396: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3450,7 +3454,7 @@ Function 394: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 395: DrawCylinderWires() (6 input parameters) +Function 397: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3460,7 +3464,7 @@ Function 395: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 396: DrawCylinderWiresEx() (6 input parameters) +Function 398: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3470,51 +3474,71 @@ Function 396: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 397: DrawPlane() (3 input parameters) +Function 399: DrawCapsule() (6 input parameters) + Name: DrawCapsule + Return type: void + Description: Draw a capsule with the center of its sphere caps at startPos and endPos + Param[1]: startPos (type: Vector3) + Param[2]: endPos (type: Vector3) + Param[3]: radius (type: float) + Param[4]: slices (type: int) + Param[5]: rings (type: int) + Param[6]: color (type: Color) +Function 400: DrawCapsuleWires() (6 input parameters) + Name: DrawCapsuleWires + Return type: void + Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos + Param[1]: startPos (type: Vector3) + Param[2]: endPos (type: Vector3) + Param[3]: radius (type: float) + Param[4]: slices (type: int) + Param[5]: rings (type: int) + Param[6]: color (type: Color) +Function 401: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 398: DrawRay() (2 input parameters) +Function 402: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 399: DrawGrid() (2 input parameters) +Function 403: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 400: LoadModel() (1 input parameters) +Function 404: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 401: LoadModelFromMesh() (1 input parameters) +Function 405: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 402: UnloadModel() (1 input parameters) +Function 406: IsModelReady() (1 input parameters) + Name: IsModelReady + Return type: bool + Description: Check if a model is ready + Param[1]: model (type: Model) +Function 407: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 403: UnloadModelKeepMeshes() (1 input parameters) - Name: UnloadModelKeepMeshes - Return type: void - Description: Unload model (but not meshes) from memory (RAM and/or VRAM) - Param[1]: model (type: Model) -Function 404: GetModelBoundingBox() (1 input parameters) +Function 408: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 405: DrawModel() (4 input parameters) +Function 409: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3522,7 +3546,7 @@ Function 405: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 406: DrawModelEx() (6 input parameters) +Function 410: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3532,7 +3556,7 @@ Function 406: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 407: DrawModelWires() (4 input parameters) +Function 411: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3540,7 +3564,7 @@ Function 407: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 408: DrawModelWiresEx() (6 input parameters) +Function 412: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3550,13 +3574,13 @@ Function 408: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 409: DrawBoundingBox() (2 input parameters) +Function 413: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 410: DrawBillboard() (5 input parameters) +Function 414: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3565,7 +3589,7 @@ Function 410: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 411: DrawBillboardRec() (6 input parameters) +Function 415: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3575,7 +3599,7 @@ Function 411: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 412: DrawBillboardPro() (9 input parameters) +Function 416: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3588,13 +3612,13 @@ Function 412: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 413: UploadMesh() (2 input parameters) +Function 417: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 414: UpdateMeshBuffer() (5 input parameters) +Function 418: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3603,19 +3627,19 @@ Function 414: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 415: UnloadMesh() (1 input parameters) +Function 419: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 416: DrawMesh() (3 input parameters) +Function 420: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 417: DrawMeshInstanced() (4 input parameters) +Function 421: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3623,29 +3647,29 @@ Function 417: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 418: ExportMesh() (2 input parameters) +Function 422: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 419: GetMeshBoundingBox() (1 input parameters) +Function 423: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 420: GenMeshTangents() (1 input parameters) +Function 424: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 421: GenMeshPoly() (2 input parameters) +Function 425: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 422: GenMeshPlane() (4 input parameters) +Function 426: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3653,42 +3677,42 @@ Function 422: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 423: GenMeshCube() (3 input parameters) +Function 427: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 424: GenMeshSphere() (3 input parameters) +Function 428: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 425: GenMeshHemiSphere() (3 input parameters) +Function 429: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 426: GenMeshCylinder() (3 input parameters) +Function 430: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 427: GenMeshCone() (3 input parameters) +Function 431: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 428: GenMeshTorus() (4 input parameters) +Function 432: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3696,7 +3720,7 @@ Function 428: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 429: GenMeshKnot() (4 input parameters) +Function 433: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3704,79 +3728,84 @@ Function 429: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 430: GenMeshHeightmap() (2 input parameters) +Function 434: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 431: GenMeshCubicmap() (2 input parameters) +Function 435: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 432: LoadMaterials() (2 input parameters) +Function 436: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 433: LoadMaterialDefault() (0 input parameters) +Function 437: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 434: UnloadMaterial() (1 input parameters) +Function 438: IsMaterialReady() (1 input parameters) + Name: IsMaterialReady + Return type: bool + Description: Check if a material is ready + Param[1]: material (type: Material) +Function 439: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 435: SetMaterialTexture() (3 input parameters) +Function 440: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 436: SetModelMeshMaterial() (3 input parameters) +Function 441: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 437: LoadModelAnimations() (2 input parameters) +Function 442: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: unsigned int *) -Function 438: UpdateModelAnimation() (3 input parameters) +Function 443: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 439: UnloadModelAnimation() (1 input parameters) +Function 444: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 440: UnloadModelAnimations() (2 input parameters) +Function 445: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: count (type: unsigned int) -Function 441: IsModelAnimationValid() (2 input parameters) +Function 446: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 442: CheckCollisionSpheres() (4 input parameters) +Function 447: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3784,40 +3813,40 @@ Function 442: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 443: CheckCollisionBoxes() (2 input parameters) +Function 448: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 444: CheckCollisionBoxSphere() (3 input parameters) +Function 449: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 445: GetRayCollisionSphere() (3 input parameters) +Function 450: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 446: GetRayCollisionBox() (2 input parameters) +Function 451: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 447: GetRayCollisionMesh() (3 input parameters) +Function 452: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 448: GetRayCollisionTriangle() (4 input parameters) +Function 453: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3825,7 +3854,7 @@ Function 448: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 449: GetRayCollisionQuad() (5 input parameters) +Function 454: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3834,148 +3863,143 @@ Function 449: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 450: InitAudioDevice() (0 input parameters) +Function 455: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 451: CloseAudioDevice() (0 input parameters) +Function 456: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 452: IsAudioDeviceReady() (0 input parameters) +Function 457: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 453: SetMasterVolume() (1 input parameters) +Function 458: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 454: LoadWave() (1 input parameters) +Function 459: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 455: LoadWaveFromMemory() (3 input parameters) +Function 460: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 456: LoadSound() (1 input parameters) +Function 461: IsWaveReady() (1 input parameters) + Name: IsWaveReady + Return type: bool + Description: Checks if wave data is ready + Param[1]: wave (type: Wave) +Function 462: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 457: LoadSoundFromWave() (1 input parameters) +Function 463: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 458: UpdateSound() (3 input parameters) +Function 464: IsSoundReady() (1 input parameters) + Name: IsSoundReady + Return type: bool + Description: Checks if a sound is ready + Param[1]: sound (type: Sound) +Function 465: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 459: UnloadWave() (1 input parameters) +Function 466: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 460: UnloadSound() (1 input parameters) +Function 467: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 461: ExportWave() (2 input parameters) +Function 468: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 462: ExportWaveAsCode() (2 input parameters) +Function 469: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 463: PlaySound() (1 input parameters) +Function 470: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 464: StopSound() (1 input parameters) +Function 471: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 465: PauseSound() (1 input parameters) +Function 472: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 466: ResumeSound() (1 input parameters) +Function 473: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 467: PlaySoundMulti() (1 input parameters) - Name: PlaySoundMulti - Return type: void - Description: Play a sound (using multichannel buffer pool) - Param[1]: sound (type: Sound) -Function 468: StopSoundMulti() (0 input parameters) - Name: StopSoundMulti - Return type: void - Description: Stop any sound playing (using multichannel buffer pool) - No input parameters -Function 469: GetSoundsPlaying() (0 input parameters) - Name: GetSoundsPlaying - Return type: int - Description: Get number of sounds playing in the multichannel - No input parameters -Function 470: IsSoundPlaying() (1 input parameters) +Function 474: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 471: SetSoundVolume() (2 input parameters) +Function 475: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 472: SetSoundPitch() (2 input parameters) +Function 476: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 473: SetSoundPan() (2 input parameters) +Function 477: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 474: WaveCopy() (1 input parameters) +Function 478: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 475: WaveCrop() (3 input parameters) +Function 479: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 476: WaveFormat() (4 input parameters) +Function 480: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -3983,184 +4007,204 @@ Function 476: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 477: LoadWaveSamples() (1 input parameters) +Function 481: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 478: UnloadWaveSamples() (1 input parameters) +Function 482: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 479: LoadMusicStream() (1 input parameters) +Function 483: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 480: LoadMusicStreamFromMemory() (3 input parameters) +Function 484: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 481: UnloadMusicStream() (1 input parameters) +Function 485: IsMusicReady() (1 input parameters) + Name: IsMusicReady + Return type: bool + Description: Checks if a music stream is ready + Param[1]: music (type: Music) +Function 486: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 482: PlayMusicStream() (1 input parameters) +Function 487: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 483: IsMusicStreamPlaying() (1 input parameters) +Function 488: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 484: UpdateMusicStream() (1 input parameters) +Function 489: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 485: StopMusicStream() (1 input parameters) +Function 490: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 486: PauseMusicStream() (1 input parameters) +Function 491: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 487: ResumeMusicStream() (1 input parameters) +Function 492: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 488: SeekMusicStream() (2 input parameters) +Function 493: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 489: SetMusicVolume() (2 input parameters) +Function 494: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 490: SetMusicPitch() (2 input parameters) +Function 495: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 491: SetMusicPan() (2 input parameters) +Function 496: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 492: GetMusicTimeLength() (1 input parameters) +Function 497: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 493: GetMusicTimePlayed() (1 input parameters) +Function 498: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 494: LoadAudioStream() (3 input parameters) +Function 499: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 495: UnloadAudioStream() (1 input parameters) +Function 500: IsAudioStreamReady() (1 input parameters) + Name: IsAudioStreamReady + Return type: bool + Description: Checks if an audio stream is ready + Param[1]: stream (type: AudioStream) +Function 501: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 496: UpdateAudioStream() (3 input parameters) +Function 502: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 497: IsAudioStreamProcessed() (1 input parameters) +Function 503: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 498: PlayAudioStream() (1 input parameters) +Function 504: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 499: PauseAudioStream() (1 input parameters) +Function 505: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 500: ResumeAudioStream() (1 input parameters) +Function 506: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 501: IsAudioStreamPlaying() (1 input parameters) +Function 507: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 502: StopAudioStream() (1 input parameters) +Function 508: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 503: SetAudioStreamVolume() (2 input parameters) +Function 509: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 504: SetAudioStreamPitch() (2 input parameters) +Function 510: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 505: SetAudioStreamPan() (2 input parameters) +Function 511: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 506: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 512: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 507: SetAudioStreamCallback() (2 input parameters) +Function 513: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 508: AttachAudioStreamProcessor() (2 input parameters) +Function 514: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 509: DetachAudioStreamProcessor() (2 input parameters) +Function 515: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) +Function 516: AttachAudioMixedProcessor() (1 input parameters) + Name: AttachAudioMixedProcessor + Return type: void + Description: Attach audio stream processor to the entire audio pipeline + Param[1]: processor (type: AudioCallback) +Function 517: DetachAudioMixedProcessor() (1 input parameters) + Name: DetachAudioMixedProcessor + Return type: void + Description: Detach audio stream processor from the entire audio pipeline + Param[1]: processor (type: AudioCallback) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index a28cd779e..45305951f 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1,8 +1,11 @@ - + - + + + + @@ -71,7 +74,7 @@ - + @@ -219,8 +222,8 @@ - - + + @@ -446,7 +449,7 @@ - + @@ -575,10 +578,10 @@ - + - + @@ -592,8 +595,8 @@ - - + + @@ -652,7 +655,7 @@ - + @@ -693,9 +696,13 @@ - + + + + + @@ -843,6 +850,9 @@ + + + @@ -976,7 +986,7 @@ - + @@ -1190,29 +1200,15 @@ - - + + - + - - - - - - - - - - - - - - - - - + + + @@ -1533,6 +1529,9 @@ + + + @@ -1593,6 +1592,11 @@ + + + + + @@ -1640,6 +1644,10 @@ + + + + @@ -1844,9 +1852,15 @@ + + + + + + @@ -1894,22 +1908,6 @@ - - - - - - - - - - - - - - - - @@ -1926,14 +1924,6 @@ - - - - - - - - @@ -1955,6 +1945,18 @@ + + + + + + + + + + + + @@ -2005,6 +2007,9 @@ + + + @@ -2244,23 +2249,6 @@ - - - - - - - - - - - - - - - - - @@ -2312,6 +2300,22 @@ + + + + + + + + + + + + + + + + @@ -2331,10 +2335,10 @@ - + - + @@ -2494,6 +2498,9 @@ + + + @@ -2586,12 +2593,18 @@ + + + + + + @@ -2623,13 +2636,6 @@ - - - - - - - @@ -2673,6 +2679,9 @@ + + + @@ -2721,6 +2730,9 @@ + + + @@ -2774,5 +2786,11 @@ + + + + + + From 3b4f9faebc42a9110d9ca9d918edddff9f6d0667 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Mar 2023 13:17:14 +0100 Subject: [PATCH 0351/1710] UPDATED: Notepad++ instellisense data --- .../raylib_npp_parser/raylib_npp.xml | 305 +++++++++++------- .../raylib_npp_parser/raylib_to_parse.h | 85 +++-- 2 files changed, 244 insertions(+), 146 deletions(-) diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index 521c8a768..e764a1b63 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -65,10 +65,16 @@ - + + + + + + + @@ -181,7 +187,7 @@ - + @@ -313,6 +319,11 @@ + + + + + @@ -459,7 +470,7 @@ - + @@ -521,7 +532,7 @@ - + @@ -878,41 +889,18 @@ - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + @@ -1287,6 +1275,13 @@ + + + + + + + @@ -1316,7 +1311,7 @@ - + @@ -1352,6 +1347,11 @@ + + + + + @@ -1420,6 +1420,15 @@ + + + + + + + + + @@ -1427,6 +1436,13 @@ + + + + + + + @@ -1498,6 +1514,12 @@ + + + + + + @@ -1669,7 +1691,7 @@ - + @@ -1678,7 +1700,24 @@ - + + + + + + + + + + + + + + + + + + @@ -1773,11 +1812,21 @@ + + + + + + + + + + @@ -1849,26 +1898,6 @@ - - - - - - - - - - - - - - - - - - - - @@ -1889,16 +1918,6 @@ - - - - - - - - - - @@ -1934,6 +1953,24 @@ + + + + + + + + + + + + + + + + + + @@ -2004,6 +2041,11 @@ + + + + + @@ -2136,6 +2178,17 @@ + + + + + + + + + + + @@ -2155,19 +2208,25 @@ - + + + + + + + + + + + + + - - - - - - - + @@ -2339,27 +2398,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -2425,6 +2463,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -2460,13 +2518,13 @@ - - + + - - + + @@ -2694,6 +2752,11 @@ + + + + + @@ -2839,6 +2902,11 @@ + + + + + @@ -2849,6 +2917,11 @@ + + + + + @@ -2900,17 +2973,6 @@ - - - - - - - - - - - @@ -2978,6 +3040,11 @@ + + + + + @@ -3056,6 +3123,11 @@ + + + + + @@ -3141,4 +3213,15 @@ + + + + + + + + + + + diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index 4a9daf896..96732e319 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -20,7 +20,8 @@ RLAPI void ToggleFullscreen(void); // Toggle wind 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 (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) RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) @@ -50,7 +51,7 @@ RLAPI void DisableEventWaiting(void); // Disable wai // Custom frame control functions // NOTE: Those functions are intended for advance users that want full control over the frame processing -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents() +// 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 @@ -91,6 +92,7 @@ RLAPI void UnloadVrStereoConfig(VrStereoConfig config); // Unload VR s // 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 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 @@ -122,8 +124,8 @@ RLAPI void SetConfigFlags(unsigned int flags); // Setup init 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(int size); // Internal memory allocator -RLAPI void *MemRealloc(void *ptr, int size); // Internal memory reallocator +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 RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) @@ -140,7 +142,7 @@ RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success -RLAPI bool ExportDataAsCode(const char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success +RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, 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 @@ -234,13 +236,8 @@ RLAPI float GetGesturePinchAngle(void); // Get gesture pinch ang //------------------------------------------------------------------------------------ // Camera System Functions (Module: rcamera) //------------------------------------------------------------------------------------ -RLAPI void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) -RLAPI void UpdateCamera(Camera *camera); // Update camera position for selected mode - -RLAPI void SetCameraPanControl(int keyPan); // Set camera pan key to combine with mouse movement (free camera) -RLAPI void SetCameraAltControl(int keyAlt); // Set camera alt key to combine with mouse movement (free camera) -RLAPI void SetCameraSmoothZoomControl(int keySmoothZoom); // Set camera smooth zoom key to combine with mouse (free camera) -RLAPI void SetCameraMoveControls(int keyFront, int keyBack, int keyRight, int keyLeft, int keyUp, int keyDown); // Set camera move controls (1st person and 3rd person cameras) +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 //------------------------------------------------------------------------------------ // Basic Shapes Drawing Functions (Module: shapes) @@ -296,6 +293,7 @@ RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); 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 Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision @@ -305,13 +303,14 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); //------------------------------------------------------------------------------------ // Image loading functions -// NOTE: This functions do not require GPU access +// 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 LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) 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 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 bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success @@ -323,7 +322,9 @@ RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise +RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells +RLAPI Image GenImageText(int width, int height, const char *text); // Generate image: grayscale image from text data // Image manipulation functions RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) @@ -337,6 +338,7 @@ RLAPI void ImageAlphaCrop(Image *image, float threshold); RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); // Clear alpha channel to desired color 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 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 @@ -366,8 +368,10 @@ RLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color); 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 ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle within an image -RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw circle within an image (Vector version) +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 +RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version) RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image 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 @@ -382,7 +386,9 @@ RLAPI Texture2D LoadTexture(const char *fileName); 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 void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) +RLAPI bool IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready 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 @@ -397,11 +403,8 @@ RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters RLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle -RLAPI void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint); // Draw texture quad with tiling and offset parameters -RLAPI void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint); // Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. -RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters -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 -RLAPI void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint); // Draw a textured polygon +RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters +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 Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f @@ -410,6 +413,9 @@ RLAPI Vector4 ColorNormalize(Color color); // G 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] RLAPI Color ColorFromHSV(float hue, float saturation, float value); // Get a Color from HSV values, hue [0..360], saturation/value [0..1] +RLAPI Color ColorTint(Color color, Color tint); // Get color multiplied with another color +RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f +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 GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value @@ -427,6 +433,7 @@ RLAPI Font LoadFont(const char *fileName); RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set 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 *fontChars, int glyphCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +RLAPI bool IsFontReady(Font font); // Check if a font is ready RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type); // Load font data for further use RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI void UnloadFontData(GlyphInfo *chars, int glyphCount); // Unload font chars info data (RAM) @@ -449,12 +456,15 @@ RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); 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 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 *bytesProcessed); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) -RLAPI char *TextCodepointsToUTF8(const int *codepoints, int length); // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!) +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! @@ -488,8 +498,6 @@ RLAPI void DrawCube(Vector3 position, float width, float height, float length, C 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 RLAPI void DrawCubeWiresV(Vector3 position, Vector3 size, Color color); // Draw cube wires (Vector version) -RLAPI void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color); // Draw cube textured -RLAPI void DrawCubeTextureRec(Texture2D texture, Rectangle source, Vector3 position, float width, float height, float length, Color color); // Draw cube with a region of a texture RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters RLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires @@ -497,6 +505,8 @@ RLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, f RLAPI void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder with base at startPos and top at endPos RLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires RLAPI void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder wires with base at startPos and top at endPos +RLAPI void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw a capsule with the center of its sphere caps at startPos and endPos +RLAPI void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw capsule wireframe with the center of its sphere caps at startPos and endPos RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ RLAPI void DrawRay(Ray ray, Color color); // Draw a ray line RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) @@ -508,16 +518,16 @@ RLAPI void DrawGrid(int slices, float spacing); // 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 void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM) -RLAPI void UnloadModelKeepMeshes(Model model); // Unload model (but not meshes) from memory (RAM and/or VRAM) RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) // Model drawing functions -RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) +RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) 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 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 DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) +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 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 @@ -548,6 +558,7 @@ RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // 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 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 @@ -583,8 +594,10 @@ RLAPI void SetMasterVolume(float volume); // Set mas // 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 Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data +RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready 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 @@ -596,9 +609,6 @@ RLAPI void PlaySound(Sound sound); // Play a RLAPI void StopSound(Sound sound); // Stop playing a sound RLAPI void PauseSound(Sound sound); // Pause a sound RLAPI void ResumeSound(Sound sound); // Resume a paused sound -RLAPI void PlaySoundMulti(Sound sound); // Play a sound (using multichannel buffer pool) -RLAPI void StopSoundMulti(void); // Stop any sound playing (using multichannel buffer pool) -RLAPI int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel 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) @@ -612,6 +622,7 @@ RLAPI void UnloadWaveSamples(float *samples); // Unload // 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 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 @@ -628,6 +639,7 @@ RLAPI float GetMusicTimePlayed(Music music); // Get cur // 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 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 @@ -645,3 +657,6 @@ RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream 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 +RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline + From 1aacefd6fabfffa69f5b1bb905263941ad2af823 Mon Sep 17 00:00:00 2001 From: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Date: Wed, 15 Mar 2023 09:56:24 -0400 Subject: [PATCH 0352/1710] Add raylib-sunder to BINDINGS.md (#2958) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index e232b1b6c..80d6bcec7 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -70,6 +70,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | | raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | | hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | +| raylib-sunder | auto | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | From b31c8364566c15ebc9911d86adfe10199812fd40 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Mar 2023 17:51:03 +0100 Subject: [PATCH 0353/1710] Update CHANGELOG --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 10c96f7b4..654c7bb40 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -50,6 +50,7 @@ Detailed changes: [core] REVIEWED: GetWindowHandle() to return Linux window (#2938) [core] REVIEWED: WindowDropCallback(), additional security check (#2943) [core] REVIEWED: Security checks for emscripten_run_script() (#2954) +[utils] REVIEWED: TraceLog() message size limit overflow [rcamera] REDESIGNED: New implementation from scratch (#2563) by @Crydsch [rcamera] REVIEWED: Make orbital camera work as expected (#2926) by @JeffM2501 [rcamera] REVIEWED: Multiple reviews on the new implementation @@ -177,7 +178,8 @@ Detailed changes: [bindings] ADDED: TurboRaylib (Object Pascal) by @turborium [bindings] ADDED: Kaylib (Kotlin/Native) by @Its-Kenta [bindings] ADDED: Raylib-Nelua (Nelua) by @Its-Kenta -[bindings] ADDED: Cyber binding by @fubark +[bindings] ADDED: Cyber (Cyber) by @fubark +[bindings] ADDED: raylib-sunder (Sunder) by @ashn-dot-dev [misc] REVIEWED: Update external libraries to latest versions ------------------------------------------------------------------------- From bf3e527dab226101dff3c97b67bf15bfa09386e2 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Mar 2023 17:51:23 +0100 Subject: [PATCH 0354/1710] Update config.h --- src/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index de9f4641a..fbc7a5b47 100644 --- a/src/config.h +++ b/src/config.h @@ -239,6 +239,6 @@ // utils: Configuration values //------------------------------------------------------------------------------------ -#define MAX_TRACELOG_MSG_LENGTH 128 // Max length of one trace-log message +#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message #endif // CONFIG_H From 7ab24b94eee690058de99359db072a9e1dbf4039 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Mar 2023 19:09:27 +0100 Subject: [PATCH 0355/1710] Delete c_raylib.xml --- projects/Notepad++/c_raylib.xml | 3144 ------------------------------- 1 file changed, 3144 deletions(-) delete mode 100644 projects/Notepad++/c_raylib.xml diff --git a/projects/Notepad++/c_raylib.xml b/projects/Notepad++/c_raylib.xml deleted file mode 100644 index 521c8a768..000000000 --- a/projects/Notepad++/c_raylib.xml +++ /dev/null @@ -1,3144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 03516b1c689ee8fd241d646064fe15bb1fc452da Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 15 Mar 2023 19:09:30 +0100 Subject: [PATCH 0356/1710] Update npes_saved_w64devkit.txt --- projects/Notepad++/npes_saved_w64devkit.txt | Bin 8510 -> 8604 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/projects/Notepad++/npes_saved_w64devkit.txt b/projects/Notepad++/npes_saved_w64devkit.txt index ec5f0412f9f34b85163739bd08980121ea505cae..92375c24aec58c21a1137c86acd9d3b108a1888d 100644 GIT binary patch delta 28 icmdnzG{<>^loWRnLk>ea5bH5yOxBlD-uy~3gdG5Tk_aXM delta 12 TcmbQ^yw7Qal+@-5DHV1AAjt$Y From e06047419cc5c2e4d85b937c45d18e8a37db0129 Mon Sep 17 00:00:00 2001 From: Brian E <72316548+Brian-ED@users.noreply.github.com> Date: Thu, 16 Mar 2023 21:45:42 +0000 Subject: [PATCH 0357/1710] Added my BQN Library to the list (#2962) I'm the only maintainer and have been working hard recently to get my library to be as nice to use as possible. I found out about this language list, and getting a new language on it didn't seem so bad. A bonus for my efforts. --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 80d6bcec7..bedf4b988 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -71,7 +71,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | | hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | | raylib-sunder | auto | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | - +| rayed-bqn | auto | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | ### Utility Wrapers From e8a5d7b85ca76b985c0730ceffb04f3160b40401 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 17 Mar 2023 00:05:40 +0100 Subject: [PATCH 0358/1710] Update CHANGELOG --- CHANGELOG | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 654c7bb40..43ab3ab44 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,10 @@ changelog --------- -Current Release: raylib 4.5.0 (16 March 2023) +Current Release: raylib 4.5.0 (18 March 2023) ------------------------------------------------------------------------- -Release: raylib 4.5 (16 March 2023) +Release: raylib 4.5 (18 March 2023) ------------------------------------------------------------------------- KEY CHANGES: - ADDED: Improved ANGLE support on Desktop platforms @@ -180,6 +180,7 @@ Detailed changes: [bindings] ADDED: Raylib-Nelua (Nelua) by @Its-Kenta [bindings] ADDED: Cyber (Cyber) by @fubark [bindings] ADDED: raylib-sunder (Sunder) by @ashn-dot-dev +[bindings] ADDED: raylib BQN (#2962) by @Brian-ED [misc] REVIEWED: Update external libraries to latest versions ------------------------------------------------------------------------- From 18a36b3e066f5743757cfa9ecbe784bbe20d529e Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 18 Mar 2023 17:35:14 +0100 Subject: [PATCH 0359/1710] Update raylib.sln --- projects/VS2022/raylib.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 673ccb4b3..f58f2ed75 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -2287,7 +2287,6 @@ Global {103B292B-049B-4B15-85A1-9F902840DB2C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {0C2D2F82-AE67-400C-B19C-8C9B957B132A} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {E6784F91-4E4E-4956-A079-73FAB1AC7BE6} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {93A1F656-0D29-4C5E-B140-11F23FF5D6AB} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {F81C5819-85B6-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} From fec96137e8d10ee6c88914fbe5e5429c13ee1dac Mon Sep 17 00:00:00 2001 From: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Date: Sat, 18 Mar 2023 12:37:04 -0400 Subject: [PATCH 0360/1710] Update Makefile comment to indicate arm64 as a supported Linux desktop platform (#2965) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 1667a9744..5d7086d8a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ # # Platforms supported: # PLATFORM_DESKTOP: Windows (Win32, Win64) -# PLATFORM_DESKTOP: Linux (i386, x64) +# PLATFORM_DESKTOP: Linux (arm64, i386, x64) # PLATFORM_DESKTOP: OSX/macOS (arm64, x86_64) # PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly # PLATFORM_ANDROID: Android (arm, i686, arm64, x86_64) From 76b5959bb5467a18641b64db09092e0461b72f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Martign=C3=A8ne?= Date: Sun, 19 Mar 2023 09:43:51 +0100 Subject: [PATCH 0361/1710] Fix missing symbol when rglfw.c on BSD platforms (#2968) --- src/rglfw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rglfw.c b/src/rglfw.c index 794e0d2e4..59f5ad2f8 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -108,6 +108,7 @@ #include "external/glfw/src/posix_module.c" #include "external/glfw/src/posix_thread.c" #include "external/glfw/src/posix_time.c" + #include "external/glfw/src/posix_poll.c" #include "external/glfw/src/null_joystick.c" #include "external/glfw/src/xkb_unicode.c" From c14c7f0b695edb363d9622777fee1986e61afd7e Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 19 Mar 2023 06:16:52 -0400 Subject: [PATCH 0362/1710] raudio: Fix warning on discarded const qualifier (#2967) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `qoaplay_open()` function expects a `char *`, but we are passing in a `const char *`. While this works just fine, it does issue a compiler warning when strict: ``` src/raudio.c: In function ‘LoadMusicStream’: src/raudio.c:1290:45: warning: passing argument 1 of ‘qoaplay_open’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 1290 | qoaplay_desc *ctxQoa = qoaplay_open(fileName); | ^~~~~~~~ In file included from src/raudio.c:233: src/external/qoaplay.c:86:34: note: expected ‘char *’ but argument is of type ‘const char *’ 86 | qoaplay_desc *qoaplay_open(char *path) | ~~~~~~^~~~ ``` This change casts the argument to a `char *` to fix the warning. --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index d7ee183a8..9e64546d6 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1287,7 +1287,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { - qoaplay_desc *ctxQoa = qoaplay_open(fileName); + qoaplay_desc *ctxQoa = qoaplay_open((char *)fileName); music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; From 2e02474b7aac2e6e3fe680a3aa996973585eeaad Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 19 Mar 2023 11:25:29 +0100 Subject: [PATCH 0363/1710] Update core_loading_thread.c --- examples/core/core_loading_thread.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 5a988bb27..0538dcee3 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -15,6 +15,7 @@ #include "raylib.h" +// WARNING: This example does not build on Windows with MSVC compiler #include "pthread.h" // POSIX style threads management #include // C11 atomic data types From 3c02f0c75b88726ac91975ae369646b657ecb3b2 Mon Sep 17 00:00:00 2001 From: Anand Swaroop <72886192+Anut-py@users.noreply.github.com> Date: Sun, 19 Mar 2023 10:38:30 -0400 Subject: [PATCH 0364/1710] Update h-raylib version (#2970) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index bedf4b988..3d1d58953 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -27,7 +27,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | -| h-raylib | 4.5-dev | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | +| h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | | raylib-hx | **4.2** | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | From 03e19c7f4349727765fc93a5db102ba3e84330ba Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Sun, 19 Mar 2023 18:27:21 +0100 Subject: [PATCH 0365/1710] Update raylib.zig version to 4.5 (#2971) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 3d1d58953..0b2acc36d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -68,7 +68,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-vapi | **4.2** | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | | raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | -| raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | +| raylib.zig | **4.5** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | | hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | | raylib-sunder | auto | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | | rayed-bqn | auto | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | From 08670ecea123bf3892206281b011a0c65a3789e6 Mon Sep 17 00:00:00 2001 From: Webfra Date: Sun, 19 Mar 2023 20:34:22 +0100 Subject: [PATCH 0366/1710] Add const qualifier to char * path argument in qoaplay_open() (#2972) * Add const qualifier to char * path argument in qoa_open() * Remove unnecessary cast --- src/external/qoaplay.c | 4 ++-- src/raudio.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index 549e9f778..7f937f4fa 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -62,7 +62,7 @@ typedef struct { extern "C" { // Prevents name mangling of functions #endif -qoaplay_desc *qoaplay_open(char *path); +qoaplay_desc *qoaplay_open(const char *path); qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size); void qoaplay_close(qoaplay_desc *qoa_ctx); @@ -83,7 +83,7 @@ int qoaplay_get_frame(qoaplay_desc *qoa_ctx); //---------------------------------------------------------------------------------- // Open QOA file, keep FILE pointer to keep reading from file -qoaplay_desc *qoaplay_open(char *path) +qoaplay_desc *qoaplay_open(const char *path) { FILE *file = fopen(path, "rb"); if (!file) return NULL; diff --git a/src/raudio.c b/src/raudio.c index 9e64546d6..d7ee183a8 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1287,7 +1287,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { - qoaplay_desc *ctxQoa = qoaplay_open((char *)fileName); + qoaplay_desc *ctxQoa = qoaplay_open(fileName); music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; From e450c75f6f33f17a3b491c16fff0951846eb6e64 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Mon, 20 Mar 2023 11:41:16 -0400 Subject: [PATCH 0367/1710] BINDINGS: Update various versions to 4.5 (#2974) --- BINDINGS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 0b2acc36d..12b91f85b 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -6,7 +6,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | name | raylib version | language | license | repo | |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| -| raylib | **4.2** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | +| raylib | **4.5** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | | Raylib-cs | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | @@ -42,7 +42,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | -| node-raylib | **4.0** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | +| node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | | raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | @@ -63,7 +63,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-swift | **4.0** | [Swift](https://swift.org/) | MIT | https://github.com/STREGAsGate/Raylib | | raylib-scopes | auto | [Scopes](http://scopes.rocks) | MIT | https://github.com/salotz/raylib-scopes | | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | -| raylib-umka | **4.2** | [Umka](https://github.com/vtereshkov/umka-lang) | Zlib | https://github.com/robloach/raylib-umka | +| raylib-umka | **4.5** | [Umka](https://github.com/vtereshkov/umka-lang) | Zlib | https://github.com/robloach/raylib-umka | | raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | | raylib-vapi | **4.2** | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | @@ -78,7 +78,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. | name | raylib version | language | license | repo | |:------------------:|:-------------: | :--------:|:-------:|:-------------------------------------------------------------| -| raylib-cpp | **4.2** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | +| raylib-cpp | **4.5** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | | claylib | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | ### Older or Unmaintained Language Bindings From ace7aef0e619b810173939ce7ae8edd89916deff Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 21 Mar 2023 00:43:22 +0900 Subject: [PATCH 0368/1710] Fix typo in rmodels.c (#2976) Upate -> Update --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 2b1d0f936..6db7016a2 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3400,7 +3400,7 @@ void GenMeshTangents(Mesh *mesh) { if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0) { - // Upate existing vertex buffer + // Update existing vertex buffer rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0); } else From 9f7a49bec388fc886cb7d84228bb70c23bd2f1ec Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 20 Mar 2023 18:03:37 +0100 Subject: [PATCH 0369/1710] Updated version to avoid confusion with 4.5 release --- src/raylib.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 4cd9e4349..6e5d023bf 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v4.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 @@ -82,9 +82,9 @@ #include // Required for: va_list - Only used by TraceLogCallback #define RAYLIB_VERSION_MAJOR 4 -#define RAYLIB_VERSION_MINOR 5 +#define RAYLIB_VERSION_MINOR 6 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "4.5" +#define RAYLIB_VERSION "4.6-dev" // 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 From 4f43ceb0d21b71041ce0aca2a45819a6728362c9 Mon Sep 17 00:00:00 2001 From: Mansour Quddus Date: Tue, 21 Mar 2023 03:31:29 -0400 Subject: [PATCH 0370/1710] add missing space in one of the cameraDescriptions (#2977) --- examples/core/core_2d_camera_platformer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_2d_camera_platformer.c b/examples/core/core_2d_camera_platformer.c index 1ae9e0fcb..b149ea8c3 100644 --- a/examples/core/core_2d_camera_platformer.c +++ b/examples/core/core_2d_camera_platformer.c @@ -90,7 +90,7 @@ int main(void) "Follow player center", "Follow player center, but clamp to map edges", "Follow player center; smoothed", - "Follow player center horizontally; updateplayer center vertically after landing", + "Follow player center horizontally; update player center vertically after landing", "Player push camera on getting too close to screen edge" }; From a139ba9c4823d2a6d48f57c930bf3ac6ecf2d33f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Mar 2023 12:04:03 +0100 Subject: [PATCH 0371/1710] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05ba5ccf0..b4e187cf7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) [![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases) [![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers) -[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.2.0)](https://github.com/raysan5/raylib/commits/master) +[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.5.0)](https://github.com/raysan5/raylib/commits/master) [![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5) [![Packaging Status](https://repology.org/badge/tiny-repos/raylib.svg)](https://repology.org/project/raylib/versions) [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE) From 04229c5854fc311354e5b848f454ef20a9db0fd3 Mon Sep 17 00:00:00 2001 From: Gunko Vadim Date: Wed, 22 Mar 2023 00:03:38 +0500 Subject: [PATCH 0372/1710] Update BINDINGS.md (#2980) update ray4laz to raylib 4.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 12b91f85b..43af6131c 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -47,7 +47,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | | TurboRaylib | **4.2** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | -| Ray4Laz | **4.2** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | +| Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | | raylib-python-cffi | **4.2** | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | From d61303b1b068c5ec579f314ef727f81de1749328 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Mar 2023 21:46:20 +0000 Subject: [PATCH 0373/1710] Update BINDINGS.md for raylib Odin 4.5 (#2981) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 43af6131c..4e754150a 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -43,8 +43,8 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | +| raylib-odin | **4.5** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | -| raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | | TurboRaylib | **4.2** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | From 7565e274b15c5e64e814ea6fa41553ad5efcef9a Mon Sep 17 00:00:00 2001 From: Jarrod Davis <69952438+jarroddavis68@users.noreply.github.com> Date: Wed, 22 Mar 2023 03:55:58 -0400 Subject: [PATCH 0374/1710] Update BINDINGS.md (#2983) Adding raylib for Pascal --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 4e754150a..fed0e16c5 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -24,6 +24,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.2** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | +| raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | | raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | From 02a8a49961190cb2076955d0960695134ba83fe2 Mon Sep 17 00:00:00 2001 From: Hanaxar <87268284+hanaxar@users.noreply.github.com> Date: Wed, 22 Mar 2023 12:59:28 +0300 Subject: [PATCH 0375/1710] Calculate exact image size in GenImageFontAtlas (#2963) * Calculate exact image size in GenImageFontAtlas Calculate exact image size with a method based on total glyph width and glyph row count Current method seemed a little bit overkill with square root, log and power functions and only approximates image size which can be wonky with some weird fonts like cursive fonts. Proposed method calculates image size directly with a simpler method and results exact image size needed. * Update rtext.c * Update rtext.c Changed do-while to while loop, and also added an extra step to calculate maximum glyph width and excluding it from image width for extra safety. --- src/rtext.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 9eec0059f..da93af7e3 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -694,14 +694,19 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // NOTE: Rectangles memory is loaded here! Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle)); - // Calculate image size based on required pixel area - // NOTE 1: Image is forced to be squared and POT... very conservative! - // NOTE 2: SDF font characters already contain an internal padding, - // so image size would result bigger than default font type - float requiredArea = 0; - for (int i = 0; i < glyphCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(fontSize + 2*padding)); - float guessSize = sqrtf(requiredArea)*1.4f; - int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT + // Calculate image size based on total glyph width and glyph row count + int totalWidth = 0, maxGlyphWidth = 0; + for (int i = 0; i < glyphCount; i++) + { + if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; + totalWidth += chars[i].image.width + 2*padding; + } + int rowCount = 0, imageSize = 64; // A minimum starting value to avoid unnecessary calculation steps for very small images + while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) // maxGlyphWidth is maximum possible space left at the end of row + { + imageSize *= 2; // Double the size of image (to keep POT) + rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size + } atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height From e55bdd5d8af913016b74776d0878b30eb830f138 Mon Sep 17 00:00:00 2001 From: Hanaxar <87268284+hanaxar@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:00:13 +0300 Subject: [PATCH 0376/1710] Fix packing logic error in ```GenImageFontAtlas``` (#2979) Basic packing algorithm currently follows this order: Copy pixel data -> Move offsetX for current glyph -> Check remaining space for current glyph... Since X offset already moved according current glyph, remaining space should be checked for next glyph. Because of this, occasionally, current logic causes glyphs wrapping around texture. Proposed fix accomplishes that by moving offsetX check to the beginning of the loop. --- src/rtext.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index da93af7e3..e8493778e 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -725,24 +725,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // NOTE: Using simple packaging, one char after another for (int i = 0; i < glyphCount; i++) { - // Copy pixel data from fc.data to atlas - for (int y = 0; y < chars[i].image.height; y++) - { - for (int x = 0; x < chars[i].image.width; x++) - { - ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; - } - } - - // Fill chars rectangles in atlas info - recs[i].x = (float)offsetX; - recs[i].y = (float)offsetY; - recs[i].width = (float)chars[i].image.width; - recs[i].height = (float)chars[i].image.height; - - // Move atlas position X for next character drawing - offsetX += (chars[i].image.width + 2*padding); - + // Check remaining space for glyph if (offsetX >= (atlas.width - chars[i].image.width - 2*padding)) { offsetX = padding; @@ -766,6 +749,24 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC break; } } + + // Copy pixel data from fc.data to atlas + for (int y = 0; y < chars[i].image.height; y++) + { + for (int x = 0; x < chars[i].image.width; x++) + { + ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; + } + } + + // Fill chars rectangles in atlas info + recs[i].x = (float)offsetX; + recs[i].y = (float)offsetY; + recs[i].width = (float)chars[i].image.width; + recs[i].height = (float)chars[i].image.height; + + // Move atlas position X for next character drawing + offsetX += (chars[i].image.width + 2*padding); } } else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect) From 8b8eddc8e29ddfb679100c5acc290c065e177d42 Mon Sep 17 00:00:00 2001 From: Rico P Date: Wed, 22 Mar 2023 11:01:05 +0100 Subject: [PATCH 0377/1710] slightly optimize Vector3Normalize (#2982) --- src/raymath.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index 422a42eee..54eaa815d 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -703,12 +703,14 @@ RMAPI Vector3 Vector3Normalize(Vector3 v) Vector3 result = v; float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; + if (length != 0.0f) + { + float ilength = 1.0f/length; - result.x *= ilength; - result.y *= ilength; - result.z *= ilength; + result.x *= ilength; + result.y *= ilength; + result.z *= ilength; + } return result; } From ecb6a6af32b4219207f9adc9635c280756dd0f57 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 11:08:46 +0100 Subject: [PATCH 0378/1710] Review format --- src/rtext.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index e8493778e..2d01360ef 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -695,14 +695,20 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle)); // Calculate image size based on total glyph width and glyph row count - int totalWidth = 0, maxGlyphWidth = 0; + int totalWidth = 0; + int maxGlyphWidth = 0; + for (int i = 0; i < glyphCount; i++) { if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; totalWidth += chars[i].image.width + 2*padding; } - int rowCount = 0, imageSize = 64; // A minimum starting value to avoid unnecessary calculation steps for very small images - while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) // maxGlyphWidth is maximum possible space left at the end of row + + int rowCount = 0; + int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images + + // NOTE: maxGlyphWidth is maximum possible space left at the end of row + while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) { imageSize *= 2; // Double the size of image (to keep POT) rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size From 19892a3c3a08a9bfa291d0d8c745ca23a27c9972 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 11:15:00 +0100 Subject: [PATCH 0379/1710] Update resource arch for 64bit #2978 --- src/raylib.dll.rc.data | Bin 11246 -> 11246 bytes src/raylib.rc.data | Bin 11182 -> 11182 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/raylib.dll.rc.data b/src/raylib.dll.rc.data index d506b8d28651d7d7e17519bcc0a382a161b688f1..7403bd020b0658ed1c8ee569850e2eff9a0b3a50 100644 GIT binary patch delta 144 zcmaDC{w`c3rHzpR1a4?EFhE!=3>!r!r>w2PP-4nX(}%nTRctOsz`2RMs?32XvTodBGr a0B0G%Sq^ZPUQuyTGDH*DQf3AgAP)doB@1`} delta 144 zcmZ1%zAjwEhmny11ZHS6FhE!=j2lHXw2Rmo4nX(}> Date: Wed, 22 Mar 2023 12:43:18 +0100 Subject: [PATCH 0380/1710] Update CMakeLists.txt --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9087f8eea..5092bdf47 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ # Setup the project and settings project(raylib C) -set(PROJECT_VERSION 4.2.0) -set(API_VERSION 420) +set(PROJECT_VERSION 4.5.0) +set(API_VERSION 450) include(GNUInstallDirs) include(JoinPaths) From 770e239f73c45d1912fe3a9f27ce5e9fa11444ea Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 19:47:42 +0100 Subject: [PATCH 0381/1710] Minor tweaks to raylib events automation system --- src/rcore.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index eae49515f..b1632492b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -595,14 +595,14 @@ static const char *autoEventTypeName[] = { "ACTION_SETTARGETFPS" }; -// Automation Event (20 bytes) +// Automation Event (24 bytes) typedef struct AutomationEvent { unsigned int frame; // Event frame - unsigned int type; // Event type (AutoEventType) - int params[3]; // Event parameters (if required) + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) } AutomationEvent; -static AutomationEvent *events = NULL; // Events array +static AutomationEvent *events = NULL; // Events array static unsigned int eventCount = 0; // Events count static bool eventsPlaying = false; // Play events static bool eventsRecording = false; // Record events @@ -925,7 +925,7 @@ void InitWindow(int width, int height, const char *title) #endif #if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent)); + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif @@ -1073,7 +1073,7 @@ void CloseWindow(void) #endif #if defined(SUPPORT_EVENTS_AUTOMATION) - free(events); + RL_FREE(events); #endif CORE.Window.ready = false; @@ -6945,11 +6945,11 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // TODO: This system should probably be redesigned static void LoadAutomationEvents(const char *fileName) { - //unsigned char fileId[4] = { 0 }; - - // Load binary + // Load events file (binary) /* FILE *repFile = fopen(fileName, "rb"); + unsigned char fileId[4] = { 0 }; + fread(fileId, 1, 4, repFile); if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) @@ -6962,7 +6962,7 @@ static void LoadAutomationEvents(const char *fileName) fclose(repFile); */ - // Load events (text file) + // Load events file (text) FILE *repFile = fopen(fileName, "rt"); if (repFile != NULL) From 0925851f896fd82ef91804cdf2429d3a75e72aeb Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 20:11:03 +0100 Subject: [PATCH 0382/1710] Update rl_gputex.h --- src/external/rl_gputex.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index c20bdc67e..f7cc00ecf 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -814,5 +814,4 @@ static int get_pixel_data_size(int width, int height, int format) return data_size; } - #endif // RL_GPUTEX_IMPLEMENTATION From 6287f68c0bb172ac70bfbe30617d9480ab4eab1f Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 20:11:36 +0100 Subject: [PATCH 0383/1710] Lazy loading of default font for image loading (no InitWindow) --- src/rtextures.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index b5f9998a9..fb63f2ff0 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -219,7 +219,7 @@ //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by text) //---------------------------------------------------------------------------------- -// ... +extern void LoadFontDefault(void); // [Module: text] Loads default font, required by ImageDrawText() //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -3152,6 +3152,9 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color) { #if defined(SUPPORT_MODULE_RTEXT) + // Make sure default font is loaded to be used on image text drawing + if (GetFontDefault().texture.id == 0) LoadFontDefault(); + Vector2 position = { (float)posX, (float)posY }; // NOTE: For default font, spacing is set to desired font size / default font size (10) ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color); // WARNING: Module required: rtext From 57082d66008fbb315162541751767fc3387864ac Mon Sep 17 00:00:00 2001 From: WIITD <52134513+WIITD@users.noreply.github.com> Date: Thu, 23 Mar 2023 22:26:55 +0100 Subject: [PATCH 0384/1710] update raylib-freebasic to 4.5 (#2986) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index fed0e16c5..dc9f6700a 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -23,7 +23,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | -| raylib-freebasic | **4.2** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | +| raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | | raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | From 1fc8f1bdbfdef1aaae897d018f97673963201ab4 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Sat, 25 Mar 2023 05:32:46 -0400 Subject: [PATCH 0385/1710] Update raylib-d binding version to 4.5 (#2988) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index dc9f6700a..f07c5e42e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -19,7 +19,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | **4.2** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | -| raylib-d | **4.2** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | +| raylib-d | **4.5** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | From 02bd709043b9ec0c4557323878ba885c6582b24e Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 25 Mar 2023 10:38:21 +0100 Subject: [PATCH 0386/1710] Update BINDINGS.md --- BINDINGS.md | 74 ++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index f07c5e42e..e0585117e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -8,71 +8,71 @@ Some people ported raylib to other languages in form of bindings or wrappers to |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| | raylib | **4.5** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | -| Raylib-cs | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | -| Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | -| cl-raylib | **4.0** | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | -| claylib/wrap | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | -| chez-raylib | auto | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | +| Raylib-cs | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | +| Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | +| cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | +| claylib/wrap | 4.2 | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | +| chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | -| ray-cyber | **4.2** | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | +| ray-cyber | 4.2 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | -| dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | -| bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | -| dray | **4.2** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | +| dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | +| bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | +| dray | 4.2 | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | | raylib-d | **4.5** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | -| dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | +| dlang_raylib | 4.0 | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | -| raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | +| raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | -| raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | -| raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | +| raylib-go | 4.2 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | +| raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | | h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | -| raylib-hx | **4.2** | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | +| raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | -| jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | -| raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | -| raylib.jl | **4.2** | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | +| jaylib | 4.2 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | +| raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | +| raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | | kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/Kaylib | -| raylib-lua | **4.2** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | -| raylua | **4.0** | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | +| raylib-lua | 4.2 | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | +| raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | -| Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | +| Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | | raylib-odin | **4.5** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | -| raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | -| TurboRaylib | **4.2** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | +| raylib-ocaml | 4.2 | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | +| TurboRaylib | 4.2 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | -| Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | +| Raylib.4.0.Pascal | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | -| raylib-python-cffi | **4.2** | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | -| raylibpyctbg | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | -| raylib-py | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | -| raylib-python-ctypes | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | +| raylib-python-cffi | 4.2 | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | +| raylibpyctbg | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | +| raylib-py | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | +| raylib-python-ctypes | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | -| raylibr | **4.0** | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | +| raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | | raylib-rs | 3.5 | [Rust](https://www.rust-lang.org/) | Zlib | https://github.com/deltaphc/raylib-rs | | Relib | 3.5 | [ReCT](https://github.com/RedCubeDev-ByteSpace/ReCT) | ? | https://github.com/RedCubeDev-ByteSpace/Relib | -| racket-raylib | **4.0** | [Racket](https://racket-lang.org/) | MIT/Apache-2.0 | https://github.com/eutro/racket-raylib | -| raylib-swift | **4.0** | [Swift](https://swift.org/) | MIT | https://github.com/STREGAsGate/Raylib | +| racket-raylib | 4.0 | [Racket](https://racket-lang.org/) | MIT/Apache-2.0 | https://github.com/eutro/racket-raylib | +| raylib-swift | 4.0 | [Swift](https://swift.org/) | MIT | https://github.com/STREGAsGate/Raylib | | raylib-scopes | auto | [Scopes](http://scopes.rocks) | MIT | https://github.com/salotz/raylib-scopes | | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | | raylib-umka | **4.5** | [Umka](https://github.com/vtereshkov/umka-lang) | Zlib | https://github.com/robloach/raylib-umka | -| raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | -| raylib-vapi | **4.2** | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | -| raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | -| raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | +| raylib.v | 4.2 | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | +| raylib-vapi | 4.2 | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | +| raylib-wren | 4.0 | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | +| raylib-zig | 4.2 | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | | raylib.zig | **4.5** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | -| hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | -| raylib-sunder | auto | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | -| rayed-bqn | auto | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | +| hare-raylib | **auto** | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | +| raylib-sunder | **auto** | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | +| rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | ### Utility Wrapers From 1a110dcbd73a9eeadd1b8aedef2d909b7536bda5 Mon Sep 17 00:00:00 2001 From: Astie Teddy Date: Sat, 25 Mar 2023 23:26:38 +0100 Subject: [PATCH 0387/1710] Update BINDINGS.md (raylib-lua -> 4.5) (#2989) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index e0585117e..38b969267 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -36,7 +36,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | | kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/Kaylib | -| raylib-lua | 4.2 | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | +| raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | From 17c443ee6db0a111d111e766bb517657204c3574 Mon Sep 17 00:00:00 2001 From: "Jorge A. Gomes" Date: Wed, 29 Mar 2023 08:52:33 -0300 Subject: [PATCH 0388/1710] Update BINDINGS.md (raylib-py -> 4.5) (#2992) Co-authored-by: Ray --- BINDINGS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 38b969267..2835bb9c9 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -51,10 +51,10 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | -| raylib-python-cffi | 4.2 | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | -| raylibpyctbg | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | -| raylib-py | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | -| raylib-python-ctypes | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | +| raylib-python-cffi | 4.2 | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | +| raylibpyctbg | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | +| raylib-py | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | +| raylib-python-ctypes | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | From 8367abad1a925fc7cf60fd8e71b5f6dbf6250ddb Mon Sep 17 00:00:00 2001 From: chocolate42 <95491609+chocolate42@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:34:37 +0100 Subject: [PATCH 0389/1710] [rtext] Fix GetCodepointNext() to return default value on invalid input with size=0 (#2997) * Fix GetCodepointNext to return default value with size=0 on invalid input. Modify LoadCodepoints to work when GetCodepointNext returns a size of 0. All internal use of GetCodepointNext and GetCodepointPrev checked. This fix may break external code dealing with invalid input as the old code erroneously never returned a size of 0, external code that doesn't properly check for size=0 may endlessly loop or overflow a buffer on invalid input. * Change default behaviour of GetCodepointNext to return a size of 1 instead of 0. This matches existing prod behaviour and guarantees size 1..4 is returned. Simplify internal code that uses GetCodepointNext that previously had to account for size=0. * Simplified progressing through a UTF-8 string in ImageTextEx and MeasureTextEx. This change matches existing precedent in DrawTextEx * GetCodepointNext: Add 10xxxxxx checks to multibyte encodings. --------- Co-authored-by: anon --- src/rtext.c | 21 ++++++++------------- src/rtextures.c | 8 ++------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 2d01360ef..dda994874 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1071,10 +1071,6 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int codepoint = GetCodepointNext(&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 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 @@ -1205,7 +1201,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing int letter = 0; // Current character int index = 0; // Index position in sprite font - for (int i = 0; i < size; i++) + for (int i = 0; i < size;) { byteCounter++; @@ -1213,10 +1209,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing letter = GetCodepointNext(&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 the bad bytes using the '?' symbol so to not skip any we set next = 1 - if (letter == 0x3f) next = 1; - i += next - 1; + i += next; if (letter != '\n') { @@ -1734,8 +1727,7 @@ int GetCodepointCount(const char *text) int next = 0; int letter = GetCodepointNext(ptr, &next); - if (letter == 0x3f) ptr += 1; - else ptr += next; + ptr += next; length++; } @@ -1896,28 +1888,31 @@ int GetCodepointNext(const char *text, int *codepointSize) { const char *ptr = text; int codepoint = 0x3f; // Codepoint (defaults to '?') - *codepointSize = 0; + *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 + else if (0x00 == (0x80 & ptr[0])) { // 1 byte UTF-8 codepoint codepoint = ptr[0]; diff --git a/src/rtextures.c b/src/rtextures.c index fb63f2ff0..bd652e6b3 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1266,17 +1266,13 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Create image to store text imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); - for (int i = 0; i < size; i++) + for (int i = 0; i < size;) { // Get next codepoint from byte string and glyph index in font int codepointByteCount = 0; int codepoint = GetCodepointNext(&text[i], &codepointByteCount); // WARNING: Module required: rtext int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all 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 @@ -1296,7 +1292,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co else textOffsetX += font.glyphs[index].advanceX + (int)spacing; } - i += (codepointByteCount - 1); // Move text bytes counter to next codepoint + i += codepointByteCount; // Move text bytes counter to next codepoint } // Scale image depending on text size From d8c7b01a3cef6ebf3f5ea7db3974c1de2316171d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Apr 2023 12:49:59 +0200 Subject: [PATCH 0390/1710] REVIEWED: `GetGlyphIndex()` #3000 --- src/rtext.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index dda994874..1d2b4f35b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1239,17 +1239,17 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing // NOTE: If codepoint is not found in the font it fallbacks to '?' int GetGlyphIndex(Font font, int codepoint) { -#ifndef GLYPH_NOTFOUND_CHAR_FALLBACK - #define GLYPH_NOTFOUND_CHAR_FALLBACK 63 // Character used if requested codepoint is not found: '?' -#endif - -// Support charsets with any characters order + int index = 0; + #define SUPPORT_UNORDERED_CHARSET #if defined(SUPPORT_UNORDERED_CHARSET) - int index = GLYPH_NOTFOUND_CHAR_FALLBACK; + int fallbackIndex = 0; // Get index of fallback glyph '?' + // Look for character index in the unordered charset for (int i = 0; i < font.glyphCount; i++) { + if (font.glyphs[i].value == 63) fallbackIndex = i; + if (font.glyphs[i].value == codepoint) { index = i; @@ -1257,10 +1257,12 @@ int GetGlyphIndex(Font font, int codepoint) } } - return index; + if ((index == 0) && (font.glyphs[0].value != codepoint)) index = fallbackIndex; #else - return (codepoint - 32); + index = codepoint - 32; #endif + + return index; } // Get glyph font info data for a codepoint (unicode character) From 06c17ab7f1eec157f70a9fde702a6332c89763af Mon Sep 17 00:00:00 2001 From: fubark Date: Sat, 8 Apr 2023 10:05:41 -0700 Subject: [PATCH 0391/1710] Update BINDINGS.md (#3002) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 2835bb9c9..beb1e25aa 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -14,7 +14,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | claylib/wrap | 4.2 | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | -| ray-cyber | 4.2 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | +| ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | From e57ee9c0e84de9cbf6fddf2f46786e87c64cb64e Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sun, 9 Apr 2023 13:42:15 -0700 Subject: [PATCH 0392/1710] Fix warnings in raylib for MSVC (#3004) --- src/raudio.c | 8 ++++++++ src/utils.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index d7ee183a8..1eec97dab 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -228,6 +228,14 @@ typedef struct tagBITMAPINFOHEADER { #define QOA_MALLOC RL_MALLOC #define QOA_FREE RL_FREE +#if defined(_MSC_VER ) // par shapes has 2 warnings on windows, so disable them just fof this file +#pragma warning( push ) +#pragma warning( disable : 4018) +#pragma warning( disable : 4267) +#pragma warning( disable : 4244) +#endif + + #define QOA_IMPLEMENTATION #include "external/qoa.h" // QOA loading and saving functions #include "external/qoaplay.c" // QOA stream playing helper functions diff --git a/src/utils.c b/src/utils.c index 771271d35..da92ce520 100644 --- a/src/utils.c +++ b/src/utils.c @@ -145,7 +145,7 @@ void TraceLog(int logType, const char *text, ...) default: break; } - unsigned int textSize = strlen(text); + 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); From 8f741d894ab350eec275df8eacf85636d38861f3 Mon Sep 17 00:00:00 2001 From: eternalStudent Date: Sun, 9 Apr 2023 23:43:06 +0300 Subject: [PATCH 0393/1710] Minor fix in DrawLineBezier* (#3006) When `i` starts with `0`, `t` is also `0`, which results in `previous == startPos == current`, this segment is not only redundant, but it also causes division-by-zero since `sqrtf(dx*dx + dy*dy)` is zero. --- src/rshapes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index f7ee8f5cd..52c5648da 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -241,7 +241,7 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 0; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) { t = step*i; float a = powf(1 - t, 2); @@ -286,7 +286,7 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 0; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) { t = step*i; float a = powf(1 - t, 3); From 9aa71f04f2410319f7a8862a073bb9639939a4a3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 10 Apr 2023 11:02:00 +0200 Subject: [PATCH 0394/1710] Avoid tracelog about not found uniforms #3003 --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 86208de4c..937535a56 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3923,8 +3923,8 @@ int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) location = glGetUniformLocation(shaderId, uniformName); - if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); - else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); + //if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); + //else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); #endif return location; } From 709b14180a6ed0379c134bd86612994cb2f36f80 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 13 Apr 2023 23:08:32 +0200 Subject: [PATCH 0395/1710] Make assets loading extension case insensitive #3008 --- src/raudio.c | 24 ++++++++++++------------ src/rtextures.c | 32 +++++++++++++++++--------------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 1eec97dab..d636b8fe2 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -749,7 +749,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int if (false) { } #if defined(SUPPORT_FILEFORMAT_WAV) - else if (strcmp(fileType, ".wav") == 0) + else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) { drwav wav = { 0 }; bool success = drwav_init_memory(&wav, fileData, dataSize, NULL); @@ -771,7 +771,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif #if defined(SUPPORT_FILEFORMAT_OGG) - else if (strcmp(fileType, ".ogg") == 0) + else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { stb_vorbis *oggData = stb_vorbis_open_memory((unsigned char *)fileData, dataSize, NULL, NULL); @@ -793,7 +793,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif #if defined(SUPPORT_FILEFORMAT_MP3) - else if (strcmp(fileType, ".mp3") == 0) + else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) { drmp3_config config = { 0 }; unsigned long long int totalFrameCount = 0; @@ -813,7 +813,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif #if defined(SUPPORT_FILEFORMAT_QOA) - else if (strcmp(fileType, ".qoa") == 0) + else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) { qoa_desc qoa = { 0 }; @@ -832,7 +832,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (strcmp(fileType, ".flac") == 0) + else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) { unsigned long long int totalFrameCount = 0; @@ -1425,7 +1425,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, if (false) { } #if defined(SUPPORT_FILEFORMAT_WAV) - else if (strcmp(fileType, ".wav") == 0) + else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) { drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); @@ -1447,7 +1447,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_OGG) - else if (strcmp(fileType, ".ogg") == 0) + else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { // Open ogg audio stream music.ctxType = MUSIC_AUDIO_OGG; @@ -1469,7 +1469,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_MP3) - else if (strcmp(fileType, ".mp3") == 0) + 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); @@ -1487,7 +1487,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_QOA) - else if (strcmp(fileType, ".qoa") == 0) + else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) { qoaplay_desc *ctxQoa = qoaplay_open_memory(data, dataSize); music.ctxType = MUSIC_AUDIO_QOA; @@ -1505,7 +1505,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (strcmp(fileType, ".flac") == 0) + else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) { music.ctxType = MUSIC_AUDIO_FLAC; music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL); @@ -1522,7 +1522,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_XM) - else if (strcmp(fileType, ".xm") == 0) + else if ((strcmp(fileType, ".xm") == 0) || (strcmp(fileType, ".XM") == 0)) { jar_xm_context_t *ctxXm = NULL; int result = jar_xm_create_context_safe(&ctxXm, (const char *)data, dataSize, AUDIO.System.device.sampleRate); @@ -1547,7 +1547,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_MOD) - else if (strcmp(fileType, ".mod") == 0) + else if ((strcmp(fileType, ".mod") == 0) || (strcmp(fileType, ".MOD") == 0)) { jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t)); int result = 0; diff --git a/src/rtextures.c b/src/rtextures.c index bd652e6b3..20244d914 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -340,28 +340,30 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i if ((false) #if defined(SUPPORT_FILEFORMAT_PNG) - || (strcmp(fileType, ".png") == 0) + || (strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0) #endif #if defined(SUPPORT_FILEFORMAT_BMP) - || (strcmp(fileType, ".bmp") == 0) + || (strcmp(fileType, ".bmp") == 0) || (strcmp(fileType, ".BMP") == 0) #endif #if defined(SUPPORT_FILEFORMAT_TGA) - || (strcmp(fileType, ".tga") == 0) + || (strcmp(fileType, ".tga") == 0) || (strcmp(fileType, ".TGA") == 0) #endif #if defined(SUPPORT_FILEFORMAT_JPG) - || ((strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0)) + || (strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0) + || (strcmp(fileType, ".JPG") == 0) || (strcmp(fileType, ".JPEG") == 0) #endif #if defined(SUPPORT_FILEFORMAT_GIF) - || (strcmp(fileType, ".gif") == 0) + || (strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0) #endif #if defined(SUPPORT_FILEFORMAT_PIC) - || (strcmp(fileType, ".pic") == 0) + || (strcmp(fileType, ".pic") == 0) || (strcmp(fileType, ".PIC") == 0) #endif #if defined(SUPPORT_FILEFORMAT_PNM) - || ((strcmp(fileType, ".ppm") == 0) || (strcmp(fileType, ".pgm") == 0)) + || (strcmp(fileType, ".ppm") == 0) || (strcmp(fileType, ".pgm") == 0) + || (strcmp(fileType, ".PPM") == 0) || (strcmp(fileType, ".PGM") == 0) #endif #if defined(SUPPORT_FILEFORMAT_PSD) - || (strcmp(fileType, ".psd") == 0) + || (strcmp(fileType, ".psd") == 0) || (strcmp(fileType, ".PSD") == 0) #endif ) { @@ -386,7 +388,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #endif } #if defined(SUPPORT_FILEFORMAT_HDR) - else if (strcmp(fileType, ".hdr") == 0) + else if ((strcmp(fileType, ".hdr") == 0) || (strcmp(fileType, ".HDR") == 0)) { #if defined(STBI_REQUIRED) if (fileData != NULL) @@ -409,7 +411,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } #endif #if defined(SUPPORT_FILEFORMAT_QOI) - else if (strcmp(fileType, ".qoi") == 0) + else if ((strcmp(fileType, ".qoi") == 0) || (strcmp(fileType, ".QOI") == 0)) { qoi_desc desc = { 0 }; image.data = qoi_decode(fileData, dataSize, &desc, 4); @@ -420,31 +422,31 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } #endif #if defined(SUPPORT_FILEFORMAT_DDS) - else if (strcmp(fileType, ".dds") == 0) + else if ((strcmp(fileType, ".dds") == 0) || (strcmp(fileType, ".DDS") == 0)) { image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif #if defined(SUPPORT_FILEFORMAT_PKM) - else if (strcmp(fileType, ".pkm") == 0) + else if ((strcmp(fileType, ".pkm") == 0) || (strcmp(fileType, ".PKM") == 0)) { image.data = rl_load_pkm_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif #if defined(SUPPORT_FILEFORMAT_KTX) - else if (strcmp(fileType, ".ktx") == 0) + else if ((strcmp(fileType, ".ktx") == 0) || (strcmp(fileType, ".KTX") == 0)) { image.data = rl_load_ktx_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif #if defined(SUPPORT_FILEFORMAT_PVR) - else if (strcmp(fileType, ".pvr") == 0) + else if ((strcmp(fileType, ".pvr") == 0) || (strcmp(fileType, ".PVR") == 0)) { image.data = rl_load_pvr_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif #if defined(SUPPORT_FILEFORMAT_ASTC) - else if (strcmp(fileType, ".astc") == 0) + else if ((strcmp(fileType, ".astc") == 0) || (strcmp(fileType, ".ASTC") == 0)) { image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } From 70d7f67bd87cbb4782217d87312159f42de00948 Mon Sep 17 00:00:00 2001 From: Benjamin Thomas Date: Sat, 15 Apr 2023 10:55:40 +0200 Subject: [PATCH 0396/1710] CMake project example: fix a couple of typos (#3014) --- projects/CMake/core_basic_window.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/CMake/core_basic_window.c b/projects/CMake/core_basic_window.c index 4357ae598..0e5542967 100644 --- a/projects/CMake/core_basic_window.c +++ b/projects/CMake/core_basic_window.c @@ -3,7 +3,7 @@ * raylib [core] example - Basic window (adapted for HTML5 platform) * * This example is prepared to compile for PLATFORM_WEB, PLATFORM_DESKTOP and PLATFORM_RPI -* As you will notice, code structure is slightly diferent to the other examples... +* As you will notice, code structure is slightly different to the other examples... * To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning * * This example has been created using raylib 1.3 (www.raylib.com) @@ -31,7 +31,7 @@ int screenHeight = 450; void UpdateDrawFrame(void); // Update and Draw one frame //---------------------------------------------------------------------------------- -// Main Enry Point +// Main Entry Point //---------------------------------------------------------------------------------- int main() { @@ -80,4 +80,4 @@ void UpdateDrawFrame(void) EndDrawing(); //---------------------------------------------------------------------------------- -} \ No newline at end of file +} From e2da32e2daf2cf4de86cc1128a7b3ba66a1bab1c Mon Sep 17 00:00:00 2001 From: RadsammyT <32146976+RadsammyT@users.noreply.github.com> Date: Sat, 15 Apr 2023 04:58:00 -0400 Subject: [PATCH 0397/1710] [raudio] Rewritten `ExportWaveAsCode()` file saving to be more like rtextures `ExportImageAsCode()` (#3013) * Update raudio.c Review `raudio.c`: rewritten `ExportWaveAsCode()` to be more like rtextures.c `ExportImageAsCode()' * no tab november accidentally inserted a tab somewhere. corrected it. --- src/raudio.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index d636b8fe2..cddc08354 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1038,29 +1038,30 @@ bool ExportWaveAsCode(Wave wave, const char *fileName) byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n"); - char fileNameLower[256] = { 0 }; - char fileNameUpper[256] = { 0 }; - for (int i = 0; fileName[i] != '.'; i++) { fileNameLower[i] = fileName[i]; } // Get filename without extension - for (int i = 0; fileNameLower[i] != '\0'; i++) if (fileNameLower[i] >= 'a' && fileNameLower[i] <= 'z') { fileNameUpper[i] = fileNameLower[i] - 32; } + // 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 wave information byteCount += sprintf(txtData + byteCount, "// Wave data information\n"); - byteCount += sprintf(txtData + byteCount, "#define %s_FRAME_COUNT %u\n", fileNameUpper, wave.frameCount); - byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_RATE %u\n", fileNameUpper, wave.sampleRate); - byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_SIZE %u\n", fileNameUpper, wave.sampleSize); - byteCount += sprintf(txtData + byteCount, "#define %s_CHANNELS %u\n\n", fileNameUpper, wave.channels); + byteCount += sprintf(txtData + byteCount, "#define %s_FRAME_COUNT %u\n", varFileName, wave.frameCount); + byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_RATE %u\n", varFileName, wave.sampleRate); + byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_SIZE %u\n", varFileName, wave.sampleSize); + byteCount += sprintf(txtData + byteCount, "#define %s_CHANNELS %u\n\n", varFileName, wave.channels); // Write wave data as an array of values // Wave data is exported as byte array for 8/16bit and float array for 32bit float data // NOTE: Frame data exported is channel-interlaced: frame01[sampleChannel1, sampleChannel2, ...], frame02[], frame03[] if (wave.sampleSize == 32) { - byteCount += sprintf(txtData + byteCount, "static float %sData[%i] = {\n", fileNameLower, waveDataSize/4); + byteCount += sprintf(txtData + byteCount, "static float %s_DATA[%i] = {\n", varFileName, waveDataSize/4); for (int i = 1; i < waveDataSize/4; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.4ff,\n " : "%.4ff, "), ((float *)wave.data)[i - 1]); byteCount += sprintf(txtData + byteCount, "%.4ff };\n", ((float *)wave.data)[waveDataSize/4 - 1]); } else { - byteCount += sprintf(txtData + byteCount, "static unsigned char %sData[%i] = { ", fileNameLower, waveDataSize); + byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, waveDataSize); for (int i = 1; i < waveDataSize; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n " : "0x%x, "), ((unsigned char *)wave.data)[i - 1]); byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)wave.data)[waveDataSize - 1]); } From 9d38363e09aa8725415b444cde37a909d4d9d8a0 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 17 Apr 2023 12:18:06 +0200 Subject: [PATCH 0398/1710] Remove trailing spaces --- src/rcore.c | 2 +- src/rtext.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b1632492b..3f2a1df8d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6949,7 +6949,7 @@ static void LoadAutomationEvents(const char *fileName) /* FILE *repFile = fopen(fileName, "rb"); unsigned char fileId[4] = { 0 }; - + fread(fileId, 1, 4, repFile); if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) diff --git a/src/rtext.c b/src/rtext.c index 1d2b4f35b..2df8be8dc 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -697,18 +697,18 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // Calculate image size based on total glyph width and glyph row count int totalWidth = 0; int maxGlyphWidth = 0; - + for (int i = 0; i < glyphCount; i++) { if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; totalWidth += chars[i].image.width + 2*padding; } - + int rowCount = 0; int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images - + // NOTE: maxGlyphWidth is maximum possible space left at the end of row - while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) + while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) { imageSize *= 2; // Double the size of image (to keep POT) rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size @@ -1240,7 +1240,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing int GetGlyphIndex(Font font, int codepoint) { int index = 0; - + #define SUPPORT_UNORDERED_CHARSET #if defined(SUPPORT_UNORDERED_CHARSET) int fallbackIndex = 0; // Get index of fallback glyph '?' @@ -1249,7 +1249,7 @@ int GetGlyphIndex(Font font, int codepoint) for (int i = 0; i < font.glyphCount; i++) { if (font.glyphs[i].value == 63) fallbackIndex = i; - + if (font.glyphs[i].value == codepoint) { index = i; From 771957458daa8b25c7f89c3ec29dd739075457e2 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 17 Apr 2023 17:50:46 +0200 Subject: [PATCH 0399/1710] Avoid shader attribute not found log --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 937535a56..e786fa380 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3936,8 +3936,8 @@ int rlGetLocationAttrib(unsigned int shaderId, const char *attribName) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) location = glGetAttribLocation(shaderId, attribName); - if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); - else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); + //if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); + //else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); #endif return location; } From 535680668be3ca2e688a73132150c16f0160e0cc Mon Sep 17 00:00:00 2001 From: Soutaisei <131299089+Soutaisei@users.noreply.github.com> Date: Wed, 19 Apr 2023 20:25:25 +0100 Subject: [PATCH 0400/1710] Update BINDINGS.md (#3017) Update Kaylib to Raylib version 4.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index beb1e25aa..2191e88bb 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -35,7 +35,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | -| kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/Kaylib | +| kaylib | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Soutaisei/Kaylib | | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | From 7b7c0c83ef3d52695b7c939ad181e7ca3faddbf6 Mon Sep 17 00:00:00 2001 From: Mingjie Shen Date: Sat, 22 Apr 2023 04:13:11 -0400 Subject: [PATCH 0401/1710] Fix offset used before range check (#3021) This use of offset 'i' should follow the range check. --- examples/shapes/raygui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 112370aca..ea95e5c85 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -3743,7 +3743,7 @@ static int GetTextWidth(const char *text) { if (text[0] == '#') { - for (int i = 1; (text[i] != '\0') && (i < 5); i++) + for (int i = 1; (i < 5) && (text[i] != '\0'); i++) { if (text[i] == '#') { From 2d04dd8b88959bf28f070ba5d8631088e1273b56 Mon Sep 17 00:00:00 2001 From: Dan Bechard Date: Sat, 22 Apr 2023 04:15:19 -0400 Subject: [PATCH 0402/1710] Fix off-by-one error in CheckCollisionPointRec (#3022) Checking `<= x + w` causes off-by-one error where `CheckCollisionPointRec` will return true at the same time for two rectangles rendered right next to each, but which don't overlap (e.g. when making a 2D tile editor). This is clearly not what was intended. --- src/rshapes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rshapes.c b/src/rshapes.c index 52c5648da..5d7eae0ad 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1601,7 +1601,7 @@ 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; + 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; } From 05af08080e49fdcdfd603e11c0ddd7d80f0b63ae Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:38:56 +0300 Subject: [PATCH 0403/1710] Update BINDINGS.md (#3023) update raypyc version to 4.6-dev --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 2191e88bb..057e6852b 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -54,7 +54,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-python-cffi | 4.2 | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | | raylibpyctbg | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | | raylib-py | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | -| raylib-python-ctypes | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | +| raylib-python-ctypes | 4.6-dev | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | From 838fc7e3033945a5df68a4e360405dc96e910987 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 22 Apr 2023 21:17:53 +0200 Subject: [PATCH 0404/1710] REVIEWED: Some old TODOs --- src/raudio.c | 2 +- src/rlgl.h | 7 +++---- src/rmodels.c | 7 +++---- src/rtextures.c | 4 +--- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index cddc08354..8dfead37f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1389,7 +1389,7 @@ Music LoadMusicStream(const char *fileName) else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif #if defined(SUPPORT_FILEFORMAT_QOA) - else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context data*/ RL_FREE(music.ctxData); } + 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); diff --git a/src/rlgl.h b/src/rlgl.h index e786fa380..38d4ebb0e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1416,8 +1416,7 @@ void rlVertex3f(float x, float y, float z) RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx; RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy; - // TODO: Add current normal - // By default rlVertexBuffer type does not store normals + // WARNING: By default rlVertexBuffer struct does not store normals // Add current color RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr; @@ -2047,7 +2046,7 @@ void rlglInit(int width, int height) if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL)) { glDebugMessageCallback(rlDebugMessageCallback, 0); - // glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE); // TODO: Filter message + // glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE); // Debug context options: // - GL_DEBUG_OUTPUT - Faster version but not useful for breakpoints @@ -2648,7 +2647,7 @@ void rlDrawRenderBatch(rlRenderBatch *batch) // 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 (change flag required) + // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (use a change detector flag?) if (RLGL.State.vertexCounter > 0) { // Activate elements VAO diff --git a/src/rmodels.c b/src/rmodels.c index 6db7016a2..6b2b4b9f0 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4525,7 +4525,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int 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 *)); - // animations[a].framerate = anim.framerate; // TODO: Use framerate? + // animations[a].framerate = anim.framerate; // TODO: Use animation framerate data? for (unsigned int j = 0; j < iqmHeader->num_poses; j++) { @@ -4825,7 +4825,7 @@ static Model LoadGLTF(const char *fileName) 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 -> TODO: Verify this assumption + // 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); @@ -5517,7 +5517,6 @@ static Model LoadVOX(const char *fileName) memcpy(pmesh->vertices, pvertices, size); // Copy indices - // TODO: Compute globals indices array size = voxarray.indices.used*sizeof(unsigned short); pmesh->indices = RL_MALLOC(size); memcpy(pmesh->indices, pindices, size); @@ -5803,7 +5802,7 @@ static Model LoadM3D(const char *fileName) 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 not normalized, then that's encoding scaling + // 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; diff --git a/src/rtextures.c b/src/rtextures.c index 20244d914..102e244bb 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -326,8 +326,6 @@ Image LoadImageAnim(const char *fileName, int *frames) frameCount = 1; } - // TODO: Support APNG animated images - *frames = frameCount; return image; } @@ -1252,6 +1250,7 @@ Image ImageText(const char *text, int fontSize, Color color) } // Create an image from text (custom sprite font) +// WARNING: Module required: rtext Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint) { Image imText = { 0 }; @@ -1305,7 +1304,6 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Using nearest-neighbor scaling algorithm for default font // TODO: Allow defining the preferred scaling mechanism externally - // WARNING: Module required: rtext if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); } From b9b045cdd8b81446340d6d213eb3e2d79a171d12 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 23 Apr 2023 10:41:34 +0100 Subject: [PATCH 0405/1710] Update BINDINGS.md (#3026) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 057e6852b..8f293e78e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -8,7 +8,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| | raylib | **4.5** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | -| Raylib-cs | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | +| Raylib-cs | **4.5** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | | claylib/wrap | 4.2 | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | From ac2e9cd00f05bf820faea737bb9beba75dc90529 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 23 Apr 2023 11:48:01 +0200 Subject: [PATCH 0406/1710] REVIEWED: Update `CORE.Input.Touch.pointCount` #3024 --- src/rcore.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 3f2a1df8d..bb8852826 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5944,11 +5944,12 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) 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 < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++) { CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; } + CORE.Input.Touch.pointCount--; } @@ -6720,20 +6721,22 @@ static void *EventThread(void *arg) 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; + if (CORE.Input.Touch.position[0].x >= 0) CORE.Input.Touch.pointCount++; + if (CORE.Input.Touch.position[1].x >= 0) CORE.Input.Touch.pointCount++; + if (CORE.Input.Touch.position[2].x >= 0) CORE.Input.Touch.pointCount++; + if (CORE.Input.Touch.position[3].x >= 0) CORE.Input.Touch.pointCount++; #if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_RPI, PLATFORM_DRM if (gestureUpdate) { GestureEvent gestureEvent = { 0 }; - gestureEvent.pointCount = 0; gestureEvent.touchAction = touchAction; - - if (CORE.Input.Touch.position[0].x >= 0) gestureEvent.pointCount++; - if (CORE.Input.Touch.position[1].x >= 0) gestureEvent.pointCount++; - if (CORE.Input.Touch.position[2].x >= 0) gestureEvent.pointCount++; - if (CORE.Input.Touch.position[3].x >= 0) gestureEvent.pointCount++; - + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + gestureEvent.pointId[0] = 0; gestureEvent.pointId[1] = 1; gestureEvent.pointId[2] = 2; From 6472928cf1443fdac73abee356709b7e529f62b4 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 Apr 2023 14:16:48 +0200 Subject: [PATCH 0407/1710] REVIEWED: `ImageDrawRectangleRec()` #3027 --- src/rtextures.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 102e244bb..3c535d9db 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2692,9 +2692,9 @@ void ImageClearBackground(Image *dst, Color color) int bytesPerPixel = GetPixelDataSize(1, 1, dst->format); // Repeat the first pixel data throughout the image - for (int i = 1; i < dst->width * dst->height; i++) + for (int i = 1; i < dst->width*dst->height; i++) { - memcpy(pSrcPixel + i * bytesPerPixel, pSrcPixel, bytesPerPixel); + memcpy(pSrcPixel + i*bytesPerPixel, pSrcPixel, bytesPerPixel); } } @@ -2996,6 +2996,12 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) // Security check to avoid program crash if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return; + // Security check to avoid drawing out of bounds in case of bad user data + if (rec.x < 0) { rec.width -= rec.x; rec.x = 0; } + if (rec.y < 0) { rec.height -= rec.y; rec.y = 0; } + if (rec.width < 0) rec.width = 0; + if (rec.heigh < 0) rec.height = 0; + int sy = (int)rec.y; int ey = sy + (int)rec.height; @@ -3008,13 +3014,13 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) // Fill in the first pixel of the row based on image format ImageDrawPixel(dst, sx, y, color); - int bytesOffset = ((y * dst->width) + sx) * bytesPerPixel; + int bytesOffset = ((y*dst->width) + sx)*bytesPerPixel; 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++) { - memcpy(pSrcPixel + x * bytesPerPixel, pSrcPixel, bytesPerPixel); + memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel); } } } From 64f2f86d325a46856c9f5b6fb28b32a0ad9d5abd Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 Apr 2023 15:15:57 +0200 Subject: [PATCH 0408/1710] Update rtextures.c --- src/rtextures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index 3c535d9db..d83d11e5a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3000,7 +3000,7 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) if (rec.x < 0) { rec.width -= rec.x; rec.x = 0; } if (rec.y < 0) { rec.height -= rec.y; rec.y = 0; } if (rec.width < 0) rec.width = 0; - if (rec.heigh < 0) rec.height = 0; + if (rec.height < 0) rec.height = 0; int sy = (int)rec.y; int ey = sy + (int)rec.height; From e2996f167ede62b38cc280a46a8dceb6b5bcd01e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 Apr 2023 20:01:26 +0200 Subject: [PATCH 0409/1710] Update rtextures.c --- src/rtextures.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index d83d11e5a..5e39260e2 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -525,6 +525,8 @@ bool ExportImage(Image image, const char *fileName) { int success = 0; + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return success; + #if defined(SUPPORT_IMAGE_EXPORT) int channels = 4; bool allocatedData = false; From 98cb7a19a1a444726eb31af34d4c25cd06c347cb Mon Sep 17 00:00:00 2001 From: kolunmi <113054217+kolunmi@users.noreply.github.com> Date: Thu, 27 Apr 2023 13:17:57 -0700 Subject: [PATCH 0410/1710] ensure distance is greater than 0 in CameraMoveToTarget (#3031) --- src/rcamera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcamera.h b/src/rcamera.h index 8b37ff7ba..852339ab5 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -294,7 +294,7 @@ void CameraMoveToTarget(Camera *camera, float delta) distance += delta; // Distance must be greater than 0 - if (distance < 0) distance = 0.001f; + if (distance <= 0) distance = 0.001f; // Set new distance by moving the position along the forward vector Vector3 forward = GetCameraForward(camera); From 662dfad670eb634c1aa4cab2bfa31880cd995df3 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 29 Apr 2023 15:03:19 +0000 Subject: [PATCH 0411/1710] =?UTF-8?q?Correction=20of=20values=20=E2=80=8B?= =?UTF-8?q?=E2=80=8Bused=20only=20once=20in=20GenMeshCubicmap=20(#3032)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Correction of values ​​used only once in GenMeshCubicmap The mapWidth and mapHeight values ​​were only used as a limit in the for loop when they could be used throughout the function. * mapWidth and mapHeight removed from GenMeshCubicmap mapWidth and mapHeight have been removed from GenMeshCubicmap in favor of using cubicmap.width and cubicmap.height --- src/rmodels.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 6b2b4b9f0..9c4c3388d 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2940,11 +2940,8 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) Color *pixels = LoadImageColors(cubicmap); - int mapWidth = cubicmap.width; - int mapHeight = cubicmap.height; - // NOTE: Max possible number of triangles numCubes*(12 triangles by cube) - int maxTriangles = cubicmap.width*cubicmap.height*12; + int maxTriangles = cubicmap.width * cubicmap.height * 12; int vCounter = 0; // Used to count vertices int tcCounter = 0; // Used to count texcoords @@ -2981,9 +2978,9 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) 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 < mapHeight; ++z) + for (int z = 0; z < cubicmap.height; ++z) { - for (int x = 0; x < mapWidth; ++x) + 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) }; From 66f0de280705f10959a4ef0c82f3b34d7350ea15 Mon Sep 17 00:00:00 2001 From: Thiago P <33945251+Stopfield@users.noreply.github.com> Date: Sat, 29 Apr 2023 15:34:14 -0300 Subject: [PATCH 0412/1710] WingBGI now leads to the correct website! (#3033) The old link led to an unregistered site (http://www.codecutter.net/tools/winbgim/). The actual domain has changed to (https://winbgim.codecutter.org/). I checked the wayback machine, it's the same site, take a look: https://web.archive.org/web/20190421035959/http://www.codecutter.net/tools/winbgim/. --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 5ae12c05c..735e66514 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,7 +5,7 @@ introduction I started developing videogames in 2006 and some years later I started teaching videogames development to young people with artistic profile, most of students had never written a single line of code. -I decided to start with C language basis and, after searching for the most simple and easy-to-use library to teach videogames programming, I found [WinBGI](http://www.codecutter.net/tools/winbgim/); it was great and it worked very well with students, in just a couple of weeks, those students that had never written a single line of code were able to program (and understand) a simple PONG game, some of them even a BREAKOUT! +I decided to start with C language basis and, after searching for the most simple and easy-to-use library to teach videogames programming, I found [WinBGI](https://winbgim.codecutter.org/); it was great and it worked very well with students, in just a couple of weeks, those students that had never written a single line of code were able to program (and understand) a simple PONG game, some of them even a BREAKOUT! But WinBGI was not the clearer and most organized library for my taste. There were lots of things I found confusing and some function names were not clear enough for most of the students; not to mention the lack of transparencies support and no hardware acceleration. From 59596e4266aa19692dcf29e3e0c4b3e7ffd50598 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 29 Apr 2023 20:39:36 +0200 Subject: [PATCH 0413/1710] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index bb8852826..500e9edde 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3000,7 +3000,7 @@ bool IsFileExtension(const char *fileName, const char *ext) const char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext char fileExtLower[MAX_FILE_EXTENSION_SIZE + 1] = { 0 }; - strncpy(fileExtLower, TextToLower(fileExt),MAX_FILE_EXTENSION_SIZE); // WARNING: Module required: rtext + strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_SIZE); // WARNING: Module required: rtext for (int i = 0; i < extCount; i++) { From ed2caa12775da95d3e19ce42dccdca4a0ba8f8a0 Mon Sep 17 00:00:00 2001 From: star-tek-mb Date: Mon, 1 May 2023 14:02:34 +0500 Subject: [PATCH 0414/1710] fix for latest zig master (#3037) --- build.zig | 16 ++-------------- examples/build.zig | 14 ++++++-------- src/build.zig | 8 ++++---- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/build.zig b/build.zig index 9959d8e0c..2bca6a1d4 100644 --- a/build.zig +++ b/build.zig @@ -2,17 +2,5 @@ const std = @import("std"); const raylib = @import("src/build.zig"); pub fn build(b: *std.Build) void { - // Standard target options allows the person running `zig build` to choose - // what target to build for. Here we do not override the defaults, which - // means any target is allowed, and the default is native. Other options - // for restricting supported target set are available. - const target = b.standardTargetOptions(.{}); - // Standard optimization options allow the person running `zig build` to select - // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not - // set a preferred release mode, allowing the user to decide how to optimize. - const optimize = b.standardOptimizeOption(.{}); - - const lib = raylib.addRaylib(b, target, optimize); - lib.installHeader("src/raylib.h", "raylib.h"); - lib.install(); -} \ No newline at end of file + raylib.build(b); +} diff --git a/examples/build.zig b/examples/build.zig index 65586f12f..f94a3b4bf 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -26,10 +26,10 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT exe.addCSourceFile(path, &[_][]const u8{}); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => "../src/raylib.lib", - .linux => "../src/libraylib.a", - .macos => "../src/libraylib.a", - .emscripten => "../src/libraylib.a", + .windows => "../src/zig-out/lib/raylib.lib", + .linux => "../src/zig-out/lib/libraylib.a", + .macos => "../src/zig-out/lib/libraylib.a", + .emscripten => "../src/zig-out/lib/libraylib.a", else => @panic("Unsupported OS"), }); @@ -70,10 +70,8 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - exe.setOutputDir(module); - - var run = exe.run(); - run.step.dependOn(&b.addInstallArtifact(exe).step); + b.installArtifact(exe); + var run = b.addRunArtifact(exe); run.cwd = module; b.step(name, name).dependOn(&run.step); all.dependOn(&exe.step); diff --git a/src/build.zig b/src/build.zig index 84684fef8..b39f23ed8 100644 --- a/src/build.zig +++ b/src/build.zig @@ -90,7 +90,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const cache_include = std.fs.path.join(b.allocator, &.{ b.sysroot.?, "cache", "sysroot", "include" }) catch @panic("Out of memory"); defer b.allocator.free(cache_include); - var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{.access_sub_paths = true, .no_follow = true}) catch @panic("No emscripten cache. Generate it!"); + var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{ .access_sub_paths = true, .no_follow = true }) catch @panic("No emscripten cache. Generate it!"); dir.close(); raylib.addIncludePath(cache_include); @@ -115,11 +115,11 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const lib = addRaylib(b, target, optimize); - lib.setOutputDir(srcdir); - lib.install(); + lib.installHeader("src/raylib.h", "raylib.h"); + b.installArtifact(lib); } -const srcdir = struct{ +const srcdir = struct { fn getSrcDir() []const u8 { return std.fs.path.dirname(@src().file).?; } From a4a5a798bdb546646b607b3348a8f2d43b161a09 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 May 2023 14:03:32 +0200 Subject: [PATCH 0415/1710] Update rlgl_compute_shader.c --- examples/others/rlgl_compute_shader.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/others/rlgl_compute_shader.c b/examples/others/rlgl_compute_shader.c index e19a6c2f5..544e0a534 100644 --- a/examples/others/rlgl_compute_shader.c +++ b/examples/others/rlgl_compute_shader.c @@ -74,10 +74,10 @@ int main(void) unsigned int ssboA = rlLoadShaderBuffer(GOL_WIDTH*GOL_WIDTH*sizeof(unsigned int), NULL, RL_DYNAMIC_COPY); unsigned int ssboB = rlLoadShaderBuffer(GOL_WIDTH*GOL_WIDTH*sizeof(unsigned int), NULL, RL_DYNAMIC_COPY); unsigned int ssboTransfert = rlLoadShaderBuffer(sizeof(GolUpdateSSBO), NULL, RL_DYNAMIC_COPY); - + GolUpdateSSBO transfertBuffer = { 0 }; - // Create a white texture of the size of the window to update + // Create a white texture of the size of the window to update // each pixel of the window using the fragment shader: golRenderShader Image whiteImage = GenImageColor(GOL_WIDTH, GOL_WIDTH, WHITE); Texture whiteTex = LoadTextureFromImage(whiteImage); @@ -105,7 +105,7 @@ int main(void) { // Send SSBO buffer to GPU rlUpdateShaderBuffer(ssboTransfert, &transfertBuffer, sizeof(GolUpdateSSBO), 0); - + // Process SSBO commands on GPU rlEnableShader(golTransfertProgram); rlBindShaderBuffer(ssboA, 1); @@ -143,7 +143,7 @@ int main(void) BeginShaderMode(golRenderShader); DrawTexture(whiteTex, 0, 0, WHITE); EndShaderMode(); - + DrawRectangleLines(GetMouseX() - brushSize/2, GetMouseY() - brushSize/2, brushSize, brushSize, RED); DrawText("Use Mouse wheel to increase/decrease brush size", 10, 10, 20, WHITE); From 7d68aa686974347cefe0ef481c835e3d60bdc4b9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 May 2023 14:04:22 +0200 Subject: [PATCH 0416/1710] REVIEWED: Modules description layout --- src/raudio.c | 31 +++++++------ src/raymath.h | 24 +++++----- src/rcamera.h | 15 +++---- src/rcore.c | 104 +++++++++++++++++++++--------------------- src/rgestures.h | 17 +++---- src/rlgl.h | 117 ++++++++++++++++++++++++------------------------ src/rmodels.c | 25 +++++------ src/rshapes.c | 29 ++++++------ src/rtext.c | 28 ++++++------ src/rtextures.c | 53 +++++++++++----------- src/utils.c | 7 ++- 11 files changed, 218 insertions(+), 232 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 8dfead37f..5e2ccf996 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -11,23 +11,22 @@ * - Play/Stop/Pause/Resume loaded audio * * CONFIGURATION: +* #define SUPPORT_MODULE_RAUDIO +* raudio module is included in the build * -* #define SUPPORT_MODULE_RAUDIO -* 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 RAUDIO_STANDALONE -* 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 -* #define SUPPORT_FILEFORMAT_MP3 -* #define SUPPORT_FILEFORMAT_QOA -* #define SUPPORT_FILEFORMAT_FLAC -* #define SUPPORT_FILEFORMAT_XM -* #define SUPPORT_FILEFORMAT_MOD -* Selected desired fileformats to be supported for loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* #define SUPPORT_FILEFORMAT_WAV +* #define SUPPORT_FILEFORMAT_OGG +* #define SUPPORT_FILEFORMAT_MP3 +* #define SUPPORT_FILEFORMAT_QOA +* #define SUPPORT_FILEFORMAT_FLAC +* #define SUPPORT_FILEFORMAT_XM +* #define SUPPORT_FILEFORMAT_MOD +* Selected desired fileformats to be supported for loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module * * DEPENDENCIES: * miniaudio.h - Audio device management lib (https://github.com/mackron/miniaudio) @@ -42,7 +41,7 @@ * David Reid (github: @mackron) (Nov. 2017): * - Complete port to miniaudio library * -* Joshua Reisenauer (github: @kd7tck) (2015) +* Joshua Reisenauer (github: @kd7tck) (2015): * - XM audio module support (jar_xm) * - MOD audio module support (jar_mod) * - Mixing channels support diff --git a/src/raymath.h b/src/raymath.h index 54eaa815d..ddc3f58a1 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -2,19 +2,7 @@ * * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions * -* CONFIGURATION: -* -* #define RAYMATH_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 RAYMATH_STATIC_INLINE -* Define static inline functions code, so #include header suffices for use. -* This may use up lots of memory. -* * CONVENTIONS: -* * - Functions are always self-contained, no function use another raymath function inside, * required code is directly re-implemented inside * - Functions input parameters are always received by value (2 unavoidable exceptions) @@ -22,6 +10,16 @@ * - Functions are always defined inline * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) * +* CONFIGURATION: +* #define RAYMATH_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 RAYMATH_STATIC_INLINE +* Define static inline functions code, so #include header suffices for use. +* This may use up lots of memory. +* * * LICENSE: zlib/libpng * @@ -703,7 +701,7 @@ RMAPI Vector3 Vector3Normalize(Vector3 v) Vector3 result = v; float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length != 0.0f) + if (length != 0.0f) { float ilength = 1.0f/length; diff --git a/src/rcamera.h b/src/rcamera.h index 852339ab5..0b9b8076a 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -3,15 +3,14 @@ * rcamera - Basic camera system with support for multiple camera modes * * CONFIGURATION: +* #define CAMERA_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 CAMERA_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 CAMERA_STANDALONE -* If defined, the library can be used as standalone as a camera system but some -* functions must be redefined to manage inputs accordingly. +* #define CAMERA_STANDALONE +* If defined, the library can be used as standalone as a camera system but some +* functions must be redefined to manage inputs accordingly. * * CONTRIBUTORS: * Ramon Santamaria: Supervision, review, update and maintenance diff --git a/src/rcore.c b/src/rcore.c index 500e9edde..476ea8e2f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -13,75 +13,75 @@ * - PLATFORM_WEB: HTML5 with WebAssembly * * CONFIGURATION: +* #define PLATFORM_DESKTOP +* Windowing and input system configured for desktop platforms: +* Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly * -* #define PLATFORM_DESKTOP -* Windowing and input system configured for desktop platforms: Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly -* NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for mirror rendering - View [rlgl] module to enable it +* #define PLATFORM_ANDROID +* Windowing and input system configured for Android device, app activity managed internally in this module. +* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL * -* #define PLATFORM_ANDROID -* Windowing and input system configured for Android device, app activity managed internally in this module. -* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL +* #define PLATFORM_RPI (deprecated - RPI OS Buster only) +* Windowing and input system configured for Raspberry Pi in native mode (no XWindow required), +* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ +* WARNING: This platform is deprecated, since RPI OS Bullseye, the old Dispmanx libraries are not +* supported and you must be using PLATFORM_DRM * -* #define PLATFORM_RPI (deprecated - RPI OS Buster only) -* Windowing and input system configured for Raspberry Pi in native mode (no XWindow required), -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ -* WARNING: This platform is deprecated, since RPI OS Bullseye, the old Dispmanx libraries are not -* supported and you must be using PLATFORM_DRM +* #define PLATFORM_DRM +* Windowing and input system configured for DRM native mode (RPI4 and other devices) +* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ * -* #define PLATFORM_DRM -* Windowing and input system configured for DRM native mode (RPI4 and other devices) -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ +* #define PLATFORM_WEB +* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js +* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. * -* #define PLATFORM_WEB -* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js -* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. +* #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_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_CAMERA_SYSTEM -* Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital +* #define SUPPORT_GESTURES_SYSTEM +* Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag * -* #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_MOUSE_GESTURES -* Mouse gestures are directly mapped like touches and processed by gestures system. +* #define SUPPORT_TOUCH_AS_MOUSE +* Touch input and mouse input are shared. Mouse functions also return touch information. * -* #define SUPPORT_TOUCH_AS_MOUSE -* Touch input and mouse input are shared. Mouse functions also return touch information. +* #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. * -* #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 or -* blocking the device if not restored properly. Use with care. +* #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_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_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_EVENTS_WAITING +* Wait for events passively (sleeping while no events) instead of polling them actively every frame * -* #define SUPPORT_EVENTS_WAITING -* Wait for events passively (sleeping while no events) instead of polling them actively every frame +* #define SUPPORT_SCREEN_CAPTURE +* Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() * -* #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_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_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_EVENTS_AUTOMATION -* Support automatic generated events, loading and recording of those events when required +* #define SUPPORT_EVENTS_AUTOMATION +* Support automatic generated events, loading and recording of those events when required * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly) +* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX, FreeBSD...) * 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) @@ -6721,7 +6721,7 @@ static void *EventThread(void *arg) 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; if (CORE.Input.Touch.position[0].x >= 0) CORE.Input.Touch.pointCount++; @@ -6736,7 +6736,7 @@ static void *EventThread(void *arg) gestureEvent.touchAction = touchAction; gestureEvent.pointCount = CORE.Input.Touch.pointCount; - + gestureEvent.pointId[0] = 0; gestureEvent.pointId[1] = 1; gestureEvent.pointId[2] = 2; diff --git a/src/rgestures.h b/src/rgestures.h index a7440fb98..0be2fbb5c 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -2,18 +2,15 @@ * * rgestures - Gestures system, gestures processing based on input events (touch/mouse) * -* NOTE: Memory footprint of this library is aproximately 128 bytes (global variables) -* * CONFIGURATION: +* #define GESTURES_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 GESTURES_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 GESTURES_STANDALONE -* If defined, the library can be used as standalone to process gesture events with -* no external dependencies. +* #define GESTURES_STANDALONE +* If defined, the library can be used as standalone to process gesture events with +* no external dependencies. * * CONTRIBUTORS: * Marc Palau: Initial implementation (2014) diff --git a/src/rlgl.h b/src/rlgl.h index 38d4ebb0e..85a8ec368 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2,82 +2,81 @@ * * rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API * -* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) -* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) +* DESCRIPTION: +* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) +* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) * -* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are -* initialized on rlglInit() to accumulate vertex data. +* ADDITIONAL NOTES: +* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are +* 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 renderer in batch, +* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. * -* Some additional 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 additional resources) must be manually unloaded calling rlglClose(). +* 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(). * * CONFIGURATION: +* #define GRAPHICS_API_OPENGL_11 +* #define GRAPHICS_API_OPENGL_21 +* #define GRAPHICS_API_OPENGL_33 +* #define GRAPHICS_API_OPENGL_43 +* #define GRAPHICS_API_OPENGL_ES2 +* Use selected OpenGL graphics backend, should be supported by platform +* Those preprocessor defines are only used on rlgl module, if OpenGL version is +* required by any other module, use rlGetVersion() to check it * -* #define GRAPHICS_API_OPENGL_11 -* #define GRAPHICS_API_OPENGL_21 -* #define GRAPHICS_API_OPENGL_33 -* #define GRAPHICS_API_OPENGL_43 -* #define GRAPHICS_API_OPENGL_ES2 -* Use selected OpenGL graphics backend, should be supported by platform -* Those preprocessor defines are only used on rlgl module, if OpenGL version is -* required by any other module, use rlGetVersion() to check it +* #define RLGL_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 RLGL_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 RLGL_RENDER_TEXTURES_HINT +* Enable framebuffer objects (fbo) support (enabled by default) +* Some GPUs could not support them despite the OpenGL version * -* #define RLGL_RENDER_TEXTURES_HINT -* Enable framebuffer objects (fbo) support (enabled by default) -* Some GPUs could not support them despite the OpenGL version +* #define RLGL_SHOW_GL_DETAILS_INFO +* Show OpenGL extensions and capabilities detailed logs on init * -* #define RLGL_SHOW_GL_DETAILS_INFO -* Show OpenGL extensions and capabilities detailed logs on init +* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT +* Enable debug context (only available on OpenGL 4.3) * -* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT -* Enable debug context (only available on OpenGL 4.3) +* rlgl capabilities could be customized just defining some internal +* values before library inclusion (default values listed): * -* rlgl capabilities could be customized just defining some internal -* values before library inclusion (default values listed): +* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // 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) +* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) * -* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // 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) -* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +* #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_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 +* When loading a shader, the following vertex attribute and uniform +* location names are tried to be set automatically: * -* When loading a shader, the following vertex attribute and uniform -* location names are tried to be set automatically: -* -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) -* #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) +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +* #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) * * DEPENDENCIES: -* * - OpenGL libraries (depending on platform and OpenGL version selected) * - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) * diff --git a/src/rmodels.c b/src/rmodels.c index 9c4c3388d..34352ec1b 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3,21 +3,20 @@ * rmodels - Basic functions to draw 3d shapes and load and draw 3d models * * CONFIGURATION: +* #define SUPPORT_MODULE_RMODELS +* rmodels module is included in the build * -* #define SUPPORT_MODULE_RMODELS -* rmodels module is included in the build +* #define SUPPORT_FILEFORMAT_OBJ +* #define SUPPORT_FILEFORMAT_MTL +* #define SUPPORT_FILEFORMAT_IQM +* #define SUPPORT_FILEFORMAT_GLTF +* #define SUPPORT_FILEFORMAT_VOX +* #define SUPPORT_FILEFORMAT_M3D +* Selected desired fileformats to be supported for model data loading. * -* #define SUPPORT_FILEFORMAT_OBJ -* #define SUPPORT_FILEFORMAT_MTL -* #define SUPPORT_FILEFORMAT_IQM -* #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 +* #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 diff --git a/src/rshapes.c b/src/rshapes.c index 5d7eae0ad..45cf6ac67 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2,26 +2,25 @@ * * rshapes - Basic functions to draw 2d shapes and check collisions * -* NOTES: -* 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 +* ADDITIONAL NOTES: +* 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. +* 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. * -* 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. +* 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. * * CONFIGURATION: +* #define SUPPORT_MODULE_RSHAPES +* rshapes module is included in the build * -* #define SUPPORT_MODULE_RSHAPES -* rshapes module is included in the build -* -* #define SUPPORT_QUADS_DRAW_MODE -* Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES +* #define SUPPORT_QUADS_DRAW_MODE +* Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES * * * LICENSE: zlib/libpng diff --git a/src/rtext.c b/src/rtext.c index 2df8be8dc..1f9ce1b65 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -3,25 +3,23 @@ * rtext - Basic functions to load fonts and draw text * * CONFIGURATION: +* #define SUPPORT_MODULE_RTEXT +* rtext module is included in the build * -* #define SUPPORT_MODULE_RTEXT -* rtext module is included in the build +* #define SUPPORT_FILEFORMAT_FNT +* #define SUPPORT_FILEFORMAT_TTF +* Selected desired fileformats to be supported for loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module * -* #define SUPPORT_FILEFORMAT_FNT -* #define SUPPORT_FILEFORMAT_TTF -* Selected desired fileformats to be supported for loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* #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. * -* #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. -* -* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH -* TextSplit() function static buffer max size -* -* #define MAX_TEXTSPLIT_COUNT -* TextSplit() function static substrings pointers array (pointing to static buffer) +* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH +* TextSplit() function static buffer max size * +* #define MAX_TEXTSPLIT_COUNT +* TextSplit() function static substrings pointers array (pointing to static buffer) * * DEPENDENCIES: * stb_truetype - Load TTF file and rasterize characters data diff --git a/src/rtextures.c b/src/rtextures.c index 5e39260e2..5c8029e5a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3,37 +3,36 @@ * rtextures - Basic functions to load and draw textures * * CONFIGURATION: +* #define SUPPORT_MODULE_RTEXTURES +* rtextures module is included in the build * -* #define SUPPORT_MODULE_RTEXTURES -* rtextures module is included in the build +* #define SUPPORT_FILEFORMAT_BMP +* #define SUPPORT_FILEFORMAT_PNG +* #define SUPPORT_FILEFORMAT_TGA +* #define SUPPORT_FILEFORMAT_JPG +* #define SUPPORT_FILEFORMAT_GIF +* #define SUPPORT_FILEFORMAT_QOI +* #define SUPPORT_FILEFORMAT_PSD +* #define SUPPORT_FILEFORMAT_HDR +* #define SUPPORT_FILEFORMAT_PIC +* #define SUPPORT_FILEFORMAT_PNM +* #define SUPPORT_FILEFORMAT_DDS +* #define SUPPORT_FILEFORMAT_PKM +* #define SUPPORT_FILEFORMAT_KTX +* #define SUPPORT_FILEFORMAT_PVR +* #define SUPPORT_FILEFORMAT_ASTC +* Select desired fileformats to be supported for image data loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module * -* #define SUPPORT_FILEFORMAT_BMP -* #define SUPPORT_FILEFORMAT_PNG -* #define SUPPORT_FILEFORMAT_TGA -* #define SUPPORT_FILEFORMAT_JPG -* #define SUPPORT_FILEFORMAT_GIF -* #define SUPPORT_FILEFORMAT_QOI -* #define SUPPORT_FILEFORMAT_PSD -* #define SUPPORT_FILEFORMAT_HDR -* #define SUPPORT_FILEFORMAT_PIC -* #define SUPPORT_FILEFORMAT_PNM -* #define SUPPORT_FILEFORMAT_DDS -* #define SUPPORT_FILEFORMAT_PKM -* #define SUPPORT_FILEFORMAT_KTX -* #define SUPPORT_FILEFORMAT_PVR -* #define SUPPORT_FILEFORMAT_ASTC -* Select desired fileformats to be supported for image data loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* #define SUPPORT_IMAGE_EXPORT +* Support image export in multiple file formats * -* #define SUPPORT_IMAGE_EXPORT -* Support image export in multiple file formats +* #define SUPPORT_IMAGE_MANIPULATION +* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... +* If not defined only some image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageResize*() * -* #define SUPPORT_IMAGE_MANIPULATION -* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... -* If not defined only some image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageResize*() -* -* #define SUPPORT_IMAGE_GENERATION -* Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) +* #define SUPPORT_IMAGE_GENERATION +* Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) * * DEPENDENCIES: * stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC) diff --git a/src/utils.c b/src/utils.c index da92ce520..2bdc490b6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -3,10 +3,9 @@ * raylib.utils - Some common utility functions * * CONFIGURATION: -* -* #define SUPPORT_TRACELOG -* Show TraceLog() output messages -* NOTE: By default LOG_DEBUG traces not shown +* #define SUPPORT_TRACELOG +* Show TraceLog() output messages +* NOTE: By default LOG_DEBUG traces not shown * * * LICENSE: zlib/libpng From 3a21301724686335c8dd79e60e051f4fc2c45a41 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 May 2023 19:29:14 +0200 Subject: [PATCH 0417/1710] ADDED: Comment about Matrix conventions --- src/raymath.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/raymath.h b/src/raymath.h index ddc3f58a1..fa72400d3 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -3,6 +3,11 @@ * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions * * CONVENTIONS: +* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all +* math operations performed by the library consider the structure as it was column-major +* It is like transposed versions of the matrices are used for all the maths +* It benefits some functions making them cache-friendly and also avoids matrix +* transpositions sometimes required by OpenGL * - Functions are always self-contained, no function use another raymath function inside, * required code is directly re-implemented inside * - Functions input parameters are always received by value (2 unavoidable exceptions) From fc56940055cbb7b6e5f4f79d84b3d8c5707d31ec Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 May 2023 20:40:45 +0200 Subject: [PATCH 0418/1710] REVIEWED: `ExportDataAsCode()` --- src/utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils.c b/src/utils.c index 2bdc490b6..4175fb693 100644 --- a/src/utils.c +++ b/src/utils.c @@ -298,8 +298,10 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * 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; } + + byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, size); - byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, size); + byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName); for (unsigned int i = 0; i < size - 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[size - 1]); From a48bb6e1ed7b33190e486ba65b7875f0dff73701 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 May 2023 20:51:54 +0200 Subject: [PATCH 0419/1710] ADDED: Comment to clarify raymath semantics --- src/raymath.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/raymath.h b/src/raymath.h index fa72400d3..fbb970486 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -8,6 +8,7 @@ * It is like transposed versions of the matrices are used for all the maths * It benefits some functions making them cache-friendly and also avoids matrix * transpositions sometimes required by OpenGL +* Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3] * - Functions are always self-contained, no function use another raymath function inside, * required code is directly re-implemented inside * - Functions input parameters are always received by value (2 unavoidable exceptions) From abcbd9817e9163bd10008b42848dfa2d72ebb6c5 Mon Sep 17 00:00:00 2001 From: Jussi Viitala Date: Thu, 4 May 2023 21:21:35 +0300 Subject: [PATCH 0420/1710] Lightmap example. (#3043) --- examples/shaders/resources/LICENSE.md | 2 + examples/shaders/resources/cubicmap_atlas.png | Bin 0 -> 37160 bytes .../resources/shaders/glsl330/lightmap.fs | 20 +++ .../resources/shaders/glsl330/lightmap.vs | 27 +++ examples/shaders/resources/spark_flame.png | Bin 0 -> 7537 bytes examples/shaders/shaders_lightmap.c | 169 ++++++++++++++++++ examples/shaders/shaders_lightmap.png | Bin 0 -> 229671 bytes 7 files changed, 218 insertions(+) create mode 100644 examples/shaders/resources/cubicmap_atlas.png create mode 100644 examples/shaders/resources/shaders/glsl330/lightmap.fs create mode 100644 examples/shaders/resources/shaders/glsl330/lightmap.vs create mode 100644 examples/shaders/resources/spark_flame.png create mode 100644 examples/shaders/shaders_lightmap.c create mode 100644 examples/shaders/shaders_lightmap.png diff --git a/examples/shaders/resources/LICENSE.md b/examples/shaders/resources/LICENSE.md index 96458eca5..7210e34b2 100644 --- a/examples/shaders/resources/LICENSE.md +++ b/examples/shaders/resources/LICENSE.md @@ -9,3 +9,5 @@ | raysan.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | space.png | ❔ | ❔ | - | | texel_checker.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | Made with [UV Checker Map Maker](http://uvchecker.byvalle.com/) | +| cubicmap.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | +| spark_flame.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | Made with [EffectTextureMaker](https://mebiusbox.github.io/contents/EffectTextureMaker/) | \ No newline at end of file diff --git a/examples/shaders/resources/cubicmap_atlas.png b/examples/shaders/resources/cubicmap_atlas.png new file mode 100644 index 0000000000000000000000000000000000000000..9fc404a70b541330b4c1418dae1b790ecb056434 GIT binary patch literal 37160 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 b>;E4B$|lPUU0GMd00000NkvXXu0mjf2K(5M literal 0 HcmV?d00001 diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.fs b/examples/shaders/resources/shaders/glsl330/lightmap.fs new file mode 100644 index 000000000..95558610d --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/lightmap.fs @@ -0,0 +1,20 @@ +#version 330 +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec2 fragTexCoord2; +in vec3 fragPosition; +in vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D texture1; + +// Output fragment color +out vec4 finalColor; + +void main() { + // Texel color fetching from texture sampler + vec4 texelColor = texture( texture0, fragTexCoord ); + vec4 texelColor2 = texture( texture1, fragTexCoord2 ); + finalColor = texelColor * texelColor2; +} diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.vs b/examples/shaders/resources/shaders/glsl330/lightmap.vs new file mode 100644 index 000000000..00278eaa2 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/lightmap.vs @@ -0,0 +1,27 @@ +#version 330 +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec2 vertexTexCoord2; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec2 fragTexCoord2; +out vec4 fragColor; + +void main() { + // Send vertex attributes to fragment shader + fragPosition = vec3( matModel * vec4( vertexPosition, 1.0 ) ); + fragTexCoord = vertexTexCoord; + fragTexCoord2 = vertexTexCoord2; + fragColor = vertexColor; + + // Calculate final vertex position + gl_Position = mvp * vec4( vertexPosition, 1.0 ); +} diff --git a/examples/shaders/resources/spark_flame.png b/examples/shaders/resources/spark_flame.png new file mode 100644 index 0000000000000000000000000000000000000000..72cea2e9b079c3510a7a4bce91bb7599dc6dd88b GIT binary patch literal 7537 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 +#include + +#include "raylib.h" +#include "raymath.h" +#include "rlgl.h" + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_RPI, 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){ 2.0f, 4.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 + + 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){ 0, 36, MAP_SIZE * 4, MAP_SIZE * 4 }, + (Vector2){ 0.0, 0.0 }, + 0.0, + WHITE + ); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadMesh(mesh); // Unload the mesh + UnloadShader(shader); // Unload shader + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + diff --git a/examples/shaders/shaders_lightmap.png b/examples/shaders/shaders_lightmap.png new file mode 100644 index 0000000000000000000000000000000000000000..bdd8d3b91c8484ca4c8ac2edf156ee6988e95671 GIT binary patch literal 229671 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%0A0bd6;+2`j10!2y$Kb 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 z z^e#ndr?Ei#l*w%U@l0-suEbn)JlJtg0Ek$4w=PYg_;zWpodmKk8?6?a9moj$O-TtNe2wN}C;Kj4LL z(Jsy?Z?@R;h{g6T&EKTIknQ)T0-$jESw&u1Dsxma)EPuD8P_R4Orgr0rIf0gChnSY zW7#fH49c8&&tISB#9Je>@dF(A;=9>7Ujb31NFShq@#z8fV6dFgw)u(A_@WOG6`B<* za|xS-l{jPBoVs|JRsS0K5d!He`be_e_cAQ?P9*54`LWhwqNZOoV>df>; zgYjOU+20v=j9N+bn-K@ByL>Vecx@Aw;pG@TOA&*q=f=DVpmka z=G?v=;p_mzP5O>HXysF~%LWZXb}xq9s8iq$KHKRCApxaHQzul7t3dzf z2Nd~_LdrfwoE-xyhyMBJDS*bFd{1Z`3g4d*qFpzz^>em{@)pSC4J9pMWTaoxYyiof#*$akcJq|gsSk8rztHA!vn-LVKt3#0r3Wm4$TMBEnM96YB*%<7i~Z5`aAjyHt?Ihu{04UHut!?S1Lr0q@2`%Fu43pLy-ZEn@o9?)Ja ze(ol`ol7RKPBe1#`%o~9>ddGzmV6uj(Z;tuo&lE84>!=4I;5)3?F;Ae*poc^+10WU zYCMbW7@7(bb>ttiim#4s;H436MVNA4c%S)E)+!nQWCLV zz&`ocFIwntzQBt0w25A6$k`U0cUJh_h9N7=PVu8Z7e@P01Wsdc)%6#Ju<*NXUH$55 z45RNK589Eg>SbJQ*lee!{z|lNVPR;vvOn0g<$gBCKfRGMS$;*DD^gXgH zH<7;WJbpyt!U;>wvW*Bh4iwVEs^9OIJhQl^83tGV!e23L502u?NBbsn1`F=KfuemF zhW{k8{JPb}=k7{P6J>Nzx^d#wz(ly<40>zeHfxQCW}P89Bf!V_$PdxMDw3qLa{k_-uNVvMblKzzf?z&t2xWPiUzZ zKep#Tk3gMM%YR$T2&agzHd`d*!*s!QEW;2yM}kIG$nRW#-R6f2px+W1)ml85y-FHh zNdPtX>$U&w!PY}xf!A(^wn-xVnSt=st&boO>NhF(ZZ)zn>3CBEQcNp|s`gEU%m_lE z?iB*zoZQ$ox$EGIWzTc%N~k+-Bl^5Uk%2_azuOd(=AVXyfsYr8CyA87<2}C&qDi- zj*iDm;ggSuH|B+=I-T)cUld3z?FBHK<0=LCRClK@?B$7HZHDlXqfOsw-m0W2MTUAL z7PMlmtA`t`;}a7XtSvu`gor}&bU!V{#G2|2@2QQA6l~qZ{Tyntzc4mB>s;|oS;t#& z!Zt4y*$>GcS`?JTEj^L(tto3IF{EGm5uJD>M0APev<(emX_V!6`gzH~!aPeW4?vi~AxgNwz zYF6BD?Rkge-&f}gTZp00Ej%pfU!Z4#va2nR4*-&KUW^)wDW+CAnE<1v!pJ!52RbVZ zI%cWTJP8Yb(u9An&?q|$vp}xt-y;F0Pw|96j$|GV66Tm6?L5wIHZ4`;jedr?>lFGs z--=eTc<;RC|0Q7RUc-c-5^%3QGzij14`$+K1+=cD!aNVEk93Y!Q6?qk91IK3QB;#& zSM6;hU+A2tRz<6G-HwsRQ%f=4Q2)xpx=9Sljs5Bu?d%ystcXCU`>1=amp3mB&dxSt zRKOt~CIs9~%K}C(PtlFJxRE3NMOBaVm=FdKjKm;zDgb21mL|7Cd5vL%dub{ zQ(|42r8SfuffG)xS5<^Gw&xjNwzX&ngd2oy0^|9uX757FuNT5%9GLWXJKk;xSJ+sv zGlV5mH0Pb97S*cn_bnc=&!dauuIqkT&%qE#E!mQH?3x$Smt8ROr^&+Nv08B9j2T<^ zmvjN{d`ow!6D}HE!9E|$IRmiR2cqyh2Nn^j89r}fX5l$qsI}u}ko7jCCygbHxexX3 zk3mFmsAm6V`Ic1{(7??5>M=5PC8O3K`TFmyCFwjgabQ`{q@V!yPyUaowmG+dANxx> z@16f5-pS+==3k*{^L})5z1F`M{-b(dMgo~|yWq<^9%9Ii>A^Ok!VFO#pYB%$G zOYx&c(%Dx6vr`36e1+|hb0*J>kk}3_58z!lUoVk~-}DM!f1C}ZFIL4MtbDv$t1k=x zTx|;yGCDU%F}{M;PGPt;MDy)I+k~&ge_bG7w#>fVKC&m}w3Fr4?8c;QO|PDk{$WSb z@08)bT(1HFIdyhHw8C*}wXPwMyK|kO=t#|a4WJk9`^kbHG0C=zf>VrNCL7HD%0V7G ziYdL85j4Z1DJ1@Aw=%?pp@f}hSS%8!hN3wrNS5~Aes0Sg@l`{_yL zrWto@J}H(I*V77igns6@;>V%^tFkmxiq#gB)n`2plV;483`zND_;w62$|8{>vt}?_18aHrIS^)T-M=B`Kce}nqv2~QB~MmV7>7oZUR&%9=r)~7 z-y84`sVn=~V&F5>2ZeDD4=K6F{0>u9#Jz2inA?CdKW4@#EW(}eR;3FndU5~gO>DC< zS#sR(J9t=`yd3Msv#RKpu3x0?yG&R19M78m*g<;b7fIV8>Zg1J8U>}9=HlhNfVU%{ zBo*B4+CK6z6;QQ@g$=SeQI{BA{i<*~i=5+R*^Un>i*V4p3LtDxZM6AuXIcd%Dw|+x zB43Y|pSNL*5JW_!`3NeDSn_qJjvfKPRioyI!~tK#g&30eJLtwO)8N)X`>8f#=LsNK zY@)Zc=m9PJAaJe*3+4;-chH|nG1g^EwyXB8mJxAajlT*1M^|f^g>aV^9dwBq@0a?G zj+pRbpe&e=vh$puJue(?#e*cP0|a>jsk&KwY)`qcWOKd{IfN7fUck+lerD<~t}U%_ zQmsSi-kh+@!b<4b6m7f%4w~fb@yotBg8XVynzQvNecpN^7SP5zVmhOf2i9Z9qLwR@ zmpi!EE}F98tZ3H20F~>VGwQBH=oOX)(ehu z$I(Yx(bS<=aEJ-b=C!aQHzZmm14NrOq`cy{N6(!PXYXrh9m4zxk7z|_ z*M!|Nmh=$3CrsxZo;WuC79XBv`-65k7j1ep=tort#C( zRFsu;W#xiH@)2e~$Jt3K!r8&J!>|3flV(VE-ZmfAEOiqetOFqEwmHZ72;AU?*md65 zH5X|zTWtJV^)7Tlr9zo#HsZHTs&j9BVnH+ewy6}}Dxi$(%wGGb+NHEebwFX0fQ3Af z;+s%12bjC@p0Z$e-3Q;VP8lfSOv&Q$k{i4@$+xVhSm%0BYL#zSr0-HF!>xAF14DM1 zIxs&G)44k`Xg=KR00wl(wSE(|sX+|T!yErTn3Fj!`Bsw_AWMyveAQ<(9q7t?5j|Ht z$;XJjZkyIWZOMC29Hmm~<0v}&wA(9{GKEs4AKkfEV{MqFXAyw%Y_uoDCUwf$SE8N2 zhpyzYD;MNdnAT{kgN58R^{=g3yf}3%SdSV^2<>Y7h>2KiWh10>r$hgYaQM!G{uu&| z*0>wREk#C9#Jc-ag^RTA z2G~K*obfM#Ek3HR*HBJ6-C>>EtsOhA`HqUQTogtS8_i}Hj#=e~5EldWZFuf=J5zo} z{L!gE<(4KgT!ItJALe!U7N#4^I?3y6J%_`^h6y&nIlj;39joPN`is@;u8a*6*WF~U zkhK0{wOOX$L6^C3y999ohW^D)b?yV7%x&kXvS#Yp3ni}>XzOOHbCE7-txr97o|BfY zq`Q|d@MAp|i(kxzvp0#l&7(%bXL!c1Ofl;PlO&atz)0L?8fDdGqa-*r*Wi*}owzyF zhPP*sDP7))ksi?91*{7sNE69zQDz^0UD&8G5R5(Z0GA+xVXaSML9`!xT=?CfdAMk_ z93})HO65Fx*k{x#(gvzmV#(7de2(o8MGI*ems*vN-r-%(KsXJvjG=9R%S{G@wpm-b8Y)R|IFZzXNXCE4A8yCPKyhQzomFIkyNjRh!cLrDJ}Zb2pomyeF?Z2^~NX zof@?C%-jgG$P>eD7;-Ce!wXCKp|0KEeG{DI(3z9G5!QCTPO!GQU zFeS(2yw2*u{i9JfO{L~xVT&^&^4*7~1Ji)cx_~a~su}7P_BZg@uU`r?`|TPl_yoe# zAaTqV)#=3@`7!c)=ZWRKz!4TG3&*tr$M)s@!Kxr#mduwpnK|VbHwkY#MS$eMgU&PU+RNE4hZY)7sClE1IY!Wt%NP+HDe(=ArZW3PtTVC?)%( zoGf{2pA6~Qs%bYpWfh`&^9}Z|GySr>C8OPfJ5BJ$fZu#KwdO5(~gDdZ|qUuA2eW#HDGl9icR>Bt$2W1(rC*-Ge#M=Fh0)V9zy}l4{ieoDyyhDR)7-e`E`}&-kcDuxE z%Wugu7KcueFPCT=aQ2;JyaP>R8s$4}j+^Tf2ti5hdB#mZx1`&DL=`7mE}=K4a>H~Z z!B(^F)HES4T_H2t@rs`pSaKXLzGU?LGE%c9C4l#yev6b~RB?;x^xw!_r_KCEe9R9G zb@F@a$Y|Lp>#@4qiar2}00%|^RNU`jabaGOyPNi6?gK`KWi%bqiNtiOVZyi1V(Fpm zO@D8?s)t&4)TXC~<3*2hOL4#3xuuXCI>Ml(7F-giVsD9|RZq7S=o=Cyv51 zBQd)lu-y?maTU}5%@(Mb9c+^x#by0N&!HCz`M?OWa+=4km}XCAWm z`Q>pGa=jtaO6Ruj+9^V2KJWlWwA&XAQ$x&cbJUBg)4#HIHd!3#TVY3mSv!x3u8N{N z;VE`*G}nf<+H{Au<1ZTUP=D-SUaS3%Fm=bhR9Z<4Zwt9q-#Pu0_K#l&!1of~yi~}= z?@*l46X;tfk#-Xl3j=H&KwDnEE^ItwYNLi}!qIQE%OUytQ}m^_yBV&Rxa<8llMdK3 zh%5Tq`$j5xYqb6$oqwZ_EF%s~sEe5wRP?DNyjIQZfl6O(b_iFL~fQf{dY})co8p!g>leD0` zCzCFbFaUS4?V!>dXOqCuicj|t8fSwjczZNYKH(*Ale9vA?tnu)jXTF>m!Q!U@C(NW z=;eOO&RLS-7SVmR`K{T~qFl~2tyJ`HxPj^9dP3olG#L4~Uw*WpS1cIH_R>QU z{DXjJdvwN^dk8naMLq$Yx+>k4W z18;HO_o_?^R(1QL!;=%eVT*h%e^cYj%@ZxrM8=7TavIlTx7(1Q``%O$uJIR}jN7zu zL5O@67C#QRep3fJ{{V;?$<01653e%1RBT+SldcxOr;ZYJZwQH)6v2`Rsjr+(lm^%z z+mq*jD=@Bq5tOLE3QWUlHZkA|n%Ktmda!8iF==y9@5JiF8rfM*Y7uhDQ3@s+q2%0x zrNKA~^gorT2oJ~tp-NABVVm_DrH`)CyqGV)$mIj}3P{p9>t!{bhMp__ziIxXputz6 zX)E@roer94$cK&orwt>y(l#k@jg+_U&)_8vEyxKQjJ4M%SN z3Zf?tey;f3Gt2|r`ng^=e8V}j(kH8sEs%3L<1S1IpaV1G5zIm7q0d44aE3TA3!4KtMk{5y zH_upHs1yrUnyS`PtDrEZ>q6jK)6S1LtD-22)rY+$-K&#l_;qH?G*L{n^e8()DiW&{ zh^L}apG2{r!9t+cOSl<@x@7e5i_LXQB{L#`comjpd}mcGGCd{Fs?VZ-==R%{x}lS& zF7KtIn2yp@*D0t_g7At(U8w*SQJ?j)Xtl$(MJtMj7<92wF%-Fo>U7t_iC(BI#4XqH zbxjbR<{?dv%Pu+qGRas95i<~Smmmu})*cyj=oB*AHr>77Ue$0}UerNT;T*7O=p!VQ>RkZ$gN@t{toPPqiB`51RqPW^XLWD{WwZYLkDZt!?%@ zatsv26N$I?geL@~t@w9Pv(#&b7HBMATI5f#BYgeIM}0(Xxv>~H9_7Fuj0J^!PPZ=? zt_PlH=bbS0GgE5Y5g|Nv6ILu&LthpV_Aeu5N$y+qoi~#bX4xx8$l?i2;SXa)rGyXb zbbLd_{7Wr~xzf;X24xugC%ZAW%&=}#Dn6)Yl(MWfkuOOtc}p5vzcJPgH|ni6)eR=N zefNLou&{Umd5~(xjXn=;}$kfR;&SakTXC|lAt91gi4TA-C1v$~Vzt|$1uU4Sp; z!hSma1mtyMAqsiIueN+Mx!wHQ*-KK(XB?M#N zEJI!C5FWH(yqM5}Za6;zX_@QCh3mX;*ow}{sD1hif&W8nmFJ!RK?(W#nIBpKidS5v z&N-K8XK%wn(TudCfUlzXuGN1ICX}!EpK^h?8K$-W1xw_RmO8h`Cd&y?2Ed7#&-F%) z%4xihA`mUxbFKt=4I&t@zYM89h3Z@^L++E0fU&)2cJ)dBR9c_*)^b|nEH&Ph;j;!K z*0t;>|8CyXr4V>}aUUVTMyJ@b?+F#^n_Lo4^SU8>pILJ$VU< z5ts(y4os$%-Poml%KXi2!p@`UFJkGc6yIzs20qnDFseQh!&`tR(ie~*n)>)JK$&^y ztI();B?xaKcAoyJ>UjW@?8%ixp0#+`TLz(CTxik0x3H0;3mnT}76s-U`#6+4)>s>u z&4Q@7TY~xGTHADd)$61Q!WuB0c=vHuTG&BFE*~+nNQ1sE4f%FG_C77!~2C5$e_BDZG^% zsN5Cgb7xS`%p}v+qpH9YF;@Ef0oAU6MwMOEbpE9tkzGI*xY@3UBlGcL*R2Q|L+yh1 z8>4(UZffi9-p(u3z82KQA;o)+?a?TnJd)K<;MmS9AG>j!{|RVEX1FN`OKxazZiw>| z+oEl4H(37OQNGC5vgBC(;HUg_;_Wkii!(|&95VES7DwEovzz!{W7JJuf!CC{*2#+o(A;$g42->zGhm^xaJOxup#zi(*WyH8Z?lpdI)N|sL4%< ziWl1g*64L=qDnC(tww<_+E1i}qkW!3jnZ)oC-1^oZC+D~)wKp(hD|Pacblf}9C(9Fffe3m5;*^)#hPGY1-)Tar};2*vNe`JTPzTS)}IPa6odhq3{ierj3rgld8! zDD^j}ZhOyf3RM@^L@X9NYvnC*muIW}qn)Kc4=iAJ@5~g*KhQWKi1QolMeg48o%CW_ zes(P!S+vm7FLG+;(6F-{PjVHz1WYh(eLRb`s>|wFw0zT|4nQw z@mhg}p!t2EptEkw0?GZ!d7Pnd3J`agW0_hzfAsYfoKq5X?A9n)5-NnF$d z{`P5P|J*F(pi%nc{=INsW_sc9o0FRfs?80}tErR#Q1U`b$bm1MXEFsYYeXJanR240 zld=y?4uve?btAsOI>bR>_epfng{?sE6wYVnDYYOl+9=S<1 z5EUUSpSunb^8rfb0?KNPa@*c&xtm71zEEqu)HEVJAN^f1PlLfC@%-G11MV`#W~DkUdM zr!apNzbYG@9BLWp$siu=e5mE$&F^>C`fANNG)MD3z?8}2)=>5CX~I!PG) z8+!#xDMF7g?f25C)E4AocSf+M^%$UbOeT4y*)>n9wfeFW3Q6rTA?vnZ-Iqr8SoCgzJP zF7eh$?L3TB|tz zU@zT38NgbW)ACkK$<<>T`5WJ#huyA4i68Mod{fgyjFru;x#-L;JH5_^E>#d zlBOMyb|o)lTSX;O;J_`vF?|N^2fYO`hvJiPtTv^ovn7srgS}8ZPK;V2m4S^H7%Bd( z1}wkgCiFpPt5Q114xYtRdF!q=&-K~*9WVjwgn0rs{g46U=sne=V`RtWs2G1wIxH#M zw743ey-yGN9_QRZxqDGdMnhUigKL=D@bn$MYjo&xwD=|d*{=&otlUy)A>QoN-v!3J zYdDP96moGn0q$J403NQlceVw&X63PiK^f{u{H}OKmf@t$-hTeuR&xCet1OZI!&7n` z0O{n~%EzlXx@Fprz<`BVtvBE)N#Ix~3z;*{5f_@f zkb96TaGuQy8;#8LJ#4Ocyq|53j!;U?$0UYR=3Y`Hoh86bNEt-|s}SKIH9r`G1pCZ1 z>o4byyk$t&vq-1D)~DuM$Q;OQc~SfXKP6IKQ=}Hv%(L1Qv)`)Dex-G9?sTHr#A9M5 zS#OQH6UwP$P9|fO=)~I^X`gcnZ;QrOyc*yPQA&n1*KQ1r*(I4l zH`0b1aIRt`TBQ>$*HY@AtTieShQ7-0j@UF5e5^(qUaX%&a6=*wP^*lG*O01!MtvF4 z$^;pai{$K;lY-q?jB;;K;+sWLSCnA?yc)Z&E{oH7|DGttmfbOvb?GF_JCZl6HQ}~^ za;d+M+*ozO5tSOGPo=yb${I%|xP3sR#%hSEY8dm{7XX&V4FHpl7l;WEfp%NqhvL{{ zX`?eN_kCSEh>1C3!|Evz}F!r_(L$}uB6{%rzr^cZf^ zxlR<`lYP*+Tp8`belKeIMyk7>-#;hl&XgN(pdTE2owG_tO|sfIia|0?99BI7+3eX1 zNSYuWd)jy*!{YthWBa2v>iq7r3|O`AUB~zKc190>?cieyGtj>efat<^`?V^+&x)X) zfB}-r2Uh-cZb#SycoqxcE@|m56s#mLyR4V`C>L7uwhh!AHZuI5%_H_PscvCtHNpFc ze2!3F8Cj@Wtt;Q_(z!P3NM|KW|7&74c8lq*7?F_e5h2%&p}bcWXxOX)&=NYwVa8?u1`|NSwJMIbvdW@Vf&58a3_8KCOOJjKPU_+@ z>E9wgahWO{ul}R%`8=x5Wb1pEfIZl~gZ*IfDSCSj`977i#feG*cQ>1&pPYR#; zbeikPBH542%Rxw0*`6e=NfSPR-3Q2?GaQ@ zdM48D<~;iLS=7KFBB3H$1sUe;zeRVz`z$pYm_zibOge;M3jh|Zbzc?u!2sf(kw=I= z==JCVttDrx@l*-D&N!W}4*V>UUKJf$WMLr7DO#L4|G|AW4qK%6VHNJlA9b{ z3u7Qq*yD)lPR6aVaFKkKyGWpYOPAK-e6M4hnzZ@{5i9Tey?H7;YC)f`bEt1ceo1Hg zY3=850-Cd2Pw0XDL*A$tC$?FN1yf-Hi`R_b6Ix9bt?DITrd>EI88Mn z3xqa=<$~5%y$oxui|066yUI&scX4)qD&Lab?@2#E^>e5}`=A6)&wYA$icgS9MrP|j z{#2%#|1AZn%PtYSp?1aSUOn3|w))IP4)Jh&T*=T^m=zb*^4b4?xs%bEsl2E7>+f0T z+eF>#Q;xtE?;LIfck*lB?HYUBAULQMzuBW4bB(|Y4{S!Kw|Oz{+YH15^YC5MPu|eP zUpkzr{GE4o?lfdm9bSw6qyql>k@Xx?1*B!1o{P#g9xd{kd2Nt##0JC}1z!&MK1?G7 z?E|*ioDF|4TwjFu<(%6V@?@opW;sQ|3I53ouQN?KaS2l zp2`0I<2$g8%^c=1$Bj8v4s*yUY)+db)ZIblt~rECLXt|^Y|g_bw@7LZm5%r5r1PAo zkW)p`cMiGbmXJi@`hD)-e?98)@Q_`u>-xOkuh;X*L6FMMvl?DyJ$v>qtvFxJ!U*!p zKap|4D>@dr0hzPfc3~oPgm1Q$j(Ens(9 zM!`<_)Af~uxXZM}cFoBmW!Yu}9F0@15i~0Qzb1IdUgHhOOP|>mHxU2x#6K|>z;B4r zCsHIskjkSKr~s2(1m4t>;(@B$LimGpL*l(96w>bQAM@h`#V>#x>~*Bm81jQJ{-z53 z^$j`z=KjA^_{lTHXQ}cg2Sp#gllp7vm%o6mnkaEs_Yl?=RkTbmYwZD8^)5r?d{4dj z>7T4HQGl8KKcF`Zj}GPZNa*j=43c?MqdgYN0X&MM<7R>BOIi&amc6{GXWp?xS%u3#*>j z+k$;w(+@urwV6Hs)ibYldKC6l>#zeQzleJ;cB#PEjQUUL=02SH7wndlin0PrH=|8T zWoN{L9AJ1l?IcqSEOF{6mrlji_H?WjXo8o)4%pUXwUEkfQ%W1d%AfalE$|`H|W9ntd`*k7DqRdsU9oy3JGF>1MGcOdkpJEo7Nyv6_W%h(S?S05S9tHW?4!ax5<1SY8??o7Y*-e!y*j zDIF-$L0Q=00V9$<GFEx<^)?#4=6am?D-Oo^leX36AsfuetsI}xN-==!+sVLewsbf(7)m?o%n zzRLIYoy@ZcIwj$aHLs6rQ(h;eXysEwgQJk2=IDFF@(YSG`Ug1?anMlWF=chxYn(KI0{VBwOe(G@0+cX9IYTtWR&Pwk(I9!8u8P-EGoO$ zf&;`k565;_m#TEH?h~E4M~r9M1!o9c{Z&Ks3sLny;}06RmMVhz@NpxHH(}Ec(k>#m zoaA>)TAmyNMh&E&7b)85Rfjxf`x(SxJUBUd%uBzhPC2P>#QEM+lIEk^K-V~BB8^>2 zJfwKj;A)iwcIPK+2GL6drOo~)O3(j5EaDyRar6m{nzE6j%EpkL#KkIDjn)A`sAmJF zALpvR|3gLyHlNZvy%SRCP?W(nfg>d8zyyy17pbf%EJJ0egwcXVy9R6ug$F;&z zV;$t4v=+?Z0+2=hUQ=FQz2-$Ai)}ijiXVtdenoUG?#vxnmw=iU@Sg1;0`78}yO%>g z7%apLu^g5_JHZ0o?J6h*U>;O7g7?aX{9QNz0>t|1kQCvYzM85hPrKWNX0TQ>2aq2u zlrPR_oOGhw>2+V-NxnzdS5kGJL?sx4&W;BrFJupS<3;ip^uT6VXABDv1gbs{Ks9%f zCYs;|eAF6Eu-D);YTQ4yLhH48Om#>YQ!1V$c5f#QJ%N%7Rjiy7=b)N%q27*ana%@4 z?BbJjKVtkwhBkngm}>{A5&A<1hL9S+3*LN9UNHQKR6e|r2FpEnsa#*gaoVIZu+q$) za%9x#jMvj^4TdeFQ`Ap-ta4QU#tk$PW>WR$@V)6X?hNC5;tyU@i<)zc2%Q20l%FJZ z`oY)K=#r*Yz)YdT(Uih6!Fe=gQN(i$16S)UFRsX^zuuKh^{#(~@!dSAn|bz;s$pMKgF&o^I(%og^du zl?z=M_m?t!;FP_BDpC&?yp?jg_-ww9uXwHJp3nb#yIumq@uiETw;$pCS3!2?$U8&u`jEGuaGuuR0W zK7LCqQ%d8XzS~vII5-%9Z-N?9M7M@#3sLwL$fWeCVG0;yn}|c|SNTo|6;u3G8y9T! ze7#N^>!9ypg--rTW;)WIPZS_^HTUCG9Gf;JOHWh1Ky3SKy=Ok0B++rLZ^VVE+_Vs6 zKDx}CTrQxj$YL{ebN&~bb}p19Ie);HT84;XVNKLeo%K9GS7>?PCJ+9f|Ish6FI_j5Y&z&W+s|EM3Am4pqfq}no z1PdNq>4Bv@JEUOwjEI@jo@GP9k7vi%5fSHpu-g{Eqee#JSlb z?VDWm7*o3qOn2KB!m-MzYR3OktLw_)Z^z6h#&QYv&X*Re9?S20!u_WY3O3@v1NTZM z-1W|74st#@GxNjwPIUZn<0#QL1^5Zib5I!j00Z-ZkoHVn%gJ{U+7J`&GKuh%o%0KF zlh-vtE9s?ybE`SxgeQB67Pnb51#B}SyR&oo{y1YKBcuU+9#n3dFLq?@Vpf^NFz%as zJ8EgV4fHplw)db%)(8o@_M;MYerJ|R9{{=>`?a$%^)=Q01n_U|Bb^K1^kV;vb|Bt9*v4H6HS9-51oBaSDcTjw`*YU zv-UN3e=Z*q1Ba&WlrYjoQvNIzqeZQ3v8OzYlN27+o;!GOVQH!0qkEi3Tb5?(WMp_P z)(dkcB+a|$IoO0#B`tARktn?{NMrt|pa=;Hgs8Sc5amSLS~x>4W1l$PnUhR+&~PRP z>3B{?s)U;6?1>I-%4&kl8-u4Mhb-64gp=VaM`ltxx~5VQr{^Y=t7p?Ru2Xl1k5$QA zSI==mt^sE2B~bH42+j*TGoJ$A6tYe)LwAi**O6-PFtk28nf)UyR|@8{q&)^$-S8c1 z7fy>l4=|{qGi}jBW4U$$J%w1gW!(c#He{ww$0sQIAZ&+TEZ%>OHiO~aXaP7VtD}tR zv&j9sFd%Aj2Ei=4NXq+V_9Men=cX5 zOl{5xfnjkfo8t?6AW(I_N3MmM8udEy<1%mjkzh3LNL*EfI!gQxQCsyZ!J?OSft*X( zMEF~u{Bw<|L<`AFz@5<+4x#Ggz-0eoW{XGMy`Hy-A1oM=QC-aURo#5p_ z8fsr^h5Zj)K43BLMyGfHxTMVHF$kP>p>W*dUqNn{M@hj*Top zp*w`AM6=b3)`&UumKIZvuQ{MCOu&K*$rB%rvS&X~Z&7%vxZovqVgpVw!u4IqsnHthxu@%=BZ$(#@?~iltZd}XhHfG1rjh5GDh9Q z?v6qkpn||mkOy~Ds=UPow=G#kx^B)>FxH8g@M!72!)~!D?yDuPtz;~ic!VPN=cn5r zflrB2eoG~=1EDZI7didM*aX!9e^XRZUo9Q`h~Bo4wyC^(-#ZUGL(y~7ffUY$t+o+jDrmx);t5{aXNs~ib@D7S3hM>8O{)tyaGN0Z1TpR+Z;Dy^ z7M4@d(MZ1KF;4@Hn`#FQWIiwj>qWTXa3w!9+)s)UGsGn=Yzx#0GY2=;x|op+lL2jC zIb!Q*ExIK-m}sXm$Y~w3V$=+!#=tx1bjn`SwtrI?qx#rgIiplRWO1knhK;b7dpE~E zrRZH%>k+CDz~*;ns8Nt9Jf(@#G53fpu)HgSJ8$p7et~?mQO}E78<<)dJ9mg$H~pu= zj8Xst>lj*_qcl4NI16fwru>11_^Jvs-=OB8I6PrQA78x(U!-bc+{WRmOVKCoyFoM1 zg(MTAWz(q1njvWNkr49WxiNB<3rv&%1^O{O_X3eRngh4{g9==TfLBopY0o0>&%)!i zYpazl7S}SLXuaJ6)t=#UN@g1f`-7j)kplM~WYOa>!88i;+F0Cx0zMN8R_unF(`gAz zEg!7yKQyF=1hWNE-(Q&>zLgX_E%#e_os(pd{xpr`^+;IUj#*1S+9C zmcFZ;Y*ICt+{SNU_Mx%pnAV0SgJDoMIj{P}rM?Urt6@Xo*Y=fYi#y~VB6JJ}T;}Ut z;Ik0RTC z+zh2{zieeNe)2Q^R&0nHgjp?8j>8_S{>3#QC?>RdGw`ZM4o$gGcIkyB%HWKJfD~Nf; z!m`@*O#_eOu;5d0oyyvXXhM{bD%9%Ju5f&@(gd{T*5ppZ1~o>>OQ{lh`*17$oZe_p z`#eNjFb8WCIX5>%-HPI<7x6dkbQt*6nh`=yVS4N2S29glDEkLN8;SFiQ?<*On?HXB zqPo}ZgoII!tluuIlgBZB9xi%A-GfS?ks6(-Fc{5<_z8D5|LrU?3vO6E0dU%1Rti1{ z;F}vVNmQGP`1O!u7Y zx(*29mzDgzIvd|9c;IQczgOE-mx0&l)io%%SEnmLx2`tm9{Yll(IwpU_7r$m!U|_v z0bahh9dq-R0R4o0&)B?N0Es%kJTsp;I4-|mu>-bednd-_rq*hsC%|#dXYUPuj@=>v zNnrV1-$oR5P${r=@!V2=vbxu2K_AvNOQ>e$=jxT`wfQ~Ho1f#gQSxKe?D_Y`tR{n$ z*ouL2=~~MKOhj~5vNal2{QLrTv+ z_}ZNJ|1EqTAWxCCZi~>=?Cf_afYVrTrfEu!O7y1NRq$&bDXdENQGe##Mk}t6pjs^V zgH-1Fke6wN`W$X9ugLCVX1-?P1ZN@-9Y8Ntniu_XOMJqG*uw~qKN#Q|+LooxRHm_9~u6OCN)oFQ+{>q@0Fq|z`%N-`)qE0ty;H9G_i)nx6 z&Cnt&`A)#5?x_B4EY6OhG7@jM3t)|TaQBTxuT;ZnPq5iaSmU$edif)(f%|{8>XM?9 zM(!hWYm2P--%>|)p75Ri1K^zi0xG`;knE2O zP^!E^5B7zb+=oo$uB|w}Z`%hhYJmN^mVAaG`sK9k+bT{v;Ql9cp{3iM8QOw-Vd+#3Xxm*x_N(X?8i_OU~)QhHd z9ps0XqR8&UhVt6iOl|cP-h@MF>TT^fW2=<_&ksn%4$N!tO*1Wnk-BRMas6;F9N z69|h7h(2ZO0U=;u43f(sLoK+ik@cO%q#afRn)WX#Xvf=XKFS*w%o{#4OO~lW$hW!& zrE|?$CaSAYLF!{g2O4?13eSi7x9~iJRlI^Gm9U=@(tcXBhZ-d23&WB zg4J{BD4^)QQ6Yp{?=o@fRSza+mM)FYwt~uKB$iL4aTja~MHk>qC&*0Dx<(M5(-woH zOp<4L-lrPgDYSDz1D}0Hqtk72OA(PMuGU?c<;_(@RwU|(dFZ>X)~^zLBF+wsexprH z(mv2(VH)71->;$XJClxHw_}=y+^a)>s9RPZJylz5867G5*v_7yC8ro>b30qqOwAWZ zFlK_Y#2JJlD;He?JzgdX&MQ|E5YIQroi&w9RNpk8V8POX9Z1Usezds1Dak5))$NWA zn+$PeJI-&=MlKFWUKzf0EKG&ys_h)=rK`0 zijQHNGmi1>=wE&Eb#EW0&xCr9Z>^tm8UcZ{1-BOYU&kF7?D z49K5WEo9rm1;gFVE2PUcq1hR#qafah(s!!hbb?~k6HWBsa=qf|ki)FeNe;UiU4C|r za3~TGYhXR1gMOgc7pjc{ypDmw^|2rpOZdQuD-xhiqf(j;KNPMHX37oSpch<(Fn@Eg z@g1vrGK9g7C67(|I3fsPafq+7C?A9^4Kc1@J&vk7&F~EEVt-I+x@FL=}0`J zXzGOr{4G0SfkeK`xAGQF36#r|i<87V;8qt&i&D!G2z(Hb#Z8Gny7X zjIQg~ld?N^OoGiN^6+RwB))JUHRceZ-D5H||3q_!kdyVryrzQsU}-^C*kqa?VUUms zf9@q=$66N^03v`ReH&IWG6>NyW~#o9jDGIL?Hx&bj8-r5)rr3WUq*-T5^(DGcw|7y zE54pP!tBz8o~vX#5UONz5bQp;2&W>|sd@;~Hb?P;2vUa}r=R!eu^#BW6lx>32DFnh zoDf6nLfI%6S7F$k(LfI*RMWP4KlWW?2fm+$w!VWns)Ih^3fQyoWznIqxa(e;17BbU zCK#6n#b>t_!Jm)TcqJK7F7ETN)0SR<+g6bN!Y*1Y64{yqvVA)nVuI>Ds1b)W;sQHg z{tw5W1&CYSc8@iWPjBGZa8$@h)-(#W;}Q8cy5$(CHrv%R7KpR(h581~-A<#*KEzy| zONZ1n{{iF0OK=^{;5#O*&sA2*f7NUrq3z{ly*>cV$q@_nw#|6WQ(VCZr-G6$l5a61 zz0S5}MHu#u=Wt-4HU#WP z;n*8~hsilRr1R{<&oi;s{8{gO*I*YiJHz&&xxpJ*&u7&Y<8{0C>_&~k2Tm;^is=z!*aIo$BwTp* z38XJY`-_m{&$jz>)UH5vJ&5U_X2&hDf3n%H6Cxh47E@7Do2R1264f@5ytI~&`59)( zS2%^)Y>@o=S1OqE&EwFs#v4%GU&n!=>IxXw3nF=6N7yG^S^w_p@lu{}<%4X6oq?#V zT|3mynEYsknTKISkHSe0u!?8_jJKxzmCe4eW3}b&7xqQ8vT34wA-DFXha!n#|AwMGaZ?JSk=G=ra>T(y{Wr@e?rnwDsLKZMcqbWui2 z{F=S2-`=c%mQ%M}Zj(I~wDcB3YZ`7UZ&u=6NKG391ULen_D&> z6DQ}v4YWXmoK*AD71|A-LJeag|57#4TrT*| zRmu5?BR+k;wWPUCatS!=`_~n%T3OF`1$$<=z&>q;n*~H)Ym9UVUQNA!Bf1(fMoTb7 zBnWN$e049JkZTzf78_mK|*ktxH^xSH}loKPuYDd26x)50}R9k)6 z{s&6~vSFqRzXEseQ9SaZZtWh0+&}7_rE}T0IjXNG?KNK-{Z?*rw}(4v{b_h3WyeYK zqg-Nq`p?vqSY}~U@x)xKp|6_r9m7nsi+YUUP&uNv{AoRi>4>(xjLBbM-9zueWo{yh zVKIkpSK)o2K7C*SlksEws$n=+DRjJ74rT!)ff z;-DUcN*Oq%HeW-RJry+jUf#@2hj-vClGZXaY=-_0?k8s=hmO>*@R_ta{mI2uX3miX z_+Mbbqj)>OV>fps(jr52t6?HgK-8KCFya8jF@LBN7H=cokNBn#>fxj|zV`POvg1uC zvnwVJB&9myHuCjAEphgR3J`*WsbKWq3N@|B|25-nk%^v~LxW`0Lx1FPd(Aex#>OHc zu(jkERGT+&ttHx7umMCNpyB6v8N}Gu>8a^n(JaXoA8t`|ce*W^CDwL<8NY3fk3_8~ z0K3Dor15i@k#u5O--Ez^_M(5&?xwxqJC@h{R;4 z(R|i_kURW+3L^MsI*pHnawlS`T3d0q zHOrjAxI8Ydw3A)rZZnDs9IB}QLfhMYO%Jn)RVm5Q14de7~|;cPt5 z!JU85BAbM&nOdxJ6-G_*-ymW5zu6r~cVaG}aM>jXVj>vG5N~z|eQ#Zhw5xsgN+8kc z9>P|A;K$NR{s=845YwBUE_+4VT?rKw9jd*-$!P%l&(%&khMZ1%5uKrnEP(Y>C8iw` z2|an8y1D7E{&J~h^a>xd9Aa*2k86$>VlU1%P~7PoXDJD#MAqw@^vCM=@8_u2UqKFy&|hZj z?Qd8$GjfT)(O`tPZW4~udl2^uR6AYj^$O)KEtBqe(eLhq2)kj8J1f#R?ivQuokQPg zGl_D=9)(bWS;(9V+%ja1a)CS08PPoD7;#-K!O{Hz{V`Op*MZ4vf|9RYif><Aj}NR7aMd;;AN@ls zPE@}4otAITP|M6i7(Lax^)WsjUbrdrVa0EyVU0=9`zo$)ID*nlt%$-}#hmdJfY#QA zTF{5x22Uc1C(tK7RrEFD%y`@WBR{`OmlQiMsWn60POx54=f60S3*f?(!whn}udWeB z%*W%H*+@PwZ=4o0F}l6L3do#wW#+}6)AV?PfIF&eyqQXfG(bkakW4woOexgEP0Gg< zsSzg)pK426H_mruwH%8m==IXy+-`w+x8fgJ7s;EdV!V)K87L~XsSP(TtK~B4%F~8b zEpX;zFS|P@$?rq?bxk;WRmBA@Tk=U7$H#f?N z7!+Qlx~`$Z;*RIPiB#KBb0^U{u#==;{0eR;Y}kp$3p+WH&Vo()vaRF3H*!^(>S-;Z zNuk@g7RvZ2lFj46*MwR7^lbzg<`PtrZW_G7=9lm- z>p0+x?=m|-!Ze0BiQx?AC*^WO=^J;fjqM3Q_Oc^PUH|p?A^WbjZp-Z;SJcJ@+Ka?? z_NH^&BG$%sWm-Zj%=QzHs`nXl1S7QHczNNw-N{_XLKzEer zSUG(+LE7K(_)*1kyr|p#Uc&7qWZ2Cu5l%_ujyzjy``@}yiQ2WvFQmV9Qbyp%9XphQ z+=o(7-j}@(kvaV~Mw^X(liV!823EAjR6YqrG4(Tzg^<%Qk(y#JUorg9O~xKgc%b?<1XSshHbp|yFrZcSN3K1+fW zom+iFcY*!up`7a4N@fx$NRDkWoVk}*`X)`uCDo+I~|e@06{Uv>-A(N zZ0J~DvTQ!HQt(d3#A?-FhO(aE|IKpI{4IW*3Ew>=*pN>_$0gX&-%+TZhdpHHfDrd! zI%HTbj{=I*g|5b*Z*ZbM>xObg!AB3XKRH&@^84Tmqz#}me@TI3`6!HM)gA`>GYd1! zM1V#3P(0_V1|tHp2|GrtY{RB(bxgP<^NR@D2=z3o9i)19aO!A^o~9gX z{zqv6sMc)!2L;ct(i?$+!wA{mMWXVJTBnS(So2Ac&FJ-PM)Qnpu?2B zb;o+64>%)@F!L&jFycr{g-~&%1yKrHRt|t?y`A+<0m)h0cKG@d?QOcIqVR;!a(r?o z5+rtkEOcfn_tDF=a>duRkZK0d zv=A#^-xL(@g>Ta2i}3)}0KVFpO~kl*fE>Gbo--O-U5Ff}#kucLav?PZgke4HPw<`U z6y9ZMYHlLV9kfgQ0oCrNE)_Ex@-i=`hZ$@Bl%xyBX9FILVIA$AaaWtCzzVTKZC%Q? zszusb0`_wD<~YZWq#^|D!nv@Pxq*U9rs5X#>1fu@H|*!!H4-z8@+h3X!xH{Oj)I(n z-7cnwgdNFRYtta&WDGL$^f`KxD&>g|%KkH85*okr7uZmrM~A{k8&Sn;UT+(EwfSoW zx39o;{)XaDs)>Ig+xI7aXT9+5$!vKp^RTNKg>cb%#FFKplQxx43*eh6NX=Q&R$Lwg z(KJ^fZ7I5Wa5~F9uga_OUj4ua|7=DQksrnTcPAY*Cl4M10DSm1sqoWUu0@*^&qnQ?Y@SrmT;;~ zP^I@^6X)S0jrlJ3F8+dh1g&AHG3%X3GRjaNTMNIz6sWUHy$9Z~ z@1oars`S-1h!L6PQ0)@>2q;$W$X3(3ESpg{JwWfw@HRl> zHJ(XTBLCac(yGRrY{+>KpLhEkP}2C(4+~bAP(H3U z)QZz?H>$Y9-;A(;Qiv3a)J@{KKUAxA=w8CFt*~Iiz6@~UMN={5I%+k+tK+v?4#eb z!#ZuI`KlMdkCMJMd-7c4ptgrY(Ljg%1FY&<6)*7z4~c(KtGnid$#QsoAygvFc)LC( zO&XeKT2P@^R>s;zP_(uk4-H)+S-OJ=0l{FSN|~Xt>pIDIV}p|;_q1A$;({fq9ZsQS z&7exH-6{0W2;)|z`o?iuXeaAC6z(t~9?UglnDlX|oTuH%*Ny<#ign?ct;yB^Oj(mgnpkjiH{>h``;DfwDg=NJV z@5&AynY4oV;FV@eNCSx%pkygm3Hcc}qGumQ-+DkCp!`loC#)1$2dxoyTe1IwBUK*X z%PKVJMr!5O))z@yvZ=^zMDiZC4~g+2(tB#9>ao(ks1e$^F`E=-w{o&;!5w7&PUpQ# zcA}@kf!T1nvFY~vGQV~GR4&}@tX&YESixAi-X9lT&*d)%=G>c=!*us;>0uy2W&%DavSn@64Fm|X2j{3$!< zS2uP_e+fJ>5eA8o$l3y~>h=$5&5&256Vax4Cs0kxk}2w=NBl7EH8f zv{e~u!>@g%N1=HB3y&Y6_wZGP9>+H!@s5q@!p{l=1$Hk5s(2_n+syt^=z}Z1P~{i! z@c3URcz0^ns4JB&D;>B*RY5>DA$~zvzY$;mdbKa8?TQZdP|whttTrok=ftyXpQbedQ(+p_?es2z;+Z5I>QDDzf#%5OA#w-Fq{{yA=-m$cVaHm}T3uUh^|gWrq)(xyX#KrtuV4iH6$bvQ9=sVINg z43GSVmlDQW`a!shfA$S||C!<*v*>nvWV9&{{_F~mLoiqP!0&>ckc$?n>@yq3g2Sn3 zKBJt_>Bh$;!ySH7t_PMxh8)tifUa;7x3FB+!sD$qzf*hlHFf76t6o26zcqQaoi0bW zg$-L21t}sc?1y}{L zF5`{crP+TZP%e+kajRAuF7(e1Y`%Y7MbWUV8(N8kop2x%-;FFaNML0@U|kW^v`Bm2 zXQISjjGQM8ePPEMwQ0Vj+6?rrkE||L8h6ttpTa>#W)6%vG!h`?6Az%p_ZpV8?3>`t z=HM`TPag`}8riHN29t=TdYugOU$q)LpxOa6A>8|4Z_%w5VFOWnwxpdMtjKwL1U#Ta z=u;zKV22%dnaN%)O`h)VUo{J2sSMLIE^;@<6TmK|;N!OHEc;BN8{c;%&EO;yuU<`j z=Oq{SRB;c;qrZ#j#5~9qybtPCy7?Hl1asH|5mfUh=dvsV8~7f@+?=I_+aYOcAR z`%6-Vn|_!xjE=_*ZuTY`?OvMybT+8NJ z^tJ%Qp8w(0VTYe)s!kO{I}+5}JvdBcPo#%>-OB0`uD*E+jZFB53m*_gjgLpM#tF!i z|5dfDK-&N;ZKfrNeE5^cW$-NsAK=wKMR4XT`7Q9Jzq&oup70fWztErRsf4rw%=Zd* ziUs;yloRfP3mUS#D^N3_Y(}z&b9b?AMBjTXn|Fyqw>4#TR`9lK6}o0+$DsImh8ckU z(A5;`*bso+K?>QVd3Hr(PR%|ozHNe;G zb*j+eyb9-OVaV|@3QEK90X;d5aAUK*;>m-?0x;J%Y;JpZn)`)w3Vr<}MEMKpdoI&1 z-bcm%5+GUp9dyEJOm`3BRYsdWfXF^{71-a+SA1eHUd?^f2iuudxW9V{KN@yVYcQaw zXNxh7^OiM7=}-(?;T9@qQL$wQx|?9vpMr;pj+I=m&Cv6pPV*6-o(-rk+M_}gii4hT zpJa2f>)P3o-tL}Jo@8v*tpX_R)$I{WJZzC& z&BZU+Vf<#EexZJISDff0))rxrF-;4J5ryH-bT`T86?D3zJm;9^H30skauj7UhkC$M zWU!pHC>nL^!O^jP)f?~`@;94BP#*Nctx_9NgXV`>cQcUjD!GijRl zAWFJ+-MJEdaKKXUXwkR}DHzA5%zIDD|K`9m^`)_m6YgPvll0!9*tGzQ74Zve3e}z+ebfPo($-q>oaaG7dTJ7k+6r)CU2qu^rZpOvx^{&6oIk( zev6U3TS9rqe`&_KY0KdI+Y~dmOxp5K0le4AtSAc#KMq7SQpu=h*r>waV-Wl)d)`EZf(?j6CDO+KTx}=5|t?zfzSDjlXb5J^5v;#eGm8KrEMapXM<;! z?+M2`$9Suz#>jP6nX*K3B)ihw#FOOy!#FrsFQUFF*B9AdtF^l~jOVw~cAY(m$si(< zNE`Lr=?F=Q^Y$b7r?KMC|xdxl_R@& z?*`d`(fQ|0Th^Nme4u{+?ZkEgbKFTEHeY;jr}(UXv)*6WM?Yysg@WOT*K#!}wW%ukillPj+udj?ORjzT5N zFJ7UaV|g1GOAfhT9K=DJ3+zzaVNcG2VBWhh%fz^PtT7IMGU8NN(`C%bv7@{d<`I8c z9I}Ee|CDqwS(ff@bB=wpp>(FZLo#Vj{ue4Z%hQf^(0pmQJ4kMrO^IP`gY^{{T&Qo> z7C+XX$b?Gt@#8C~d&pRa7s=j%l!3-e@GVBBc7aruO)i&UAA?!6sb5tXI^H$=eN&F# zY-=K;n*Uw5K5AUIhJ?7Y40&DHWq3U*_M9}q0(3lT7VYAk-(66aT!kQ!K2cU^VI{M* z3Ocq-(>z(x?7YLEjCgmj6RK~-{E4{G2zjGy`?r~wN1~?n;o~TG3Ua0kk)dbMGP!-J z!)H#lM^PJdxM^a4@+r1)_7;6)u~@PZn-+$;$RS%fxsBv(vR(BzHxa~Gz?J%wOb1*O zlv*@famI;>3UUVUs9Ld9*OuDoLXw=fkZi-w=&^KGuBbjar8hLk^7-J~)iF!@+P|Lv z(2^nzq^GIsH-RwoWmMPUmDLt@&zGfwgEVwPX=_$TgBDp%(d^m8N|eN9LswP|scyMh zJ8;x%e^UUAZ0Y;IP~r&7jIX;oW!qw<~m zpN*tho~>x1WT+YSA-Xzp?p`5&@9tCEp-e$;gAJl?w-*Za9H|00W)N8v)jYAz_-t^K z#`*&5qu`mE)Z6ncEhI^HVm@^R7+dRmwJwr`t8(XmDd!yrZ#=p=orvoDoLJPSe6+*w zxB>Yjg4G!?4-Ly`aV>$w%LJck+Tk|LgjU2Os^}j=^itKWhP`)HF}rbR1Y?Udg;VfB zQuO6k4AP>t(xNTRlm@D);wt#fK5gN`8l!{9jXA8r>bQNayp{f{%Dicm$Vf>N4fu@=ASfs9E6N=TH_WUumM?5h-07i2ByZnvLS!dA7ewV zU6>PZY0~ZMy{{vaLag>)=c+O#EPItK^8pBI>n)X3D#!klI<%}{B206NC_&mGZ#@+i z@}upMPyVbQ%CdIt{@lH^q_b$Az-zq3)}1}vW}=|cc0;QGuwZk|jDN_g6^zA6GcsX% z)~U*2wvRPZ2ljER-z6IsT>5JcWwqm5>iuT<&tCV;a}-3ZiueBLgv-n%ImV0T5hi27 z$wYlubOXl~+nlA&Fn{Oa%u);t4j(s3d{7gLk5{Oj%kr_VK-nd@c-cQ~HylKn!rVTnZ(-5h z$b)Ok#9CWasuumW8Oeo`Dfa&2sVIrEljygDHgRO5TbQZb6C3@i{}$iNZ`cV(T+qrl z-9*2c>=}f9tlSp>8kkX7#=L8i&KAH^rZmCeaql4P{pNhM=v)+8P88X6M-ryx?i?{D z{EqpQ-16DfXf>PiMB(&b@GA^{N7}`FL3u_P4u5vwwo%J)eShJ4{CD{-JX{mGIo|9A z-E2?~p%dqK{tGL(?Be*e&!H04k^Opp!E_&G0AjrycMxIq0(R1tjgIg*&TDskLz_DMf8KbtN3UP=!c-)pXfwTx^>N^Nlnb1JztF; zlD&^_NN8ltVL_b@)$fUD4)=ba)!H_7ASSl;vw_|Xjos)mkau96Z#wK+NH#o$N?BBW zbtrf|`>T`9sIBgv6(>EGE#Zm&m$DXuS)g_Sd*-sS)ze?x0>3K&q z-@bAUy zKLsMq3)?IC^zGihZq5zubVC+z3JSdKET@fz{J;13vq z;q7y6sR8m|rk_69bjV*#(HNySL)T>3l)=xuif9%FHJla>ac|>W89%WDHBd4xg8kMi z`tTY-R`qz5hJVR!i61D~4`-P-RVw>nE<6T((#HxHK#tb%OldHfyD9HOSwz~}st5;# zU(FgWz%!jAIA7wQ~*ZtPp|b zrOS=rf$RGPk?H!RZRF4w+Tl~^KKyxRfELxUVV-oiCv`kAXRDIoDZxJBa2uN<0GTs> z@L6*%%{Mf2dRI)WcRpc&k8!%&-AHooAc|snnP&c$+g&SOSMc1B#U|l6C!O@@c;%Dd zQc=&4152n;&_WrXZMb@+{5*VkgDe2k?;F=$anf@Qyt@C-odSk{BONVG>_XqcitDj9 zwv9hCs42^sQus1NvLPV$ZG@lv7knkyEqk~d&U?ZUpsSQlftL|KiJeMTa8|?QUg~YY zZ0F@bODhGJ?qc?&Mc_}j$(QX?w=qZit1jTyB+b|L|6Vp8*iu1=XDDxL=dnNcC+Q;N zHw!qyf!q4dMn~4JgpY;Mzok%N|Iz4?FRa}rqO8R5Zq&?nr-Uk9pyVIzc_T%_l%1oHG*8|#PI6Z=CZY(~YTV;=y^ z*p=LP)=8=9+67-5#zLIjT*YbaY(9-)>=x}m3I<`9Fqwu=6#GN+nKX*(U9a}O=*ZB% zf=ankZj1iXE451#EwPmD!cNuC~x`jhhe2dx+faNm94_j&=3uhZH#1UAoq zW>3d1l)eS80X7%q{DwR&+f>5I1}gp7`&Y`5y$W!7I~DG?jNkB~6L?F<20JYos355> zBEBhx0|^F^b;OiC5m$NA)2ZSok1Ctw(H*Zf1$=6p*p+DG1ko;2$=mQTrU^QY=kIEZ z5|(bW-#gYLNbB>hV+Eh%GT`=@*;kY;VaM&Q9)VMzF6+Biz{}A%*B9c3f2g;AWo_i| z*wWK~zPg+pmb1b!px2L#M{@qj{z9l!pe$|KsS~J~UoHpiI4s*yUYz~c5EOi&9+L%LDZN`h2eI{eHckPs&3=IQK6d zV;m@xzgS@n4Tf8FKwPX!iVTp{%}`;*&i3 z_N81-hl4uuvDi6yEHFNV-(8Ez6yG9Vzt{Fu021gX%ZZfgsd6Jy;1wF6AEDjWV@DEt0Ia zviNxM5|KclYK^3i4hd^X*U!{1sNS>58XFw!71cVlE&e z6&Qjf>n&Et1nCU-6#G#WrgRpx235*y74_$=U(BQ5sM^3*$5PG6X1 zUGc#Qq_XW`Aa*@$_o(_2S;2nZm`Wtjoo__cdFYS$?9|OZp&Tt!&}JN8a`n>{ zy_z;Qr{Sac_p6w=0UfLCmmtK;WiENY4WQc9TxPp}Zrn5y92n?_iSGBZZjq?wd}_NM zAw`NXM#dIP zSyf+OPb5la8#x-C>Fv?bGN^W0=82tV+i^RvzC8EL2JJn|n!@;GANobFKnKN~^Q8C=W!GnFB2{g@ z`bKM5vIOX3SB?9q9J+(*@d3@QDT{rrko9?-(4TO^Yr-KsCGWrV$e)J8yC3^FRf15oYm_;>x$66Sodh>- zR~)1pWaYUCGje+O?w|oJN3*#$V@hS}({MH)vpHlGmDa2q#^diWWNr73 zxmyU`bKIM=TUyUes6(C#%z3_IUYtOvhb6cocHK8I-Ctd%7I42`NM5_eV&1OC#HV=9 zKU?FFp;+bGDCm8+NV)q~H4T#C`d&R}+Ky)^Pa85vAa3zG_hc=oJ@M_RsW`~h{a#sW z%>bv&=g#e~N3tz*ac@oz+pAli^LkEiwf5C7E5LcoCvcR)-`EXP3vs)wR)9g##E20$ zt%x}jtE8;5^FhstgshM0`YF;kvcar)EFN6p&U-7y3f*e`F3e_a;LSo0w8yI3p;u|V z>70OzB2URB@ESw%BpiCaPQ@wC5z$bqe}CXO3!n>LD_m~d{a3XsgE&+e8H?u6#2OU1 z01m_!yKhP`o~d(4m-*E(o~=MMG*2q@E_MOYjTUY!98B2C3|S{eqL%&24WL=DCIFv_ z4w96$bDs}|w5soq6tJBvH=*+PT1>RGqAaKXW28B4S1!QPG`(MGQmg`(S7lMdi^y%haRcBaLYNQN*KG@SPBU|UP@8TK$5ZVBw^nEy-k0Ccv_N5X<$z&_9ry}tY>Eh@dESW0Pv zvVM_EMJ~ml`n}J*mGk4|eA}BnuYgeI6=X2RBD3ES^*?1-kI`4cX^UDb=?F4>K74y- z0Le0~W5Q(fveNoO6`&{=ih0oWXe?h3-M^`46Mv{n=`me|c6{H0`S5X$boZi`Bz4r>v_W!ns@9AiydAP zW5RtmpTfxF9G`%+l{{Ca{G%jGCy>T&gq6QfJgUN6mT|8}K<^-XzF+x}_lV&#jedKZ>H%4BP!7g@hCMKfW_3*UJKvDNU!N?|cenhPxrc_-i)cJBMw`nO z)F*UYLvKa{Sc zX3xD6sPOrDgu3dN>_U)Ms>?g<`VmJF3l=CXo~T8PBcLNS>fNn|iGptCj^G@-a<2Z^ zWPAiSMJf@DRb+2eaRLwZT7(Q=i^OIr#=d}L4~=V6+>lqsjtyZe@pJc)*_t8Xg}wLx9sV`>N_*9 zkV1vbETyxZSFoW%F;x6Nrkf`nogh=qhsxDXHmkGK6zY4GI=BxU1cTSS)PuF1$>mx4 zcNMrw!)97Ez|Hn32V?Ws0a)96wVk=oH8_d$^L`fY z5*Hrfpy&=by6Ejx$9>z7=Wf`}$ua8sBLq|3zeRr=%ISQVtal!#vL9ovA<_BEG3}9+ zP{IA;s!%F?p7k=ByP4Rt4`lv-ln+-x^fY;Y&sM)co?yt%&57EGjmkFo)BU?O2`3W! z{Td9w=w4D~-^Q(<6ul;@=vitRpG5y1Y=Nl5WKDZ=ey}LL%kZ7~X4!W#`KDIPWx$sR zK6t5_2ThMx1o>YSCQG?$)xXa|?;Oc#P(FxgJ-0gBOxmdGw9?#V!qM5C>QMcCZp!aG zzU_D#ofDx14oJH6KRC(Ul8 zp#2oU+l-Hlf>rN2`Db=qu?gHEGwgK8EBpJE^39?l$T818FN-Aq8KtRCC!dUXywk0b zw2j|Lc2P#^C9KNImdFq3%MR;-J3A_^=QSBOh2Dr3%KUxJ+OHt$NL%$?XXsayHW*Q5 zBf&jm$3`p(V1DMPlj4SlEx1J>5X@+$ltCL8Pu>-1w6|1;tW-9fuL?XOLDI)=Oxgn9 z3iun%ZToajvAVH;fjqSAhuvqvP2IeLTJF=~z^^+4P zks)WP+&?Lw$Il340y3x?DW4eG{d7Gn-!p?DKA>aj#kZzjI{@T>ary6!@g1~}L;{c; z(j8Va0e=4r-78m!3w8L6d*t%>S8CCP(78w_P;w1-qPWJ=FRm0m0u-IM=WtIC@u%4) z9i%qxICvjxT?L`>vP4l@X{l{qq!ok-3D&haQa$Q&qa}h?BQd#7Z;Mz>_fFg>4Fdb^ zrhVRQVI0=!PH`I!HU-%Sj@Wu2<^xt_S}I7y@^g62l9Bm$jWI{f);0Tgv+I&vs4O(H z&zNwatS@~q;L5O?w$%aLko#=Ue7bKa>Xp*Y@1*p7(Y{4dzT5M)m}+gTDY#az)^(a<+UQ5W`vCt4&I|5TL=6}u-xar2m**gtb{tB z6gH)YxA0%;8I+@@`P`$hb|XDGFZM}V_7W_7CLN?Mx73#a4>H{vKFfqqu>@D1gOQbg z=huwskx$WFVYIPz=Mn<^%+_x-0DJ#0o4RLy*<8a3KAzZaS@TB4;#K` z=kcUPX@vGpC{Mo~Xe}OTl}qXK8Jf+`38h#4HIeTx=ms*)$iYDRh@{vyu|g0ts)=GH&~lpiW?1Lj>TdyS z9iKCaOQuMpR9Wble7NBDw&WjSLt!QROcmd3#)rTQMBWS?zZfF7r~-dxE3L!SD5 z%?UH#35VKjTlsrQASy{4RB`0*=>NOmP?O@l3M-t8gnxRf=pk;xRzoL`rOnRDY}dj; zEASTx$UrLZw=a;UO82Gu!1fFpx5JzcAly$3r_pz1W))W8#ocWscfox|F?aqi`NSno zks{T~E8s}NIU$ClJ+a`Cc@Tj(|3_V3<{@)4?3{~M2}#FC? zI)xjE^`*X&-I+u2a?6ky1NDWWz4BZx4&O`dNLRY2w)G+|N1cc!fFl?z7|M@>`xD~L ziu71pIKROdcTEma7=>-n$_UpfqO3+7(L0ekqDl)>M)zTpX__iNAt7v47{c39J!gYL zD}j>Nn7X_~3LrRC`&lIQ*Iw2M{t=OSzBsj|WM^()p>@IP*qr(OKj2pf=lFK88fp}a zMUH)2gnK>=tC`ORU-H-OrYBaiu#Owq;HLp3&)$ByT5J`Ely8 zOTUSD8ueFA=yUE(!_)WH>;(8Mw|)Ix?VtA}tPBhg#i4LSIFy3VvRL)Trh;F6X?5%t zW)m&=A+@OyiZnmovI?=SR3h@gxoHaKWK>$Z4Wm$h*rO4*-lCs-Kx5E|9qD%zqGM~a zmQZujn_H=o{Z|!1deVykmc7GUzsQe?sG3@7&Hs%8W3-G4HD9{)$zGZEihoz}eh$-W z;CxYMd5Kuq;L_Nrq8h*cdc73qVS&yTb36*i3_T~OTihg|V5bfgiA8&Q0*J<~xPvYH zE9w{bB?uyZIP-2p&NJ$L3}ae|dnaewFKbjx)Z{guJD6}pa!>30`?>m!DLRZ&?;kCL zb0q7^>UzJsu#S!>TT0zom9wo;w7kGAT%7{Ph2Nz9W@lB0v@UkG$F7^$pj`XB6V8RVWCh$h)%0ON-L!Aogep-=)`Xv9~r zmh|R=hZ{>^0+G@jT-U}mZd9?!;$_VeP%l!1NvI*i~vE!o0cc6t)*wWvm z0CQE>ww|wyQHOA}dh8rcqDn!3w#htb8XyWLPAM|d~S&(wxvet0DtshzLG)cMLDWfQWkdF zf5fY(EaoRWM12iCSIAy;b*QTxN5Dsq3|l{e5vD0t&uWaXvi*l-jM!*RKl@Jip> z@@>VJK1X_uTQKTn3Ww_3g5fom6j{2qrLltwC8;+19rRXSsghE+tA?NPfLcjs(Fmbe z6VNP2vJLNMlNiah16c>(BBSlWRCol*2+IR~!z~K*UzA94E2i0cp>MS_!6_ROfmg_u~slPrw6O4Un(qKU5(f!TxY;i-BpT~pI99` zB1=a+1EtmEoT3OPLl5{FO1o$M?u2);%m30!nxMGdipI|yOS6cF6u3*HsozPsDagfW zQQxR0%#~P&x$CYpK`q_+kS*oz@Pe(W-4!p7YU%QN`O@Nyed*=?!80YA;-Rc*XM*04 zp(v%e0F8e@VDVbG?`9X&*Z4V?_uiyd?(yoE%~Jd%VD?9?{TuU_^#l`rX1_u^B577-dn$N}X)VB7OFvD$6V(@kzQokkaf=2+`UFyYXiU7x zyZF*Qf~~fo!mLpE)_Vm;_||tGFfQ5`lIE*Us%;&mwUiNVD>tBYcop3BfndyD0gUxo z=GcU?!is$aZ~{8-9-?nOy-K;Kb45Lik@7L!cMS7Ih=6@AGL4(d8FdO_u5{XEms931o8{guX#c{eU8| zG`suBHR-8?S>5Jvc&o7Ghe+2XeZg%4l;{o{ikp%C+&Wx{U zP|xQY=(k}@J$vfGhK?xX%li&fvha|~IEZ9{omZ9{}1N2#!$X)jw+ z?`sUK5i&r78xzYNcUmjvLJG_PTXGenouP1PRUHTke`UYLBltqczxMStzU5o%yfKk= zE`~Yzxj(hgiC)UlcEdu7V8p8aIhKV!kK3eY^^<+mt5$ptr+*+?8g5o2rEl&hoArBI zFrWF!25j$wD=B~nqvtGSwrci>LV%|9g5E0 zm*inTrVl&{bu&8{kSxHkhaA`|j4{v=?b9yw zvdYMz^s(iDwh0t4g#3NpQy4s%8ExOxWDm~9(uYb#$d`QbAr+|F!CKIK{{g#i&WAqk zt$_9cx|pF8u;=CAPOUX}-|^TO(AG;>%m2W!;*63+HeBz-#igJyI=e2^>VOKMQI%Po zXq^159-t4vb|!x**ir`uulsOF8*{pI)DwZ2E?JIdkq-|Sl#*+H6^-iH?c~tlluPBa67)qkzD4MHOEGO!k(v(Kf@kzCc_#`3;Z<|M&Uxf zrX|RpzfjwvM{)8S38)Ae7j;Xi|I&8*LAKJIeh>n^|EE=@mk^m?;-jGCplgJvbD(~a zo(x*^;dXka7kfSOWZ+4T5+2#{UmudEbwoQ|Sj42hYD*7)rE2tm6;=HwMO&w>`7Cq; zHzaW+byC^7EQLpSoj}SH1@@! zuHwqDf*+n;tmY*}se=YavF$x=^2G3gV(_S33muH-w!Jd!voG#VbH`?XSt*Q28K?bZ zxC6Lk^(S)QMFmEXG)HT?2dLLp99M+ZXX>(GT)lZkJ|i0q!W@-<<`ofu=@!L3|5R8% zg57*wC@e+eO-}=u0Dxeplg+nXx!hagd!1_5sbNZ{AsbW-49mW>lToSlS!d zxwNA7%49+PJs511SE_zCiy7p`M?{aq{vr2&&_K)s#vk~K-xjJ~ec$^?^@nQMN;()x z;2$M?^`FXv1}w3;S%A<(9=mN=4!4Ytr1LEYGuz;rHXGz9^I90*BdjsT#4U|?V$%@) zg}}5v;a;WoF^#eG;Au0X%l193l_6&#M0`yQo5P)%Rw~2l9_J2Z>}0m6P!S>UOhSxJ ztHpbi$!@_p4B@gN<*sP8@Eyu;mrT&^0Yc)UZogxci^7)zTV?VWN^$7i)9tl7<>|d$ znTaE5Eo{==0>sT4r8tI0Y@7n<=Tp3czgP9S_yiZS-=v=^ikUI)sO)bwxWQ>DOA)Lm zJq?v$Ft1bsekpp#(b+clR)O5bv)?^u-HgN)Q@+O^#6$!;HNy0Yq3RTGsJHN>LaWS4 z@?eD)OjHgsP~Z+At=p;GXB!!aU}#soi8J@Ls$;M86}Bsc6VS*QOTW$A!+A0ypp+Uc zOncuY4H3GybTxSMBJ&Jc#pzpacYH;-f0}G!XTyizg^ys$DKJ>9xr(WK4PM&UQfqL$ ztz`1fRLlXT!Fz0zc&@>F%$lae$q}ygVoQ;xYBC7&bJov~hWBid@4r3PC;>O&OqlA! zhbnF3!`^q$&0Tr8)LruQ;!h#qCC%&{qiwOB1;dShly{!j5c)LW@+M@8`Vo}#ciI&< z1fqU7$2BZ24vO9$ApC#KX%x9EN7JmhAGUeFqGs!QeKv2pRly&8PIn`l9ctaWF~t^^ z1?dhCc??dO9`vG*JvZ8G%HGY?$2>+F+{%#i{*u551f&-?xKhw24Sm-8Xt1)H=ciP-Xi zw!9W4eAREkkqyQwxX(sbJ66bs*VS3Z3(MGZJ~yeL{I)M+*G@&_%eCA$E1e(YS!HJq zEy9|{wngn+z0@5Hsy165%eq$zcW<<-zLxc81U(q3%`ssicb@hyLsq#brM5=@A!_$FKxv(_vMR`Zdyv<9#l*(LoXsjpS2gfJ>EjF$aFJ2SmCvz zT;lV~4@T1jTfe`Mp!>$VjI_gBlgOsXeIUwv{-0hkA7VIa^dA|g*OWbPgeZYsFL3`;Q)mN zDMc5z?LuElt@M51bwc5TP9432pexT-c{|Kbb_$L#wmcwM=BjeW1V4}kE?&0N!_VaFbv`>SpzJUF?bQ)(z$6=6 zv-rlI&YDwc4XYd9=IbBpd0$7=NJQ@Wkj2bu_(hvQ+1#!7xubM_lm>fo?WE9cf)<+t zwJNM5$xb=CCRC6cHcp))zt3t+&IK%%>=_F(>EScHD=q7_ut(x^ghEM5m1455d+mMR#Wj$+Bh`>1@VZ`rHdQX{s70>t`j>tI z*F2@47KEY%n~$plqD+rbm)v|6wtep3|DLuqN%vH!5qP&&q<{ky$W^<8BE|MF0bK&u#jf~#x3bepI5cm*d|?2n^{{JuT3df@KbkSAl5dE zd2p`mQ9mP8y16l{HyzOgc{>W=EnsGDZaZ@8{1)ey*K0gi=s7Y*qu1x&;&62%zJ*I3 zQ9-$Tm_DX|r<6Yc=Ea(8B=fv*^!+pbrv#B0%w)iR78?}xPQ}HF-ee8{3S){tb$pew z);n%4u=s>yy72>b4?{^xwrEJDPuPHHsKPxx13<4s>6{u$>*s2XsxvxPhUWc7Ejl5SN#MULOIen#e;^N1V~*rvu)TRCuD=9Sv6|?7L`3Ua;}V~NW2VlObma+ySI@fKY?fBL!lI-!m}2Iwg$Ka0tJ>hlCj-j$A*>#78ysr_nRi(-FSb52KxcI z5IT84#Y9cry#AfSH`~^qcz+*aTmmSM11ysV%u#eM%*IJF$(DwrDYw6(zN;5GEnJK+ zab=Xg7lZJM=Y#4L;|FV8r=`;hLC5<0`mmxjj?A1$%n46^z$U02dWLk?%yPoj&iw_nlak5IXU}xF91VO1k2f;TC&RrYMY1@&% zm6mi07#j4kE>;Lv14pe^nQD9~_gn_*|DWy(^b4Qrow@k(FrtI>k?gdZlVT=yF!G;4 zT&!0vM?;b8dxF4(+Cm5vv`4*%ziM-WhE=F0iR0NM6<`@OB=|e3Uord~T^~)1K}zna zhhlWfp*JXbeg>`uiXd_zfhhPFrFCaO|0(@pRD`(gx?!PFU9-XAeyEzajgJ5uZLCj^ zC1^C2tE4do_}Ki@4FjrSA;xP|Se^w!REf_KkH?{^qTrA$=xfAXmF`oF+wd-dHNOW0 z9>gEf*E-+uTL0O_F{h3;>eXS(t5{2o?|4y$cN5xo6IMH`#ONi^e3H)b^#!0zwC&Uk zSz{+ACh^SQW+u0K&a|H^NN`)BUGyeYQBYaRhL1wV)@=2l@T$#B)C)GK49U6OTbT}) zR9*JkE-j{v9v0lK{Ke~UzD%PQ*q^4{o8{?B;ygcH*T>XR>_C`%3SbBORjNTc*Tb$q z@ao{|#?RW%(2Of1RHI7?#f(Jb$EiFJT48U}fkE#~uKq5(^u2{UWj;R7oiJYrRuYz} zB5sG4f~t#E!fl9J+hhV2ylTdvy6M9Z4&O0~*!!wIz4WyIx1I$hz3hu{LSx;Ziquw( zy{=8G3QRpOdwslkMaq2?4^>u9D0`D0ZzwsG5CMWXtF`wbfO*=72BE81Dp&{sj885! zo-kW-l)*wG2Z2{}=)x)(h^4TNVE&{8Kml{bPcl+MN=Nj!fj>##f;A;qS0fOfj8daN zz3w8-3btYfLQIY(u&sWM4rXR_TOo`gjiA@0I=E>vUe=P6^E!Pn9{MMn0|xb*3qp=E zzs#=fG=7SDlQSCTo-MuXpR9AUr@F|_qGS8}EvW=?G?k;}_G%h2irU0zez5Hf_;xPfP zuNU9~O1YOs-7S%3Mh~jLg`68nbPkNzl#Kk4%)3GN!m5VL#5|D~pi*7fOSYlF4se+l zp=Cd-N`G&KVX<(HQWM&(Lqd z!qmwBeS4nhtGePq-)%@Ap+5rMl@ldR=M*|rAN8!pJJhzTcBwEQ<>U4V1%WCd29!j# zNQUDBf@qo}h4siXp}Hj)MdJUU#Kcmyq_y8b(WxFM*Lw}3R7br<)2r%(%`Z^@*%k5URO>4I-S zRdoUY{ywF1j!0W4m%u={B!?)Nrkgn3f1rri{)^;Srx~0t3lt(~Hb-5S+vzIOl1b5g z=4Es}&(b`Yn0QmhCS+3$14e>(BoQ`sV0=sMs{QmgS@M*{UDZ~z?{?G^$wmx>30Ck_ zAc0&d!wtMp)c>|1^bTc;9}s5$ZIqStTHt(+tb=)aUU=W~7VH)tn$OaB{tJcOEUOce z(oD^XW)<`}9pKLY)V4Vys_>Nu$d&ukRa#4VnvdZ6n|jnSzK~c#1_!bElqTE5WmN!r zX9dV6cS`QlbG+c$D*r-nP*wNaqRantG3a<$t4w)MOTqI~R-=<6`P;5!ROHnaG6$Fa zj;ZUC22NrAS@!Ghqu%I$D6q;lL{26#BNP8QB0=86BR*Edxc)W|7DouRg`$A>lgOaSy)8eu~?tgZ`EN4sjLL9tA&0e)Qv|A_?% z7@ePJ#ZkVMmQdF+F|XN+J-?$+e^QYci7p+9Q9%vl`dq~I-;z7m(+h+J=Dr@k5E$ajNgZ712auh%oB95IXsCCd>=~R><26Rl_1u%YB&z> z8kfev$)XLr5|WQ;TtYr&6W*a0^LeSD2|)M$9VE3nl}2eX@V$C0CT%IGZUElhey*M< zc$qF_f($i>Z2P))Q90faeD%&%AdYF2O*E2~P@e-B$+XsRe5jJ;U2zt%F-zU)&O)#% z-c@qXmt$Y?862CJA0|jupy1U~Lp3)GM1^Kj8VH*bNJe)Qxyk>ehs|Y=BMxJeSn51iR#>YeHZ;S*Mz7awbjQF*dJBdRl|SK<&CC9 zb-!oJm2*3OBse6*m^^dWv}#8on+!0CuEcjn_Y^c(?HneGVs65ioi~GNov5>~c4{Vy zKPf2saIL5|;HzxpV46ew=ZBxEfq?opgPb^Lr{4q#^wdn&wazk!8Wn+Dgz;a^v5Yo( z<)LWg8#vwYT_oo0r}7i8QA}@&GLar1L>_dDQ$!859m(lheVh3MzBha#rGWF-BBw*D zr`j)H><>i3av_J`Ed@PfD>fAnwt%dLn8Z|hqNvNVhS@k9Bi#y;^TOBI{O|?Q9Q5NN z{ij%l$x(vO(j*)5Np@%1Z{2q`7W{S$5F)rv01gGQ1XHwF;6|$DL64Yq5#{?b8hghu zLx(0$*kvd`Eo8Zpq5pDLI`vPry+v)DDQ<~Y)J%kbC#9~LqGwc_jJ}pJGAl=z+7swu_vOl#n5AB z+0SujQ5HL3uL^p5z3sKE)Q_w+e2}nyEjr6}vjx4|?``Jm%~pxLTKxWI@JRdwXdeWk zc$--<*(jLSw0~19U(3GA_jhB|KKo^^^^7!}d5aEY?VO^aIeL;_mrGGaet_MU3Nf!P zP4De2Wk-0h=40j~m+#6=1O99P^Z1z2B|U&G>Xat>T`%v2A)XpD5SlINePTahs2ZZr zC9_QZ-~wazem=Y|5F~tdxCpanSm&RP>g=}e(k7%S_wNVn=7)qT^c&b z=x_(sc6|g8;{6lST z?(#jNn%AU|dv$T2WJsgHtiNS*7Rq$CkGsymVHi?Zs%D>||3kt9&lqxT#++kvZN~KN z8O2jH#wuHD$`g-}huQYQMPJF4bj-6hVRP7-ip3sH5&!`E3T+DFo$L%y5<~qmw3)Cw zjdPg$Xn~ZTJbVjzQ)>tl7G<0(%Qz1m@>*Dk30x^O4@_7zxS4};w%5S@!!lJ6{dK1h zQ;R`yrMxv1SNK2#(*fmc#O}m7a(PrK8v(A{CmK^#YvyQp#o4K-II#ZNM*Tgfn{RrN zh`zjuBTIxUZ%fIX4?m)B)2Z<5x?X#bvM%V-0|O2Sv6by9-N;n;IyW2yVczN2T^71b zw^20)GMP?yS$;ds;?g!l+w2iS2l zh-sQ)?B5;L$FwEg)6!ODLvSF+LS6-1BF1NZ-)0)7gp};{T1NgM2q!hNc~@p=us4tw zD^0@wQJfnqcPn;n2kI!XEmvW5(uL(WZ|{8;@t@PEM)y#J8?632@LW1x&-}P z%H7K5_{KTVKbB%YOSx`F5+>}y8cPlFN~R6+ZDz@f;oaiFLQOMfqCQ(7aCbavkrI*5 zF|jLd0)zXDzYE#!ur77NHN+5>+^k1 z-^(qwTiyPY!$7=x&*C>+xl-m^nspzB8JuGI0xj!p7F19|pt|!-lUo?z8Q!2jOP!pk z{?bxj>ooycKRn&0p*7}P-xBM25%Wy2?rzNqY6BqEz7ZeE*k@b^$@wUWl||t5v^9~s zhe{*6fv}@HHs%BY#b4*I>pGn{5VR!XF;*izCw@g2K(oA&yJ^3Q! z3g);gh;aC0uPAR?2Q#`Of5G@y$o-nVTg8bKOC7YS*Uvi*Ugbyqm@=R?ex5%$!@wyD zJq&xl^1iTrKgUq3OEw7olU&aKvEujawSS3pSvdH#ZOw<~<*W~LUWF9ScLEB+QH%vY z6PWNS;aeq;y=t4ZcOfSj1Uw)5i?2Xasx&$tW+Zrkdr?kq>i z5vnvQjTz-lpy!XSW{xe=BtA08yPym_Lc?~|@6Th^c?nqtAs_pK4FP64+U6 zU=jTE8Vs+7X+;16x>ZgNZ!k7{#QO?e%UKTE?W6nWKnOfH1y<5VpGNOA8G!z=CHb*w zYeLeBz_#F8xLMKuxjG&EwAwz7f&Wc*+l2k8Yjd=y%zQEP^Ge|ZG2->+SU*TKDK_r@c>z&$MZ!v3`N@bzA}2I8buGlJFqmgzl)NkQzxB z$R4d!H7lujOpm;vysgC457WdWFX*Uczkt_@#D1T;4ZY`#ewPQ+&U)U+YX2UCZnXxc85yqqG!tvpKwWM{fzL6b;T9D` zb#}04UEHF)Ufc6eKuoOh8p|BY{!FMx(sQvvgzyml__o+Z;-9YCx`A&WuhFl` z>$DV|q_f0)`zIKCOv!OV@0JenEI~ZaEuEaOAI(cK`A!{FdhKJ;cxm8FIIZasXebjK z0+)g+(uoO+=lz{!^J5qtD{$^np5-VtMP>K2LMRJ!?MPm;H`R$xMqT*+?q8V-l7N%w&L(z@^uYeG8hZTK}O-;T<itArYy@FD)~4}*C=XbjCS;S%TS zy=*lSmd0pUg;8`k0`bsci_o|E98aFm#>|!(%+@nf6zPK?tU%TrW-e5TNWE(SA7%sU zrN7jbH6}=L#LFytRUNS17>)C|d;;t;#IP&|u}!9qaT$eH4TKmYJpmnp{%-tP`mg|VPlE~J4HzPtUH;Ppr z>ZR_(QLv7v0%H(j`OF^4GLWahl6FKSCljjKIZ;%L`reiN87ue1C?)yyL^7Ft69#nb{Bt#G*uC2p;n80mZo2xdGaMM=W`MI zy{dzytG}JEZ;q=JRY?O;52=gl_mm9)0(^wr^UZ2DQcztT!N2%e*gM_ z8Nffhp!$zumfVV3hwz46Ncj&0cBz;r+AdOSxo{V4k{Xg`e8t{48qZ~>$?)9N9~zNl z*k(9R>n-LsB1E6pzRs%N)JnchDEi>(Z%648_q3_&9cTLnn1(F+8v+i*bx9t@b(wab z=s&}=BPPO4Zf;B0{xQkpTTh7E5(e7272@7OSW)Xl5aw3TbheI)uz3S72_Db%d*TH=GSsHn&*v#8q>d>!#5bw2&`kL=|V*JDG5dQTXj}QLj$PA z{Hox#s&G6dT1po!Ay=(gEH3yMCn`(R`{-Hi=1Cegm}2iYnQv;89k*c0fjB-zHqcvAqpa6kF(DAUyt!*ePwf#IT-!)=n{Qb7k*d7W=-WCj*FS4tON z=k>dAV4KejrezclATXw|rhEL&7{)jxii;2Jk#gU>pWLJq?|A{9{fK_t3=BB#hN&d| zAj2k`T0cG!g&}qz-3(MeY~uQ_C>CMh`m1dS=GDWD?rAeE;d<}P)ZfzII!3cA)Ok>6eZuw#ojXUATt{nU-Bg zgc{rj(ODLZysba7ysbM{5zUfL1R?Yx2bbE|5X)Hc4ayr2a8H53yE|(kR}F*pRgap5 zsS;20p5ClXAtdlUz{*0d#4uUIa&l}ViVyi6K{vUHQW#;0eEiADB}uH$E1AHyCVUf- z@R^+k>|af3U`0&X2)cx6?Vp^RLzzHUY!9*78nVp)J309XIC$Fx3Mt|~jaZk=V*8@e zLyo@{m(1NXhjQYbjlTL`-cy<`A!bjrjR%*i8hm;9>dC0#S;xRX>7VDC$7qc>K^&F> z`^0@EF|xbCI!e8Hs+Ar|Cu0-N*-efT3{chgCeQef6QQ+5U(&liF(uU-3zM2%ilGjq zU1NGS>OW9-S@Z%W7rpNN)YFsSEGB$@aNDbOS#i`A(wA^(-++PsPkVB?+7H!lH@+mC zT)kxTUCDAIR!@U8?dbS@bqq}^HH>T5qy#G&aS#oc(YHvhhm1O*_ZR*{yE?%(WuPsj zXTS=gZcI-EIp!_uT<=E2JI0-@@eU(ebqAvm*bfrG_UvZ zNqVN$#|$v{JnBu49of0ssY##w&@$mA(p5T9vk4Qap<Q0A9dN+!}vLQ@^RE;EB# zyf5a`XHuone*f3exrZ~||9^Zlwy`lovpJ5*p_ay+kHeZns*y@k_l6wGt&t=Zn+?OT z(Lqt>Q0dOycdEOjy4xIAQIxwVx}9?4mYY(kWWRU6uB$)0F4^Jpe!pJN=i{+T3ilzM z|6g-&4DlNDrskz&@`vfq!~{Crq!33I#p6iWjENhl&hskz(;#A!8p7D+XK19b2kq9& zn+bG9)+p2`nT~39I)YY>O{muF#xeROP4TgyZw}S|b#L&g0%wa@%5c#pSlHIDWgd&X z`HQNS@p4`!$ZKC3R``@vHV`L z-Z0#;0{_l-w$EvF-S^Sd@8*Ytb(P4acit?AhLw^rmqwsxY#CfG$38B{!O8VkfNLkU zMPs*bq@7r9oxktw=m+yw0_S+`R9lhl(n5#fzUg>kfosu2Jocs;kg07v2}8hwOryMF zP_c10CGQm30xgPVFl+Pi<#?ab+2#ffcqKySCeloD$1#n(u0z4n>091!l?3H}^&|ee zI^R|Q+QfiJR}%R9$X8yfL6TL#X1?&wKc#KUF39kwG1>sz`Ouz%&XF7|>L_TP{0eD_ zx+VA5*$yX9jT-Q~NViMf8GVX5`VHef|G>y4a{xA_zYvthIU4&B(gq#XF-_f0L~dCw z=?KN!&T1xhoxV+wL8UQEY57n??(5B`zD5oQ{Z`}mlo*K7IaF@6sj-XbE+D&(^{bz1 zd#Gvpa*;)QoF_^w#N#3(Cv<*0BLU96|KS(T!2nq6Z4TK(1+;R5!@}#B0(T<&M0P;w zu?C_5hr#vQ|*zbIbHtrUUt;ACdJmB0SebY7fqe-gL|btGC^pFp1DZ#JtL z0tnxgBD3czO)a7wH_!ddEsfi_-V_G8Uu&n@U(bYR%Gi&ack{6ZG-%{qQIi?JWZqAz zHu-epV{e>tK8Ci(U(nMn$;L|tq{p=bbRPX6{=qut7ZXij|RCf3eMf~ht|0r z-=5LJNoNN+9Yz$A*Q%KHz;`;%-okF`HxLAK*ra$hm zz6Xf}oBd6T)x-6)bc+#UhtjQ_Md)RHFH#hQ-MCtIvc%i(J15Q>fVb2&`Mn)ezLw!@ zTb%gkwlrnMgn!Rj87p9~EnSF_%I(vCSEY6k?9QF#@!WF8w8PU;5`B$wliN-~r}h*g zkJtSWZLxUUqo7c@*e@4x(Tb8V8GCVkWUq?%QQ!8*1ex0$EbZ1l|0eZ(UT^W_vT=b% zhc!X8_U80_ZmVXzSx+J5pkxBolOIC#L|r#j(vqZ9>1~s+C7QbmM7jSCCr|DV1%$7p zHF4G<|3Gca{*)NUZz%Guh1Zq|>KfUCZ!^B}fsh8%_%Au>$E1I%REz0mcxef){!7!#jtCez;4*e?5H(in8X8gK=0qml)&p{eHV1b`QsAQukJY8 zdEQME-Ch18y!Ywk(ee$#!`uU_Bw!DtXX+;UwCJmC-6%z2dGAdZLY2FEKnUyy3Ecu2 ze6QqvGq-zBDkOc!KJCI0m=1M+0lqhd)epn`twOgba(Z<1c6KiJaFe+9#{eb!A{7yPvAcl>iG^ih3TRvXHs zG9GLnlC<`~40qdAmz=R{{#HSE=1>>n5?hQa1NyYItH*xSC@YuC@V)YDut#3H71*j6 zwLEw}&gm27u&TniGziBnp=D6Z`D1>C;E4eq@|goF8yAsKreC8s=~VP;v(;<9kEh0~ z(i9z1mYmAepy)Z~an&+C{I?_0e1ecya-^SEZC)AL=5IGM$Q`*yH(~Ovy<{acDm2%1Yu|s3RC%@<;*dQD-K}f@zM7-Id51~@orlS zNOK`0gny6(lSi9U@@HWLe(#~6F49I@u8e0yiLdVK6c1}SqHsPem-A7RvVhy5KH+_r zV%h0>!^Cdf_CRWfHpz+8_peRnupa?9)1#GFV*P%$lB8yAHJ4v1@nI)Enh$6~t1ynJ z0XO*bpDl;wIbB#wy&UPJ@*QwzUHy-Cm!qEr{OPe<>MMhiL{-< z%bM0@1`)dYtMyw{#6^bzN9m^FoWyRm5uPfQPJv}}tDmO+CUDaNlEagh;6Bvu>*lSb zJ-5z)M|P~*WXI=A?i1VI+nZ!7)``0RX8up?)Gk;*tQpx(TPMvuMz+mp`{-!v?%*F7 zB~4xmufP@@l=R9k!9hFq18|GG*gG>*4UX0m3dZ5L)Szd-BfDI#qqq8_+u9*Ds^&*n zX4$Kx#PY}=Wj~P(052>Y8&SonLvR~Fkq#zwC|LB>n89pWe_K|N=dkaft)^>35Pjcj z?yFs*ao8-6sv(?81tR-y;YRpsJ??`tWb7Mf5SC%);!cjSP2oP8 zcq~+Pk(}6~}Q`?fI=SrlE!1eIvQaxT{l+mL;WOOr!C`dazq2bh`=8-3sKF z`ZBU(ogtS_w1`hw=_cHg# z+K+9(hm_X?8LRQNXcdo?0hamR#AWlW9{UytOtm%?jGR1NI45bxMmGsmiYMQtFZpxo zNB!}{;~~;4!k~ETOaCv%up4hAFWh0Usbs_3#BM#TXd3Xcf3NOda|~?-iRqE!c4aov zw2UXMMB5RJsCHy@#P8}24s#Yeyp@o&C01l8G1*~`*cQ%coLJqC{}QoiUS42F!8pRy zKI40~EA4=zC6I@8zg4J>jv0{D!aLt5UWFu^41_YsyCJ*iZGZWHYP*oveNz3fvCad| zNmaA^MP4SisfOkjBhc{qL~&=BY^|lN7WO|~uHiY-YK@!M9!*y8E~e1FaiU7>6+r?+ z$&#+A6}@tE+CsRVIr9s*Q8f*v^X{@f%4Q1osB8ZsHfo?isRYTSG_*;kW#R+eV)rS8q5`PGkttl-4sj!ghhCOML$)m5u#w))7yO2h3x2!}%$=kl9UXKrU7U z8QwtLzPkNcbhv4!FvZvXPfJTOEkyVS%G-@OUfss&K7YK~YMuW}X(HRoB+<&V%4jvN zle6TkAf*r{1-e9}AS=ek`yj#?_0!L+6 z>WST2)8ZW5jFnvH@X?q=qyA5dTo~zb?F{U#d+vGd^?Kq5LIr&0N7-vOZBf5`_rf%H z*00p8Zl$F28S4wpx&Aw9k6Ycc=A-w@aknowMw!pJJCo}Ru!E*dzokou(TR{fZroGS zV$vH0w*eC1TY9Tt{W1P0m}Uyu_yz9*3LfwDrLI>1^gR~nziV$2`e+(QtSbAQ(l|79 zOk5my_an&O(oV?vV8ApSoy9LaOOZMavM&aFup&qIC2{)vCf4kIN$Ey@edYA}bd1DtPtY%_?=)ViXfzk4!cx(6HcIzBAyiw(s+;W`2gudL3 zhxaHn7)}mxs2I_U=Ub z1fwQh6037P4EsLoWCGf>32M@!FC&97m_{B9MUs3TI!nv=LXbZf-bZXY-`mcV^PC`e3e-`- z8OB#vyJMk#_mcj>;FQ`vA~6IW#*yKXF?mF9Yzm-v; z6sEfP!KUlx_($o`EU%c#0Fgpj+BF4b^z?93$dQ`oJ?d7}BLvs<+k|T+4&~xQ;=qXh zgDH{6imH>v7cjA;f{i{dMeZtxC%^&|pxqts>f%bgXWFh!gAUGl4tph5{*2+kX)zqn zzBaRKDrpfri?e|cOWSPe_Ll`H2qUU$6yJDSeG{5Z1=!bvd6##zypbls{!U`G6hRp9 z;Z#r}IILu=Q;V@ijmphf#gql1Xp1>ak0uHHVEADsQl<#w*i8DC^Lfcjgwludcagq| zd*0Gn?g4Nt!VQ$FCAPZ{eC52#Mr5R|<^+0QHUxRuxQ^;1{_KA42oDx=P023AURO1f z^S*-^oqQ>_*gSnp=KH*LjCr{uQ;p+>ET)k6=q-^Po?75_616zLb;s(G1}s#;Nb{I+ z1nE!i`Bjz820dk}F{}it$rjy0s88BPG7{(qn6aDfgEZ6_L`PQH21a_GGG13Jo&155 zfbA=OvJW7PuNFxvk(TYfXe(=b^e5HTnlwP`G;EmUCfPa6OV1dQ`#XYu=g=GxH4@vj zM6IzIF;Cf#kH-0R27fS0^Y6Tj-75-Gg|9#TBZ2716tD2gzpPTl)%#f~30AN4mF)bD zj#3S;`*URxaEbX*(SN?m%xv%C&7BtP~g3^@pilA-G;K`N*njZhVX#w8lqqsNsuKojl z;l+J@{{d*q_fju?wY~c^Kuo4e^<`tbs>WBBv>@exfnX#P1KZENkktwCHbuGUe=r4C z9ESFXi@KAs+%UL~w6yE5Mfz5d<7)Y6ItZu^oVEI*rGm6+XxjalVkfv4_A%FZl&Vz) ze!B%_g*xfRzPdT$Ulwl9*TPC5x0gbd>ynQN@c)Dv?Nk3$L3pV?Tzcvw%U$jA zgl|+buSFJ`*BQaEI+mWq@FF1hpkOt-(bJ%wti}T8`IM8>etx10s@l~A`p><~Bgm^c zovla5Ztx6z_0ntztMb{ms>YHKuoYVbc)4iMNNMD`1?mMYA&SyWvI<@F_F9BPBqsMG z)pU~KW-I zQ;x{J8_2e{k^NosB^b|abr5d{Tux9VJ;3?IgO~GtId8j9}aYh5Qu>svT zB?s~)hr&PaufiVKy&I5SGbolxh;yn5W7r8WhB%pf=2u18mrZH5X+~BCE(>lfxz6a! z2T*@As5&5vYJ}sB|5V4WTQx6cEK%Z#kj4hHAv@Vzmz-8N*3hcq?!%fXl-i4e=mX55ALs$d;BdiYg2<`x*;aBgOwCk&AX1vxG7BK_OT1P4G*sU-8`rH)93lU0JJ?FG3h<_V4@%I+6bzE z@24Rz{>fPmRk}{6krTQ6GqJqSAmfer@O*vxL+QpRF+|-vV4v0#>Q+K}P+wKzgXbnY zkzik`Ptmd<05{j9ZJo0E6tK{Gf!jU$yg?nJd7$|nHb$+`cD>jDd+8!=g{fB>NB5fA zB#_B-d*8F))gQ~nj>I?Qn*W1FgksJ>{0!BqwuQEJv2kQ3p|ICPFu<;`ah<6Bw>?4U zCD%{uMB_@1i3TpRvWF8V$9B8vo1XV>Y0@aHx5;97CGmN9_vBK`?!QHq?9Iv60e5t! zw#9tX|MgRjlWUvPY69L1@_=qCG2khFO+I~^>m(k=#UooGD-SQ(RF3@5vA>Dhhr=XDi009d3rI^+z zxYc)u;V$8{Y&6YvBBuv?m~n!wcItas6Bl~e3TI!k$<*yBN1z{CiJi3({jgx(!>`8b zR4bzD7C3=Lhcm&m(&-P_8tAtF#n2Evjc3<5$~`TJzTnzEH6_wLY`g-|5mY~XOiR0GPLi4f0)rrhd_1ZKj*DwUO?CFx|wuX1sUcwL7x?;Ywg zGJa-Uve~rKQPrS!$8Q(8BkG@scEs)zY4=OdEET%sTCWD)ykNxUz7VMz3UK+m8`W$L zvHqfhONo_xDVqtBbpcjKN2B-U*g^Q@QtDXAc_-yMqjLxy@*9(;e>+dU~8fSj741qID z*aG{~SG%2xNCsEJ>Q9kK4;;$N-}DU&|MCcLFN(;a`WvB_A8X2LSYT;wX64DIYE3`J zH{V6=2JFil#=mgdL>=)?H%yvRVUDQS8GX{3u#=6Klv8y#{X)ba*H&srsITv4r%_U6 z-&B3Fc%s)e9yS|Y`NY5f{c>4~&p@_AJdLBF%AWr>XqG_=f`f)AzWwIdI;eccvq#~n)< zzvUoruVxO`JtB3l`7_bKYN%9UyvlSYoy7 z{ejlgyo->t?WtCYj5M{%VYxyt3I93ZFK!d}(b(R)POj2KbSj@}ku$bAy=C&}+(Kg9 z%<%=JGUqzX2m2d~r)2DSUP3fp_kvzzW!o@(qdvGqJnB^*ka*%aU+VHS-%9x*xpoYC zz0u_Y>;c;@v{q;}^dfHD?;dRo-?QAW;6B(~9;c!5;TOz4nQR3sYQdPuwNCp66GPd%R^J&TsXkzij%4 z>SupC4&HM8D`t_p9JH@N2LIIsaVQjVp;Y6OR%WaR$fqpfNuj>LTJ)cMrTaN^f1EtZ zuC{}49x?W029I6jmAlr?Dhxs!wSYGj14v2V=kR z6$lk~mtXK|om2bb>%6sm$vc-uK1D=QP}1f)*XTs{_>-dq-A-9a8jjk*Hl)^>Ac6ro zkFWQfeD!3&~uATcwRifKSx zhVQp~lG70i$y%V5@DW;C=3Dg66)WQ7+hC9jpgOH`$vkBF+Mlh+J0NN!qGOd zKpnV)^B;|O0C%0%qyodc!ExN?uUs>O!SM>{w#t|e zhf=3JGq*tx2Xse=>6v9ZK%YQqr=qkxEv9GkruU)k%-B*zC{pLU!%92NgxK$i&kz62 ze28{S$C!gblKhR{G$DeFte*S%$dhJoz0(_a zM5&z!IxSG_XFc$lii5O#iX6|C3^&qUP=(sDdfw$+lZ}a`igzh+EofFc*f@YqA1`+c ze$PORe*2oIDgxY)d-tPnZFmvAfC_2}-pmCCpjg}S&8*&qHNK&}jQtC7y5LEet_z1M z&0cG>s@V$bJ*mvj4Q4M_0Koc5K)*iNHqh3Fn2L|PiTny10;ONM`)2K;GzPuh=$OFp z;z2PwzuUb07x53S$wSUV%6MW+y*vI=yZYuL@UZ%#4hs7_PEq`C7Gq~Mo5qUbTwIP! z$DsRUYW z!8Q0o^5of`j5HVT`azB7-mmE7~kQ+w|D~S_- zQ_&l&Lk3tcia}$#MleTe3uUAq`f-KwN?cbHdT;bKXBuIco_U`K_+8)~b@pyYtKn|2 z07CvQS8cx<;qQv@w(Or|pLV=niiGhN+D@Os6A~d5OW_GmeBmdA+jJWd}>q)x+n$Z7r&EJ`NkILG+-S+wum^!4nrlt{h0;&v8}9p2jS@+xRiG9(I=dQx zrng#{J`aOLJuXZ))a>!?+cTt66})ZLxv0r5i27#kN4m1hz||q<=?%q1In-{8<+XsA z`UY)=m#JwZOukss0~j@SwEhX+PxBQok=A!YkgH<;M-|Unrw#DEXt}Oy6hTu9y(pM*>y`q{!dmk*n$H-n%Y81)4Rjx{j)@TrM)H3PxTDs&EwU$u{>91X zd*3@`OG(piP)iR^GY2WGI0VTdi?ghMD3U6EB`nyTH*Oz2wgQ$P+)cib!tle3Ek%hR z3BezY(tKbE-j*x0QdffHru_|LiFSZ1zUCJ(y5pT$r5+>lyuTx+zM@MBIn>zZnWpqB zoP#>PSVJluM-5fA-|D(zmi|Yx9V9 zK9Z_2&L-}^N~?G=Hn0N)x&iaP@vj4ZDb;umiz@wOxB)|{HSI@fUH`>b!PC4L@D+2! z$=EY~lsCr`fU08}aq_zOKh_n7_J_(B zs~rNj{D6YpN+UV+v9|IKw(HVv7#9ys`wKo0NDOOk<+cM}Qc9^&JkTIfEW@r?}`GvS6&MotIxWjLqOfH``44Mom;; zpXW{L9azrNV9JUw+0I`ZtDRLfsO3$7wS@60lt z|9E$w-L7fBZ~q!vomZrM#RMOgZbfE&gPJ}>eKaCD1#zD=)5zc|nCy;AJnR9p&jBZ3 zauR*p6MR;!4s^S6H4uerN<|oSv=k%k)KWXwHow5QeWEfk;sXfx6w6&05Bs0Pe>3$+YcRzvzt*FgsQBlKpS#+lcxi#CNEGm2LYZGz`L~|^Z8j0?THzYMn zAa_JT+QcWaHM9nG6Ul+aGl%1>9Glo61q&j+W)UEz|CKALOhY{~i=<$q)jEZ*{p2qb z{YqWJ2RW`3X+{KIdLo%L8yQ#%jONSYACJLc&WogTltj9iZyDk1qLO!T?wY@1q>CRi z&-_LFs{ba@>BA~M{V2EBzxXivsDqx_ejB%g3S>!@3i(8~_TbWFK@)sPr}Ca=r?<-y zqJz%#URub3cRapBba|aJUbkX+O`I11QjP#Axoy zka{o}1Gm1L2hwQhT)G=Mc_GjHI;U;T^_CvQF@tJ*yoFAH8M2+T9&}}rEnqs_%lY^X z%nES@bOxla`)8vqhXZrpFhfZ@%=5D>wTW*|cI0coPD3rRelE3C%X657dO9^~Aa*-| z4=;C~4rvxr^*$I!VDde@+<8WuuZ})#OTgIQQQ26uAsaO|>uYUG)LMDQy$8#EWuo?U zt@T|0d+Q@R-}y?|Uu$^zkOOXh;Q7aaVRbr@q)A#sZeXE{T5*mzZ{a-ue@lGAhnlt( zZlG`S8C-y7>pjlhcfQANhKdZV0&=rzf0}Ko_sP!lzyxkN07Y!^(oE%>2sqcoOQhXp zuPO36__fz6w|FN!R@=Mjo4D;?AeDT{&}7)C7gW*uwWzS$OU2L!(cpA`7izwH!=Q;5 zV3bxsYoZzf0sgMW(Enr{Fh7Hk5uZq{X3?Af9L)|)Lxf~fh|bjn>;-gOj{Jtku&Htk z99a#Hpa+d7r>E)!hNt-Vll5ciHBhMZrh)jjZ^XuFVkUEMXJkPRg=ybLI^A zeH#h;pm;)r0A=<4`&$^V9hOL%6#o~XZhA|tcr!dv&F?cT)Nbc@X?(qgABz|MrZ|3FJ{BOiV5aOE%63OG zApiJT!-3>59hexJDMRDCESfIdcbvldUxR{24@HiJwF^zGIvN9Zeyr(PXpo~B(L4Qn z)wf56{L+#Ww1K+Py?uOC z+?LdxGqCcZhP?nyo~blBI{p~rmarD7p#cq)zb9EXJB&9MOJ0Zh`;R*4RfP#1^n1zv zt7=c$4HP|ru6(S6hZf43L4M~HYV4@J{-8Vti0eep!8&kcvw{{>ErI@HbWFT2Sy4pJ z?|q$aM8DWSZ|I&yXY6(W6wa`jhH1iJGqnlGBfMGLZ_UfFPO#oI!N_PN}A z{VW@GD{YTD!&qx|r+W$|S1nt`rjf72a2o=_AnjJ6>Ciq-w|bra+*Zum+TlL)6{XHo{de`4j&WvOxBg9a ztzPybQmyXe$ zs6tJ6b$0hZ4wcrE=^txCOm{Sz%krF^wan91md7>TGIOJV(DBGEb1Tu*ep$#Q3vpAj zvS8lpOQC`@Bki+stLgMq`!H`9+je)?Pi!GJ1MZ~y(OrkDs$tLjUGc-!K7-5KVEZq_ zivJF4I}h%ZubMP(KQ-?$XWj8986^LgL%!*Z9&CrXi^itF*mxg-JuCo^d}#~A}#y=_zs%Vk%;miZc`zAnCw*~KUzlv$UV*-QOl?MQOGp1uxieX||i1yk57Y-dIt zP3ORWnio>fi}otJ|V@g_5V@CH;d24Bh^= zXF+EuNVbJtY)Oj_j`>_tx}GZ&;n zKX+U0U*IbjHi-AK4vX6W_U<#>2TY$~K40X$xml1ZcO`Wl{9<7L#2F}pxYP7TM%t3py@H2>T)DU4RvG$` z=5b6F{6!=9bC^Y1kfYj|Id1u?Zl4!&BXUdw=A=Qb%Y#;`%r`4?H#CXW#MoVtpqw5j zkF6}cUdD9?8`2{p*?QqQLmDMk$u~EkBZmdp+eBvy6qsXPMasz zF2n4YwL2-&2K`2_v_Y!0)A((^y3qmVI2bP>mCsByz^8#ffom6y5HRZHl=iLeZwXgj+}tQ z;nh^Ua+Q%lsw$gLh|6J7&4+yP8I{ulsDA*vj4>iSzJ|KP<*2aZ?ff%)#otS}SrL8HV(k_T| zRCp%j_EzODESpIijGSzETk2VXo2(i%)#zbn~@FB+&j|vKB<{U<$$`nvXX8n7A{CQElPufcsacHl) zNwLKi`bbDAm~{r3kQrqG@K#9lp9vi@Q!J8_~c(^P2uG2fi{^_$n%9SU}QI{K@Ntog2%mz9rN# zO~1Dq@FrWCl#BmD8GqKnBK7sMxEB4;{sS?xRs2kI!w;T2wtXY`FVdar zIA007O}g^GqEZk8CNeKloyAi*HzDFI*v0~~+~j?JvU$EyoF_Q7GCy|{?-he(LiOqxTe0$ zR z4)C38wT%L+imL8=2gG~(e-VvtnL*2k#44G$g{%|}Hoja9c}e@2FNjwMoosGs>)SvJst68X?ksIw=w+VJJxt* z4nGk-yQu2$R6C@uPeFLQ2^~;X_r_kXbT=6gghHxgZuRua=NjP>|4dx7X94z)_I&52 z)roEG=m9w0z53QI0ku_j3ORj%P?(A#LBEV{5zwdiOD}vC(cx>0JQxjG8h}^S(HGfFN5Ylo2Sd6reAsIm8@FY#(_l zycAn-8F7ZioQvsrYeC0s2Nsvn&3uB`NY3oET`Y{#Bn`x5Mp|6$=I9Uf0@j5`jFQct zbB#_Wvo#$J-)j0rD!3pmel-?;u{1AyA5b5Ztf;=Cl6sMS+ehC;k{f5Rw*u8c1#8lY z(daF|6ln{B=IfLy<`3DZzm^L;j=RNVPq*5BIBWx&%?9BZ*5TUxSXr$O?Y8kT7k&3q zJRDC1v#&u`$3Ns~cFAVU)mehL{Xbt#sKCVBDu4Z^l#Ud`j(3wrB4upQ6_$eBwgX0V zSHCM)aLk>U&0UVA5Ss%-Yg+e3OA5A_^g6KY9VFW}IJ@bg87mSoQF1yau|4K#PJ;jw zqnA8dAy!JRNc&L<${zkKAaHN2Me5LV@&U?GH9{pcrObNEEE%ve>r*ohs2iTr3(lquIb;!9d?PB7%8zs5t3Wm7>Upzl3eaP3t+DatQ|9o&w-xim# zDZZowHtLfvRp?4nuS%8p9XmPhVf{4_ZhI^j9!`X`w-I)GKV5>JroH5!E?L_{n;pHN z3&b+gOQYbcHX<+vkb%BGSuh_w$2D(8rDMsXA63*o6V~E*PETLJ8aBNo zB{xOz#;-1CLEZQAg`jx-OPDR#;Eb^#M1wj?!dB+ahH*jyBl!p8PGNVQBCdgS=p3oI z^$#$T3fj0h_vxpktIZ`(bkG3=TQ$zXMu}!HWy05aE>=z+W%2y3er;LOf0xWy2~%_7 zhpgJH{UEuJ7vsHVIJbI#*sx#&8GPB{A02UX_@*SkV-5@2(G17K#v~utF%jPC3F`ph zds+zWX(5@Tn^BXOD^zIpijJK0|AmXK*>5*p_Uo!gGbuDrHNcacAAwvAJk&#>eUb(m zULWn`bms^!P<|+n!MpO088@B9_6KwSRm2cly;*mt(u{QRh|V2U?|;gl1g>Ae+L&ouR|0{NBF7fpS+rO@<(Zr>&27tLUKcs5x9n$2=Z;x;4-7IGK~!i&+Rok$Q+ zoC}n`lMd-J2UZI%$TZS78he6c6edBOt5W6?F(4o$k_@TWfKd~lvgL5` zM|YS7?G%q0(B~CQTc4K79a%#k)ZXbYGA6)v#54C*r!tGV`*5e=fGQTQ?r5oD5H4_x zLHWjgRng%TpCg%*lsTJ87r3R--T@BPZf4f^U7_v|y8eCZ@n!&anO}WIK|)E{u0PCa zfw8>|s}{t_!Hxr)o-K>`?yqI;xY@9fVeMDNDL%Jl#4zoipM~&n<%&&zFNNASaVpYx zdw?nF+&c=4mj&5vw5QMVyQy{A7IeJMbt|=3{3#3|X75PG=>(VwOhs@$0*k%jX<#zi<;bU&PEEtv+iL@Bef9 z2~i7cA~&)%il~#{gIN3rfUu`hjj~vR(>BRM&rP~XmDndl;>G2TGi7oG&%k;*737=E zNb#+C2NGyo3pn@ish9{yF;x8Ca)&4MfpU{Ni4pn3=BZ2hNVo98u;PI6`LR<14eXza z1&1B)11fdAHBgh5i3VJ<|HZE?x?W(a+nFGXPwML#aWGA~BPpv9e6z1DxN+5NPov7l zW*uIM0G9-{!8XH$Hff)Ia_W{XB!aR815Yd{m(ZBchnwBz!2Ry@2=_^g3Rs(x8;$Y7 z!M{!yB^hMnQ`#ilRC{c|voCH`$oYQ?nFZL=!8S~lyKJFrZNcwNf^$F2yCIrBUt(VH zH+hq;UeYdgOQ5H%w;-{fay=0Ttb5Rr`1U6Moft>s-DV@3Ie&;iBI%;opdN7Y9A-=A z;nANJfBE&&GY~E=E)OlP#jUV%1jo`7#!1vAmgy}WC& z^j~R~Z+IK8Gux0xZjlKJS>DD7w7X7N8y~y*A8mJZUvC`!{h*?ElMA9ZRGhj1bRG$i z-!xCqH-ZZv)8sxVuQ~4m&b;j0+vfwu$9)H2y&^BQG@36vVdxm=I2Rj&Jp`(jN4sFld%3ODOnf!s4l0JY)qMN-R(OL`p@Ahm**}5y8?o74+m0<& zcXs(hq`Ssedq&lQiMJA(H4uA6PtTEM1#7H z1{lM#F7ILrryO@70^6aNlhh3=a~y#da)h?3zRVNlEB<9_;U0 zA-S~`NlpcWOjVenkph$|m}R`gSVlC>_{CW8zrI%YclYmnz!yP}_)DjF72ZN^ zu&<@{03)qAh$t0`K&YvFk4m2fW_29EbMl52&Y-KE@S5rVz+@=2+b8?V=h5Xs)q7=D zJ=_65|7WiUe`R1c!q=&hOB1Xd=H!dKs|C}eHr+lVT^e2ua;FlNocjA676kkW-n5l| I-Jg~Ff9qpl!2kdN literal 0 HcmV?d00001 From 5573f0f1c7b29bfe46d0b70487e4adb4d01cba62 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 May 2023 20:35:20 +0200 Subject: [PATCH 0421/1710] REVIEWED: Ligthmap example --- .../resources/shaders/glsl330/lightmap.fs | 13 +- .../resources/shaders/glsl330/lightmap.vs | 8 +- examples/shaders/shaders_lightmap.c | 132 +++++++++--------- examples/shaders/shaders_lightmap.png | Bin 229671 -> 209671 bytes 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.fs b/examples/shaders/resources/shaders/glsl330/lightmap.fs index 95558610d..827473d25 100644 --- a/examples/shaders/resources/shaders/glsl330/lightmap.fs +++ b/examples/shaders/resources/shaders/glsl330/lightmap.fs @@ -1,4 +1,5 @@ #version 330 + // Input vertex attributes (from vertex shader) in vec2 fragTexCoord; in vec2 fragTexCoord2; @@ -12,9 +13,11 @@ uniform sampler2D texture1; // Output fragment color out vec4 finalColor; -void main() { - // Texel color fetching from texture sampler - vec4 texelColor = texture( texture0, fragTexCoord ); - vec4 texelColor2 = texture( texture1, fragTexCoord2 ); - finalColor = texelColor * texelColor2; +void main() +{ + // Texel color fetching from texture sampler + vec4 texelColor = texture(texture0, fragTexCoord); + vec4 texelColor2 = texture(texture1, fragTexCoord2); + + finalColor = texelColor * texelColor2; } diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.vs b/examples/shaders/resources/shaders/glsl330/lightmap.vs index 00278eaa2..d92c2f097 100644 --- a/examples/shaders/resources/shaders/glsl330/lightmap.vs +++ b/examples/shaders/resources/shaders/glsl330/lightmap.vs @@ -1,4 +1,5 @@ #version 330 + // Input vertex attributes in vec3 vertexPosition; in vec2 vertexTexCoord; @@ -15,13 +16,14 @@ out vec2 fragTexCoord; out vec2 fragTexCoord2; out vec4 fragColor; -void main() { +void main() +{ // Send vertex attributes to fragment shader - fragPosition = vec3( matModel * vec4( vertexPosition, 1.0 ) ); + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); fragTexCoord = vertexTexCoord; fragTexCoord2 = vertexTexCoord2; fragColor = vertexColor; // Calculate final vertex position - gl_Position = mvp * vec4( vertexPosition, 1.0 ); + gl_Position = mvp*vec4(vertexPosition, 1.0); } diff --git a/examples/shaders/shaders_lightmap.c b/examples/shaders/shaders_lightmap.c index a37c9922d..b636c8b28 100644 --- a/examples/shaders/shaders_lightmap.c +++ b/examples/shaders/shaders_lightmap.c @@ -46,81 +46,82 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 2.0f, 4.0f, 6.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Camera looking at point + 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); + 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)); + // 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; + // 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 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"); + Texture texture = LoadTexture("resources/cubicmap_atlas.png"); + Texture light = LoadTexture("resources/spark_flame.png"); - GenTextureMipmaps(&texture); - SetTextureFilter(texture, TEXTURE_FILTER_TRILINEAR); + GenTextureMipmaps(&texture); + SetTextureFilter(texture, TEXTURE_FILTER_TRILINEAR); - RenderTexture lightmap = LoadRenderTexture(MAP_SIZE, MAP_SIZE); + RenderTexture lightmap = LoadRenderTexture(MAP_SIZE, MAP_SIZE); - SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR); + SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR); - Material material = LoadMaterialDefault(); + 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); + // 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(); + 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 //-------------------------------------------------------------------------------------- @@ -139,26 +140,29 @@ int main(void) ClearBackground(RAYWHITE); BeginMode3D(camera); - DrawMesh(mesh, material, MatrixIdentity()); + DrawMesh(mesh, material, MatrixIdentity()); EndMode3D(); DrawFPS(10, 10); - DrawTexturePro( - lightmap.texture, - (Rectangle){ 0, 0, MAP_SIZE, MAP_SIZE }, - (Rectangle){ 0, 36, MAP_SIZE * 4, MAP_SIZE * 4 }, - (Vector2){ 0.0, 0.0 }, - 0.0, - WHITE - ); + 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 + UnloadMesh(mesh); // Unload the mesh UnloadShader(shader); // Unload shader CloseWindow(); // Close window and OpenGL context diff --git a/examples/shaders/shaders_lightmap.png b/examples/shaders/shaders_lightmap.png index bdd8d3b91c8484ca4c8ac2edf156ee6988e95671..0fd67a6e06be36081d8add7d64815153fca4776d 100644 GIT binary patch literal 209671 zcmbrmX;@Qd*EYO&c18$E5QRht38JDEB@hHmkZgtmtrV%XigQ4Pw563&rYe#hf`Ez| ztCc~KI8^EV*cyc@j%)@66)-3&AW9S!M35jVQ^@`x zI@fugYj2MT53#bCY5_rz)n}nWOCSglLJ(XEgu5R(A2@>X-$z*b|LX|QEk)#> z4}AW=V`NLtQ2>IjLi0PD?cG5#x9)4ZvTf{oW|CL~NL@wa96kNbwcfs2no4(2EX3>W}pL3Ks zJ~f?rYWLE2nM{}azWED|UsSIg4gRVBL#e4}!iSY6<~kLhb6*y~tgi^d4jV|x0oX!@ z{sbkM-cXlm5C?3r7f$`nhV-l4REy(p`(=oY?hkfVZfM+0`7y_4I4Xvm?88-JQm6WQ zbX34Wz&C^va#@w`p>sErQY0-HfATq*$V(@F)8&G6RmhmD@kSb%SZ|=7Q5CVc#a3-_ z6zO)lX`|dkD||P63+hS3TC}a4=2rIcaQg0WTEMt0>+~rHrmHI+KGoT~dL4D`QRl6R z)D2~sAGxq{Iu?Wa5mV%HqyaNcj*^>hTe0jyl_byKDseo=^BQ~@f`7+C-G!gQt+{f) z)Dwv3L-_E3d;|JIEH+2UsVRO}#X1ozb=0!zh<>J_kygz%^wzSe$?>(FF8S0mtpwT< z7Z%4{O{9yBkd)t!x3W16>R`1*nA1o*)d~Nm5(_mmRZFoK3I-cI^pk&i&W1AG-Oa_ zsIOh8e=}#^WVWd45|b+E^Q>*D$-yxOKlU;z%Xnvw;msUlSe?v} zOIOS!d)Qa8dN*Jap@z3dW+-4sW@j>dwvhTUTrw5s*$gvjX`k8Z?yBxs`#(h<)mPJd z>9BjeyKU3ng%ZSLrN@WrW^NYbOXmKVMNz0GIlDV;`;)Vay{4P&&S;U2+$Oh2l6n)N zEq?j}TEt0eS+iyN`<;6kmzlZN?K}MsehW8ZCR#FTMx;}PqXNx1yJh?EEhC#m1g^1M zX+b@-d4#>1^uE}-oJMJGl9T_KGsgZAY1tYP60i-sE7Od>6mIb^vetYlU*MyLFWA2? z&Yb*rQTX8_MS%qeAFWoQ0=0Glp(?c?{EOJZp)P6_qs5ih*D4=j7cGI4ebFLzWG&*^ zMe0Z-xtj^uOlo`=QqnJrx4T|J=>Mc?%$8N?MlFk$`sjfy#_+~n`$w*jS!Uz=5bnt| zsoy%AokxnS%idb73h9&u%uTz(d-+*x8RAj+C^%5=N&dgSK#S?x6fe>K=&!Ju)TaTW zl2@v-e9LASZou%})s&)tQ4YU@8h2ndPsD=Ul2=#J-NTlzFihg8R2A)&F%98#op``* zhyP9VUk6qKt; zzFM72y6z(9~2y~ylcE)nNvz%M$sd(QhXbIP>4c4Ec zIVakZJ*!CxlaaE0aDN&qok;f7a7@s460&FYW{R*FDe54Ph1y3Tex7ApIlR(~a%gT;P%@={BRc99(XWh=XOlb! z;g@Nqv08d>CMH7SzqG9z&@2>aS#%rceB`u>yb2Dxwn^D=|qIv83ikk738 zg7}Q-{EJlB?00WRv(In$W=D3v7T$MEh+P)^)5wP^w2%oqEMz^qN1FwHpo3Bec88(O>yECoZtrctgR0yXwUxCgM1SZnC4bZx2WbQzzH)8T$(?sr6Gr2_ENPd%Nfn z4RY+Z3)_bD)V_Q<;0ywJW#wtQ?-qxtY^W;t=(BFOZZ|j~~3NL}I<=e9by^t9CcdGTyDu zt;EsLdKv!?6&A7eQCI2>;L*QF%6~3WS(i2OOBn5p5%vYLb=v5ZbFEez)|p+l5VTp$ z;8--c^xlUqfP&89ku)sIQJaibev}+N>?}7Cg3HG(+K-1s6EwkhmThUQqH!q2PZNHc z)DhVTwfzBC=IR@1NM;PV5nRr^CF9g&$4Kn%bPf1nwVS*PX=X%aHDZ`o8IqC%C5^9_ zQ`an|uiQboxL{mXVnZr2%QKVJD1VZ%>2j{NVFr^qYl{qvHWqMh{$kn0=PX%VeDwWR z;akbLh3%oTtG73J=6whgGoN$NOA#8lX#hPe6aGLYmwWDj6BayKXS`|^Q7+1mI%+mX z3cZ|D->a?D3WOV|&%BMpYxQU6=%2rIMaL_!A{xD+24^m%`L^f5o}F-&t6_JotghiD zzN(4G^$#IPQlWPKIuK%wmEYc1g0ew)m%TB!PFa={9W;Dk?^_=SPy!o9KJ?U&IYDf= zYVNmjgM#4~L7LdlG}7sspR)J0_?o6(uMf{fS`OZH`1)V<7AG9mIR1PUT47aV z0cuyyxZvV+%j0Ijslfrk*LT521^U=lEo5N zRD0FG@GUmoHu&t_`hU9jDh__10jEskngi_?`!n%-JXK1N)lr*o9^@jsM)T z2ScR-;z$u|=FGaYb6R?7EpTpV9=j@STbLO2KDzI*H4luAG3P%#I?B=ZTQG|`t?i!S zh0iaNXN|Nt+f3_E_UvN6&J<1S-Bz4qxvNdVPiV z;cZnBJ7OIvEn{AJ2y&rZW8HQHYPIsP-(Va6TK&X3!eFO3L75J(>7l$4JLj^$u#)@T zq~vJ*X!R}x58pa{|J_gG$RSl;m%e9I0D4MmFd5wPY$)W8}1o8Jf1;+jVs?aW?@X(X%;cJ9@z>9U* zLimId|7|&5z*?qtkQy@rSi*xyxn1UzGQl%}?iGBTFpnd?Gc0|INf+BWuI6kNV-~F{ zS2(pK+q!%OKk*x<9O^+<(RO%@*1Q6inqFh{}o7_hG)X>VNQitztBX~~yJw$|ige4(R#B|C|AyqX-yK<$P0TT3L zC(`0Ra;uKsMDz=cnWrk4+Rl_#Y+5fKPIigEFonh`h1~Bd^@sNWT%;=6y$%nrtEi~p zr6cZQx?1X}2k%#WIQq0G@Yz>n8H} z{KxuN;!Kj)PWXq7oeW!@BqzGLz&ew=+Iz`KWF~c8T}>T!wvKUm^$zOd@-6pw9 zrgcsM#!d95TSizZPSeHt=2~dMIlDFnEXf~`C4TlmSvSdbW_K99J6F+$2Q8B(kB%(sdy%&WZAM*4FK|0RX~Q;yIsC`*TJ3p9ZWk3zNFwJdSaGhIV5( zD7)3`Vhq8FhA&BB&ft6iky>VpS)XUJC0uMGjO(?pZ#_lQujOI@i@_`Ps}{^=((TO2u5{UeM} z@!2(>EmivSr^Sb4@O+Uf=Cjg(p})^+_V+#%2yo)_<>)&Z%57mpLD3PlNZB|1J0noi zx2X3KB&FDt?fNIFAyGtRCuZS%D@84N^5=$yLhbH50Pt@?gMZS0I4Lw!Nd^{N@)cAx z4uHb|+W&=iz=_Z9U9+1=bs*hQ`Kpb@*3px~m_BFVwuDCL+g^1Mb-Fpj;W~ZgFO;Q} z_I%dGd=1a%t18mu*uXZV%HrY>(l!G1!|)Fh+V=qjt2Z?b*%VAv+OdUP zVW}{TsVU%q2}O(?JqBO(<$GEV3KB^7yB#%6rjK;G4VlpKQk;Ha1N(wZrQY>NCv2^5 zI4<(gCNJWC8Dl9gvWfFNZbSN#{W1ry&&a2@A#Z4HPVl^%XlP9&eRU+|r2nXrBxMLq zNcrhbnr7U}r*y!o&TP>hZx-Yb?wC+F2(P_Ed9;jG^c-qXVG_P}##iswIMTe0;Wb%fg;WsgQ6gb(jDW`4(cE4c7&7+EZ^`-~iJ6JCr>2D%7_g`5`hEa4l^$ z_0Z7@xV1wS-GS$;o;*}?%*9=nwx=fj82-k5J{UN*;~9zs+^pvK)OhEjak;KK(!S=~-m%UWq`v%*&?iYpYG~0@aKn_N(9IR-e}7 z(k^#a*5Oz?Z)DE~*LBgH@pH|5nF3tfjn12bB9@;mY&XU2mn&x)mfA^0A6k!XGfU5yFCJp z3G?JG^yCh@o)w|Nq#T_#BRPN@EEOL6S@Gcu7itODG3iKLs=&jh3dcMxN%Psw!q^Si z`3u%!f$pob+$Yl3CkaH<3v^|Vu92SFaYLE20wCTXes|8Q2m$@#tQ~Ok0G1TM#rR~7 z+0r*=$nFl4E=Hd1))0pETMBrn(M4loJR``35FjGRD@m0oH5H)NGevO1=#;n@NRT>a znOT(a4D$+<^0YrsiRkK-Id7QFb#`_}P8DAd4Pb8ooSbLu?hg{g5VC<`tKi!@+mxli zJ#+9_sb;f7-BW`mkZ;aTl9-9?q$S+StyIkt-RBVx1vSIBp$EIb6uxN^{3zEfxJZ5_ z+pvdPt<=9!ME>!|w2tOEr2lsP8LURx8jF9Li(R5f!tH2f(b)fSr zFfTKy5VUwgShPn*Sc813r8rb&+4G<>ep%{|RB-8b5wbXvZK&>TzfPuYSUkgImzpcF5oG$XImuvlL#WRN5k;Bzc^ z=&*MLv+PTLGXtJ`W)zu^07|B95U#1gV$c&4Y^ zye@(sZ~2iPzugI^Z~8(kLQ{?h=B;-Z9@ywE((V|~X2>g!bNnpd2tHw7F(K{Ns+0h; z^XeVV`(Ss1$aYYV5wh;&1jAuafK_n067fXdypla@WC4Clt$N}ef3DL-cg(oU8gzAW z^*W(d&wgz8>v78^NHu|uEz%rVJ06ngmp-1oI*i`)2D|8P*wu{}a3Z>%yMdHgMP8Lo zDICY-(dsP|N%Y;vk|>&CTRsRsZilA!h1g~%Pt%Hpx+`6}jbCfg%(nvJPfl{LayxiF zt?dGo@;UmsHi601?ds9*rtcrEh2{^)p52sz19!S1ios!}+(l)bW&!eu+CAxy4!jXy z5nA`#Im@VZ%sQDqE75qEQ@5)}c!}`8Cx)d?S{`SaqDwvH;>k>_&((1h{-s;g#5pwM zxg>o(qr?VDmZg4U>wFYkg|tai{{fOiS@+8xDQD9xJ&#q9i@qLVzt%vp z%KG{=IUD)VwcCuw+{5&MFb&7NOUHEc3C-vci&UJ?O2|_agR0Jf) zYc8+c!SG&ar zvvETg=wlM~EGFHmm0d+&yW?H<6SnTB0YTdUm=4XbbXr(NMW958dL37jdN7D@WS`wc zZZ3!l>H!3$RG?qgv}bd5frD@h{CEBx`S3_6 zbSI-?p$roXD^&d5%2RqCM-iK8-RuN6?7$R4GkY>k@L*@zLbdfb*O7J=G@!C!8E(Ll zE@TM|7y6H|+ad<`SbNMNMT>E-EHu1hl>ImkeoO9DQd?Z>WX363x|cQ)v7}7Z&;cZN z8W-GAK$trcSpLh~Db!$L1N|_7P>X5Fwp@R1%Qx`pK@z}DcAO0FOM!XO%d9br0CJEE+}L8r-y7l+c#o4or+ztVmbIqJCH`YPROLO~BL@AAzZulW`a0BH6O} zC6wX?2uCg1esbxw0e`-=eyfxI`Pa-Ag}ehwb)AC5XFx61P~iY=b6I~e!8AB;^-uX0 zYZhg^{*<<6m{u69N)AB7>b}S{e0AQeni?Il8IiW#OC!tm1x;n>yl!vx9nEa;7+MyUfM3mPcrmO0yRCj^O; zCYK;E5Yb|MFi+4u#zJ#1k^X#2l62bXx9f!PUG$0)E1kE_Y>paczU|IyS|~g=^8LrKjq0@ai|%@Y zo374V_{w7%{k(uS_lgpplX~&QBWqQ-bXGzm}uj#awqs5 z#QQHXv}bT*CjZebXyFCBmSU*E%apy=iS;@Y8{?u<^LWUGUZX5^`+cR+M0Y+FOX>?U zW&aQmOHL-6Re3A?!z-dQ8#6=%4A}mYa#fxJ<})7CNWRf_?N6{Z3B!W+S0n6&FMDKl z>56Rfv*4MElQhros_3!Ep5F1ngHK%Isn6{6cV!^}e71i40d?qYbY^%c%@j-fJ{juC zZb*Y(5@Ln{fK*S_Zt1)OCqLUmYs*?8-o2zv6gD2Ce>MA z);?dJt6wP0HHI@cu<3|}AaR;qNeFqtQM>lT;3B&lPCYSCZmVzY}x z7vgh69-08v+i9DEKX1<4QU{TG#De5NsuI;%!|>lki-Gs^yYfs@$5 zkWM{5&MJC?DTdjHqTpAwzp7N@2o-dE-6rVqDb{>@&pB1}tAA+Vw4a65>-4{26)L;K zYKb-8NYRzTSNl8J-T99;8IL6cmTJwoI$5H%ze|iGYnIV_t;PclgnlQZQY<3$F*0-D z;`UK?l5ajW*+=VHt*i@U4!=**J5JIM;P~!`vI7$=+RUjRp3Z&=FbF`0u_}Ct^$kFC zFMyJYmO2{Nr>d^Iivt$x0z9Tlsw?TQGDX5s2jh0QWi3mSE>{;>1F0}cfgZdY6Kik!-85aXg8^O`amQt{n=pBh#64=RC0f~i=)ka2hF zMA_zjv}{8KFuePjTH*le=md;_RgcFjAxW0qX0b$ZS>SsMeibQlYDmLtxJROe0AP^r zSU$dI6?YwxN^`;Q$h*|A&zvggu`_9D9bG&kJKjLhTE2t;nf=ADCfc{dv>t^Ca(p#r zm3rAt@g7NdP^&cl(Ir!DW$Kv>g)RAP2l99XJqVA`R))W0UmP&Wme9x-+2p4Qb-nbQ zh8)0@JQ1Vej#;-cPI2bMLJQAhq;I>_2iEyFWF#r@&Hf3(Bm;}xn_#Wq zyHc}I$kaYxrbKU@mXFDVrPG+k8Ijt8NruDLhC6dkD7DU;RkzNgDcelhi2Zi64?8Q zI#2G7CI7Al=sTPiM7Q7= zjMMfLXb}v9j<>jS>c_@lbTi!)(spt{klZuH{W>b0CHyT{^iFhL`E0f|HFPmGu|Qb< zJwPOX`5@hA1A_HH2ukN@x!q6w<4;q3_QAfzc>}Vy^tHybvke}fQ1xdnqfb99%i#X{kInWn=(fBUfU?=+T%eApl1 zK+;P^Ts_50p3&N4$&FwvmAP8C0H~*+^ ztKi=+4G6Yn(;Y$w54{bi4_}^TSTaXX<hSK!0uYryE1cKPcgMKmNAu4=bb&OVk3p;52+>gp?Ms0c0)QU zbt3jIz8p}WD?Nm(b2wuMNMYtUI!T;Bn3S@;Coet;3z5gJ&=bE#dCe zt0#7-hQWCM9lu#e<9|b0?QxCXwgrw7i!v07+Gnz*R^>LN$+Sn~1GFF{Z>?$`kNo($AA=4y!ppt+WsWeF#d z<|9~pD(iJB9)2<7POk1T4Oz-ZkT^3EZgVi_pU$h9+RU5xj_VoZV^6jnrV;UUAVW|6moG2zdmc;r(^7%Z~bGXxH&U1 z0u76Bp?EFM{O&4!x&EJ24s6vuX317IHD7oxoQ`CgT=y3cYixExG0AVcJWfNQ46I;(**8 zatkrtX7-|xRG6!u5y{LoY-#4<_EkjnfmJGry@o|g6HDH>($j9(mN>(nf{R_q*$@iA z@Hn(0j`DB}e&RlD+oU*m+cQTdOlO7SkJNvR z5bjMAp++UO&Z;YyK+hivbO(vrdmI|dz$V5%_o|f51g#-K1dr5fS8$e5gNK1qMU6ZO zUEc1H*d%=Oa=Bqfq^6O1v#%?Z&xsEqT_o>>?^{P<^L)qHj48q02L=hq%__Qf~twsIc+aYDnWH38^F0_A1s5gHebmHhgskreEM``iC zgjDlxN~jXPQ%@GnYLQ=o^%NDs3F*t&{a_=OgrY@OK_QvelCer?b;dQE+{~`RAJ*_9 zu|dmIs8p4`%Ka?-jee4=J|=+wFGRoI%LLE;bq)U_dL26OlY+HaWqk*Rra7c8u#7Vz`NqyTo8I%Bz(l>$@IEp;? z3hDG|QXzav#VN`0v^fd4V5M$`@F_pu_{R;^@+mUS?JJKO!5hC#m~(}OVOcJ zELMiasAEx#@8G2!b`v%*m)<6?oK7hN&I;JAbkyq55x61E1SR=m6H{IJcVAHx9;(QQ1WK$^HnkF5?Un zf|i_EbWO&)V)%X}ZGN>s0EdmVH*xehhHc4uxF-=4)j4peNkM3-oo>cT(Ny3sNp?jq z-K;#h61E@5!|Bc?NELN;&pVpWD^ejP3Qmf^lGXhaY6;8JuDnY<;1Zuj^%<))+A9R{ z1^1P+tk!0{vpY++1X{(*qyEE7TvM|xxWUHXmTOpyVK3|MdfF_qwb+T{IDM(pw?KC5 zK2sG$9qMM*DH=bb2*%SN}D%{>FTQYTo-GRIz zWO0nP?n$Sb!ahcvZNrt+nn|Y!CeDJ8lDlxh$nb5P)J~h=&1wjzf}MWAoc$?0w=*LV zMFE9zYd^O#}hE|rbmj)#0PO&^OKqGN6a59s9@W}v|&tmlCVqO>2 zv8&T<_>Ye*(glmVK0M7}0Z`)QJ~#PEII-e-iXjZKkGCZyN~R3&|3n_>Sk6}OW%zXB za1JnES$Q_st3bkXQD-9!`YGw{R#`txIHMckGf#bL0UY+rf#kNdV-;z z3jFymhTOmid^J`+%&b`_BLOMRzEkD0&fT2V{;m@8KoS?zK@oRAM+sS79L>4+P9>rH zZFxzlAlJBYnVwZk5Ro%|H8l|@LWDOcapXEb-8wU;DC?gfBpwINNYznJd12Qvr7CPmtF_ZNmewnBnTFxJ^|ozV4=7 zt8aKNQ|(F=juDIXU%b$+V1tbWMOUXZ{=x&cJi80H-C4g?z|Z{mZz|AS0fsPXdOaW+m;^6Hc@l5~% zJdXOz1-J_MKK7N=UZIX8Wyfh(Q1p&8t&em32{j`XHYkDI5=;%=Z%MPZv&wMmLW);X zdKIRuWb`W1+TjFjK5B)2#~%VbyfMX)4wN0>|9BesaudGlUIj@i>A>vU{NJVs?UUcW zURSq9kUI+y{4>7NTXhj%f_oEwj`YZqv;=U_{^aND*H>q7u8ufNhx)c*HNkoET`{E} zCLx952^FS7zDS3aa)5JSouUgVD^|m0F65j7)vzr7#8X*!25N|-_C~Iuw!B=XeI6>@ zs(-$`0`r`3D#9ZclWAZAr5>+W&Ta83dFZil-T_?iJvzw)zASpq`KVdu!ER zCjb$k0stnQrkMFua7qoo%G4%#p<-M87hSpKyW7V1cv0R6ey^^?ZkpMBm`O0<9ZmSK zcXVRxrzQENXB-HCV;5XSVx3x0t5hJXD;|1w2cTL9araeLL>Drhx-x?z*-+&PD6K&HMs^1g*jXhO6ON^9frfq)RmH|) zuSY14GFfXgwt-9Uwmsl5n9V}57vmvdt)UO{FMbDYvs==I9P3oM-iB5bP?jJ-Txwfz zk^G(Kt&b|;PzORY7Z6;(-vLKo+hV7`A^$Uo(V8@Wa04M6YX@qr;b`)Zlt zv!z69ASKUA2NxP&kt;*}QZu$~huil;D{*=v&DrMo zU3k)Z@&#IA&lJUMTjvcgkX0wu)<4fAUv0N&t3(Ul4eyzp1L)nO&NYPijL5W}{FFdV zdw%Uk1q-uMJhOLy*Z?9Trc(tuJ(O`e(0_3y#Bsp};+95HFnJIZ!!=6V(Je^@BX8&2z)eRK%7S0 zvh>Aw+mD6vO7WjY`>G_ZOxQ8noG0Pucl=!rP&KNS3*&SN7J-pp8`UWW99ogZ#yNiqfS7L0n|35PR zO9lt*!9Ztmypr1t`{%P>C2A9c&_yS)Eep6x$lffRT3)lmy8P6R0k;xos3sTKb!_gs zQG*SAJz@0~T<{SLjOAgcP%mT7x|rYymuGCmvZT6l_#oUq03`=SVozQ1FS_G!7I}_N z4NAO^j?@~5dd<;TJrxrnujA3)4a2lmhbfQ5U>6OQIub|tR+G*;VtEqn%ySEeD!152S-~C&7`QNvJ^>fJ+kYW2r)!%UAnKMVSE#eUV> z*Z*w`WbWj2zkC(zlVo;K|N83`#Mu%Q330ytK~erIUhZ|{|HQ7$LLh>Sqsg4##Z$@; zEQuO?Cinef=fJB6r-_ie&;HNfWZvWWq(7Zr-CQupYg~Jw(fID}maP5`Ke1_KM6FUW zQtJdZ&iOGfp2~G+&TrJ6E0fFRu^V+>p3%BHckFk){eL~?M6l1K4){LgPBkm-2givf zocj0Y5A-6>{Niw>Xp8ytEM;ykv!fzCX4w2(T-^bD;Q5iQmHa(Qf>4dW?|SH#HU4nO zqqpM>SQ~jta!i8Xf59zZz>DMea^tjon^jv8>MEtswyuoRv&=>_AXcc zot#;#>4$uOER1?l|0#3%pR(v-Qy)^jTxd4;*&t$j8Uo@5ZWo~{niN5M)=yM031WQx z@DMwQ*mjqR|4BbCCLJUP>vIqF;=8xcRnNV=KDrKvR^Q+FUq|N`uKfSx|173zA_XV? zTi;ybBucsym$hy1fxBsabj+R*MQRo&?jL?Uepnz z3XFjSK}-JJ3=IHF7cgsAS9GAGY^1ulgGn4IovCG6Xb>y=(B<3q7FpeKKa=75JlY`N zsJnMRmfM$~8*}u3J_w=T-=n{kR!!enSn?(V3F>vf>T=Tdp@pE&4WV(T6 zQL^vNklW|6xI<}s+lbd*lYbZ6Apb6=5qh294M}I-JSLubto6J9>6_61yL!z0 zJ9^Zu^sr*jyr>(OzhL6?moWtuSCDy^Hs3Oi^}7l4>~k(~&Eis6%GWkhovW3>94jFI z!m&ZZr`ktCA|H4@OP!eAe0Drtn-pw_Mh6Ie!7nmh!7uAH;TE~w075gCGJ z_cKwt`QCv&`HDR!F8uFT;)<(q zsi8RuJjR^~1E)e^Q$3aNOk@ zj?X1OzFTP&wCshFgSk9^>7BjrGcd_4!unDWe(MZ=g#1bleLuYCvHJ*RZPhg+EtCLD z<=k`6`M<3v-J#W_;a0}?AonPV*MscK;k6sf)_WB4|GQLVDd`i0`&a*7zSJ^RFZaCw zz6>w1=uEsQ4Z6Mga?~)Tcc~8ScZbFK;#>S$ocOHZP_SreW0_B6tE|hv?QyB@=ofI! zKhfPycJMs)q&v{z9q+)q53Eyt-QsOCpF9YmX7!)ua<48__l`q78=*p^y^?ikV^owp zb5Ql_ym03!E>|P9Hf)^+_6U$1YSPcxqI2j|ZR;-tK0X78llm)wq0#@a!7q|fW)={8 zC9>7}tut)_nzRWv_}SQhv5>lo($KVD^56G4G!RPuWlHxQS=`B$PV1;SuPL9ujTT!L zuPM*i^SJiSZpUhA;je$kfx3pip~g-PmXCg-7yJ29=3Ur~yw1@jSis(egP3g}>ndf#K8w(3A{ZA2RCW7r~-_Q$N4^6t#ZJv2@(47GJmwGo=f z(B)dS<<$qBUH$#c`rb5@S@_NXa7AxC7JhNGw#H&ZpnLq_h=6LazQhFf+aD8v*`x|D zVE;&^hNn}5_4QPy7~1I@EDy<0@w#K>n=+?>+gCZ~6t|LZ6C4_eN$3?y4-;W^$Q^FG zZG0uJCW?!}n-SzL&)E^oQr49(#`vRsE{b#GRGuFA{ z3knJ0nF@V4BZr!tW1e;(ew@}fSG8WBZlg7wCYaeNe9}`j$Ga+K1A6}I;7^D*zRJC& zkd*h+kEF#f?%avheK~ca%=P`}`i41Sez(vYr;tBa*yb0M_fCsR&T8g4BvhqN;1lnE zr^l!Cv}}_{55FDYr5;qV z`TL$}ouI;Zb&J1~%cHtB4%6Gu4~#rql_kJ8M%2e$)~sL~4)0Nhfx>&Lvl2)Y& za+D9flqrJAXP3SL-d8{^yD?G;23XTNOV1?S!HA%3-2G7cZZ~gwR_y~##*J}&L_6>tc_>q z#D^f|G$gHlo|slGlhYtqYTRnI6xbymr`=%^+kOS^Q7~%Q$M@50N>uEXj@QV2iP(YF z?g3K`%-oW6%HiD<@bZqRkiH1)=UiQ1S9vwDg~eV;`FTbFGn} zX{bq`r(ceOHf7Q-bjhA|%Yh#}lhN&%z;%|nz%y<a}mEP{zh{Sa-%7}I2`rp!_MZXB} zugcSvq)T_uE(Gt~^M6VhZcA#J+E+K`VQk3cJT%mvWPuJBw{;B5=5$B<8{%ldJJvh^HKv&; z^NLCQGnNwLd)awz{QW?yoO?VjHX1S0OJ1ZVLLZL~YgA8OGN@TDwSYM=3|wa7Hhg;|Idvq3=YdPkrcK|E2P+B~4O4zG@bD{zOWkan)W`$l{lz-Z)lr0F;Y9n+^8UO5w*|QiKV`E z&u7(g0{3vJjm@Grq<-<4ZEMTwsE6E}#ByDep4ag8e@|JSO!yh=dIYn3!g}2M_mqI< zf!S`eYyMw_Pwww}z%0f}`NdbSZXAB!X_NZ<6vchON$eeFueP8RjttWdy&GotCYmN} z8c^Qsaf5_8CP#G9d;_cIR^fFN-=}%|>{=M4 zd1KMBNJlg4auE5BAmgnq7fqu+TfqZsn=#=zK8n^UHi~1ui8r{3Vqa&(W~pP_EvQKI z&hp&IxO1wFi73`L^mkwH%>K{?ABO##9x2Wh-)Bqbqu1r`tQm zNOc~GHPq9L#+g)oF3l#MXN$z&m=bs4p3*klxRwoevE0OSs@DP1wOIzFnflG(4KLai z^wqbt@)>Pxxq7^HHZ`m3B#(3OM+I*u&mK;gVQjaU&P7wQ@bFut zChf9?sV5B5c(r$Y@-w&iWuzShvCXS@!7!Jr%2(Ma{Osaer_g`0AbF?VatD^+#uK6+ ztGufMy>0|b?Q+9%w6U3~7Ek5}o0>>HkswvolIhxpe!-EGi9p?-v7M>?^&4S@f6O;r z)y@`tC=WOyD|iuk->G6Y$;UoNRc7|Z5b_=OsA1Y~dlz?(S{Avh8joVqIpD1W{8=7m z=K{DLx?R~QkKJ@jj`ZZ~&U%PQc_}OYAEw?t9LhZY|DSvAIX1&M#f-s_qSk2~a%N`I zPAW;YyHUzGgf?lCG?kirj8F+3Hsmyw%I?}yY)Q(E3hOkLEu@U7D2Jf~!p!fj{e8Zl z&mViW*Is*FmHWQm@7L@3dOjboj~`(V%lYGB?f=U?EHh)z3&DSV=QGHy=Zn2&w)owz zDZ5@20$+*=0Lz5b)DR1=Hxq%b+;WJU4U|TF7I0=`nfko$#f z3Bak`dx-{RZqlM&xU0am{USWE!vE1X%C=Zs;X;g!J$9SUl)=zbPO$>>vYzy6YaQ+) zgDy*4sxjJ#eEk{Yu|yuEL&)lqb?M0cFeT*=;VLcd%9`Y@3vRtgW>y9jtin2kW6L$0 z)~B;Q8c#6j{R`1Ito+NhJQV)*^Q%t3Ub>y(gkfO3~*lT1*s>sZ;#=s?M=F$ z0J#~NP2JCx`wu0xUqHSVakG!|Tpp-XUt?*7Hj-C+lWtv9aR&2kMU{?N5A%cs@S^^L zp1lidYtT**tbAa?F5_2EjQ&p}(X!&ZSXKPJEQ;2_W(8&Lrq)PK2^&irwx)1Uf)zzs z{KGtsM<|`-3|f3r16vGDVX?2IlLh?3)t9l2YT8GU5^_1Nd*KVFbt4k##hvjg1cEMi z#?>_$fGnIUwZRW_id+CmwrP@f|KvDZ53o8>i@Cr)lZi;6yFOU`uoXl3?4%CI71`^d zR}*Tx3h5_dyQ|~)Vh5za^VZJ4$H-gkAloY1ptXSnpF;iR(4ot22mZHxPXo$0h~^D_SI7gFgZB~BcBo~`BcZXVdUTZ{rr|x1 zIJgZT9pJGK0>bMrU>eQM_;&{}WbqWOV&DY+>GTw(hc<3d8S=)iOrk2<4Bi_U@B^xK zaewnPe=WgoeN{H&!2SH<1a`76ixHJE<9-dj2B9{9@Q;~52 z*Wl;Sbmnsi`uhW8g9kRf@tL0N%BHd|?X)fDz%0I(IJnq~-}stJU~BY@jnk@H$eoO7 z+ONe@!}qcs!D5h5a+GbVM1iit&{bHIg|~6lVnR&eyR-c+)c;C;VKSzi+tSUn$Vdiv`pp0l;87b^zc2kG0CAgV`9J_`a| z63q{Zv*B+g`LW;@#Jq}W!x$SA@98`@#h;KE4vR0koUFM=8&1Szb{yGDe#*xiziqAs zrT*|@ggGfcVz|`&D=@_+zJnub#lC^uWh<`{cb|Y>Ylp6EIl)H0(R>{jDYhwO6fIsD z*RY2HoVnZrlJfDs!5@zcyvxGJX}5VsPCjrQdVitV%u*n6xh3@LNs_vDf8DckJ+;zfC>`K z6!5bYJ)5ypO#D^ErS^ot51!!$y#rPeZjBp@Qps7hBn>8C9z*aPHUnWF;wud80vT&F zmsHSwteW;S!?kUW+j7rU@Mr{%bvsq^e;SA zZByCz?W|dtnRtk0_w4z;zP~hcGT-yl;YL3Ab4)pLF3eN6Vh< zPr4}aq+r8+qWM#2Y&wU@X(_f%Vacx^XRMUEv&tAcY^LbM!kFjD+T91CktLGl1M0f3 zxwF7;{b1#3D6xTLZdq22_dI>_G*G4rnU#IUmD+{c6^ugsQcd^0lz2DuDSc4@k<>9H zh(uV~_zzCVTubuP+&V+5JcBXl{8cT>XtWz#9Sq(E9<+NFw8`DT43zhY79Z^(eCEau zX7KX9F}TgOM|~or6$LiML1)$SBS3j1#Ti{3^^wH;A=x&nV@8XVtV21NXg329pWHDH zjV%WQQBM5R0gmW-GP&~3!{^V>*FNvo5wRJL^sTvc$3R0y`uYEZQ;q*mxQ+Xccabf{ zMfw%eJo|pN0-c<6+FWVzsU7X#FNT@N=IuaT3nOj-Qqd&Z``7FO2pAPEESsjp zuvK{AwI(ft?DWn_gMVnGR^6XvMcma~6Fy;A3{k3}If}69-{xItGfq;FS%fa}ku&s_ zB5i5O`fA3uu<{{kLs2jR;>V6CER+B8wHA_R^*f#xcJ?;lb4am)l&lu`qTh& zZ7*cE6&F&mG^OhavTgvKL+bAdL;1rCxS^D>9Mf&LNY#*v!`{ut*lmr#wSi#MgL-># zIpy7-CT?3q@YqcmiWU$8tLJ>i@{uyE&F5uv)be>s48#jkZ0mb-%>+rZhHXk%L7W&MOR zy72i7Vx`x&qNH;uskcbe9`~FZ6@dmmegpzXrq4|GXL&7A9$8HF^EK8iZX@10+NoXZ z=cnAUS#e)48r%SR_jv`n0jp>gW~_?wg3T8IE$k-C*HI`Lb@I5R6^TKCp#hi;=bAjhJwrS6T0uA^P-Vr>u z>gXo~*IjkEVfK~_c@BX&^i$^z|F=sz*Kr|a)fiB|x!?cWyc6YlKS#J4bVL2RLU?MA z!q<@!eb!A_?q;qfjdbNsfS;|hWSgJ?j(?(HDbglG1mRPxlKVDG+m%Hj=lhBmq&TCWE%&8%EQ2O^%^BP~xtz`hR5=d8Q_lOG<*#*O354o$lQM#JspBWs z&AYh^Znpah-eJPdx?%_kn{2GFtp zawmUbBmVEd5qDRsA)B{YaHKxgfCs(x6_3UIfK*(kedqwb(4FAzF*U7oSlzG^Y+J-my(B?*ybP9Z)alAnvtl z)0s6Y>3UnZt$0=%AExs0@b=Z+mbGLXw>dW`kTHxf6}wCSLT1+HfHAVJ|@&njg3GdpPf54h@ghK>MZ9{^(s*Nu8*I+wx!hZQcm(e^{#6S>VN6* zCnu%$G!k!yCPsW>TzsaHmaD^`BE$_owNW0O?=bZh=V|(lUP~1kO8I?L)5%$B@+@i2^d{UxOnTd;E#E5>31FuRL-xf>+G$BQts|-8|ki?{lFB+ff4G3BB zRm^qeI{!n}lMkuY7i~|5Bk^ar#@WCDx#X~7(_JJMDs241N?c?h%w-4$UKR?_ z$a-T+630BEIe!%5qwwO7r|1gmftsMJpM7q~% z6n}@PRlmn6sxNYi5@QOklI_5j;@kst3N`*W3A6QIbk$o2bhH)eC28&!)_)_J8ph{M z&v~J9Cg_gMAe6;4h|ur18;)2aU+VmHf=TGy4uN-LHiP`(N7vVF#1=#To8fwZcW{Q~ z?u*mQ+#vqoyErY4mmF-aM(E1dI9l(lrpIrT4xS|bCpx!4Kj1xveqG^DjyDHXh!ujj92RaeQ8Ho!ZkaLCo zs803x;dsP(Kzf;CIITV%`G>%mKA&&&+=Q)uHek@`Wojs*L*Tf|Uml7Xl|ZexkQJnk zDdI6|r@nF=Evvv@QP=u+VE6lihN^gON!ha}ilKL`&w+{>z!JLyuDq1?5cw+$)N5Ip zzZQkPrwsdS2}Ow>DmQuZP|Tx!y9XhsA>1SOwBtK8qL5^3~w~oKbnbqdpg5@tEAQUmOJZV zmB!z?b)=9Bk1*s-1G;y)c`q5V0v$rYD!4TzRUZ|LNC?%UifcU{G)S2B&UtTHw2 z1RP*)l4Gk<1A9CsGbl2mKSH2@5`0C-%$iCbJ)U(^OL*5v>94Dxz7We}3`FNCdTQC* z{c5u#I#X2A=gzlkUnem1oMIh;-Otf3T$$3y2gwRQZ=U;V)Lg1oV{X+zllhqS@56Am zF5$QYLv9YaQc^SzG|Y38#0ZIn-S}QR+{bjJhp>-WY(4|7a4?(#7+)4p7_H}$&PUvY zqrSSjPSLsL_M8Xeh^en%yz2J|x`_t@6a_1z&!k@6C$}b394X390h1t)v$YV2^0wwU z=3L0MeQ#)U;d|$kvgf~X=;U|Nv`q_X1u_zoC29C@bZX)Z5TeE({bP$_9=w1>e@=_1 z(uvVU#6kW(%@j47G8m#*>JhCsWjENdNRZlJQ}%qCM@9T1I>S7 zL9(@R7;)PVjg)*g_Zk==d40W(NR$=Qu+R~XvZl*MBd1b3jvupa z4ogl*;jS$c6af3Xh1^7&Klqv%&>+oBFG7r_H2nX^PifV{u<*@fM za^jzE%0lu$TLWj1KcJQqmfk*lU8TOZ#V`C2lzNRmzO~60ZxHGNuhwfx-hDP|CE))o zruQ`$m-U7wS*MwoB8_5nUjJxlqiKINRA?1-^!>kG-=e=t=} zXK138Y;e6OBWx8}T+oCn$b3%8$0 zjGUUBW`K^va7Nt+!Ob;A)KYZE5kK^7H()VPg>#K-Z>by+_n*pkY!-75Z&XQ~8+__= zja~0GJX(UHfC`Cu_I|TsLkH(*A~0h|0?+mv@MT52(P1Zzy02Ma0?jv_S&haB0wo@n z>d;jLN7MZ#4AZrmlF;S_TiWoRxy^>4T!Iq(S`e37X-}-H`!0tq7rq~b)YJbhcldX{ zWQWda<#->o72arYR_v|Hr^+aJUO{FE`enEjUDx0z)4hfOG*pXx(_c9k!P|7k%2Y+^ z(bC*<0Su!+MuFBNu>GS6(2rC!_aO4-UCg^7m@ScC2DKuZ7M4hB0jnB}TSYG@~t_0#xC*--8u=@96dT_>_KDgcv{A@uwPy^~!e# zVwLu_>58*=i|O>clIE&Tp24~}wyfn0dOk|xzBszvtW5U~YW$+6mrhqFLff}bAMfkE z)<6~aaw4WaeQ<~CC)ZPB&-?Olnl}SH#*;^93ASIbtw~&9)g8N! zOZek1vTF4{iLDe@_0~#wRSS6L8S?E~LL#pE20?Fo){fw%4Qth_^wsUkF;1xucK9>5 zOO2?$Q9k4g;{5`2wM)XoDDDn)k-Gtf-2xg9UBUZ@0^khU^3Q*d>(+mdtLgjW0vVS` zuU|kuVisre`;19t&j-gc`$r_t<>ZQX>FI@WJtG_-w1K)`BRQo-Gz$X`heja>1%T^@ zF^9BYrFMF=?KF@h-fh_M??(RiIDD{Sy>Jg85mOX^T)6HXx??+)>%rhM&(^viK3|R5 zb^g^@Kmqa@L#8q-B8#BzM(HD~g;CzJb+OjV2u;$JU#*u8Az7CU?rm|}!0-ISHDWKn z_)47CURUzw$7xk96mK5@4MOy4BYzak_<{Oi>vj2msH@6_c*@bA#vpH z)Ymh8c0xNMtSqEQE-@g0YVS8c&o@*m8blem5~OI>kQkGcdr>sVo-e2h#kC0$TMllF z@r~%2K#y(<5p=6&#+TLC(Aq(OZ|I~(W$JPAxfn%r8PH7=A*Ncb0^U~+13+y}TQINM zYq1qY6j3ELvHo!6_3MyksBV7(RDv3TPa(JqH;IO zda?ZRKABO8`2;b3kyi9UO7vBqU{>NCFt@*Ds9e|Uilskq!@e3B|EveszuFCcUZ-~w zvb+^dLBNDxVz%JHD`v$D_|Qw2lF(_^(C16)=)Cix&o|udhaC0yNDEI-J$&fPt1|z^ zZu~>mPj8+iJ7m|T{gI`;^J+4b=w#C<4Ok9vCtB1p-Mgkv@aG%>@Wh@aKW9L({=|_y z%>Io4oO)0pR+QHF?zur%B}FzOGplkUtm!$D!zXe&@1nX+G7r8fy<_hb#qAvCP&=uf z6Ho=f=JT&6KoUUf_sq;pQs&RtU9sGqqbpW(e9P0ZC7Vg3lqK=xH^7f zR$hZEep--dK$fo$MuT|U75u~gig%w_sK!_k@=k>rRbyHcf#zAPdosd0<4xLz|E9AXV7Cm-GIqm4*j0gmFmuX zOigzTXn6R%Ev44*wf#+jyqpf~7+Y-8=e^`|=kE={vKY`12ub@en{WqWlMo~Eu z4YoYaN=~wkj>yGV_bjNS5;~eN8KZSBd5%6&UPwW**e?d%&}WA-O_6n0^3qhT=*ys3 zduSd?=wY^omM`Rb$p~+y_t6&5Nltvu}7o4v<`dGhbjjaxE&YV zZVx>b^HfL2Ja;HV@5sXg+%h*pYlzz3Vr7<-+47j$?yPB3h~L=g`A?ipBVW`wj)K`B zc(ZIxH42wN2Lia?B+X}#wMtduK@iKUj6ZR@AW!o1>wL+NYlve`T`BVG^V1ah2 z(8apP8l1~G>M%<*F#|a5q?a{zdjQ&X)Pb#6>*;}1)#t# zql!|c!C}5yRun!gjI@ONh?LWv?DDVmxZK* zlyU$VH!n)dAjE{bFV#id@hyr?&4SvKuct9zewGzzm7P(`{{lLRCxOrz2^8Xkfem0P zc-#=^Rf9iOaTGzi^0*u7wBHhx)EQQMngh&KzBbu9=&k#U>~Q*DTw~pR|GR*xgY>ZA0v}^@ZU}TVf(HulmNxqA(gzl|<7o{8`Pt z#(-W_t6|oSnW8>g^vWD&r5^vEbA23TQk5EgvXh{ok}rLNjKT;uyCl2mD&iJCzUq~s zDB@$@OZ3#cRiF(VvuA^&9CRf_+qc+W%7nqw=;U}$6*zt6&yI7=|5dg&QRSz@i=TuU zf>-+t`76Q8e6&qJqvDY-@4{uU`QTMpUE~T6y>!uM=_Sk$w2N9NHhj%SvhB=3=J=jS zXR+p}kS)oKj*LjzE+gr|&c8{F)Apdx&ezz_PR|-ux~xo>{H)yxY5$y3a?G9bdf6Q9 zl>zR-dl;l(>2K9QGZkdQ4DA~C%@q~W$C&=SRiqlDK%q<|)+{eBK z>5fFiKOWw1OP7r9z}wz<1Fn4$0FQBBI3z*|#UT8q0dCJu$w4AuonygEG8BU;Xu{+I z4;NZxS{1rw(lrP998|w#V<2%geS$i*Vd}-u{qwuGNpb6E1TV=@Zu;hmxY00^5 zn(1H}O0q^JWK!|sp;l%4%ER%`BPwi-=biMXX5T;Rrg?*{fK+UG4j3;B9l%}xFK`+p zVl$}H^YzHrLu`p{j0il@1akph;Wx5I= zrG`oJX1v4@eI;Yt?1p1qI2mrDR zpjrxY)w0i+wdUdtPkY2*u*3pf!O2(QDdMpZhcaw7?H1-4Q#bl{zg=-TD+g>u-OSWC2ZY+6+V1Ee@X=ogf4w$ot1KcT>Mc< z>$X=DM=C*$oNOBOu2#wVWyoqe(v@G@UM^PAx*5>*nkbPl2^dnA5R@h~P5CPm^yEjR9C!{g z8bSt`t*7nuXESp~rfAIeW4Dj(bp0sF_oUy9^Bwc24r!7u^l!K;zfbVh-I%_#z==LxcLa5XlVBc(Oumy2n zz+9Lj3Rc?5M?RqO8ZRen$-s!&U{#BfJ%5;TQD&T`u}nZRNd)|JW2b4od!8;1~{Bwg-g#23L*r>eapoFsRNfB7DKqKIFJ$fVa=D(J5)p;EE5nd-FsQw3u)KxVI~2Xj948#Z+Hi0aeKW$!}K5lBgZC!XLA?4zcH<7(*oMfFU-m(pS%gFvtc&9d;_8&2lp z&-{HB(MyxIF{w4Km>Y=ZmfC{UWzbk`FaRWE@R1T*plv)oj@;>ED~*yuP? zz{lOo(tO^>{UAawkEq22zWYP7zuj4;*Ut2B-}pITv&};%zLDmt+mu(^fAa1gCIaq1 z>He_-veQ{89}+}LtpCJbeIy2x-{?=%A@U=}3xFJX>oL_2ZH7U(0jTi!rw>yXL!I6v zMT@bp@2NTjfb@?XAy-erX+;G)1gdEXAWw*s866u^_Q$L1+*w-RI03VELN~p}(IOOP za9xTVd4Pa0I-_{|TZH1BP9$b`hMF3j@P$#in7Wu&g=m3wM(zyC{^vlCoAeaE!w%ZW zW3a;9{`HETdlv!t<6ba4-fr4T`D{gDgWOpgF30!M z{<}wD0`+lBo%=Z{V`%lKQ++P=lQn8Ef|hBz>%vTSJ_v>d@~*|%bAyThtP!hyJPZR2 zAEw5;niia`@qHu9pop6E*)p?VO#x8sJ?ZXQ?3;Rb3)8P+B~{CuZ-O7$5AFAhiP_EI zPUejncw0dWjz$W&o$)dFZc zrU4iH>!Zd6LnQ9kLl|Z(n3pM@T}RelM>NuEdP5v%;Z|YVt`+{3*bEF{y=+G^H{q9` zMb@p8ujZ%6P!-|(7(ooQ)mDq;lr$^k!1b;BfzyxA3a}Fuk-!;EtNMxXoSxiUtIUR` z6F@#WgP5kVt7}na#WPovzEeAgy|mB7u+A83S!X=%?L+?R2*WuF?vc^Mi3&guySh)w zkCzvc6{SoV;F|=7v$P)KXI2HVe}G8uh%!jnu zsEDX3+8Qxcu4z|7qgGifKU&QvD<{YI60WvqGCJK3N*YG7f19@7p2HU;;&btUn1`^_=DtjFQVwv` z_KB*wqQ2%m@)%NCIF`CE0eE)tovY{V4IV(uaW?>%=QdLGW4x1aoS8Gp{Tw2jQKRmb z)IrCkWf|nKBC_b6Ugr?p`g#`o?qK6<-A01Q*D$GAv(tKk=N>Al(Nz|T7{<8)p^v<;3pnGFh?c!-T>nkR-mHd55`zr8a zT=@muKo7x0fkKd+ss+QYOb+Tnr;CtlTUImJv+(GrDVh-!iP>+Ga~JWcLfa?-toZb7 zsHYNl%yKYrQ^47wese)XxD8w%u87>HXmlKWzDx1VuCbIHeVhu&dm(Xnh9nXh14qy* z)n%J|b*!Ltr}VWg49vuCrn|9L@>&Kto*QuGW_}Xh3!+rKAnmm`8ywu$m`;St9BaXn zBjT0`;&58h`E7G>Ti`78`ep6ni_PdCA0DElstv4juerv-!YXOkf=d^7;yo(>Lwa1) zq-4bpjFxYrrti5944hcSl8xz&L?gui?-d_rRdB)<6?U8@NF{=N_T97O>h`2#z+o|z zpSk{oq~H{@a^Qn;{VL$U=SZ5hd5OR&Mu$Cg{IXQ~pxA#77V{(+aSe=;VpkDgg`3wt zt~EvL9)k^#LMYo0x+7tu9xU!$3krzQ?YQFF0!=~935F<=?f5Z7aZ#H~)gdN0=YWkv zErM?xaB`HKg$n@#x@!+MT>6mLwV*?L`int_n?zB>{rZBVroRT6DwY^z)UYi{HEMg{ zfnS1Z8WW>a^qkJ(TH3R~#6Ve5twyI85>i6sZH=sAe{%Fr1K}V7C4SR~BlPil#z3-N z$Fg4fSLcD{4mh@|W8T9O&ciFy68a+S^ zJpEnErT+C@UAo!{?y(dz;kSVx0{EFQm4!)msP>BktRi+I@zr|F_E@~<7K2K1HWYu@ zO*bNUVYC?d<;Sbkk}x&kkzrQx)yU|!DM~emw}M#tB-j}Ib6f&-lHmOUQ=jg#pGRew zWM^EcqmCG1HWn2{3MwC@NT3dKUpD^h2Bh2^Exms}%V4 zj?I+bf@zCD{4Zk*3JKK%I*G~F0IOM9^JcPvh*n&Cj9yy!4~nb$ne~pew6ziG;o-Z8 zZ`1%x4Add@_U)0tFpPj7me*1{w#1!zS@#tp`<8KOhx;J}#JU$9#xqmqR1Owis@-jLzl& zUpj!Ry;GO18$nC)XTlLpSXS2O1IuD@6=$&{`Ln5&zxA;ye6YoUPR*zR=Z@UEiIcJ! zl)X$r!+@@E$G`!_J5hrC$|;%SUG(st97(eqqn5ru5_hWzcRQF;-J1;E1U$Uw8BKtm z?Qor=4vmrBL+)G1)5*nWKV?E^F3I-oEL%j3Hf6E}Tw@&%kFA8YiX#?aCVbgg_j_Y4 zb^Bf{4*{HNN)V|f|5jFLQ{4!ngtzlry0B36$aiEaA_BeDkes-+@yY5F zCLjJ-ave@8Uhvd6BH3wSsPWHWdQ{T4!28vL&N5;zqWR%m9OfBWxC|J1pblWh#hB#H z5JiqEvBKLtazse5SglJS=>IacdOeluYcM^RdKJXt>w0_VVW+QOMS{@aA-ucAdE?5> z#~B^9V8fG&5kJ19Bzzvy9_51o5u<>+H{ksnOZJHLtC(%i8xDs-*Zej+Q~|vu3Zzq? zu34g8nMA5g_a?gb2C|bGKWrOw8}3*QFi?S#qmq^w%=$CfghLN7c0b_S7!JOB_kq2y znO$!q!=YG1BKRw-462h);4AY_FaY+xH5!QyAVO)i#$Y;@wIon&rb-#smCXCHJ+tmF z0wF$nS{rG?TPN@u-r_NJBpLl|{z(mcECNbRkzBLMMeu%rSr$G)t~~b<4}P5>3Rl0d z;{yw$oQ8^a982&EtZlIH2_rcGVqE|@c$S4Cg_Vzxig=2(6h6qT_fu zmGZuHM`hfXW%Vu!ix+nQj>biBnOM|2`f*+__@`v76Jd5T(x0P;J^44R2)B-*`TRXkR3BzY3`@Vfq2m_W1_s(fN3vbGZv zQlN^T2o<$-=LxFeF@P;YCJ&>rPN}bJ5Qj^1ESpG`Zu&A|e?MoTxnB2$(3SRQPA{S~ zK3O;adL7H*_Y4!`qVNwv{B3wr0FC{N8KEV$9%4K2z?5{3n=V^&M3!O|{SL0|nw`ws zMO7m^&;IC=B3a$0DGD<*`25*AaA1*fEp-%lsEdD8JY6Qg^`{#3uj?fek-Rnzw0W)x zGkFA)*C}Iuk(3(&-^4F6hSuX6na57#T7<;8N*cR`ctVYB`!+{-eZCFXLXq{y1|i1r zCIZVe1J@bo!rLiAb&PWE-aV?Unal&Y?uXn}ZyexfE~05&c{50QT9s`^2@!xZmyY7s z7V!?A=p#z?SVXBQjRLdEEbAb!q0}dP<4v}jxJM&u#i$lH%7JTOt%99rc?Gxx!PvV` zpyV)5R68Wjs#5zrgm$TH&d~9kDDIiIL4IfF5{|9-1AzDCJ&v=pWT5B^CbIy1vCE05wnyUYMPgYvP-9g9-Y|) zFn$8dV`h7hRj&!O4~y$EG;Sx^zIV7qC~VoW(`^guyW3N~?E9Iv8ZZ=6c%+t`TS(}# z=;yF&6}9loloD6D%s>>eOxQ$9d_I4NIS#mPlW(VqfL9-aZagB4YEL&j24T5f8eP$v zJwnRsa>!YCC)=@HMp#rbPBh$~vfn!k^hyA9 z{^Rf$d-LGhbD<^(?SXS^s_@A#%q70$@$*$GDp>^5W(I zA`g6y&?Z36Z<9FI4ir^uBF_||Wd^IL1(S&Cc`@PL!W_Oo!|n(x)RM!wth z^vLq5`LB-{Jw`MT)_W8oMley>-T_uz&d$B`pmGq$MA_MjKuLivn2mKd655YqCJ)Xp zm;o$kVsh?~UHxPT$e^ZVQqQcgrs`n9GV5VKY7duMWDjtaFOzHvzGXfH{w?DSOV-gO z4r!%^$F4S1=u0^Sn>UB|(; z@00egL4HLGG_^FSEZTyVJSF*GAxThx+oQ2v65PGj&kd#VOFIs zyzUA$>$=0Dxg__4&dwkYF&snhL~hB#r5Yd+wDZ)cBV)G}2Moy%3EuwK9X~A&OvtB^}4w4q>8JtBxM3MNO}_ zD?lL78Ov8_6l>l&yJmRc}`tN(z4W2ve_q+bLhhw_%nmIdNkMp@62-&;0vzoA2IVFYZXi4!Hgbh7u!U9!(cWP~G`3 zg$&hyIo0>+&Lnl#Q_L86y*{LSJav2|* z+>>$xy&ouh`J5Mf8d!L^fttXs{XN3)6ULWQ0!rE8M6g!eiOzebX=<3P#=Q4N=*0vh zJl9Q&dG2Sk4OlN7dcGC++7{m7^#MQ1mh4DJ-$sZL%YAc^xb0V`ShsF~1+&u&hC@Bo z)7gDXgqcl;6)OiK0n1y^Fx?=c=%)V$~Qx(c#_+Xx;*l1{;rA-^I3FK%}_)OHRJF!|XRgu2JlR zw>6>4uLMBz#)EJvb;0N8p9sp7ka;fZQ|bf!9BcRtZ=j7Rn$^>xECz;zA*}->kCJfa z?^op;y^fZf-I|Kxm>Oe*`!ud=9qJ{TZzLv@6YdbL;v=ab&f>YopaQ2A|7Ib38#4NF z>eE{?d^yP+J{fDRtlUPPddK7!Ym%1!v!O?$8&-bNfI-t%TGQTUKP!&U0b9-l{bwTt z*w%_UtStd+SMyDCD4px^NG$zcHAJrsTT5Wj` zR+iRcdY86=6-KDp+>0U=;DhwiUmpn-WW9M4t{{v`=aGf(9}Fte$?@HK=GGx9V%Ie5 zke0oWkLxT0gZ@+%R3=-*qdQ7;=mRQDb?M*h^sSE}Mv@G?8^ z!53~$L7yKU&`JS0U64HWD}+JV@rcEkBi^b)pXSt#Hvm|Di$qaGHrpwnt9NA2VP z_S+ZM6SL_#*5BsXmwL2iySa}1v?I&JPuwklv-C=lT5HCSyHhiv-_v?d(t>>zf_;sl z1^=^yysd2qDY)&5S5^M5irpaBDEja8VIdAO*puVdb*T=shoxjl%BXMQQyMDhP;=z_f~Hi3LJ@ zM+5Tb&nG(Mp%~|Gx`~NkO1#4jHZL(>bg358^$`kwL3^VHSZe{Z?V^E3T`WKK)h`+w zDTUKhnYVwR<yo7eYjm zaXH03E?Xnc&=4T_&ROsZhzj>YHHQP{R->#i+^AQWvu~I{Z(}5q*OV4vxp5E4nzqIn z?V=C_#DU(Ic~tS(D9~SxqYO6UVjBsz!%+Jnj0eg!y!9S#Ou5=BmUn?@Lt6v5!;dir zO)TU;9+CC$oi1hoE05ga&oT7b_1E_w)MBc1lPRwC4cK8E3RY2rj@fHMcD8i>fAXdZ z`c+K%HdAYJLhzv_4?!y|ya|+!NE@`Ar4C*DS5_ofR?ceSe)AAso_rhC%ea1js>u{n zeRms9w1*Tebs5~q3+Oin)&1}rR4pHdyY1>4F)u%k7eRZ8WOz!Yt89uxJ&B_^=<^NG zay9O8?RyUDwFEsJa1?7#VXt2U&ynjK(6;k9<*k*mg3f*L+~da!+^(FI&as5I2RS0x z%TWk>nU`7Oyp!nN=1GrezDCbigMmi2v`KR351(@Pl^Vt2nC>Z+aktwv^m+JzV&jc5 z?zd%tpUf!RH=!SxUk!g{b3<#%A967E9%c12lk>(t06ow{0%r4WlFf0 z)Q<5epbY5XW>}>@NyYE&;tv|cz@9)6gyDg~9UWb6zzHeIAQN^YcL05Y&Y1QgD}MeSc~ir^$q5NzBj*di+&S%|#DkU9zn`&}hy~zOQMpt7-~Uq8V`InCH||@z z!tgg)Zf1@A$}fuhCfu)JVOu|IxWY<)X|Hk}RC3sx>#R=I#hFV_`#*Wq$laFbyZn^V z%hgY$TWwYn{4Oo4>i~gMbL^SWO@L_d zn3Dx!RSY*@%K-z|Iq3vgW7p9C9#92jC$)HhiFx)f*Mkq~Q}~5+?y9k*ua~M1?=R?M zDpf{uo57fvV3goOfd`-RzzRoezbpV46}^Ky=}}4XE03zD>y(Tf4t`2yztmT_BGS9F z%=-Sf%-7S1b$= zAo)x28uo<7Gry~qJGN(KkRp1CqA{ZRMC5(~!8a9G*+BxY6cI2J_AU5FJ@p7Z z`i(ZuDq9cKNvWo_G4&2Be1KgLI-eZ`SFH>Z&{)r)Xcn+qM2LSBgsawF5J$JS-`F4z!o~A53 zX*Ym-#Go^NIrMa7?OpB%K0q#{Cu!?p=lAF|9RW=ecitD15~>Ha*~}3I&I(;Vl4> zM`P6Mdjd)3+ul%qhy0h!H12iz{ZPqelh6e@$0C`ObT7Cjuhao z+z1&1REAaW91;(P$|k(@v`Ua(a1hz#+2SS*)|(CMf3jh!6V?C zIbZ}^E5x!OSr<)Ss^I~erkX4r*g$!EA3;zxN9u`JslKcOuncQ|0QHgYy_aQvfWgS} zFf9e4IkpG#Z3{SmLHK|&T#^_bkcHju+SvLJ3al7H%)(k1k)!EuC*sXoN0$6Pi~F_V zhM3XjIrWcnYK}wLpy^F8Q9NC(uiQP<0`k*;3FO<1(Pu&idLV|&e4nNASVWUVG8JpD z0BeOYKj(gy5#?1{M#lz8ljDjWVwIjA*xeXm8~@%dI)OlUmR$+1LH0uK5!ozPt4p}y z9K>F@I$JOLvRB!Q`X*eHXS#fDTUxGGjw>8*itRSz4drmppOgG_$<`4*J{yIM_&^Jp zSwUw&_(KprzbMnu0&QywS9F&iM(v+(M!U{Ts=1CX>C|BP*}IhzCyH+AR$P}t^J6;d z|0*fJ>zg2nooSWNJrZj=2;+(|iez zU_3~m%43de0Qb4`Aay}>(edYlakfBnn*DFY@&_2PWTk=x6)eI#)LSYx{&@#w=BTl& z7S}Rkfe;5=0fXG!aZC_2 zlE^(T;6-tB1lI(R$L}rjt?iNz+yYQ_^M{KLOwv^jdodoY->H6UVyWq<=JIH&A((sM z4!9nLR(%G2%?U&E=phqjg*9*(bJ$*LVqx%?_fK}F)U3AyHRKv#WR7nowJ@n--Ur@J z?b9Ya15MRDN->3$?lB@vEuE$=&gNuq}i{KlG^;AaY4fJwiZ%q~9bxs?; zXY(WKtI66rToC^e)$7ZG=hK)cEq-G91I&8K%-2s|(o!5#dXD4)13(JTA1s%`cb5?H zmb!3hIRv%<6}aL@l4r585tZY!9`!@S5Tu+^O?}f-jkakz($%27MZUTK7CO(}%y*jD zeL8jJOYu}GE{*o_oTEP$K1)^L!9OGsXk*hseVXZHW!+wC}a zhcUCEQ>5;2Z2BDz`j;XJU9>{G6LeeQ7KGIS4Y+7F7?69EZo;lBO!wW6xi`nT1Y`ZT zvkzAIFAy!aj9yPFNrO___bA>?b7~~1EO`A#;JD`{&JaK3%tlSASdtLPqsESBxeuBY zC8}t-ci-R(TOPHx;J~0{(+~15=%yOFX+b)Nnx0h27Hjg?aEfNSwo-aD-UP{Anq<@nxyJEzKC%-M||yZwl_7M^L=9^Kt#p-UQPB zgq`yamUpe&TZI~;x`l(JraS1VvLYn!F@WQt;XGb*AU((w@Eg&`uf$pefOdkii)N!F zMt5cmDoOn`;ne|7sunCg;{ldMz!ZQ*ebo+qh+re`M3mnR{&al?z$~OgJOx&y19;7K zNmM0N9jNTuLEw7>hl9Lnr62GYeAeGd=)74a->{qT*~>)HrU5^_WmU{oudn=6>-)*I zu0qnCpjo3y>R6Rt77kVz6=*9T(B>Ph_^;A=KG#ysEeUu(mRK@0O&fgZtSF>KYKRqk z?@q9*SS0v=Tk*Bdw;IXi}1`BY-vu!lR=f~}wB8E3+{Tm(aZ|ySc zdX1uKfwU0VBoi@bJwPC{QbOAS-$_TZ*Fko50+C8(04!Jx*G`lQW(XC`XuIg0^b$7} z6Y$Bc8CaWwq?Q2C3`^=4oGCIj*7Plu-mJ~5W6;^|OS1g6;eYshBfORtX!9E0_^G*>R} z;#wv`XEBxUr&$k_r?r1?MDD(4P0{k}OeP%tJno;AyNDJGO#n37m5_f@j?ECkAN(MI&vSWrOyAV!!dU6u_Me)CdU5Dg@*dWL}25A&xOba?up>%*z%rhn^eH&C=3 z(^E!o!yisj=T~#BLEy-i5o`{7(3R^HWw{HqG>rP;U6M9TNiA8@w`EPfTMIzW3K*IgHqOq4rP;7s;}q!-oH1X%C3ZFUD@nMHR@SJO+>wx8lkgaXs&A~pvn%VBfI@(`6v6b zeV8cI9J3j3?#Iu(KNGlze-JR`yaFxftWA=&H@u4 zflZ6-uQoH7kd%`vbZOoTDTN_fU?ruw`x4pp1*P`1iyNX=qZ7UdY0q~`9ulH0aF z-XMjQdrZ;8WOJpa)B-c?@3765r2IzwP!{*=|3}rChsC)6aewZ)XQ^gt+M8*ec8->* zNu_mKROpc95Ta?DiV!M;n0uO3Xqo7=DGfphDNBf^)kMokD5qsQg-SFDMK$w${mviH z^Ze;@U0u`ky}$QodB0!pn+p+hsQx+Gmsump(#^ulAf4+PS^C4zT!n>;lDxClqFSps zsPGa2w(Js1&|{1KjUEb0D9gZz*%jRVZBV%%YE!DFwhiI7u! z)7mL?ndHRXSeYfz6CDKP^8hD+rU3fRs&ug7gOmIH=_9ccJWpXQ5st`m{%re8fb_-Gy)bb zBn6TMYw8#^79zTZ-j(3O>oZ&cP^1zF z!3+$la$^%b<^DjV%3>T(j%3zyGKb|I?)=B@bAUISDR`BMbSN^x9e8eh4!=jz062MR zCNb^Pp6|{qnpGwKw%P1IIi~`O=)B6Q-5;rb?n|C{AJsi zUl@ZEgn<)6RIDnm+cLhA%-&>EL0=#_-;DE}&}(zRZtOK; z`yF-^P`GPSWgP4WbFqP*XOYn7qrhrbulK;QuZx7H@9O|#=Rx!7{nNarvuerCw&6AO z7>ahqdZnU8WOP}8{9n(6P>czWW@3F1?LWd9?ZjQ&m{OSytx^^x_E1H-N&r~5ZAUGxu8=q-iR3g zd=;)&F%MR$7D2ILzg3&@*z?y~Q3vs3RFqUPB2abqPL=n%F*JbF&E_JDjhG#8uz$W1 zXgnf|UubB0m<4)0TS%ELV&o8HkS4CQ2Rl*d2JJo0Q7kDwqO9Nb^C#)iGfZLjLjjmb zLpCJXfJAitgPib(PKF%s;)AY-&O<$I3_u~~ZS*Lw`B?^i7coTx7OE@27D_u@49RMx(X}z5$mM!|8%N-LM|`D|Wp$k{DX}scpq=Hi*Vq7{@XXVD_v|Puj%z z#r(jHkrE9YvY?#qMbZQrjvhHa8i7y|qs^>mdJPx^rhHeG&&eKgS z_d(tLiVtAydOiA{SogvcAxDRFr%A%a*!huImc~uuH`C~ z_JK5j;x2wPOkJOTw{qO`tGcCCT-ExFPDS`Wr~_Sk%y(pTDp$ND99W?NV#>>`f!O^d zp=T9zXcH1kyqkKO(!G|2bnuY{Ie!Bmy@6D zq>;CDg!h64Yo8xtKEpU{Y*hJ@$H+gxEr{i2E3+7rbZ$t#LZ2&o@Si5s=RDd1-hbK? z7vPW4pCty+m&m>RA;z2_TRF0bJlgFq1`@gtSYWIj$7AvF*=osE@PWh7L?K{%^0JrT zzk3figx;r6vi#+5MDG3EnE&qg^5wc{SRXCjnzMUdgXNXF#kths#mD}?>8`&Hj)L8a zM-E2}Mq-!P!}DO}=MLn@9PXNpJFaS2^90Io?8-quQ5qz6UKU_n!Ogxqd5WtT3&wY2lh`U)I^SWyyqaIe z{p)QhcPo+2hpK!!6o9U#;}-YaN}}Y>cE5mM&^{QKxD#1(Rai=r5L>6xb;z~ygPcKsYDRGw~UaA@3UPr-|;3&q$H0us0nQ_-2jWucAz3PdYaCIia#?AFseFD%N^tC;yS!~N14CYN3RJgNU}5T%0rH&7p~$CWi<*>?hdn_aOErXxQNk@ z^iH9R+@YVqn0=TunU{=emD@76L3@sG!F*Y^9&0na0SoLzFHM9?$p8{rIU0FgE|R5F zkS9LqbASHCW|D)WG4=<+4=uJktUQ_MhVCqMgF6fBPzg1QCC-^Xo)?mJ=24U;mZp<`BrN$oj~I9my1&>SGel_|#f zxKGwHgFj+E?3NOZ#5?XM-6w7a=ySp2m`tQpeFxz`bx+=ZoS;SEsY9g+zqK8tJVDp ze1Up&vy7{F#*q^C;x<{(nzHSHBmon*mfKB^_k2}9)D9FtPNniE_Xvg7Y*3rUf(Hs{ z?Ac8#s+QtbUzMwsM)r2!@DxUcdV07=ph6uKfQ{;rYdY{@OHez7KaJRdQ&+b*kv$+= z{Ycm*aTJxbP=v@3+b7{n)IOBo`9&r!(HluR27Rgd1@&Jw?`Z0xs+2sGpunrWloZsY zTy3WKrUIA9puYHycq+k?E3Qn) zi32-K;}@q@_bYuyF=9Z^Gn>YyU} zWRnY-=J?C+@Mw`{;kHO!d!HF2m-X;hBMeV>K(MwAFByjGxzm^uI3frvL6Ad$zGKO_ zPbI{(X^z?G_*U2np(71QCGJm^H54?M0$x-qpLZSGWJ8sq1FJ(BM~@?uB;l%$hlGC` zcOj)g;_;9vME5DmE+$7&r)p!Da*Md8qM@~MZz6slGa4~4&a8cbxzk|KT?B<*6rmLzp?AL97^L&pwgC|sKi*M8AY zUavQ%LXti)j3Y|>R3z1z9LZyM;4HHYhRj3V9%)^&bF`mOpJ&Mrg zJ@T*`Lpk-uO~!Jo+I%eKREz>&w=#3nY5em)K|))Zi;BtH<=@2VyYPAGW+H_(ba6I5 z?x7YiF#%=Ia~7a>;56yH`^IphPeITkWEed^`w=f`_ea5}FKWTCUd$@$ShzP>yub6p zb(IU}1UyYPRZaQ{*q9~zAIiD2Cl;REKg0d`7B9(tY|mf*MNTaR%hb54C*T;3fAeI2 z7}1f+8oGeUrskBKJ6=d(=Tc*a@bvg3p3dDVo>6$`TM~)l;a0CuB8AT%X*PvQgiz;B zgyo6+L{*n$&9wt6G{oLj}`XDdGgrs^8HkJe{2IY&HyD%S?T6{lbr?8WD#*jt-~E z{5#z$UepP{1|4J0fUoj05XqLVgiiWsUpD^i8ius5H68+Es&PgIY;c(9dV%1q0R^7j zhs0vnoB0;Ux`{w7pN@v0nr+7xMto-YSgT?V^O*)sfb@PINXHL?jG;48Ksz!3>0w3&R< zw81s!3acImGS)l+!d|fzzTy@)!zce3H+rMY$HiC_H@Bfm&d*j17y`|MvXtL3Rezy0 zFyJaCAqHdWti}x|n6(lUjKDJ%b6b&4pxHuqP8Lykxfkr3mH^NM`|#JqZx-KP_JE|E z1Z8e4up_(|ZY5br0w%XejwFDZog``V5FB+c7H z_N5hLSN{BcKgGL_D^-9Z^`y#{KN2Yw+Buj3 zpSri?CDVEZc24cy2%tO-M z+dKkDRmFqOHCzr}Zv+7Q#9bf8bf#6IX3Xgt0B88shJ9GoG;+j&U>Z5IVp-K?l85!D zSIcnPf`MwZHyH6;-ZCjXW;RKEB2X3!Gzxm<=o)*lgL>hK(>^~0tbhUS!cK)zLsdz} z^7y5lH!X+AYlnV;ZCrB~wSmyj2QEj~=CWkGXmzP-c^zrh26^skfqcn}dj4t)Tu+BIU~hU}AaSh5G&6pGkNE*m zTg5fGg7QI}mKgjD^9acr_PpzZy;u+9BN+2*24PprA+Kk?a84f}NNqw66;z<3*8rBp zG<zUvLq0p$NE4ck3;~ZI>8otXZ=gH}kSjJuR0QO!oUPB=6rHG&vn(j?dcZ9Ru0kVYJA=zRIKBnx~CSfo`8qs>JIWU4XyZk<;K=K{Q^A~{ngPn4}WozY~ZGwH^Clg#Gf)3 zWqgLZN0N=d+XQ(IDD_MuiJ;CG+G_by)K=E0AJ%|2!1#f%%*b>P(~f_YM?>naXzczj zH_m$)w19apXirA6Ra3&m8oVY;U?AtZfQnwlO>@{DzEPzM(;rjeG2dma z@+!N%f}N5II{TYZ-5Qzw<53Yp?wxbuVBO{}?>J*bzVVdJD6F z$MZ@(M`Hi0op2RXrS?;96#0$P&5DXd5 zW&uOn^xHzUoVGsrUre!bH~higUijk-BZ3QsYcvyscBK`noHk2!K6NBW;I(~JbXYOW zG<>(KX`LP_bS1Xap=vZE$1fiS0BE{!T}#T43u#mHg3*yfihSkba}m%ZQbHfK)~5~i z1jKh_=oI<^cqsMiL9c=D3m-$%)`VV|EnL7)Jq6U;fF9tC{A02mm}{kh!uQ-t@d%f4 zflkd7Q@t-!*5k~mAD=Cz?^BSIzOOK)2TXdxmV8!9xZ`~en22Wf8wqSV&@0avBYdsT zS5oz}QPZ&>v!V5r^XQ{mwp{XEiebWxtB&bt%1VeO@>tfpzoEXtGJ)%O1O5*0z`c8n z_PB6b?NR0FTrLFmfhQ;#i#cisyi7p?QGBmgx`4)T>TDX9OMru$8Lg5kNAd&EbhQ_p z&1pXP9D99_)Q=))mlUCgTriWpgKrZvyhxu-F1@X7+P@O*|Jc`@-q02 z7d1eywR^3ExQacZefe*~wM@C`r(Zdi_T==P8!q>>$yd$VAK;O0>D_h}n_oz-EG3N@ zlB9zszcX2KnYgedCV^DQ;>PO4S;maF7W}przkn%PA>=}0HNXw5GVN9r#euliz4M%4 z7qp|f*9#d~!-ful?)je<>C`ZUa|}zwv34^3DYfJ0hCNN)etPL9^Z#s$RpD{485x#38_M2wpKqJqpMrV;9c4XY$u7 z(5qhKT|Q_f_bjW5nupoCZ)b<5D9w>u$&s3fa`1NAVU>V)pRfn%RW3wc$j5mYnLL0Z*!;#8%qn5enN3(~})&6&umR z^pxK-_$771Z^EY5=K_(^$#uO}J(u>$$(_f*ehk6uCu2!H3(-NSGEqyT0Oes{Si~ie zZS^IcD&_l?G#*27C4X`W-Muc zq8^mf7}?6WL5nWbZ`fr2ISgo9580)-nhBl+NuGL`0@S%gofea>;;g#wYtHO^s*dP5VLsrI5<_(D=fdWk$|0O2f#(Jp3cSA zmJg)h&&tR-!B;bnJ-agmM1z3wxixhBzPcMSU*6*1PBrkUV0F5H6((To}ic9 zGE_eW^7x)Ud@iJ3ZxNR3!EQfSt9SQCMK%ZACL34;`Cy&tU;#Y&Wrj|af0+tn4sQu( zs&!*jYS20t7x3q+u)A7(wz*1P?vkE|#UhWnZ_6Yr0)+0S^<2x1+_d~60i7psdj^4| z8(atrdtk|!Xux*g6p}L2CVT;~=o9Yq6SuCp`0+FB5e<2#$ z#PAIz{LkA2Psz28C(J@dM+rZMH~~87GY63@mmr?BgOqmE_Nje5Jlm8ZMNcgt*#mMp zspvE6_Pd`XBV8t=Eisdr5e2j~TM1Q{`8c-(l~fSx_6|_kP@fwaE=K$!b^bRFyGGlp zNnv~I3Af+3&L@Y44W<`IVD*NQ<=m?~Vi?S3yXGR`SyLL`B{-n!amqWd9ksl8*z?Si z1G`uYImJC>O3>_Cp;I;3-4BH$NsbiNWH!*%+?`bFUK$pjYIe|uiTA=eFELY|h?A$c zZ{}(ncwi0xh{rk;rWc3SN%wpZWT#lshDDHG|8LI%w~!E{;Q0>+w^?Ff9w$7l=H;+t zH_c9-{&{&c>X7@LQO+jg(XZ+tE=IgjXy8i(_d6$bHBi_ZNbFkpr?n}U`|0zR&u-dVsSJ znvr&}G8{Z$6U9)>k!vY~z$GY>bI*=hSsh%%4^!SE5p?{BV_ z1zZ7!9|=A#AUS&Qo7eT1VXUTIoZu$)%PWx4LU|##J;S#DrMW!EV&kI|s7^3`)JOq; zkT>JZw9XXd8&|hZm_0ePMF&YY&Y%HSpi!pxE}^!81`PY9nyN2xJ`%Q{IGGFt8q}%1 z^o7R{^w@1w9T@Dbw49%$kQgY(;)a_C{cyb?`KpZUG6NO&&0*bE&l-t|EGg@dg>;80 zD0uMzwtbg0+Lc2hh7JutL$grv3Cv0EkGlvkOCJR<#X&_V(FJt#f@8vav!R|GRQ8AC zQL1VmfhQK5T?D7M6)~*;^7CV_I)|2aO%o9SPX61|2@%{p4;u|!XkUuEIhZaCTrzO5#O&wGWhQ*=Azxqs1VLY9wE zc|e}A7L!bQ%Zc31C{+~@dx*XlW{lKt$NW3?qBf|9w*qu;#PaA}@uwQDYkhF?wc%d2 zVXL*E);EceD&2fDF9!v>*Auwt!W?OBs5Dy%ZoZM>x|V976;Gzy+cQ22D77H`PDMf# zabhl}^>OqHJ-VPN_Rk+Pmhc!kWV2*d%ya!r;w~Uj>p@;TbmpE~LAevDlE_O7YgMR>$4^ZonZW#R5I8Az2ia!t8IHeQ zGj}x!0dlO(>t8e9{+Nqg=66G1GMTO(54>(0o~yVUC_Mb(I}2C-z*?rj#@&xRwTCm%_9NKKMbrs7tQXE< zfM^bi18X1{=eKGj^GL)>jF~AHg$C-KahZJf0l3yDnM_nt?^ZKqGUG)Bnei{H1P^TyRPX$_@HfHoPo;=vjd+=%F`E5>3v82sfq{3CRh5 zwyQ09Tfx=G{6$0`6l4S?fCXI!~|cxp!ClfE58^g$mJunGrL8E3^s(J zqL2E`dDGZdJ_Dzhzga)ZU!htbHdV%W9kyI&qw2#9=C^onw>cJQAnVb*al_h=fx_W|C$~)^{k#4DhF58p5q^} zSC%I-ulgF){xqoWN<9gQu!b9ut8U)23e9>I4cV2r2_>;Q-H{wGP)SwUMMVp9_sL~(gpNwiHw7t5q= zrKFwkI(OVlORF{sdb6o&+b_?`)$*K8=9iXpcZe1rKgk-F-&jMeRR2W`6u9n@F$!=g*>A9&-F7Jj3Ms4!ph%U(8B|?b zG;)41hp(NSaEc#Up|Pyupiog|S&)CKfis}4_3y^IxPO^{JWmV-Vx3@+_US0jeH$1k z898L^KjiIn-e-PCp!n}-!RzRHZt3@Zz)3{%)Zk@k#wU8l``7}6Z*GxVRJQ8q-*PcU zA>I|FIMyzN0)gA7*?O`e%9R=-%+680jcVe=52iRogw47jKM;nCAwQcCz67PaPP~A5 z^Bh%Fgp_N=r1R9e(JdfKHQnlJ$~Us$PoLnA=t7L_l5JZ`I~tVzzppuaIb~Qmwqh?X zBe!a;LbxB+?eA|!PvQ_Qn+)P<*TV=wzda%6FLL^}iZ60v#n<>Pi~i?xye0y;Df6C@ zyydI+XKd>BvXqgTU#WWJ3&w<#e+7vdx)Xt7oa>y5GA^LvD_mfsRLux!ZAU#>YFN1T z7MLsIxdvyu07^=$Rx_W}yKN(UXmuf=SevhAII4QNSoA)3XCvMeIf$!ngik-n=I`+G#`xLWgM4)$Vu=6W)eF@7!W1kTWSDycq|F6h4^{PISnQo6sG;ZKEG=Qk=Dxx!rw{t>K`<8HyWpUeR zS=?2o3eNM-P6ph_tZ36V^<#{yIX6LdcX`7@13Q46xH7z$Xa$&&l98l_qh}eC@&t#~e`429b_GJn?f4=RSqA;%RT(#;eEq)q$0Kw@wl%flzwzyzR%>DI6 zxe$0>LDD&LEE#03{gcBXn_*XydxQHZLcNI#Ku!dbM?S(y#Ho6D)lZzC?|CMp-J98t zFlG#DBZLb3Ng(bTcNhDYB%5S<*L0=~>IT`fX{zBB1{%1#YZXkUWa-+g#b4p4`hd<9isVlYYwU zEYL=S^{z7Xl1d!JbfH$fqx6vBt{j?oy9VYw8?e$6Ft1HO2~?##`4+J}V1T(&wobTq zE?PnqplAjutiVm&Cu*wJT`I+PVUYlzqhNKMd`B~tuVOjr(O1tYL|zcMmf=tiNj6Pj zA5yFu`zU|$TorPo4r^B3a>mr1{pc=sIwLuH9LScVajCXm*N6b{tjNt*w}*EqZ%f10 zP%fUyh5T~z+f7~nr$t`Dbija=^^KNY_L}eSqMa^%MT%#Lw&2hkA^wxqaI|hOqefZ0 z{Hwf>;Zy`at3_T(nU1wU5_#Fs7k)28^0Vve+J)5UR$)vDreqpr==8jew6nDZ*_JYr zWLF81cA42j1xX4b8tv2{^o4qY{yp4aZ#$|^gUf|`o#8z@@lGBxZ*66+npe(O{ic=kIsjYVV_Xo&=WNHt3iJpgU$ z(?(%A<+-r33|tlcOMnADGhBJ2xOM+n_YKavb&xC7m1{scjMG+=45~?o6#xsw6ZmJ0 zfIW8DXmY2n7Cl@47v6%S1F41^{AEV;dH-q40YAYR2Ce+eWJJ1>Z&gR(&xRF~5Dw zYRBIhp7Vs^uhlj{)8GqR`jCbG?yx(4n+Z0=SA=qLAv??}7aQlIItv5PvyluJI{-`9 zq4k4*TOFP+g%VyIucGSlOyfD~p0v^fbkiSKg^lB$-*%qiaJU1owA5Uv^{Cz$=ur%h zqf zyu?gR|6dLT0n8(@#{A;o=J*kd_}h3_rzTg)k?(K+=h=hLZ&y9gg@WfS4gya9iD4rR zfXgI4byID}WgGHh#CJ~AhwcPJNw@rhm(J(8UM&=MosDM`N2;3?qaEdY-D!Ba!IAF@ zyY3+Qn-)(@j?XP(*{^y)(zyrI-`88K7Fsj~3)pw;){h{G&(K)X=4&F{#@Bke6!kV+ zeA$ue%6HWVXi3gkxGMj(u=~uCJpUgC%_L$h2ysUa3EGg~H1b(-QVP!Y|St#LA$H!+x zPU!h)X5Kv(vwIz;^UZWvf=7Is)sg{(MPsa~V4UmRw-3ALDr%ogG{$_nxO*8H*O5XU zmP_Y%$!H5QVo0?@KU>A6TNgO8CstO)L;Ec7pXX85hcQXpoX za~Lv&oI_JS6S!9v_OF7(4!`A9zkZyTRH zm$z({yVP$+t}i~jpUiykRKY@)N6UOzN;2M$c*d72*c>I^BD71&%kIP`*%ucj=9&OJSuHzltFy{g2FmT=>R7L%4i> z#Ai>fGoZx#-cg=XBcn4v1qQA7r*T1LH<#yB#@n_UG->2vYfEN?UUl=7XVt64*`Gib zB#Fuo{IyCrQW}*pmdr3E_;Ov;K#x7NlfLleUM_d~#YUXAqK2e(uVlKro|8gai?VL` zMJoBdQ%?HLMEO^;RaKH(Csb)O0*mk8j8yik6@aX=B$DCa8b5AIxJq>{mCeloz`##{ zgktD*hXFCE^o~GWm6`@c)@eJgp3i~qew&z*?<0D5ClmGSK(7jnhF~}gTm>EKF_#{#e z3$D=zJ`j7pg$L}s+xy&nCI|rZ5bddYzeUy4IQzqiLdas6ZYJ(jf_GVB-YjK&DP%MY z=oDtUuwU_VZfRD(e)=J2zNTa>RS?ruJbI_E1<|}Pr-akQ|0@a_z2SAA(jeM98j zMbMpLvIseZLu}yC>wsVVQsActhV;b-&=)GED<5I`3w4&E_u0M-ABj}OcRmXJHC<{v zGVBCI9*${C_O8W2%;NXBq$ICH8Hg)Mo*O48t#X&FRR9!RPh6xCQwAgV4pck%N-0z`WEN8Wf$nMX5G+MWFP`T0leKYWaaNh&)Fs$QX6(9`Nd$B&i-1r zwHl}QETe^J-+sbj1{dZS;X6YuA?;ZybT!Q+N{<;!#?rq~ek}sl$;>NBkqZlEe)I{% z+qgKf2;tjk+kZ5O^>_HU|P1q&-#^;_8(WFhNr*x9+wP7FtZNDu}(-7rWWoy%JAA@Wj|N1&^z^gT}h7m$T!B&PggF=74b;*E@HSUDIMe9^lrHE6$Ih4U!~vy%9a9%hHV zS6|CJkz1PH&hoVfqaoy^oi`71Gy{3g&@P~`$6NQImyh-$aX}wbv?>ZUdi!V+2}@PB zczSrW%ZT^@c|kHo*6h2SL0W1q{GOmPU20f^X9FUE30y>pX1v<#S{k21(lJ))TlM62sX9Ith%=;|Ws zr!U4?3|4?CsrxHP`H@qo)>=W|QNVtE>IgN>Y1GRQlNDLhkhaTMd9~Q&P|!{6qaEAY zKn!ET8kdUP*`L-h9CKy#I{>*+e|@lFTKeX3bMdvgItk~NS41@8Vke=uXYGW3{=+xT zrBU(XK2@>ZHMyXa+WS5YkfFp!{ba@+`5>9v4{^A^R$;@U!Vl+*_jnmz!PfZO9rB2i zJN!1?0cxNwI5Rj;cJ-JxdTT?#pSy!x^r|c{F#G|RAkf!P$5|_(=Vy)Jx!N5d7rV_N zhr7NL$G&Fq^?+B5ZX{wu|4FEOV$?g#S|EgID@5{~^5wWIKZgY8I!Sg5PacD$gnFdyW^?^hPk06A>s4Fe0J$q^LW{kVt9Ir) zbJgtBI?0AL67>SPXCXy)j0xY~^AqZKNS=MKCka$C<)!keRYOgHruhl8X3#Xm$NgN4%ZK9tBx{1u81{ajz?~Nb;sp252_%!Cdn@IeMgL8~Y*UT$u zL5{B63BO3xW_rV)U!kmT{Ca`y2GVf z2XfG3J!<0C1FE=KoD-EoA@f~Lq|bA5p?CB!l&u|fb9r=X*@F~S-ZlK&WQKdCAX_EK zpxy@NSxNH~z}`&awNm^m_w56u#>|N<^Tc-Y`}-8@%Anp=P?9g1`)QmqcNsit=LUvL zQ#kG7&6Jpj4V2Z_ zOe9rvIojvIc);0}9CS}otbXkgh9I}}2o=1&E5}FVdmk`c{Sltn9RHi=5eOL1rnMu` z_7v;}RP06M4S(TS()r=3c|5Uw^Lt|8E)P)sVWlrS88ws!oo#5RCUnk`-;cq+&yb!w zn63=2`)Llmf8a@L-N(dd3-56r{9w zs0^9+@Bq^)w2g5jdO)OdR}Tb`?+7~2J(;CP{>-5~t$zxYECK`O^QFzgR?}8g-&6zU zGG(|A(-Rz#ipMVL`0aL_G6|b&rCK$03EQ3luGt`4W!!lIzjqKzT9VAd4h|^x@0~X& z{NTy^q7S91iHjtQ%Y-{yJef^Xee<(KYWp)_2YzPVEX9|YlN;K;+OluDo0jW?Sx++K z$0p<=bY@WA^vEx>qJ~1XY|>_O6MaUspBkNj!2JGJc5aX*W?Q*nk5#(C<L1Cgk6S=FCE{^JXLY?x@+LNFPzsQBt+hOQ@{P zT%|mojJmBCfhmneTfJMsrjX0;{yv^!is8=aInD?fhMnR`8MLY}TP2s4)!MbKhR*Y- zVnuGEuW;5LVfi3i3l(EyHDeNVdWM;(libd=&|e0EIF*SI#3mI^^kw*0pmYAkCax~z zmKuq%2Z=N~gA3h!4LmC`ckX0DFsw?~O|?V(FL6-**UCbH0^X4-_k_D2%%-?HW(xfb z;W5!h@mC#*`967rD}{U1ld)V)w3PZ_5q|XEGm@el8L8)Bjro@sGuk@g$@Wi!lGd74 zw7{Q&7ScE8$S*9nH?96BcejV1m0e~9^3rulrs#t(p>dLG@jz0Y-B^+~8vgaq#<66Y zw~IlQ`7v=H1bAczEpW~q88PDv#}Z4SyZcV|QM0*QEi<{(LwL!7{d%qqE7d`rB5%^! zW+&|N`6i1-;x^!q%z$tL_Qw;Dqh88ojYiZzmgvh~VW3BbhE+0(h*fU*zo9A1->^nK z84-ytZ)v|-D(s_u!3dLOd-GZiebmv z%GL4XO=q?C?=l{+`c_Bk_zNU?InYxw!;aB_-s$fC5}4cx5;@rmr?^nbknB=|r(l5Q zSAN{sG}zerog5m`ah3jakPY6D5LzA^3agj?D} z(CdQys~n?HGSH_Q0&UYi6=D&pujHHif;yC#lvd&JxlOO0lwU$bYyoj zg;A<*u^C9Csl{r~aW9#~{u76y9k#Ql*SMst+prY7RLjcVTXdt~H#|84k7BY~CnhX2X{iNMDIpVO*L09p? z1n#co(vys;MN@GRp98+I4BYHakXufTmZ!fd3~<;~$W^;`p;192^@!F(HSDdv+$M=< zH*?7bWyQ@Y+MDGs+gl3GitpNsFMU1n;GiJ?Qi6xTnm}u5Myl&b{fa=hrOryJ_=yLc z#`&+ha831c__T|(b}Upl9|c;Vtrnt5%BXOiLl?J6PcvP}#vbxOju!p)$vixY49l-Z z13+FP*Fcf^ZUJPR62>DJ@E{k?R&N3Sr$)fHP>J26dhVL*j0WX@5>zIZaDN=Df@Y7? zP&2QF%E~BDpFG0Vxh7i9a=O_u8n(+xAlYuh-z;njeAfWzUy-%}EKYK(|{ztojV zP1Kd#Ok#gTEe)f4S5prhIsvV;^|g+)SI)4D&3dM; zZhj_k7LoW5sf+FHg7+bE3L9xbE&In4mroM*%fxN}C>ZOHAn{Wxbcqx>$I`wa4%>xk6sRj~N2~wFV zOT52Ayj8uo!of86W!4&lYUfG_b$%7}&Q$4cC?+EjHlAO_04leg_`$~Gh8ea1E5%Xh zmqcO)Je1$Xv=iQKLHA2kuCD;hPWW}4oXY~)k?nHho28rpx-l`xF8;IPHE-kZy&T_T zI7g-HDx(APpqj9M0j9l~lH0@O;jU{Bs$%qzzNOry5xz>Qg=9OeW%s3v>uy$xGFE&& zeQyRD*QYl|y9*3dq?338dz}Uxm!upZhiwl1f`MQM9sQ{=F_}VM!QKB%q5q>$L630a z$`0H&Uo_bN@Qdj2mLvG4vVZVonfnUTmt-1`WhwccYU|0*|JbnhuX+5|Tlhy)APMEe z$k6TE!a5NtBuu2#X%r`1G>A)4Vt6e1ya3xgdDJO2NEo#lGqVzNl_ae?kw6!;M380Y zw?ujC9$bQx`23V`{(uZVlubb4b$eyg_6zgR+lA->Jp6-Fd64i~qbWQq7t8)c|70X7 z6o%qhgJs07^T#6Dif#j^3%2vUP_+w6J<@bTZ{?6Y+_)8zW=y?CeiOhGFFB|L#z)7r z3h9Q_Cz(OBh^w|hzygZ4?d%qXKVMb7x)Bs9b}^7QWNi+xR2XbP{S8D3bY_w>|J5H$ zo-BgvKKOnMlDAJFYkYI9q2vk^HE;Cp4_32Cn^sd*xGrxSc(N*!E}o>@3-0CGVZ?E! zW;vB5n)g)SHa0?r;1TS^!3!CxIBAGez=CJwi|%^4ydhTE0d9oKZXvaB$rof-Luo8k zc_JQY*5KRkqN4DjmLg%R!o%oMryx6x_y?|1%r2bR;*c107neiYgC2u5Z@)+(QRfHk z$~NO?G^MoT|HT+j$0$%w2vf%iWR~?mkiJ<|THGIa$6LLKvf6qO5e^^+zkcKnJH@m9 z{dl!NqM|r9Y9OxgFvXK#)0m}Nwd%5GsAc^P5qUy1<&7BWIV$cWRXq`JCRXGs-zou% z7M0#gRINk0<76#Ts>F=Ixt$^GGDFV_XvuJr?-zJqNc|BUh$Ccg$j8JlEvR*bO*8UQ z=T~D~w&~opggve++n=WF-=+rN{U9t>B2SlR$whn2G9JkTSa#k8$J)r~g1wX!(8me< z%wkv{$FcEON9C_(i1OQ?agKr97fHZ0v{y7>mf4Ta7(rs>ojA@os zPI9kpyMBYR{?oClA{y^P>}hCqQ-LY;3iIoX2hGUg9Bv{HweBGLyyN%Tp8zl<2_sm! z$bg^W@>*dmL9oLrh`8z!|7D_|?;OwduyCulSn8x96$d5lPvgwKg$i~WL@^=5ffrVo z$*L5RCt`gs#pV?{aYF8sv*$PY$mh^upC8BEqM09100eUN5p)xMrw^kA@=;%UL9&t0 zd(!sV-UdNwuRQdm5{xMKG2?_F0kdm$Qyr;&JO9R?WQt&skhbr);KHLmOjS)YO7c+$ zx3-d&VmI2F5Kx?M{ZIJ63(9WaReJlF@+M)U94t!?q3Qn^;lxqMkf)plfI!xOQ?pYo zOt7L|Fpu!7iIqmmSG`~j(X{f~P`)p&6Vw;Z3#4;xjMYbp0R|fJ-{>i4J*@^B9+L!} zZ#V%KC%V!Fhg`&Sd<^mQx{RZ$rEqZc3FzA6>~fsVseC@=Wk&>Kbq#G%w31|K=U0A! z8*~-jM2`DlPZLB^FMg*n@wJsqDv9R31Ap!_lDOzK;wVtpNnWP_m(M`4{oL%%#V(YK z)@efjE7WTATVjf3HMh7g80Z^pW}=~&?P7LHej@WR;w>;w<3Em-@uJ;@SNA~XUgrkA zqODDoJ%#^=sdJBOx&QzFw)c*;S|_#Xuw|W4TCI~#TL%@9!bMJ5=`gM^NkZ(smJW2p zr9xVS&~;s-5TbXAqSFwPuoObH3XN*--^=y6{cgAKKmAp=TesT#{d&G0&&T8bSj3Zm zKz=bSpECAD5SiH^M9BvEI4{)y+9>0zxIMzH)z{?haHr@ z43h^uZQMIc@RVGe@{8thHeyj?xb!Qt@>}NLU8GCWlpuZ$UPdgzh>QcnXF88*AI|~d zh}Y}l03q2l03%7M#_3LDr|Ce!(j>}9MlmxNw7lhjzr@>-F+`y<<&Io|k`z*CTWe?xtfYUCG0 z3<^)s?w*a!H{;?G7SVfl)Ag=bHrY!v2gH#c&NATmoHRo%(>ZQ+*h~Pbpl!03n%-h9 z;LNXlw?VbqplY>j^ABFF>jTVpR&uTJSBxlcviwT@z3c^EQS9H+!#DVdc6|U|XKs+y zaf;^v_MkYGgf|M7>CQ1tMoIENY(co}Z49 z2FdsK80Q}woc=j>61?Re7z?HI4NYktQ4C4h%oKoinNizJ8@m2xL)7n0wtv2k9dUk= zNlzSxc|*G+ELWjRs(M3+<#PSh;{9ncYTe;9m1x|VOg@P5GU4x2EdQ5ZLtUKKKu2_3 zw<;IgO>8471x})C15BV)%&|o#N~MnpqT-)NTFU7bJWV?b=k1!$O#R4G%PBr`VTXP}*=zisD^2yC+7tTmAq>2Go)?2dzd0 z;<@~5=|GmIM1_Cw=jR7L98!71N4GNkUTE+3C9c9{zG#C_Grre42rvxfo*anBc4Ewd1rtMl_sZB8)#ovqN;7z0Jgo9@t#R zSTQzdWym_@*EKq1Ew92Ir>wb8Sk%Pw>hUu-k=EMS1$4058tL2voYM=DUbf;O?GNMbYGnC;eoc5Qpj()l7xNd@{K|7u5D~fru*qUa$iceFh&nxaQHgA&ljIVAXpMU|Jr!`}dL3bx&6dHslAi2GDM3SIuhb!WbI|-7 z6i@MYW)S3a5P0BWmmVe8mP)1nO(2$-l6Nj5yDXw#4kUW?6D*4b-Z<3Y69y+f&*13XHDd?_jmpdz;6m$_3U zhe^nl?6q%I@1Mn!Wh$H zUddL|QkQm?7c522`GbFhGVhtJl;)i-p7vWO(&P9~JP%0lfIp7CEPl=(7jwwjt%{$7JNu4MueUg4_^)No%F{`0Cg;JKP2*C!U1`(-c14~L~nsYAAEJO;SXcQ9Ti zGWHy8a^J({aFAb(Xnz^tdAEA`vr;TQF@Q_NejIUw6D;e9M~+yY`j%ahS%lrmBd@@^ zv1Jk@`)m_zNz}%Op82^`bCxPPditbo;-2;c?|i(Y@4`#-C9z2MIjEULFs}yY^=^<` za&)+r@_;1O3hf?R7S)U!Bfc!7}{DZN{|$XD@ki|kC=3s@KP z0(*QK)3pN{_zh+O;elgu0BOFA*15y(p81*=6XoEEQvLTl#Y^-}aV_v(ADgbV9isQW z`vhkWmiD`An>H;KoPWzKjK_Cr!HYHtr8>zmGg8^}29x|$y{X|+qmS>mshsu0qLk#z z&h`2L7H1i)R-f!gTR>qP^ArHah5fBsI>L?I`7p`+qBe&yQqf;EJlZtx!lsxZ=hRKZRK z-9P3?rk(1sp`eXoN4#mTUz>u7)ri9(x)761bmgDzrqivTBIcwZh2rJE8xUQJXrwJv zuaC}86sYZfvgOz5e!Yu8Hd;|^gUl~dQS0@8su8T2rIY9GIxx;sbm!sV1?^(%Amf>)hOvk|mN4rKK+ zxv|FS*tBMDYjm#ij`PEV+@bfQ=o>h=H|lqR!26ldA(!6}rA9neX8Ma?_61c~2gtyh zRUA&NT}}p6nfeD)jw)x@cwKC3Q3Ep7cz5mtklH4{myW%jShWfiV*leL$I`rhM}ho3 zXXh%6O-s@{Q#f4qE0i(RMS&`-O=xx3{eT9`-~c1yu;oD`4-;qqgG+lBlqx$uT!)eG7Q)+}Mvx;J^SS`*tyzvqMG>*!64B+NZtwNc{`qn) z4q$Oo8&&Aj^+XYrZ`e~ZkdYFitg%uYw(vgMjK6TwFJYgv{|nYQe;8FfW*Qw)t{DJ1 zbKI7n@XS|>#jFztB{g1DZ^XwqP1fGxKlx)tdD%yt1X^PBRaj`uub@(ZRNag0$x2fmsa zT3i3}JH!^AG3u*^Y9yQCTTI4Z@QE)ow!kU;;C2c78M*;8VvBu?A{~!VN8CtwKQ7;6 z9C9C&&N~eVn*3s$y9w?xP^B({9491s^(f)Q1I!kC=}3*bAo<(eY@Pez)2%hus#}Yc ze=n{KGTL*7JQyv^(~XPiNJB5@+`WI561OT+8)Kwae1pQS%i~fMam!T<(CkyJ<9rG0 z`HD(Gq0Y3aeM#w(2OdWxkIo(IXV&d#r*6lvyP@2wuRja=I2ux!6K>AWT{{U)4KLTy zBjfBDC4@gfZ_pGh#GeQDvz|&)+9jR3B9v*bG4&W9B>|nmR%O6bZTXO^oVFN7N@?*j zdaG;$)51ATy#0WLP&Zuk7cJG_%%gwW5phShm(TiG zs(-8w`HB6iEA#AGW}n(%=wy_2#i5{|*Cb>f0H;q1Kj;aPttB~QyOg{5b-is}`b@8~~5be``p?|;! zK8uB)aedwx2MGJ``K%5DjHy8Br2}pj0(Z|EOpTd|yvFjvL(D%zetE0IrKnGG=HLub z2I@7GF<1Et;)A^Du@#aZ0doFQ({$>9GaP&c5cid5T~JGN7*)7V%a(jtEHB#6(%Gs) ziCtqH2bsgYc|CvmV&U z2&V3n{5dl`!0<5=hks*>`4&!g;XL&9%*lCx3O+qj<;z+C%l197;a;msRv znt1nten-{How|ba_%s*C*3Y?ehF+m_5Ha#GaaRbkn&2`%cBp@{8NiK|qYnw`Be_!YVom<6GPMQy~dc?1m(Zu(c#A3^XDBBua zxSQ;|IrcPZb_UAS?-SiZHwI#_L>)c5PFQAI=UEjH{$nRUfu#wU&fJqcuiMbhf?tU( zv8Zd(G#UACO8r2X{R*k1E4pK>9IQh77bj;4cF-@!P(3iZT?WUaqd^fNM1b{|II^MB zv!U%C3m*+vR#Qj+CQx? zC$phXf(PQ0E2k5HtnmM>=gk@XuI~BNBfldSL25B@wwZ_;T9|IStv1V)#1~@4EnST? zD_KxX@t--Uyu3(|fU94H>4}KO%v<}D2HyFAK_@FUhz}uQ*Tpvd(1I`Gm0R9Y6Nj@6 zW~RNR0Wbb(i8-V=AeEg-MolHb5uh@qZ~|Fvz|x>!8Bo)iOUS@ZNnW7B!azYdfErH*T!l2K(RGkV6y`N! z7(Q_VxZt!iYgL6TVy9{rj@X)HA&3N%CCl9znYR?bnZTS&-RuoHN}&UvQ8eT!r&&mx ztz3Mh(gI?O9_R`8`9h-mAJOGXi^ITq-u4Do+o+6pDdTo2JIPf6xO(?={p4NvRv!>{ z8+x`&RrGIlp)R>(TDwt0o3DGUpSQ;r@8&hee7rC{#UI%U?+T&ofdmDlhw@So%He8hXin)+B=*6n}*Oa z2Besl!SuDSsUf2|=X=xzWU0777jUL|CA-fuO040s1&S-o@N#+|X`1#0j63|{wx6`u z<(R{2lK1Y<6D`3?{zhD9czS_0@g@YGrX?YaUJ6PZPFokR9{ptlWyzg^cWo>*2+$UW zV}pb((8)Z8{^UE!pMUTdYCeB{PJQ$GHXekwEdIJg9{wgx*iG+`ZYR^Wwq|?B)Hu`v zmwXVKkV-v1ovP093tLp=2QMuI4o)C=zgXL-V$NUB;Xfv?aQw6wYo%fI`{ji&<)yU2 zPQuEatr5^j8mm<5GL^g8cu%kNoL^9o`u@(4jK;?{20CBsWH}o{?~0s~ZwnV{+vjb? z7%-d6h?UJ0TI33+qgLAE00tXlXK_MWz{$;jh4Rpp&-&ZUp8l6^@h$ zlCw6>uSkzcpMud$;@s4Q?3=6kB=|6fea&toUw324ee^%8z;A7Clb|OHd1QE=@$3EH zu^xo7?`kMf1R4UdCcB}8`5M?gMM%e)A`(+uy;W}`tMFCR%YZZ(YPbBgDNWZm&8S&>yIgG(dkkOeLhjbzO#&kDM3FZ!C1gLg7&ya=4`C95>5}uq9GIhyodtS$x=c+(s|A6W&5mi_AfHKiD(%RlMq>PSHqXR!PXv9cpb!>S zP<=Sg907H~6#iIfAwv&L4F>i>#K#zWp`Q!L$C#9yE1_F*S<1gIhuoK3w*uc=N2$Q? zT@wS&AJ6|}ZH6E+i$8Jn1=a8vRQwk1>?Z-28MH1y*Yk^3Qu92~A9LBswBLYYDfZa| zc&jH$dzq?xt`ylDgzV+>^JWOak)DI8(*I@BD9oWO=M=bNfL@Q&_pLEZBG-TGOJrX^=y77%6Hd%Fu~6|0<^bg%TM$Ler@smd%D7S zs5ViH?pBQ~uPc%0@D*oM7OyLDon5s?`gDWcRlCh+iN|pIt(%Kpdgq4oC>XCGe!J`o z?F$PB%}CwWELK?1_3jY*NRU}X0^VV?AK*LjUuO#jNvCcSyq7Wh0Q})Fn5D+}qxn}Z zn|DV}lQs=i7JamYfNCi6#PHq zhfdKd!$?B60?CZjV3H_)Xyo>7DthNOxD&;4IS)%_y-n|V)+QFhXle_%5p(Kqwj#dbf%TCRq;8}t->pI_b#Dq)lP`-A%Jp(8?%`?b zu)kvUqM)FoH|7^^uL5(-p%*NjJ>jcp8RBVmlZxE)Q$6;RCr!^KL$)Ma$gVAY3i_B0 z-<*j?>vv)fZ4LQ09sJ3Nbr|!o9q+M;^#zNaJ6Uk{@0r+c3)y%?r!!g5~&s zj1Ik^HXn3SVtT%D6*^NbF^25!53(m0`G7TQo1q=KYZN|aba#xy9u3PVC4EYR<5#fK zSd=dkqz_))v<#|aqs)j5zGt7_U#(U^K)C!r`CzKa8J~aB|>k?U(-QA`b-297OxgX(1n=Zb7iIv|Ekp<)!p^yHr(X zx?bal?&qf!?o%)flm~FgHG7rYcT|yS0xdg_@1e&EAcuhTP78p(=a(Jk$=05haIN{r z^;2&!AoARg{ud*{2flVJ2mQHO8T~roe#8*%aBEFW&BA+}xeE;+MW%B5oUqU5$S-DM zduL1XE1Q;)>K?}gZr;Yf#3=Eo1x<`u9f$cr6{whCaaH@1{d!WAZ|Y{IXoF^#a?{#_ zNE=0xw|2EMGij4@^j6~A@K>xRfzGVjUV}LYjz?B(mKpUd$UdeIZ4MykXmhm}2e?9x zTVnC+`-F!vtBIsfizS}yrGBB;J3-g!77@vUpzWzCE}{)ZDV-4I8QAZ9u;0=y|MopB z*K?P5dLTMiQ1S=K(i!WlmRcneDA7I-(QZ zS{H6Yk~I?+oc(aI+G&0_mveEFy+$`T&`4hQxshVAX%Xp&LXSwSrL{InP|u(+Uu3ym}j>UcG_4~>-lLf zr0Mw{?h_MU6G*=^1Xlxa+H1e%Q|)i{zZP}EkNO1} zZd3X$E(aqHGuY4~O&rpgrG2K2%?LU?lQ@Mba7}eRe2MnL)Gp3+^N*>5bGqO|NI97V zm8IV!_z(_BVlk&~kv`)(V19o8fp72ngBl;ZK~NLVYP|6t_CNJ(>VF@9%cgmF&-3ju zLu>E+CBb?>Y`Y5T4E3@9$wIIt2FRX*+1B%j4C;U=xaKVjy5C}+nLWv|GEqt6BHGmpw){b zJZ+Wh5~>@s9$89$Ve5TK)e`KZ^aIrq{vUfdtgBn2O(#+IZnI%p&ten1c@=H{3eHDK zJQpu6oMz_Afz-Y5?HjL`49)8o_mJOY9fr*;obl|=N4>nNF9F01Q_cGI9jcMX%aoZr zAY%L`rGr`-m)M{z)6En&L`-zEzMTI#`9-Ixd}0FGt6Sm+-iQ|1nL3|R<&;bCfQeBC zFKS(^dUOh>rbj8_lOqq~ZN0(+1CBVcYq{x*QLtqBV%CH0Kk^}3FZ>)@+R2Jq^X^8upFKwSH=w+$qcDA4GFB~i_0K1umX3rS>s^CS><*UghKBDo8_eCt-N57b}?`(ew z^6Ssus0*xbH9CW9#hnMFAfgLgnTIt+W-kq4L;JI2JqvPIQ;jfh+kbbRsnNSnLy z9(tjF7bfueiGt`HZ*5$ele)kkl2Y%mjn)j#a_?-68xaS#Kr7j>|IF8GFnF17tUogGwyVNg7Vb5^Z-p0R;g-wQin7EAGH8la-N{K(UuNC0yVgabIJR<^}5XRVf z<}sYi+|&Efhbq)nbPNxkiO1;3hFHvcj}}C`el7M|K}J?N_ANb#sHXfOKThtI2V8h4 zFxQp8;m^>CeJ{nn)|a?tHgXs8d6?-YJ94s#aKMKXg-raNHccJQu8$Mc;rfD;5{Y2> zy5(!`W27_NMHDb!m>H3b>oc3b4J64{f1Q5BnSbdsxbS>y_8^*;YROznXHXV#(g~cYx8y>GCu# z#kW~vVw~jf>6)vUqzW!DT_B9}IM3T1&Vw9Z0e|NF?PR-&q1VBgCSAXOW;U0MNiapH z312AEWIh72a>0YV>ZO#I-?0=v&Yju9n? z&?!06L!S2USsgN@4|-6h$MZkff~w38)xRMmg_EF}$gxp+|NaBXk%el(tBk>-YIyDf zcWHD8CX$K>BU`{ivqqBJ!2eg`sb$Z1n_D<-NJo`EEM&=0b?bLSiZ8%LP2tzz)_^l4 zmwX>cJf{!ouS=HP4^x^~pxH4x)|;Yh#(CiM=>PBR=)g72!h4=;j~!aCyY3ck%^iH7 zp2h?$&|O6moehdF_msN$WF9GY$Nbm_GoUdG+F%$lH6H)6ngrD@7-K#P-pP|F zDpQut+{jbEmfLBMYF9KUJVSraElOo@U zd)?Qml~{e+{RbG|f`lF%X9OU5-s=Iyx0-m9_?}tK2B6XHrpb#rbm&z(p}xMzG}SSu zRu`%Qs*88hJDyNFX{t1zy-VU=2RVvjmH3pyz&*_nB;dQjqmx3=GOS!Q%(E|{{E;YE z1+OG&g9Rr@D?=7xDpJzFl%@?zA6s-k|Id|zU`%r9EOp3Qvl_a<4I4_01;8S9z~cHfb;TbEkY=KH0)I%VFuS ztUj#dN`H*ZhE&Y}n7*Y)3ys|&^$MpRYbgl+3T`IEr|15HK8hVXou_VJ*RC6U2r&Px zz{k)rg)G7cb)W7B)PrXWt>klLgQzy1iXV`aNRtEOSY25EvXzu53f z&c!A#Yf-Vk7fV*#N=|XKTA!Z4httA847_}4LN$nwp-#=2Dy+Xt_WYppIGgktc=gL$ z@p(V|Z$3pTe==|WHOLE@=4A%&dIN6l6WG+jf9#|sGP+7oixxIL;x9Byxk?2|WG!YP zNk(@{P&0jN;48JCct1*Mb;nVqKa4$V*Z8OuUJL&nfOC7#o291fB@*pFHEzPp(OpQ( z4kNZ&gjh@!#A-Er7kZ&*N=Vzm-{dZ{Jzzs#(Ai68(0X}!aT7=^Fe`5~vU+VCg8 z=MOopSi6rLf8D}KXmOLwdRaRk4fu@>Q|m6?E6XPj;lWjYohHa6>%O^5t9Uv(M*XWe zr+>1%fy(1Apcz^*kwxa=M^EqaRV>MPk(b7S{+rGO|5-ebG#1jZ4c)lRf$Nf%ojCU- zX}pKV*>CII1l%i=#_VS~@Tr)4u$Ar`2zRRqx^u(qVlicpvyok;pebLrY{t4PI7(2Z zmci>$_>V!9Az)vw`WwI%s6%B&Jya-Z3pVQ7cax-DxSp_})@p)wvXyDv3*)Lx%+p1H z$G#Y>xTR2pPm`w=5=Mc@P_fkMAXp6=LV2@J1e~1s38|6-e>X=u{S<4bW;hI4%~rfS z0Kx;IIM03B?|tMa`auav1}FX4MUMa4B+$?(Tw&2YL_A<3?4drRU`PG5Kl#M~`*WF| zpX@}2L}#afl&f!~+3*@v-Run9`Xx@_ah2Y>zY`O6jd0w$=rJ&EHO#GqOOK%gp|2HZ zlKm{dO&UUj_fr(hES1qXc_y{`5q@($001_~-h|@T8xvl)VUDjZbO($?C|?uc1+vpy z6EIO8KT#i*o64O=r*9p>G9_(f&#M%d8t9y)_``J8)istb?3p0U)^FZ@m#`(B6chdn zepQ1@VOrgGa%LTvZQiyJPMbEfw0I@CWh{R(+G@?BC^U`A9uYF4_0`4?fp z3-IG=6olZ>u1=2J&cm5AilbRz0AQl){xqPV{feI=y=uaHR%81fWoGge#dhygz={qC*gOrhc!n6H02r||Ne17!WI;Fc;K!O(%Du;u(IMa>j9cOOZSdPK;YM1j*bP zhPfG@I_M*N>8=%ex~y2D)l?DJaE|Hgp9%c3@0c5-ym^xh+1GT_102KVm{|T!_a8wn z5Ds?Oi0CNCGK|u5k#od3nX|w^7jCP+Vo{HMcpbv2?+1|H3>dr+4mU_XS|pQN-35r$ zt$fEu`-Z#Jg^^AZq@I5DcvewAO(E~%mD2BEJ0@}MUL$w7PE(i}p~#Z~4Q^Zu^rXrw zV=?cAM3uuwYIZ5D@D~4lV_(i(e~`#A`yIBSN*?L93a>D)Sc(R_8JWmwqO!_iXqKxn zF}zW2($h+K^|gas)+pe6-qspPB-IDp&Al?){7R9@O3764`U33MG&Gp1Ej9I1EHYYo zUn-2<9$BGn81ONJBhz(~c3{AU;S)X4xNw@*!`BeDSV@~pR_jfM2)NeDnHbNPm_JLd z_#aD|CcV0;J7yqUG1Dyo_o)z`d8i(F*|Qs1KaH*;`O$cXGHmBzeH-_YdH$q0=iRZRPy7I{x4F6G+Do*{y{ZxDgwWMAOtH@jYiL!zikGdJ21r zDa~pO$=enF7o`?85xt>^KAKiGVQ&_lpcpr*bd4U%U6xhKT=~c-lsqXeTx#i(*sbQN zT^waM+L39;DP+>I5}r7N@v@cQ`GmUfqJyMf70PR9R-u-nkIx1+Qh_3#n?45kvVMXD z(7}_9O>?KXSfG|qWcR(2uTj%Ny@c_^$Jn@x^QPh6@Lf~4m!!qEGKQ<5Y4S~RuoB=k zboYmR2zaO$JRj^hBJQ4`a{dKd834pw3aW#BHS1U3M`n$HzH8n3`OKq18eG$lE+E%1 zYPkB>}sFj4({Vfy3TLdHXjbwf@bX77*jVs=+h)^VKTtqN13h4ahk&F@=)xT??7xq_WH66 z_sOeCv~{BGqs-&=8m=sNJv?JN%dsUUBg;GH4KXR05O+VLpZ7Qrv+T)N-q=_*n(d(} zXukj0G8%bu;b+0%yhL*vk=X2PuS%Lr%-)pST4v}n86AJOn~Qv7c`*_luiT1u((Nb~ zz0rTm6<~$CPe_)*Ux~zv5|kQ}w~zIK$&Rg2GlliAXBQ9Nr1IkA&W9-{On>6-hZehe z)JwvqCQW-@U{XB!AB0nyd(D_neCdzFu-9X1;f$lQP5$9o1Eg@nYz@T)T5&j?)7r_S z%6;sp^z~Xn>MGMc^}lzfXa1)=KmpG9sb%Bz{5RPA$B^kc84ifakmrL;N}nJOw`&$+ zqI(w%?~lTT3HYB?>qY1BAIVWfqn)z*)Y{E~+h)USNu2q_kVgfPNAui1k23#0f?I|% zd&B*YNsp6U^Cj3<8Z<;FwuHf-nOZr!X`8@RHR;uCQCPmZ@JA8sX4;e)gIexf6bb7r zHx3h=VBBmpuOxO`O7hl$*qY31ex(te4$^ElvBmvOf3`*$D6agy3MoDRXNzUqH9_dL z-<2i{a`+Q~sr;fk}O6p#@@$#vUA3b>;$O%MPDsq1Rl3?`06WhM*EJ^h zGh#olkq;3ToUv26?bcVG+g!%VUV`MmM$aQlN|EK)H9kHaDs&h}%mgg<7|D5GCmf(~ z;12Q|=3qzWVpD|t9P?{MrbZJg-Bu=1eJ_iO7CmO!@sJp#T1>EG-bf;c+E|A$FZJ+`>8Ufie}Xxc$^3f)fhRovg98YbHjIJ0ifMv69M zZnLKGW*!DE`UtA34%?66r z%pt2mUQeu5I5ow{v3_Uf(RY@s&7UoVlS5dKU~nyc1}$7nA5*yyqo+vcdw{5EAl%dl zZ&^LV;3|G1i(osB zs?<$}4?*VX|L{;N{V^_|^~0&&Hb!+Tr+K0#x1r?H2HDjygh(+ILhcWJb%nwBO$%p0 zTBeb8r;``Sf{W8TFa)rzVCyd8A~Qi}`IcoRed0hVZDm7aFJI(a`(tBWQ`WpkFpi(k-uTnRRnVaXoy_x}TGC4XJ2j89aYOX+$4!}dBWJ|JPC zMjG}Kce!!WhQD8suGX%9gPZwYw&a1VXd9?s9y+&Glm*qEC-j0`IG-Nf(l)DQMr(h2 z;fdP|{(!I7KnaWZ+-O;lrzmp}B`NRkRV*1|O3p`Oo2D8(RK{Ca@%TqE{O>2z3K8g) z6)hj?De-9kdvP`rN3_VJ`YzO}tIa!5{u^wbwt5y6Q(XY`${+Wh8Kk?W^VuqS75?k` zn4{NY1(+DQaiO|+6}1qYw{!uBd6`Hi=fWugo*)xzpnPqhG-5$jPpN$|9hj(d1TS;# zXPbx}{+NXOAeGmpLO-u%?T6Ow|Bgw0J3~HRVbTbjPk~i#)XE2GlXPTV0|xDzfgM!zpAp>FSN-5!jQ z^;+SFv;i7wAXEfaB47%IqC}KA5P$WDxggj&Jg7n2AF3u?Np<7*$$-f+hOe_GZpw4Q z?KPB#8lpI_gI!>DrXi4dto4fDQ0z(`qTA0?0~ky=z&JMbMf>44{< zM$WM50>J*Mh=+&7A&HT4zrl!s_rIl+kN=ZSpa2_u8t@9)e#IvEc+??FsvD5#V9`7B z=6cEV>j1_Y8`KW7@=k)D5hq!9DB93iWL|jGM@l8xrYYgQ6EM{8bh-2Wr z^gc^$o{jXFjg`1SO)sj}6200)*Fdi@cT zQ<1~vdoh)wX#d(Zf`0tX*{4o0>yx$nUZX^_GV~j+M^4&m(4br2I6eL+G00NkOOI0? z$U)xu%JOsUgpL$p3OU)-OSDo-@d$06L!~4s;&O7c&$WvySL(#9pwILQd4x5Nj%8sK zE-G9?D%eWUyKgJ=oQO(7j_l=26asGtA8$m3_ArQw139v3Q&zhbxo=q4>@Rd;N~;Mi zD3I75+$X?Xwww%xxmIJ(_}|BG*oAD^HUSq9wXr==BM@=5-IDFBZ*_P*OKp8hVLrzI3f3w#M%WJP)PKWva)5Le3BH zIP0BoBppp!8vf*TD(3cx)a0wNarlP-a%v*Cc67=R^p}upuuJcPL`gV^2hHX=&B}5D zjiS%pGs~G#98S&DM&*p+Siy8#i)wO?%D%k3ow~R|vVBTgioN1z{wyO#059p5%p4O? zlik=xQcvbmUVV%nKJzk*vmXgvDFfG1Q~tP5Kkl*bU6YE>h|< zLY2i$wV~5E*45D3s_3ybwCrHq|1m=-vH(V~si87oqTh{RJN95qxnjwr3BQd4$gkO4 zzptMKg1}FytSxQ{a2wb1%Lwcek2Zbh%-T6mcGkN7a#wb@`~cAeI0T1NBGM-3q-UNQm!q9zbFy~JoowoailQg0f|1ICzj ziQ38Tlp(SbSBqM7o zS{=gc$}P7;x(h2GqHkmMUuabnEA3{0vXuW;2-a)0kyMP zeP?1|HwT%mbQ-07%7ESOK<x(Vkn# z@eg!ObTKKqA((hHh40)mjZg77(EJD8!`IGv>P{>9^)td9!)*PVVRBlG zp~p0rA0Gsl9dhK|ajyS0CXo->c7lvK8C3zv^YnhPW&%HrY)>>`l+k5nI#mq$3TQ&RU6(in%3F_MxrUo@-Zw1I zde!Pu{3-s9i7N(p-4?169Mt{?HC)AvpAXG}k7p023=ZFIg>}FgMJs$oi=eb^rE=Vz z^^9A#k}fW!LR#Sgr2apHGB+SoM9<7(!;*z{j8eVv8rFhbasyYO{SYPd(jyV*isYqC z)8s{0M3Zx^SG5LNu9h13b6g9DG+i96pp^e4#vO=ESJGKJPQUp)F&h-y_856VMYEqQ$DY=P9Ff`NnZn=Ub(N^2-;L8HLGzCe6nXQvy?ETr1Xb9gmBhj^NU#`|-#zv*<-?u^bf-loW>vQ0$iAQv|KEal_3DK+s1YxEIl;av5c%Sj_{ zT|?TURh({yZMUP;p9x&XJbX^xT`}a>1s|;qcQ##Su#ulXf(~sBzJh-x5@el>(Cvvh ze3ehvsc)ybOg54eYq_dySZCuDX;?S~m3HfM!`1ZxNrK(XRFzo(4h$&l7AwqOuxel( z26!aIV0=P_lORvNK?1C)^9han0W0p^l&$cLuY7sa!}DN)c01Q_c2NJm6~rL{wj?Ga zaNyrAN5=krX^jXpY_C!sV*je4X20Uet2;1%MoGqXE05sUh7vyI!pHX9nfi9T|6TEQ zP-6Gwg&OYHIyDNaI$G&wU1iMtX7{lA!xi=z1|%h^A9E}!&WnwJ2vd9`WZM@-AC z#Z)ICdQCNfEc^xNap4^((4jfGug0RVGW#S^!)47pUw2R6nLKei#V0($UoXe#qTyo8 zj%OTx^@9A3MEk!7!5Q_|@ZRFs^Uf@2F(vRvKpi38F%bU;?cxbKk;pg4hF))VvGb8U zF$&jnnZ({9n3q_3Fc$8b-~NF$v;H~MirLl6D7V3$@2mL8{3H~DF2&g@~2^1uOZt+9-+0)?HhCB^7&N*jjeF6esny4 zq=XgkBglJ-DtdE^uy^KR>vf@(z0!k=f+}^P+=6p7w~gshU+=d2|Kq@~$oHO$%?Gl( zJkuz3fn%i43d5eoRr>^PSJoEUPoc~U@tHRbD=#%O zgYXzkV16}ARYyuS?EGo0!%LjQ=U=oJeY#Oe=I(OElU^#Y&kl=48PScaGeu^JBnSHi@hs3puZ>qnw2$_0pHbo zR)IZBup@5Gh&RGnu(cC~>&=xPCzV&u>MwL9;dUR5OC{e7Eo+Che0kyNWxDQtwi< zfq1Go{4{ohE_B22woQt`CV@g2crk+e7caL7tq|?2}f`%@mV6 z1UoeRpJ>Gv@1fILg#(TBYkgXQ0bzZ(j`XQpDVUz0J(RDh9PC6N2VQy|w=)d>#BZDB zR}(LbTdCZbjXKMss>mm&)tGgA2+cBZ9wf@+QWsMJ(SItbuv@F12O_-caSawS71@xI z8J3XCW=7$_QLMrDR&$!(q@!pDV2*>*)5`<;dI;4C``ff_kN(BA69f7=Hs*;_hnf#nyX1`TA)FD`Dkm-7v7IB zVlP;RQUx=DU7_e-AU3bH@1sdl%X=P_6pVYZg~}O;o_keK(gst)YiLOxtZx7AsjRiQ zLqL|{%2JXvP3yifpPCUbLfM}cHJ+Qlr}qGvHrC`g2rX0m#_x*Z?cc!!0IZ`SlRoqu zk^LeJZs`MDP|r4EWN%=z_RWP=bLip8@L)J-efV$fl=)j6pG8l$R5>s*5sCEr$rGPu@lJugat??VO=#P@sxmB)o?@BblGy|;&xEwAme1%!wMs~Q#C~C*m zM1RG;(1|^h7zAHju$2RsePdpz&@i8#;n+3~FZhRrMBgPdTuGgYoT>DA&#_zH6UyX- z&n5E9{_TO~*4Ud9JiFIDthQ^2?Z(D=!5JXYR%4R}%JTHMK+yV*`AJGmo&%P^!c2U| z$SM4(zHdjcX$xlwQ}8_uY|ZMPh-sW|SZ-@HpDGDBk&>HRXj;RNnMVW-(ZQZj5blWu zt01?BekNB=b8*J17??~U9VwQybHZBe!kuULZx(!*?T+lIJWa9*-5lWT~IO~9yxwYtE?zCGhtIbc`E_y&Fur*m!5qPv8JP9AZmmo{52n-UY>V)tLo=15Aik3SV` z$4}@$TAz7)&f}lV3o^=zX=`ns#K#!3(_UlO8F5)$KI1&|w{P z*0xGTB*#01EvX?Hk`TMLjuahGImB|hyB$Z}=v6`FOa?Hq&ExJyv0q7~L%)viElO5nz|Z`M*Y|g|U*4@; zm>eg#1GlS3wR+8QIHxYGl~WVxn}}0aw5Z0{TK86zh zJaWEDUTRS% zrFZXQjcV;7(U+H!KjG9Ddom?n2SNGX*Oqn6qUyx{LwVS)$nxqMVO}e|J*=$MAnKxV zC6UmU4R1~*F1w@oAmt}S#^a~M{plQ>SGaq2xf}2M3GDewe4SPa%zFO+^jK zhkgkAatnxDMn3y3X?ywsRYVZOEZY_$Le*-jLFV>AWUc%vlQVJedT|#}%BG)i9chmq zfwzHJ%%-ail973hl1yOS#`!UoYmf6?TlG%>u;CJ}qrZ}&qdvb)MZ3dlnB2LexXTP! z?s}Ivng&d3d~XsGd@M86-oaPeovXg2_vY;&^X@Z4FeTHi8c16ic|y7}ELV!Xy_D2Z zj{7I0buVWtM4Z{Kb>nQC5DI9ctxDXy;bvZduK0HvQnn-)->j1;L6IHS>Nja=j z4$#>qY=0_ZF${hNUjArSx|2UJ=GI#|j?@Goo}XwH7UfoY?XHgnSNRrwz3xo)63T!v zYdhF>w(jEa4(V6b9EkQ*T9Rfgrfb)J^O$I`Ie522_ZB4Gd)oBwlG-;?tS@Q0{(*FvnJ?K^v%=L$IglE% z#Eg5DE~Nroe?+ zZK9F92Z>Q1u*=@EF7lJPmvppw->FgSq-W{3*5bZ8N`InRKJ-S6bAxaq2c&Y0bWbnF z0W`=70@iC0%J#eojjFw&#Dw_R&xpqzo*synrqhz=*p?r%#oQi;vX2~K>1(wa*bb1V z#Lq)_CqoI&AQ)A3GmTamt6SLsB`>tSv^=eCKj9p+@%*~TF|4POA@mT98UU*E_rn;A z9yuIfq&QQL|H)?e$8Cum!<~0h?OQ27%3$mf9LA0+jY{YNIG6C$9eG_SBnTx}*^F_C ziZr{jI?PFX>~^3lfIU~Y`IGeGA9C~Wc=C2T(^1TD15yjhFGJ-0W}t&5D)72dw?5V#m0I*tlzM#1BF>#RDe)k@(5tPPwx#`eUd=VLLL78 zt7fh7`%&Jp$r1Dv_JtlraIf)nc5A)ltj7rR9~WtCtz^ke(tywm%?q8BL~W_lFzvsw z>lu-Od9;iMgvny;>ll?4T^HXo&`OL0?DT05>kfK^B-=Y$3qaWK7QD*(%6IPh3dWX^ zblRh(98bRq|p3neVe)&2{RVGAT@AE6&tB z9;2puLo+Nm5``9OK)C`Me0u;|GNFZD9s%$hktZSLU((3Ps_fmW;l;unzU9dY^v`vu z|G(=Hg4M$U-{P|)l!nC4_z|jqSuc8Z4XCN$ERHB&5{z6lBTJTf!U<-?m;~?mz?G{X zq$k_Cqy$^6xW}ozzzC^V8E&1Aw1y0@lsyYKP(#ALwPr7In#|f?ldcY0fR>f&h=h8H zVwtfGgEZs#!v2xGHYs@OsGT)&PWeq*5EIs6IrFUd6T%_A(3=(s4<+^K4HCPcf=Rt? z%xLrJ8m5z2t``+Da@|SgXree4oB7>dnBnwx>-Oqboj1n~%nug0<+2z<~RVj$8O!f#h`%Zc`hH+XMFAyIZT&Ik6nqvX`Sg(Ly5QG>G#eV3)q#v!6T81EEJ_%=GC|I?zkXS%ANXjYXF!!uXm zoXc?Ay`!{ge(Fogqxx~C*)vK?tKVomZyKehR4Ne#@kK9e*p+H{CPq+7R2YW9^>N$b zY}+26U9mGc?)iSMjWZ3|TTOm4|4qYg{*74p_cm%{NG6 zEd#w}GRxS@jkV!3ioY>Ua6@%_pv1MfNlVw%3+A#toUyh)B^0bK@pso4=2`YH^Vo&&{tFn+KC;o`-2`i z_^7(!FKLAvSjZnFiC&w6+gKP%{Ps(Lv?55Ib;)^Uv60MtOuo#;0UYr7yzv>l@gQDm z9GsE`C9fenesc|s`%D*pF-fwVVj5%~}cW{j0(60Z?7t84AkV zJqM|nnR{A!{K*5vY|4IIu^j8lPFemMY{8yQZ*NHfqseF8lBliqjW2S`2X%8oIeL_B zssy-#B=)4W`v~x!>q_d8DHSrdwll+?f;tbx2w)H(#^3$|Oe=&{TJlNkC~;OQ=0=ik z5k~q`Leh?Rky8eUw~iCZ7z; z7hhfNrYYCBYmO@16*_m*`UkOJ)&@6Q@(c-(-NLj|-EA|%dbwwru<b$I#&lM{Xz z1770C=3_T)h{whS=3B{I^?{kE!!;n3U)v754JwoP0}=bNHT*Oz6xdD*c?a6@gx5X>Gg#yHV=5!yMo! zSLToy(xx-?*h-rK;$x|=E!qjihrI_ug_En-j-Xa4!GHR;T=DsITFMSMWewR+^JBIK z-rh^Wvn$!SH%pWxa|r)$CclJG_+GFk%YjJTWxse7)v6>d!I#bV{#=4gWsQ919?ckcj(av~L0{S!G4@OeU1 z*BVto4HuQlda`RGt938f3xheH92B`Pp4@lf6gIh&=$_{Q>591&wwP?%cuj)rD4u|m zc|39!qw1Ax-w~q|S#;rggPsKrap?p<(R`A3HtIexI><=vV(C8N!q)2oz!B7{e;=BQ zmbqI~R0XkNr_*9@p_F{BMRuk_SYflK6BVQY=HtXzYdvea{X-?zAD-w0xptiY5O#xq zke`KSv4YN;pA^QN;P&PFLW|NyxLftpZxiSHl3T3qk zj6j`i&Gn44I^DUbXoKLRL6pt)c(m@tTD(3q<Zjw(TZeGEp_NAsU^>(0 ziBgF~KXYSU{%bAo<^ft(UBL@i+t8TFfe} zZ{}a-CFe{CT2A&>jGvA2wO%9<-)|Z6EYK*K0?O=ok2>6SqW6IAeT#HP)On=BhV3Dl zjOQiQ(iwE|$_I_SdFBE-q3*u|Ir+quKOZE|pjYQN-UUaDGgl(x{X?#y>?Fr~dL#u&a$&8{gMB36~-l6L!nDM`gDxpa9ADR7*#LbN%g)%`kt%cgd*Y+Q@)s#FUEQl7i1V! zdCZkckN$-_p|71BtJPRk4Sb+#(j8Uzsd1XU2BO;tYy^*g4Yn{HWPqR&$ic zRp?Vw6PverSA#H5B-x4Z*1rj^U!@&-((4QRF{8cQMY{r9+eusUBT}|Dh_1LtVtM_6 z>0TQesEaMjP698-TB&h7#|sU$3+L$iT%upA(sGZ{lV_p}GykD)NymBo6%=s~Y${Lf zBJ?Ez`-d{T0_m2oRPn)m={KT*a^~v4l!&gxv3j4Ok=1FU$auJojNP8BTry zQ#j@o5!T=qF)O$^8F#UGHc`~Qdw(@r_th)3F{Wjd=@=bae_c4bG3 z+;5h4PbxLq73!c*r&vlq_QWcEk4s;IFhzxXZ6oFsLwZD=D|*2F;ou5Zr$GjuwNxs( z1k0HO=7sIv#6=doB$mq=+>8gOPhuBXVZLH)R_Y!v07=YP6ftK1l@;k%( zc}WQWhS-ayVpmyR|JYuOw|kOZ-UY%)HeQa(R(!_HC8#8-eIo0P?hA;zA=Zo6LIk%Y zy@V0BiS#9Hpbr=E2G??!BmI2{aSq3Oo-bn$<|<~9Spgaz1WysXiTQwkOT1xyF9_GK z;r!5X=V0P(=pPX?iY#H2d^gtsJSRu(g(*pJm6>IV1Fkp15XjFpxs- zIY@SV$11HN4A=?g809v`sCd`|yMFA)`t>S}O6jamljxX!$_F0>`^SiWUG>HW>D0yd zgaYG2d~%q)ltxx3uI3IBQk_%FuZ zd$%7eo2QcN|CC!i?jf}}a*(eLh0FbPi$4{Ns)3)M2IDh}hq<_$W@HWt3=iiijfS|aJ~4{Kv!&$co))Z7|54R7ICewZG z!mRYmFQW_l1-z0+61R$+SfKzJ5!vCX)V_-~R1El_sk% zg${q(r2de^Z@8)**PQI?9KWfgtqc&HnM1#8&0M?O4Zc|ySZXJH z+cDBDIL%LYN_HZVeX#1!0y^kjX7nc3M(JC^zrEbsTSVPxm$d+~zFCJ@HjCfo&zw$a zTsjN7JA_+GxT(8gO7m5PMz*T35p!)nn7v`0GGa1L-hQA@z9~`Zy_83g9Xq7qx3v|# z5R|{8kTWsaf?09l0p_F)c2x;tmuh5GZ zkp_#-*b&sa_v+V;NDQw+T8wprhEq9MuXIp@C{$bBsRnTXgQhFI13Eww1tO`SmFaX1 zcgv(GqlD1I0=MyS6h*+ek$>H@RJVY+DGfCLkS-lA*cX%GlZlMat z9CKN&=t>B6^OBwEj?GUvv=ZNxC>B8{3>1LBLpvT9;Q8|!DUDn>QGLm^9FlbxpKp?2BRr^p6KI&+d##Zf^SpckpS+E%WW^!;9b)AKhIxIdzV(?c7N;s#&&A=x#dO z=Q_?&i20Tv$J2Jj@58RR=URKg$75J|AY-%?yXZq*Ep@rirL{GrrMlO@ay_?|ZGn=t zK!KEne^;$&)qPB%M$$yDU#?NbAWXqcj_kQfe@V394Ky=>AM{x%jtvg@ms}ZzNxc|Q z?$9Ii2$B8(NOb*l_#I9^A!#U_!}$TlGw8Vt<%|B|JPKMbe8QU-B!VFY{O#kpHE!^}0p%>TZgfgUvF$pAU9FJzqLMe$4-v&*i!yc@GK6Jte&3 zEz%t)kl!1T&C`ia1K3S}=tUweb^PJBNb^Z5@R8^Nl`SxK61hFrRC(P|q^s7J6g?d_ zhGqt!d-k+o0EOA9CdOnL49M@gG`Lqhoz1rOm%qmoXR+PO-G|LOMn@(`ozjqN4p6bb z%iOsS)yn4|C1HLesHAs;tAk$Ugh_APMIuw#bmh+^YXfrmYIUSbnlFUP9|2j9C}@}) zmu6O!u&~f&>wV-L$uUNu4;CQPN1h;;g$Jf(FHGfq11p6H1-%e2BY*YGcQeA3zEj3o z*GW$M#iNWci#dRl%d@bRd1HL~L6M??7RU``LWR3R?EVhg=8{Sx7tKf6vrX906r}OY zu_wr4tG#I253JD&dd+@vdeD^2LI)~c@hrgELF zGJ<%h%1*qVq}W3GmZ~gXTH|X7$R3sBv*hl!4&e$xsaa+1VeGA4yfvAGAt&~VYZOoD z_A=8%Yxauym>1AX-5D{A0$+#7I{ssUUSf~APO~J9CTg<}Hn zsiVaq|2m^*vIuQ@@`(ECQ@nNDvNBSF!gf<4akH4%nl4WYk?V`m-v*!E^Ljb7&d@k;}XuM`Pnh$$KS32#eHG`j-FEI2aYucV3{co#jwHI zpuFp$GW+m%K|(po=M#&vR)@k77Xh7~;dxf?yZSXzD44n?iWU=~IP{yD68sHapj>|N zb39#(j$r&Z>9_MSt9}#jUMr=g20M#Eui}tT;~elaDc?5C+;xg5dZKrijiXq#pWh~i zjN0u+Crvs=k^XT50kX=u5p!Zo3=Ft49UJu9i2aUQIJtdq^{q#M6NLzS0ELvStNOs%=#(^^p-pv*1tm7Rusqc178mMcQ_OFv_1j%j&7<5bH z2a>+=i{9|HTK00CZ3Iu(U=Ax`G!Jw`A939Bb+sQ}kf%o@<#D;>wk!MK%BjW%sy+aKl?3|i7t~audUJl{jFn=)nu<555@&{8Z zKMXRRxV#r>4v{Cv2(ie)u64vuep56Kn8LMxDlAV8SpNYy_hntM!5sY+)hhnk6(pVt zrCx2=s9y1FptGjwGJO=$vjoMsv-W?LTNzHV{Cg$e^JJZF%3(vxhkO?+Mp-Gr?t$PF zcz4p8vuAj#>;*5LNm5qQuN9Pt@QOKmae>rKz8v$8K4ep}b{35wvo49{v*j?%79q}^90qjjw8eRyh zE$y;WHNkhgZpjq}K{|CQc0enLOmEkHe{m4&8-zRIG&UkmBX8tbKeireWksnEcT10U6OOQuGvfq3#LHABPq#KdVG{ z2|E5_CRqHsi(i|7lL7+M3r?GuICTK~^5wd8z2#>p z^LT{;2`_P%K+LyJVmC29Ra~S?MibEkx)z(r%2&?9tbat37GCdow7)>hsVdk34WDWl9)gOcF3y5|_EkX|^3itgP+IeKzV zP+{He^;SJM(ef)W7X|%*eKnTu+xK5>|NWpZuDU(7TS8=utO!Ysu#PcyHy__6v6T zF(G#78HOwYX7Az%U8yTK;Wi4DN7&P9*Ug+eUm9&6xPMdSO9$x9hzSJU_=k{Nq~FXB z+Y5Hup*?%OpjFXh|4c@UDSdU~63Nx9rt^C*Hh08Bm7 z--SwFJHeQXI4_$;cMtm#XgM@atdpp|^~%q!{Ie4<^FrKd%8v`cpY%^4%SMR5Rs-I- zQw>5yC2npU&guPU96#5-8AUQ|*wBVIqtrRRyqkf(I)K84KV0PFt<{z_3KyehZ$>dz zFLVP|n;p|XI&xO}_v%M!AlTwKG1O&KHH3XL3|3#9>s$G?e<~+WCmN8$wsf0PilAYj z;cGmF+gU9z@FlW!S3JQtrML%R&=A2TbgSv=XlYicz+Jl-hJ!!eTew+wL2AJ6~Dk3&w>WgkA6|3cYr@CBuR4<2@L#M8-md~-yP0d%c~ z{+b`TP4LO4XpJjJ`CB5NYL-;Z8pkd>SKE($s<4kz?+XnPg`UjL#=lN`bqW=hHGxdH z=s)a4zyll1sb48S^Siiy;vw*VYp9M_F6?{)^xLz8k*}?Ug?X=LzZu3>e>_3H@g$A> z-e(jb$gX?9H{8a!i-HKZO5$P#N?7V6+V{w3J4L;g zoZSF!zayvbQ2*J*8-l-Q*L*hp*#|!|Hjd}FU~>4c1um?dw4bN5Ot${YSaNTo;T-7n zj%U+NC28+)x(%pEKfc~@YOl>t=FAQ>=ol+k%yr{gL2kNsq(w*%QP3cxt$%Im{a9#b z7r3A!2J>zXt}|Ef8TB=|h2Sy8j2h6zxP;rBNK6!k_LU$-wytEzOQt(bX@xRVb`MKH z?PVK|FcaXQrNqV!42A+1l{Uyt zm;*Z|2s~m@z4!ODD$h(Ob)+~eF<(2~V(jLJ9ZNqqPqA6+ z&S8z@U;75~3&$47viMC)3%XjLPNiHOxQFdrS^M}gl$&g-2r>T5_sQQ&Y+DjZm(|xq zhbSK9s{a+D!lA_m+vqm0ajTa|yEWWJ{k)dU%RRw~ueInCp20-cx>Z#nSMccv=A)ES<#K9>r}~GnDsHuU%8%z8Dgyt$-Q7kG5;rAarh2kXal z>>An=jFS*OGe&$reKgCiHTz4v!l3+ou**M;Eyg9HK8{XGWsH?0)wph1)0uJI^Cq86 zLQ)r|C%WD^2Is8IkDsIb&H4&uK&Ig}bMq~KG4r5;jq^*~s!DSy`JgRXy|xv$T1rM9%7) zM#!gpl!%^BzVZ@({=gJ1AQO{1iIG+!Bfko{B~*bpwyjn#(pNvb$h~?68o(5Q-ztsh zHP7Zs4BiW|nPy_XR-oUQSZ?xIGvTx3>>_ND zE^iBVx?YL$JupQLV)g%Clb`fx!|y{Sq!+SU^s!TB(F19X^`{?MR#2Fa&w(yHVKMJ46~ zo-ghr_oata1!7?pR3(zgoM+ZQ#iGPN8x}^65f(Jp_Q$4|=)FnR8hMkH)aYqS!aG+j zGn8I87pj_&*i}!48t;??vaXdu`-r8(H$KOqnN@13viYJhH@#r?>E-_&F#JcKTbFNw z%UWzdTI$mS%b!9_ZcCT!vwx(6OJUfm$Hqs_3A6Gxl2Z0-1HuO?>8Ev#) z2lQd>R8e9^4!=&%v{>Oik~e!+7%46w+PY7_XBmB-enwonmx4c+(G|yk*NE9RV~J5o z;Ex$m);-J42IKD8(OkFc&Fz#wzY(P@wv!DPVg?R?<*L09|Lq)xV3~5%rl{o`pdjYL z&ebu7i`NMZmS*5dB536A&y;-qN8l41kb~w$xN`yl5)z3xCcB2#h0H2D-qyu`+=y7Q z#;wY`xr^#2L-m_cdZ|g6Si5#){L3`D`*kSqMI1Eqnax;+>Bmiln-Ft@>$Eta=ehd?7mm zm6n!8_b(UG&d=OT$(rSKIv4UgTn)N*4ub6?`%Iz|CJD}KMw?iU8nF9T zoEaJYx;yjl1{{#5I0%DY1^+lTMSPTybmj1@hjaxHE+z~ge= zTHO;9Ea4z?!xKr=kBr&W>)wk-CPNQuP|-D%=`@3#rRN=QO!i2Q|yx?9%Hp29HL znTOq=mcSDT?WV^i<1>fj1iTdecaHg#>-2f{^C(NLUtH~t2t8;XyT*oyO{^AB9r7I< zI`|caYJC#abCTZZHY?u|u+pfce!OGils50oC+ERvnaXulPGpc6={fv&u>jU(<}oSz zt{utwRECC`^S`r=TMWLTjO@jK#H3sPJ3e?l0{vac#~>h#?Ls1|cIia#t${Dy z{U{9&uaa)O&DfIUudJ}i1u4$BUZ+ny*D7oECqa9`o!qGX$%hMbXj$V=bG%}=qo8@o zhVH(r#W>Bbsry@G?|2DW7c7*&)^+>JeP&?Rwzp&_TF@UGTfb;L=ps987H+0R-zQ0A zg?crp5v)&=R?0I7PwoAi{bOCq_SLk+q-R;s>Y1n07P=E+ghPVYeFPxz2c{PTNuAz|3tI~BVHPSW5q~vU& zD?T7*__;g4#sPfR@F!=eH)JaSoB)QLQyRc(BU5+sf6CA4l~k*@5RahL4RmERU}V}~ zp@T8M4eybJ<%Mw-F^MDB`J%mqZ{Gknie9!mR=am^jpp;O?P^*_CnGA|SbDr|l+M8> zz?n2!pT+|^F>6#)*fk6c9hU;5DUKbNfa;)lvD-Dbx&ue!E@Hg86DnF$B2~}0$?@k=VrWSOgl4eS?vh_4c)3|P709kF~P|diH8?YGF z@lPIY8m3aNcFu|!!FH6l)?oYcH5{>G$W4q};Q1l;bF3i!w;?Y8Gv3WjVweJlfRCxS zrLd_&^_*?@O=^*&=*J!e02^0DvUn%%7-KAt1a<+3{h_Z4<#P%Q*=m@xI$48VkD!wV z`9)$^mQrFo3j}${wUq2#qVF-hzg<_JEe|haEyu6)ec0s+&qJOkf=O$_R+bTHo3&6l z0_P{Pe}ue6SuL}3MKYA3+V7qJI&yZHkAbBlXb6d~4KdyP;6BO(_Sle_c@N+t@*Z?3 zf9f8?O4l(?*YE7t?c9F-&K8X_nB5y`ey_DAcI#O1)CcD0dpqU}wzOtH9htqsh))UB|Essr zt$n*-5EV?S_fK-qx@N1MWS?iAEuwX~{u~*}b7~XxNedP0R#Fsuoy&s`l-Q3_-*o8T zUNpi?cx0Lm-y){on$r6zx&2{6Z>b&7;Yc(TvSJ%<#bd&|%2vB8WU`sO6ctq1@y0s+ z)C=#d>Ey70%A>~0s3#MZRO-XFmnY(pOj`3rjd$pxA-x*|P}S>`t1w$n(}CE6uYX&d zzt7@&^#`u_!z|J37pHFS0BmOUeX2G2FlOQjB2v5ZKIquO$6K%&(^M&fto|pmc_i0=ofhXI>7i(pcZztTRdpqr@Ga?F=9oZ^-RD$|yl9m~uzT3K`)# zHR&2l6kCkFeV!03{a98S7$=xoa4IyLkE}Fs#H|SUH8i{<)1{N^nmRgov~CrRXf1x&bn2%_5(L_FYJNY?pxR^>3v^-k`@DM)Pn&@(poy} z-L!O0+nf?0A~L>6r~k?SJ184=(iJ=Gv>#~kthwL0&?5a%1AWBEJuG$WBZ=tGQ>6>J;fmr4g7EI23MR7uDg?1( z};q=&)RsZ&}=YhKA> zze~!wHM4MM!zS~b?lPWs8=r=)Yk5Nq6gm!ri3det@=pGdtC(e{I=2#ZHC1q8uOk`i zzoIAb1%;_IAGq!sF;P@OOqxpm!0oj*Y=?IU*hk4#oic2CS)Vq|*#`e45)(fMY#*zY z_snvYvmE92k$`Hnn*>`!ls*ID{C((_lrz&;EI+w<0PT4Vhs(Anbn;ExcI;T&Rst^ z`p4*JCY0xv+J&rK!92>$Mw zd>cxk-j@K*w%AP!`Pu%#fHR3+kc*jDN-`mJJ=u{aTbYhFZ_g}U7=Wa z>}&PjXOghL@t%wH=3L8;11bF5Fk_rqGzC5Lz|Et-AhQgudmi9H<&AQVP#k_xqCBsx zZOvz0w|$cfLmNJnNHGY3d*$q7B`^3hWZv8uXES?7`)*P|+beWoOGYMTZWcby>-t+k zSxffJHXEzdpLD5-k7~&iOoi@=U^FOhmbUCExYPpZQqqT~UhNS|Dg3di_Gn0DIAm35BmLytkn(~Pt@=kEkade6~ zfBpYoNE%SE10DX2mxxoRj=((Muyr0F2`NF8nb$7 zs5F5&XY&2lM19rw7p6TgrIZg2F^|8cGa`MbQ$(06#6GO?O=&&7V1)-ti<=Dn!I?+( zn5R4tol+v}$i8BFp^Lk}flgN(%yMJRY=7GQnY*aO6%$Y4{k1XeVBaKl=05U`EO2I$ zI=;m{uK8Wa;li+_?_)-Whb-!b ziAsHyO9Xs_dzzBlDJ8B!rp!W0shW_@ky%u?cS9YhEWb%YWaset88@Q zpanML)Rm_RjC=4-{kHn_*me0p^(t6vOHst?%rT+#zLTM%Iaa0A;>=-3!E_E-08CH{ z9)L;wM@pavWQttLk{XQUUBE2me(KkOK-68&ky0>M7Y=^R`xja|!HqCG{gpU~)_u!L zY7oW%0G-4iBdjpi=|eDCJWy<>9sc-NBU+k&c z_VO5T`Z{~zg7U&+9!m*>6l1&@oadtEhkfT4xFN>QR|(!9ryDaw_%4Jxc1N_8Vc zaeJsh?e^)@wve#T1kc4d=NAS0g+C1$%lh0W8XUVAjL5g?ikEtqey&3{guK)laO4QS zXfP&jZl~YWWhc5UDmZxhC-=fm@g|2NcaE~*t>Aav+|3Erp~*U1m*R}Fd@-LMqN46b zAzT6Hfa8@gH-ULt zmXh5nGrzQz+rz%6y}?%CEM;JT{TR^~&?cGx1QFJrIaiEE)f${L+JDyC`waGbFwY_b zl$h78M9aqK0I(XO6{dGa=$khXJt@4@NG-sNVs;y}5ty*fS)?xA(#E|lhOLrQZqZ>AD9e(c(jn{RKc=*G6?LbptBEXITFapS&u znolDVINKLikmgyo?I#{3^kpLra}dk?+Y*7fX!;Dr#adiFm<4!`>;)f>;c|t7QYgmX%@tSC$o4Y>Ctzr5*E=3rTbBbHgULRoWeN+Blh2%p@(R zY)2P1&;@B^^y%Y`*}9BI9Oqt1+(g5%#RaL&$dzI`FocD?OtUAus}0kVNH*tP1NAa) z>76G|X~l%+frI-YDfazR{C0w1Q0EN)i$NRE52dCO&%1_wPAiZep-n@1PSAEwUa3CF z{MN%yS>rWdNPW&JYv_$xs{%>Opri+z1?K53so?O2+KlQxB}?1)txk34Of=9s|Mzg{ z{qN!EYcR*X_qjz0<%eHFj@XD!5kxOPa5X2XA>=vmlxgZq#2KQqrmi{CUAukQXF>Jx zupq%dzV^f~-i`;7pEB|a|GMGJLasr948Xk#aVI+*9uq8PBX5v`X^u zgkqC%I~V)069D{rywL?iq~+d>C?wPjCA>i=hx{@(4^Z6|TYk7Ha28_;CsLK{ZKL!ksk@JQ zIVA*$Zx>AmRpqvMF|za1L&7c)U0%p;={h!$7__WwHXcy4Rrvw{*ZYZi;W~ukGpU?; zQK%aS^0StoTbx7Ybl&W0 zWZav#aL_Ca9V16uKO#N8ovZdIGS`gfEt?lO%G#|AzYIQjJYEH&_TwY1{G>qEmoctK zLS!-CO+WzG{h$Nj-l6*M-m&NKpN8?sl2@4GL`*RONpwd?q+OkLp-OiTX}I5#C3Jd5 zIwL>t)g+Z0V)o`A9=MRlc`B!_kgmX%SJ7=C;Sr608ukVxH%UW?~NK^v5ZT@-G;1m>1OC8yn#HphR|ikzNeUA+` zlx7f$#I+jkJPUT!1M+>lf-W=v`vw(k)uf#~$ABLP_OWW@M;A7DgV3ZQc&YIy+#d}9M%StOxo0%jvl;5Rv7}eqYO>+O(W+U>CYjMM? zCveH~y?1v%)C@Q1sj+(!C<#1N0mX~ky@7CHHEgO`Z4QffAx;-@4J419GV^tys;xIo zLm{y%vAMzS8WcWxV*+&j8`i*6_y8NxvsaJ@1_Ljlj`lRqF&nd+*qm@VtBMqhv`e{a&D+5JHg9&F+&V$Hj0uaB`=EVxWrLckf^&WY3c8yT_P9H`;VQB{u%EGMCaGaZh&c5UMcjwhm z2Bo$*DR3Y0`PMkVHgru% zG|S_J`Vp~5ta>&z5$G+aY+n{Ir_sTW6RpMiu_XY&QH}eufO*n&f_OWtbTP@>ueM3~ ze~3Ewcqae<|8Ki?a2PYfmczyn zx6a!L6+-Gwspe1#)u`lr{a$^(fBgP-ySZ_@UDxY+Js;1<XJ6L&`qB`p)Z!JhOfdCbN<E!F=L?FE2jX`z+ zXYuGC>A>Z1I6<$%BjFQv!vn^PL$cQN*SbjAONQ8!PA9%yi#y*3w?2Yr;bkLeZN`Gv zfUpPhxp{_|awAd4&lmj3pJ3Bp3BsJ{-jOk6U=gtBK={n>AE*@F(-g4So2(=-4m3ew zv-XiiJ@DU_adP_+8C8|Qg*B6YZllWM@+PA7Lz(}YJ`>ef z@@$*s9APPW*;@32=fbrjL6J^kl5NHBaau~$%!h5;s7KIo{70$#Ta~{*L&~G;noG#Z z$+mbWwBl)a+G9##w_wi#ak%EA52B-J|1L&=SB<*jBpzr&J=%DDe1H&LojS;lrm0?9 ziz#uzU&k%|#98uhq4<+*&}+Zex*WmWbCu&>&Ext`wj1zP!Fu@Zsyq2dP}dG3WBT=P zbJ-@7FBfHQ-gU%Y!mM`SqgQ|K$0z&Og}G8(1clUYdmJ#adZWzGr%Jrky3hTKNT_7- ziuiD~JJb!g95@YqCpH$WL4&2~s^0kB$342C=lBqFNnL>RS+d@Rct^+EQFn z^DAF_WTyBov1BuRR&87f8dq#DO#dDt*CR8(sg(3|N@3Sh3Z)KyIjb;uT4n?qqwy6Y z;XWojZfQjJuk9fXec33cL>)yG{yDcit3 zCEWZX`*7?aJAZ}~RHrYg)7aX>*qqbqF5w8y$(p5bj~#s7>sqK-tqRWw)Y^VVNbY&Z z)X2%si|^JlgId-iTDOT%bwHWsBuNLjLk^_rw%CQF&IerK8Ty;mgnD4tw=~0dui}>z zZ{e(Z=Nb3&&>CQN+$6~fx$ROJ3!d!cy6rhCstGKo?iQcqr>UcDky5_x8W&l@Y;LJx zN4@qXt*2>^#XA2UNvo%h?r3ksgWM`ut%P|W@+)wZuO+bK% z4d_mhmFW2^mXTr=jfob^tnh_5bEFT|D-%BvTjbB$%{Tu;nLoV$32IoxL_H^j1*GR` z)Lu=`LSHgo&_>s1If(BXEktX%ibhk@J(U!AbQqqY97XTi%;Na}_A)Zn<6g}sC%I9Y zpG#xn5!fjIG5fK@Azbo`W$5p#rhbpOLo@CQ^8NlwVzKuqu{e#>qUwPuqMq4cq6f$7 za%?dlt+g~pd|`KUzzs6!mh~ehyYb=qxGx3LGbvs_?bBWfA7EFUqyLb)j2M-d6YMma6Is3<{NL0%2FZx;tw%?9u$9@1Im-BkA5i z7kI25O@v}Yh{A{1bD@#G#cD0t3gLugHp^A*t;E%Ih2o`Nc1kp-zzUE(Ovr@e1`z|s z%suVfQ{19BEu|(mcyz3&8h_=6_#E}ml>B!y55*c{pX(Uf(LN<|golZRl)Ca~s3NlO zw+Cr%G$0X3fI zT1o)bkd%TMWkU(GSVj;saO@Cx3D*42`0oAEE&g?R)W;Q>jD6qj;ax0byr}O31Wx*< zTw>1nI8n?NSo?N{(<<}G~Kus1pB;RC4j4$Ol8~C|95T3GKZu*az zK@ZNRl^I9dj%(jQh`y(%!s0?W2dGr01~(eEvuG6g-yvP0S-BtsQRP(phU2YAMAVSo zh5Hqa+Gi$ZdZh@tPvbQ@_;?P{vLm5@wP)L3dBRInGJhDr!IO< z#{PILy}T+X6|T7~RVPq6jza|=wq(5W+Q$A(+@@KA(@!rWG6jGS&`Qd?7esM58RP&} zYk7t)KA1y|w=r zDMd!FSUdis)j9K@7mqM;%ipt@XoDzGn~K3DA5&?Yv<QA;s#tBzpy-Pn`nW-9F=gKqbRSg6r8WgfGjNkY}~A9^qZY*NB+ z;^S3fGJ0n%rblu=NmQ@@@P#aU$#MtNW1w&ae<>wn`($pYc6k%a{{c&cr%>CgJ5x=6 zvb1**s@M32qdG`6YG)Vn-MDb=jBI-r`E?O)Z#0$4nQ`MBjtbd$wcJ!=IU90p=6;*bE#^=|=h9!~x(sp}P0zHL z^dPjJ1hdv5;KEl^!-K-p_SRt{Tj5HqzH0<%j{tuK>RSR(Lg(v3+jZ%-FC(W_4J$xf zLNlt@XPlHm?n_M`(ijnYT1jKVfG0m2?=&PkAb5U{6EK#>j839;H&0=TjHZ77S{i+3 zu=Q_cj!MN^smj+JIKKP6cCA({yw|fmO2J+ej^;&?0{JfTex0svd?>|_V9CvC?-e}(Ud)?A6T8%jn>afKIygc1%-F>$YhHu#MfD-F6?F} z<3kl?@yH~+a>ue7lyfZ-IjJ&geFW2=`+@E70M#j0Uu;st;G_!TmUmOaI5jbx)tr1? zGcngh_0f=s+yVF<1r0KdZDCh4(Ayg|S31tdaXD67>GR$V3mK)_3bv+eN`Zs;1VcPV z%|Nc_JWF9&FgpyyA)4DDryD#)1Z~r9*RaX;pIy;G= z)I29-na*(HVs76AZ9AY}HWF1rJQ33koJYDw+I$e z`0``;30I<3jnH?9g1l~{>Q&5~=bE$|kq$YeM{n*QG9na=t&Udbee>OGG|}#@zJ#e? z5IY}5`;^}3K9HJlMTX7$<@1u(Z3UcsD;vbap>7y4fYRJWkbre92yZ*lYIgS<(t07e zH01Zibd#1N)<*MuiY*Hm2Fp#-fgR}Ke*k_?7N=g-E}1x^hDQ4d}8S6+0U+}QjYV-F>-+H%to z&Plg~j%>rL($grsKqt;+uY&6`slrdaMjh}$J;3(WN z;mL@3sCW+bw=Y0%iQMUugS!jE;pf+3%DOEzm^cqkjgk184cHyWen_oN!PtDke8h=7 zrOT`#79;vo#us+tv~ay^ojYx49vl9HRz3^rv>4WZfCLOP`P(=s&hw6UoG0g?Cwz6w z9B5Glu)Ct#+f>Tbq_iXlQT#CVGE(seRTTRtpx0Gg?|j04EkVkjJR~P%PK^JUnuwun zQoA^mq4&0X(=lbI&V?;QnmpgN#M4ceiGH(esPP`Ephq|4OHM=v6Lgqf>NMJ*F4%%z zPpX0Hw|4{cb5~hVz>6H34)w@2x2A@? zOY1%FbIYVdKY`nF2$mbQ+Tw$1WXr(Ax=;RJ9^DmPA1pGo_LPGvO|CRnw|gW|kfyy1 z33d0goM3VnW=HEC@@h-1aFe`a*6;3Vf* zs+NWuhT<|J!@Q}`>}6vnG$lINA|v!pO4i;LTen2PEg~hn-9oW^1ASTi>rAp$$!yj% zw2Z8#zA7i_IqG|!;F471i1;J@Fz~DRs!pz3A~KZNw>1KcTAPg&^k~gNFOI>n{*NBa zHY)r2^fN!OAG7$%Y9@cYhQ$}SS%6TwR*l5VHYvR|N2(u~Cf6Ny^0zeb@^Lps$WiKw z!RcIb=N+u;sJfE#VH+njBnxWB-#iC>@0%oiQ81ZeKL_g$;YHnUQ(ctt3!+@%sv(|Z z!((IAHzrfW;wJ~t#a6&`QNd4hH-u`rRwFl81sJg-jlNanf#ON21iD-15t5G{0~x2hJuL+y3sTpbkJji#T| z7%AqIsD7>RO4JS{CX!SjXwuO}yhV&>SmJz6o}MRtrTZL0?Xim$@rtYflB3*2DrLSw zQ$Jk;Jyso$4!PEcwl^lma?NobJ;OA~z!>szCEmi-6%@MTTe)!VJLp9;Y^#seGgqEe zNd!5O=Q%2j&QTS{VEop>MwbImbG6^+1U^8pk^SsXUA95$j4ngszqNMYzsnkm_Vn`^&%Xkug?{=jgwg3*eNc25z_NX~54&>+ zRuIVnKEp+y_ZF}EEDpvEUD?Z>l46Ge?5F`%rPVFr4n$2hnKYxmX`#<97W|F2eLLMG zeVvVlIfW`B{Z-4pONt`%PQ;f(^$wDjWCtE1o1Gk5M=H%F|sO_ACJ2OZDmV zQ82Af{cSmV%sl#s1LTk4m<5RzxJzzv<32@e&A(919kJuE4`=cejdN$E=O?_%>Yrdn zL@4(Nj+24p^zn#w;^f7^g%PKLFn04=lfpb?`3{Bt&nyu8PMhnf?!W$s_+1Gob?pUD zn!^hO7!-@pPAY=^Z_-HY-isp0~siJDy;X^gx z8-A~U&~D=4)5RgX7Yy)rs21EB+9|(gxLDAKOS(_(_{$34;tz_ESNw-gz_xJB2=x0J zJzJWG`@ZpfbJtx`&gQNYfPtWo<{xw$;eGSl*rKxi(rnY2*m{o|DU6kn)8YrVK z^_?Iccq2C@=mT_*ja8)dqjdq5+QJXmt|EU&iWb}T$em87RRaDG`9+;v<-AKKl^AA# zLZnxt?S;9D7#MWx8hun7<-)NZ(tz=_+O*%PsOMVTf%9KJFTyuoxS)kkJNW}=G^PID zN8YHUfztUUlH0^#K5TmpZp}v-IEl+sks7|-=M1?Hn@!$sr>X-8bPf5i@ogPP;8ZAr6) zWN!*$;%3ooq$}BjAXyqag==*VKxDmkr5~&LBVemcM{?51&P$Zp+vs|63QU$Kz*Ol2 z{PqZPfw)buulWJ4ls@t02MwPbtA_^`I$xh|F*%7}C#8^^8QQR8VR+an_L_O$qZ_P`$;Oe;8niFT+rCl-Y&?PCYdy-(21vkjHTLSekfcHNI#xrn0LyubzfAH!#}6~-ef`y7dl+1U zu5LmDO$Qku)Uy@DW@OQ7Da$xWO-+hNv+7d^W7bL9aE!=C$b*$uMJ`cwm`&@VPLXa1 zs9f`@)E1s`*fn*{U19d=Hzx%fsFuT_Z}C8(*<`t%l#k9y@!ul)Mc*vXHa|^&T3-~h zHz!3~H_S$l1piWWs9YCRWtg0E4%KTI(DcwU}P79V;=*7~S67xrECiBt12$C`Yw@mg4P) z^1T(+OBz3Xpm1fA7fLSl(SRx+ZwF?BSU1Hj)H{95n*hw-@4y@u3q=1UebqW2d9!BF ztw1Af#Z5?De%LFIuzKgiVEJ)I$-9Ad>UlHPxmYWV$`^I-8 z(7r&pgdB2p@U4612bwVpe!W8GZYI_klNM>?6_uL6V&O8W;7wjYyIzB%poQj=LiCG7 zCg-@fzi$(^cL*BGYxo1p41G=<3o4 zKA5{GUA@bs5C48}{=#{|*G0;qK1xDV?{6rHkfcC?u6QiI(2;Ut2@eDO1&G+`^_IJ_w+Wr6Z4EtR@#Znhpo z6u*1SOq}g==PzREt6ySZ%EBpifH>b9xyo-9AB_y zGEtUlmReqIfBsW*k7~G$O9WMGN;ByiBmZUDYsR>9wV|yOl#jEFl|3=}7KOckft+Zr z&)89-^5({vPT^{>WD2$jypAW(7?D%@498+>-aFDN7W0O*kN6R`Y_~`P?w`a)V#6A) zI_g2vCua!y`N$yNO_*WFmeP&3(_M@R$7+@4Dq`4}si%g%)n#GK<7Ng+RspeD8MD5 z7sGrxKk$q;`O|daHpJ`q5tRL*rwf{0+OKVtW-h8bH8!)PP93+YA99;g>jimnnR;(d z8){UnFrc8eiB#Tv9z&Ki1{KtP5!7&liB9i1SJWiDQdt92+ z@Tel(^}!?&qR(JmB3sNGq{IdyaWVHk(3X%nJgD_%fB1hnQR{LM<~G6o8RYyW(v^3`3H~mv#0PmL z`Y8tPB&f?p`95!(bdK@i{Tz2v+3(J;nK`KN%CIIHCX5md#m8}8j*^#8@WnCkUI$P? z=8+PB>EcCAlJ?m_g}^GvN{eq#`zVTKOTVFhAS~(cE|h0WGn%-IyC|Dn_bKP728AuY z9Y^2IzoD0??$J!Gl$OI8cd1n0yWxt7!*jS{5l7i{;W4EY0?P#$-K6og0FK`&PIW0X znTeu+^nvlrbJ%hjVp8Qbru@z}8xuy2fI6ECnP_KeQK)DJLwQrSq0DSgJWch8wS#YK zYEGq8MPt>^iz+K`1yJ32U{`QmnFQ^jS{ey-X9;g#s8`lZ3Hn|%5$$z*9_F+=YMjgP zd;bZ4e+2i*mf4!EvJ23iQd6!H_Mf)u_Eb2co*QTxP^sBaVOayX9fkexcCQe-n#ufcSwWYxm`+*4RN$Ktu#o84zdxAu#5<+7AMQ1n3+ziEJy)YYfgAHDq0sey+ zT6iHGQQePUa}<6+;?2cLD|XBB1JB4#_zuOuR8%bezCn+N^_aT)K1q?w zp#ALAX4F5osU5vDv_74bg0vI3n;A%Mkp)%hXRB93X-D7T^WZl^y=9iXbU)2^GfF;w zg*ZtuALUfc>@Py!)cJh@z4Rzcu(}$v)LmAB`vZo2>BAkT zfFla1UTC*>YLR+7x_uI)yB#zEYIWc-jVRHnl1_U)2mRK@LmiJ10gH}MyF=U=2%Tji zE_#Fj89jEzJWvba;GmpxS`ndjS3x(|3x;#l6U9!mpRw&*Iv0X6NQl z_K^zaf6b$n@BN&(e?o43k*KdK}9h- z#LcK{0Zl<1_U=T1EjxV(nbCJ#ISzI6rqRXgCuPcs=K**TWziA|V4ukPeD6JnV)Z69 zwZI-IeV;$bdW~jOBR=m`pEsI?$C56}nr72tdGkJQ28wm_q@|Cj zd861O&5@kl=(7c5s47Rh=!RKZ$cL*Jz~PT^x1goY$EM8uAk=T6UjWwx78nrVcqi`k z49Vsy;&T()>kUZp;*&;oH-6GOso1z{h_WmD4H=+Ir9)IEQ_(smi8vZu|6rPSLQsqk z`Jvw#gzchAf>pzey6FCc9A5{N8Bu*T-qBe&NWZ#%E>1ys-Rc2$4|SeI7u3aG&IAwv z5tFzgIF`-nXlYR?+RuXtCcXrZR(l8f-jvXrGr4aM0Oh~>f670x|MB0ituxzJod-6B z97KHL(?46iVm&$fX@XeKh6aiv;G_I^GShV85XW5)sY-Sdor#v^zV=&ejxdgy1%}9v znZ>te_Ljka`TTR8nN$E7RE(qz0x~Btb7ly=`_-i^TR=O1o6d+T)IgZ|x{Oh}O!v_$sQH-0n zmufVA*akTC)uPs{w@10+M$?vuIfW;@+VdAHbJWb*r8Cc@C1mbW+}C1YGLP20e;*qR zoi7*TEhXp~{cNy6`I%OUPTo*BFLz@wr}d$Di?c9$fIj z9v-?N1-@&q=gr+(CFEH8dDYt0AK7L$ve{p>Gqn_|bfuVci;&^KC=i^`IErf+)$!FZ zOW@jLU(a7SimttDluo^C5~?&vNu#JBsLl(I(cjI{ZP+T*^VsgoX_A3GUK$6&tfjFm ztwkALu`ZM%agB+%NJH@mZNXhMz}8|_A?UwwM&H%m=E}1Rk~^9T#iuM3F1V$YUI#dj z(9Y+|Tr*Yup_k`Si`0d5E!JFCH|d_?7z4~Y4YVWk(J7c-DT1qm7CN#p!`VtyTp)+i zK=|s1T6zqq_F=Rk8YBL@$|bM43*?RuS(oo1*)WK>AjB{<C9Z= z;60~VZsI@RRQMInW?c-acrDCQ(eiF@_=OJp?=h&}@FEv#b{BH5Fx)mOJ+eQARUUyp zn=A5EISUVwRNAkKm+Q70FAnQY6Ka;CDJq(8nN052nLjR_|55Z!3=!b$k9kfxx$KZs zNprkE2GW#>`##E_Gz`E!>~h@5Z6g(jxIwL%at{POkXk7Il zq!-`bzu+>xY0dTe6|Ry6j7_2GmnDAAY2fH^=1` zng87tpHo|fcF>PDE+nxE$!m_)_x`4}{*x2Wo4SAw)hRE0p4~;sI;U|e;IDS?>+aUZ zKK~kcZcuzOSw7oTHpE_`=s&tG1qMgMum>zxu$_LVfS#G-lZ! zC<>M##|D9~u9}1X9>OQDpa=}4vh!WLT}%4du`7x1JLX6yd?I$@uXreNda2?Q2P)iv z+h+rQ(q0BCo7;Jbh_p}HSpVX}oqAz-# zf7?TEnywyu&WWU2{{C$Mt~vvr(50yH(!Qqq2c=Z);K@-HmO-{g?@Jl>raw|Ir=D9? zCet|nw^pl4uEV7^f+M@WdMTrm>B(4AT;GaLe%1~5Nj1++@lZyXiL5@7vbmSdc?{c9 z4$Ta(QqEwz$bIQs7dOj@_qZb6x`NE3=m(23=Io-GP|}p)XPOdIhDh6**}%1)qCLho z`=$W)_<%mPBNgzHYsK?^ldAo$fmb2>N2DLlfvyj9K&BitvyX6UdSIIfds{ucsx3&q ziSE64Ja2ypmHa=@`ZCnn%bs)7XZ!5pzvxN+ybydrJhyvK+L?vIP78+FActkay$ZI# z?6Aec_INUM{1ig8M7iD)nNvE}0d(mO*vGu19(x5Ji%Sgl-S}JzVBf=Av9w};ipgb% zAX=c!Q1+mmINT515IS1&z35g{&w2bGIQs3Q1xBqOzL$`J}t*gwaz{S@AdpF&} zyfk!20TQ+_G$-C?S%^Ee>e>_DSgN(3Yoc;C;KnQIp`ZLTEAK0Lqr&rq80n~QZIbv{ zhfOS3Y9tN09X6--gUu*ypU*WT>~#*DN{x1>ws$cVgy!igr}nb3j5w@*B)XSg%lFFt z52^Szm8%_Icp!A04ngr2jd-g`2=6bl)~5@qaY<1joRf9@Pp8+wvHsXHS$?SrL%c>Y)a9~R7aA$msH$= z`u+>GS$$J7R4IowLIbnXGx}&LpYYw2IH$&W;p(^E?*}y#Dur(9K^`D+Rbtt`#@wP^ z+eX=Wa~n~7>R%2>_(yW~T6pp`x3}mIpJoE_Qz``cRxCgC=$Q1;~i9A?@r(AEk5KAMwPBD z+~0FqDSFFnG3!Y8D>Th;Uqtjq59S}PvC`lUZ`jL`#J9X~_ViJ&Qh{!7Fut|%0ihMB zofh8(=X*cQO+V#-f6K;i;FcQzqh^$1O(b|90y{Jec5!SLw*PR0PVx@1pEm)-sqO66%*ytMiyBi z5KnMsn}8XwhwU}wa=zSSN*l$L-8duQ*gYUB8~1WG`b+Ep4tVk(8>Wfg3vj?+UP(Ra zaR}_@A2y@5s%v;`()Soak0vp9>;Pb&-BI5U(QT@xa~~~G@15di4n;+#jS#*Qc)p7} zIDTp=1=NN7oIpRr14WukIz$VPWGgaHSB9z4`5Bqv*4{t8pUB?K`y5+FJSe^svgS_N z2o6-Zb*jwfZtZt4Jza~C&y-u|dOK-dFD2WI$qJYk@6+nNt*=e6JDF4`A+^i~m9&!M zKi5=gB}(AjHris-d<&`QI5xT-pXBOa`=~Xw*ok*Q`>AaTLB+L%UWAM4FTTcWi1g^% z#eE3Kkuz${+4}57vw4hMvUvngW6gqA=6xoPe4yiN*4~tPp8?G?5%Y}OW%E%hHxH$W zW0r7;n$Hm}BrUiU^KoB0kuW**_OXb9bs9y`26f_>l~R_e;-o6%?{ggeWI25%q+YFy zEYmwcjE}*ZRdS}wcKVMmkrTO)QSFpbZNhQ%6+M><%s4b@$!iZ%Ro=l`Bm>q89zO_@ z`OVcgQ#p?vFmt$Z1IWOVIFJ5)ayD(0CMX@my|zy_5JPC)J%GS{&`8cK z`hD)Wddl=`nxd4_zI~&zO$CTa=vrD=J3=BEcWawxOH&KXtt}IYA+DDye7>cF71h23 z`XLK01wL6H6Q{@|CPMBfQsKUzgrr;oUoAYaSJ-4CHsD0yy+bC|Ap--Xf9oUcjDi%(_akWTbG?y?SM%?xchJNpIJ zdUb|t7{%9xYjg%7vf-Oi?Zl@u7ZA#O^xQ8sp!X2C1l((CAN~r|9Hh#ESZnsr(w2@e zxdbFMqfC}od<2Q=$X-SCY^|S^Fuk6ehpY|tlX;k#P<|}pAH#I1P7!iQ?HWUpwGxoH zS6ZL&et$lLZ(n_%W0d;Yq9Y2GiL#WN!JwZxPK z^_M|Ao;XiOt35KzJnP$5;%`=e@cz1Dv|iQ<`B%YMUd~KzEQ;aiz?Fx!BDs3knvaQh zKDkEnB#PC>rlH$%Z|ls)GF8LX%{$Yps;*@{H8J-ttVWmzJ9<^lW=-j4(Kzz%%JhxD z0`r01X`kuR^{~0$@>@pH58Aoc9Y$lcUi-|emOm$(Vr?O)_UXKS3u*+{stGR~6f<@S zPOMjsFBUz9^TiMK5wE7#-m6O^0R{add($(oR(}xE-hj}auC@#+Y)B2vQrSsLq)44! zm}U{6b)L#}i}p1RnFvwBJB0+6knvguwNFC(R@}=B=us>YX!W@X+3{L#SI*ktU^aci(|2^_0(QA&@iw znw~y$UN!@^+<&4xN@o2zfV~E3cN3HRG32E%a zgEow-t|8s^Cl$E89`r&qf!m-(IF3^Fza+`LhAC1nokP9Bs%;EOK{ASz7tq`Oe#&v2 z{L?4T|DV*t8Kf=Sn_qVLsdgqK7p^fj9OddG1W*$A?*0p@_tss*+A8wG^(b=6s%-%W8#Z7loV@eC$wL%G6)QxM&y$pDm@AhZe|OhBC|wa!rW? zoL}PmI$I6I=QE!8AfY!j)1IQA*bQAr%1=KFnL!x$p-=e2haoJ*`v6Ts^wRj90o$V; z`u8~<5Eh{SStKdUFy1*i$`Au0#A-y`_as})yHlU}A9La_`j@!b+^c%nYd-2xbuaiw z>*Y9CO$vp1PP2JJUdiJ){9r$#`)(wxWCWg@XIxdiREIfUK1IFeLox{UPtiGE#4OLt zLEneTiJ(9`{yR3_A6@%EZO4aH#fff&qpylt9gTSx(x&`wCl`b)nJ>@kM39u1W*+H% z&$xJ#_%PN<@{>yN)b?mMudxv=rF+ItQ^oxpUSld-U^Tz!qE9{c9;E8Xl4IVl9M2`! zJ~he)v;Y&){lIyz;18H#gzzSX^yW|-pu8889{{HtIE{*|c$iFeog~+V>U`A&>I8}P zyq$VmOsasj92nd9r)DnDE>Mmf0Z!#}t~|^>e-}z&xK^Qn?73||;NI)c?3Mtc!k^W0 zKP1%uk{R^9R-EK3X6fgM+6aN34B%wIdz2g#3QnOrmrpf;iM2{W6K`9VI^U}N2Jz2e zQ)t8rNdB~~0W|`=ae7L=OV=v#?J1VS38xP)iu8!Ov8Z<3_JUkD20rz8&YfY6rjfo(KlNoq8P z3zVpOfj-E+<7__;U`wVOIav7eW)PeV8=lEsKxpr3LWM`C*Cu^o%SVBKAs=VxpOYQD zz{Oe|V{W}OZ~LHTuN`-_(pk&r8;XarUGCFOSQ*MaM?SbnrRU$AKtrVwokN53%|Ul~ z_9u2a&16xmJ=pxn(}BjGi7Ne{%052W;j{;Spc0*^8l=Va>@@$ zwYDi$+;V(6_L*Yx(sM1Sz&I0ZP+2LsgG&FE!sbgh3I z91NS!TFODnpOy2GC|}F*#KjV`?o{SJpDtmbf1yht*`q|haEPv3aX|Wvb7Ik;qec|u zPE_>dAR+4&)!Lw3JALnpFe%`7eh1mEk!;xQIemi7uRw4gMm38~(HH7|(GLCPp;PCB z87`QOg_8o|=XYTvVzO~EKH1nl`!}q?3(Oj1N$h?f1$?%(<50>$_X?J@MQtQU!eTR#6Rhr>OYBTJMFERIwO38;T>=ukPqb#T#2n$h zv%xDLsTR66f0D^H^_=JnGfj3skpjaM6l7fc8v>RD9ZRWTLtNS@eA>5rML^0VWFETl zws)`$V*~C!JMfE5DIL#r{Ns=LB+0p-WW6AP&2~9FY#8F@06dsQ|GULp8apy+#(y34 z#S}OThRl{Inp+S;F1+s`>ctIxEiSk7KOR@{IV;Jqu-#3!y{!o(zEtj*Zy*BvUy!HH z9aFW%jVd;qB&W|cYaEU+*R1YfF6G;Y$?&JSzKb7;Vo8cOobm2aUUWkOY%kW&04-a| zeb!b8i>b9vXh$}o=Dg>nZHHzEU=wuaqCo=NUu+V7?9(I?;#{7t!#KytYw@{5%i1Vi zZ&{gGud*aUkY#hR{gwB-C)viqNZD>x?SMr0VD?;qV^`n(VBU5;n*`nl!54%`1Pfr( z3^Zd(`pWdKe$T3*am)99<4p)sIEG$50*5EOltdfvhRQT}B$OH8r~@WX4PX zls(fYjxB9cVB9I7RMt>*--{DaoR(WNr|x-7zrX~nxh$EB#8;g%k#0xMEM|MDY*uA$ z*6`}U0Nnj^NV|Q!ueNuE@Wn-OQk&>2E_PSJ%RBV>Y0~>j&3g&DfICN6IEemxV4m60=TyI33jD1__ut>Y$uXBYDsdG*zbb`n-Fgq+RQtCYAh1??g=e7A6!M zz9yqzA|~ZMK{<%Yi4^WTsb=l@lG7TIcz)*SpO=c@vUdDy6D>1Wdg|H{$o!-|eDbU>M5LcZf7xlOG2aY@7EEev2uR zsr=T5RupNhJf0IM7Z+)2or#T!fivnCakB2z=iQ>yRn(O)KW&t>pRGzK+Q9x&3PKeH>giVT5Avo_+G z0p)!}P({4~@e<1aQdI!m-~nNfmaqYzLQ*;4Iz_Y5l5}URiTEFSp7TG50zHWQH|q*; zUcUA|*^X;HaA>*(VEJcLXG#U3XqVgn(6jEuL19F}3R{p(*6F{p$C7sYhb;ZgcJ#3D z10sVX#IphC3b?)n`O3@o&FHMIURa}?8JU;0)LWX^Z0-bg*@o@TNHETKZO4#0AI18f za2?!)zgs1&N+;OJkpU&)QLt|B4*&SN**Ll;{hH7olm|mSfd!2Qj$0MeREi|HGHQ=@ z1nHdFhR+~p+5+U87&F=9^sbAWfjN8kPZFr2T7>?zB^r1}aKO#<#&Gl*(p^u8NhX>z z$+6P(zTouBtIfVgRq&+!_^?e`iyLEY!-(O==Ql-zHDAX|!m%lYd=ht}AuTv-ny_O% zr)g=~*<1(t$*PRK%itY|;M)SsI~-=yR_wBi`VLDls&0m3YWtjgjmw7FXTU}a`#Lpj z&omnv-=agbOjogWC(67(y^G!wENW(8jv=JoiwfR}tq<*~zK9~|Cw9P;yoOEp-?gDOt->bP<*t% z>ME!)RkW#yK4FrBIh4Jn*gQwXfzwN@(s`4OU*$H39!BkRgw>AtBne-EGV+UsiiAQ6 zbO<3uZo=o0xZAZub9~(FXQe-Fo8`nxTazz2xD=BC=SDX1I}F~WBQrCk`ooHAn*mkK zqaCydsOr-yqU2cDRm24K2b;LjoR2=$sFco3bbdPG^U5?LO9?d}7B4ewj@HWD+bQ}t zSJ(vu!eF=-nY%@IUwJnX9n0%?M&{9 z|Epdw{bM{9S{J2!jCC;FPwX9$Jwb;r`!!_6e?4FVF-Xq**FNV3UV$)rj}sIYEse7m zxlx8#bVbZY`t2NLu*&O!y|T;XO&z#T-p7}QTssVQlij zU9)cTD0bhGr}7}d82E(gX{JJFH$qieszBl#So{Ed(2nI3p!+$=W5k;zt9 zhZ3HiL5Xazqs@G#eHoTLJ~)&b$Z>$})P<~)o14qZ>s!QhOhwx{&ElSlv_RLW<&$Tr zd@XwisoOf`dp*cX@kj0Xzod%U?$pVa?fCEK9Kg1MuSRnm8T)7u{d(Y)fg&SKx)KL0 zI@T?PEE+KHGLl|H_X^U`kH!qIu`nM%IcXBy#qxfAxPD2bKYqx=6WTGB9>HOnUYhq& zvq?Ah$9LUnO08{Nat3a^cj+@*ZWv~D;?^&AwNBYL&IU*aqhBi7p|aA8V0WsFSeTD= ztNHmmZj|blAB%D!9G?;GmCjAlvRsk8i_lpEiq=vnNHxr$daK!h{e!0L)!Hw&5M4~M zqwAv)2h(27e}AxdxcLM_{C}g=Q$?XUArAw`k&fb%Em^J^dFXv{I(orj^;k=lm7ggk z9+%+EFLSw1whVm}-cjSo75=nDzSoYVb)>TM8hh-(6BWBih(7mOd`^)Joj)gDxkA9` z66r0|nJgvYrv{mmAG)B`1!eCZ^#HueBVoYvl^2`(vg%C zz8Apm7E%eJBoJRqI%MF+f2<1qYw6+v)KY7phQ1s($VO1k_Yh3$8PqQStR(+1s+N)V z27W~SJu}gSlc_bgD(xQ@MlChOA>&d?V4W=XI;E^nE-4TJk)tqYBnwzzY?CwN_2BE|JxE zG8%45@a{pS)b6|7^jrT$V%ienN%3%Ak?QWmg+rKS7V!#c3-l$95WGAPs&K&?1Zq7rNn4>aRYp{!WIeL7~WEe7Jgjz!z?H83AsRH z_&p*_h7d-KuE>B(xujpaM8%1LNz`vf#B(%}CtWJUFwby-V*y%coLJS|tc2=n0ep4& z+kM;sr8@u4mnjlH^B;C9zV8#l^9U!fqMTzrdsqXW!%tZWDIB*DgCv9FH*_20)F1H_0jjG2SmsAh#dSAc62<@+-YXI5ehuO-6~ir7JGr5 zz8}7LN4u`jNG0^dDz$fp#V5FCd*%mNDhu}!hftp1@&@}hiFZhAwjp_)ScZI#7_yJs z;Lh0exd46b6~HyH&uuE4L&{>2fPLyzux;~%x;q`jH=)7z%1B83-hiqGTp?n2XI=mpW&Emul#Q$4%;S4@9 z?$H#vo6mDXQLdKZiHR|CProlT&s(ZNRMapS(08PZvUPkU;%)aAgqz1(4Q96@p;nn3 zrXpcA=RWE8bLe%mw8|*zV=q?SNjpvc&n@*S3V%T7kO>c`cGx5=8B0JN|62-fB-_o1 ztiiTWX<>j3NPl8tc%vl_QsV5!WRqDl`=epuRicbBXe zdkLHFmm>yNw}b#pK9vLVZY*OqikdGuGfIMyOyG1V^3SS zxO6J&>~(IxuhRcFPWx@+qKVZ2{d0xDsL!IC9oTE!@u-k_PnftJ6{fS;mw(qeh*U;e zUmVuLG%(37WbTpM=aZHUxSQ<2=k240YX5?>xzMr`I+(`RI0a-X;(X=W^X|52lrk z%Q03avV*{F>;g0DQ!)c{q2MWN9jjc(v4F~)l@Wt1XgH&cR(Got2CSC3!e@|pXlddF zoj-ALy}~kP>-9s`P}w%CN!Hox_?Uhn?pp=9V291_CfehKNYT-d5h5;g1(_+F3#_me zCw;*me=jYf3m`BdR_iD9Ufe1R(MfdCg%eNYEb#OrgK)Zw>(Qg(8cCa#j@eRpv66iKyb&GF`2SZslvv9PAm4e!dkA5ELx2mo0~QexoAexagdk-I0Nm4A<#da0MNj zz$rtYUnkE%9u>HcNlbc3jo*V|AhJZAD&a%v%m{J0*>mk_op0?fp@EnJHk2^-3bppt z3>>#d3{ZDbrG$@Cf#e&@`ouPF_u|yAK5!1MbBD+RY9#i2=4b!W7n3(dex*zRA>QLX zvMvlubTSCc_Uo58Zst4aQ#dCl@1uSwl@Kd)5?wc9CXbyl@2wPSSK02>jy#cW=DEI} z-22%eC!H4{w$NWk-)z*&g+97KD_Ys|fdUH}y&(#9^4bB+y=93Gy|Mh$2XXx;Td*&G zi0iegOZ0ssvFVFdt+FxX-9l#PJal}eLKi200&N<_XQWT@k@XMKjrKxIE z&jEBAM}unp;bMTxWjH!&{=dg@`q|^aWihwvpD2KP*e6oDu{c5Tb>I^_nvZdAi0S!{ z&o?rOR%9)sUwI;SZEem0~8JuyFq?2ev>+I$%>^NA*sKq$9b;#`okWMMcEY+W1rqF~CGxkNAv#fZ7l}jM1 z*1BTfH;|b-$8mi&-mcd|uv>1ZpHlwM4RDI4!7Xd~cO<4_g$|Lbu|DM1aw4fY#2kQU z3mv5)gw)mM9aW~L|&TO-ZRsGmaQ5i7)efavOxZ9E0CMoLRg1T z^B(B2FudL5PzUj2Arnpr0zspf!1{=0noeX^LX{!(`vA_xzfR_Y zzoZ%~zO*f%(jl5-^tMoudLNgHC%D!7mBPTbV+Iy2P}>>t!d>$-KO|63BGg$VV59{# ziN9Ma~Ofg+t5LkXtxty-IJM_jZ9FDJqpzm8Xn!vGnQQF<+ z0nCy85lwCi)Fq5{ht$6qtUu&GmmjKSE#xz}&MYIA$jcJp9@!$C!*pf&Y@0$RsDi)n z0RNZy7k;Fk+pD!fb#uAqjpq)Ne_z9|SkW<@SMV3wHKNYg+Oo5y(59O)T0r8`bF4tB zO>WYcpEX_M%?J}qu3*lS9m&67TsmvALLb=k9LN0@U!hPb()=X(%>w@rNgk_B zI+BvoMLp*t?ukZ{fc)zH!y1QJ&Q<#(4%6))QZ?Sap_!Fw2CwR8EY;fgoyiQ|47$FXT9tF`== z2Vi)v))nH=BNJw|dp_%!TdG-!ynsMVYNN{tQzTUALT3%XkW;J|F#qKTcPLK1;C_DC zp&O#JTGd5x3j;#!$T&=8C$2vb-NO8#nf_{#`A<7J@3o?fFk<(O#`c8+=MnH}TS-H- zbaF^ZWBy%-vsYNOoRQk{iJ@$Inmt7?hHmWG+1!HZNKpJqKJlttJekcL5yVP+*(0EW zTmJ>n41Z{Q!l)R~PWUKh%E;v=SWk*L@U5#@cTGHJ?Iw^VF=9b1-^>~1CiQI8$0?ie zJxpH2Rn%943VbkdJPHQl_EwR8`gR&a{zRdqLt~usFPwyEQ5Frn)RrLBRYhpb}DEJbEa~q?& zf|KH2o@@b{>z(uYty`o+9R&qIQw^e>8t8PAw9g0(1e%mmsPTc`wK?sfEkwRE@kVj=&2uSho+k5y1#(5!{MGNg-cKL zr=}iT26yQa7~y2b0}WZwF413vb3v(X7s5@h8nQ#A-rH7T&poeO`bRcgYI#_SWSag# ztRuw?JqI+HVe?FLrgaRw_t{%^xYa6$V&mQNO;BIbx2D{ORvU@N{$G9w-PceKZsUva z-NM8sMfE&w-;V4hmC>Ub!=O}}XkY&zMKgX#MCvNrr1%ztbGdqem6)l{CbE zOoE3~7#eo!Ma*Ha%$VYyZRX9jcV7o`@9oZ^#HN8cwm_>3d97IRLiRW*ZXwG$kkn>~ zh>t!+6x&>+_x0^?-)^pB#_!-C8f0gUP8EAQovmB3A>T&a-OT-B)^SX*H)&>Ft4?>c zsDSH7U2_`JKMl4q!!@a46zQ8!Y@eo*gfNZ&ai+Dk1Tx-6RI7PYpV>V=U^5)x(^{d} zNCdrvVb~+`tqe>*OPm^_$u4?@{o{i8ldZUGW-i~cU>fgRmUDc-B^8`vF60p=WS5yg zMc`iU33I{O%{67da(#6a5VcwGW40I?&#Y^*G#@Myk&?UFBf@Z9o4_{m@bM^ zle#+IIA3%R9h&l481Wuxg_ludf=`cJr(Lo13U1MT=?xjcX&kV;;2@Ha{u|_^MoH0M zZ?WT@YOKe^G>6g~gnumGNRs+S!~z-v-0#Gk6!J#KNDQ09n%EC?{+eVL%=VO;*|3z({Eml~R3W>6#K) zeVRp0Lwl*lj>|zKstDTyj$M4YHRvxeKl_Uu-4ase_r$GNr2`W$PZ8A&U(v!~VA0jQ z7CuneF9vi)t4;;#IX=7b@f>?>!=BI;lKp4!t4Wz#@r>>yfDKrb7QHY=y!2A*VIyAc z;UfLcc2#$&elVDLowSyp&DQ_>82VXWyG7&v*P=r$ecHV#c<7_ z*)Tb7J_w|0>UezRXj;x&m}dmx_-WcVw$^BX%(XP|hZ$psmpH?hs_NEa1fW*cM6$p; zmZG3a>Ko>7 zTAC!dPGYZdPynL z8qyboI%zB2xB#cV_x;@#EIU-`YI>z2z z3fI#b&*Yqf&eBpxT6U?&lrHMR=9|EV8MS}56?J2}3jbVd08U>$#c8mW88($uLs7TZ zf1y3D5Csefl@FFB79ZBHL=7b9auO)gb|wFp7wkF`J%jN`!Bq6?kPJiD{h&R_dq04E zHh&{>HF1Umf`1;l`Naowz=W}>YHVE9Le@qw0FJ$S`Del}#Y%}Ud zcIQ~%@H9?qDidv5#mo>V690(6>W_0OR>msc`0^aHG%Ws^CwMRP@||aMEPCv}*-J1! zHhyy4fp*SJ!6aa+F!Vy23zxIECIxF#QD=_?F!mLJupm%3x|@4`oDdCs>XrYPiNo z@d~$;-m|%GnHz@Luyu^e%R+fH3s>irlQLr9{tXgozw3#AqMo@VMacQC@N?U(*4KZi*ZMkabPZHO3TZD zVvjpz5&e@3%(4a;^S>3n_Av;T1sdxE&vNul(MoquCpV^Sj0L@Q6~vl>O?!VJe8UiK z!yoON$L&f+@TniR<}Flos@dFp7{$PM>qp0-9?vO?wAk`kbg~kBbBBtl02&sX)~&Vm z_^4F>gf$%RFVvdkSk4#w)x@4akrpE(h#w=PfGIP*L6-<5@ok#`QAWJ{JX^N;zBn3t zYcaORK^z&TB!rCfk(QQR{pK(!=+@*-m0i8i}!?q5!fREXgoZnHz1l&ZK_bL%3 z&Mk2lq3&2SRBa1A@)X4fax9Lq!PV>JqoU)A;kH6)|2UMq^GOTOF*tLepy5H;~ zLhkV-ZHjXJ(ksoR8AP@b9h8}Ud5s>8Vi~FMDmBS6!H=B(afZXzU}iqTg4cJ0)vL9D zD7A58m5Jy27IKxv0KT&v#d49aNAD82F*&>Litd@nljiVgo^^z5qvNW#+vvKXx&h)L zz6BXqPO+~QCzX3D%?0-%8&2@Qn(UB^=*}Hbf|myVk;c?WlB^SQx>6M0X2iOOm(|he zfADr>E$)yYAY^MnxXvLxWmGXjOp?jB1uUwhQdGhFaYH>1MJp_0tUKaynRg072R!n> z4wydkB7QC0m31W!m7&ea{qGc6v9b6a5xD~|&RT=T@s?@frYtNcY=2R7Xq{e~JDi>g zB#*A%=%^IzDF<%dbg_?j3&H7esObY)ca{Ef5J_wr^#x>~OFI3HW7~*MyxPHGuog&t zi|odFFm?#Fl5C9sQ8cy=--*6U>4@a{82c@Tb97Wan)dgeJ#znFaewR&T@@bJpm;`~ zCcm^S_krEbOF9Gp(HV{-UKo6mW5kY-(AEIQUK-K!0Qe?#*+(Ss zV>nZR%F1+;<>6Cc%iSE*L{rqrK|Mk2Nyz;TM6E+_`TGaCClQwxDtgh_eCi2R_T~jb zrN8M9^+wQ79=ryRtbvl^X((^i2GG>eDv`I1V&5u`nD{0x(>_1%+=FY7-#R=ZL&)*WN*5oA6#`P~ze`2FS+LM|DXF=ns2Tw`pUF8EW@c zj$>!`f?eSBIqB}2UG!yJ9OqMx$W$ZC6dlhL)>LZ?Ho;C8(dl00oEZBCb7?rP8*Y^! zHrz;DVQJ)CSfCC_XYgFIp%ZQtD(AvZ@dQoKlx?!p+97aL7WP4ua)k{%334l7UD5h% z@X-G+yDyYg+T39IaM;cKUm;xoAM9EC8F2<7GRr1hxe$)GWFw9TMd30pK-(YTn{u*0 z&Wd(%WKE&s&a0^cJvvx>VJuCpzIMT@xE$NAr@S_^Z+T zQ%#q7t{dXVOA?2GF0paajxju8#$%8>=bN%e*_G_j#+vQuSuF>Vrqu_%hgP5=C;PYX zes8{4Q_GMM9OqPbxkrCL19m~koVFv(X)rHv*YbLLSiea_bAJe(9zapys_&?e`q8MD z!=%V4*mqj{;^V^;+9^|3bC?(_(_Z8%>5wa*(DF9}m?XCIu<;J7PUl~`yBMdlw6^Ut z&0#;d0C0b!l8tRT^q;kOX(<%W4*>)u`BBYQ=2K%2bauh3C4vT|C zRrQ=x{aiQtoto{Y*RG*of;nP*?BRJpQ9NfQ`roKOe3i-0h6}EZyEZ{%nW!T~_3C@b zk27E$=0ed3XYMKL&^}SWt~?C6chAtKzi=`GB_+^a7km}z6gE0QiR7O-d>8exifKK% zmzT7H++@Fv&)Lq8b?}hp@)x*g!R8x^6oonY7U6D(=Og<4__aE=i`40gj=bZy+Kj|X z-4*M}dl;)EdHT=oS1htGc{xK1yjc9yvF?CiK0l};t6v9S`fC3tBaL>%)dDM zsNng(*jcI#&du<=$+#6*lw-qdmxa8$6 z->lo{jN3tZvhw09CZjkY698nrcP6+{8T0HrHx+0Z_c1h*w_LNX0XJJk$#1IVdg{~U zlpEZC3@wim?r0K3#}^?~CU1RoSKx`M;woclzwWk{lMY~Bpc`4&hkP#Jj^#JuhkeVz zeX?7!Lig?(aMOIVdMIv%YiKPqx!K)-BZ0UY!>RQJtjP|z$ha);Y>SN}#QrhBTCuT@3LPeN(Y{iW)kzPQ_t zDiA=rXPLCK1v7e{8M7uv-VbD^gg`09QHKc>KO8;NXHwj+=a3RFYoz9>L(p>O6~z|- zl~X0_^50qb{DyWPTdLa1u9;#9`kw7nAKEOtZQ$o9GkaV?$q_f$v?z2BQ8FdoJ654y z`si*_qDH|(Z;pR`l&~r4J3b|jsRZ6;Y=D2rU+7$B8?lK^(X((=YV>8TRDCxCgc2GI z1V}_cVjW}0G-;b=>MaS~Q*%+f=NIhs2NzFAz^6{QsP^^$VqL#$;E>^EI#4E&p#M2| z>{k==*Ph`gZuKC$et;ZP6$uUOOs!il-e3jSB>CdUoC-0woCds>it+&@7C7E7i>hD-9VZVe#9{(e(3jK5vzUP5%d^!9wG5Org61ST|U%g zb40A=bx%7TE=APQ_eBH|l6Wv${gw@18-jG-BM8foexq0TZ6aFn1mdQfxjRy% z2`uJYD^Seruq&-bY7XXHz%`vI>Bky^QvLfNH=sd(*e=mys#riSI25!zv05*P-~4O1 zRhXUolss48zFam~aNeu!%tVD5FuvJ0Y~a6}%f~XAC+DL1)A<5M=1D@+ZXM8`iom*I ziog%`&7Ly21$fGbpo(uN_3K{`Ej4U2A4XI!UT}eZZM9ZU^%X^MZRO5S?(G{k;cuLL-`u^%c&+`1_K+ zLY1zz>bBw_8vZKe$!~+w$ye#g@GJtVyibb#-z@>>jtIU;y;sq%mMZD!B(g$InJO}f z$AQ6m&j|X{J5(jU@-1I{4`pM=*{;3)kfAESL+)3mZ*aaa=(ug~F2w5MA^lNy@0?4} z5<{Q#8mL8tqgZKkvbb)RR}?QMH}+{*qH*(S*L|yG|{^f+`3HHj;SYoAgJwW#?(u=H8JY`xhv6i}LSA=SW zNw!qD$X8FCT~Gq;pjCP?O?K#Cxq7J6zAR)xSSOretvh>l8X7Zp2X?y&g=rC8cy1+q z>A}zESxxUAhu7SMPMzY%B!VQtw+G`sBTZYtqYohz3N=^5>jKC$S2g%|AiZ#6nGE+6%8pk2w+FFvF3cf?$V zfjX~8PgDH(`V_e-?dyL{1kF^So#!+)(JIX6YyO5tVgY zRogbnhHU^&XhZSN_X}3xbD;Y#6UUImqY2*{+ZCecz4;4k5X$Z!w9PQ}3c`jLah~ZM z_pdjJ)?H1zQHhi%&lJ}hJCb(rk@oI&&B?|=M%M?(ZlqER8R;!Da+fhv>Qvy|Q#!;> z-Dv5E;U7(Qtk%K}HF0&_3fL-dJ0kNtKp2qVi7}-xfFg?HHcY8W<3!o-JPz^Y`ZmH* zZvtt9RKUnWZrmk$Xjq?$V~4w0swN_s*f_#rxt~W2*rG)C1FaPv}^Zr zEj&Of(E-`)C$1INC*TxxOG?^QHiv%{4epY&gKXY~9LV({ROHL+I1MRx0sozmd#<3BM0qj9Js?m^>$ z)t@=<_cK{^0MFvC^5Quz23CVi)UQkONjEWL61+wc1dKhRq(g#$5Cwc|R zIHTonmO&A=S7qVxxQ zsys#Q*!o`(kch^s^EG-)=hj&=4L7nq8U*F!rDmh#+QgaSI3KvRZb3O{={w4|%SPBWjzPLvg!x>l7FN)pyAc4ywYMZE19AnqxTquv{ zoZzF(2)lYE9SC`W#Oxl5I;klZ_(W)xA+$BiM+y(}-+@rf$sH4`aSTI0S^;SkL;?d_ zhU$HUtV?^M)|%&`%dbH1*g3ai%J2{UF42%wb=7_>Z~352-)*VItFSG(Z0i&_gGb@8h_anpb|M2#Px0WI^JO zd_HGEn`)MJWVgJCcEkL}KK=OEkLB}$p61Vj0$1tTM`Ok#MJG0}6yvtgDmTBAJ6W7b zsc7;LM>kEBs-_!WQxzHaUMwWaHyTJgWsa>m*G^(U4L$_(_}?Gk+O9AI(M$h9pyVC+ zmIX(^48c(`k}?I^xUka8!d-)wQAka z{h3{a9V$0;!U|bz0SD$bEC&z*m4$598^dX^xgw=w(s=)(zkc}djzR}3MB=B=RSr|+0N?_9ROSZ2t%Dl z;(20NN6>t7`^94kKhuGKdUptieg+2gHeq@{^Ti#&xznYX(CeMf{9XdAJDJC>8%*RZ zPT$~IO$r?cy|iO%OhENK$9IFtG{%uEKCs`aV-IP=DUT&1Qxnp{~S_=UMU?m*Cv=H>z$ ze0+9`_8MX>);XuuxrrHWwc$=H1~DwLuGzsE&i)wqRfplskXIaCK^$HvXw@HjCv1Cb z(6laK3i{B|BJ9aC?xoaGZta2IFfPD)r@%z_dX3H=bp3jDHJR6VZ()yPtuAqDI9AGZ zBr1|Tn?>suy!q~$nvT5=Lka#%vR~%4WKJ z)+!b`XP?!K{wV)iYpd3d1=bH+yo@Ibz+2#9_Oh6$bkL8X_;e~#HGxZx0At65+F$Dk zQa25s%Bs6vs6Jp>5F{$>mrMfP8z(-PexyoKZGr<~rO#M7Ms7%R#tzYB2{45^dc*`& zSc%6LLm8_moP?WlraU|08-2T3YLznpIzD!o)_hvnn+D``S7OoUfPl%wv4eZ6jat&y zJZrCMjc3!7agR_xrMKgSB5y6ufz=4rYVk!X{flh#7}o+V-Bm7&Nm7Jp;RVza1f3!K zmoydC0C2)=w(PT!|S8@ukup;MAb|Al(QWtn10Z zp~1JFL8N_T)oE8doHdG3Y08Un|Jmj;mj2YU1>9Sn2`rjPc?>e>Y{wkcU9Cv_F5uSx zivrvm$8U?#uq21xU5GQRl|W~n^8Gpr=#LK4o&L&BV&nd z{(WqAeex`* zJ|uCf;%rc~U~7%WqHM_Y*-z|9RwGt)mj#dnCzZ-o0}I$aujRUCtNdYOyP$MeP9G&* zaV4M|KC|49yf&#aI{JvYfLMTA*c;&vG8y`FpDd>@33Wpl*De0aay$qG28rfYA9Am* zOQeGsc|K5a{pNY@dOoyrUYO`M&bH6uno_A0=9qtxN!{kF*I}cR0ye_|{Qd~bQO~%v z(7jVyK|!O4#LG~jOMnF)5@`S71%@qqKq&;*eT81UhBnEdDqARRgW|^l(T_u@rwD?d zG}wP$&Cc@Cv_D?wuT5}Yu1Br?eTCq6r8s}_ZO+p;lj|~60`NaU0o9-q$K&WsMJ9h#Po-@DN zC=`$FhQwpfMPrMB<-cMbKSm8zH(|&4iWKKM_zQMqvRH_?`e4(8{+*j|M(vO_dojW| ze|Ia24iV+>Jt!5f+XIcQWs1N+3T9y3*J6C|BFJ3_I|{1M%iu>=4)EdekEo1DnT4Lx zE(3?_Eyp(5#OBz>sadxaoawK$XIg;&ep9z-ZDMv#&H~2%WhNWHG9sSngHp)qr(4;| z`s|DNHSvkBVEbveEAm-&kjt zVb161FWWEuZgp{CoChHCE?gIX4scvXLic0c%uU(M-?TjbrR=`I_St=3lw?ugz#W*P zt<6Ld6x~62H=wG#=zZQ4D)Xag<|DhquWsQ$GWSG&7Kn{5*uHSJdEXCYollF%XJ0ew z$SLr##y8Jt8B4N=izaESB&G*tfHe3sT8ojEk1y%>~kIF>x z!J=Rer4NkqIUoEq%^!2!6`EMBb^zs_qz$+YO>rg}240;YY=W@-uiU_dtT30@Lq!2%1?H{+LP&&_$o%gJ}I%m?8vn?QL zbKc8#^ywvHFP&Q;4LLP`azA}vAm`yxV3OyzV_Nbf(3Muvxlt`zu5Vlf@dG9Rf%vgp z)C<&(m`^)J8LMPq3hreu3qwWCA^G9^HQhR_)!24eEIu|*%gps`deb~d^V$dB{z|x5es4pJnctP6=2De6hCa=f*jfuyuQgN_|Apo_EiLUGH zrI628>jyW*7C=~q6Sw>&bC>nvfHb$ZaGSkNdupN|p^kvI+dlYEsHvm!;9N0blhI$t zHrKv+@`Lu6FWv`PgPnbA#J6)ffNA|K1h?8O#59f^=IN$B^a){t0cWb7wA--X7sSY( z)v&`Y%6YEGpiz9eXL+&t1!SP68TH>o(dh1PA-HmK8bD9RTSC+=ty0|V^5Md-Y|OM9d|ikGgwxL@%&W+Ho6SrOS$$NB}HL?04l=RXn# z*i9EpNSiKqi6Umnk43e{mX3)r6L~Jy{py;-rR^mjSLnv#YUd;q@Z)N|LS1bilq`z# z#*On)Q~WzcVwWkOU3cryza82!IOPr=F8mH>u1G~(C$a6|v$BnNIVWSR`in}J4cuc) ze`jx^xXEA;7?q%a=ms)foKCq`UvPHBwYvq|`i$G6f)X$+7}FOBB2uF*<96$1_8Dfn z{0ul9mvWvM7y>4{Vfi?>T)ZIWl}l|Gpi&VoA2ZdPpcfx4STo#mp0oq$|Lq3XF|)Lr+5wMD3Ka-tR`Ko>}-iz9}yji+dy!Pc0n zs{tLQbK9oSH*Q<>>hDEY+4jwU#lwl`SD5zbM@Pqp&Od6C-F>K(b#!$on%f8!`Doiq zLe84|SZe$la#xYNuS?QMBQS~dI81c;1*?fCI#^_6@A zEHMx~lbn)3K!u-@kA5{RN(-V`HDDCk1MHN1%yijeUN1$vCpze(S%ldF>(+ugw@@2; zh2<68^^%T);dN#E9~Eq;8m62hZ*-~W76u*Vr-HTWfNQoG@@mVuAoU}|0*2AWl$;9j z;9VqW06s3llawzL7uenSTGH9}3orYds52)QHui2FW}Z!3Bs&<-`2v?8iqr33&J3n7 zp&K_TSNn<|eojnQoFm4tjIs97_XCma`s2ysjuwwaWg_(*`ywva_$=& zj=y!b-P@Jc*TjLeC^AfLye0#r|jrox%<31l<+*@1Ux|FdDta4kXV}u8Ijun;^mxc72;PL zr^(&Zw5x${*p-_@K_6?RA=)PuzKtM`B>VkRJiHE9sAvXscjA%Pq2BA2nwDYX@<4s) z@C1A*tK*^W;}$X0x3n(!9zcjQ)!!Gxi>8XpE>y^pMtm%L?Rz!)LGLh`jV|(yT?7ke zw#B^8s$boP#D798iK{6iTyo&BSf*)n;X+2U=?&yZBk7OJRa zh$=)IKD!55)Qr*~z&$LH;CoaZ=jDl8<)uE~zn%FiLqpPPAptk%`6ySl>Ym7rxd@sK* zptyf%*6RDvJTV5hBKf-%i>TkzRjFWR7=%>k7`k$%`vO}GJ*CEX$48`>(r0Ug<_9*+(9P0#uw){S~K z!MzOVQcCPraYjoH*CTazORgUMp&!-SKX#E2_ zIBOVpP3p}Q$@yMH=c1v|$ZCV8&EVFeUxomLObDM?p#X{XB|kDnV|eF3V&d?p747KF zQP`wGu{Vt-`iylv0s<)F>&$O`{y}px=R9kUcar*K52oV+{`G$(RDKqR_lj~r<3JI3 zgdwp(en#gi@E&;ok3#d|7?XUPt&Ru%bq#Si_gUo4NDh+@8MU9?cLLhH5vyflb0G9L z3V6riR|BHOE6#AagGl=upv%eL*|f6MO82?GLA!zb<($^c0|bMIYD)TLZ^e>9OCFMo+Nw@=qI z>sbOxJYgf3l%yWh&g^@(RqYFCFfudM9oYMu%r@eRx`+^$j=6^;o4K|OLeFYIi``K* zmmqpnq=;Xk@vpiELM`bh(zuglqB-2_JuRpg2`#8`jbieoDCE8YKRQ|u7xGatnYJI7 ze*XV`_OAUJ&B^Gevo!T|&^G=;jJw-e?b@fJ>SMnOH7xU)FZlzuQVlfbv;`k^h2RxJ z>9Nv>PB07xva13*o{}0D69`M`!%3X@bPhJoZ@oznH-)?jBRE zzjAOco}Y=?l!6`Guhe2O7aayu%RO9>4{Q-tNbuOtgP(uq2OOU*{uiTj{SD-%Gi;pM z{HVbAcj=(bb>!xCy2mXQlkSCGj(O5v@aQ51Iz$K=nh5-aY96ALkOIk# zNI5qr#v*(Rp^Z(l*rkkhUM56h!2v5} z^86!F_h-~fv9lQ_&G%nnJU`$Czl)G0V%G0rG3$3XcfvrOeJNpT{)o8b7!bI?EhsON zPTxUXT!!^n{?QP(vr8YW`{pWhe*mOSExKz^#u#ZTP~{oUwMSuOOX}8{y-^O&wZD?` zbobQ>={Mw^c})5sJbnhLvz#q%{YcRS{(>KjcJE%-e@|o-6V#v+8XFqFT)HnMD4yQ{ z|Ca>R|NV?p`Ihg9s#I|Ri^&&I=;@61QQ_F<`y&0&I&1L4%YWAgNmN0JqJ)(c{_IM6 z`>fxI9abLKBpungv+TO(uM4Fc{U@|SmDL6~a1WftH@!V}XhBoqp#|ly>B%#sYTIe8 zS|R$R-$57{Y?CH{IknaweZw$^bn6VJeo8yuSa}SD#)q;);W8z`V z3O9zhF8+T?gXKS;X@vmH96L~5S&=JJeWTFDP5Y-#hzH1mBUOL8!;);mXcSq1mq5W_ z!}Pl(`+ASI9r@`I->@RfwaPD)G#cPSs}Im#(?_YxrWL2Usr+Ps_*&+}YB3&qh!mV^ zC<%PTYQZ}3ujaW0=*1DOR}OaPCo6=wLXV}b!j8?iAJ4;2zR{e*qMJKK{VJrmk{ zMQBIcV4!>p$m50XI~zF(@>z<)T%uOYZnKo~xxgp+zLI9t-?YoL$DQ|D@tY;%fdquj zcwXn)L6KG~-4X@6aXGYT2YA?5Y8p3BpGJCn-st&n(D68?-|z(g!>&}Q@po$Zz>}_% z;h2olS>#;(x{m4MAR%Aa+%wqy^$fH(9`kA%^;|)SG=N@Z;L`0>q_KWtp?RdVSzmSa zhQd&&9_5~T%%${1M~lX1r;3x7R^F6)-wRE%pG5LeyU^XyVP^}s`s!AihLU9VT! zf+$NmMJUQ3^0y^n+F^gLC*kr0g1gI2c`FX8~wA5&1HE@YTuxD8`eSO@iJ7a1nafA1^uxp zJKn()gc4pa*Ke><_vx#<|De%{V`cksU8Qqq=L$?3C!(f7JBeSZb*tF|H1bq+H6s*d zl@U(=B28J)n4@EZf3^7wZEu5Ds!%JGmAT!Rsh~Glt765}l9RhYj`3( zM?i2Mgj_w;>dx3_`~dd?n8BWHxMyLCTZ{kH7GKo^Y4AD29L$v!_p{jX4>f?_=e$Bu>ZVjq2}vqRIedQa`}h0(=eoMO9QL{1@7L$~dOjb|L066>O5%ctAKp`W za^fCz2}y6qyJ_QNzKA0igmaBkU|UkM*E30vXouNalDaXq8`bODt^}c0Y&VJj_u7jzWYhnWT4N>w3%K8_~S??<>k~tBU zN%cQD(DG*OuHRtZ3rpW`9Lzi*Xw4h~nK?Epks>;an7*g%-Xj-xFGzpm`|Q;Ez-D?uhM zS_5tI8Gf}F?CI)MTS5Za;l0>ZNpR{)cV6r=Ry*_ErY2lz1tQ61*{hT)r)kbiPQzIX z>tq0O+PQXOYB95VK-@j7p~aMin27#{%!zU8-c+SEW&q@~1=$NAk=6V+bLSgffgT zAs60r>g^V*G$izZJe#lbLBu+ygUPRUp$)T2D*Bw-tqk=5tG#f2#&- z7ExMbuh$xn{#ILG`x9G9Ik)F4eS2|5iF7ndMle0a=;b~$zH?uBgrCi6xKW#@nn0EB z1Bknm9P}{lQ4D6F5plBV*@gLhy{mmO&9{dPb+!=5?CuR3%83y9`z^9?a<##pew_ea zCi4&<6^R)&0)YnBDQT1%w;cha*P(1dd1>i>N#c6ol!^iabie^L{UhGZ=+r6cF|ff{ zibh!do3hvU0TjLdEtHSIym5JgDK{2o_6+KG(}oO*!K<)#rzmgN2@&<&1UV7uYf$Hi z9`)hR*sYy+H|4IC{eFjG2*rH>CKgu|9*`CgU6E;88|6{B7#(y_8rQ`fcoS&kS|_II z;l}UKnq?nr2avU6f;G*AF>Ba`amLwJ7D9@3@Wl>&`dzmD>ja;S7=@d zE{RVxqHY2Vifggn(cBv8(>xZ}#f8wVfe$K_S9{bLSlb52u03;~YrRGhWjFk2OgWaC zt(pk2p1<0V4C?GMZgqe%bZUhP`;dcn36xU1uvL!4G` z&OP^sQkuQesi!1d!O*PFEnJB9RRQVcxK{9*;3Y~`la?5)5cE%Z>8~?^PBD+ z3;$Q_i^|O5S+zwODP?ds-yqDr@Popy2Axo)8+4p4e8Wv=zzUtOSbKn&xL~p=`DL=b@-plJGfP$TBxBtbcG5p%gT0I*b#m3S zQQd2^aeSFD=n*#OhFxE@ z#}xo*$9|A7LDhLUS7zjSwEfnUy6vN!%7{lw+(e?}5Hum;K3f@MaPkX>d0ho{{~d_Y z_JYlN?XY&(_2tY+$AEdMEwkDjd_O8V#uwb#P~`6}P~SerHL-H7HMHyHQho~z5(!uH zAOAm9x|oPdepzLw0$j6hpQJ-Jtk-V85EF-Ri2SR)pcIchB2@JKE!`2KeQyML*Nwc@ z2Yq-B?QB)is|ge|C0m2FcYK9ejGd+#kqz}*@`ImXSEUGLQs0cFOc3*%Yx?Os8BnyN zan{5&OG0IKUWR>F5UyW`be$RdQl@+o7RA2*G-Y+c)S>jQZ-j2)Kd|Vw&e43M^Mj$d zjWWtG5#y9@nGdQ7)?-b7G~R&QubiQ*%9ZsiF9Kz*SxBhH@QPY=_yM+I+rq(70#Fk| zX?;~jHKuJbKax1G4vx>%t^1u9lju>ZO?{!`|2>yL-Eb9GqVB(-1RRrf!EuL0hcJmZ zV9E{0zBj%`o(?0NLa%_%D8${LaP;8j)xglHbxmHiw?q!sh5Z=-PlRtF3`XpQnHR80 z9+LK~lv7bs|Hmo&oPa^>$#um5?#4XFL2t}v%HCdUre#8mNuey$HLkHv=9+)WUCX(` zw=HxE*8#{tKUvq${Gvp`GJm5WE5kKuf7-+DA_wDx)EMn6#K+=DraUGr%J?qwMLGEx zuAXbznFl4b6DV)9Oc0gyN?oBx@l#2=IPP5Vu8KZPCqf8u1DPwfUrR?*R z<@{!@Kd>bttr+W%>MkKFjmX|T*gE>=`<~$%?kg5E|4#Y;HrK3iNpcjx6&pdnHKYJs z@qF71Y#X=xJLuIVd`(g0sQl*$>2~E52RNQcb;S)Z}FtIp{%AvZ?P`^K>feE60Yg=C;n;Jv{{slH{@T!OXO>{O9CAvRd%C+XK2 z1qU--MOMo(1}&-K<-R7q*oS7~a>J+5e1A=a4AflzlST>!fXE zn{(YCtQ&E*qGH9*Zs0TfZeLecP%f$ z`c7-eANiMr!3>%u%uHb~j}TpU>DqrozFI);wv_%jd@;iwM!Y?jJ9w`F?2 zQO!tq>#~>o`pX9kQpi7ioj1Q)$TyG?YBXRL1V>U`B#7E9-6Z5g-qh7f$ht;P0YLc5L!Yj-Z_vk9A_ z?{yhF0{`4)-2s^Gydb*&|+q!LX3pAfraG69JnJl4Cf zrF>ff0aB;Y;2_E-ZV>748X0==yP=!wSD-p>_^O(0_tp{Y+A1hw1o&qPG1V9sX6Sp|mehYnkgI z9zZ<*M$ddLOK;poD;R(x!#fd@MWS_Vh0C?zU-XbCyIH{!&@zGLigyuqlGqzV0^|Y8NvAuXHb8>+c zA$evYJN6bN%h0zpCu5Vl)Q;7|-7H>E<`t|?jCri*%S_QCBZacgwzFKh>=Uo=Jq-V_ zQM{KcH0Qbdk|khfh<29GN%Ty^6t3JWfb|WnWa_!*jbG_w5xG^Fn_g7j z{lO_+r7U~X6YbdjNin+;(s8sEWEeqEsaWtTUrphLDlg6@F1x3L1vb(X!RqCSU3&;*lcu^xAqs;hn)=wUb z2aV*FOw?p-{}={E8w$_LW$Hp|0mbj8+>a z|I0C>oSELavjHyPF>VHr@osDj+f^^&DmLcj$a8=rRp@uo-Xa{69s?t`L>F)OpMLQ^ zwmU|JJqHqreYv=)v4r_4Y@mBXYEu?iaim1X$KBygo6GH=30n=Bd7~<9=#eER(!o#v z4z+H7-)#7%r&(8skq*pCW)tFl^T1Qwh}TX`B!I0SN<59EOSIiY9a=bt?w(HcU6l)D z=nF5*EJj;jXRlLvRuHSn#c}3boNw9Ro7{3|bi<3VljgZibDUjReC&!mPudFbi{}xm zG9jn++%ec^_q_2+(kON~u8LrSD4EP@R!^C-!<8V+Io1RWj za9WO5^htk9wrMDCR8h0+tE(l1xFk^lEt_fJ{p;B zO!@(u93)j#DK46f*Z5O8$Fx|^zaNoyOPDs~+2}95gDHYuo4#rK&&$K28@wOF2NOZcF>Ej$WvR@Bt>C%o^`e$ zL=Wn(tol(spC#-{jmMF1t;3xf!CI5Oj^(ZO4GM82L z$k;KgpnQ&gI~hfT>95u`{!fedo_bB?gZRXHnwLsK$0^D#damaf^H21Gdd?rzIp%_j z*-v`M6tOM@bS&)>JcA3UeNBW%YFX=NSU1BDohRWEH3 zSF`xfQbdb(@>gHzM@iAJkNvPH9DEp#8e{G^2IO+}G-bQBVh~8m=k90His#?wUyIRYKVeH#2Xv_spaD>}S+2R#)l!emPgc& z8Im|?BLA1-;f36zb|4cPK2q$U<;YDX9!|mxzl3p_wv5**l+2RMsc?RS4lcDoO6Gq1 zEDoErU9rr}bR!<_o7=?rWhd!fCvN~h-B4xHL1Q$MaA31OkuaZNWIsV z739X|&JTW36LF0{E|C6BHF``4CYFd_O$SR~7~=RY^J4LV~BwtDxpcg9WiJv45| zB}I9v4F^oXbQ*Qr;V95ED-fWc9Ad@Qd%4p6g8eUrqjM_WerQx6K8pmMDFAyD6Po!n zK$2jpm5MjEY^7(Db5qYxb4r~DXOEg<^z)FU-xX8PD$mW36>jkO)!cxeFl#Tt_t=&i zkL26AF}n796fV{?O(!WqTE+<^KU|Qa`CfWZ0pr&$Uk$+z5Si887q=?0OLQn^Ql|gk ziy~0(qgZlOgKJN3W1wv%Ov0DC%T9Df+)PneZf<5{UT`(m{n8pqyx?9%%EJx}Y zDPvn>;@JXJqN8#DPdYo7=UE)I*d8+wL0zEVXj{nFYau8P72k)ir7ei7oxm{aksI(W zzCZQ6&eR%H+lD_xzc%T0`X;dAwZvRQ?B+Suh6#f#nqtAjiAI&MX)>Zgcex@5Lz5bk z!3+#}KNSxe0WK5~H?I6+n{aSPnj5&~)kcZf%Tm>k$#U;W@revQsIC+i35h>mi!osc zQ|OV7Q?DEcw-?+a53_VkQ~~13tfy=3Nb_yD$1wxk?>h3;AP-SQll5|s@v8Uw?Mv|v z(ChMDuZTZ3#IDXm>cFhZb|z%Hy5|kzn(kzf>1Sb*pIivt`pD7ICwt;T{2rQM)tTFV zr?MJ>S;JP|rkb(3#)BWjubfbcADRRk6``b+W?YdPHrm`b11d1bo%ER5JAIL9ERy|5 zBcVK85INr=d1Z_|%1UV^vrT3G=wCax%9mDhN{WZ9)PsG{c3!}d*Jm@j?Z2Q+2rl*fP&t$UU)!e%E)v$o9pK0f_Y?#t0tBJa?vC?-ys(Gj^bz?f_}+XA z;>>peGRl4T;JK8|i#6pcmwm=j^{&b1Q>8dS^I8Kmlbdge z?PCl*8uQ$So{`n%XDN=fXRg&-nyw1NNob^Zb7H?q(PkHa=YCtqD9Iw z)0uj-E6YWQ*r#&c)y`l}qVnDX?!iV+=GgjWrD$l!an~y~wLo z&)4>m)+}%m>DcinBuvua8$l3@`ey|Vi|2@NIbKY+@(-jAv_9C zNlVg^dv2=eAz)*oCrAr{apRwyo-dNg9SZDn#7;M^j~hI4CM|+Xa>ZEg#27f>;$?q! zpVDmX;=Np^Z)ED+9Xxh8@dj1>9o***(=!j!7>&|U??s<@vle>a0Pgt$ow?m)R(?Rs za+%)zO_Y~vj@W;!0Qq8@)-6T((T}6no^vd4NvK}|O0*~CMY)rU;dpG6A`WwY0=Ct* zWrfEti?7c}E+BKfTraZfshhHOvepNmM1G8GZqf`~vTI6(F3y33yyY9|Xk*7sDs|}K z0m0ufjdRB(E_x3uE>T%7pa{%5I&xkm@Ebmo*r&(G%qLi3y4P)msa&>cjG)Z;%VWId z%6XpSEjG6l^GhFF@!_kdi@C3y{hz`d^GBK5^l3rAW=b6)2N{l8zYNceYe9vW#$kwk3(9Sf_%6g?!X?SIplc>xF zIV!kC@^(JjJAvJa1ZhNwp9r*mW*v{!zU;FX3zhEheH?(oy}7SE>By|3vGxJ0&Tbxa z0hj{#Zqkjv^0lT3gyJvg!Zwi{&a7D=Z6Z}t;Sp8Mm?%c3Lmk{*S&jq-_r3GOYGyyq zB8QxcX?;`3yT>TMTS28qET<*GNc}Atp}A3?#e@4{0{tCW)fa7+&iy4>y`LYIc37T= z9pB6g#zM*46>vT%in~hX?mG7PiVsPTUeclYVRfta(ud}K42L~!V63D4Mua;(TvyxH?9>X4qptNbE z2$t1Gw){yuzm**1Ia)iBIpMh~i6-BI3DnrEvDp6VZidn}Jdq(BIY!=7@SZXz0R_=x zYl3A4$jLNCQH*q24_RKRGFkKA*PnmQlgdD67_h^NNUFYX_H|!7EoUB(e!WbP<>6}f z0RJ-}=qI9dYy2=)&BLgRnvnIUMbdO9i*+bt?|R9>PLMU}nxQy#sJZAB8xE)ng#u`w zZ!oQo%eIi$sfnHh`8WuGLHX!j!j2&Y53P9GalpDYolLV;UQ zeh+u2%b*X*3o%KrOUp*SQk4SH!6eF^tDZdSZcMFT;pdd`gW3~i05+@vQEY*#x>;Tr zwK3b>)IHZ%Rr9r<+G9I&Q+DGk*}VBARUVsiTl;AKRL}{30@OqY^(DUPmUAZ9X>_pm z5BfU8zU||`1V_@ds{%K}1yBmIIYx$f$#)7yZE;tc7t>|`ps~7j?t~g`Rj?;!l;oUO zSqlp9&QiPQ8TT3cM(p&@pDsYYjSJ@@;Y~{!l}3+*SuZ(-vB6GkE}?z$CBQ>g!{+5` z7=pw!f4U8p&9T;BPyJ^pzsd_*gK+k5EDtwzY`zCrD9HPtx+EJ2W7d)P13r(eNtH^G zm%MtqctM!Y2*#SE9#<8H2S|gzZAaSS?Y}iPp|0VzW(O7(G$JGFL!`v&*e{p@prXP- z(F&%`3`^bLqu~HX)U0_8-~qP~)A8|c$$z1<@5-gSPShhUPD*K}0k4jNRaQ2q=Cr>!>sW)#;O(MQl)DSVFC_Buyp z>E7Tzrei(Dk5Ozs6{RF>wiM9+Cbkjt-){h_1SzyKP3ng6cC-J@{A?~S?OpM^-30N&?;rf z7(s0N|DvvJ3LLN{itd|$aR`mKB?R~kxz@geziLK~d8On_DZn7ye)M*(8fft-?w`;9 zGr~EBrsEXNHhJ?M8Oio^r4RDmnb)(V&t^PvW=|B7P>6Z7`I!N;j#}rDSTD?|DwDSQ zf!!COX1ZlutKv3t+7aWOImziH1W)8$9h5u*441#8T)>jcFZ>~E84~`a*MCtjYL37# zq0kS=H~+vCHEMyOLBnR#y;MUxOjf`7SU3fuNvB9!cLl=S*4;|~mf-MK$(W5n z{4C4~MF@Y-G{tNtJ^xDIg}p-=_g~82X*Rbh%>HkM>*WAwsEtcgwxzt8IV-Ip;w(jj z(^1~l7PTe0fPaC33qU{04Hy|D?Me38M=E^_$S@0}Plp!a@8wd;z1eK_Y0S)y6tEuU zb|PMUgDqx;0|g(T#LrmfSt>LhXo=w`isdTg!o$=v^Z5>%h~GRwQ43{r151vaZQvCx z=U>#hwwNPDJ~t$c7aThej0Dz~uD$b0S@EFzxi-|~N5%_@=l>wYt*n|5g5d!~&bM-@ zm?UJB?HBqLlyT;yS?xjLMOigtczn8j>a^fvvE=?dy}d!NS&SJ5v_&}i1l!~%{qGWZ z2MgYWg;WJ&Zx8)nNszFpKB>#fqzL74YoCKc8Pr4Z}NUAEzx@cQksX%ui z^-Zamvn%)6ac`r|^Lu_O&g*Q&su=u~uct+lFO#Lu2)^O0y)z|fjBkX|VKeBphIOy@ zCEu#0IsMCgVgY|>$`N?~@S#9@dPh#?IwFqvuCZ;&F zxIm7={B$zQkv2L{Mdunxdk8M}LTy6@^vPJ(_AT$`B?l1^@XP?%QRB$<5HM z3+e0RuM){)J(EaJQuMq2A|%gKhtdp|oo0ass#XWX5eF!(Phpz;SRG6j0}7(g?(ewowXkvB5$OJok6bHZ{iTk7Tm(dsC2r+o5Dd7Suu_Puhr^3sn4h?YZf0K zMx7OWhdT~p_zvCY_XO44l}t)MUZHUfNrMDj=QUVbl?QX~ffnH}D`xC12ah;dj~@+Y zbqIx0PqzGknitVTeeR7I6I0MGc~%?9xmr48H8+TDL z!K?~486zBh4x1kc(0>nC>_k4y+_aSH9uqK-Ib`J>QBEl{fDUX0*LOl9bC%JBY4iMC zEZoyB7P+i-0Q99;$q@gLySi2c>rG}Xo=J<6b}8am9(0!EIom-y*Ehqs!z)OGd5Gvw z39wF>jZ%$BVRPN%{}gLVA0Y12KEeWuQUkh$t9PH{>)89lrP8)O(Tm7K~Lp zstX|Xj0y67O5cSL*|`cv1aD=6^wd77e-kRnjw+=B5o-<6xVQj$JrZxvgx0e!E6xQU zQiPDU@dpiealyPgc4PsX$U1~A`u&}>WBtR$`OX=9GLBs1g|hqn`5kA5%qMO~UHi|g zxDQ7t`EPF<@yW8~a{E)x;PZb#6@mxVyMabMCpqYj?g3_IS8d;ptBOyF!5>n(9|d*0 zn~Wbuq3J<-$eqDh#O=tGla@0D>n&B)ZpWnj%V6xyEzZ7{qO1M&~D^+xM!9v|HAu9YYL-9@ppWcj zX56L9%jq@f>^HD+LB>WB)EQ5x`7%rIGSIpT5J~*X#&Xqe4{(K}ZSJC;98%03MrdNj z9rnzOCG?dWaqsdBNpT;bd#el^EOU<2hsnj9p*fTNxcO zG;R_a4z)|}796t(qso^!G5XCtv+{J!tek1<;=gM5`g8Nzg3u;z-{>DYF-(6tP{M)- za{l-t#x%8g^$PX@H;@f$(2np%2Q-|=r~MQ?$)IM2+oq%zS}ZgDP&w?TiwM5`k0l?r zrd|$A*@-Z?l`FrF4L$Rn?(rP6S=D1VzDCFXG;+Av;Rav}EJAyAsK)yL8naEa-iHou z9cYY2mtuS8HEVQ0)28bRd6WjIBuzxrfV%cAq&ugwG#g0Aj+g5{i21lOE&~nHbi~td zmcD${Zzc`4&25)2#g0~MgE#vyFG2S{E^yHI6&rV4d<*?1-LL8v`a_qsb(NYe3v_i} z_N&=&MNm!}&BI=Pjx37cPQ6XBxgG9pdCL>9<~tB%X__tH9-uQ_Rw&DnGc}f*dCgg; zPuCXb+-n3%x+?%?F-vI#-$M;YY8Gm{3Kef2BetMlkb>~5rv+5g{LYLo!l;o=?O6wc z=?#;Zt>5SuK7FO@WpsN|_nRN3{6V-c=O^Hy6Y6-g%QrBVIN_L`04Yky4L-s0{N(9~ zG0ls$sXKDnP&M*fPm_vSYu4B_0ATG<4BLdw4S(V`O=TWH8{P0VNvxNIGIoIj!f7^a}l|J0aLa{}oL! z#%Xxo-8{|-eW#nO^XQB_i(qYDs zR3lY&Mb0|rdu@5q9PXIJ#awVI<*P9u?I7QtTh>q9Z!A)EnWqZ6FXL)&w;-J7bnCXO z6!Li!ATJ-8qw7@P(aa=FpIBUiPYr#$l~36I;6e1f%G3z}=sF8PpQB`IFG$#5ZubL6 z;^yz`P#^aDWrE8sSP$36tevAME%as8?EE@jrQ3XWHSXIEZ;>7Fp=k)_20L zGRd#D6l)PgWvMYSF)K`<(mSv(YOqAd)X}!mBI9wRiy=%aAf!$}s?r>14CX{hJ~+~1 zMk~>}`a0;^CLQl8U3Z{=dC2r1&3wlO&!!v(uERC=y7BwW>E6aZ7QyqJs8MMu;*B51 z0C7qjXP((^p>vS7d|cEn9)P+DEyelO8DBUr`83G#(5A4AYSI&LwTQ?|yqIeKiYz=4 zRa*-89m5`Nnr-YveXGzg2ivT@ju{6qfv(H2&!5d+KgWThLnw|-eZ4?mb7r|}0m>

    YI?@rkhH33k6S-d*`O_O_7aVj+dXz=eK_D>%tzj)0p@z&Q<+V zXrK?Hb1{F-)5->Vmz!_leE#1UULAgV1r8b_;pnkX#ZyC>yVl&H5eR9i?>TQzr0HFO zX*evH2|!+JC&q-}{mo=ogT&PK;x;}ePkNz?(={o3osUfU+l8m0w)E11v`E(3l-qwB1Kr6#$gA|A>6NiT?2a?n0wWmlaskeiIWt8nF(OcSm&HrJPv`Yeemoj&( z8yzO?(y?YYo}+fUVXFCVfzYp9Ec4DA)?HbrM7usWI2%*!Dp$d`>}BkQH@T9{k4UwP zSl_yLMv66!u?fw9kt`U>n*wLqjW#i6;P85bwZ=}lu;!J+S-YkpHs}W&Y+UwK!_PXw z`C~R(Re~tRuwiB>;LR{AN$ywR2>b?&Zw@q z8{P!Q{-RRd?M1c^IoQaS*F;O61Tjk+Dz})DJYbz1qBGvMr}i4_ARO@vT5B|kX#g5O zXMMd|k3h&pD&o9U#in~$!>Z>g{k!|7b|_HG5uXvTPYyL~Kl0J~+~<{X>5bxnSm+UV zGoe(EVAW8RBFs?nvtUA{2Af5UDHXNK+oV%v%gmpos&N5#vVn1d*=E)7hpOS(aE3!H z#Z36TF>8e%Z3bnk?o=*m-wnf>I{@Yb!T88swT}8FT_dz5<#vF2-{T0;iPUOFd>3x# z0oLGTBFi(HCufsk;TG*`SG3XbNJ)!!lV5cuHrduhKQ2bs%@u#oeaYte_Kb&c2zZq! zc&JF2uwiS;)Ljl?8v^i$i_lsbgHOmaIDz~XJQ$)ecnBxM7-|wqXvn?Z%eE|d;i*Dp z;KUQ6f%i|sz4r0fw@4tzx^AN@(0r)r3i(IyLOl^J+RE0v_Q)XFY)y07RoN4)jVn}@ z4dUfw$Kh?HzRec7U1N$#Yz`ITN2envPFRdv;_gWhJO0NP`KPzJGo@oepe|*6DaIVJTISmQ+H^re# zRcZ39q7vPF$TYR*ljJFix5jo@ZKceu@J%rOKyV-TMNRn$X#Xe*>3gD`>uf$-M4fU`U*hdLMY1bTN@JPvo*Y{PO zE_?%Fx{>vK(Pmpl2{M7co|0eWBjiZ~QgJ>F1LCkR*wrrsM#eaSR!qo2m)XL4Pm4S8 zh~RYBs3vOAmnmxM8}qBZqw?ng>Fu*wv-4+*?Q?LX#1|~brOfsfOeY4F7MF&dM(p5P zj|qO!zzfS~&f|u+5jp=vU1xJH2e^j14brpDN{8$&PK%1QyRsAEstt=nq@KKw-1Za^Z3)oKk;S8 zLe$=E)JC?6ANLy!VgE;!9`ssXT4)C$?^qn6l&BnHw^LsgF}e;&T-45r|I2CJ{`Kp) zbQ3ChI0X$L2gg;uKOkDx)8T7-*9WvvYXPs%b61up9ucF0^&=E^&={rEYmVVGRa48} zLc*(_#wyhiwJ`8F=D1lN0MTx4!fP=O8CY|IZ36}2I=RDqJ(Mg&}J;b41`!#7<0#7(8zd7)z{k86Ykja)Jk#dg;@zllB5l;n#oKA-0uIyRTn9V+ zzo}ZwVAQL^g%IAvB(h~c2?6!XUNmzM8$jmP>{RQ|N$yt;{mMt)%ZILihRi#0cjL&< zeq~r*LUkRI_}@>t{qgzM4EVFOo-Xg#;H?9;yeXWhe&x&Szs@2#4AXosM_+wr5w-b> zfxGP5g{Cp`XbEKEmQsm+EKR4?o%B8CSFF>yBP4h#6~ELzxyL(M{%B;-vv*|fUvZ5q zExwKMs#C3WfqW#DCaJ~iSdPbXRY`uk2_iGemcbHU zjjA!r+=Ve1lKMRY{rC_5cc`#yX5oD;>6Re^Ykl( z{x<|s6z{uS|EPu2rp1gXI`cq-M60S_eUEe9&ly&VYWVZi=BMUk6&HT~#wE4+L4{Sa z?@jkqei-$JC3-+c2z)l&?UXsWL^>AT&=tp~1-yRE_%Zm6*cldlf^n%e%FOy;K&93r za^l@&ri}_szzQSfnH|}G81pz-| z5aHth_2-l*+$Ar2DGbr?4MWxo z%ld_>JIK5^eZ(_oOuDfWd~SkzQ&_62YsYgnwN$(hReTR2h^6H+P$Ss7eXtV;ves|O zi5FIjp1WNBy-9&MYs^b~H%`Dc<1o-GBuF~@WlRBU(32QMv@6KiDf&r#TYzeDBJZga zzGLL|_RNzwkJQ?u5(3&h-47D4{eZTk5T`6_hgG)QsyyBtJj!!6yh%#!JL7Sg%s3CF zhG{hMZ`-=)F`)PcLx-=@&67NJl|QART2y!r>uY>DAf;~8YF2To4A@+A8fU$B@@sCx z!YErtluq}jw7Xw8&!(UN@^)q!V*Tv$Zi^!9^_kT+x>GF8kEddIqq0OgZCD znQ(Z9Z&m0@?at*-j{@{=H(V2&xfT{<{PtCPlS)0vx*0<*a6|?^;||={oQlLtk}MTf zd-|?7c42I(#y6(K)p2X%--*-Ql&iIz`7JS+I>1$D4JfjDYruMil4qKC8ScP>2Q7}D zicIWhh<<8cHbCfg;3fr_xt+K$n=agI-LG`3w}M4D;J34B_Wv;JD1l*<+=T}H*HC<(0R0an762@A_fc;wl_ z+41U>2eWD)BCVinQD9epb(SLy76=5UwiV2>ETacA)!EH{zNgtut{WIjGl_`aZ)d-7 zURhgVOyR~mfJGzA*3S9`69Nv)-UrtbiG7POj$_xP!X3htUXXMPHgKP$+@K8ZR#E0+ z9@kk3GSg@^$7o<90-riMM1THUhFA>D!$+R{*bv+imJH5B6tCt;%_C6)i z&L2Orkbl?=5v7x`Lj9`tGV|mSmP6;ENWI5V-p3d6HyUKRQY+T}5MwZy)8z)hioUZ$ za)o$|WJfwTB-OFb?VAN6RqZBbvD$0{S|28D*gZ!Vn$?|w`&!_t0|1{Dtmu+VX6k>V z`)pME9l_QweYqU*ZFM1|&MiCC{up(PHJruq172{&>D*CWt%DV@83%_r(0W*Bt(7vg z*qOm;>JuGMJQ1|S%r?4wrP3&mc8_n9ye35=qTLw484}HvbX-wvxGMW`cm~zs6!0N6 zRX_Fzyw8RK5*oLv@W4ooa5X2On>F1xvdF4niE#`W(?pQ(pu?(;ow6#u*3)p!P|oR> zLowcqi|s{38H9?)qE=Y~2%K(>2jN?M8*{pL?VOP*etBhZ)W|dg#Oe{fRyD1vyQ&rU zFt>EOmdxkmtt-FQxB||XmO?T`nH^4-N~wR4*W@PmhWj>F($}I)vfda(rl&QZk&Y7y zo-3;N=f?oxu_uE4$Dmq7n$t}u>ED}b{$*T|v^g;?4^f7oX*!ELKzwW1gj==2xP3k7 z-C`miaie(La#g;!q1u(_%cJS6`cfiTr6^KZKKjzIV< z*>FX&@(wcPx|DxghH|=G;OE3%|%LR+u~Mbo^l5PSbU+iiZ|cs^~j9P-xhrmI>aIi(E7Xn zBgw!>6fOdTUyqBaY{Ru>=Ivw1V@veoq&>P)^?z~TTt86LXDr~vg9sR{McuDMsO_^P8C#(-l z?`twxC1T~aUdIL%R2Ju0g>gfL!^(1%S|_M!b0jS3UGmw-?2b^5Rb_q=cQT>`3S-R2 z6U)%2g=BTW-vy?<{fK*C44jVRJ4lM(|Dh&J^`GvS`W*H8v`i|sk^(=@n;*y%5$N(0 zDtkC}9I9*)8)f42L~Q?%EkDU*-59qAm$r2K(uJx~27=#CwIma7>17epA|n}wjIvJO$crh}2l;V^ z_-CZ&SfSVOX^Pd6MnERcMJ9p2@^vEiI=cz?ar08qeuJxA(4FZ= zjW2jj3l`f=o^pLjoq91*DWkGkW~O>V75ZF;R}whEydP?=GXVcbnFRIM&bPjpUwc$P zQfx~`!0}42)}9dq=gT~>E)SbPJ5LQw%yQ1~xb}|5Dx3qg0w<%FHCTP*K1QKkVtv7j zK|$414@FVw5}ZG7YK(QhHyjE213?Jb%(2ESa%Dxkje`wKu412kO6J5G;VpP@sZMy% zhH{f|6EKdF6N`?+>X%|Ne&guO$F{tck5dYY8w>8AFEI``QNP zQLOG!sqtt=6DlB1Xi-FEX=0%DwZ`qb);C1)U+9znYdCEF6|8l?i371s`{ABvZV8JK ze=67jK-Mk>Qlbg+cpHvx5T6l{-#iFAxtN$=;eN$t+cpfO8DNUnqW&xR3^TaWV#m%n zuW@vNzG2|_=`+hCIp<*WN5q4ho8M~!@K5xB9T`h@&^WVhF~3)cnMh416?fr|va_fr zql}*#J+(eT_ypr*FDH9gk=VGm8RH*di7G`nXz@X}+3D}}-9c!lK2%7%N)Te7E3jtU zLD#QqW%1gWPY*2mOnQTsdWtS8apFIViq1yV^mlIQ_j%RR-;-hWhJitSh$*_DEgDPB zG~9VJwSWs87C#a!_y!5NvY4#UP|rOE%+P_(7-n{fvV!^P2DL>*lU|Ey;)Rvmim}A6T;dMX;J-Q1ESx&(t_*m0*wH#ia*B%a3G}FGz zG=a*~20PLGhj8*PapDiTv(bu)!LNN{zmB0JUVEH;R}nPT%o_H>U;f?>2u)r5p=N`0 z7SU8aFFg1I<_bFGlel*#wM=!dZrzW9PYX{ayC?^~O>l~wAl0Qzif`(Nh zfGZX}sEvT8^}Iy>1e?55d)(f#cnkP4?x%J!D9J$B*7anK66?z-1&%}0O*)3f!kkLA zm)9VR)Qf9@jfaGfTV%;M468b6IhtHp$hS~k@>z9IF}Kv7}?ACeudtyz+mHWvUanK#(2R;y7yj@v zfIg#m$CxkQ1&3!MU%Z7`cpTbvZk|hN)%e`*#0?S4&8{nCE{0A@Rm-j1 zDH#0TQ21DC!4M~$j}|q5`TYaca-;fxh;%%%bl-nG42!=#queDHR}`-2JW$cVz()!= zUjTI-nBji@E|4U;)IJd)>QOOQvET&|s(AlDWfW`p(Qu73r2xg^w^nS9t5P`Y9Lz>u z;vM(dhw{_gy0cAUsRTpoNzxi}*&lKB1(xZTyl%~Ku3t|%3b%*95nqq{nJ+R>bgfM? z101b8C4dP1?lSS82UNIf;}_;c=3PYplJ8gZp#}O7wc#sFAoL9*wx5ai&fb&!K0O9k zSfZR0u#ZzR*DC*!!sEM^BmPY*#L&7Iv(Ic3#3R?Jxz7*s(nl3A!5d)!nv*r7y%Sdt zJ@a zJW;B%B`LKiOd93bkUX@19A5YzVet2>5AH<^w2Hz7=uhg%PufE6JU-H%S?XvU#7tMQ z-Y3P3q4CwbX-`u>~4oXxcFbjr0jchA6(jVq=wzAm0Gq44G$ z9wuN7)>g&av(!UWZvqO8%)5gz?=COXuNN3<&5`v6ehAX~u+Ag8)s)EB;%XXMz9`1g zZ^Eg_$Jl;U$SxH-P0)yeCGz?vLIwLqRDTC+%_%R*c|-Ek#W(;0!X`d?@sy(&F_$`) zK7=1DaQFOVo6rqE3o{{?D=R)?EgQ$?V4>EGsGM>rl_Pb zIcIZTr9vm2G>4GfiQc1c~6n6bSeSeQf z_2{o2J?gqXpZE28zh2K5`*RbVXcpvVR{`4D7&#WFZoR;?*%9MziGsD|sknN#&CbE( z%%8-*@8-JnGe_7R@~6>kJ=$5Pz8x>ie06k!Y`;@?h+(i{&xuc3MeJ%Zm&*d2gXsqS zL{4A>>WIz2Ka6i>_Wq=VsC4ui4L1V9Ur=AR%)p?lo&6rTZ!*v&Iivc;rV>Xm`l`sg z@te}pl8IP2^U6xdQW zkDy+I)V}ip{hI&mWO7U52OxMo4P$5Hap}t`@flV%qFIv0 z4aD0QJCbVr`3gd;Lfe0r?+VHZlWOP1wStm~pIoW#T%O)ZMAeDV_)`mgKC}Z~J|On` z%&Pla7VFy{opHtE5K4q!scze#Wex`jOm~70whMCMkhTz2cfIE1LS8v)P&K&`*WsN0 zU0dfOZDo0>_tjaM-U<>e4BJM7wqDgYSrvCzKEGKx#Pf{XayHYoZz z+Bs#?i63>;J`8lQj}5;#!g&uRo3JWx0<&iga2Te)`UyYV!H}Na=CPqZtm(~9ZiEyV zhRNo*0_c56w4U!|Kp9|?Z&x*ZEO$0N=TdlHRJ4Tq!|W_O?i*;u+vIdwdq+HUJDa?J zCq5QCuwrT$6=o+Nlxysj*l)Hi4Z;<6N>hz(ymSPs61c~PJP4ZvA+?~{k{_$7w~G74 z>(40J+H$7(_(}9#?T2*u2Jh=7o|RPSwF7JcGv{jgNuPJn8bub_A96-_9XSmVJAmR2xYWklZ@x*PU4t+&( ztNzh|h3Ia0%9;-O(qx0rgoaDx%3pb-HexhenOX&;wK;cg<_**rW?JV->9eQUHxBJ6 z_kq8Xpy;vR28}DK`}^2_OaKGV)2ZV4Xf8kB>y}_HZH|<_UJH0{yS&Ts_J#;c zo7$eu5( zOnRx*(8KinWLk3tgj+qoY=$$CM%N{h zcQ1&)ZL>%El>$2@{|^)hV$v@+B4YE5{Mj-tcyv}`Yu=#0wyX0ezr&r!=9N#SD8S`I zPy?T2&r~n|lE>0u`88v%WCK=_0|or67S|uTtcEveTTi)RU=)YZbJR8fhVvoNhF{2Vd^&` za?H3g-Q^T{-7duw1ndZz+@9(2p*A?uy+B{_RrL;D@Vd+BwAGuRd1Gs}s8>+UD>$X> z;rfa+bQ@R}hC9duYrWL$V>?l=p$%GEiQFG)Ge>EzM&$-cWK()=x0<8r7}er&==yQS zQCiOV!$|lIb$ULd>>A{uzH-zFQvzrqUo(f0);mP7@S18`-nDoaoub^rz7 zZaDGc-frPTQ=kH@WPwSE2q347G#g#rG2Gj)GSsCHT|uEVs9_CIt#@oW*2e^ednQf^-|v|b9BE=G2;O*V{}P=_}^@YHc{P>#_AH$LMW=3*_uh!*k(aa_U5B)C10R8wn?iH#Flis>R(G{HO7d7dU^Maq~fL+7t z+{pvRlO6yhp)ANP+JR}-Hzkh87zCgamNoVlDDzfKQsPjX^D1Vp zf4%jR{8C}~70D~eQhepzxzPaQwMsoaUjCxyw{-hQL`VW@8+63%%8+A4 z<9GHtbFgfsRJCCWpv$V_H>sR`&{*mJJGD_;{^yFlK_p5Zno2eCjmH%sq%QCToxTd@ zYIn`w)n-izOPBQ3;-M#?g6>K8J)%>j7u>Jn@C`^9ucFi6S@;&urV^mL)9hz|$P>zT zVP8HKTArc3e}!}r)mN;hgkC1xJ89jF-nv|zZB+Bx)>mpkzJQk`)R;<+MjM#KV($%t zEzE*Zu@U!UK)Y9vK--Y^6%od@j^UDyhh{A5GcG=^taK1}zoT#$sUXs`q(s#{su0Ihrn~e{l1C>irNlXldBfrP2~J&91vFP_1jeC zsy6M5-iz6_;@f1P;1^3gR~L24Q~fuA zsT&%#!P%^0TnHZIcneUQ-lpQh(AK&6y(5P$PbQje@Y&KRXS=W+c-9LX9JiPB)qaCO zcJp+&qdXwC9J>b3)(&CupPMlLC{MCh(p%ib&9b8^5z9bq(lGULWZ{-xrkmL!_ljE89XjuY|FOHJIzKgMV` zDDe|tzyLH}2q)02^|h<=>z2p9MBhCv74B;H9F)OZr_*}N_8zZ)2xvzaft2_jdRWhX znOCF|`itElH(9>xGOlipQl2F#vXQIcp_nyN;TCAezOU539%faU-fc#Q^9Rs>7lQLX z!EM*-AToB-Q(<*3`V{G)sNu>w;YnZIE;N*|lKx2MjhjHV9h5UUKwCjObXc_wv#JLf zN2e9O{|aIqGEF09Y|9S6lE!$P%*4C&SXj8u8N`I`=Il@Ln&}?>Z zhrB$8w%r%!?~E(dRD-JbgfdTxh{6pHbz3Aig)7_{1L=B0Wn3IMbUIUD@arM>KCP@` zUaJzl(kKG;=5Qy$wO1`-$$0&4ERUzdka)D%*_AylPcB63 zaC5A2X(&^h`prtgkI&-v*1f$T6$_@HH)R>i*asA0a*uYn2DDKNW0yD)l$Fl9g8KoR z7g~*o5?^b2JTD_4Bh=dLIPJf_&p-2en`?Z2rW!wLc(?~M=+l?IBz;-L_xeWY#iiu$ zyv4{!9%Qjy_;9utv>aO81JZX-a3nW^rorBozVcf9IYGz2P5q?aHsPLCEo$2(o7l&A zlvjc16{l7H;&(Be%RGJMMDQilrCpm!l;E0DlcO@g^`y|E-G_!t^q<7dIwZY>3Qr;! z9sQaSbi?^tA~7h1NR+OoDmpRAf9XOuy<~*XZ1(I_E}nkH37VDf_U+lO<7?qg?{OQ& zxj0c51Xc0DbW`U<*}HNBP3O1e5~Pu-R^`B7V^^_c(P+U%a+|;Jv~EU(=@X$L$E?oS zpSqbixT82I;+sd9q6JuxGlx@aLPf=uWg^ZG=~X1p(g<+z9>uCuy<-(^P%Ygq3OGcR z+lw?3W>RmKWFx{2#?}v%^w%m(gjH}#u0a}&i{MIweKW`>iL(our!zjxyFMFP+9R%8*S>*DYDd*WEdQyd7al#bq{vb*p}uA z;+|0v<(VoD%j37Bv)>`n{iQq@2ZY7uQw+^QU*Ji`^>mWJ#?nb3o&$J)9e=#}83kj6 zb^B&UYh!c^Z9+EUE72`~7erVnKG0e)AG#fHc%(iNvez&-;K^?hvFKVae2`quR)c3T zt2a|@)8<&+nab8F2x^0CW*b76y-7BEzPLD2i{mKj%jQHEgt0f9vB}fhN!(H1Q&U`D zkTtI6SlJHoK&qVlfQW>eXbJcBh3A5Ys7#A?7s8Z&kKSoVuJ4{V9-hoSC|zGzwqRNo zeptoXfP%dnuut{LJk*0iw&35*)g zi0_hH6s~Omj=#qIh6irmFIX;Yiex{D<2odK71ZMUMM#(N#+rJcWlrLS7L>{qnTx}+ z;|+!1sE4NY$B5wWXcv^Aj27XZGHF}xjRRcA;bWBRHKFX=T2C^ULm9ROrlJxtXq71{ z%R5K<#G9|TZKLU!oiwGclI_h^z`(JoS$kPH)!jvG=gf( zkn2Z9@cmTO7rS5ZgD?CX9{J>3e85UaDpdas{h-&_)OZieGk0KNssMd*)CTVu6kUE7 z%E+EHy_qB^+)4}7m=MC>Y^_2NuST-&MTFe8=#6t@)XE+O5^bMgOcziy1GUh;h4$zz z>G@}o5eZS?RMd&LCi4%?`@Lnhz>9N$0gR+%YP~r4EAJ{VH_ZO>5B7AUKU!cpz!`{a zOe#-jl_o^kc$=7gqnWh`S1GiPb+4BJL3;uFolG|4GPSU6Ol@LvfonO#v(1nL(3;Ot zLvhl`H>tRU3Bji{OY8AB8ngZ?92G65-7te{g||6a_EFqtwN4_v2dv`izXRzQPq<2x zGdJ&^c^E{`5gv3{)GpP;#w{?RxJ%##ddb&_F2t@(ddcUZ!*P7FS?0Yq<25Uu_^U!6 zgH}xUlLAJi|6#0OaN7?vXvc=949GR06cFRgGCQ&?pos^FuzTI7JMj0Q z4sTmX9X0>JT4RE#0GHcEeF`P+6hnIh%4v(V)+`oxg8L;xFZ-K`V1Z ze{q7f9dC~DSJAjfasFQK@_QR2*HgsPX~Wvrs7qhSTn)tjO|aW&mYo`ksebVmjhM8G zdz>4?oop!*cg~_jJcR~*^io-Pw zB5sIZsFyyJn;gU4Od_cpNgm4cp#rXV=`Z|s-eGCcq0lX)9_W*p+xL@t=(2uYqKDt- zG|*|aUCUuZPbYqSe0M-Al%Bs45V(OY-Ce1S(6T+qB=pAF?sL@{?6CpK37|xZ@Qf`(SX7kxtl1 zpTnO^XgKNp*-&9OH|*k-Yc^27{-oD!->TonSM2-DcM-8(Sr3h93TxNrC! zb)S{zzg^-b!*4E!()MSj7c-jPEs6O_jc}(!M^Vfs*c@PF>qAu9hO-Yv?DUA~j`F9J zUm#h0Mas@~c?{RQ>;6q13i5)v_^QN{EU))Z_7Ope5y>}_`wU$ubcaA!cUWAMw{iWy zUa>iPiOPWxFVH00rrXC#ar;@RIQwDAhMW7teQHn zR>)sv(d}wO98Ul}j7d19*SZzH-j(=Re0xyzZHy7Ga7gh}?`j_^F@IXR$zrp~0D)tM zwz2K5#q9T3;n?;3d+PiLE$=;7=ia^Commita8n`=4rHSiQxOD`%ix3>}!ndrHEQ2k++n6HiY zU*RzVQatV_rfv!wO~Cvbox;6tTsf3yd_-c8P?xSSZ`G%}sW2+uuaOYtA)si$OL4;# znAzP>V-3^N0p8$gg!MRzJ3EEj_ZA=b!dXYhoc6m+6bL)=b$_!z@-E~d)IDAx;pKRd zKa)N0_n)O4J8F-Pq~Avmt`>DkGD>A|xj~Vtf$#ua8T^%+YR+FbvPqy@9fz3tx({8S zrS^a0#8$W%)Sv%J-Mba*oD^_~y?5#;ADwNHrFEbKDPOusZygc&T7MRuDhB)3JRPz1 zf#Z@-K6-nb_OJF_44hQKUWMq|s-9oHxBFp7sTBjAuxUum zq%0XKu<%D#|YW^G3{^s5x26;%JvlY zqR>%h#GFBZhTxN*Ugj0SavvKz;1fE=`N2EG?v;J^W8(W`k~b*xN~6vEHYwJ~IUhZK zNNd@*sntdgZ}5hV?T?3^Sv>Fnsk!W0ACK1Yda1sbKbW=zp_;V{W8Lq45xZU)@AOn% zXOYABid2mrEkv<<36lY~!;A@5+X&lk zg2!%C_wdejdVu)u391zQFzOt7^nN&FVh;6Y4y_0EtM`0E8r4D8_XKr{enf?$2*JCa zC11WS($=5KHRR}yS>oDnTLqi+d|^18kL0r3?#W~FS9qzr;2xmXnfpvK*cepXh2oFo zr+$K) zZ$-x*D>=-*_kJUHJj5At`y@jF{uvffRnc!gp(j${A14`E(6ie2?=xdP1=UM&RtwYt zGCHA{rmFvDDen-2eTJIikdF5A!!Fab)5-LR&uI9~6m~m}KA7aG!?L?XzGATLJt{)e znkhTm&oH-BvtB7PwCd}TKG&`@{;$SjfOxZA_1t_$RAh|@%{}(CRWP9_6tSfh@oj0i z#gyw7k&-Qp6J=?rQA_X~QUd-yjR+kmbSTD0{Y9oH{aj82Dedm?AdN zA|pAM>=Eij?)T`!oXY3%#yrvw;0x%1hJ#)8*g&gc!V&)V@lcHEW^U8-0Q?nYUO|2L zFB1yR!*lp8kTD+Rk)+^}I{voL1K81W2UIS=DsBh#lSXZz_6vv-Gjrkrc6gh#PCMC- zHZR{@^E!V-Wx~QO6q839O;bc!h6u#BwiP3|1TJ$Rh1)OnK2x290jaRcKFEJ z@Q|Wp-~Ps0hHqJX{&Y9^_t}Gom=Q|)(Zb>&sQM!17UQ+jtMNiI2&ds#9t-1v&j|Zm z>Hjg?h=lFmFQ&)UUSwDs9PTW*OJO)qy;Sd;Afy2B6Z+*okA6v~95v|oAN6@x zWI9OIrv%Kyt{PqEd2YiRPxs306FT{4XmLliMp*VEJUg289gk*(iCT1p6ztUuTZADl zW>L|_#y#$_`-qJ;(Uee8y2s!wwJirQ&2wTGwVO)pZw9jsG&6pFV&%3NmU`tc2q$cO zEwYus7V^eN^&C=@={X2}2xh-iJQN`{7j*0oHfS{H)mj~9viF0sG@M%?d_wCy`D#2? zor#-Jur!ZG#E(v;HS0SF%F`WpnKfbJy!2zV>Mn`-Gc0<==_l~m5%E35X&w$Ur9*Fe zsYDKL#EulHF|aEb#Z|XX@}hJbtFFU#O`)8>u#z^pSd+|-*hz=dV?i+yPS>{PYx01S zdU$wIv>q17jJz{B=x#dB1@&NB&FKbOTkuZ3>mo@@`89(V$Wcpp?aXBUxS9TFCEm2U zZy3L`*f#7dHo?LU{=E2@vmSKYNvh2rzu+A4zTphwx0Z$24ybB-w9OGYF-oryihsUU z?uzb;qeHCCU`?+H$Fd+ZIKs&{zzi!@Hpld&Vt!7rs;?ozkg*NnbY#<69x?(Fn!0qJ zCn{9m;ak88k5&5gH8g`MYU4^8+85Vwxa@D~j@?7d_pIvsl6yvt=Kf%*M2R{UE+t7` z1Ew+seyTCmiI;1M%>in-*K3xeM&Y)7XF(MOG6y4!E#)Tp0{M|009;qe>7Ad!F&3PO zdH_9lP1W}La-~GU{2)8N8qUzD0SL!W+u~L~wO&FPaB73jFGA~iNuL-Ujf8fc<-5?! zGEvF6baXA=!RSBgz7}Y}9ra&+a{#yHx2*~B+)8iN>hyk6w<=&WL$(h2_BCA3sZ25lReIf^yntaQ*(vT2 zT+eS3_bUAomb6pHZJN{00aZUb2i?&J}Bz7&KglcHBXB@31f#!sdWD8rs|L;P}Nt~g~H)b9$ zNk>4+epcw}wooB3D`-JD`%a-tL#}(%`>aNNZI2yGb2aG?3~;!n2$kX_N7dG`ZIym8 z=WWg12%RfwHg%W#hWTXY0LBPz|1+JiF=V6o1ZYw2crPs>0f#O5-6IlO@z*vfup6oB zx$Qi0loWFKJJPsZQI=x`HneZ?(R&EjB$@l>^DOhFBPfAkGDW7FX6;p{W!+A2ewc|4 zOdxmP_s({T*f`%x7g%TbkRJF52e9D&_5jQMgWhr`?J_=gr~|$jLg`A>gl^txVN=Ss z!@chQ9>oNwB?!_QuAzAssc+C5tU==54qq*ZyN3!TXPrBhU>pDE?()M#@MQ>zw~#+l z%&xfjKbB5}0UKo5J)ZqSUrHOt+gOo(C};tW1CYtcUYT&kChie4`o;eULHP{!OIV42 zfwZZ{g7Mtg3x4Mm_p{Q>@b3C^uFh@$*W>FcOzW|PtVM88bIkU8TGAkE3l)SCja?G_|*~e&Ce>_ zIrR&peY?-vuP=K7(2}B?9n;^ z>(7hd;P-*S4uAYMDcJ($50Jyufu{y$!b*=HO3n_i!})xVUf`WYYqV zi`e`;P!Hp$$x6DDHf?@awK^)aGkme~qrV;GKJ3VBB^kH0M2ySmg0TKZ}Y~Ufvl!D>H0BY%w`Jw8MT-J5%qkL+&9Y;_h{mFEnmM0{?l z!I%6;-N2@ZfXivRi?)T;?%;nXJP|)BvE0B?#(`T+*fT{nwXAUoEQ+?J&1ntd#Hw;i zA-_B&K=})Rpcp|#V-#PRL=~;KWb0O3m#FzM3t$OP&NWQUe2T*P+Sn*+@l)RV2W&D^ z75!?yH^UDb7%%{KFCC5vWzxk4B(WlODnxs9fu=Cf4HjgOOW7bf??UY|Bzf~}?PYeJ z9bS|EQf;RHirTQucN$I_9nlg^jR!AKt90}(bH9taw12SxHLO7J>?8FES z7q;D<*ZWz-ndC3=j{VrhH@u8*lfx!}`8e2!W>PMI0KmW}^pMtx9wv8X8Z%6KwT_bR zXYH?KMKa(Faf5)Iw*xniGIS;Qr1Npv$Hjxf><-w~9i&B}2^&$aiuFpas!sB&pU>F# zQrp*PCR)28xZR6v9zg|~6(&>)Z*7wGj@*FO=WceT07FP4X4M1XH#%n#J#Z^xxoeq1 zNE#lUIjRV0ONTyoeF@319gKRFezAw7eJk zfG`}at|Gr-!j9Nr3cSArIe`GALrUl@$?ZeNcCDNLLdkdV%hkhPLIc~mTzIgJckiyT ziw@aWOxK!>J;c=K@>NsU((=JCo`xPcstNO^I#MF zyp5awaWCuu$_OrClHaRT7_#>v2`1<#(BEa>G5uo>fyG_s#`O&>LhDH7(~Hvu74zRc6yonnPX% z*wTSi&mm|FYOSdi;0aS0AdBP$etMYWs=mLD!hty)*liVc%W)wJqMc)alGU(FSHH~h zBv4;xSP45kpJzOePn*@gbs7x5NmJCzdo;UAS*g=04Z_9bihkT3(|!|BeW24CXVbnx zh;0%8QP#e|xAubTu5x%nFU2fFLakD}_$zQ=?h-qIem@^R9;ZTw%1`LdPDsDF7hBV1 zSbn3ib%S^|O!~nn#jbc#dSGSN{lG%SkCk=A-*V+$BkxBBg!^r_(4e5cgJwQKp-8H1 zI7H7Rdh1v!j-!HI1nm@QHBQt>9V#1lVA-I<3s0}x4qzR)wBKit2D<36H0#H+I~zg! z<9VP-ao_XL;UD}9?Rq9VyTR6TV|5sNfZHjD?U!S&)|Pz?1*snoRgIrYQjKZcag2kv z<|ps6_83%0W?q^XQ+wB7s&_ag!Zgdhcn7X(0}cvmh1Og|uh)Q}9e9;3IX@Qo}@f@b^kp?FsXUt9u$Nf~MY`J3CrX5@_`_7;p z>Jv9$&Rzv)HRL-jT_AnN(u`V!KE&)TT+6U{>{`nr@+(=mchWRt+ICnq-1p|V?+ubO zaVXH2##N4}It1@3OFG64&Gd&l;8i`gkuKmvo#g!ynfXU}d0W}0tHE8M$iktb5&U`+ z7R`S%di4|42}>k=0P+GzYFw=nEO!pbLka7)aY`A(kn?9=!++F*?Tp0kcHX(KR6j)n z9q-W8074hLf2I=A_TNCJ&g2_)kDK|rio0jP&?Z6%Z&HK3=2=BlX79rJt*fnJ)eqrlByRjAo zq%98IaBcFO?$;gl%;H7Erk+*57DbT1x5LHWSw>oi`N3s&1dbPeiOgq7&>EDguJA)& z5H5@IAdzQ$jlBpdALDl{Jo{e_ye`HXfuE`u{{-51h=OBc1R%Q5FMnK3gWp9J^iw9= zcg*7h@d-lu7z?|a5W7G{iZo|&vH-jHAG|%e!9`D^XASD8>?zi*a(G-q+q<(+Q}rgM zd0Ye#3kS^*zmMnHOx1Q%oQ0#zceepIl0kd#C43v$qJhxcyuJe~?_XX_h_2z;!T#&B z*lxL~jj)^n#@{EWfD+qj&^fK4zT8@kV$fw+K@>{BhO!G5dc5k0{!jjn1l1Ya09xhR z5tVa06&rlByl@eY0IA<;a6QAQK@F!nBO_*h;5{D?QRaabCqXkFG6flxe#gcu#vMUU zXUXM;%_X9M#QbQ<+9Y?5n0#yKDZJ#zC33INcuM`u*P6z$5S-gdKUMwUc{68d^*~pn z;kG{hgRCNdz+;l;VrQ!pyPhuxhYJYadVfllpD)1H>%YG*y_K!| zM+)plYqm%4Q*}OuRH_g&jjc~A!{eQM_>u?TsY?T;?HT*|chMcj#G~~q4FbX_K1SfP zVS#dOKJS&YCvZgipa$1-_Tt-J+8>&gO@F8QO;p_1KUas;Z^C6A#tQOzurspeCcf%C zJ2|b&HnJGeA#MiPo-jgFibXGCz(Eyfg_?_!*UPSws&dExfYEpwym;|Vlq*iZPoI9> zSEW4HAffs}V`X@HyCMc-6RI--W38Rw=>gvxQ?BICJ8_ z+@lw2s|dn`00UYdF431m*R0fOKz-RPjq+ow5*|Fr%11wXp!O>8Cuwj5&00_cwKH-2 zc^2J1G;O*vi47l?8s^41SH=2B0Q9_D(b{XkJ7g7i*qYl6R>!061jwJF_f68yzj#Xg zQ^d&{(g+i$vU$+Ae^YxlVAuBMSL&? zU%nsCm+$v}j&l6Oa_}%V-79?_@3A&wc3v)rD_=mpq_vwE3zQ2Q#U_kf#F_)XGnn(w zgmiYiip6H*bM|#}@tFXcl!ChGW*~$>-d!UNNVxFK3HI3#Tl06@Ou>rwfFl3?#4e#^ zVLBJx^K+Io0W;Ku=qMHz(4PJy66;+r#6>{{@*DQt_?p)4sDl9wlqa&6Us12s^MZRe zNI}O>a04hKebS|tsWvSmh^(ZGEak(T*w~+VpQQzRC}r!*k-m9+;?lbQ`r!o@Buj6E zo84-Fg@X>AnV8!sT|pG4V>h)4%QGmN=tFh zs$0G77Gj1L-FgJ<_jCp}xH$Qz>;g${4MES_Rp>E(Sa;AO|G}H>N~r-Gx5AE(;#@Td z8i;&O~29YN{@2~Tv({h zgvd#-`dA*L_-{M~XW>VigR0RVE#q*0*RfZ=uo}WFDrKQRsQuA5+ex45Y5V+2#8T_! z;t9xhsF?`Wk!TaZ#v?xv3O*A<9r{8nn2O2SyckjzRXpDakDU-bF>FQ?DX14eQ+?qa>C($GB9$bh-{4tItYiu;~TUlfV!<5Z>RwF~|d9OsksR78Z8 z{9yS$^=s;DST7}(4JqR7j2USmzr)i(3;3=N1{|HOC3tICw3!*rUP31ZX$Je8OvZ;C zQvu(IT)#In=vDeRFG0UG#X0mk0qc=jQAqqZz)=yod3JTN)nOz!J%2%En`R=_dIil# z_1K^3=-n#{7F$Kq@2X53F7BuOVjZ5bWBb|}sJ_zyMA+xVQmT^vE&qFHc;zYCUCGzr zq$Bh>^4Zf>n_>nV~} z1Ur|Sa*t22=nnrb2*&;fMy-x$%k)rP6iKl_+OY&4D1mM6U`V-k9+5uMBYXy^0Z6hc z1_XK^W#f;Ns28R!?Rdz(Tjr{dkZx&b+N0-R6DVQ(C1>o zcqmEX<@l2Muh5gb-~xiZdPA?&(2hu?Ff9NRmg`WkBRJ|EtKHu>xg2}x4BK!wyB)_U zDB55EMi;y~l;Dg8?JRwy8$9wyz&GwXuU--i^s%q(!{$NxDz`teBL7Su0#dbesZ;ya z+2}%yNyhTUeZJsLM7aJRj)fst%V3w1HA8Os{{$a>Hy6S~Zy*SgV& zYh5`p;b>Mj|ls$P@aSDC82&b2}t@!3XB^HXAa-mUtysFXTm#3pLI{ zcm}lgy5`oji37UL91{W3_c^^+!3p6Vx~X-|`+0Vx8#It7Z>fBp(c-g zi}+OI5|vU{scj5j)f z$%*cJj`5#h4d8Cpt6W|&I*s}UoR%&YUPyR&WI{d4ulZ2~%wKn>ZG#luZke&-`o4u?9T zES?b5^`oa4VvT^$@K?$jJJKD$P#>4WBTAh=)EjmqYmOLfR;Raxrc2cOxBA*Olm;z~ zcl)0=0$%WsjXfD3y?n=|CKefFljmCRh;gsEh6eer;GY((yEM^7NLg0KsS@-_dz)As z_9!T6KXiM9^*o^#VK~=Nc4_KflW3x>4D8GoM+B5crJq3|`Pxp_`{AB6wZz~txaKG6 zMI)*g`b3k0g4Dn|1A9lQt9`(6=_I@anEi90NgN6zY;0>b?kS3{}3{>Zs9*O8$A_lJr|1!qG)eI%5vOBN(4F z^jtfk&)q^j(9oM$1Lvc4q_>o9F^2|)0Iwv?-~NXJa3WM2f0$PHE9 z`jh(VB)>G{`BJ##nH-fDD@n};CNRpua>=MQSvy*@;wk$IW<5r+rlSEhNltOi3y$&bg*BxWG7SpUO?7LQQj^xvafu z%rgQR`Hn6{lq2>;Ymk(WdkQ|})4i0eASbOj%Yg%)TZio5vt(kfh2KiO@aqB{XH7rr zTMZu^+*-`n*;X{spf#vLHDBC+-%nrBXJ&{KmQgl4_5|ss!cLrAnW;$22N?>*FTZIU zLPg#ob!PLWw>-o~G8CKljx75fb@}T7bE}s4GdBC-s zv`;TUBjTu@1cSN+s}OYxb5-nZE0Wx~s=A!;qyX`z`BYq!NYxu)kUU9MCo-#W=L5MZ z5jQ-(rgJ8U5>W$T6Cdzlj|VarL+!D%b=J6OSFISF^-@Lycx_&8M7_#;a|$LwwiPcg zN;asuZL8?#xusygbd}AK!CVt?XWmb3Cq?H%ra_~y@HQxOREeEVjc!$YIL;mhFCP@) z=-J_S`x5#b&f*(4#Pf&Y`2}jPjL0!%|1;Ztp&hPXM7@O8kancmaraT1H7)Ki!fa93 z^a?e*26Ni|)R;ITpF$D4>Txg2W4N)t(eF{G@<3-ugMYFU)BKPP<$98RSI1uVCjQs~ z&viGuMl>figPrWm4c|~qjKzkd(}d8HsXy=gpe%b^-@KEur7wlOBJ z2Z`RtM;oydnfLSw_UE_XB#YUF*fl3tCY+k_z?iB+N2m2IG(`zk}fnqs^wy|Cql&F+H7aE~o`1H8OO` z4E6YxRO`SUw=i_HerSg;fuQ&huVNL)OVK0Omp7S4S!AvbD&F+*V8k{%J2AfN z#M)eEEhf$4=P&Xk;IkcYc*0!*)+g2r4PiLvOocl2I*xedv4>kg6Z@%MK>rlN0_;2u zv|bnC&0`kJK#BZP`;an^$;Bu!hrFF+`^yn%<>0Sp#Jh`3-k71k%)o~|*?$=veF+&? zBl*7a@-vnxhhcK}cHSqSAZY)0^Jlqm2lfNML|q$RtU$QmDEUR<3x1R1W%YP@Xw$_K z@V2+7ABqqsE)(Ts~aRDa`N%B1aN!xKxf^&uH(7)T|X3Q-&`=)QYKBGku&$$pT zAos!%E|PoC$q!fJmmhPJ&LXNMGU2PGGUuIX zv6^TH&;?%KLp!Q^q6k_{EdO=ABOUHQ@?QRAXQL#BzpWib_NOdwH{{`&Nq zMs&!rX8a=+lY&R^naS2WQ|r_WGGM>S|4?57!rnK;#x)v_OYGIx-vebMfEFel(OuC^ zI|n&JhN$tAKG${H*u+Bt=v^1|3OC>i8WH-1-0#$WA`tPs2JtgbQoU1@QBo{m$wN1E zs=u(HvFc-^D4$i@kt$AS$gxx0L@j8?uf}p*qa~lD(tJ>{8?yK^l6^^ZuLI>%f6K=^N-t5leC3UOL) zi^KK2xF+6&stw0z^G_jy+iSRCa(nQ=$jrd3zOqv~`yLNLS_pnP^(~9ZnE%xwlm({2UXnl6B z>F4_t7f3a{sTAT<39VV45OBo&{Sln~Evp2gSj}iGWO`{(#4U~oinI){+Zy;-!zDyS z%l&8G=u%%@QrI>~KO<(I^v$x)185%0BkT_mgHdj#UGKyrhW#gT)&~}INgG@#Q}#Ez zFrQe*(nG&G2CHv2x%UehpUkw}P=gib=`NjWai`xr-9$LFJfW$)1pUKO?X~<7T69pbt(RVHG8%8UG`yL8~z_pkMoA3b%g-eLEcwnuNl9H<$`firxD( zK9u!?76NP%?{5^;qEs(7L9SQ*gG5g@|E=fDk<6M{r^n(47*z~j+lbi97TT?0^H3{( z_M{d7fr)Z@cj0g%xo2RWH9}?X8j_*ay`~wV;L$nPieHs=^Vzq2q?av9F8ub1kif^*Jt_J5IdCbFWz5&3QZMpx& zlH%i3&MN%ir_ni$YMlsk$qD>{v(lz6f0e~Rf*W8kq|xao+XpQr(`x0iy}19lz$9=3 z=ye2jw4SefAg(1#oO0a{v1V7b*nKUA+{DKyvqj`$jM-Js? z+H8Rx#}K;Vr!sc>t+1h-BhjATUjk^)IPi1Z+Hmw_8C%CP*tlp=yibfRiWTs<+5=2U1AN>b{aGSO`P?6hK*yHt%2t=Os5&H6k$N??@LJfAN7{f=g4~m zA$;#Ez}%xidQ;Rk9@1?>dPi5U6qq?64Ye}8H+>Dz;CA`1xSvBTYf0I3s>QmYSl>~$ zpw6H>Hr!jg^-t4f_zhQ_+Sw(yxZc3wT~?A~e3tP~*{o`R$t

    ?FvUOtu2^pu?+&9 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

    +i6tWWh__ZXAlCjk{JtVIuGWKs6N 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 P;Ag{nPtI*;uJHc>(O9DK literal 229671 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%0A0bd6;+2`j10!2y$Kb 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 z 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 I-Jg~Ff9qpl!2kdN From de748dfffefeba1ba9bcf0c90c538d32c9cb2020 Mon Sep 17 00:00:00 2001 From: Gamer-Kold <84700933+Gamer-Kold@users.noreply.github.com> Date: Sat, 6 May 2023 15:04:40 +0500 Subject: [PATCH 0422/1710] Fixed broken build.zig files. Now works with latest stable compiler (as of commit, latest is 0.10.1) (#3045) Co-authored-by: Talha Qamar --- build.zig | 2 +- src/build.zig | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/build.zig b/build.zig index 2bca6a1d4..042338a6c 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -pub fn build(b: *std.Build) void { +pub fn build(b: *std.build.Builder) void { raylib.build(b); } diff --git a/src/build.zig b/src/build.zig index b39f23ed8..e4c1e0c44 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { +pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.LibExeObjStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", "-D_GNU_SOURCE", @@ -8,11 +8,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; - const raylib = b.addStaticLibrary(.{ - .name = "raylib", - .target = target, - .optimize = optimize, - }); + const raylib = b.addStaticLibrary("raylib", null); raylib.linkLibC(); raylib.addIncludePath(srcdir ++ "/external/glfw/include"); @@ -103,7 +99,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built return raylib; } -pub fn build(b: *std.Build) void { +pub fn build(b: *std.build.Builder) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which // means any target is allowed, and the default is native. Other options @@ -112,10 +108,10 @@ pub fn build(b: *std.Build) void { // Standard optimization options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not // set a preferred release mode, allowing the user to decide how to optimize. - const optimize = b.standardOptimizeOption(.{}); + // const optimize = b.standardReleaseOptions(); - const lib = addRaylib(b, target, optimize); - lib.installHeader("src/raylib.h", "raylib.h"); + const lib = addRaylib(b, target); + b.installFile("src/raylib.h", "raylib.h"); b.installArtifact(lib); } From 53b7b26c45867e8b373ae1f9ab302117d3ba047d Mon Sep 17 00:00:00 2001 From: Alfred Reinold Baudisch Date: Sun, 7 May 2023 10:33:14 +0200 Subject: [PATCH 0423/1710] Added ModelAnimation.name, initially with GLTF animation names loaded (#3044) --- examples/models/models_loading_gltf.c | 1 + src/raylib.h | 1 + src/rmodels.c | 3 +++ 3 files changed, 5 insertions(+) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index d8b34efed..d5ebff237 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -86,6 +86,7 @@ int main(void) EndMode3D(); DrawText("Use the UP/DOWN arrow keys to switch animation", 10, 10, 20, GRAY); + DrawText(TextFormat("Animation: %s", anim.name), 10, GetScreenHeight() - 20, 10, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index 6e5d023bf..2a5a2f63f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -402,6 +402,7 @@ typedef struct ModelAnimation { int frameCount; // Number of animation frames BoneInfo *bones; // Bones information (skeleton) Transform **framePoses; // Poses array by frame + char name[32]; // Animation name } ModelAnimation; // Ray, ray for raycasting diff --git a/src/rmodels.c b/src/rmodels.c index 34352ec1b..1aa045df2 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5375,6 +5375,9 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in animDuration = (t > animDuration)? t : animDuration; } + 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); animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); From 05e71f990c3e5fdbc4f7a7888d3af534cc2bf113 Mon Sep 17 00:00:00 2001 From: Scott Helvick <4016577+shelvick@users.noreply.github.com> Date: Sun, 7 May 2023 11:21:47 -0700 Subject: [PATCH 0424/1710] Version bump for CL bindings (#3049) --- BINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 8f293e78e..c66f6a34d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -11,7 +11,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib-cs | **4.5** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | -| claylib/wrap | 4.2 | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | +| claylib/wrap | **4.5** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | @@ -80,7 +80,7 @@ These are utility wrappers for specific languages, they are not required to use | name | raylib version | language | license | repo | |:------------------:|:-------------: | :--------:|:-------:|:-------------------------------------------------------------| | raylib-cpp | **4.5** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | -| claylib | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | +| claylib | **4.5** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | ### Older or Unmaintained Language Bindings These are older raylib bindings that are more than 2 versions old or have not been maintained. From 204c6765bd12a63a0cab90ee536bdec321b80bee Mon Sep 17 00:00:00 2001 From: Shoozza Date: Sun, 7 May 2023 20:22:37 +0200 Subject: [PATCH 0425/1710] Fix vs code project (#3048) * Modify remove trailing whitespace * Fix invalid SetCameraMode call and missing argument to UpdateCamera for VSCode project --- projects/VSCode/Makefile | 8 +++---- projects/VSCode/Makefile.Android | 38 ++++++++++++++++---------------- projects/VSCode/main.c | 8 +++---- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 9b8cbea1f..9ab042e8f 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -166,7 +166,7 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif ifeq ($(PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler - # WARNING: To compile to HTML5, code must be redesigned + # WARNING: To compile to HTML5, code must be redesigned # to use emscripten.h and emscripten_set_main_loop() CC = emcc endif @@ -301,12 +301,12 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) # Libraries for Debian GNU/Linux desktop compiling # NOTE: Required packages: libegl1-mesa-dev LDLIBS = -lraylib -lGL -lm -lpthread -ldl -lrt - + # On X11 requires also below libraries LDLIBS += -lX11 # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor - + # On Wayland windowing system, additional libraries requires ifeq ($(USE_WAYLAND_DISPLAY),TRUE) LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon @@ -358,7 +358,7 @@ OBJS ?= main.c # For Android platform we call a custom Makefile.Android ifeq ($(PLATFORM),PLATFORM_ANDROID) - MAKEFILE_PARAMS = -f Makefile.Android + MAKEFILE_PARAMS = -f Makefile.Android export PROJECT_NAME export SRC_DIR else diff --git a/projects/VSCode/Makefile.Android b/projects/VSCode/Makefile.Android index 29d437b1b..7e41ea52f 100644 --- a/projects/VSCode/Makefile.Android +++ b/projects/VSCode/Makefile.Android @@ -50,7 +50,7 @@ PROJECT_BUILD_PATH ?= android.$(PROJECT_NAME) PROJECT_RESOURCES_PATH ?= resources PROJECT_SOURCE_FILES ?= raylib_game.c -# Some source files are placed in directories, when compiling to some +# Some source files are placed in directories, when compiling to some # output directory other than source, that directory must pre-exist. # Here we get a list of required folders that need to be created on # code output folder $(PROJECT_BUILD_PATH)\obj to avoid GCC errors. @@ -77,7 +77,7 @@ RAYLIB_LIB_PATH = $(RAYLIB_PATH)\src # Shared libs must be added to APK if required # NOTE: Generated NativeLoader.java automatically load those libraries ifeq ($(RAYLIB_LIBTYPE),SHARED) - PROJECT_SHARED_LIBS = lib/$(ANDROID_ARCH_NAME)/libraylib.so + PROJECT_SHARED_LIBS = lib/$(ANDROID_ARCH_NAME)/libraylib.so endif # Compiler and archiver @@ -109,8 +109,8 @@ CFLAGS += -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=$(ANDROID_API_VERSION) INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external/android/native_app_glue # Linker options -LDFLAGS = -Wl,-soname,lib$(PROJECT_LIBRARY_NAME).so -Wl,--exclude-libs,libatomic.a -LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings +LDFLAGS = -Wl,-soname,lib$(PROJECT_LIBRARY_NAME).so -Wl,--exclude-libs,libatomic.a +LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings # Force linking of library module to define symbol LDFLAGS += -u ANativeActivity_onCreate # Library paths containing required libs @@ -141,7 +141,7 @@ all: create_temp_project_dirs \ # Create required temp directories for APK building create_temp_project_dirs: - if not exist $(PROJECT_BUILD_PATH) mkdir $(PROJECT_BUILD_PATH) + if not exist $(PROJECT_BUILD_PATH) mkdir $(PROJECT_BUILD_PATH) if not exist $(PROJECT_BUILD_PATH)\obj mkdir $(PROJECT_BUILD_PATH)\obj if not exist $(PROJECT_BUILD_PATH)\src mkdir $(PROJECT_BUILD_PATH)\src if not exist $(PROJECT_BUILD_PATH)\src\com mkdir $(PROJECT_BUILD_PATH)\src\com @@ -163,15 +163,15 @@ create_temp_project_dirs: define create_dir if not exist $(PROJECT_BUILD_PATH)\obj\$(1) mkdir $(PROJECT_BUILD_PATH)\obj\$(1) endef - + # Copy required shared libs for integration into APK # NOTE: If using shared libs they are loaded by generated NativeLoader.java copy_project_required_libs: ifeq ($(RAYLIB_LIBTYPE),SHARED) - copy /Y $(RAYLIB_LIB_PATH)\libraylib.so $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.so + copy /Y $(RAYLIB_LIB_PATH)\libraylib.so $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.so endif ifeq ($(RAYLIB_LIBTYPE),STATIC) - copy /Y $(RAYLIB_LIB_PATH)\libraylib.a $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.a + copy /Y $(RAYLIB_LIB_PATH)\libraylib.a $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.a endif # Copy project required resources: strings.xml, icon.png, assets @@ -195,10 +195,10 @@ generate_loader_script: ifeq ($(RAYLIB_LIBTYPE),SHARED) @echo System.loadLibrary("raylib"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java endif - @echo System.loadLibrary("$(PROJECT_LIBRARY_NAME)"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java + @echo System.loadLibrary("$(PROJECT_LIBRARY_NAME)"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java @echo } >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java @echo } >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java - + # Generate AndroidManifest.xml with all the required options # NOTE: Probably not the bet way to generate this file... but it works. generate_android_manifest: @@ -225,7 +225,7 @@ generate_android_manifest: # Generate storekey for APK signing: $(PROJECT_NAME).keystore # NOTE: Configure here your Distinguished Names (-dname) if required! -generate_apk_keystore: +generate_apk_keystore: if not exist $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore $(JAVA_HOME)/bin/keytool -genkeypair -validity 1000 -dname "CN=$(APP_COMPANY_NAME),O=Android,C=ES" -keystore $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore -storepass $(APP_KEYSTORE_PASS) -keypass $(APP_KEYSTORE_PASS) -alias $(PROJECT_NAME)Key -keyalg RSA # Config project package and resource using AndroidManifest.xml and res/values/strings.xml @@ -238,16 +238,16 @@ compile_native_app_glue: $(CC) -c $(RAYLIB_PATH)/src/external/android/native_app_glue/android_native_app_glue.c -o $(PROJECT_BUILD_PATH)/obj/native_app_glue.o $(CFLAGS) $(AR) rcs $(PROJECT_BUILD_PATH)/obj/libnative_app_glue.a $(PROJECT_BUILD_PATH)/obj/native_app_glue.o -# Compile project code into a shared library: lib/lib$(PROJECT_LIBRARY_NAME).so +# Compile project code into a shared library: lib/lib$(PROJECT_LIBRARY_NAME).so compile_project_code: $(OBJS) $(CC) -o $(PROJECT_BUILD_PATH)/lib/$(ANDROID_ARCH_NAME)/lib$(PROJECT_LIBRARY_NAME).so $(OBJS) -shared $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) # Compile all .c files required into object (.o) files # NOTE: Those files will be linked into a shared library $(PROJECT_BUILD_PATH)/obj/%.o:%.c - $(CC) -c $^ -o $@ $(INCLUDE_PATHS) $(CFLAGS) --sysroot=$(ANDROID_TOOLCHAIN)/sysroot - -# Compile project .java code into .class (Java bytecode) + $(CC) -c $^ -o $@ $(INCLUDE_PATHS) $(CFLAGS) --sysroot=$(ANDROID_TOOLCHAIN)/sysroot + +# Compile project .java code into .class (Java bytecode) compile_project_class: $(JAVA_HOME)/bin/javac -verbose -source 1.7 -target 1.7 -d $(PROJECT_BUILD_PATH)/obj -bootclasspath $(JAVA_HOME)/jre/lib/rt.jar -classpath $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar;$(PROJECT_BUILD_PATH)/obj -sourcepath $(PROJECT_BUILD_PATH)/src $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/R.java $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java @@ -263,11 +263,11 @@ create_project_apk_package: $(ANDROID_BUILD_TOOLS)/aapt package -f -M $(PROJECT_BUILD_PATH)/AndroidManifest.xml -S $(PROJECT_BUILD_PATH)/res -A $(PROJECT_BUILD_PATH)/assets -I $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar -F $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_BUILD_PATH)/bin cd $(PROJECT_BUILD_PATH) && $(ANDROID_BUILD_TOOLS)/aapt add bin/$(PROJECT_NAME).unsigned.apk lib/$(ANDROID_ARCH_NAME)/lib$(PROJECT_LIBRARY_NAME).so $(PROJECT_SHARED_LIBS) -# Create signed APK package using generated Key: bin/$(PROJECT_NAME).signed.apk +# Create signed APK package using generated Key: bin/$(PROJECT_NAME).signed.apk sign_project_apk_package: $(JAVA_HOME)/bin/jarsigner -keystore $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore -storepass $(APP_KEYSTORE_PASS) -keypass $(APP_KEYSTORE_PASS) -signedjar $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).signed.apk $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_NAME)Key -# Create zip-aligned APK package: $(PROJECT_NAME).apk +# Create zip-aligned APK package: $(PROJECT_NAME).apk zipalign_project_apk_package: $(ANDROID_BUILD_TOOLS)/zipalign -f 4 $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).signed.apk $(PROJECT_NAME).apk @@ -275,7 +275,7 @@ zipalign_project_apk_package: # NOTE: Use -e (emulator) or -d (device) parameters if required install: $(ANDROID_PLATFORM_TOOLS)/adb install --abi $(ANDROID_ARCH_NAME) -rds $(PROJECT_NAME).apk - + # Check supported ABI for the device (armeabi-v7a, arm64-v8a, x86, x86_64) check_device_abi: $(ANDROID_PLATFORM_TOOLS)/adb shell getprop ro.product.cpu.abi @@ -284,7 +284,7 @@ check_device_abi: logcat: $(ANDROID_PLATFORM_TOOLS)/adb logcat -c $(ANDROID_PLATFORM_TOOLS)/adb logcat raylib:V *:S - + # Install and monitorize $(PROJECT_NAME).apk to default emulator/device deploy: $(ANDROID_PLATFORM_TOOLS)/adb install -r $(PROJECT_NAME).apk diff --git a/projects/VSCode/main.c b/projects/VSCode/main.c index 0794672d0..f37b3b28d 100644 --- a/projects/VSCode/main.c +++ b/projects/VSCode/main.c @@ -39,7 +39,7 @@ static void UpdateDrawFrame(void); // Update and draw one frame //---------------------------------------------------------------------------------- // Main entry point //---------------------------------------------------------------------------------- -int main() +int main() { // Initialization //-------------------------------------------------------------------------------------- @@ -53,8 +53,6 @@ int main() camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; camera.fovy = 60.0f; camera.projection = CAMERA_PERSPECTIVE; - - SetCameraMode(camera, CAMERA_ORBITAL); //-------------------------------------------------------------------------------------- @@ -84,7 +82,7 @@ static void UpdateDrawFrame(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); //---------------------------------------------------------------------------------- // Draw @@ -107,4 +105,4 @@ static void UpdateDrawFrame(void) EndDrawing(); //---------------------------------------------------------------------------------- -} \ No newline at end of file +} From 81e2c970950cc9a671b270376065ec6012c28c88 Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Mon, 8 May 2023 15:00:21 +0100 Subject: [PATCH 0426/1710] Update BINDINGS.md (#3050) Update C3 binding to use Raylib version 4.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index c66f6a34d..035a44406 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -15,7 +15,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | -| raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | +| raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | 4.2 | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | From fe595d60f7c15d93e03a5c82c90620dddafa7fc7 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 8 May 2023 18:57:36 +0200 Subject: [PATCH 0427/1710] Remove trailing spaces --- src/external/rl_gputex.h | 2 +- src/raymath.h | 4 ++-- src/utils.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index f7cc00ecf..fa39fe29c 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -9,7 +9,7 @@ * * 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 diff --git a/src/raymath.h b/src/raymath.h index fbb970486..47728ff69 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -3,10 +3,10 @@ * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions * * CONVENTIONS: -* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all +* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all * math operations performed by the library consider the structure as it was column-major * It is like transposed versions of the matrices are used for all the maths -* It benefits some functions making them cache-friendly and also avoids matrix +* It benefits some functions making them cache-friendly and also avoids matrix * transpositions sometimes required by OpenGL * Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3] * - Functions are always self-contained, no function use another raymath function inside, diff --git a/src/utils.c b/src/utils.c index 4175fb693..01ca235fa 100644 --- a/src/utils.c +++ b/src/utils.c @@ -298,7 +298,7 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * 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; } - + byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, size); byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName); From 152262dbfca8904e0250662dcee3985f57d7f9a3 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 May 2023 12:48:17 +0200 Subject: [PATCH 0428/1710] Update cgltf.h --- src/external/cgltf.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/external/cgltf.h b/src/external/cgltf.h index a4d9c72de..d2a909777 100644 --- a/src/external/cgltf.h +++ b/src/external/cgltf.h @@ -65,7 +65,8 @@ * * `cgltf_num_components` is a tiny utility that tells you the dimensionality of * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate - * the necessary amount of memory. + * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for + * similar purposes. * * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element @@ -837,6 +838,8 @@ cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size i cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); cgltf_size cgltf_num_components(cgltf_type type); +cgltf_size cgltf_component_size(cgltf_component_type component_type); +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); @@ -1488,8 +1491,6 @@ cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, return cgltf_result_success; } -static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); - static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) { char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; @@ -2217,7 +2218,7 @@ static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_typ case cgltf_component_type_r_32u: return *((const uint32_t*) in); case cgltf_component_type_r_32f: - return (cgltf_size)*((const float*) in); + return (cgltf_size)((cgltf_ssize)*((const float*) in)); case cgltf_component_type_r_8u: return *((const uint8_t*) in); default: @@ -2253,8 +2254,6 @@ static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_ty return (cgltf_float)cgltf_component_read_integer(in, component_type); } -static cgltf_size cgltf_component_size(cgltf_component_type component_type); - static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) { cgltf_size num_components = cgltf_num_components(type); @@ -5965,7 +5964,7 @@ cgltf_size cgltf_num_components(cgltf_type type) { } } -static cgltf_size cgltf_component_size(cgltf_component_type component_type) { +cgltf_size cgltf_component_size(cgltf_component_type component_type) { switch (component_type) { case cgltf_component_type_r_8: @@ -5983,7 +5982,7 @@ static cgltf_size cgltf_component_size(cgltf_component_type component_type) { } } -static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) { cgltf_size component_size = cgltf_component_size(component_type); if (type == cgltf_type_mat2 && component_size == 1) From af4b97a301c39ca8df628b1889b8f6acd393a0f5 Mon Sep 17 00:00:00 2001 From: manuel5975p Date: Wed, 10 May 2023 19:19:59 +0200 Subject: [PATCH 0429/1710] Update GetCollisionRec (#3052) * Update rshapes.c Add a much more efficient GetCollisionRec implementation * Update GetCollisionRec Replace macros with ternary operators --- src/rshapes.c | 85 +++++++++++++-------------------------------------- 1 file changed, 22 insertions(+), 63 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 45cf6ac67..f7546cdf4 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1756,70 +1756,29 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol } // Get collision rectangle for two rectangles collision -Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) -{ - Rectangle rec = { 0, 0, 0, 0 }; - - if (CheckCollisionRecs(rec1, rec2)) - { - float dxx = fabsf(rec1.x - rec2.x); - float dyy = fabsf(rec1.y - rec2.y); - - if (rec1.x <= rec2.x) - { - if (rec1.y <= rec2.y) - { - rec.x = rec2.x; - rec.y = rec2.y; - rec.width = rec1.width - dxx; - rec.height = rec1.height - dyy; - } - else - { - rec.x = rec2.x; - rec.y = rec1.y; - rec.width = rec1.width - dxx; - rec.height = rec2.height - dyy; - } - } - else - { - if (rec1.y <= rec2.y) - { - rec.x = rec1.x; - rec.y = rec2.y; - rec.width = rec2.width - dxx; - rec.height = rec1.height - dyy; - } - else - { - rec.x = rec1.x; - rec.y = rec1.y; - rec.width = rec2.width - dxx; - rec.height = rec2.height - dyy; - } - } - - if (rec1.width > rec2.width) - { - if (rec.width >= rec2.width) rec.width = rec2.width; - } - else - { - if (rec.width >= rec1.width) rec.width = rec1.width; - } - - if (rec1.height > rec2.height) - { - if (rec.height >= rec2.height) rec.height = rec2.height; - } - else - { - if (rec.height >= rec1.height) rec.height = rec1.height; - } +Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2){ + Rectangle overlap; + float left = ((rec1.x) > (rec2.x) ? (rec1.x) : (rec2.x)); + float right1 = rec1.x + rec1.width; + float right2 = rec2.x + rec2.width; + float right = ((right1) < (right2) ? (right1) : (right2)); + float top = ((rec1.y) > (rec2.y) ? (rec1.y) : (rec2.y)); + float bottom1 = rec1.y + rec1.height; + float bottom2 = rec2.y + rec2.height; + float bottom = ((bottom1) < (bottom2) ? (bottom1) : (bottom2)); + if (left < right && top < bottom){ + overlap.x = (left); + overlap.y = (top); + overlap.width = (right - left ); + overlap.height = (bottom - top); } - - return rec; + else{ + overlap.x = 0; + overlap.y = 0; + overlap.width = 0; + overlap.height = 0; + } + return overlap; } //---------------------------------------------------------------------------------- From 452e3b494cd6bceaab290d0f5f23bff9c7887a1b Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 May 2023 19:25:12 +0200 Subject: [PATCH 0430/1710] REVIEWED: `GetCollisionRec()` --- src/rshapes.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index f7546cdf4..278886423 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1756,28 +1756,27 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol } // Get collision rectangle for two rectangles collision -Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2){ - Rectangle overlap; - float left = ((rec1.x) > (rec2.x) ? (rec1.x) : (rec2.x)); +Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) +{ + Rectangle overlap = { 0 }; + + float left = (rec1.x > rec2.x)? rec1.x : rec2.x; float right1 = rec1.x + rec1.width; float right2 = rec2.x + rec2.width; - float right = ((right1) < (right2) ? (right1) : (right2)); - float top = ((rec1.y) > (rec2.y) ? (rec1.y) : (rec2.y)); + float right = (right1 < right2)? right1 : right2; + float top = (rec1.y > rec2.y)? rec1.y : rec2.y; float bottom1 = rec1.y + rec1.height; float bottom2 = rec2.y + rec2.height; - float bottom = ((bottom1) < (bottom2) ? (bottom1) : (bottom2)); - if (left < right && top < bottom){ - overlap.x = (left); - overlap.y = (top); - overlap.width = (right - left ); - overlap.height = (bottom - top); - } - else{ - overlap.x = 0; - overlap.y = 0; - overlap.width = 0; - overlap.height = 0; + float bottom = (bottom1 < bottom2)? bottom1 : bottom2; + + if ((left < right) && (top < bottom)) + { + overlap.x = left; + overlap.y = top; + overlap.width = right - left; + overlap.height = bottom - top; } + return overlap; } From 5978358e5877b0033584334c5bf6ec7d8cf22ae8 Mon Sep 17 00:00:00 2001 From: JupiterRider <60042618+JupiterRider@users.noreply.github.com> Date: Thu, 11 May 2023 19:48:53 +0200 Subject: [PATCH 0431/1710] Update BINDINGS.md (#3053) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 035a44406..a10e5860f 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -25,7 +25,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | -| raylib-go | 4.2 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | +| raylib-go | 4.5 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | | h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | From cc17a7656c04e114ca67771f89b0fb8f493f8a1f Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Sun, 14 May 2023 21:14:16 +0200 Subject: [PATCH 0432/1710] Update build.zig be be able to build with current zig master (#3064) --- src/build.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/build.zig b/src/build.zig index e4c1e0c44..58cf84fec 100644 --- a/src/build.zig +++ b/src/build.zig @@ -8,7 +8,11 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; - const raylib = b.addStaticLibrary("raylib", null); + const raylib = b.addStaticLibrary(std.Build.StaticLibraryOptions{ + .name = "raylib", + .target = target, + .optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSafe }), + }); raylib.linkLibC(); raylib.addIncludePath(srcdir ++ "/external/glfw/include"); From 818312683ee92efde550f7eaa88fcceb773eaf0b Mon Sep 17 00:00:00 2001 From: lesleyrs <19632758+lesleyrs@users.noreply.github.com> Date: Sun, 14 May 2023 21:15:32 +0200 Subject: [PATCH 0433/1710] update cmake example project (#3062) * update cmake example project * off is the correct one --- projects/CMake/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index cfd924fba..cc606a4a2 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -5,12 +5,13 @@ project(example) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Dependencies -set(RAYLIB_VERSION 4.2.0) +set(RAYLIB_VERSION 4.5.0) find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED if (NOT raylib_FOUND) # If there's none, fetch and build raylib include(FetchContent) FetchContent_Declare( raylib + DOWNLOAD_EXTRACT_TIMESTAMP OFF URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz ) FetchContent_GetProperties(raylib) From 26a3536958e9d4d641edbff0530c41625871b6be Mon Sep 17 00:00:00 2001 From: hamyy Date: Mon, 15 May 2023 05:20:43 +1000 Subject: [PATCH 0434/1710] GetCurrentMonitor() bugfix (#3058) * GetCurrentMonitor() bugfix * GetCurrentMonitor() bugfix --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 476ea8e2f..881739c73 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1785,7 +1785,7 @@ int GetCurrentMonitor(void) monitor = monitors[i]; glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); - if (x >= mx && x <= (mx + width) && y >= my && y <= (my + height)) + if (x >= mx && x < (mx + width) && y >= my && y < (my + height)) { index = i; break; From 6b92d71ea1c4e3072b26f25e7b8bd1d1aa8e781f Mon Sep 17 00:00:00 2001 From: Gamer-Kold <84700933+Gamer-Kold@users.noreply.github.com> Date: Mon, 15 May 2023 14:23:36 +0500 Subject: [PATCH 0435/1710] Reverted commits that deprecated the build.zig files, and added a note to all of them stating version of zig they were using (#3060) * Revert "Fixed broken build.zig files. Now works with latest stable compiler (as of commit, latest is 0.10.1) (#3045)" This reverts commit de748dfffefeba1ba9bcf0c90c538d32c9cb2020 so that zig build script works with master branch of zig. * Added a note to build.zig files that denotes what version of zig they have been tested with. * Standardised the note in the build.zig files --- build.zig | 3 ++- examples/build.zig | 1 + src/build.zig | 15 ++++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/build.zig b/build.zig index 042338a6c..12c0513f6 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -pub fn build(b: *std.build.Builder) void { +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +pub fn build(b: *std.Build) void { raylib.build(b); } diff --git a/examples/build.zig b/examples/build.zig index f94a3b4bf..6e13ab3da 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); diff --git a/src/build.zig b/src/build.zig index 58cf84fec..48e93c326 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); -pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.LibExeObjStep { +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", "-D_GNU_SOURCE", @@ -8,10 +9,10 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; - const raylib = b.addStaticLibrary(std.Build.StaticLibraryOptions{ + const raylib = b.addStaticLibrary(.{ .name = "raylib", .target = target, - .optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSafe }), + .optimize = optimize, }); raylib.linkLibC(); @@ -103,7 +104,7 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. return raylib; } -pub fn build(b: *std.build.Builder) void { +pub fn build(b: *std.Build) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which // means any target is allowed, and the default is native. Other options @@ -112,10 +113,10 @@ pub fn build(b: *std.build.Builder) void { // Standard optimization options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not // set a preferred release mode, allowing the user to decide how to optimize. - // const optimize = b.standardReleaseOptions(); + const optimize = b.standardOptimizeOption(.{}); - const lib = addRaylib(b, target); - b.installFile("src/raylib.h", "raylib.h"); + const lib = addRaylib(b, target, optimize); + lib.installHeader("src/raylib.h", "raylib.h"); b.installArtifact(lib); } From 3438325e7d657e2eadd71226c147f9f0f6ce5f55 Mon Sep 17 00:00:00 2001 From: Crynux <43756150+crynux@users.noreply.github.com> Date: Mon, 15 May 2023 03:24:37 -0600 Subject: [PATCH 0436/1710] Update rmodels.c; free fileData for LoadModelAnimationsGLTF (#3065) fileData wasn't freed for LoadModelAnimationsGLTF causing a memory leak. Added UnloadFileData line, freeing it. --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 1aa045df2..c4073eed3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5434,7 +5434,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in cgltf_free(data); } - + UnloadFileData(fileData); return animations; } #endif From 3f8ef4e05f1bb4ca61c43dbcb87f9a9bc0d26f0d Mon Sep 17 00:00:00 2001 From: Pixel Phobic <65147646+PixelPhobicGames@users.noreply.github.com> Date: Mon, 15 May 2023 02:25:44 -0700 Subject: [PATCH 0437/1710] Updated (rcamera) To Add Analog Stick Cam Controls (#3066) I Noticed While Writing Some code for My Game im Making that there Isn't an Easy way to control the Camera With The Analog Sticks on Controller. I Added a Couple Lines to the UpdateCamera Function :) --- src/rcamera.h | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 0b9b8076a..22a468cfb 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -447,14 +447,28 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); - CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); - // Camera movement - if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + + if (!IsGamepadAvailable(0)){ + CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + + if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + } + else { + // Simple Controller Support + CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.75f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.75f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.75f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.75f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + } + //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); } From e17cf9ecd7e770e349a74211bc61170f4d23f8e8 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 15 May 2023 11:30:31 +0200 Subject: [PATCH 0438/1710] REVIEWED: Formating --- src/rcamera.h | 10 ++++++---- src/rcore.c | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 22a468cfb..c1b048e01 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -448,8 +448,9 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); // Camera movement - - if (!IsGamepadAvailable(0)){ + if (!IsGamepadAvailable(0)) + { + // Mouse/Keyboard support CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); @@ -458,8 +459,9 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); } - else { - // Simple Controller Support + else + { + // Gamepad controller support CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); diff --git a/src/rcore.c b/src/rcore.c index 881739c73..31a06f7ce 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1785,7 +1785,10 @@ int GetCurrentMonitor(void) monitor = monitors[i]; glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); - if (x >= mx && x < (mx + width) && y >= my && y < (my + height)) + if ((x >= mx) && + (x < (mx + width)) && + (y >= my) && + (y < (my + height))) { index = i; break; From 675efbda3b19aa656bc935308013f0af7466c894 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 16 May 2023 09:00:44 +0000 Subject: [PATCH 0439/1710] Fix Android app freeze after calling CloseWindow() (#3067) Fixed that the Android application was not closed properly after calling `CloseWindow()` and continued to run. --- src/rcore.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 31a06f7ce..b41322257 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -718,6 +718,23 @@ void android_main(struct android_app *app) // NOTE: Return codes != 0 are skipped (void)main(1, (char *[]) { arg0, NULL }); + + // Finish native activity + ANativeActivity_finish(CORE.Android.app->activity); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Wait for app events to close + while (!CORE.Android.app->destroyRequested) { + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) { + if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + } + } + + // WARNING: Check for deallocation and ensure no other processes are running from the application. + exit(0); // Closes the application completely without going through Java } // NOTE: Add this to header (if apps really need it) @@ -5731,8 +5748,9 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_STOP: break; case APP_CMD_DESTROY: { - // TODO: Finish activity? - //ANativeActivity_finish(CORE.Android.app->activity); + // NOTE 1: Call ANativeActivity_finish again to free resources unconditionally. + // NOTE 2: You can deallocate other things that are NativeActivity related here. + ANativeActivity_finish(CORE.Android.app->activity); } break; case APP_CMD_CONFIG_CHANGED: { From c3f049fd740fca6a1cb2eb78c90fb9f20c6ecb34 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 16 May 2023 11:02:00 +0200 Subject: [PATCH 0440/1710] review formatting --- src/rcore.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b41322257..127abcf38 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -727,14 +727,16 @@ void android_main(struct android_app *app) int pollEvents = 0; // Wait for app events to close - while (!CORE.Android.app->destroyRequested) { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) { + while (!CORE.Android.app->destroyRequested) + { + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) + { if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); } } - // WARNING: Check for deallocation and ensure no other processes are running from the application. - exit(0); // Closes the application completely without going through Java + // WARNING: Check for deallocation and ensure no other processes are running from the application + exit(0); // Closes the application completely without going through Java } // NOTE: Add this to header (if apps really need it) From 3a3e672804d7c0efb429d45b22554b357c0dc11d Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 17 May 2023 11:54:32 +0200 Subject: [PATCH 0441/1710] UPDATE: miniaudio v0.11.12 --> v0.11.16 --- src/external/miniaudio.h | 3964 ++++++++++++++++++++++---------------- 1 file changed, 2338 insertions(+), 1626 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 74d584153..35eaafdcf 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.12 - TBD +miniaudio - v0.11.16 - 2023-05-15 David Reid - mackron@gmail.com @@ -398,13 +398,13 @@ the be started and/or stopped at a specific time. This can be done with the foll ``` The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`. -The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if -required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` -before anything will play: +engine. The current global time time in PCM frames can be retrieved with +`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with +`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling +a start time still requires an explicit call to `ma_sound_start()` before anything will play: ```c - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2); + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); ma_sound_start(&sound); ``` @@ -462,6 +462,9 @@ dependencies. See below for platform-specific details. Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. +If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, +etc. you need to link with `-latomic`. + 2.1. Windows ------------ @@ -946,7 +949,7 @@ base object (`ma_data_source_base`): // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. } - static g_my_data_source_vtable = + static ma_data_source_vtable g_my_data_source_vtable = { my_data_source_read, my_data_source_seek, @@ -1286,6 +1289,10 @@ file paths which means there's a small chance you might encounter a name collisi issue, you'll need to use a different name for one of the colliding file paths, or just not load from files and instead load from a data source. +You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this +only works for sounds that were initialized with `ma_sound_init_from_file()` and without the +`MA_SOUND_FLAG_STREAM` flag. + When you initialize a sound, if you specify a sound group the sound will be attached to that group automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. If you would instead rather leave the sound unattached by default, you can can specify the @@ -1425,19 +1432,19 @@ can be useful to schedule a sound to start or stop: ```c // Start the sound in 1 second from now. - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); // Stop the sound in 2 seconds from now. - ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); + ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); ``` Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before anything will play. The time is specified in global time which is controlled by the engine. You can get the engine's -current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as -audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be -resynchronized for some reason. +current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented +automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` +in case it needs to be resynchronized for some reason. To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will take the scheduled start and stop times into account. @@ -1446,7 +1453,25 @@ Whether or not a sound should loop can be controlled with `ma_sound_set_looping( be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping -sound this should never return true. +sound this should never return true. Alternatively, you can configure a callback that will be fired +when the sound reaches the end. Note that the callback is fired from the audio thread which means +you cannot be uninitializing sound from the callback. To set the callback you can use +`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it +into the config like so: + + ```c + soundConfig.endCallback = my_end_callback; + soundConfig.pEndCallbackUserData = pMyEndCallbackUserData; + ``` + +The end callback is declared like so: + + ```c + void my_end_callback(void* pUserData, ma_sound* pSound) + { + ... + } + ``` Internally a sound wraps around a data source. Some APIs exist to control the underlying data source, mainly for convenience: @@ -3697,7 +3722,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 12 +#define MA_VERSION_REVISION 16 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3777,7 +3802,18 @@ typedef double ma_double; typedef void* ma_handle; typedef void* ma_ptr; -typedef void (* ma_proc)(void); + +/* +ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting +between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get +warning C4191 about "type cast between incompatible function types". To work around this I'm going +to use a different data type depending on the compiler. +*/ +#if defined(__GNUC__) +typedef void (*ma_proc)(void); +#else +typedef void* ma_proc; +#endif #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) typedef ma_uint16 wchar_t; @@ -3796,7 +3832,7 @@ typedef ma_uint16 wchar_t; /* Platform/backend detection. */ -#ifdef _WIN32 +#if defined(_WIN32) || defined(__COSMOPOLITAN__) #define MA_WIN32 #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) #define MA_WIN32_UWP @@ -3805,7 +3841,8 @@ typedef ma_uint16 wchar_t; #else #define MA_WIN32_DESKTOP #endif -#else +#endif +#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ #define MA_POSIX /* @@ -3826,31 +3863,39 @@ typedef ma_uint16 wchar_t; typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; #endif - #ifdef __unix__ + #if defined(__unix__) #define MA_UNIX - #ifdef __ORBIS__ - #define MA_ORBIS - #elif defined(__PROSPERO__) - #define MA_PROSPERO - #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif #endif - #ifdef __linux__ + #if defined(__linux__) #define MA_LINUX #endif - #ifdef __APPLE__ + #if defined(__APPLE__) #define MA_APPLE #endif - #ifdef __ANDROID__ + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MA_BSD + #endif + #if defined(__ANDROID__) #define MA_ANDROID #endif - #ifdef __EMSCRIPTEN__ + #if defined(__EMSCRIPTEN__) #define MA_EMSCRIPTEN #endif + #if defined(__ORBIS__) + #define MA_ORBIS + #endif + #if defined(__PROSPERO__) + #define MA_PROSPERO + #endif #if defined(__NX__) #define MA_NX #endif + #if defined(__BEOS__) || defined(__HAIKU__) + #define MA_BEOS + #endif + #if defined(__HAIKU__) + #define MA_HAIKU + #endif #endif #if defined(__has_c_attribute) @@ -3927,6 +3972,21 @@ typedef ma_uint16 wchar_t; /* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ #define MA_SIMD_ALIGNMENT 32 +/* +Special wchar_t type to ensure any structures in the public sections that reference it have a +consistent size across all platforms. + +On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use +wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all +platforms. +*/ +#if !defined(MA_POSIX) && defined(MA_WIN32) +typedef wchar_t ma_wchar_win32; +#else +typedef ma_uint16 ma_wchar_win32; +#endif + + /* Logging Levels @@ -3973,7 +4033,7 @@ versions of Visual Studio, which I've confirmed with at least VC6. */ #if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) #include - #define MA_ATOMIC(alignment, type) alignas(alignment) type + #define MA_ATOMIC(alignment, type) _Alignas(alignment) type #else #if defined(__GNUC__) /* GCC-style compilers. */ @@ -4304,61 +4364,57 @@ MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) typedef ma_uint32 ma_spinlock; #ifndef MA_NO_THREADING -/* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ -typedef enum -{ - ma_thread_priority_idle = -5, - ma_thread_priority_lowest = -4, - ma_thread_priority_low = -3, - ma_thread_priority_normal = -2, - ma_thread_priority_high = -1, - ma_thread_priority_highest = 0, - ma_thread_priority_realtime = 1, - ma_thread_priority_default = 0 -} ma_thread_priority; + /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ + typedef enum + { + ma_thread_priority_idle = -5, + ma_thread_priority_lowest = -4, + ma_thread_priority_low = -3, + ma_thread_priority_normal = -2, + ma_thread_priority_high = -1, + ma_thread_priority_highest = 0, + ma_thread_priority_realtime = 1, + ma_thread_priority_default = 0 + } ma_thread_priority; -#if defined(MA_WIN32) -typedef ma_handle ma_thread; -#endif -#if defined(MA_POSIX) -typedef ma_pthread_t ma_thread; -#endif + #if defined(MA_POSIX) + typedef ma_pthread_t ma_thread; + #elif defined(MA_WIN32) + typedef ma_handle ma_thread; + #endif -#if defined(MA_WIN32) -typedef ma_handle ma_mutex; -#endif -#if defined(MA_POSIX) -typedef ma_pthread_mutex_t ma_mutex; -#endif + #if defined(MA_POSIX) + typedef ma_pthread_mutex_t ma_mutex; + #elif defined(MA_WIN32) + typedef ma_handle ma_mutex; + #endif -#if defined(MA_WIN32) -typedef ma_handle ma_event; -#endif -#if defined(MA_POSIX) -typedef struct -{ - ma_uint32 value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; -} ma_event; -#endif /* MA_POSIX */ + #if defined(MA_POSIX) + typedef struct + { + ma_uint32 value; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; + } ma_event; + #elif defined(MA_WIN32) + typedef ma_handle ma_event; + #endif -#if defined(MA_WIN32) -typedef ma_handle ma_semaphore; -#endif -#if defined(MA_POSIX) -typedef struct -{ - int value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; -} ma_semaphore; -#endif /* MA_POSIX */ + #if defined(MA_POSIX) + typedef struct + { + int value; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; + } ma_semaphore; + #elif defined(MA_WIN32) + typedef ma_handle ma_semaphore; + #endif #else -/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ -#ifndef MA_NO_DEVICE_IO -#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; -#endif + /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ + #ifndef MA_NO_DEVICE_IO + #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; + #endif #endif /* MA_NO_THREADING */ @@ -5096,6 +5152,7 @@ typedef struct float coneOuterGain; float dopplerFactor; /* Set to 0 to disable doppler effect. */ float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ + float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ } ma_spatializer_config; @@ -5125,6 +5182,7 @@ typedef struct ma_atomic_vec3f direction; ma_atomic_vec3f velocity; /* For doppler effect. */ float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ + float minSpatializationChannelGain; ma_gainer gainer; /* For smooth gain transitions. */ float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ @@ -5685,6 +5743,197 @@ MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_forma MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); +/************************************************************************************************************************************************************ + +Data Source + +************************************************************************************************************************************************************/ +typedef void ma_data_source; + +#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 + +typedef struct +{ + ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); + ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); + ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); + ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); + ma_uint32 flags; +} ma_data_source_vtable; + +typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); + +typedef struct +{ + const ma_data_source_vtable* vtable; +} ma_data_source_config; + +MA_API ma_data_source_config ma_data_source_config_init(void); + + +typedef struct +{ + const ma_data_source_vtable* vtable; + ma_uint64 rangeBegInFrames; + ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ + ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ + ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ + ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ + ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ + ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + MA_ATOMIC(4, ma_bool32) isLooping; +} ma_data_source_base; + +MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); +MA_API void ma_data_source_uninit(ma_data_source* pDataSource); +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ +MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); +MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); +MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); + + +typedef struct +{ + ma_data_source_base ds; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 cursor; + ma_uint64 sizeInFrames; + const void* pData; +} ma_audio_buffer_ref; + +MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); +MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); +MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); + + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 sizeInFrames; + const void* pData; /* If set to NULL, will allocate a block of memory for you. */ + ma_allocation_callbacks allocationCallbacks; +} ma_audio_buffer_config; + +MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); + +typedef struct +{ + ma_audio_buffer_ref ref; + ma_allocation_callbacks allocationCallbacks; + ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ + ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ +} ma_audio_buffer; + +MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ +MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); +MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); +MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); + + +/* +Paged Audio Buffer +================== +A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It +can be used for cases where audio data is streamed in asynchronously while allowing data to be read +at the same time. + +This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across +simultaneously across different threads, however only one thread at a time can append, and only one +thread at a time can read and seek. +*/ +typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; +struct ma_paged_audio_buffer_page +{ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; + ma_uint64 sizeInFrames; + ma_uint8 pAudioData[1]; +}; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ +} ma_paged_audio_buffer_data; + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_paged_audio_buffer_data* pData; /* Must not be null. */ +} ma_paged_audio_buffer_config; + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); + + +typedef struct +{ + ma_data_source_base ds; + ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ + ma_paged_audio_buffer_page* pCurrent; + ma_uint64 relativeCursor; /* Relative to the current page. */ + ma_uint64 absoluteCursor; +} ma_paged_audio_buffer; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); + + + /************************************************************************************************************************************************************ Ring Buffer @@ -5724,9 +5973,11 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB typedef struct { + ma_data_source_base ds; ma_rb rb; ma_format format; ma_uint32 channels; + ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ } ma_pcm_rb; MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); @@ -5746,6 +5997,10 @@ MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); /* @@ -6256,15 +6511,20 @@ This section contains the APIs for device playback and capture. Here is where yo /* Some backends are only supported on certain platforms. */ #if defined(MA_WIN32) #define MA_SUPPORT_WASAPI - #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ + + #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ #define MA_SUPPORT_DSOUND #define MA_SUPPORT_WINMM - #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ + + /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ + #if !defined(__COSMOPOLITAN__) + #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ + #endif #endif #endif #if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) #if defined(MA_LINUX) - #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */ + #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ #define MA_SUPPORT_ALSA #endif #endif @@ -6697,7 +6957,7 @@ typedef union typedef union { - wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ + ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ char alsa[256]; /* ALSA uses a name string for identification. */ @@ -7040,7 +7300,7 @@ struct ma_context ma_uint32 commandCount; ma_context_command__wasapi commands[4]; ma_handle hAvrt; - ma_proc AvSetMmThreadCharacteristicsW; + ma_proc AvSetMmThreadCharacteristicsA; ma_proc AvRevertMmThreadcharacteristics; ma_handle hMMDevapi; ma_proc ActivateAudioInterfaceAsync; @@ -7384,7 +7644,7 @@ struct ma_context union { -#ifdef MA_WIN32 +#if defined(MA_WIN32) struct { /*HMODULE*/ ma_handle hOle32DLL; @@ -9501,195 +9761,6 @@ This will run on an optimized path when the volume is equal to 1. MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); -/************************************************************************************************** - -Data Source - -**************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - /************************************************************************************************************************************************************ @@ -10314,6 +10385,7 @@ typedef struct ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ + size_t jobThreadStackSize; ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ ma_uint32 flags; ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ @@ -10900,13 +10972,17 @@ typedef struct ma_sound ma_sound; /* Sound flags. */ typedef enum { + /* Resource manager flags. */ MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */ + MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ + + /* ma_sound specific flags. */ + MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ + MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ + MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ } ma_sound_flags; #ifndef MA_ENGINE_MAX_LISTENERS @@ -10928,6 +11004,7 @@ typedef struct ma_uint32 channelsIn; ma_uint32 channelsOut; ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ + ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ ma_mono_expansion_mode monoExpansionMode; ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ @@ -10943,11 +11020,14 @@ typedef struct ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ + ma_uint32 volumeSmoothTimeInPCMFrames; ma_mono_expansion_mode monoExpansionMode; ma_fader fader; ma_linear_resampler resampler; /* For pitch shift. */ ma_spatializer spatializer; ma_panner panner; + ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ + ma_atomic_float volume; /* Defaults to 1. */ MA_ATOMIC(4, float) pitch; float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ @@ -10968,6 +11048,9 @@ MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocati #define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF +/* Callback for when a sound reaches the end. */ +typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); + typedef struct { const char* pFilePath; /* Set this to load from the resource manager. */ @@ -10979,13 +11062,18 @@ typedef struct ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ + ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ ma_uint64 rangeBegInPCMFrames; ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; ma_bool32 isLooping; + ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ + void* pEndCallbackUserData; +#ifndef MA_NO_RESOURCE_MANAGER ma_resource_manager_pipeline_notifications initNotifications; +#endif ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ } ma_sound_config; @@ -10998,6 +11086,8 @@ struct ma_sound ma_data_source* pDataSource; MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ MA_ATOMIC(4, ma_bool32) atEnd; + ma_sound_end_proc endCallback; + void* pEndCallbackUserData; ma_bool8 ownsDataSource; /* @@ -11028,27 +11118,28 @@ MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); typedef struct { #if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ + ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ #endif #if !defined(MA_NO_DEVICE_IO) ma_context* pContext; - ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ - ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ + ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ + ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ ma_device_notification_proc notificationCallback; #endif - ma_log* pLog; /* When set to NULL, will use the context's log. */ - ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ - ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ - ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ - ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ - ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ + ma_log* pLog; /* When set to NULL, will use the context's log. */ + ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ + ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ + ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ + ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ + ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ + ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ + ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ ma_allocation_callbacks allocationCallbacks; - ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ - ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ + ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ + ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ + ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ } ma_engine_config; MA_API ma_engine_config ma_engine_config_init(void); @@ -11074,6 +11165,7 @@ struct ma_engine ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ + ma_uint32 defaultVolumeSmoothTimeInPCMFrames; ma_mono_expansion_mode monoExpansionMode; }; @@ -11087,8 +11179,12 @@ MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); @@ -11187,6 +11283,7 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); +MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); @@ -11271,8 +11368,10 @@ IMPLEMENTATION #define miniaudio_c #include -#include /* For INT_MAX */ -#include /* sin(), etc. */ +#include /* For INT_MAX */ +#include /* sin(), etc. */ +#include /* For malloc(), free(), wcstombs(). */ +#include /* For memset() */ #include #include @@ -11284,18 +11383,33 @@ IMPLEMENTATION #include /* For _controlfp_s constants */ #endif -#ifdef MA_WIN32 -#include -#else -#include /* For malloc(), free(), wcstombs(). */ -#include /* For memset() */ +#if defined(MA_WIN32) + #include + + /* + There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols + such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're + unavailable. + */ + #ifndef STGM_READ + #define STGM_READ 0x00000000L + #endif + #ifndef CLSCTX_ALL + #define CLSCTX_ALL 23 + #endif + + /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ + typedef struct ma_IUnknown ma_IUnknown; +#endif + +#if !defined(MA_WIN32) #include #include /* select() (used for ma_sleep()). */ #include #endif #ifdef MA_NX -#include /* For nanosleep() */ +#include /* For nanosleep() */ #endif #include /* For fstat(), etc. */ @@ -11304,6 +11418,7 @@ IMPLEMENTATION #include #endif + #if !defined(MA_64BIT) && !defined(MA_32BIT) #ifdef _WIN32 #ifdef _WIN64 @@ -11343,7 +11458,7 @@ IMPLEMENTATION #endif /* Intrinsics Support */ -#if defined(MA_X64) || defined(MA_X86) +#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) #if defined(_MSC_VER) && !defined(__clang__) /* MSVC. */ #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ @@ -11718,7 +11833,7 @@ static MA_INLINE void ma_sleep(ma_uint32 milliseconds) } #endif -static MA_INLINE void ma_yield() +static MA_INLINE void ma_yield(void) { #if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) /* x86/x64 */ @@ -11753,7 +11868,7 @@ static MA_INLINE void ma_yield() #define MA_MM_DENORMALS_ZERO_MASK 0x0040 #define MA_MM_FLUSH_ZERO_MASK 0x8000 -static MA_INLINE unsigned int ma_disable_denormals() +static MA_INLINE unsigned int ma_disable_denormals(void) { unsigned int prevState; @@ -11780,7 +11895,7 @@ static MA_INLINE unsigned int ma_disable_denormals() } #elif defined(MA_X86) || defined(MA_X64) { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { prevState = _mm_getcsr(); _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); @@ -11820,7 +11935,7 @@ static MA_INLINE void ma_restore_denormals(unsigned int prevState) } #elif defined(MA_X86) || defined(MA_X64) { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { _mm_setcsr(prevState); } @@ -12008,35 +12123,17 @@ Standard Library Stuff ******************************************************************************/ #ifndef MA_ASSERT -#ifdef MA_WIN32 -#define MA_ASSERT(condition) assert(condition) -#else -#define MA_ASSERT(condition) assert(condition) -#endif +#define MA_ASSERT(condition) assert(condition) #endif #ifndef MA_MALLOC -#ifdef MA_WIN32 -#define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz)) -#else -#define MA_MALLOC(sz) malloc((sz)) +#define MA_MALLOC(sz) malloc((sz)) #endif -#endif - #ifndef MA_REALLOC -#ifdef MA_WIN32 -#define MA_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0))) -#else -#define MA_REALLOC(p, sz) realloc((p), (sz)) +#define MA_REALLOC(p, sz) realloc((p), (sz)) #endif -#endif - #ifndef MA_FREE -#ifdef MA_WIN32 -#define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p)) -#else -#define MA_FREE(p) free((p)) -#endif +#define MA_FREE(p) free((p)) #endif static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) @@ -12046,45 +12143,32 @@ static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) return; } -#ifdef MA_WIN32 - ZeroMemory(p, sz); -#else if (sz > 0) { memset(p, 0, sz); } -#endif } + #ifndef MA_ZERO_MEMORY -#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) +#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) #endif - #ifndef MA_COPY_MEMORY -#ifdef MA_WIN32 -#define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz)) -#else -#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#endif - #ifndef MA_MOVE_MEMORY -#ifdef MA_WIN32 -#define MA_MOVE_MEMORY(dst, src, sz) MoveMemory((dst), (src), (sz)) -#else -#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif +#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) #endif -#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) +#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) -#define ma_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) -#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) -#define ma_abs(x) (((x) > 0) ? (x) : -(x)) -#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) -#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) -#define ma_align_64(x) ma_align(x, 8) +#define ma_countof(x) (sizeof(x) / sizeof(x[0])) +#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) +#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) +#define ma_abs(x) (((x) > 0) ? (x) : -(x)) +#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) +#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) +#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) +#define ma_align_64(x) ma_align(x, 8) #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) @@ -12535,406 +12619,408 @@ MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbac } + #include static ma_result ma_result_from_errno(int e) { - switch (e) - { - case 0: return MA_SUCCESS; - #ifdef EPERM - case EPERM: return MA_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return MA_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return MA_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return MA_INTERRUPT; - #endif - #ifdef EIO - case EIO: return MA_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return MA_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return MA_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return MA_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return MA_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return MA_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return MA_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return MA_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return MA_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return MA_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return MA_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return MA_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return MA_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return MA_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return MA_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return MA_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return MA_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return MA_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return MA_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return MA_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return MA_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return MA_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return MA_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return MA_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return MA_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return MA_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return MA_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return MA_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return MA_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return MA_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return MA_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return MA_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return MA_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return MA_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return MA_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return MA_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return MA_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return MA_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return MA_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return MA_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return MA_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return MA_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return MA_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return MA_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return MA_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return MA_ERROR; - #endif - #ifdef EBADE - case EBADE: return MA_ERROR; - #endif - #ifdef EBADR - case EBADR: return MA_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return MA_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return MA_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return MA_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return MA_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return MA_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return MA_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return MA_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return MA_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return MA_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return MA_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return MA_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return MA_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return MA_ERROR; - #endif - #ifdef EADV - case EADV: return MA_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return MA_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return MA_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return MA_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return MA_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return MA_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return MA_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return MA_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return MA_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return MA_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return MA_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return MA_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return MA_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return MA_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return MA_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return MA_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return MA_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return MA_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return MA_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return MA_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return MA_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return MA_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return MA_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return MA_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return MA_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return MA_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return MA_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return MA_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return MA_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return MA_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return MA_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return MA_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return MA_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return MA_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return MA_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return MA_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return MA_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return MA_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return MA_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return MA_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return MA_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return MA_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return MA_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return MA_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return MA_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return MA_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return MA_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return MA_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return MA_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return MA_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return MA_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return MA_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return MA_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return MA_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return MA_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return MA_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return MA_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return MA_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return MA_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return MA_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return MA_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return MA_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return MA_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return MA_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return MA_ERROR; - #endif - default: return MA_ERROR; + if (e == 0) { + return MA_SUCCESS; + } +#ifdef EPERM + else if (e == EPERM) { return MA_INVALID_OPERATION; } +#endif +#ifdef ENOENT + else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef ESRCH + else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef EINTR + else if (e == EINTR) { return MA_INTERRUPT; } +#endif +#ifdef EIO + else if (e == EIO) { return MA_IO_ERROR; } +#endif +#ifdef ENXIO + else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef E2BIG + else if (e == E2BIG) { return MA_INVALID_ARGS; } +#endif +#ifdef ENOEXEC + else if (e == ENOEXEC) { return MA_INVALID_FILE; } +#endif +#ifdef EBADF + else if (e == EBADF) { return MA_INVALID_FILE; } +#endif +#ifdef ECHILD + else if (e == ECHILD) { return MA_ERROR; } +#endif +#ifdef EAGAIN + else if (e == EAGAIN) { return MA_UNAVAILABLE; } +#endif +#ifdef ENOMEM + else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } +#endif +#ifdef EACCES + else if (e == EACCES) { return MA_ACCESS_DENIED; } +#endif +#ifdef EFAULT + else if (e == EFAULT) { return MA_BAD_ADDRESS; } +#endif +#ifdef ENOTBLK + else if (e == ENOTBLK) { return MA_ERROR; } +#endif +#ifdef EBUSY + else if (e == EBUSY) { return MA_BUSY; } +#endif +#ifdef EEXIST + else if (e == EEXIST) { return MA_ALREADY_EXISTS; } +#endif +#ifdef EXDEV + else if (e == EXDEV) { return MA_ERROR; } +#endif +#ifdef ENODEV + else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef ENOTDIR + else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } +#endif +#ifdef EISDIR + else if (e == EISDIR) { return MA_IS_DIRECTORY; } +#endif +#ifdef EINVAL + else if (e == EINVAL) { return MA_INVALID_ARGS; } +#endif +#ifdef ENFILE + else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } +#endif +#ifdef EMFILE + else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } +#endif +#ifdef ENOTTY + else if (e == ENOTTY) { return MA_INVALID_OPERATION; } +#endif +#ifdef ETXTBSY + else if (e == ETXTBSY) { return MA_BUSY; } +#endif +#ifdef EFBIG + else if (e == EFBIG) { return MA_TOO_BIG; } +#endif +#ifdef ENOSPC + else if (e == ENOSPC) { return MA_NO_SPACE; } +#endif +#ifdef ESPIPE + else if (e == ESPIPE) { return MA_BAD_SEEK; } +#endif +#ifdef EROFS + else if (e == EROFS) { return MA_ACCESS_DENIED; } +#endif +#ifdef EMLINK + else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } +#endif +#ifdef EPIPE + else if (e == EPIPE) { return MA_BAD_PIPE; } +#endif +#ifdef EDOM + else if (e == EDOM) { return MA_OUT_OF_RANGE; } +#endif +#ifdef ERANGE + else if (e == ERANGE) { return MA_OUT_OF_RANGE; } +#endif +#ifdef EDEADLK + else if (e == EDEADLK) { return MA_DEADLOCK; } +#endif +#ifdef ENAMETOOLONG + else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } +#endif +#ifdef ENOLCK + else if (e == ENOLCK) { return MA_ERROR; } +#endif +#ifdef ENOSYS + else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } +#endif +#ifdef ENOTEMPTY + else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } +#endif +#ifdef ELOOP + else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } +#endif +#ifdef ENOMSG + else if (e == ENOMSG) { return MA_NO_MESSAGE; } +#endif +#ifdef EIDRM + else if (e == EIDRM) { return MA_ERROR; } +#endif +#ifdef ECHRNG + else if (e == ECHRNG) { return MA_ERROR; } +#endif +#ifdef EL2NSYNC + else if (e == EL2NSYNC) { return MA_ERROR; } +#endif +#ifdef EL3HLT + else if (e == EL3HLT) { return MA_ERROR; } +#endif +#ifdef EL3RST + else if (e == EL3RST) { return MA_ERROR; } +#endif +#ifdef ELNRNG + else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } +#endif +#ifdef EUNATCH + else if (e == EUNATCH) { return MA_ERROR; } +#endif +#ifdef ENOCSI + else if (e == ENOCSI) { return MA_ERROR; } +#endif +#ifdef EL2HLT + else if (e == EL2HLT) { return MA_ERROR; } +#endif +#ifdef EBADE + else if (e == EBADE) { return MA_ERROR; } +#endif +#ifdef EBADR + else if (e == EBADR) { return MA_ERROR; } +#endif +#ifdef EXFULL + else if (e == EXFULL) { return MA_ERROR; } +#endif +#ifdef ENOANO + else if (e == ENOANO) { return MA_ERROR; } +#endif +#ifdef EBADRQC + else if (e == EBADRQC) { return MA_ERROR; } +#endif +#ifdef EBADSLT + else if (e == EBADSLT) { return MA_ERROR; } +#endif +#ifdef EBFONT + else if (e == EBFONT) { return MA_INVALID_FILE; } +#endif +#ifdef ENOSTR + else if (e == ENOSTR) { return MA_ERROR; } +#endif +#ifdef ENODATA + else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } +#endif +#ifdef ETIME + else if (e == ETIME) { return MA_TIMEOUT; } +#endif +#ifdef ENOSR + else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } +#endif +#ifdef ENONET + else if (e == ENONET) { return MA_NO_NETWORK; } +#endif +#ifdef ENOPKG + else if (e == ENOPKG) { return MA_ERROR; } +#endif +#ifdef EREMOTE + else if (e == EREMOTE) { return MA_ERROR; } +#endif +#ifdef ENOLINK + else if (e == ENOLINK) { return MA_ERROR; } +#endif +#ifdef EADV + else if (e == EADV) { return MA_ERROR; } +#endif +#ifdef ESRMNT + else if (e == ESRMNT) { return MA_ERROR; } +#endif +#ifdef ECOMM + else if (e == ECOMM) { return MA_ERROR; } +#endif +#ifdef EPROTO + else if (e == EPROTO) { return MA_ERROR; } +#endif +#ifdef EMULTIHOP + else if (e == EMULTIHOP) { return MA_ERROR; } +#endif +#ifdef EDOTDOT + else if (e == EDOTDOT) { return MA_ERROR; } +#endif +#ifdef EBADMSG + else if (e == EBADMSG) { return MA_BAD_MESSAGE; } +#endif +#ifdef EOVERFLOW + else if (e == EOVERFLOW) { return MA_TOO_BIG; } +#endif +#ifdef ENOTUNIQ + else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } +#endif +#ifdef EBADFD + else if (e == EBADFD) { return MA_ERROR; } +#endif +#ifdef EREMCHG + else if (e == EREMCHG) { return MA_ERROR; } +#endif +#ifdef ELIBACC + else if (e == ELIBACC) { return MA_ACCESS_DENIED; } +#endif +#ifdef ELIBBAD + else if (e == ELIBBAD) { return MA_INVALID_FILE; } +#endif +#ifdef ELIBSCN + else if (e == ELIBSCN) { return MA_INVALID_FILE; } +#endif +#ifdef ELIBMAX + else if (e == ELIBMAX) { return MA_ERROR; } +#endif +#ifdef ELIBEXEC + else if (e == ELIBEXEC) { return MA_ERROR; } +#endif +#ifdef EILSEQ + else if (e == EILSEQ) { return MA_INVALID_DATA; } +#endif +#ifdef ERESTART + else if (e == ERESTART) { return MA_ERROR; } +#endif +#ifdef ESTRPIPE + else if (e == ESTRPIPE) { return MA_ERROR; } +#endif +#ifdef EUSERS + else if (e == EUSERS) { return MA_ERROR; } +#endif +#ifdef ENOTSOCK + else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } +#endif +#ifdef EDESTADDRREQ + else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } +#endif +#ifdef EMSGSIZE + else if (e == EMSGSIZE) { return MA_TOO_BIG; } +#endif +#ifdef EPROTOTYPE + else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } +#endif +#ifdef ENOPROTOOPT + else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } +#endif +#ifdef EPROTONOSUPPORT + else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } +#endif +#ifdef ESOCKTNOSUPPORT + else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } +#endif +#ifdef EOPNOTSUPP + else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } +#endif +#ifdef EPFNOSUPPORT + else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } +#endif +#ifdef EAFNOSUPPORT + else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } +#endif +#ifdef EADDRINUSE + else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } +#endif +#ifdef EADDRNOTAVAIL + else if (e == EADDRNOTAVAIL) { return MA_ERROR; } +#endif +#ifdef ENETDOWN + else if (e == ENETDOWN) { return MA_NO_NETWORK; } +#endif +#ifdef ENETUNREACH + else if (e == ENETUNREACH) { return MA_NO_NETWORK; } +#endif +#ifdef ENETRESET + else if (e == ENETRESET) { return MA_NO_NETWORK; } +#endif +#ifdef ECONNABORTED + else if (e == ECONNABORTED) { return MA_NO_NETWORK; } +#endif +#ifdef ECONNRESET + else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } +#endif +#ifdef ENOBUFS + else if (e == ENOBUFS) { return MA_NO_SPACE; } +#endif +#ifdef EISCONN + else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } +#endif +#ifdef ENOTCONN + else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } +#endif +#ifdef ESHUTDOWN + else if (e == ESHUTDOWN) { return MA_ERROR; } +#endif +#ifdef ETOOMANYREFS + else if (e == ETOOMANYREFS) { return MA_ERROR; } +#endif +#ifdef ETIMEDOUT + else if (e == ETIMEDOUT) { return MA_TIMEOUT; } +#endif +#ifdef ECONNREFUSED + else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } +#endif +#ifdef EHOSTDOWN + else if (e == EHOSTDOWN) { return MA_NO_HOST; } +#endif +#ifdef EHOSTUNREACH + else if (e == EHOSTUNREACH) { return MA_NO_HOST; } +#endif +#ifdef EALREADY + else if (e == EALREADY) { return MA_IN_PROGRESS; } +#endif +#ifdef EINPROGRESS + else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } +#endif +#ifdef ESTALE + else if (e == ESTALE) { return MA_INVALID_FILE; } +#endif +#ifdef EUCLEAN + else if (e == EUCLEAN) { return MA_ERROR; } +#endif +#ifdef ENOTNAM + else if (e == ENOTNAM) { return MA_ERROR; } +#endif +#ifdef ENAVAIL + else if (e == ENAVAIL) { return MA_ERROR; } +#endif +#ifdef EISNAM + else if (e == EISNAM) { return MA_ERROR; } +#endif +#ifdef EREMOTEIO + else if (e == EREMOTEIO) { return MA_IO_ERROR; } +#endif +#ifdef EDQUOT + else if (e == EDQUOT) { return MA_NO_SPACE; } +#endif +#ifdef ENOMEDIUM + else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef EMEDIUMTYPE + else if (e == EMEDIUMTYPE) { return MA_ERROR; } +#endif +#ifdef ECANCELED + else if (e == ECANCELED) { return MA_CANCELLED; } +#endif +#ifdef ENOKEY + else if (e == ENOKEY) { return MA_ERROR; } +#endif +#ifdef EKEYEXPIRED + else if (e == EKEYEXPIRED) { return MA_ERROR; } +#endif +#ifdef EKEYREVOKED + else if (e == EKEYREVOKED) { return MA_ERROR; } +#endif +#ifdef EKEYREJECTED + else if (e == EKEYREJECTED) { return MA_ERROR; } +#endif +#ifdef EOWNERDEAD + else if (e == EOWNERDEAD) { return MA_ERROR; } +#endif +#ifdef ENOTRECOVERABLE + else if (e == ENOTRECOVERABLE) { return MA_ERROR; } +#endif +#ifdef ERFKILL + else if (e == ERFKILL) { return MA_ERROR; } +#endif +#ifdef EHWPOISON + else if (e == EHWPOISON) { return MA_ERROR; } +#endif + else { + return MA_ERROR; } } @@ -13864,6 +13950,13 @@ Atomics #if defined(__cplusplus) extern "C" { #endif +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif +#endif typedef signed char c89atomic_int8; typedef unsigned char c89atomic_uint8; typedef signed short c89atomic_int16; @@ -13874,18 +13967,8 @@ typedef unsigned int c89atomic_uint32; typedef signed __int64 c89atomic_int64; typedef unsigned __int64 c89atomic_uint64; #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif typedef signed long long c89atomic_int64; typedef unsigned long long c89atomic_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif #endif typedef int c89atomic_memory_order; typedef unsigned char c89atomic_bool; @@ -14725,10 +14808,26 @@ typedef unsigned char c89atomic_bool; #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_compare_and_swap_8 (dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } typedef c89atomic_uint8 c89atomic_flag; #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order) #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) @@ -15250,7 +15349,7 @@ typedef unsigned char c89atomic_bool; #endif #if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) #if defined(C89ATOMIC_HAS_8) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint8 expectedValue; c89atomic_uint8 result; @@ -15267,7 +15366,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_16) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint16 expectedValue; c89atomic_uint16 result; @@ -15284,7 +15383,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_32) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint32 expectedValue; c89atomic_uint32 result; @@ -15301,7 +15400,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_64) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint64 expectedValue; c89atomic_uint64 result; @@ -15779,6 +15878,9 @@ static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlo { c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); } +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif #if defined(__cplusplus) } #endif @@ -15854,7 +15956,10 @@ MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) + +#if !defined(MA_NO_DEVICE_IO) MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) +#endif MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) @@ -15962,158 +16067,16 @@ MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) #ifndef MA_NO_THREADING -#ifdef MA_WIN32 - #define MA_THREADCALL WINAPI - typedef unsigned long ma_thread_result; -#else +#if defined(MA_POSIX) #define MA_THREADCALL typedef void* ma_thread_result; +#elif defined(MA_WIN32) + #define MA_THREADCALL WINAPI + typedef unsigned long ma_thread_result; #endif + typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); -#ifdef MA_WIN32 -static int ma_thread_priority_to_win32(ma_thread_priority priority) -{ - switch (priority) { - case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; - case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; - case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; - case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; - case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; - case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; - case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; - default: return THREAD_PRIORITY_NORMAL; - } -} - -static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ - - *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); - if (*pThread == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); - - return MA_SUCCESS; -} - -static void ma_thread_wait__win32(ma_thread* pThread) -{ - WaitForSingleObject((HANDLE)*pThread, INFINITE); - CloseHandle((HANDLE)*pThread); -} - - -static ma_result ma_mutex_init__win32(ma_mutex* pMutex) -{ - *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); - if (*pMutex == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__win32(ma_mutex* pMutex) -{ - CloseHandle((HANDLE)*pMutex); -} - -static void ma_mutex_lock__win32(ma_mutex* pMutex) -{ - WaitForSingleObject((HANDLE)*pMutex, INFINITE); -} - -static void ma_mutex_unlock__win32(ma_mutex* pMutex) -{ - SetEvent((HANDLE)*pMutex); -} - - -static ma_result ma_event_init__win32(ma_event* pEvent) -{ - *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (*pEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_event_uninit__win32(ma_event* pEvent) -{ - CloseHandle((HANDLE)*pEvent); -} - -static ma_result ma_event_wait__win32(ma_event* pEvent) -{ - DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_event_signal__win32(ma_event* pEvent) -{ - BOOL result = SetEvent((HANDLE)*pEvent); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) -{ - *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); - if (*pSemaphore == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) -{ - CloseHandle((HANDLE)*pSemaphore); -} - -static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) -{ - DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) -{ - BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} -#endif - - #ifdef MA_POSIX static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) { @@ -16129,23 +16092,28 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ pAttr = &attr; - if (priority == ma_thread_priority_idle) { -#ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; + /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ + #if !defined(MA_BEOS) + { + if (priority == ma_thread_priority_idle) { + #ifdef SCHED_IDLE + if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { + scheduler = SCHED_IDLE; + } + #endif + } else if (priority == ma_thread_priority_realtime) { + #ifdef SCHED_FIFO + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { + scheduler = SCHED_FIFO; + } + #endif + #ifdef MA_LINUX + } else { + scheduler = sched_getscheduler(0); + #endif } -#endif - } else if (priority == ma_thread_priority_realtime) { -#ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } -#endif -#ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); -#endif } + #endif if (stackSize > 0) { pthread_attr_setstacksize(&attr, stackSize); @@ -16350,6 +16318,146 @@ static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) return MA_SUCCESS; } +#elif defined(MA_WIN32) +static int ma_thread_priority_to_win32(ma_thread_priority priority) +{ + switch (priority) { + case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; + case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; + case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; + case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; + case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; + case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; + case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; + default: return THREAD_PRIORITY_NORMAL; + } +} + +static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) +{ + DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ + + *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); + if (*pThread == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); + + return MA_SUCCESS; +} + +static void ma_thread_wait__win32(ma_thread* pThread) +{ + WaitForSingleObject((HANDLE)*pThread, INFINITE); + CloseHandle((HANDLE)*pThread); +} + + +static ma_result ma_mutex_init__win32(ma_mutex* pMutex) +{ + *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); + if (*pMutex == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_mutex_uninit__win32(ma_mutex* pMutex) +{ + CloseHandle((HANDLE)*pMutex); +} + +static void ma_mutex_lock__win32(ma_mutex* pMutex) +{ + WaitForSingleObject((HANDLE)*pMutex, INFINITE); +} + +static void ma_mutex_unlock__win32(ma_mutex* pMutex) +{ + SetEvent((HANDLE)*pMutex); +} + + +static ma_result ma_event_init__win32(ma_event* pEvent) +{ + *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (*pEvent == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_event_uninit__win32(ma_event* pEvent) +{ + CloseHandle((HANDLE)*pEvent); +} + +static ma_result ma_event_wait__win32(ma_event* pEvent) +{ + DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); + if (result == WAIT_OBJECT_0) { + return MA_SUCCESS; + } + + if (result == WAIT_TIMEOUT) { + return MA_TIMEOUT; + } + + return ma_result_from_GetLastError(GetLastError()); +} + +static ma_result ma_event_signal__win32(ma_event* pEvent) +{ + BOOL result = SetEvent((HANDLE)*pEvent); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + + +static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) +{ + *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); + if (*pSemaphore == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) +{ + CloseHandle((HANDLE)*pSemaphore); +} + +static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) +{ + DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); + if (result == WAIT_OBJECT_0) { + return MA_SUCCESS; + } + + if (result == WAIT_TIMEOUT) { + return MA_TIMEOUT; + } + + return ma_result_from_GetLastError(GetLastError()); +} + +static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) +{ + BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} #endif typedef struct @@ -16399,15 +16507,20 @@ static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priorit return MA_OUT_OF_MEMORY; } +#if defined(MA_THREAD_DEFAULT_STACK_SIZE) + if (stackSize == 0) { + stackSize = MA_THREAD_DEFAULT_STACK_SIZE; + } +#endif + pProxyData->entryProc = entryProc; pProxyData->pData = pData; ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); -#ifdef MA_WIN32 - result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); +#elif defined(MA_WIN32) + result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); #endif if (result != MA_SUCCESS) { @@ -16424,11 +16537,10 @@ static void ma_thread_wait(ma_thread* pThread) return; } -#ifdef MA_WIN32 - ma_thread_wait__win32(pThread); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_thread_wait__posix(pThread); +#elif defined(MA_WIN32) + ma_thread_wait__win32(pThread); #endif } @@ -16440,11 +16552,10 @@ MA_API ma_result ma_mutex_init(ma_mutex* pMutex) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_mutex_init__win32(pMutex); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_mutex_init__posix(pMutex); +#elif defined(MA_WIN32) + return ma_mutex_init__win32(pMutex); #endif } @@ -16454,11 +16565,10 @@ MA_API void ma_mutex_uninit(ma_mutex* pMutex) return; } -#ifdef MA_WIN32 - ma_mutex_uninit__win32(pMutex); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_mutex_uninit__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_uninit__win32(pMutex); #endif } @@ -16469,11 +16579,10 @@ MA_API void ma_mutex_lock(ma_mutex* pMutex) return; } -#ifdef MA_WIN32 - ma_mutex_lock__win32(pMutex); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_mutex_lock__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_lock__win32(pMutex); #endif } @@ -16482,13 +16591,12 @@ MA_API void ma_mutex_unlock(ma_mutex* pMutex) if (pMutex == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return; -} + } -#ifdef MA_WIN32 - ma_mutex_unlock__win32(pMutex); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_mutex_unlock__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_unlock__win32(pMutex); #endif } @@ -16500,11 +16608,10 @@ MA_API ma_result ma_event_init(ma_event* pEvent) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_event_init__win32(pEvent); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_event_init__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_init__win32(pEvent); #endif } @@ -16542,11 +16649,10 @@ MA_API void ma_event_uninit(ma_event* pEvent) return; } -#ifdef MA_WIN32 - ma_event_uninit__win32(pEvent); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_event_uninit__posix(pEvent); +#elif defined(MA_WIN32) + ma_event_uninit__win32(pEvent); #endif } @@ -16569,11 +16675,10 @@ MA_API ma_result ma_event_wait(ma_event* pEvent) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_event_wait__win32(pEvent); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_event_wait__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_wait__win32(pEvent); #endif } @@ -16584,11 +16689,10 @@ MA_API ma_result ma_event_signal(ma_event* pEvent) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_event_signal__win32(pEvent); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_event_signal__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_signal__win32(pEvent); #endif } @@ -16600,11 +16704,10 @@ MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_semaphore_init__win32(initialValue, pSemaphore); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_semaphore_init__posix(initialValue, pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_init__win32(initialValue, pSemaphore); #endif } @@ -16615,11 +16718,10 @@ MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) return; } -#ifdef MA_WIN32 - ma_semaphore_uninit__win32(pSemaphore); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_semaphore_uninit__posix(pSemaphore); +#elif defined(MA_WIN32) + ma_semaphore_uninit__win32(pSemaphore); #endif } @@ -16630,11 +16732,10 @@ MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_semaphore_wait__win32(pSemaphore); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_semaphore_wait__posix(pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_wait__win32(pSemaphore); #endif } @@ -16645,11 +16746,10 @@ MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_semaphore_release__win32(pSemaphore); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_semaphore_release__posix(pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_release__win32(pSemaphore); #endif } #else @@ -17697,11 +17797,6 @@ DEVICE I/O #endif #ifndef MA_NO_DEVICE_IO -#ifdef MA_WIN32 - #include - #include - #include -#endif #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) #include /* For mach_absolute_time() */ @@ -17965,7 +18060,7 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -#ifdef MA_WIN32 +#if defined(MA_WIN32) /* WASAPI error codes. */ #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) @@ -18125,23 +18220,109 @@ static ma_result ma_result_from_HRESULT(HRESULT hr) } } -typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(LPVOID pvReserved); -typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MA_PFN_CoUninitialize)(void); -typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); -typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv); -typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar); -typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax); +/* PROPVARIANT */ +#define MA_VT_LPWSTR 31 +#define MA_VT_BLOB 65 -typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); -typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif +#endif +typedef struct +{ + WORD vt; + WORD wReserved1; + WORD wReserved2; + WORD wReserved3; + union + { + struct + { + ULONG cbSize; + BYTE* pBlobData; + } blob; + WCHAR* pwszVal; + char pad[16]; /* Just to ensure the size of the struct matches the official version. */ + }; +} MA_PROPVARIANT; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop +#endif + +typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); +typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); +typedef void (WINAPI * MA_PFN_CoUninitialize)(void); +typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); +typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); +typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); +typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); + +typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); +typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); #if defined(MA_WIN32_DESKTOP) /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ -typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult); -typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); -typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); +typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); +typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); +typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); #endif /* MA_WIN32_DESKTOP */ + + +MA_API size_t ma_strlen_WCHAR(const WCHAR* str) +{ + size_t len = 0; + while (str[len] != '\0') { + len += 1; + } + + return len; +} + +MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) +{ + while (*s1 != '\0' && *s1 == *s2) { + s1 += 1; + s2 += 1; + } + + return *s1 - *s2; +} + +MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) +{ + size_t i; + + if (dst == 0) { + return 22; + } + if (dstCap == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + for (i = 0; i < dstCap && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstCap) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} #endif /* MA_WIN32 */ @@ -18156,7 +18337,7 @@ typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, L Timing *******************************************************************************/ -#ifdef MA_WIN32 +#if defined(MA_WIN32) && !defined(MA_POSIX) static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ void ma_timer_init(ma_timer* pTimer) { @@ -18274,22 +18455,22 @@ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); -#ifdef _WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(WINAPI_FAMILY) || (defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_DESKTOP_APP) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) - handle = (ma_handle)LoadLibraryA(filename); + #ifdef MA_WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(MA_WIN32_UWP) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } + handle = (ma_handle)dlopen(filename, RTLD_NOW); #endif -#else - handle = (ma_handle)dlopen(filename, RTLD_NOW); -#endif /* I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority @@ -18312,11 +18493,11 @@ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) { #ifndef MA_NO_RUNTIME_LINKING -#ifdef _WIN32 - FreeLibrary((HMODULE)handle); -#else - dlclose((void*)handle); -#endif + #ifdef MA_WIN32 + FreeLibrary((HMODULE)handle); + #else + dlclose((void*)handle); + #endif (void)pContext; #else @@ -18919,7 +19100,7 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state n } -#ifdef MA_WIN32 +#if defined(MA_WIN32) GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ @@ -19639,7 +19820,7 @@ WIN32 COMMON *******************************************************************************/ #if defined(MA_WIN32) -#if defined(MA_WIN32_DESKTOP) +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) @@ -19657,19 +19838,34 @@ WIN32 COMMON typedef size_t DWORD_PTR; #endif +#if !defined(WAVE_FORMAT_1M08) +#define WAVE_FORMAT_1M08 0x00000001 +#define WAVE_FORMAT_1S08 0x00000002 +#define WAVE_FORMAT_1M16 0x00000004 +#define WAVE_FORMAT_1S16 0x00000008 +#define WAVE_FORMAT_2M08 0x00000010 +#define WAVE_FORMAT_2S08 0x00000020 +#define WAVE_FORMAT_2M16 0x00000040 +#define WAVE_FORMAT_2S16 0x00000080 +#define WAVE_FORMAT_4M08 0x00000100 +#define WAVE_FORMAT_4S08 0x00000200 +#define WAVE_FORMAT_4M16 0x00000400 +#define WAVE_FORMAT_4S16 0x00000800 +#endif + #if !defined(WAVE_FORMAT_44M08) -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 #endif #ifndef SPEAKER_FRONT_LEFT @@ -19694,13 +19890,30 @@ typedef size_t DWORD_PTR; #endif /* -The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We -define our own implementation in this case. +Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this +because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The +standard version uses tight packing, but for compiler compatibility we're not doing that with ours. */ -#if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__) typedef struct { - WAVEFORMATEX Format; + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; +} MA_WAVEFORMATEX; + +typedef struct +{ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; union { WORD wValidBitsPerSample; @@ -19709,13 +19922,18 @@ typedef struct } Samples; DWORD dwChannelMask; GUID SubFormat; -} WAVEFORMATEXTENSIBLE; -#endif +} MA_WAVEFORMATEXTENSIBLE; + + #ifndef WAVE_FORMAT_EXTENSIBLE #define WAVE_FORMAT_EXTENSIBLE 0xFFFE #endif +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM 1 +#endif + #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 #endif @@ -19829,21 +20047,21 @@ static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) return ma_is_guid_equal(guid, &nullguid); } -static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF) +static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) { MA_ASSERT(pWF != NULL); if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF; + const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { if (pWFEX->Samples.wValidBitsPerSample == 32) { return ma_format_s32; } if (pWFEX->Samples.wValidBitsPerSample == 24) { - if (pWFEX->Format.wBitsPerSample == 32) { + if (pWFEX->wBitsPerSample == 32) { return ma_format_s32; } - if (pWFEX->Format.wBitsPerSample == 24) { + if (pWFEX->wBitsPerSample == 24) { return ma_format_s24; } } @@ -19951,7 +20169,7 @@ typedef struct #endif /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ -static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp) +static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) { MA_ZERO_OBJECT(pProp); } @@ -19977,17 +20195,9 @@ static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ #endif -static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ -static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ -#ifdef __cplusplus -#define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance -#define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance -#else -#define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance -#define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance -#endif +static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ +static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ -typedef struct ma_IUnknown ma_IUnknown; #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #define MA_MM_DEVICE_STATE_ACTIVE 1 #define MA_MM_DEVICE_STATE_DISABLED 2 @@ -20084,11 +20294,11 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); /* IMMNotificationClient */ - HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState); - HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID); - HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key); + HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); + HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); + HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); } ma_IMMNotificationClientVtbl; /* IMMDeviceEnumerator */ @@ -20102,7 +20312,7 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) /* IMMDeviceEnumerator */ HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); - HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice); + HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); } ma_IMMDeviceEnumeratorVtbl; @@ -20115,7 +20325,7 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } @@ -20152,9 +20362,9 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); /* IMMDevice */ - HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface); + HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); - HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID); + HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); } ma_IMMDeviceVtbl; struct ma_IMMDevice @@ -20164,9 +20374,9 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } + static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } - static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); } + static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } #else /* IActivateAudioInterfaceAsyncOperation */ @@ -20201,8 +20411,8 @@ typedef struct /* IPropertyStore */ HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); - HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar); - HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar); + HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); + HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); } ma_IPropertyStoreVtbl; struct ma_IPropertyStore @@ -20214,8 +20424,8 @@ static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } -static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } +static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } +static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } @@ -20228,12 +20438,12 @@ typedef struct ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); @@ -20248,12 +20458,12 @@ struct ma_IAudioClient static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } @@ -20270,12 +20480,12 @@ typedef struct ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); @@ -20286,7 +20496,7 @@ typedef struct /* IAudioClient2 */ HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); } ma_IAudioClient2Vtbl; struct ma_IAudioClient2 { @@ -20295,12 +20505,12 @@ struct ma_IAudioClient2 static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } @@ -20309,7 +20519,7 @@ static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } +static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } /* IAudioClient3 */ @@ -20321,12 +20531,12 @@ typedef struct ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); @@ -20337,12 +20547,12 @@ typedef struct /* IAudioClient2 */ HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); /* IAudioClient3 */ - HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); } ma_IAudioClient3Vtbl; struct ma_IAudioClient3 { @@ -20351,12 +20561,12 @@ struct ma_IAudioClient3 static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } @@ -20365,10 +20575,10 @@ static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } -static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } +static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } +static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } /* IAudioRenderClient */ @@ -20420,11 +20630,11 @@ static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptu #if defined(MA_WIN32_UWP) /* mmdevapi Functions */ -typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(LPCWSTR deviceInterfacePath, const IID* riid, PROPVARIANT *activationParams, ma_IActivateAudioInterfaceCompletionHandler *completionHandler, ma_IActivateAudioInterfaceAsyncOperation **activationOperation); +typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); #endif /* Avrt Functions */ -typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsW)(LPCWSTR TaskName, LPDWORD TaskIndex); +typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) @@ -20518,7 +20728,7 @@ static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) { - WaitForSingleObject(pHandler->hEvent, INFINITE); + WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); } #endif /* !MA_WIN32_DESKTOP */ @@ -20556,7 +20766,7 @@ static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificati return (ULONG)newRefCount; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) { ma_bool32 isThisDevice = MA_FALSE; ma_bool32 isCapture = MA_FALSE; @@ -20572,14 +20782,14 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m */ if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { isCapture = MA_TRUE; - if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { + if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { isThisDevice = MA_TRUE; } } if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { isPlayback = MA_TRUE; - if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { + if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { isThisDevice = MA_TRUE; } } @@ -20640,7 +20850,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m return S_OK; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ @@ -20652,7 +20862,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNo return S_OK; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ @@ -20664,19 +20874,15 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMM return S_OK; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ #endif - /* We only ever use the eConsole role in miniaudio. */ - if (role != ma_eConsole) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting: role != eConsole\n"); - return S_OK; - } + (void)role; - /* We only care about devices with the same data flow and role as the current device. */ + /* We only care about devices with the same data flow as the current device. */ if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { @@ -20771,7 +20977,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged return S_OK; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ @@ -20795,12 +21001,13 @@ static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { }; #endif /* MA_WIN32_DESKTOP */ -static LPCWSTR ma_to_usage_string__wasapi(ma_wasapi_usage usage) +static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) { - switch (usage) { + switch (usage) + { case ma_wasapi_usage_default: return NULL; - case ma_wasapi_usage_games: return L"Games"; - case ma_wasapi_usage_pro_audio: return L"Pro Audio"; + case ma_wasapi_usage_games: return "Games"; + case ma_wasapi_usage_pro_audio: return "Pro Audio"; default: break; } @@ -20998,7 +21205,7 @@ static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevi #endif -static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) +static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) { MA_ASSERT(pWF != NULL); MA_ASSERT(pInfo != NULL); @@ -21017,13 +21224,13 @@ static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAV static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) { HRESULT hr; - WAVEFORMATEX* pWF = NULL; + MA_WAVEFORMATEX* pWF = NULL; MA_ASSERT(pAudioClient != NULL); MA_ASSERT(pInfo != NULL); /* Shared Mode. We use GetMixFormat() here. */ - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF); + hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); if (SUCCEEDED(hr)) { ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); } else { @@ -21046,12 +21253,12 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context */ hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { - PROPVARIANT var; + MA_PROPVARIANT var; ma_PropVariantInit(&var); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); if (SUCCEEDED(hr)) { - pWF = (WAVEFORMATEX*)var.blob.pBlobData; + pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; /* In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format @@ -21068,7 +21275,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context */ ma_uint32 channels = pWF->nChannels; ma_channel defaultChannelMap[MA_MAX_CHANNELS]; - WAVEFORMATEXTENSIBLE wf; + MA_WAVEFORMATEXTENSIBLE wf; ma_bool32 found; ma_uint32 iFormat; @@ -21080,9 +21287,9 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); MA_ZERO_OBJECT(&wf); - wf.Format.cbSize = sizeof(wf); - wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.Format.nChannels = (WORD)channels; + wf.cbSize = sizeof(wf); + wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.nChannels = (WORD)channels; wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); found = MA_FALSE; @@ -21090,10 +21297,10 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context ma_format format = g_maFormatPriorities[iFormat]; ma_uint32 iSampleRate; - wf.Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample; + wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; if (format == ma_format_f32) { wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } else { @@ -21101,11 +21308,11 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context } for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { - wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; + wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); + ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); found = MA_TRUE; break; } @@ -21163,7 +21370,7 @@ static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pCont *ppDeviceEnumerator = NULL; /* Safety. */ - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); return ma_result_from_HRESULT(hr); @@ -21174,11 +21381,11 @@ static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pCont return MA_SUCCESS; } -static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) +static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) { HRESULT hr; ma_IMMDevice* pMMDefaultDevice = NULL; - LPWSTR pDefaultDeviceID = NULL; + WCHAR* pDefaultDeviceID = NULL; ma_EDataFlow dataFlow; ma_ERole role; @@ -21210,11 +21417,11 @@ static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi( return pDefaultDeviceID; } -static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ +static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ { ma_result result; ma_IMMDeviceEnumerator* pDeviceEnumerator; - LPWSTR pDefaultDeviceID = NULL; + WCHAR* pDefaultDeviceID = NULL; MA_ASSERT(pContext != NULL); @@ -21237,7 +21444,7 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device MA_ASSERT(pContext != NULL); MA_ASSERT(ppMMDevice != NULL); - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); return ma_result_from_HRESULT(hr); @@ -21260,14 +21467,14 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) { - LPWSTR pDeviceIDString; + WCHAR* pDeviceIDString; HRESULT hr; MA_ASSERT(pDeviceID != NULL); hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); if (SUCCEEDED(hr)) { - size_t idlen = wcslen(pDeviceIDString); + size_t idlen = ma_strlen_WCHAR(pDeviceIDString); if (idlen+1 > ma_countof(pDeviceID->wasapi)) { ma_CoTaskMemFree(pContext, pDeviceIDString); MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ @@ -21285,7 +21492,7 @@ static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pCon return MA_ERROR; } -static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, LPWSTR pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) +static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) { ma_result result; HRESULT hr; @@ -21298,7 +21505,7 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); if (result == MA_SUCCESS) { if (pDefaultDeviceID != NULL) { - if (wcscmp(pInfo->id.wasapi, pDefaultDeviceID) == 0) { + if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { pInfo->isDefault = MA_TRUE; } } @@ -21309,7 +21516,7 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC ma_IPropertyStore *pProperties; hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { - PROPVARIANT var; + MA_PROPVARIANT var; ma_PropVariantInit(&var); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); @@ -21346,7 +21553,7 @@ static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pConte UINT deviceCount; HRESULT hr; ma_uint32 iDevice; - LPWSTR pDefaultDeviceID = NULL; + WCHAR* pDefaultDeviceID = NULL; ma_IMMDeviceCollection* pDeviceCollection = NULL; MA_ASSERT(pContext != NULL); @@ -21400,7 +21607,7 @@ done: return result; } -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) +static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) { ma_result result; HRESULT hr; @@ -21422,12 +21629,12 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex return MA_SUCCESS; } #else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) +static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) { ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; ma_completion_handler_uwp completionHandler; IID iid; - LPOLESTR iidStr; + WCHAR* iidStr; HRESULT hr; ma_result result; HRESULT activateResult; @@ -21437,7 +21644,7 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m MA_ASSERT(ppAudioClient != NULL); if (pDeviceID != NULL) { - iidStr = (LPOLESTR)pDeviceID->wasapi; + iidStr = (WCHAR*)pDeviceID->wasapi; } else { if (deviceType == ma_device_type_capture) { iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; @@ -21558,8 +21765,8 @@ static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_de ma_result result; ma_bool32 usingProcessLoopback = MA_FALSE; MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; - PROPVARIANT activationParams; - PROPVARIANT* pActivationParams = NULL; + MA_PROPVARIANT activationParams; + MA_PROPVARIANT* pActivationParams = NULL; ma_device_id virtualDeviceID; /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ @@ -21574,7 +21781,7 @@ static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_de audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; ma_PropVariantInit(&activationParams); - activationParams.vt = VT_BLOB; + activationParams.vt = MA_VT_BLOB; activationParams.blob.cbSize = sizeof(audioclientActivationParams); activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; pActivationParams = &activationParams; @@ -21615,7 +21822,7 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e HRESULT hr; ma_IMMDeviceEnumerator* pDeviceEnumerator; - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); return ma_result_from_HRESULT(hr); @@ -21665,7 +21872,7 @@ static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_dev #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) ma_result result; ma_IMMDevice* pMMDevice = NULL; - LPWSTR pDefaultDeviceID = NULL; + WCHAR* pDefaultDeviceID = NULL; result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); if (result != MA_SUCCESS) { @@ -21750,10 +21957,10 @@ static ma_result ma_device_uninit__wasapi(ma_device* pDevice) } if (pDevice->wasapi.hEventPlayback) { - CloseHandle(pDevice->wasapi.hEventPlayback); + CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); } if (pDevice->wasapi.hEventCapture) { - CloseHandle(pDevice->wasapi.hEventCapture); + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); } return MA_SUCCESS; @@ -21802,10 +22009,11 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device DWORD streamFlags = 0; MA_REFERENCE_TIME periodDurationInMicroseconds; ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; - WAVEFORMATEXTENSIBLE wf; + MA_WAVEFORMATEXTENSIBLE wf; ma_WASAPIDeviceInterface* pDeviceInterface = NULL; ma_IAudioClient2* pAudioClient2; ma_uint32 nativeSampleRate; + ma_bool32 usingProcessLoopback = MA_FALSE; MA_ASSERT(pContext != NULL); MA_ASSERT(pData != NULL); @@ -21815,6 +22023,8 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device return MA_INVALID_ARGS; } + usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; + pData->pAudioClient = NULL; pData->pRenderClient = NULL; pData->pCaptureClient = NULL; @@ -21864,14 +22074,14 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_IPropertyStore* pStore = NULL; hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); if (SUCCEEDED(hr)) { - PROPVARIANT prop; + MA_PROPVARIANT prop; ma_PropVariantInit(&prop); hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); if (SUCCEEDED(hr)) { - WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData; + MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); if (SUCCEEDED(hr)) { - MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE)); + MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); } ma_PropVariantClear(pContext, &prop); @@ -21898,12 +22108,47 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device } } else { /* In shared mode we are always using the format reported by the operating system. */ - WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat); + MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; + hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); if (hr != S_OK) { - result = MA_FORMAT_NOT_SUPPORTED; + /* When using process-specific loopback, GetMixFormat() seems to always fail. */ + if (usingProcessLoopback) { + wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + wf.nChannels = 2; + wf.nSamplesPerSec = 44100; + wf.wBitsPerSample = 32; + wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = sizeof(MA_WAVEFORMATEX); + + result = MA_SUCCESS; + } else { + result = MA_FORMAT_NOT_SUPPORTED; + } } else { - MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf)); + /* + I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself + is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE + want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be + safe and only copy the WAVEFORMATEX part. + */ + if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); + } else { + /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ + size_t cbSize = pNativeFormat->cbSize; + if (cbSize == 0) { + cbSize = sizeof(MA_WAVEFORMATEX); + } + + /* Make sure we don't copy more than the capacity of `wf`. */ + if (cbSize > sizeof(wf)) { + cbSize = sizeof(wf); + } + + MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); + } + result = MA_SUCCESS; } @@ -21922,13 +22167,13 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use WASAPI to perform the sample rate conversion. */ - nativeSampleRate = wf.Format.nSamplesPerSec; + nativeSampleRate = wf.nSamplesPerSec; if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { - wf.Format.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; - wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; + wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; } - pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf); + pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); if (pData->formatOut == ma_format_unknown) { /* The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED @@ -21945,11 +22190,19 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device goto done; } - pData->channelsOut = wf.Format.nChannels; - pData->sampleRateOut = wf.Format.nSamplesPerSec; + pData->channelsOut = wf.nChannels; + pData->sampleRateOut = wf.nSamplesPerSec; - /* Get the internal channel map based on the channel mask. */ - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); + /* + Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns + a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this + case we'll just use the default channel map. + */ + if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { + ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); + } /* Period size. */ pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; @@ -21957,16 +22210,16 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device if (pData->periodSizeInFramesOut == 0) { if (pData->periodSizeInMillisecondsIn == 0) { if (pData->performanceProfile == ma_performance_profile_low_latency) { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.Format.nSamplesPerSec); + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.Format.nSamplesPerSec); + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); } } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec); + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); } } - periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec; + periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ @@ -21979,7 +22232,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device */ hr = E_FAIL; for (;;) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { if (bufferDuration > 500*10000) { break; @@ -22000,7 +22253,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_uint32 bufferSizeInFrames; hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (SUCCEEDED(hr)) { - bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); + bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5); /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); @@ -22012,7 +22265,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device #endif if (SUCCEEDED(hr)) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); } } } @@ -22043,7 +22296,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device */ #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE { - if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) { + if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { ma_IAudioClient3* pAudioClient3 = NULL; hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); if (SUCCEEDED(hr)) { @@ -22051,7 +22304,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_uint32 fundamentalPeriodInFrames; ma_uint32 minPeriodInFrames; ma_uint32 maxPeriodInFrames; - hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); + hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); if (SUCCEEDED(hr)) { ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; @@ -22075,7 +22328,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, IAudioClient3_InitializeSharedAudioStream() will fail. */ - hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); if (SUCCEEDED(hr)) { wasInitializedUsingIAudioClient3 = MA_TRUE; pData->periodSizeInFramesOut = actualPeriodInFrames; @@ -22106,7 +22359,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ if (!wasInitializedUsingIAudioClient3) { MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); if (FAILED(hr)) { if (hr == E_ACCESSDENIED) { errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; @@ -22122,13 +22375,22 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device } if (!wasInitializedUsingIAudioClient3) { - ma_uint32 bufferSizeInFrames; + ma_uint32 bufferSizeInFrames = 0; hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (FAILED(hr)) { errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); goto done; } + /* + When using process loopback mode, retrieval of the buffer size seems to result in totally + incorrect values. In this case we'll just assume it's the same size as what we requested + when we initialized the client. + */ + if (usingProcessLoopback) { + bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); + } + pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; } @@ -22154,7 +22416,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_IPropertyStore *pProperties; hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { - PROPVARIANT varName; + MA_PROPVARIANT varName; ma_PropVariantInit(&varName); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); if (SUCCEEDED(hr)) { @@ -22308,13 +22570,13 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev pDevice->capture.internalPeriods = data.periodsOut; ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ - ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); + ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); } if (deviceType == ma_device_type_playback) { @@ -22329,13 +22591,13 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev pDevice->playback.internalPeriods = data.periodsOut; ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); /* We must always have a valid ID because rerouting will look at it. */ - ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); + ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); } return MA_SUCCESS; @@ -22398,7 +22660,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, however, because we want to block until we actually have something for the first call to ma_device_read(). */ - pDevice->wasapi.hEventCapture = CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ + pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ if (pDevice->wasapi.hEventCapture == NULL) { result = ma_result_from_GetLastError(GetLastError()); @@ -22414,13 +22676,13 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); return result; } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ - ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); + ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); /* The descriptor needs to be updated with actual values. */ pDescriptorCapture->format = data.formatOut; @@ -22460,7 +22722,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDevice->wasapi.pAudioClientCapture = NULL; } - CloseHandle(pDevice->wasapi.hEventCapture); + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); pDevice->wasapi.hEventCapture = NULL; } return result; @@ -22480,7 +22742,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able to get passed WaitForMultipleObjects(). */ - pDevice->wasapi.hEventPlayback = CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ + pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ if (pDevice->wasapi.hEventPlayback == NULL) { result = ma_result_from_GetLastError(GetLastError()); @@ -22494,7 +22756,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDevice->wasapi.pAudioClientCapture = NULL; } - CloseHandle(pDevice->wasapi.hEventCapture); + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); pDevice->wasapi.hEventCapture = NULL; } @@ -22510,13 +22772,13 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); return result; } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); /* We must always have a valid ID because rerouting will look at it. */ - ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); + ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); /* The descriptor needs to be updated with actual values. */ pDescriptorPlayback->format = data.formatOut; @@ -22544,7 +22806,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf ma_mutex_init(&pDevice->wasapi.rerouteLock); - hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_device_uninit__wasapi(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); @@ -22655,17 +22917,17 @@ static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) HRESULT hr; if (pDevice->pContext->wasapi.hAvrt) { - LPCWSTR pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); + const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); if (pTaskName) { DWORD idx = 0; - pDevice->wasapi.hAvrtHandle = ((MA_PFN_AvSetMmThreadCharacteristicsW)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsW)(pTaskName, &idx); + pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); } } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device."); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); return ma_result_from_HRESULT(hr); } @@ -22675,7 +22937,7 @@ static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device."); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); return ma_result_from_HRESULT(hr); } @@ -22748,7 +23010,7 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); } else { ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; @@ -22772,8 +23034,8 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) } prevFramesAvaialablePlayback = framesAvailablePlayback; - WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime * 1000); - ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */ + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000); + ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ } } } @@ -22983,7 +23245,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui timeoutInMilliseconds = 10; } - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { if (pDevice->type == ma_device_type_loopback) { continue; /* Keep waiting in loopback mode. */ } else { @@ -23068,7 +23330,7 @@ static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames whether or not we need to wait for more data. */ if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { result = MA_ERROR; break; /* Wait failed. Probably timed out. */ } @@ -23094,7 +23356,7 @@ static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames } else { if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { /* Not enough data available. We need to wait for more. */ - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { result = MA_ERROR; break; /* Wait failed. Probably timed out. */ } @@ -23133,33 +23395,32 @@ static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) static ma_result ma_context_uninit__wasapi(ma_context* pContext) { + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); + MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_wasapi); - if (pContext->wasapi.commandThread != NULL) { - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); - ma_context_post_command__wasapi(pContext, &cmd); - ma_thread_wait(&pContext->wasapi.commandThread); + ma_context_post_command__wasapi(pContext, &cmd); + ma_thread_wait(&pContext->wasapi.commandThread); - if (pContext->wasapi.hAvrt) { - ma_dlclose(pContext, pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - - #if defined(MA_WIN32_UWP) - { - if (pContext->wasapi.hMMDevapi) { - ma_dlclose(pContext, pContext->wasapi.hMMDevapi); - pContext->wasapi.hMMDevapi = NULL; - } - } - #endif - - /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); + if (pContext->wasapi.hAvrt) { + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; } + #if defined(MA_WIN32_UWP) + { + if (pContext->wasapi.hMMDevapi) { + ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + pContext->wasapi.hMMDevapi = NULL; + } + } + #endif + + /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + return MA_SUCCESS; } @@ -23284,12 +23545,12 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsW = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsW"); + pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); /* If either function could not be found, disable use of avrt entirely. */ - if (!pContext->wasapi.AvSetMmThreadCharacteristicsW || !pContext->wasapi.AvRevertMmThreadcharacteristics) { - pContext->wasapi.AvSetMmThreadCharacteristicsW = NULL; + if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { + pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; ma_dlclose(pContext, pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; @@ -23375,7 +23636,7 @@ typedef struct DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; - WAVEFORMATEX* lpwfxFormat; + MA_WAVEFORMATEX* lpwfxFormat; GUID guid3DAlgorithm; } MA_DSBUFFERDESC; @@ -23385,7 +23646,7 @@ typedef struct DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; - WAVEFORMATEX* lpwfxFormat; + MA_WAVEFORMATEX* lpwfxFormat; DWORD dwFXCount; void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ } MA_DSCBUFFERDESC; @@ -23509,7 +23770,7 @@ typedef struct /* IDirectSoundBuffer */ HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); @@ -23518,7 +23779,7 @@ typedef struct HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); - HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat); + HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); @@ -23535,7 +23796,7 @@ static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pTh static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } @@ -23544,7 +23805,7 @@ static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } @@ -23589,7 +23850,7 @@ typedef struct /* IDirectSoundCaptureBuffer */ HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); @@ -23606,7 +23867,7 @@ static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCapt static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } @@ -23636,11 +23897,11 @@ static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pT static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } -typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); +typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) { @@ -23737,7 +23998,7 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma /* The cooperative level must be set before doing anything else. */ hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == NULL) { + if (hWnd == 0) { hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); } @@ -23889,7 +24150,7 @@ typedef struct ma_bool32 terminated; } ma_context_enumerate_devices_callback_data__dsound; -static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) +static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) { ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; ma_device_info deviceInfo; @@ -23955,7 +24216,7 @@ typedef struct ma_bool32 found; } ma_context_get_device_info_callback_data__dsound; -static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) +static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) { ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; MA_ASSERT(pData != NULL); @@ -24160,7 +24421,7 @@ static ma_result ma_device_uninit__dsound(ma_device* pDevice) return MA_SUCCESS; } -static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF) +static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) { GUID subformat; @@ -24197,14 +24458,14 @@ static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 c } MA_ZERO_OBJECT(pWF); - pWF->Format.cbSize = sizeof(*pWF); - pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pWF->Format.nChannels = (WORD)channels; - pWF->Format.nSamplesPerSec = (DWORD)sampleRate; - pWF->Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - pWF->Format.nBlockAlign = (WORD)(pWF->Format.nChannels * pWF->Format.wBitsPerSample / 8); - pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec; - pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample; + pWF->cbSize = sizeof(*pWF); + pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; + pWF->nChannels = (WORD)channels; + pWF->nSamplesPerSec = (DWORD)sampleRate; + pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); + pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); + pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; + pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); pWF->SubFormat = subformat; @@ -24247,12 +24508,12 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf full-duplex mode. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - WAVEFORMATEXTENSIBLE wf; + MA_WAVEFORMATEXTENSIBLE wf; MA_DSCBUFFERDESC descDS; ma_uint32 periodSizeInFrames; ma_uint32 periodCount; char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - WAVEFORMATEXTENSIBLE* pActualFormat; + MA_WAVEFORMATEXTENSIBLE* pActualFormat; result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); if (result != MA_SUCCESS) { @@ -24265,26 +24526,26 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf return result; } - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec); + result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); if (result != MA_SUCCESS) { ma_device_uninit__dsound(pDevice); return result; } - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.Format.nSamplesPerSec, pConfig->performanceProfile); + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; MA_ZERO_OBJECT(&descDS); descDS.dwSize = sizeof(descDS); descDS.dwFlags = 0; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.Format.nBlockAlign; - descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; + descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; + descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); @@ -24293,8 +24554,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* Get the _actual_ properties of the buffer. */ - pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; + hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); @@ -24302,12 +24563,12 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* We can now start setting the output data formats. */ - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); - pDescriptorCapture->channels = pActualFormat->Format.nChannels; - pDescriptorCapture->sampleRate = pActualFormat->Format.nSamplesPerSec; + pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); + pDescriptorCapture->channels = pActualFormat->nChannels; + pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; /* Get the native channel map based on the channel mask. */ - if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); } else { ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); @@ -24335,11 +24596,11 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - WAVEFORMATEXTENSIBLE wf; + MA_WAVEFORMATEXTENSIBLE wf; MA_DSBUFFERDESC descDSPrimary; MA_DSCAPS caps; char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - WAVEFORMATEXTENSIBLE* pActualFormat; + MA_WAVEFORMATEXTENSIBLE* pActualFormat; ma_uint32 periodSizeInFrames; ma_uint32 periodCount; MA_DSBUFFERDESC descDS; @@ -24395,21 +24656,21 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } if (pDescriptorPlayback->channels == 0) { - wf.Format.nChannels = nativeChannelCount; + wf.nChannels = nativeChannelCount; wf.dwChannelMask = nativeChannelMask; } if (pDescriptorPlayback->sampleRate == 0) { /* We base the sample rate on the values returned by GetCaps(). */ if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); + wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); } else { - wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate; + wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; } } - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; /* From MSDN: @@ -24418,7 +24679,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer and compare the result with the format that was requested with the SetFormat method. */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, &wf.Format); + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); if (FAILED(hr)) { /* If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have @@ -24426,15 +24687,15 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will use 44100 for the sample rate. */ - wf.Format.cbSize = sizeof(wf.Format); - wf.Format.wFormatTag = WAVE_FORMAT_PCM; - wf.Format.wBitsPerSample = 16; - wf.Format.nChannels = nativeChannelCount; - wf.Format.nSamplesPerSec = 44100; - wf.Format.nBlockAlign = wf.Format.nChannels * (wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; + wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.wBitsPerSample = 16; + wf.nChannels = nativeChannelCount; + wf.nSamplesPerSec = 44100; + wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, &wf.Format); + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); @@ -24443,8 +24704,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* Get the _actual_ properties of the buffer. */ - pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; + hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); @@ -24452,12 +24713,12 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* We now have enough information to start setting some output properties. */ - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); - pDescriptorPlayback->channels = pActualFormat->Format.nChannels; - pDescriptorPlayback->sampleRate = pActualFormat->Format.nSamplesPerSec; + pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); + pDescriptorPlayback->channels = pActualFormat->nChannels; + pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; /* Get the internal channel map based on the channel mask. */ - if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); } else { ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); @@ -24486,7 +24747,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf descDS.dwSize = sizeof(descDS); descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); - descDS.lpwfxFormat = &pActualFormat->Format; + descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); @@ -25081,16 +25342,75 @@ WinMM Backend #ifdef MA_HAS_WINMM /* -Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures -are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping -the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version. +Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN +is defined. We need to define the types and functions we need manually. */ +#define MA_MMSYSERR_NOERROR 0 +#define MA_MMSYSERR_ERROR 1 +#define MA_MMSYSERR_BADDEVICEID 2 +#define MA_MMSYSERR_INVALHANDLE 5 +#define MA_MMSYSERR_NOMEM 7 +#define MA_MMSYSERR_INVALFLAG 10 +#define MA_MMSYSERR_INVALPARAM 11 +#define MA_MMSYSERR_HANDLEBUSY 12 + +#define MA_CALLBACK_EVENT 0x00050000 +#define MA_WAVE_ALLOWSYNC 0x0002 + +#define MA_WHDR_DONE 0x00000001 +#define MA_WHDR_PREPARED 0x00000002 +#define MA_WHDR_BEGINLOOP 0x00000004 +#define MA_WHDR_ENDLOOP 0x00000008 +#define MA_WHDR_INQUEUE 0x00000010 + +#define MA_MAXPNAMELEN 32 + +typedef void* MA_HWAVEIN; +typedef void* MA_HWAVEOUT; +typedef UINT MA_MMRESULT; +typedef UINT MA_MMVERSION; + typedef struct { WORD wMid; WORD wPid; - MMVERSION vDriverVersion; - CHAR szPname[MAXPNAMELEN]; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; +} MA_WAVEINCAPSA; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + DWORD dwSupport; +} MA_WAVEOUTCAPSA; + +typedef struct tagWAVEHDR +{ + char* lpData; + DWORD dwBufferLength; + DWORD dwBytesRecorded; + DWORD_PTR dwUser; + DWORD dwFlags; + DWORD dwLoops; + struct tagWAVEHDR* lpNext; + DWORD_PTR reserved; +} MA_WAVEHDR; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; WORD wReserved1; @@ -25099,12 +25419,13 @@ typedef struct GUID ProductGuid; GUID NameGuid; } MA_WAVEOUTCAPS2A; + typedef struct { WORD wMid; WORD wPid; - MMVERSION vDriverVersion; - CHAR szPname[MAXPNAMELEN]; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; WORD wReserved1; @@ -25113,36 +25434,37 @@ typedef struct GUID NameGuid; } MA_WAVEINCAPS2A; -typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); -typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc); -typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo); -typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo); -typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); -typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic); -typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi); -typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi); -typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi); +typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); +typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); -static ma_result ma_result_from_MMRESULT(MMRESULT resultMM) +static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) { - switch (resultMM) { - case MMSYSERR_NOERROR: return MA_SUCCESS; - case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; - case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; - case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; - case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; - case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; - case MMSYSERR_HANDLEBUSY: return MA_BUSY; - case MMSYSERR_ERROR: return MA_ERROR; - default: return MA_ERROR; + switch (resultMM) + { + case MA_MMSYSERR_NOERROR: return MA_SUCCESS; + case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; + case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; + case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; + case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; + case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; + case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; + case MA_MMSYSERR_ERROR: return MA_ERROR; + default: return MA_ERROR; } } @@ -25178,7 +25500,7 @@ we can do things generically and typesafely. Names are being kept the same for c */ typedef struct { - CHAR szPname[MAXPNAMELEN]; + CHAR szPname[MA_MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; GUID NameGuid; @@ -25264,7 +25586,7 @@ static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WOR return MA_SUCCESS; } -static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF) +static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) { ma_result result; @@ -25321,7 +25643,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, name, and then concatenate the name from the registry. */ if (!ma_is_guid_null(&pCaps->NameGuid)) { - wchar_t guidStrW[256]; + WCHAR guidStrW[256]; if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { char guidStr[256]; char keyStr[1024]; @@ -25335,7 +25657,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { BYTE nameFromReg[512]; DWORD nameFromRegSize = sizeof(nameFromReg); - LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize); + LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); if (resultWin32 == ERROR_SUCCESS) { @@ -25429,13 +25751,13 @@ static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_en /* Playback. */ playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { - MMRESULT result; + MA_MMRESULT result; MA_WAVEOUTCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); @@ -25458,13 +25780,13 @@ static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_en /* Capture. */ captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { - MMRESULT result; + MA_MMRESULT result; MA_WAVEINCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); @@ -25506,23 +25828,23 @@ static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_devi } if (deviceType == ma_device_type_playback) { - MMRESULT result; + MA_MMRESULT result; MA_WAVEOUTCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); } } else { - MMRESULT result; + MA_MMRESULT result; MA_WAVEINCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); } } @@ -25536,13 +25858,13 @@ static ma_result ma_device_uninit__winmm(ma_device* pDevice) MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture); + ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); CloseHandle((HANDLE)pDevice->winmm.hEventCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); + ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); } @@ -25599,9 +25921,9 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi /* The capture device needs to be initialized first. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - WAVEINCAPSA caps; - WAVEFORMATEX wf; - MMRESULT resultMM; + MA_WAVEINCAPSA caps; + MA_WAVEFORMATEX wf; + MA_MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); @@ -25611,7 +25933,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi } /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) { + if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; goto on_error; } @@ -25622,8 +25944,8 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi goto on_error; } - resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + if (resultMM != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } @@ -25637,9 +25959,9 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - WAVEOUTCAPSA caps; - WAVEFORMATEX wf; - MMRESULT resultMM; + MA_WAVEOUTCAPSA caps; + MA_WAVEFORMATEX wf; + MA_MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); @@ -25649,7 +25971,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi } /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) { + if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; goto on_error; } @@ -25660,8 +25982,8 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi goto on_error; } - resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + if (resultMM != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } @@ -25681,10 +26003,10 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi */ heapSize = 0; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); + heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); } pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); @@ -25700,27 +26022,27 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi if (pConfig->deviceType == ma_device_type_capture) { pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount)); + pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); } else { pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); + pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); } /* Prepare headers. */ for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; + ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); /* - The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means + The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means it's unlocked and available for writing. A value of 1 means it's locked. */ - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; } } @@ -25729,27 +26051,27 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi if (pConfig->deviceType == ma_device_type_playback) { pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDescriptorPlayback->periodCount); + pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); } else { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount)); - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); + pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); } /* Prepare headers. */ for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; + ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); /* - The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means + The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means it's unlocked and available for writing. A value of 1 means it's locked. */ - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; } } @@ -25760,22 +26082,22 @@ on_error: if (pDevice->winmm.pWAVEHDRCapture != NULL) { ma_uint32 iPeriod; for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); + ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); } } - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture); + ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->winmm.pWAVEHDRCapture != NULL) { ma_uint32 iPeriod; for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR)); + ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); } } - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); + ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); } ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); @@ -25792,19 +26114,19 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - MMRESULT resultMM; - WAVEHDR* pWAVEHDR; + MA_MMRESULT resultMM; + MA_WAVEHDR* pWAVEHDR; ma_uint32 iPeriod; - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventCapture); /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); return ma_result_from_MMRESULT(resultMM); } @@ -25814,8 +26136,8 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) } /* Capture devices need to be explicitly started, unlike playback devices. */ - resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); return ma_result_from_MMRESULT(resultMM); } @@ -25830,7 +26152,7 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) static ma_result ma_device_stop__winmm(ma_device* pDevice) { - MMRESULT resultMM; + MA_MMRESULT resultMM; MA_ASSERT(pDevice != NULL); @@ -25839,22 +26161,22 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) return MA_INVALID_ARGS; } - resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_uint32 iPeriod; - WAVEHDR* pWAVEHDR; + MA_WAVEHDR* pWAVEHDR; if (pDevice->winmm.hDevicePlayback == NULL) { return MA_INVALID_ARGS; } /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { @@ -25865,8 +26187,8 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) } } - resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); } } @@ -25877,9 +26199,9 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { ma_result result = MA_SUCCESS; - MMRESULT resultMM; + MA_MMRESULT resultMM; ma_uint32 totalFramesWritten; - WAVEHDR* pWAVEHDR; + MA_WAVEHDR* pWAVEHDR; MA_ASSERT(pDevice != NULL); MA_ASSERT(pPCMFrames != NULL); @@ -25888,7 +26210,7 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram *pFramesWritten = 0; } - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; /* Keep processing as much data as possible. */ totalFramesWritten = 0; @@ -25913,14 +26235,14 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram /* If we've consumed the buffer entirely we need to write it out to the device. */ if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); /* The device will be started here. */ - resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); break; @@ -25948,7 +26270,7 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram } /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) { + if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ pDevice->winmm.headerFramesConsumedPlayback = 0; } @@ -25969,9 +26291,9 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { ma_result result = MA_SUCCESS; - MMRESULT resultMM; + MA_MMRESULT resultMM; ma_uint32 totalFramesRead; - WAVEHDR* pWAVEHDR; + MA_WAVEHDR* pWAVEHDR; MA_ASSERT(pDevice != NULL); MA_ASSERT(pPCMFrames != NULL); @@ -25980,7 +26302,7 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ *pFramesRead = 0; } - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; /* Keep processing as much data as possible. */ totalFramesRead = 0; @@ -26002,14 +26324,14 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ /* If we've consumed the buffer entirely we need to add it back to the device. */ if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventCapture); /* The device will be started here. */ - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); break; @@ -26037,7 +26359,7 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ } /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) { + if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ pDevice->winmm.headerFramesConsumedCapture = 0; } @@ -29932,7 +30254,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ma_pa_channel_map cmap; ma_pa_buffer_attr attr; const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_channel_map* pActualCMap = NULL; const ma_pa_buffer_attr* pActualAttr = NULL; ma_uint32 iChannel; ma_pa_stream_flags_t streamFlags; @@ -29988,6 +30309,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sourceInfo.sample_spec; cmap = sourceInfo.channel_map; + /* Use the requested channel count if we have one. */ + if (pDescriptorCapture->channels != 0) { + ss.channels = pDescriptorCapture->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { ss.rate = pDescriptorCapture->sampleRate; @@ -30082,11 +30411,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorCapture->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } @@ -30129,6 +30453,15 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sinkInfo.sample_spec; cmap = sinkInfo.channel_map; + /* Use the requested channel count if we have one. */ + if (pDescriptorPlayback->channels != 0) { + ss.channels = pDescriptorPlayback->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + + /* Use the requested sample rate if one was specified. */ if (pDescriptorPlayback->sampleRate != 0) { ss.rate = pDescriptorPlayback->sampleRate; @@ -30227,11 +30560,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorPlayback->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } @@ -31270,10 +31598,11 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co { #ifndef MA_NO_RUNTIME_LINKING const char* libjackNames[] = { -#ifdef MA_WIN32 +#if defined(MA_WIN32) "libjack.dll", "libjack64.dll" -#else +#endif +#if defined(MA_UNIX) "libjack.so", "libjack.so.0" #endif @@ -35877,8 +36206,13 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c "/dev/audio", "/dev/audio0" }; + const char* pDefaultDeviceCtlNames[] = { + "/dev/audioctl", + "/dev/audioctl0" + }; int fd; int fdFlags = 0; + size_t iDefaultDevice = (size_t)-1; ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; @@ -35897,11 +36231,11 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c } /*fdFlags |= O_NONBLOCK;*/ + /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ if (pDescriptor->pDeviceID == NULL) { /* Default device. */ - size_t iDevice; - for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) { - fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0); + for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { + fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); if (fd != -1) { break; } @@ -35909,6 +36243,16 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c } else { /* Specific device. */ fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); + + for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { + if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { + break; + } + } + + if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { + iDefaultDevice = (size_t)-1; + } } if (fd == -1) { @@ -35919,6 +36263,7 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ { audio_info_t fdInfo; + int fdInfoResult = -1; /* The documentation is a little bit unclear to me as to how it handles formats. It says the @@ -35938,6 +36283,28 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c */ AUDIO_INITINFO(&fdInfo); + /* + Get the default format from the audioctl file if we're asking for a default device. If we + retrieve it from /dev/audio it'll default to mono 8000Hz. + */ + if (iDefaultDevice != (size_t)-1) { + /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ + int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); + if (fdctl != -1) { + fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); + close(fdctl); + } + } + + if (fdInfoResult == -1) { + /* We still don't have the default device info so just retrieve it from the main audio device. */ + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); + return ma_result_from_errno(errno); + } + } + /* We get the driver to do as much of the data conversion as possible. */ if (deviceType == ma_device_type_capture) { fdInfo.mode = AUMODE_RECORD; @@ -40363,7 +40730,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d pDevice->playback.inputCacheConsumed = 0; pDevice->playback.inputCacheRemaining = 0; - if ((pDevice->type == ma_device_type_duplex && ma_context_is_backend_asynchronous(pDevice->pContext)) || /* Duplex with asynchronous backend. */ + if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ { /* We need a heap allocated cache. We want to size this based on the period size. */ @@ -40601,11 +40968,15 @@ static ma_bool32 ma_device__is_initialized(ma_device* pDevice) static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) ma_CoUninitialize(pContext); - ma_dlclose(pContext, pContext->win32.hUser32DLL); + + #if defined(MA_WIN32_DESKTOP) + ma_dlclose(pContext, pContext->win32.hUser32DLL); + ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); + #endif + ma_dlclose(pContext, pContext->win32.hOle32DLL); - ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); #else (void)pContext; #endif @@ -40615,7 +40986,29 @@ static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) { -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + #if defined(MA_WIN32_DESKTOP) + /* User32.dll */ + pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); + if (pContext->win32.hUser32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); + + + /* Advapi32.dll */ + pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); + if (pContext->win32.hAdvapi32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + #endif + /* Ole32.dll */ pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll"); if (pContext->win32.hOle32DLL == NULL) { @@ -40629,27 +41022,6 @@ static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree"); pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear"); pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2"); - - - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #else (void)pContext; /* Unused. */ #endif @@ -41078,7 +41450,7 @@ MA_API ma_result ma_context_uninit(ma_context* pContext) return MA_SUCCESS; } -MA_API size_t ma_context_sizeof() +MA_API size_t ma_context_sizeof(void) { return sizeof(ma_context); } @@ -48353,7 +48725,7 @@ static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_inte /* Initialize the running gain. */ for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - float t = (pGainer->pOldGains[iChannel] - pGainer->pNewGains[iChannel]) * pGainer->masterVolume; + float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; pRunningGainDelta[iChannel] = t * d; pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); } @@ -49652,6 +50024,7 @@ MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma config.coneOuterGain = 0.0f; config.dopplerFactor = 1; config.directionalAttenuationFactor = 1; + config.minSpatializationChannelGain = 0.2f; config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ return config; @@ -49792,6 +50165,7 @@ MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* p pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; pSpatializer->coneOuterGain = pConfig->coneOuterGain; pSpatializer->dopplerFactor = pConfig->dopplerFactor; + pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); @@ -50073,6 +50447,26 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, /* Clamp the gain. */ gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); + /* + The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel + gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions + to avoid harsh changes in gain. + */ + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + pSpatializer->pNewChannelGainsOut[iChannel] = gain; + } + + /* + Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore + the whole section of code here because we need to update some internal spatialization state. + */ + if (ma_spatializer_listener_is_enabled(pListener)) { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + /* Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the @@ -50094,19 +50488,6 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized position of the sound. */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } /* Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's @@ -50143,7 +50524,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, Summary: 0 = more extreme panning; 1 = no panning. */ - dMin = 0.2f; /* TODO: Consider making this configurable. */ + dMin = pSpatializer->minSpatializationChannelGain; /* At this point, "d" will be positive if the sound is on the same side as the channel and negative if @@ -53476,7 +53857,7 @@ MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_co Data Conversion **************************************************************************************************************************************************************/ -MA_API ma_data_converter_config ma_data_converter_config_init_default() +MA_API ma_data_converter_config ma_data_converter_config_init_default(void) { ma_data_converter_config config; MA_ZERO_OBJECT(&config); @@ -56130,6 +56511,85 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB +static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + ma_result result; + ma_uint64 totalFramesRead; + + MA_ASSERT(pRB != NULL); + + /* We need to run this in a loop since the ring buffer itself may loop. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + void* pMappedBuffer; + ma_uint32 mappedFrameCount; + ma_uint64 framesToRead = frameCount - totalFramesRead; + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + mappedFrameCount = (ma_uint32)framesToRead; + result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); + if (result != MA_SUCCESS) { + break; + } + + if (mappedFrameCount == 0) { + break; /* <-- End of ring buffer. */ + } + + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); + + result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + totalFramesRead += mappedFrameCount; + } + + *pFramesRead = totalFramesRead; + return MA_SUCCESS; +} + +static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + MA_ASSERT(pRB != NULL); + + if (pFormat != NULL) { + *pFormat = pRB->format; + } + + if (pChannels != NULL) { + *pChannels = pRB->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pRB->sampleRate; + } + + /* Just assume the default channel map. */ + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); + } + + return MA_SUCCESS; +} + +static ma_data_source_vtable ma_gRBDataSourceVTable = +{ + ma_pcm_rb_data_source__on_read, + NULL, /* onSeek */ + ma_pcm_rb_data_source__on_get_data_format, + NULL, /* onGetCursor */ + NULL, /* onGetLength */ + NULL, /* onSetLooping */ + 0 +}; + static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) { MA_ASSERT(pRB != NULL); @@ -56158,8 +56618,21 @@ MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint return result; } - pRB->format = format; - pRB->channels = channels; + pRB->format = format; + pRB->channels = channels; + pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ + + /* The PCM ring buffer is a data source. We need to get that set up as well. */ + { + ma_data_source_config dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &ma_gRBDataSourceVTable; + + result = ma_data_source_init(&dataSourceConfig, &pRB->ds); + if (result != MA_SUCCESS) { + ma_rb_uninit(&pRB->rb); + return result; + } + } return MA_SUCCESS; } @@ -56175,6 +56648,7 @@ MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) return; } + ma_data_source_uninit(&pRB->ds); ma_rb_uninit(&pRB->rb); } @@ -56326,6 +56800,42 @@ MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferInde return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); } +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return ma_format_unknown; + } + + return pRB->format; +} + +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->channels; +} + +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->sampleRate; +} + +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) +{ + if (pRB == NULL) { + return; + } + + pRB->sampleRate = sampleRate; +} + MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) @@ -58273,7 +58783,7 @@ MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; ma_result result; - size_t bytesRead; + size_t bytesRead = 0; if (pBytesRead != NULL) { *pBytesRead = 0; @@ -58451,7 +58961,11 @@ MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFileP } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) + #define MA_USE_WIN32_FILEIO +#endif + +#if defined(MA_USE_WIN32_FILEIO) static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) { *pDesiredAccess = 0; @@ -58920,7 +59434,7 @@ static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uin return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); #else return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); @@ -58939,7 +59453,7 @@ static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, m return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); #else return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); @@ -58952,7 +59466,7 @@ static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_close__win32(pVFS, file); #else return ma_default_vfs_close__stdio(pVFS, file); @@ -58969,7 +59483,7 @@ static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); #else return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); @@ -58986,7 +59500,7 @@ static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); #else return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); @@ -58999,7 +59513,7 @@ static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 of return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_seek__win32(pVFS, file, offset, origin); #else return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); @@ -59018,7 +59532,7 @@ static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* p return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_tell__win32(pVFS, file, pCursor); #else return ma_default_vfs_tell__stdio(pVFS, file, pCursor); @@ -59037,7 +59551,7 @@ static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_inf return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_info__win32(pVFS, file, pInfo); #else return ma_default_vfs_info__stdio(pVFS, file, pInfo); @@ -62672,6 +63186,81 @@ static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) return MA_SUCCESS; } + +static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) +{ + ma_result result; + stb_vorbis* stb; + size_t dataSize = 0; + size_t dataCapacity = 0; + ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ + + for (;;) { + int vorbisError; + int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ + size_t bytesRead; + ma_uint8* pNewData; + + /* Allocate memory for the new chunk. */ + dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); + if (pNewData == NULL) { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + pData = pNewData; + + /* Read in the next chunk. */ + result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); + dataSize += bytesRead; + + if (result != MA_SUCCESS) { + ma_free(pData, &pVorbis->allocationCallbacks); + return result; + } + + /* We have a maximum of 31 bits with stb_vorbis. */ + if (dataSize > INT_MAX) { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_TOO_BIG; + } + + stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); + if (stb != NULL) { + /* + Successfully opened the Vorbis decoder. We might have some leftover unprocessed + data so we'll need to move that down to the front. + */ + dataSize -= (size_t)consumedDataSize; /* Consume the data. */ + MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); + + /* + We need to track the start point so we can seek back to the start of the audio + data when seeking. + */ + pVorbis->push.audioStartOffsetInBytes = consumedDataSize; + + break; + } else { + /* Failed to open the decoder. */ + if (vorbisError == VORBIS_need_more_data) { + continue; + } else { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ + } + } + } + + MA_ASSERT(stb != NULL); + pVorbis->stb = stb; + pVorbis->push.pData = pData; + pVorbis->push.dataSize = dataSize; + pVorbis->push.dataCapacity = dataCapacity; + + return MA_SUCCESS; +} #endif MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) @@ -62700,81 +63289,17 @@ MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_ pushing API. In order for us to be able to successfully initialize the decoder we need to supply it with enough data. We need to keep loading data until we have enough. */ - stb_vorbis* stb; - size_t dataSize = 0; - size_t dataCapacity = 0; - ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ - - for (;;) { - int vorbisError; - int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ - size_t bytesRead; - ma_uint8* pNewData; - - /* Allocate memory for the new chunk. */ - dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, pAllocationCallbacks); - if (pNewData == NULL) { - ma_free(pData, pAllocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pData = pNewData; - - /* Read in the next chunk. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); - dataSize += bytesRead; - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - /* We have a maximum of 31 bits with stb_vorbis. */ - if (dataSize > INT_MAX) { - ma_free(pData, pAllocationCallbacks); - return MA_TOO_BIG; - } - - stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); - if (stb != NULL) { - /* - Successfully opened the Vorbis decoder. We might have some leftover unprocessed - data so we'll need to move that down to the front. - */ - dataSize -= (size_t)consumedDataSize; /* Consume the data. */ - MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); - - /* - We need to track the start point so we can seek back to the start of the audio - data when seeking. - */ - pVorbis->push.audioStartOffsetInBytes = consumedDataSize; - - break; - } else { - /* Failed to open the decoder. */ - if (vorbisError == VORBIS_need_more_data) { - continue; - } else { - ma_free(pData, pAllocationCallbacks); - return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ - } - } + result = ma_stbvorbis_init_internal_decoder_push(pVorbis); + if (result != MA_SUCCESS) { + return result; } - MA_ASSERT(stb != NULL); - pVorbis->stb = stb; - pVorbis->push.pData = pData; - pVorbis->push.dataSize = dataSize; - pVorbis->push.dataCapacity = dataCapacity; - pVorbis->usingPushMode = MA_TRUE; result = ma_stbvorbis_post_init(pVorbis); if (result != MA_SUCCESS) { stb_vorbis_close(pVorbis->stb); - ma_free(pData, pAllocationCallbacks); + ma_free(pVorbis->push.pData, pAllocationCallbacks); return result; } @@ -63076,28 +63601,39 @@ MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 ma_result result; float buffer[4096]; - /* - This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs - a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we - find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis. + /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ + if (frameIndex < pVorbis->cursor) { + if (frameIndex > 0x7FFFFFFF) { + return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ + } - TODO: Use seeking logic documented for stb_vorbis_flush_pushdata(). - */ + /* + This is wildly inefficient due to me having trouble getting sample exact seeking working + robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work + perfectly is to reinitialize the decoder. Note that we only enter this path when seeking + backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. + */ + stb_vorbis_close(pVorbis->stb); + ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); - /* Seek to the start of the audio data in the file to begin with. */ - result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, pVorbis->push.audioStartOffsetInBytes, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; + MA_ZERO_OBJECT(&pVorbis->push); + + /* Seek to the start of the file. */ + result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_stbvorbis_init_internal_decoder_push(pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + /* At this point we should be sitting on the first frame. */ + pVorbis->cursor = 0; } - stb_vorbis_flush_pushdata(pVorbis->stb); - pVorbis->push.framesConsumed = 0; - pVorbis->push.framesRemaining = 0; - pVorbis->push.dataSize = 0; - - /* Move the cursor back to the start. We'll increment this in the loop below. */ - pVorbis->cursor = 0; - + /* We're just brute-forcing this for now. */ while (pVorbis->cursor < frameIndex) { ma_uint64 framesRead; ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; @@ -66769,7 +67305,7 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon /* Create the job threads last to ensure the threads has access to valid data. */ for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, 0, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); + result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); if (result != MA_SUCCESS) { ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); @@ -67402,7 +67938,12 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; - result = ma_resource_manager_post_job(pResourceManager, &job); + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + result = ma_job_process(&job); + } else { + result = ma_resource_manager_post_job(pResourceManager, &job); + } + if (result != MA_SUCCESS) { /* Failed to post job. Probably ran out of memory. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); @@ -67415,12 +67956,13 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); + ma_resource_manager_inline_notification_uninit(pInitNotification); + } else { + /* These will have been freed by the job thread, but with WAIT_INIT they will already have happend sinced the job has already been handled. */ + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); } - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); @@ -67859,7 +68401,13 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; - result = ma_resource_manager_post_job(pResourceManager, &job); + /* If we need to wait for initialization to complete we can just process the job in place. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + result = ma_job_process(&job); + } else { + result = ma_resource_manager_post_job(pResourceManager, &job); + } + if (result != MA_SUCCESS) { /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); @@ -68464,7 +69012,7 @@ static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, ma_resource_manager_data_stream_cb__set_looping, - MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT + 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ }; static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) @@ -69558,6 +70106,12 @@ done: /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + + /* A busy result should be considered successful from the point of view of the job system. */ + if (result == MA_BUSY) { + result = MA_SUCCESS; + } + return result; } @@ -69839,7 +70393,7 @@ static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob goto done; } - /* Retrieve the total length of the file before marking the decoder are loaded. */ + /* Retrieve the total length of the file before marking the decoder as loaded. */ if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); if (result != MA_SUCCESS) { @@ -72962,6 +73516,27 @@ Engine **************************************************************************************************************************************************************/ #define MA_SEEK_TARGET_NONE (~(ma_uint64)0) + +static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) +{ + MA_ASSERT(pSound != NULL); + c89atomic_exchange_32(&pSound->atEnd, atEnd); + + /* Fire any callbacks or events. */ + if (atEnd) { + if (pSound->endCallback != NULL) { + pSound->endCallback(pSound->pEndCallbackUserData, pSound); + } + } +} + +static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) +{ + MA_ASSERT(pSound != NULL); + return c89atomic_load_32(&pSound->atEnd); +} + + MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) { ma_engine_node_config config; @@ -73039,8 +73614,16 @@ static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float vo return MA_INVALID_ARGS; } - /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ - ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); + ma_atomic_float_set(&pEngineNode->volume, volume); + + /* If we're not smoothing we should bypass the volume gainer entirely. */ + if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { + /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ + ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); + } else { + /* We're using volume smoothing, so apply the master volume to the gainer. */ + ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); + } return MA_SUCCESS; } @@ -73057,9 +73640,12 @@ static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, fl return MA_INVALID_ARGS; } - return ma_spatializer_get_master_volume(&pEngineNode->spatializer, pVolume); + *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); + + return MA_SUCCESS; } + static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_uint32 frameCountIn; @@ -73072,6 +73658,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo ma_bool32 isFadingEnabled; ma_bool32 isSpatializationEnabled; ma_bool32 isPanningEnabled; + ma_bool32 isVolumeSmoothingEnabled; frameCountIn = *pFrameCountIn; frameCountOut = *pFrameCountOut; @@ -73082,10 +73669,11 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo totalFramesProcessedIn = 0; totalFramesProcessedOut = 0; - isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); - isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; - isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); - isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; + isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); + isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; + isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); + isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; + isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; /* Keep going while we've still got data available for processing. */ while (totalFramesProcessedOut < frameCountOut) { @@ -73161,6 +73749,19 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo } } + /* + If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case + we'll want to apply our volume now. + */ + if (isVolumeSmoothingEnabled) { + if (isWorkingBufferValid) { + ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); + } else { + ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); + isWorkingBufferValid = MA_TRUE; + } + } + /* If at this point we still haven't actually done anything with the working buffer we need to just read straight from the input buffer. @@ -73192,11 +73793,21 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo if (channelsIn == channelsOut) { /* No channel conversion required. Just copy straight to the output buffer. */ - ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); + if (isVolumeSmoothingEnabled) { + /* Volume has already been applied. Just copy straight to the output buffer. */ + ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); + } else { + /* Volume has not been applied yet. Copy and apply volume in the same pass. */ + ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); + } } else { /* Channel conversion required. TODO: Add support for channel maps here. */ ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); - ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); + + /* If we're using smoothing, the volume will have already been applied. */ + if (!isVolumeSmoothingEnabled) { + ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); + } } } @@ -73299,7 +73910,7 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ if (result == MA_AT_END) { - c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */ + ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ } pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); @@ -73420,6 +74031,7 @@ typedef struct size_t baseNodeOffset; size_t resamplerOffset; size_t spatializerOffset; + size_t gainerOffset; } ma_engine_node_heap_layout; static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) @@ -73429,6 +74041,7 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo ma_node_config baseNodeConfig; ma_linear_resampler_config resamplerConfig; ma_spatializer_config spatializerConfig; + ma_gainer_config gainerConfig; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ @@ -73494,6 +74107,20 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + /* Gainer. Will not be used if we are not using smoothing. */ + if (pConfig->volumeSmoothTimeInPCMFrames > 0) { + gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); + + result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + } + + return MA_SUCCESS; } @@ -73527,6 +74154,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p ma_fader_config faderConfig; ma_spatializer_config spatializerConfig; ma_panner_config pannerConfig; + ma_gainer_config gainerConfig; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ @@ -73549,15 +74177,17 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p pEngineNode->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - pEngineNode->pEngine = pConfig->pEngine; - pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); - pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; - pEngineNode->pitch = 1; - pEngineNode->oldPitch = 1; - pEngineNode->oldDopplerPitch = 1; - pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; - pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; - pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; + pEngineNode->pEngine = pConfig->pEngine; + pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); + pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; + pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; + ma_atomic_float_set(&pEngineNode->volume, 1); + pEngineNode->pitch = 1; + pEngineNode->oldPitch = 1; + pEngineNode->oldDopplerPitch = 1; + pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; + pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; + pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); @@ -73637,6 +74267,18 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p goto error3; } + + /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ + if (pConfig->volumeSmoothTimeInPCMFrames > 0) { + gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); + + result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); + if (result != MA_SUCCESS) { + goto error3; + } + } + + return MA_SUCCESS; /* No need for allocation callbacks here because we use a preallocated heap. */ @@ -73685,6 +74327,10 @@ MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocati ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); /* Now that the node has been uninitialized we can safely uninitialize the rest. */ + if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { + ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); + } + ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); @@ -73808,6 +74454,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng } pEngine->monoExpansionMode = engineConfig.monoExpansionMode; + pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); #if !defined(MA_NO_RESOURCE_MANAGER) @@ -74180,16 +74827,36 @@ MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) return ma_node_graph_get_endpoint(&pEngine->nodeGraph); } -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) { return ma_node_graph_get_time(&pEngine->nodeGraph); } -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); +} + +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) { return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); } +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); +} + +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine); +} + +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); +} + MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) { return ma_node_graph_get_channels(&pEngine->nodeGraph); @@ -74603,9 +75270,14 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con source that provides this information upfront. */ engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; - engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; + engineNodeConfig.channelsIn = pConfig->channelsIn; + engineNodeConfig.channelsOut = pConfig->channelsOut; + engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; + engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; + + if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { + engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; + } /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ if (pConfig->pDataSource != NULL) { @@ -74791,7 +75463,7 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin /* We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) - the this will fail. + this will fail. */ pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); if (pSound->pResourceManagerDataSource == NULL) { @@ -74805,10 +75477,11 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin } config = ma_sound_config_init_2(pEngine); - config.pDataSource = pSound->pResourceManagerDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; + config.pDataSource = pSound->pResourceManagerDataSource; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; + config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); if (result != MA_SUCCESS) { @@ -74818,6 +75491,9 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin return result; } + /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ + pSound->ownsDataSource = MA_TRUE; + return MA_SUCCESS; } #endif @@ -74844,6 +75520,9 @@ MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pCo return MA_INVALID_ARGS; } + pSound->endCallback = pConfig->endCallback; + pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; + /* We need to load the sound differently depending on whether or not we're loading from a file. */ #ifndef MA_NO_RESOURCE_MANAGER if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { @@ -75407,7 +76086,7 @@ MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) return MA_FALSE; } - return ma_node_get_state_by_time(pSound, ma_engine_get_time(ma_sound_get_engine(pSound))) == ma_node_state_started; + return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; } MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) @@ -75459,7 +76138,7 @@ MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) return MA_FALSE; } - return c89atomic_load_32(&pSound->atEnd); + return ma_sound_get_at_end(pSound); } MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) @@ -75568,6 +76247,23 @@ MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); } +MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of an end is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + pSound->endCallback = callback; + pSound->pEndCallbackUserData = pUserData; + + return MA_SUCCESS; +} + MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) { @@ -76597,10 +77293,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars { drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); DRWAV_ASSERT(pChunkHeader != NULL); - if (bytesJustRead == sizeof(smplHeaderData)) { + if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { drwav_uint32 iSampleLoop; pMetadata->type = drwav_metadata_type_smpl; pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); @@ -76641,7 +77341,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse { drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueHeaderSectionData)) { pMetadata->type = drwav_metadata_type_cue; @@ -76676,7 +77380,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 instData[DRWAV_INST_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + drwav_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(instData)) { pMetadata->type = drwav_metadata_type_inst; @@ -76693,7 +77401,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_pars DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 acidData[DRWAV_ACID_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + drwav_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(acidData)) { pMetadata->type = drwav_metadata_type_acid; @@ -92670,7 +93382,7 @@ For more information, please refer to =============================================================================== ALTERNATIVE 2 - MIT No Attribution =============================================================================== -Copyright 2020 David Reid +Copyright 2023 David Reid 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 From 76e39e502c826574f2ad85977258b2269bb3087d Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 17 May 2023 23:14:14 +0200 Subject: [PATCH 0442/1710] Update rtextures.c --- src/rtextures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index 5c8029e5a..6bca43d15 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2046,7 +2046,7 @@ void ImageFlipHorizontal(Image *image) { for (int x = 0; x < image->width; x++) { - // OPTION 1: Move pixels with memcopy() + // OPTION 1: Move pixels with memcpy() //memcpy(flippedData + (y*image->width + x)*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - 1 - x))*bytesPerPixel, bytesPerPixel); // OPTION 2: Just copy data pixel by pixel From fe6973a4f6aeaf0979783c7d865da3a70c0f55cc Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 18 May 2023 11:59:35 +0200 Subject: [PATCH 0443/1710] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b4e187cf7..dae2f2fab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + **raylib is a simple and easy-to-use library to enjoy videogames programming.** From a3e78c5453e5df92cda57a178101a143753cdc51 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 18 May 2023 12:00:51 +0200 Subject: [PATCH 0444/1710] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dae2f2fab..90de36ca5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + **raylib is a simple and easy-to-use library to enjoy videogames programming.** From 51387dfbfba7f9e8d1698183ef279c5785fd0d71 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 18 May 2023 16:14:18 +0200 Subject: [PATCH 0445/1710] tweak --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 1f9ce1b65..dce838c29 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1340,7 +1340,7 @@ int TextToInteger(const char *text) 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; } From f31df7521a9e59f7a1af3f923630900ad74eb1bc Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 21 May 2023 00:14:09 +0200 Subject: [PATCH 0446/1710] REVIEWED: `GenImagePerlinNoise()`, no change --- src/rtextures.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 6bca43d15..2d8056d64 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -833,18 +833,23 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float { for (int x = 0; x < width; x++) { - float nx = (float)(x + offsetX)*scale/(float)width; - float ny = (float)(y + offsetY)*scale/(float)height; - + float nx = (float)(x + offsetX)*(scale/(float)width); + float ny = (float)(y + offsetY)*(scale/(float)height); + + // Basic perlin noise implementation (not used) + //float p = (stb_perlin_noise3(nx, ny, 0.0f, 0, 0, 0); + + // Calculate a better perlin noise using fbm (fractal brownian motion) // Typical values to start playing with: // lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) // gain = 0.5 -- relative weighting applied to each successive octave // octaves = 6 -- number of "octaves" of noise3() to sum + float p = stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6); + + // We need to normalize the data from [-1..1] to [0..1] + float np = (p + 1.0f)/2.0f; - // NOTE: We need to translate the data from [-1..1] to [0..1] - float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6) + 1.0f)/2.0f; - - int intensity = (int)(p*255.0f); + int intensity = (int)(np*255.0f); pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 }; } } From 3a841ac130f90c607c29aa79288b943bbfe569b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 21 May 2023 10:28:04 +0200 Subject: [PATCH 0447/1710] REVIEWED: `GenImagePerlinNoise()`, clamp values #3071 --- src/rtextures.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index 2d8056d64..875942586 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -846,6 +846,10 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float // octaves = 6 -- number of "octaves" of noise3() to sum float p = stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6); + // Clamp between -1.0f and 1.0f + if (p < -1.0f) p = -1.0f; + if (p > 1.0f) p = 1.0f; + // We need to normalize the data from [-1..1] to [0..1] float np = (p + 1.0f)/2.0f; From 1b4634702ca9f1e76477833618ccfea9093f03e5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 21 May 2023 11:20:42 +0200 Subject: [PATCH 0448/1710] Minor tweak --- src/rcore.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 127abcf38..7a31b5ed6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5101,7 +5101,6 @@ void PollInputEvents(void) // Get current gamepad state // NOTE: There is no callback available, so we get it manually - // Get remapped buttons GLFWgamepadstate state = { 0 }; glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller @@ -5109,7 +5108,7 @@ void PollInputEvents(void) for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) { - GamepadButton button = -1; + int button = -1; // GamepadButton enum values assigned switch (k) { From e96dc46d38fcc73cc278ef7a1ad95ce63c6ce989 Mon Sep 17 00:00:00 2001 From: Dane Madsen Date: Sun, 21 May 2023 19:33:47 +1000 Subject: [PATCH 0449/1710] Replaced GenImageGradientH and GenImageGradientV with GenImageLinearGradient (#3074) * Replaced GenImageGradientH and GenImageGradientV with GenImageLinearGradient * renamed GenImageLinearGradient to GenImageGradientLinear --- examples/textures/textures_image_generation.c | 25 +- parser/output/raylib_api.json | 38 +- parser/output/raylib_api.lua | 29 +- parser/output/raylib_api.txt | 568 +++++++++--------- parser/output/raylib_api.xml | 22 +- projects/Geany/raylib.c.tags | 3 +- .../raylib_npp_parser/raylib_npp.xml | 17 +- .../raylib_npp_parser/raylib_to_parse.h | 3 +- src/raylib.h | 3 +- src/rtextures.c | 57 +- 10 files changed, 358 insertions(+), 407 deletions(-) diff --git a/examples/textures/textures_image_generation.c b/examples/textures/textures_image_generation.c index 1ab08ae84..8049a578e 100644 --- a/examples/textures/textures_image_generation.c +++ b/examples/textures/textures_image_generation.c @@ -13,7 +13,7 @@ #include "raylib.h" -#define NUM_TEXTURES 6 // Currently we have 7 generation algorithms +#define NUM_TEXTURES 7 // Currently we have 7 generation algorithms //------------------------------------------------------------------------------------ // Program main entry point @@ -27,8 +27,9 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [textures] example - procedural images generation"); - Image verticalGradient = GenImageGradientV(screenWidth, screenHeight, RED, BLUE); - Image horizontalGradient = GenImageGradientH(screenWidth, screenHeight, RED, BLUE); + Image verticalGradient = GenImageGradientLinear(screenWidth, screenHeight, 0, RED, BLUE); + Image horizontalGradient = GenImageGradientLinear(screenWidth, screenHeight, 90, RED, BLUE); + Image diagonalGradient = GenImageGradientLinear(screenWidth, screenHeight, 45, RED, BLUE); Image radialGradient = GenImageGradientRadial(screenWidth, screenHeight, 0.0f, WHITE, BLACK); Image checked = GenImageChecked(screenWidth, screenHeight, 32, 32, RED, BLUE); Image whiteNoise = GenImageWhiteNoise(screenWidth, screenHeight, 0.5f); @@ -38,10 +39,11 @@ int main(void) textures[0] = LoadTextureFromImage(verticalGradient); textures[1] = LoadTextureFromImage(horizontalGradient); - textures[2] = LoadTextureFromImage(radialGradient); - textures[3] = LoadTextureFromImage(checked); - textures[4] = LoadTextureFromImage(whiteNoise); - textures[5] = LoadTextureFromImage(cellular); + textures[2] = LoadTextureFromImage(diagonalGradient); + textures[3] = LoadTextureFromImage(radialGradient); + textures[4] = LoadTextureFromImage(checked); + textures[5] = LoadTextureFromImage(whiteNoise); + textures[6] = LoadTextureFromImage(cellular); // Unload image data (CPU RAM) UnloadImage(verticalGradient); @@ -83,10 +85,11 @@ int main(void) { case 0: DrawText("VERTICAL GRADIENT", 560, 10, 20, RAYWHITE); break; case 1: DrawText("HORIZONTAL GRADIENT", 540, 10, 20, RAYWHITE); break; - case 2: DrawText("RADIAL GRADIENT", 580, 10, 20, LIGHTGRAY); break; - case 3: DrawText("CHECKED", 680, 10, 20, RAYWHITE); break; - case 4: DrawText("WHITE NOISE", 640, 10, 20, RED); break; - case 5: DrawText("CELLULAR", 670, 10, 20, RAYWHITE); break; + case 2: DrawText("DIAGONAL GRADIENT", 540, 10, 20, RAYWHITE); break; + case 3: DrawText("RADIAL GRADIENT", 580, 10, 20, LIGHTGRAY); break; + case 4: DrawText("CHECKED", 680, 10, 20, RAYWHITE); break; + case 5: DrawText("WHITE NOISE", 640, 10, 20, RED); break; + case 6: DrawText("CELLULAR", 670, 10, 20, RAYWHITE); break; default: break; } diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 62d9f4401..5af2b9aef 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -15,7 +15,7 @@ { "name": "RAYLIB_VERSION_MINOR", "type": "INT", - "value": 5, + "value": 6, "description": "" }, { @@ -27,7 +27,7 @@ { "name": "RAYLIB_VERSION", "type": "STRING", - "value": "4.5", + "value": "4.6-dev", "description": "" }, { @@ -1032,6 +1032,11 @@ "type": "Transform **", "name": "framePoses", "description": "Poses array by frame" + }, + { + "type": "char[32]", + "name": "name", + "description": "Animation name" } ] }, @@ -6236,8 +6241,8 @@ ] }, { - "name": "GenImageGradientV", - "description": "Generate image: vertical gradient", + "name": "GenImageGradientLinear", + "description": "Generate image: linear gradient", "returnType": "Image", "params": [ { @@ -6248,36 +6253,17 @@ "type": "int", "name": "height" }, - { - "type": "Color", - "name": "top" - }, - { - "type": "Color", - "name": "bottom" - } - ] - }, - { - "name": "GenImageGradientH", - "description": "Generate image: horizontal gradient", - "returnType": "Image", - "params": [ { "type": "int", - "name": "width" - }, - { - "type": "int", - "name": "height" + "name": "direction" }, { "type": "Color", - "name": "left" + "name": "start" }, { "type": "Color", - "name": "right" + "name": "end" } ] }, diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 0a1b95856..e7101f51b 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -15,7 +15,7 @@ return { { name = "RAYLIB_VERSION_MINOR", type = "INT", - value = 5, + value = 6, description = "" }, { @@ -27,7 +27,7 @@ return { { name = "RAYLIB_VERSION", type = "STRING", - value = "4.5", + value = "4.6-dev", description = "" }, { @@ -1032,6 +1032,11 @@ return { type = "Transform **", name = "framePoses", description = "Poses array by frame" + }, + { + type = "char[32]", + name = "name", + description = "Animation name" } } }, @@ -4991,25 +4996,15 @@ return { } }, { - name = "GenImageGradientV", - description = "Generate image: vertical gradient", + name = "GenImageGradientLinear", + description = "Generate image: linear gradient", returnType = "Image", params = { {type = "int", name = "width"}, {type = "int", name = "height"}, - {type = "Color", name = "top"}, - {type = "Color", name = "bottom"} - } - }, - { - name = "GenImageGradientH", - description = "Generate image: horizontal gradient", - returnType = "Image", - params = { - {type = "int", name = "width"}, - {type = "int", name = "height"}, - {type = "Color", name = "left"}, - {type = "Color", name = "right"} + {type = "int", name = "direction"}, + {type = "Color", name = "start"}, + {type = "Color", name = "end"} } }, { diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 4b13bc3b4..2df71232e 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -14,7 +14,7 @@ Define 002: RAYLIB_VERSION_MAJOR Define 003: RAYLIB_VERSION_MINOR Name: RAYLIB_VERSION_MINOR Type: INT - Value: 5 + Value: 6 Description: Define 004: RAYLIB_VERSION_PATCH Name: RAYLIB_VERSION_PATCH @@ -24,7 +24,7 @@ Define 004: RAYLIB_VERSION_PATCH Define 005: RAYLIB_VERSION Name: RAYLIB_VERSION Type: STRING - Value: "4.5" + Value: "4.6-dev" Description: Define 006: __declspec(x) Name: __declspec(x) @@ -456,13 +456,14 @@ Struct 21: Model (9 fields) Field[7]: int boneCount // Number of bones Field[8]: BoneInfo * bones // Bones information (skeleton) Field[9]: Transform * bindPose // Bones base transformation (pose) -Struct 22: ModelAnimation (4 fields) +Struct 22: ModelAnimation (5 fields) Name: ModelAnimation Description: ModelAnimation Field[1]: int boneCount // Number of bones Field[2]: int frameCount // Number of animation frames Field[3]: BoneInfo * bones // Bones information (skeleton) Field[4]: Transform ** framePoses // Poses array by frame + Field[5]: char[32] name // Animation name Struct 23: Ray (2 fields) Name: Ray Description: Ray, ray for raycasting @@ -963,7 +964,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 517 +Functions found: 516 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2415,23 +2416,16 @@ Function 245: GenImageColor() (3 input parameters) Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 246: GenImageGradientV() (4 input parameters) - Name: GenImageGradientV +Function 246: GenImageGradientLinear() (5 input parameters) + Name: GenImageGradientLinear Return type: Image - Description: Generate image: vertical gradient + Description: Generate image: linear gradient Param[1]: width (type: int) Param[2]: height (type: int) - Param[3]: top (type: Color) - Param[4]: bottom (type: Color) -Function 247: GenImageGradientH() (4 input parameters) - Name: GenImageGradientH - Return type: Image - Description: Generate image: horizontal gradient - Param[1]: width (type: int) - Param[2]: height (type: int) - Param[3]: left (type: Color) - Param[4]: right (type: Color) -Function 248: GenImageGradientRadial() (5 input parameters) + Param[3]: direction (type: int) + Param[4]: start (type: Color) + Param[5]: end (type: Color) +Function 247: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2440,7 +2434,7 @@ Function 248: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 249: GenImageChecked() (6 input parameters) +Function 248: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2450,14 +2444,14 @@ Function 249: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 250: GenImageWhiteNoise() (3 input parameters) +Function 249: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 251: GenImagePerlinNoise() (5 input parameters) +Function 250: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2466,39 +2460,39 @@ Function 251: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 252: GenImageCellular() (3 input parameters) +Function 251: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 253: GenImageText() (3 input parameters) +Function 252: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 254: ImageCopy() (1 input parameters) +Function 253: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 255: ImageFromImage() (2 input parameters) +Function 254: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 256: ImageText() (3 input parameters) +Function 255: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 257: ImageTextEx() (5 input parameters) +Function 256: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2507,69 +2501,69 @@ Function 257: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 258: ImageFormat() (2 input parameters) +Function 257: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 259: ImageToPOT() (2 input parameters) +Function 258: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 260: ImageCrop() (2 input parameters) +Function 259: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 261: ImageAlphaCrop() (2 input parameters) +Function 260: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 262: ImageAlphaClear() (3 input parameters) +Function 261: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 263: ImageAlphaMask() (2 input parameters) +Function 262: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 264: ImageAlphaPremultiply() (1 input parameters) +Function 263: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 265: ImageBlurGaussian() (2 input parameters) +Function 264: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 266: ImageResize() (3 input parameters) +Function 265: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 267: ImageResizeNN() (3 input parameters) +Function 266: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 268: ImageResizeCanvas() (6 input parameters) +Function 267: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2579,12 +2573,12 @@ Function 268: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 269: ImageMipmaps() (1 input parameters) +Function 268: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 270: ImageDither() (5 input parameters) +Function 269: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2593,103 +2587,103 @@ Function 270: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 271: ImageFlipVertical() (1 input parameters) +Function 270: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 272: ImageFlipHorizontal() (1 input parameters) +Function 271: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 273: ImageRotateCW() (1 input parameters) +Function 272: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 274: ImageRotateCCW() (1 input parameters) +Function 273: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 275: ImageColorTint() (2 input parameters) +Function 274: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 276: ImageColorInvert() (1 input parameters) +Function 275: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 277: ImageColorGrayscale() (1 input parameters) +Function 276: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 278: ImageColorContrast() (2 input parameters) +Function 277: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 279: ImageColorBrightness() (2 input parameters) +Function 278: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 280: ImageColorReplace() (3 input parameters) +Function 279: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 281: LoadImageColors() (1 input parameters) +Function 280: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 282: LoadImagePalette() (3 input parameters) +Function 281: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 283: UnloadImageColors() (1 input parameters) +Function 282: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 284: UnloadImagePalette() (1 input parameters) +Function 283: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 285: GetImageAlphaBorder() (2 input parameters) +Function 284: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 286: GetImageColor() (3 input parameters) +Function 285: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 287: ImageClearBackground() (2 input parameters) +Function 286: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 288: ImageDrawPixel() (4 input parameters) +Function 287: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2697,14 +2691,14 @@ Function 288: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 289: ImageDrawPixelV() (3 input parameters) +Function 288: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 290: ImageDrawLine() (6 input parameters) +Function 289: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2714,7 +2708,7 @@ Function 290: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 291: ImageDrawLineV() (4 input parameters) +Function 290: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2722,7 +2716,7 @@ Function 291: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 292: ImageDrawCircle() (5 input parameters) +Function 291: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2731,7 +2725,7 @@ Function 292: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 293: ImageDrawCircleV() (4 input parameters) +Function 292: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2739,7 +2733,7 @@ Function 293: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 294: ImageDrawCircleLines() (5 input parameters) +Function 293: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2748,7 +2742,7 @@ Function 294: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 295: ImageDrawCircleLinesV() (4 input parameters) +Function 294: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2756,7 +2750,7 @@ Function 295: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 296: ImageDrawRectangle() (6 input parameters) +Function 295: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2766,7 +2760,7 @@ Function 296: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 297: ImageDrawRectangleV() (4 input parameters) +Function 296: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2774,14 +2768,14 @@ Function 297: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 298: ImageDrawRectangleRec() (3 input parameters) +Function 297: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 299: ImageDrawRectangleLines() (4 input parameters) +Function 298: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2789,7 +2783,7 @@ Function 299: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 300: ImageDraw() (5 input parameters) +Function 299: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2798,7 +2792,7 @@ Function 300: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 301: ImageDrawText() (6 input parameters) +Function 300: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2808,7 +2802,7 @@ Function 301: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 302: ImageDrawTextEx() (7 input parameters) +Function 301: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2819,79 +2813,79 @@ Function 302: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 303: LoadTexture() (1 input parameters) +Function 302: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 304: LoadTextureFromImage() (1 input parameters) +Function 303: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 305: LoadTextureCubemap() (2 input parameters) +Function 304: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 306: LoadRenderTexture() (2 input parameters) +Function 305: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 307: IsTextureReady() (1 input parameters) +Function 306: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 308: UnloadTexture() (1 input parameters) +Function 307: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 309: IsRenderTextureReady() (1 input parameters) +Function 308: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 310: UnloadRenderTexture() (1 input parameters) +Function 309: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 311: UpdateTexture() (2 input parameters) +Function 310: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 312: UpdateTextureRec() (3 input parameters) +Function 311: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 313: GenTextureMipmaps() (1 input parameters) +Function 312: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 314: SetTextureFilter() (2 input parameters) +Function 313: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 315: SetTextureWrap() (2 input parameters) +Function 314: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 316: DrawTexture() (4 input parameters) +Function 315: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2899,14 +2893,14 @@ Function 316: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 317: DrawTextureV() (3 input parameters) +Function 316: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 318: DrawTextureEx() (5 input parameters) +Function 317: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2915,7 +2909,7 @@ Function 318: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 319: DrawTextureRec() (4 input parameters) +Function 318: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2923,7 +2917,7 @@ Function 319: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 320: DrawTexturePro() (6 input parameters) +Function 319: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2933,7 +2927,7 @@ Function 320: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 321: DrawTextureNPatch() (6 input parameters) +Function 320: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2943,106 +2937,106 @@ Function 321: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 322: Fade() (2 input parameters) +Function 321: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 323: ColorToInt() (1 input parameters) +Function 322: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 324: ColorNormalize() (1 input parameters) +Function 323: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 325: ColorFromNormalized() (1 input parameters) +Function 324: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 326: ColorToHSV() (1 input parameters) +Function 325: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 327: ColorFromHSV() (3 input parameters) +Function 326: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 328: ColorTint() (2 input parameters) +Function 327: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 329: ColorBrightness() (2 input parameters) +Function 328: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 330: ColorContrast() (2 input parameters) +Function 329: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 331: ColorAlpha() (2 input parameters) +Function 330: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 332: ColorAlphaBlend() (3 input parameters) +Function 331: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 333: GetColor() (1 input parameters) +Function 332: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 334: GetPixelColor() (2 input parameters) +Function 333: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 335: SetPixelColor() (3 input parameters) +Function 334: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 336: GetPixelDataSize() (3 input parameters) +Function 335: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 337: GetFontDefault() (0 input parameters) +Function 336: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 338: LoadFont() (1 input parameters) +Function 337: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 339: LoadFontEx() (4 input parameters) +Function 338: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set @@ -3050,14 +3044,14 @@ Function 339: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: fontChars (type: int *) Param[4]: glyphCount (type: int) -Function 340: LoadFontFromImage() (3 input parameters) +Function 339: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 341: LoadFontFromMemory() (6 input parameters) +Function 340: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3067,12 +3061,12 @@ Function 341: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: fontChars (type: int *) Param[6]: glyphCount (type: int) -Function 342: IsFontReady() (1 input parameters) +Function 341: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 343: LoadFontData() (6 input parameters) +Function 342: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3082,7 +3076,7 @@ Function 343: LoadFontData() (6 input parameters) Param[4]: fontChars (type: int *) Param[5]: glyphCount (type: int) Param[6]: type (type: int) -Function 344: GenImageFontAtlas() (6 input parameters) +Function 343: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3092,30 +3086,30 @@ Function 344: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 345: UnloadFontData() (2 input parameters) +Function 344: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: chars (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 346: UnloadFont() (1 input parameters) +Function 345: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 347: ExportFontAsCode() (2 input parameters) +Function 346: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 348: DrawFPS() (2 input parameters) +Function 347: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 349: DrawText() (5 input parameters) +Function 348: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3124,7 +3118,7 @@ Function 349: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 350: DrawTextEx() (6 input parameters) +Function 349: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3134,7 +3128,7 @@ Function 350: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 351: DrawTextPro() (8 input parameters) +Function 350: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3146,7 +3140,7 @@ Function 351: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 352: DrawTextCodepoint() (5 input parameters) +Function 351: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3155,7 +3149,7 @@ Function 352: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 353: DrawTextCodepoints() (7 input parameters) +Function 352: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3166,13 +3160,13 @@ Function 353: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 354: MeasureText() (2 input parameters) +Function 353: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 355: MeasureTextEx() (4 input parameters) +Function 354: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3180,180 +3174,180 @@ Function 355: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 356: GetGlyphIndex() (2 input parameters) +Function 355: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 357: GetGlyphInfo() (2 input parameters) +Function 356: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 358: GetGlyphAtlasRec() (2 input parameters) +Function 357: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 359: LoadUTF8() (2 input parameters) +Function 358: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 360: UnloadUTF8() (1 input parameters) +Function 359: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 361: LoadCodepoints() (2 input parameters) +Function 360: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 362: UnloadCodepoints() (1 input parameters) +Function 361: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 363: GetCodepointCount() (1 input parameters) +Function 362: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 364: GetCodepoint() (2 input parameters) +Function 363: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 365: GetCodepointNext() (2 input parameters) +Function 364: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 366: GetCodepointPrevious() (2 input parameters) +Function 365: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 367: CodepointToUTF8() (2 input parameters) +Function 366: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 368: TextCopy() (2 input parameters) +Function 367: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 369: TextIsEqual() (2 input parameters) +Function 368: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 370: TextLength() (1 input parameters) +Function 369: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 371: TextFormat() (2 input parameters) +Function 370: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 372: TextSubtext() (3 input parameters) +Function 371: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 373: TextReplace() (3 input parameters) +Function 372: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 374: TextInsert() (3 input parameters) +Function 373: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 375: TextJoin() (3 input parameters) +Function 374: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 376: TextSplit() (3 input parameters) +Function 375: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 377: TextAppend() (3 input parameters) +Function 376: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 378: TextFindIndex() (2 input parameters) +Function 377: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 379: TextToUpper() (1 input parameters) +Function 378: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 380: TextToLower() (1 input parameters) +Function 379: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 381: TextToPascal() (1 input parameters) +Function 380: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 382: TextToInteger() (1 input parameters) +Function 381: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 383: DrawLine3D() (3 input parameters) +Function 382: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 384: DrawPoint3D() (2 input parameters) +Function 383: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 385: DrawCircle3D() (5 input parameters) +Function 384: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3362,7 +3356,7 @@ Function 385: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 386: DrawTriangle3D() (4 input parameters) +Function 385: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3370,14 +3364,14 @@ Function 386: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 387: DrawTriangleStrip3D() (3 input parameters) +Function 386: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 388: DrawCube() (5 input parameters) +Function 387: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3386,14 +3380,14 @@ Function 388: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 389: DrawCubeV() (3 input parameters) +Function 388: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 390: DrawCubeWires() (5 input parameters) +Function 389: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3402,21 +3396,21 @@ Function 390: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 391: DrawCubeWiresV() (3 input parameters) +Function 390: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 392: DrawSphere() (3 input parameters) +Function 391: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 393: DrawSphereEx() (5 input parameters) +Function 392: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3425,7 +3419,7 @@ Function 393: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 394: DrawSphereWires() (5 input parameters) +Function 393: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3434,7 +3428,7 @@ Function 394: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 395: DrawCylinder() (6 input parameters) +Function 394: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3444,7 +3438,7 @@ Function 395: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 396: DrawCylinderEx() (6 input parameters) +Function 395: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3454,7 +3448,7 @@ Function 396: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 397: DrawCylinderWires() (6 input parameters) +Function 396: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3464,7 +3458,7 @@ Function 397: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 398: DrawCylinderWiresEx() (6 input parameters) +Function 397: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3474,7 +3468,7 @@ Function 398: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 399: DrawCapsule() (6 input parameters) +Function 398: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3484,7 +3478,7 @@ Function 399: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 400: DrawCapsuleWires() (6 input parameters) +Function 399: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3494,51 +3488,51 @@ Function 400: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 401: DrawPlane() (3 input parameters) +Function 400: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 402: DrawRay() (2 input parameters) +Function 401: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 403: DrawGrid() (2 input parameters) +Function 402: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 404: LoadModel() (1 input parameters) +Function 403: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 405: LoadModelFromMesh() (1 input parameters) +Function 404: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 406: IsModelReady() (1 input parameters) +Function 405: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 407: UnloadModel() (1 input parameters) +Function 406: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 408: GetModelBoundingBox() (1 input parameters) +Function 407: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 409: DrawModel() (4 input parameters) +Function 408: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3546,7 +3540,7 @@ Function 409: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 410: DrawModelEx() (6 input parameters) +Function 409: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3556,7 +3550,7 @@ Function 410: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 411: DrawModelWires() (4 input parameters) +Function 410: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3564,7 +3558,7 @@ Function 411: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 412: DrawModelWiresEx() (6 input parameters) +Function 411: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3574,13 +3568,13 @@ Function 412: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 413: DrawBoundingBox() (2 input parameters) +Function 412: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 414: DrawBillboard() (5 input parameters) +Function 413: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3589,7 +3583,7 @@ Function 414: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 415: DrawBillboardRec() (6 input parameters) +Function 414: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3599,7 +3593,7 @@ Function 415: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 416: DrawBillboardPro() (9 input parameters) +Function 415: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3612,13 +3606,13 @@ Function 416: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 417: UploadMesh() (2 input parameters) +Function 416: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 418: UpdateMeshBuffer() (5 input parameters) +Function 417: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3627,19 +3621,19 @@ Function 418: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 419: UnloadMesh() (1 input parameters) +Function 418: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 420: DrawMesh() (3 input parameters) +Function 419: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 421: DrawMeshInstanced() (4 input parameters) +Function 420: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3647,29 +3641,29 @@ Function 421: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 422: ExportMesh() (2 input parameters) +Function 421: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 423: GetMeshBoundingBox() (1 input parameters) +Function 422: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 424: GenMeshTangents() (1 input parameters) +Function 423: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 425: GenMeshPoly() (2 input parameters) +Function 424: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 426: GenMeshPlane() (4 input parameters) +Function 425: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3677,42 +3671,42 @@ Function 426: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 427: GenMeshCube() (3 input parameters) +Function 426: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 428: GenMeshSphere() (3 input parameters) +Function 427: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 429: GenMeshHemiSphere() (3 input parameters) +Function 428: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 430: GenMeshCylinder() (3 input parameters) +Function 429: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 431: GenMeshCone() (3 input parameters) +Function 430: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 432: GenMeshTorus() (4 input parameters) +Function 431: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3720,7 +3714,7 @@ Function 432: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 433: GenMeshKnot() (4 input parameters) +Function 432: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3728,84 +3722,84 @@ Function 433: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 434: GenMeshHeightmap() (2 input parameters) +Function 433: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 435: GenMeshCubicmap() (2 input parameters) +Function 434: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 436: LoadMaterials() (2 input parameters) +Function 435: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 437: LoadMaterialDefault() (0 input parameters) +Function 436: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 438: IsMaterialReady() (1 input parameters) +Function 437: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 439: UnloadMaterial() (1 input parameters) +Function 438: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 440: SetMaterialTexture() (3 input parameters) +Function 439: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 441: SetModelMeshMaterial() (3 input parameters) +Function 440: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 442: LoadModelAnimations() (2 input parameters) +Function 441: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: unsigned int *) -Function 443: UpdateModelAnimation() (3 input parameters) +Function 442: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 444: UnloadModelAnimation() (1 input parameters) +Function 443: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 445: UnloadModelAnimations() (2 input parameters) +Function 444: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: count (type: unsigned int) -Function 446: IsModelAnimationValid() (2 input parameters) +Function 445: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 447: CheckCollisionSpheres() (4 input parameters) +Function 446: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3813,40 +3807,40 @@ Function 447: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 448: CheckCollisionBoxes() (2 input parameters) +Function 447: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 449: CheckCollisionBoxSphere() (3 input parameters) +Function 448: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 450: GetRayCollisionSphere() (3 input parameters) +Function 449: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 451: GetRayCollisionBox() (2 input parameters) +Function 450: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 452: GetRayCollisionMesh() (3 input parameters) +Function 451: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 453: GetRayCollisionTriangle() (4 input parameters) +Function 452: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3854,7 +3848,7 @@ Function 453: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 454: GetRayCollisionQuad() (5 input parameters) +Function 453: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3863,143 +3857,143 @@ Function 454: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 455: InitAudioDevice() (0 input parameters) +Function 454: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 456: CloseAudioDevice() (0 input parameters) +Function 455: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 457: IsAudioDeviceReady() (0 input parameters) +Function 456: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 458: SetMasterVolume() (1 input parameters) +Function 457: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 459: LoadWave() (1 input parameters) +Function 458: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 460: LoadWaveFromMemory() (3 input parameters) +Function 459: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 461: IsWaveReady() (1 input parameters) +Function 460: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 462: LoadSound() (1 input parameters) +Function 461: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 463: LoadSoundFromWave() (1 input parameters) +Function 462: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 464: IsSoundReady() (1 input parameters) +Function 463: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 465: UpdateSound() (3 input parameters) +Function 464: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 466: UnloadWave() (1 input parameters) +Function 465: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 467: UnloadSound() (1 input parameters) +Function 466: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 468: ExportWave() (2 input parameters) +Function 467: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 469: ExportWaveAsCode() (2 input parameters) +Function 468: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 470: PlaySound() (1 input parameters) +Function 469: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 471: StopSound() (1 input parameters) +Function 470: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 472: PauseSound() (1 input parameters) +Function 471: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 473: ResumeSound() (1 input parameters) +Function 472: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 474: IsSoundPlaying() (1 input parameters) +Function 473: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 475: SetSoundVolume() (2 input parameters) +Function 474: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 476: SetSoundPitch() (2 input parameters) +Function 475: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 477: SetSoundPan() (2 input parameters) +Function 476: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 478: WaveCopy() (1 input parameters) +Function 477: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 479: WaveCrop() (3 input parameters) +Function 478: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 480: WaveFormat() (4 input parameters) +Function 479: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4007,203 +4001,203 @@ Function 480: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 481: LoadWaveSamples() (1 input parameters) +Function 480: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 482: UnloadWaveSamples() (1 input parameters) +Function 481: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 483: LoadMusicStream() (1 input parameters) +Function 482: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 484: LoadMusicStreamFromMemory() (3 input parameters) +Function 483: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 485: IsMusicReady() (1 input parameters) +Function 484: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 486: UnloadMusicStream() (1 input parameters) +Function 485: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 487: PlayMusicStream() (1 input parameters) +Function 486: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 488: IsMusicStreamPlaying() (1 input parameters) +Function 487: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 489: UpdateMusicStream() (1 input parameters) +Function 488: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 490: StopMusicStream() (1 input parameters) +Function 489: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 491: PauseMusicStream() (1 input parameters) +Function 490: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 492: ResumeMusicStream() (1 input parameters) +Function 491: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 493: SeekMusicStream() (2 input parameters) +Function 492: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 494: SetMusicVolume() (2 input parameters) +Function 493: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 495: SetMusicPitch() (2 input parameters) +Function 494: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 496: SetMusicPan() (2 input parameters) +Function 495: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 497: GetMusicTimeLength() (1 input parameters) +Function 496: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 498: GetMusicTimePlayed() (1 input parameters) +Function 497: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 499: LoadAudioStream() (3 input parameters) +Function 498: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 500: IsAudioStreamReady() (1 input parameters) +Function 499: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 501: UnloadAudioStream() (1 input parameters) +Function 500: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 502: UpdateAudioStream() (3 input parameters) +Function 501: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 503: IsAudioStreamProcessed() (1 input parameters) +Function 502: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 504: PlayAudioStream() (1 input parameters) +Function 503: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 505: PauseAudioStream() (1 input parameters) +Function 504: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 506: ResumeAudioStream() (1 input parameters) +Function 505: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 507: IsAudioStreamPlaying() (1 input parameters) +Function 506: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 508: StopAudioStream() (1 input parameters) +Function 507: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 509: SetAudioStreamVolume() (2 input parameters) +Function 508: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 510: SetAudioStreamPitch() (2 input parameters) +Function 509: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 511: SetAudioStreamPan() (2 input parameters) +Function 510: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 512: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 511: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 513: SetAudioStreamCallback() (2 input parameters) +Function 512: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 514: AttachAudioStreamProcessor() (2 input parameters) +Function 513: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 515: DetachAudioStreamProcessor() (2 input parameters) +Function 514: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 516: AttachAudioMixedProcessor() (1 input parameters) +Function 515: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline Param[1]: processor (type: AudioCallback) -Function 517: DetachAudioMixedProcessor() (1 input parameters) +Function 516: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 45305951f..1c77f0266 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -3,9 +3,9 @@ - + - + @@ -210,11 +210,12 @@ - + + @@ -655,7 +656,7 @@ - + @@ -1548,17 +1549,12 @@ - + - - - - - - - - + + + diff --git a/projects/Geany/raylib.c.tags b/projects/Geany/raylib.c.tags index 78e6e7241..d41809983 100644 --- a/projects/Geany/raylib.c.tags +++ b/projects/Geany/raylib.c.tags @@ -211,8 +211,7 @@ ImageColorContrast|void|(Image *image, float contrast);| ImageColorBrightness|void|(Image *image, int brightness);| ImageColorReplace|void|(Image *image, Color color, Color replace);| GenImageColor|Image|(int width, int height, Color color);| -GenImageGradientV|Image|(int width, int height, Color top, Color bottom);| -GenImageGradientH|Image|(int width, int height, Color left, Color right);| +GenImageGradientLinear|Image|(int width, int height, int direction, Color start, Color end);| GenImageGradientRadial|Image|(int width, int height, float density, Color inner, Color outer);| GenImageChecked|Image|(int width, int height, int checksX, int checksY, Color col1, Color col2);| GenImageWhiteNoise|Image|(int width, int height, float factor);| diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index e764a1b63..890501f2c 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -1378,20 +1378,13 @@ - - + + - - - - - - - - - - + + + diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index 96732e319..3f8450130 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -317,8 +317,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Image generation functions RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient -RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient +RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise diff --git a/src/raylib.h b/src/raylib.h index 2a5a2f63f..edc81c341 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1239,8 +1239,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Image generation functions RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient -RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient +RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise diff --git a/src/rtextures.c b/src/rtextures.c index 875942586..d1cebe0d4 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -684,48 +684,35 @@ Image GenImageColor(int width, int height, Color color) } #if defined(SUPPORT_IMAGE_GENERATION) -// Generate image: vertical gradient -Image GenImageGradientV(int width, int height, Color top, Color bottom) +// Generate image: linear gradient +// The direction value specifies the direction of the gradient (in degrees) +// with 0 being vertical (from top to bottom), 90 being horizontal (from left to right). +// The gradient effectively rotates counter-clockwise by the specified amount. +Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end) { Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); - for (int j = 0; j < height; j++) + float radianDirection = (float)(90 - direction) / 180.f * 3.14159f; + float cosDir = cos(radianDirection); + float sinDir = sin(radianDirection); + + int i, j; + for (i = 0; i < width; i++) { - float factor = (float)j/(float)height; - for (int i = 0; i < width; i++) + for (j = 0; j < height; j++) { - pixels[j*width + i].r = (int)((float)bottom.r*factor + (float)top.r*(1.f - factor)); - pixels[j*width + i].g = (int)((float)bottom.g*factor + (float)top.g*(1.f - factor)); - pixels[j*width + i].b = (int)((float)bottom.b*factor + (float)top.b*(1.f - factor)); - pixels[j*width + i].a = (int)((float)bottom.a*factor + (float)top.a*(1.f - factor)); - } - } + // Calculate the relative position of the pixel along the gradient direction + float pos = (i * cosDir + j * sinDir) / (width * cosDir + height * sinDir); - Image image = { - .data = pixels, - .width = width, - .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 - }; + float factor = pos; + factor = (factor > 1.f) ? 1.f : factor; // Clamp to [0,1] + factor = (factor < 0.f) ? 0.f : factor; // Clamp to [0,1] - return image; -} - -// Generate image: horizontal gradient -Image GenImageGradientH(int width, int height, Color left, Color right) -{ - Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); - - for (int i = 0; i < width; i++) - { - float factor = (float)i/(float)width; - for (int j = 0; j < height; j++) - { - pixels[j*width + i].r = (int)((float)right.r*factor + (float)left.r*(1.f - factor)); - pixels[j*width + i].g = (int)((float)right.g*factor + (float)left.g*(1.f - factor)); - pixels[j*width + i].b = (int)((float)right.b*factor + (float)left.b*(1.f - factor)); - pixels[j*width + i].a = (int)((float)right.a*factor + (float)left.a*(1.f - factor)); + // Generate the color for this pixel + pixels[j * width + i].r = (int)((float)end.r*factor + (float)start.r*(1.f - factor)); + pixels[j * width + i].g = (int)((float)end.g*factor + (float)start.g*(1.f - factor)); + pixels[j * width + i].b = (int)((float)end.b*factor + (float)start.b*(1.f - factor)); + pixels[j * width + i].a = (int)((float)end.a*factor + (float)start.a*(1.f - factor)); } } From 84ae26cdc0ab22e7721552647cd6c30d8a478bae Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 21 May 2023 11:35:25 +0200 Subject: [PATCH 0450/1710] Update raylib.h --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index edc81c341..02d8e4445 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1239,7 +1239,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Image generation functions RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient +RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise From a4a6d4da8a26d131bc3f3e643f6fe51de281d15f Mon Sep 17 00:00:00 2001 From: Dane Madsen Date: Mon, 22 May 2023 23:20:28 +1000 Subject: [PATCH 0451/1710] Add GenImageGradientSquare (#3077) * Add GenImageGradientSquare to allow square gradients * Fix GenImageGradientSquare and add to textures_image_generation example * Remove params from GenImageGradientSquare --- examples/textures/textures_image_generation.c | 23 +- parser/output/raylib_api.json | 29 +- parser/output/raylib_api.lua | 14 +- parser/output/raylib_api.txt | 551 +++++++++--------- parser/output/raylib_api.xml | 11 +- projects/Geany/raylib.c.tags | 1 + .../raylib_npp_parser/raylib_npp.xml | 9 + .../raylib_npp_parser/raylib_to_parse.h | 1 + src/raylib.h | 1 + src/rtextures.c | 49 ++ 10 files changed, 407 insertions(+), 282 deletions(-) diff --git a/examples/textures/textures_image_generation.c b/examples/textures/textures_image_generation.c index 8049a578e..359dc236a 100644 --- a/examples/textures/textures_image_generation.c +++ b/examples/textures/textures_image_generation.c @@ -13,7 +13,7 @@ #include "raylib.h" -#define NUM_TEXTURES 7 // Currently we have 7 generation algorithms +#define NUM_TEXTURES 9 // Currently we have 8 generation algorithms but some are have multiple purposes (Linear and Square Gradients) //------------------------------------------------------------------------------------ // Program main entry point @@ -31,8 +31,10 @@ int main(void) Image horizontalGradient = GenImageGradientLinear(screenWidth, screenHeight, 90, RED, BLUE); Image diagonalGradient = GenImageGradientLinear(screenWidth, screenHeight, 45, RED, BLUE); Image radialGradient = GenImageGradientRadial(screenWidth, screenHeight, 0.0f, WHITE, BLACK); + Image squareGradient = GenImageGradientSquare(screenWidth, screenHeight, 0.0f, WHITE, BLACK); Image checked = GenImageChecked(screenWidth, screenHeight, 32, 32, RED, BLUE); Image whiteNoise = GenImageWhiteNoise(screenWidth, screenHeight, 0.5f); + Image perlinNoise = GenImagePerlinNoise(screenWidth, screenHeight, 50, 50, 4.0f); Image cellular = GenImageCellular(screenWidth, screenHeight, 32); Texture2D textures[NUM_TEXTURES] = { 0 }; @@ -41,16 +43,21 @@ int main(void) textures[1] = LoadTextureFromImage(horizontalGradient); textures[2] = LoadTextureFromImage(diagonalGradient); textures[3] = LoadTextureFromImage(radialGradient); - textures[4] = LoadTextureFromImage(checked); - textures[5] = LoadTextureFromImage(whiteNoise); - textures[6] = LoadTextureFromImage(cellular); + textures[4] = LoadTextureFromImage(squareGradient); + textures[5] = LoadTextureFromImage(checked); + textures[6] = LoadTextureFromImage(whiteNoise); + textures[7] = LoadTextureFromImage(perlinNoise); + textures[8] = LoadTextureFromImage(cellular); // Unload image data (CPU RAM) UnloadImage(verticalGradient); UnloadImage(horizontalGradient); + UnloadImage(diagonalGradient); UnloadImage(radialGradient); + UnloadImage(squareGradient); UnloadImage(checked); UnloadImage(whiteNoise); + UnloadImage(perlinNoise); UnloadImage(cellular); int currentTexture = 0; @@ -87,9 +94,11 @@ int main(void) case 1: DrawText("HORIZONTAL GRADIENT", 540, 10, 20, RAYWHITE); break; case 2: DrawText("DIAGONAL GRADIENT", 540, 10, 20, RAYWHITE); break; case 3: DrawText("RADIAL GRADIENT", 580, 10, 20, LIGHTGRAY); break; - case 4: DrawText("CHECKED", 680, 10, 20, RAYWHITE); break; - case 5: DrawText("WHITE NOISE", 640, 10, 20, RED); break; - case 6: DrawText("CELLULAR", 670, 10, 20, RAYWHITE); break; + case 4: DrawText("SQUARE GRADIENT", 580, 10, 20, LIGHTGRAY); break; + case 5: DrawText("CHECKED", 680, 10, 20, RAYWHITE); break; + case 6: DrawText("WHITE NOISE", 640, 10, 20, RED); break; + case 7: DrawText("PERLIN NOISE", 640, 10, 20, RED); break; + case 8: DrawText("CELLULAR", 670, 10, 20, RAYWHITE); break; default: break; } diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 5af2b9aef..4438ab0da 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -6242,7 +6242,7 @@ }, { "name": "GenImageGradientLinear", - "description": "Generate image: linear gradient", + "description": "Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient", "returnType": "Image", "params": [ { @@ -6294,6 +6294,33 @@ } ] }, + { + "name": "GenImageGradientSquare", + "description": "Generate image: square gradient", + "returnType": "Image", + "params": [ + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + }, + { + "type": "float", + "name": "density" + }, + { + "type": "Color", + "name": "inner" + }, + { + "type": "Color", + "name": "outer" + } + ] + }, { "name": "GenImageChecked", "description": "Generate image: checked", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index e7101f51b..79b3a9283 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -4997,7 +4997,7 @@ return { }, { name = "GenImageGradientLinear", - description = "Generate image: linear gradient", + description = "Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient", returnType = "Image", params = { {type = "int", name = "width"}, @@ -5019,6 +5019,18 @@ return { {type = "Color", name = "outer"} } }, + { + name = "GenImageGradientSquare", + description = "Generate image: square gradient", + returnType = "Image", + params = { + {type = "int", name = "width"}, + {type = "int", name = "height"}, + {type = "float", name = "density"}, + {type = "Color", name = "inner"}, + {type = "Color", name = "outer"} + } + }, { name = "GenImageChecked", description = "Generate image: checked", diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 2df71232e..c53cb77ec 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -964,7 +964,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 516 +Functions found: 517 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2419,7 +2419,7 @@ Function 245: GenImageColor() (3 input parameters) Function 246: GenImageGradientLinear() (5 input parameters) Name: GenImageGradientLinear Return type: Image - Description: Generate image: linear gradient + Description: Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: direction (type: int) @@ -2434,7 +2434,16 @@ Function 247: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 248: GenImageChecked() (6 input parameters) +Function 248: GenImageGradientSquare() (5 input parameters) + Name: GenImageGradientSquare + Return type: Image + Description: Generate image: square gradient + Param[1]: width (type: int) + Param[2]: height (type: int) + Param[3]: density (type: float) + Param[4]: inner (type: Color) + Param[5]: outer (type: Color) +Function 249: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2444,14 +2453,14 @@ Function 248: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 249: GenImageWhiteNoise() (3 input parameters) +Function 250: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 250: GenImagePerlinNoise() (5 input parameters) +Function 251: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2460,39 +2469,39 @@ Function 250: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 251: GenImageCellular() (3 input parameters) +Function 252: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 252: GenImageText() (3 input parameters) +Function 253: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 253: ImageCopy() (1 input parameters) +Function 254: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 254: ImageFromImage() (2 input parameters) +Function 255: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 255: ImageText() (3 input parameters) +Function 256: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 256: ImageTextEx() (5 input parameters) +Function 257: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2501,69 +2510,69 @@ Function 256: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 257: ImageFormat() (2 input parameters) +Function 258: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 258: ImageToPOT() (2 input parameters) +Function 259: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 259: ImageCrop() (2 input parameters) +Function 260: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 260: ImageAlphaCrop() (2 input parameters) +Function 261: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 261: ImageAlphaClear() (3 input parameters) +Function 262: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 262: ImageAlphaMask() (2 input parameters) +Function 263: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 263: ImageAlphaPremultiply() (1 input parameters) +Function 264: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 264: ImageBlurGaussian() (2 input parameters) +Function 265: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 265: ImageResize() (3 input parameters) +Function 266: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 266: ImageResizeNN() (3 input parameters) +Function 267: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 267: ImageResizeCanvas() (6 input parameters) +Function 268: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2573,12 +2582,12 @@ Function 267: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 268: ImageMipmaps() (1 input parameters) +Function 269: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 269: ImageDither() (5 input parameters) +Function 270: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2587,103 +2596,103 @@ Function 269: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 270: ImageFlipVertical() (1 input parameters) +Function 271: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 271: ImageFlipHorizontal() (1 input parameters) +Function 272: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 272: ImageRotateCW() (1 input parameters) +Function 273: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 273: ImageRotateCCW() (1 input parameters) +Function 274: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 274: ImageColorTint() (2 input parameters) +Function 275: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 275: ImageColorInvert() (1 input parameters) +Function 276: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 276: ImageColorGrayscale() (1 input parameters) +Function 277: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 277: ImageColorContrast() (2 input parameters) +Function 278: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 278: ImageColorBrightness() (2 input parameters) +Function 279: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 279: ImageColorReplace() (3 input parameters) +Function 280: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 280: LoadImageColors() (1 input parameters) +Function 281: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 281: LoadImagePalette() (3 input parameters) +Function 282: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 282: UnloadImageColors() (1 input parameters) +Function 283: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 283: UnloadImagePalette() (1 input parameters) +Function 284: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 284: GetImageAlphaBorder() (2 input parameters) +Function 285: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 285: GetImageColor() (3 input parameters) +Function 286: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 286: ImageClearBackground() (2 input parameters) +Function 287: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 287: ImageDrawPixel() (4 input parameters) +Function 288: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2691,14 +2700,14 @@ Function 287: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 288: ImageDrawPixelV() (3 input parameters) +Function 289: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 289: ImageDrawLine() (6 input parameters) +Function 290: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2708,7 +2717,7 @@ Function 289: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 290: ImageDrawLineV() (4 input parameters) +Function 291: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2716,7 +2725,7 @@ Function 290: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 291: ImageDrawCircle() (5 input parameters) +Function 292: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2725,7 +2734,7 @@ Function 291: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 292: ImageDrawCircleV() (4 input parameters) +Function 293: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2733,7 +2742,7 @@ Function 292: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 293: ImageDrawCircleLines() (5 input parameters) +Function 294: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2742,7 +2751,7 @@ Function 293: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 294: ImageDrawCircleLinesV() (4 input parameters) +Function 295: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2750,7 +2759,7 @@ Function 294: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 295: ImageDrawRectangle() (6 input parameters) +Function 296: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2760,7 +2769,7 @@ Function 295: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 296: ImageDrawRectangleV() (4 input parameters) +Function 297: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2768,14 +2777,14 @@ Function 296: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 297: ImageDrawRectangleRec() (3 input parameters) +Function 298: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 298: ImageDrawRectangleLines() (4 input parameters) +Function 299: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2783,7 +2792,7 @@ Function 298: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 299: ImageDraw() (5 input parameters) +Function 300: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2792,7 +2801,7 @@ Function 299: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 300: ImageDrawText() (6 input parameters) +Function 301: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2802,7 +2811,7 @@ Function 300: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 301: ImageDrawTextEx() (7 input parameters) +Function 302: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2813,79 +2822,79 @@ Function 301: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 302: LoadTexture() (1 input parameters) +Function 303: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 303: LoadTextureFromImage() (1 input parameters) +Function 304: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 304: LoadTextureCubemap() (2 input parameters) +Function 305: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 305: LoadRenderTexture() (2 input parameters) +Function 306: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 306: IsTextureReady() (1 input parameters) +Function 307: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 307: UnloadTexture() (1 input parameters) +Function 308: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 308: IsRenderTextureReady() (1 input parameters) +Function 309: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 309: UnloadRenderTexture() (1 input parameters) +Function 310: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 310: UpdateTexture() (2 input parameters) +Function 311: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 311: UpdateTextureRec() (3 input parameters) +Function 312: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 312: GenTextureMipmaps() (1 input parameters) +Function 313: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 313: SetTextureFilter() (2 input parameters) +Function 314: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 314: SetTextureWrap() (2 input parameters) +Function 315: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 315: DrawTexture() (4 input parameters) +Function 316: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2893,14 +2902,14 @@ Function 315: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 316: DrawTextureV() (3 input parameters) +Function 317: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 317: DrawTextureEx() (5 input parameters) +Function 318: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2909,7 +2918,7 @@ Function 317: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 318: DrawTextureRec() (4 input parameters) +Function 319: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2917,7 +2926,7 @@ Function 318: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 319: DrawTexturePro() (6 input parameters) +Function 320: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2927,7 +2936,7 @@ Function 319: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 320: DrawTextureNPatch() (6 input parameters) +Function 321: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2937,106 +2946,106 @@ Function 320: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 321: Fade() (2 input parameters) +Function 322: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 322: ColorToInt() (1 input parameters) +Function 323: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 323: ColorNormalize() (1 input parameters) +Function 324: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 324: ColorFromNormalized() (1 input parameters) +Function 325: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 325: ColorToHSV() (1 input parameters) +Function 326: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 326: ColorFromHSV() (3 input parameters) +Function 327: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 327: ColorTint() (2 input parameters) +Function 328: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 328: ColorBrightness() (2 input parameters) +Function 329: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 329: ColorContrast() (2 input parameters) +Function 330: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 330: ColorAlpha() (2 input parameters) +Function 331: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 331: ColorAlphaBlend() (3 input parameters) +Function 332: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 332: GetColor() (1 input parameters) +Function 333: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 333: GetPixelColor() (2 input parameters) +Function 334: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 334: SetPixelColor() (3 input parameters) +Function 335: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 335: GetPixelDataSize() (3 input parameters) +Function 336: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 336: GetFontDefault() (0 input parameters) +Function 337: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 337: LoadFont() (1 input parameters) +Function 338: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 338: LoadFontEx() (4 input parameters) +Function 339: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set @@ -3044,14 +3053,14 @@ Function 338: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: fontChars (type: int *) Param[4]: glyphCount (type: int) -Function 339: LoadFontFromImage() (3 input parameters) +Function 340: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 340: LoadFontFromMemory() (6 input parameters) +Function 341: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3061,12 +3070,12 @@ Function 340: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: fontChars (type: int *) Param[6]: glyphCount (type: int) -Function 341: IsFontReady() (1 input parameters) +Function 342: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 342: LoadFontData() (6 input parameters) +Function 343: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3076,7 +3085,7 @@ Function 342: LoadFontData() (6 input parameters) Param[4]: fontChars (type: int *) Param[5]: glyphCount (type: int) Param[6]: type (type: int) -Function 343: GenImageFontAtlas() (6 input parameters) +Function 344: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3086,30 +3095,30 @@ Function 343: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 344: UnloadFontData() (2 input parameters) +Function 345: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: chars (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 345: UnloadFont() (1 input parameters) +Function 346: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 346: ExportFontAsCode() (2 input parameters) +Function 347: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 347: DrawFPS() (2 input parameters) +Function 348: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 348: DrawText() (5 input parameters) +Function 349: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3118,7 +3127,7 @@ Function 348: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 349: DrawTextEx() (6 input parameters) +Function 350: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3128,7 +3137,7 @@ Function 349: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 350: DrawTextPro() (8 input parameters) +Function 351: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3140,7 +3149,7 @@ Function 350: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 351: DrawTextCodepoint() (5 input parameters) +Function 352: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3149,7 +3158,7 @@ Function 351: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 352: DrawTextCodepoints() (7 input parameters) +Function 353: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3160,13 +3169,13 @@ Function 352: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 353: MeasureText() (2 input parameters) +Function 354: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 354: MeasureTextEx() (4 input parameters) +Function 355: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3174,180 +3183,180 @@ Function 354: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 355: GetGlyphIndex() (2 input parameters) +Function 356: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 356: GetGlyphInfo() (2 input parameters) +Function 357: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 357: GetGlyphAtlasRec() (2 input parameters) +Function 358: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 358: LoadUTF8() (2 input parameters) +Function 359: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 359: UnloadUTF8() (1 input parameters) +Function 360: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 360: LoadCodepoints() (2 input parameters) +Function 361: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 361: UnloadCodepoints() (1 input parameters) +Function 362: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 362: GetCodepointCount() (1 input parameters) +Function 363: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 363: GetCodepoint() (2 input parameters) +Function 364: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 364: GetCodepointNext() (2 input parameters) +Function 365: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 365: GetCodepointPrevious() (2 input parameters) +Function 366: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 366: CodepointToUTF8() (2 input parameters) +Function 367: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 367: TextCopy() (2 input parameters) +Function 368: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 368: TextIsEqual() (2 input parameters) +Function 369: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 369: TextLength() (1 input parameters) +Function 370: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 370: TextFormat() (2 input parameters) +Function 371: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 371: TextSubtext() (3 input parameters) +Function 372: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 372: TextReplace() (3 input parameters) +Function 373: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 373: TextInsert() (3 input parameters) +Function 374: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 374: TextJoin() (3 input parameters) +Function 375: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 375: TextSplit() (3 input parameters) +Function 376: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 376: TextAppend() (3 input parameters) +Function 377: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 377: TextFindIndex() (2 input parameters) +Function 378: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 378: TextToUpper() (1 input parameters) +Function 379: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 379: TextToLower() (1 input parameters) +Function 380: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 380: TextToPascal() (1 input parameters) +Function 381: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 381: TextToInteger() (1 input parameters) +Function 382: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 382: DrawLine3D() (3 input parameters) +Function 383: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 383: DrawPoint3D() (2 input parameters) +Function 384: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 384: DrawCircle3D() (5 input parameters) +Function 385: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3356,7 +3365,7 @@ Function 384: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 385: DrawTriangle3D() (4 input parameters) +Function 386: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3364,14 +3373,14 @@ Function 385: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 386: DrawTriangleStrip3D() (3 input parameters) +Function 387: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 387: DrawCube() (5 input parameters) +Function 388: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3380,14 +3389,14 @@ Function 387: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 388: DrawCubeV() (3 input parameters) +Function 389: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 389: DrawCubeWires() (5 input parameters) +Function 390: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3396,21 +3405,21 @@ Function 389: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 390: DrawCubeWiresV() (3 input parameters) +Function 391: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 391: DrawSphere() (3 input parameters) +Function 392: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 392: DrawSphereEx() (5 input parameters) +Function 393: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3419,7 +3428,7 @@ Function 392: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 393: DrawSphereWires() (5 input parameters) +Function 394: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3428,7 +3437,7 @@ Function 393: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 394: DrawCylinder() (6 input parameters) +Function 395: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3438,7 +3447,7 @@ Function 394: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 395: DrawCylinderEx() (6 input parameters) +Function 396: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3448,7 +3457,7 @@ Function 395: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 396: DrawCylinderWires() (6 input parameters) +Function 397: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3458,7 +3467,7 @@ Function 396: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 397: DrawCylinderWiresEx() (6 input parameters) +Function 398: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3468,7 +3477,7 @@ Function 397: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 398: DrawCapsule() (6 input parameters) +Function 399: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3478,7 +3487,7 @@ Function 398: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 399: DrawCapsuleWires() (6 input parameters) +Function 400: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3488,51 +3497,51 @@ Function 399: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 400: DrawPlane() (3 input parameters) +Function 401: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 401: DrawRay() (2 input parameters) +Function 402: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 402: DrawGrid() (2 input parameters) +Function 403: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 403: LoadModel() (1 input parameters) +Function 404: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 404: LoadModelFromMesh() (1 input parameters) +Function 405: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 405: IsModelReady() (1 input parameters) +Function 406: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 406: UnloadModel() (1 input parameters) +Function 407: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 407: GetModelBoundingBox() (1 input parameters) +Function 408: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 408: DrawModel() (4 input parameters) +Function 409: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3540,7 +3549,7 @@ Function 408: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 409: DrawModelEx() (6 input parameters) +Function 410: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3550,7 +3559,7 @@ Function 409: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 410: DrawModelWires() (4 input parameters) +Function 411: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3558,7 +3567,7 @@ Function 410: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 411: DrawModelWiresEx() (6 input parameters) +Function 412: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3568,13 +3577,13 @@ Function 411: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 412: DrawBoundingBox() (2 input parameters) +Function 413: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 413: DrawBillboard() (5 input parameters) +Function 414: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3583,7 +3592,7 @@ Function 413: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 414: DrawBillboardRec() (6 input parameters) +Function 415: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3593,7 +3602,7 @@ Function 414: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 415: DrawBillboardPro() (9 input parameters) +Function 416: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3606,13 +3615,13 @@ Function 415: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 416: UploadMesh() (2 input parameters) +Function 417: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 417: UpdateMeshBuffer() (5 input parameters) +Function 418: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3621,19 +3630,19 @@ Function 417: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 418: UnloadMesh() (1 input parameters) +Function 419: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 419: DrawMesh() (3 input parameters) +Function 420: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 420: DrawMeshInstanced() (4 input parameters) +Function 421: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3641,29 +3650,29 @@ Function 420: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 421: ExportMesh() (2 input parameters) +Function 422: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 422: GetMeshBoundingBox() (1 input parameters) +Function 423: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 423: GenMeshTangents() (1 input parameters) +Function 424: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 424: GenMeshPoly() (2 input parameters) +Function 425: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 425: GenMeshPlane() (4 input parameters) +Function 426: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3671,42 +3680,42 @@ Function 425: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 426: GenMeshCube() (3 input parameters) +Function 427: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 427: GenMeshSphere() (3 input parameters) +Function 428: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 428: GenMeshHemiSphere() (3 input parameters) +Function 429: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 429: GenMeshCylinder() (3 input parameters) +Function 430: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 430: GenMeshCone() (3 input parameters) +Function 431: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 431: GenMeshTorus() (4 input parameters) +Function 432: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3714,7 +3723,7 @@ Function 431: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 432: GenMeshKnot() (4 input parameters) +Function 433: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3722,84 +3731,84 @@ Function 432: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 433: GenMeshHeightmap() (2 input parameters) +Function 434: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 434: GenMeshCubicmap() (2 input parameters) +Function 435: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 435: LoadMaterials() (2 input parameters) +Function 436: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 436: LoadMaterialDefault() (0 input parameters) +Function 437: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 437: IsMaterialReady() (1 input parameters) +Function 438: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 438: UnloadMaterial() (1 input parameters) +Function 439: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 439: SetMaterialTexture() (3 input parameters) +Function 440: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 440: SetModelMeshMaterial() (3 input parameters) +Function 441: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 441: LoadModelAnimations() (2 input parameters) +Function 442: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: unsigned int *) -Function 442: UpdateModelAnimation() (3 input parameters) +Function 443: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 443: UnloadModelAnimation() (1 input parameters) +Function 444: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 444: UnloadModelAnimations() (2 input parameters) +Function 445: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: count (type: unsigned int) -Function 445: IsModelAnimationValid() (2 input parameters) +Function 446: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 446: CheckCollisionSpheres() (4 input parameters) +Function 447: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3807,40 +3816,40 @@ Function 446: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 447: CheckCollisionBoxes() (2 input parameters) +Function 448: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 448: CheckCollisionBoxSphere() (3 input parameters) +Function 449: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 449: GetRayCollisionSphere() (3 input parameters) +Function 450: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 450: GetRayCollisionBox() (2 input parameters) +Function 451: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 451: GetRayCollisionMesh() (3 input parameters) +Function 452: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 452: GetRayCollisionTriangle() (4 input parameters) +Function 453: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3848,7 +3857,7 @@ Function 452: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 453: GetRayCollisionQuad() (5 input parameters) +Function 454: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3857,143 +3866,143 @@ Function 453: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 454: InitAudioDevice() (0 input parameters) +Function 455: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 455: CloseAudioDevice() (0 input parameters) +Function 456: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 456: IsAudioDeviceReady() (0 input parameters) +Function 457: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 457: SetMasterVolume() (1 input parameters) +Function 458: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 458: LoadWave() (1 input parameters) +Function 459: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 459: LoadWaveFromMemory() (3 input parameters) +Function 460: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 460: IsWaveReady() (1 input parameters) +Function 461: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 461: LoadSound() (1 input parameters) +Function 462: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 462: LoadSoundFromWave() (1 input parameters) +Function 463: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 463: IsSoundReady() (1 input parameters) +Function 464: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 464: UpdateSound() (3 input parameters) +Function 465: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 465: UnloadWave() (1 input parameters) +Function 466: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 466: UnloadSound() (1 input parameters) +Function 467: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 467: ExportWave() (2 input parameters) +Function 468: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 468: ExportWaveAsCode() (2 input parameters) +Function 469: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 469: PlaySound() (1 input parameters) +Function 470: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 470: StopSound() (1 input parameters) +Function 471: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 471: PauseSound() (1 input parameters) +Function 472: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 472: ResumeSound() (1 input parameters) +Function 473: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 473: IsSoundPlaying() (1 input parameters) +Function 474: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 474: SetSoundVolume() (2 input parameters) +Function 475: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 475: SetSoundPitch() (2 input parameters) +Function 476: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 476: SetSoundPan() (2 input parameters) +Function 477: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 477: WaveCopy() (1 input parameters) +Function 478: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 478: WaveCrop() (3 input parameters) +Function 479: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 479: WaveFormat() (4 input parameters) +Function 480: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4001,203 +4010,203 @@ Function 479: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 480: LoadWaveSamples() (1 input parameters) +Function 481: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 481: UnloadWaveSamples() (1 input parameters) +Function 482: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 482: LoadMusicStream() (1 input parameters) +Function 483: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 483: LoadMusicStreamFromMemory() (3 input parameters) +Function 484: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 484: IsMusicReady() (1 input parameters) +Function 485: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 485: UnloadMusicStream() (1 input parameters) +Function 486: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 486: PlayMusicStream() (1 input parameters) +Function 487: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 487: IsMusicStreamPlaying() (1 input parameters) +Function 488: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 488: UpdateMusicStream() (1 input parameters) +Function 489: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 489: StopMusicStream() (1 input parameters) +Function 490: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 490: PauseMusicStream() (1 input parameters) +Function 491: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 491: ResumeMusicStream() (1 input parameters) +Function 492: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 492: SeekMusicStream() (2 input parameters) +Function 493: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 493: SetMusicVolume() (2 input parameters) +Function 494: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 494: SetMusicPitch() (2 input parameters) +Function 495: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 495: SetMusicPan() (2 input parameters) +Function 496: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 496: GetMusicTimeLength() (1 input parameters) +Function 497: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 497: GetMusicTimePlayed() (1 input parameters) +Function 498: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 498: LoadAudioStream() (3 input parameters) +Function 499: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 499: IsAudioStreamReady() (1 input parameters) +Function 500: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 500: UnloadAudioStream() (1 input parameters) +Function 501: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 501: UpdateAudioStream() (3 input parameters) +Function 502: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 502: IsAudioStreamProcessed() (1 input parameters) +Function 503: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 503: PlayAudioStream() (1 input parameters) +Function 504: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 504: PauseAudioStream() (1 input parameters) +Function 505: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 505: ResumeAudioStream() (1 input parameters) +Function 506: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 506: IsAudioStreamPlaying() (1 input parameters) +Function 507: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 507: StopAudioStream() (1 input parameters) +Function 508: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 508: SetAudioStreamVolume() (2 input parameters) +Function 509: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 509: SetAudioStreamPitch() (2 input parameters) +Function 510: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 510: SetAudioStreamPan() (2 input parameters) +Function 511: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 511: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 512: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 512: SetAudioStreamCallback() (2 input parameters) +Function 513: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 513: AttachAudioStreamProcessor() (2 input parameters) +Function 514: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 514: DetachAudioStreamProcessor() (2 input parameters) +Function 515: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 515: AttachAudioMixedProcessor() (1 input parameters) +Function 516: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline Param[1]: processor (type: AudioCallback) -Function 516: DetachAudioMixedProcessor() (1 input parameters) +Function 517: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 1c77f0266..85b3e3e4c 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -656,7 +656,7 @@ - + @@ -1549,7 +1549,7 @@ - + @@ -1563,6 +1563,13 @@ + + + + + + + diff --git a/projects/Geany/raylib.c.tags b/projects/Geany/raylib.c.tags index d41809983..4cc6d4605 100644 --- a/projects/Geany/raylib.c.tags +++ b/projects/Geany/raylib.c.tags @@ -213,6 +213,7 @@ ImageColorReplace|void|(Image *image, Color color, Color replace);| GenImageColor|Image|(int width, int height, Color color);| GenImageGradientLinear|Image|(int width, int height, int direction, Color start, Color end);| GenImageGradientRadial|Image|(int width, int height, float density, Color inner, Color outer);| +GenImageGradientSquare|Image|(int width, int height, float density, Color inner, Color outer);| GenImageChecked|Image|(int width, int height, int checksX, int checksY, Color col1, Color col2);| GenImageWhiteNoise|Image|(int width, int height, float factor);| GenImagePerlinNoise|Image|(int width, int height, int offsetX, int offsetY, float scale);| diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index 890501f2c..7e173a936 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -1396,6 +1396,15 @@ + + + + + + + + + diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index 3f8450130..f20f065e3 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -319,6 +319,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient +RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise diff --git a/src/raylib.h b/src/raylib.h index 02d8e4445..703e70c21 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1241,6 +1241,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient +RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise diff --git a/src/rtextures.c b/src/rtextures.c index d1cebe0d4..f6a747c0f 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -764,6 +764,55 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, return image; } +// Generate image: square gradient +Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer) +{ + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); + + float centerX = (float)width/2.0f; + float centerY = (float)height/2.0f; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + // Calculate the Manhattan distance from the center + float distX = fabsf(x - centerX); + float distY = fabsf(y - centerY); + + // Normalize the distances by the dimensions of the gradient rectangle + float normalizedDistX = distX / centerX; + float normalizedDistY = distY / centerY; + + // Calculate the total normalized Manhattan distance + float manhattanDist = fmax(normalizedDistX, normalizedDistY); + + // Subtract the density from the manhattanDist, then divide by (1 - density) + // This makes the gradient start from the center when density is 0, and from the edge when density is 1 + float factor = (manhattanDist - density) / (1.0f - density); + + // Clamp the factor between 0 and 1 + factor = fminf(fmaxf(factor, 0.f), 1.f); + + // Blend the colors based on the calculated factor + pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); + pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor)); + pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor)); + pixels[y*width + x].a = (int)((float)outer.a*factor + (float)inner.a*(1.0f - factor)); + } + } + + Image image = { + .data = pixels, + .width = width, + .height = height, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, + .mipmaps = 1 + }; + + return image; +} + // Generate image: checked Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2) { From 2937f2010c0cd6c297c47711fe82436068199e30 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 May 2023 16:06:03 +0200 Subject: [PATCH 0452/1710] Review coding conventions --- src/rtextures.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index f6a747c0f..eeeebba82 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -692,27 +692,26 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start, { Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); - float radianDirection = (float)(90 - direction) / 180.f * 3.14159f; + float radianDirection = (float)(90 - direction)/180.f*3.14159f; float cosDir = cos(radianDirection); float sinDir = sin(radianDirection); - int i, j; - for (i = 0; i < width; i++) + for (int i = 0; i < width; i++) { - for (j = 0; j < height; j++) + 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 = (i*cosDir + j*sinDir)/(width*cosDir + height*sinDir); float factor = pos; - factor = (factor > 1.f) ? 1.f : factor; // Clamp to [0,1] - factor = (factor < 0.f) ? 0.f : factor; // Clamp to [0,1] + factor = (factor > 1.0f)? 1.0f : factor; // Clamp to [0,1] + factor = (factor < 0.0f)? 0.0f : factor; // Clamp to [0,1] // Generate the color for this pixel - pixels[j * width + i].r = (int)((float)end.r*factor + (float)start.r*(1.f - factor)); - pixels[j * width + i].g = (int)((float)end.g*factor + (float)start.g*(1.f - factor)); - pixels[j * width + i].b = (int)((float)end.b*factor + (float)start.b*(1.f - factor)); - pixels[j * width + i].a = (int)((float)end.a*factor + (float)start.a*(1.f - factor)); + pixels[j*width + i].r = (int)((float)end.r*factor + (float)start.r*(1.0f - factor)); + pixels[j*width + i].g = (int)((float)end.g*factor + (float)start.g*(1.0f - factor)); + pixels[j*width + i].b = (int)((float)end.b*factor + (float)start.b*(1.0f - factor)); + pixels[j*width + i].a = (int)((float)end.a*factor + (float)start.a*(1.0f - factor)); } } @@ -789,10 +788,10 @@ Image GenImageGradientSquare(int width, int height, float density, Color inner, // Subtract the density from the manhattanDist, then divide by (1 - density) // This makes the gradient start from the center when density is 0, and from the edge when density is 1 - float factor = (manhattanDist - density) / (1.0f - density); + float factor = (manhattanDist - density)/(1.0f - density); // Clamp the factor between 0 and 1 - factor = fminf(fmaxf(factor, 0.f), 1.f); + factor = fminf(fmaxf(factor, 0.0f), 1.0f); // Blend the colors based on the calculated factor pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); From bf69b3805601627a509b92600c9b70efcddfedeb Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 May 2023 16:08:14 +0200 Subject: [PATCH 0453/1710] Added security check to file reading (memory allocations) --- src/utils.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/utils.c b/src/utils.c index 01ca235fa..aa2bfc40e 100644 --- a/src/utils.c +++ b/src/utils.c @@ -207,12 +207,16 @@ unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) { data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); - // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] - unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); - *bytesRead = count; + if (data != NULL) + { + // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] + unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); + *bytesRead = count; - if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); - else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); + if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); + 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); @@ -344,16 +348,21 @@ char *LoadFileText(const char *fileName) if (size > 0) { text = (char *)RL_MALLOC((size + 1)*sizeof(char)); - unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); + + 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); + // 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'; + // Zero-terminate the string + text[count] = '\0'; - TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName); + 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); From e465ed0850106a3574d01363874ae49ef7e478c9 Mon Sep 17 00:00:00 2001 From: Dane Madsen Date: Wed, 24 May 2023 17:22:51 +1000 Subject: [PATCH 0454/1710] Added ImageRotate (#3078) * Added ImageRotate * Quick rename of the example * Update ImageRotate by changing doubles to floats and checking code convention * Update API --- examples/Makefile | 1 + examples/textures/textures_image_rotate.c | 79 +++ examples/textures/textures_image_rotate.png | Bin 0 -> 24060 bytes parser/output/raylib_api.json | 15 + parser/output/raylib_api.lua | 9 + parser/output/raylib_api.txt | 498 +++++++++--------- parser/output/raylib_api.xml | 6 +- projects/Geany/raylib.c.tags | 1 + .../raylib_npp_parser/raylib_npp.xml | 6 + .../raylib_npp_parser/raylib_to_parse.h | 1 + src/raylib.h | 1 + src/rtextures.c | 59 +++ 12 files changed, 429 insertions(+), 247 deletions(-) create mode 100644 examples/textures/textures_image_rotate.c create mode 100644 examples/textures/textures_image_rotate.png diff --git a/examples/Makefile b/examples/Makefile index cc9ef2395..c2ea35bf4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -453,6 +453,7 @@ TEXTURES = \ textures/textures_image_generation \ textures/textures_image_loading \ textures/textures_image_processing \ + textures/textures_image_rotate \ textures/textures_image_text \ textures/textures_to_image \ textures/textures_raw_data \ diff --git a/examples/textures/textures_image_rotate.c b/examples/textures/textures_image_rotate.c new file mode 100644 index 000000000..a590e1e0a --- /dev/null +++ b/examples/textures/textures_image_rotate.c @@ -0,0 +1,79 @@ +/******************************************************************************************* +* +* raylib [textures] example - Image Rotation +* +* 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-2023 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define NUM_TEXTURES 3 + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture rotation"); + + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + Image image45 = LoadImage("resources/raylib_logo.png"); + Image image90 = LoadImage("resources/raylib_logo.png"); + Image imageNeg90 = LoadImage("resources/raylib_logo.png"); + + ImageRotate(&image45, 45); + ImageRotate(&image90, 90); + ImageRotate(&imageNeg90, -90); + + Texture2D textures[NUM_TEXTURES] = { 0 }; + + textures[0] = LoadTextureFromImage(image45); + textures[1] = LoadTextureFromImage(image90); + textures[2] = LoadTextureFromImage(imageNeg90); + + int currentTexture = 0; + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || IsKeyPressed(KEY_RIGHT)) + { + currentTexture = (currentTexture + 1)%NUM_TEXTURES; // Cycle between the textures + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawTexture(textures[currentTexture], screenWidth/2 - textures[currentTexture].width/2, screenHeight/2 - textures[currentTexture].height/2, WHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + for (int i = 0; i < NUM_TEXTURES; i++) UnloadTexture(textures[i]); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/textures/textures_image_rotate.png b/examples/textures/textures_image_rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..64bbc54e496f4587e2609a58cea3ceee637123d1 GIT binary patch literal 24060 zcmeFYcTkhj+Aj)Lnt;-kjz|r?3qga5N>hsT8j7@l^pb#uA|Qewy(v{dKsuo+Eh0^N zCx+faFM&|bo4EJ>_BZFuy|ZWTnfuQtqMyL8W9c_Jwm#?j`srh?$f>o3Y^>a&DY?fhj{4>cb#z_pHq6BD2mPUW3S5vn2(FJfY-* zXwA_sor1n-RNc)rl~_;Bqk zf!?Uq$8|Ew`+(hhgXHs5%j6+x%ICGtxv}KpDaH&fC1jQ)S4ku#%HPR9di=v1KE?BW zq)x7PTlrvdNpFYo{5kF|VM+BApEo`Z12S?ObgG7ro`v;>q10ZFzNg(r>=cPu4ty3`65~810I*p+)}NOb@n?eo2hjQ}cw~7ry(N z?4tC?r>`RAiWCGG`ztaww`h<7VI~yFUlwx)cNnf)kIm+ZqLY#gT(`O6N zQ+&s>AatmgnlAbc*H0Va*AeVuLx&qY2S;W%X#mYv+$JrHN9SMn|vX84J)X~O+)5^xq9;U>-QP;%HX>YB> ztuL)1rs1k$^W6TSpPP-2pQbL<&k-tb&8-ZkQ1np%1UTDxSaSL}JHgx)e3ZC}autBj zgl180PGT1iM|H(V zU0|F9nU+>Ao*qiv+`x0rf9mJ#s-f{O@i6y)pa9?@>SO6DDlQ@>>g+7~?{Bz!-1h>6 z{6nGt;~Va}fRBo5+qk=UxZ04HS}NayL!4g5vOAf6}54)aR$1&1EY%n zw;@#@X*~JY8w3dK?44bSZvkNcx1JvMw*Ljzf76ZdggBjlR|I(dUvmFj?|;sn*cs@h zp`mck1?ovq@6kOaZo>Eq)-F(cYX#y*OKTZxF(_05WGg0P36heMln2R6NkKuDGS*UJ zmg2G!;x?B5Ch8H)-NOQ+=Z6vMbtU)$1R<D+tq7v50Z}qGGS;>dvSJ`932Rx9l&z#C$VyC10%R>A zZ(}2C1+@{Alp~6=hAKR8adWl=%xUjzX=fwq3bP|VAONm#_sJtAZV3^w|7v;SWa(iG zyr9IbZV&VH`L74M_Rcms9+m_&#bp4kr6eWfBqU@cBqhZEtJ553;-th)P>GZ9=D!|m0PbXM>0x=#(!&N2 zDkdSRASSIKA*m}Np&%x%AR!?nE~X&%Z~a}Y?QMPk-+B|Ahg0#d$sgLg1LOM=oBn!~ zj?Ig|-~N7evM0I{CnwP<6fB{Cr{HesWn)d$6Oi@y5%jqw%+3ag9{-^0Kilp94^*&) zT3gv#+e(3CWTmA*QkJ$dAbB})F_473ytuWTrL~O>^e>zKCEeY{*2CM<&E~EhfFpnv zKu;o8ocu&X3H*0myr0_;tN;K85|ak~?|_N^W5S|@nDNgUD~kSam?#oE{9BL#-uv4I z1TP>KivBAY{sS{W-~XHc{$Y#%n?rDN{?8!)5x)Ne*Z;uvA0hA`b^afA{SRFK5d!~F z=l@~X|7UPf{MT~I1_o3?-oRpsAyR!0SZH0cQd7N0az^<1)Q}eow2-?#G;}8+p`;`H zCnZTvWd<5Adpy#(e|hmD6BQ}rEf{MJ2?;03qkDICea5j<=3UPErm{!kmd0UMULRi# zqb&*xdr3~)!qIZSD3a;AF;(4l9gWR9=hm@p5nMNYl3kz5FEbiH?D)u= zE9D-~+4cJ9YS#KEeIswJDBdRD84Hr~bEw`iQS?{pHF1v=UtoszAkzagqVlKwl~;QW z+>1;D(9-R=$o1S5*h*b7C7`8a9W8_`O8K-#h?Ion>$<%N@Qd>$@ZcO73CT;6b0j2W zod5Xt|5r0Xf&YUh{}0XIU)24%_Wg7czSAvo?mPK@E1gyi**6Kv|5(s}HZ0Kl``G5u zNYnIvk~<`%7KjahG`{&fjUw&zLE{0EnE>V28()~i?Ky&$ zlbcV~$Vdq~CT;dj;TA&%`-QnKyeuNw-p%XaBykFj+LGr8#bk;Grdo1dg^8q*jxRl#_r@k2mh^`hlGKm1X>*ts-P3IAC`%KXMsR z;JtT!%Z+ocnu(;OawrVXbiB~@mV2;$M@$SF=ln)hU=op6?}kQhn~kHedgX=)-(?8Ac7H7OE<+8t;N#B zyw0mQ*g|*kZ{ASg)qj(Huqg8{`%HTd@D$gOW>pc|xqV-2KonAQcNHHEt zN)UkAylNc4aC=^nQS<00bk6qi4CW@g5nz4N3oj)Z!Bua*KYjkT+hlDLwz)C5_7Hw4 zyY~{~R4K^?T9TgUxStnez~vc$nX`N&8cWzm#1h_}@06<`<08;0PZ+)?9K6zkp?^^9 z3=>Xt3TPnbioG%JJ&Sr~yG$ulO!>DPC;X-e9S6Q7O?kcn;SUdyE8`->ne6j$T1+^_kH zy=ajRuV__$qiQ_ONO6d%Y6her(2&uHUXbr58f#4+Zqf*sH~kqEZ<4d`+uGcUVUK<- zR*IL~%8%&y6!ZKFYaQB_uHo+_ltQAkbxcbxDU;u7nXBOC-C6MuX}KZG%h1U=274v*GEJqjk@FbN5e~#ikR6i8C;jz zw&%-W*M0?9ZK=O7`FBN!ku8K^&u7#eq4#~8Zb01p`9hjD?1G}Q7WtK!tnrO4?%-d& zjE|Zga9tp{YR+b<-bdA2T-xQVrApTkUz}t8zjZb&UD#`ddYM*N>VCD}IovM~U0Ply z`qOs^hR1l6?;tL30!>|K8duSgMj=p2H;7W+pxjUV=mg%7xC;aUz#`A7IDnz5!nf}h zPW5+BBO#5geLJk`rh^+y0vyKci>7yfUn8n2*n+~PM)j+lS=~Pzb}PIHfnW{Vp$=m^ zPj8zMb^OE)e;2V@lnKuqT3-P3;>ESq3LjGo36r18Z{Sk9oH{jcfT)3wg)I{dt%u2y z(rrE0>C9LksQ~ zM(i)ETvU@Oin{#7Q{srKr9m)d)kdBXh$43^afw+O{3`?HtkQM&XQIXq5OT*&t?yKw z9@zCXCQM?3X}{%Vrjg3`52qaZ8&{+ms2r0qvmg_tOXfT=$vIvK%U zI~7cio7@*z-75u40Qbwx-Xh`TkTu!{sY~MZ^I=i@~H2;i*%9m7eDswX7*0^ z7F(DM36vy+pzlc(96G@s9WjM3F7X?350%Ms>;HC%H{q4dK9B1Mt^ieqx!ptTV~|^3J$vlx@g*;48vq)i??D5`;)>3$2gFmDsNQ(I;ZE5Zy-RV zu3Q|3$4HpT`om-w1bx7@izrLibZ&f~)+f&JIs__WSRgHZqhI5~m*hLKe$$qA#qym~ z#Blv*Feqw{>kTic@LI5svhWC9C+1;^M_;_}i#{+$+W2+!eCt9z%8FV- zz#c-BXh=Ruc+kT;%Dtk>boSE_Oe<*%1(8}>LpL<4kD zD9N`Y64Rff_LqS5n5gg4uA803N7p|d`{D4D z5@pFJLZ%~0#B`*oiIWrj+Ntso>sFX$RSVj6V!-U-hwNPBT;w$n#X!%kiE*MIr+(kO zDrCi%z!y1iuMg@XYJOcAJ!lp_MH-ZuqnrP_!De{?C-2_b3H!aL?(jJL!W2DJ-@pTyu1%kHqWFzss$&&YX}?YW6e95u&1=Z8lb?XI8t zIt+8`*E1>U&zgOB{<5GxfLD@Px&*l!(0Td8t`Cua7J-=A2b~4+4O9r5PyER+0dm0) z&+2BWb^GR@#96K`A@L2^Jp;|L%4uOPpdtiJ+SZ~9<52PBC)-mE%GP(;8&IpFNC6Me&gKUk)>{C3C|I6Q4@ zcmCqZHMW8(RP40!2Zh7>4Sa!k?-lwj8{;iUrTJ}M;+zaP;U7ajSbt|8>PKpf=Od_Q z(M&E~V1>0T0rd)cd|lhI$Mex~*XiHva!gK-4?fK`o7phH)gBkY`zFW#d(q~3f8=vo_<#J-y4$}7IValN)!&CHIdDUfE2gPk9B zD+~VAx#+hZfaDYd-WJntLxZ z#r3dew=3=)sTnckHMh>-?I=)+fX4!XS^f$Ka>jF@N@jnDSN0`Ji>lkw@?h=#)S`0u+wyZp`hjU7)xe?xh|;VfQ8C;~4U1f+3B#?ixG;<- z>8&{9#}V7(?xJqhLfD&elmQ^VQYMa{qrGG?2>cGJxSaMJP(UvV!|53gE(Y6T50+)v`(xu1tpO0#NJp7~&qLWj8FR zO6j>SFlx;k@39MCe9ZPu-^2<1C2zIXsT=7$Yseyry6W$ut(#jjiQlNH)7Bu4DtsIw=goe9eGpqob?}AtxU7WYs!r!~9jhc!w>dgnPWo=J!&$@$~y)J#>aLK{2#}!93P8KY}z?&`$jvC!! zIUBAXX$LR1>xt8~s~4m~Z~dM0L)>!^^m@|-)ouDGt8ar;&Q|HMCK0o zK3L`r4o^eZ=eKxhn5>%r1cf$1GVlX**J!w|Kphm{SarBd zJ=SCuZD`af7s}^u6n2fP9-s272cMMZ{~6W~WDEkVL@K9);nBaKFM22-{Mu3LiYWU) zYrvjvsaZ9xK)P3zZGb~_SETqzs#9aR)7KRwe(5AP-F?+AX*HEn47)h0g;pUBuR{xd zOfg*0vU)Abur|YLxmZ^RV^bI{N_8*)S)HC`QB+jXy!FnY^DHJ4P0ocB9}MZsCy_KU z(U*t-vKYW_7D|qyHs|8awd))_OtiOX7 zjywxAJFm4QXcKab|)t?0;FyCkz*!YA8_t!p!&P(MrY{me_2F#4Xl0N z&Y3I;O)l6(|HX?ce_&9}R&ym?NcqZ>GE(>?d(B)!B89=gT~RiR5J~beCH|@U@6rBB z?i1%x?5ad$pujR-#MFU`VrvFg@12=@Q)#7<9%6qv*TAd8xfKQqg5LMl=xb61B zoYCi3dn`q6d9S1Z4&ItzG8FoLYxZyGpu)@o`xp^{j%i1^^~FU_EqDDt$UIKrmVYMZ zO$U0VGpi=LksoP{#boCE(Nwlmd~UOW4*eae=`NZsZZtPA!FU8L3IWf6QgiSIJL!?a zpX{+QCe5d%P{$8-jla3pAu{`2%p|^IN}+1Po-Wn@6lRpgbS8f>2O|a zSjRFE57A%YSx8#jJeZ^;;JBzC`V53Qzi0(D6H%aIB+bs*NRdxrzYK(Y(rZnBJDld5?A>|w; z?}>=PWOi29ur-BX?4X)Ijoi2eWMM>a1{RDFK|#4X-zJRm7bs>79vZCNJdl^X*7v%> zcwsBHoGK|=jEYwe8&7c>uDnGT=if@&v4cTLm8S_TiVRlF&g=9DBH#bIZ*?sHpOJ2rIqKERo z+Q#EwR(XcQ^-s+>)^(75;lLsi$X#MQKwx~CIZp$!!Ac1ey?*Gzt)6tk2zAtTwH$CK&= z>d^;9@$P*N;Mvf+Byrzm`D7Rky=Ht#+B<1j_fv<>t8vFs*-J(FtMv;`8p=46 z1X=>$KJmi4>>o+>DGAWif7URq5k#5PZYdy|mIV@$5x4X@L!U`_4tHQ2SNdlkbw8;T zDZX8-GqY%d?@R!p&H=u&y(yme@b(wx*f!&pKXrqb7CU+#$5q-d%N`f@eBI?M{9QaH zW#;zHVs|7fu;eMOAox;A=XQc^njmDuXO;GA%e8!C)-#-zg%a>sbB zlEgO}7@WIz_A#5f`;+OZF+yJ2wo+~*Sp|IhWu6sEgyMmWqwlqQPcCMXJKK^kXOw)g z(qG=|wa!xYX~RKgJ#T%5=J~E~KVQ9l^EGZFmag+HVxUr~F*>HRGN0G;av(bgF))5d zQgi6tx68#PuaMCT=~ic|G?~uCtsTC-ln0wSf63eUd^HF??)P3qw_$zZn>+JvRf%Wk ztGC$RCXWkbl%@$)?-Hk*Vrevq5nxdP{*gW{Zph4LYh;RlG}rEqm0F@z6k6LgUik9F zX-JdvfZy_JXK<0%VW}NzD#5&EvfO)UW2i6D_)Y8N>Iev)7EDM%J*K!XCl8cH8uByv zGx_XD9wg;>J$f?oJH%74VlYFc&ulZdJ%)nEh7CFXYk7HKs^#nI_-U%?o@ZLzXS<-W ztO>0sViNpg5)_pTDfWo<0r(UU_W0N@)1>(0d$&_9<68(4yKH(?~TzGg7PoYm~j>h6aM^z9-ResI5M3Y1Tf1%Q=jF zHoVBJ#%P{ae*k$Iv{_~h4ztw#tFjuYkFDzpe z91P}Kixh|Xubr!Zr@fe&juh{O<%|zicRM4sRumGvE(i$}?BF^3NBp+u4jPqeuJpb( zbn8{^gs!c>5in?fb~2f=T62v1OMwSCvgP+rdDV0a!jaEknN-tQ7JM_=PC3p;aLnG( zqq}7rnw0C<$KZ`ts5dnSujdX7s2Di!=h$?0zAL$r3U359)oJg%)S(04iuePoe$vT^ zdJ~x7<*49rVx(M7o4=I_vT&q0LSCGady*6Xj15@tUFQ@}m$vmT;BdF<>KkF%F7n!e z7EEdA?gvzL*~X!7ffp)k-TuOb+w3!mot9n%6P=Jx20llzd@|^BGPh$VdzWTFv)cHZMo$fWP*^|WX8zd6Wbwu6 zSd*96dyBKp4mRz&JHJsRggZydmdzD7#ph=*FxzEg7J7Fj)T)22$JSl?D-;YDE)}m@ z7hf5Vv8CJ=3BY^qyHymeqLb`bR#5${{S?2J2gkT|pzPCCxk{V6>Q5V$Ts81>x{|>N zgB@gUap~W=QgXB$!Fq&$+A6zEK?kvyMg`}NXlupJQWkRdx#&UY+O0rGAd8IYVTCk$ zJ_fe;xL#6!yfJW zqx2{G3Re{*3BJJUf*Tdkh7Atn+@JYYLv`_fzitXT_sZtQA}`}Q|88>eG0te_o&I2t z&XZXD1K2y{zR`Oot)d~Xss;C<{_yHGnZ3IE+m}_UE~ibhvk>)zl5-%>-5Sc3uHvw8 z*wWRYWoFViX~f6J)6W#$4^S3W?_%q0q&yQlGVws+4n>`dO#IRc>dHZ^V#dwih`>#^ zsCFV5O1Tw>^)4^1y!c1zL+rU?1gii4|%U<+;Z=Bv^w3)iC>9(+l{d%vJ2&gFg!3OFo#CYtW>IOmN*p(uN&Zl6i{ z$J_#e&7MQLWhL>pP)5y)B2#>Wy?yDVetb1*>(B_#*~Hu2ymi`;a;i>A09p7N;ViCx z=__h@HYi@Sz1sWyntu_ZJJH=V)XiV09s4S8#F4TqM#LhvcVGlzW)(HHf0!7M z5&Nnl7Fx0XYREL+4@L3Bh?2ia@%J^q;A zegzt zI$tVTHJNER5c^g4pP7}5&EH)`im@n>-1Ix5%>v-GRap6!my z2LV$EjZSzjF&o5Sc!pWNRBcWj8KOgr6~3{8_MV%e)A@V$bt*tQRH7`J{c7DZH!^QP z9-)WdS;$pwL9(~sTkVmSO8>ksGWnzCh8;sZEd7&Jeepl<0(^VsKONIl-Z62{4LBSn zWEH2@Jo^bi7tk--NmlSg#Vqayzc2G;*bXRDD1$u{B;kF6aY7#5tme{yya4n5Xjf zgGt^9$A%ubY=wLv*8s~?GEyM4>noSQg6jq zYg{ZyrmJ(G_gG0;o4i&PqtTNSii;BtyIqSu3ik7OhYe&~X~u^cFpgH6uZGu=aRG(& zq8yxlh=C#aX&@8Y;eJLs@JFz!&jz=Le>uRB6f8fQesu(Vp@zKqe`3?ft+Et^kk2ko{$)!#KW z(!UqSq{WNo9AR`ldH340?&nwGb9J<}Z_Z>7j~-KguRT`}3+x__$^We%=`P4AG89u| z^8nE zq(>ZYv*x0VYxQl`7qj-S?{ftKTirnMH+F@iZzjK&=~3T6m#QSS^x_i^C9MFPlU$1^ zMopph51^E*fi?*`Z291^B01bxjr*M6L1=egmzvhyMEIIGvAL-;#x zM@d$(t-XE@c?;PKSwHE#*xy|uVp{tbEg)8d9$bp0tqlI1s=d5~+(ysjHKT0LS_jKA zJdLvBQAL9ahrI1NgMtUHRFT+M_#0|%6vf?>Il4uiOAA4{qh*CQna*R<>HgjEDcEH% z$iSc-Y(*_Bd8ATar_D_mNSwfVcUh&jzM0rAg2PmCXj=ne|BzYKbbKu%QSj|zj~wd> zaNzPb4r%lGPURi{Kk9{37#}Ygc0TZpfueu}c|*nFZSU=g6U?Z-(YiD;pXDU znvA|wcNma#=0)U`ojg3q2bU-)Tw5Hm5FhWdqNW0IXj!d4hd4r3^>!udvnB{@Bwg3{ zC7lp`X1!3{y=lBr#Ml{zpKsa{ZR&Akos&IQFDacY+}(JFg${OQW`jA@oQ^YgSZ*l4 zr#*L$%yqa~)vC$9Bj9z>gf&IfFtvtz_*~4DJ>DktNqMafXO(8m#!fH>cVn%fDsKz| z^X|$=u-xu7+&pAH1y_Fk!o|&cTDR{m=FQ{%#V>S%_dtrFab(s)ccKAwaA*Komy~Jj z(diW?va5gdDgKIo6~A)LQ;BaM1B8r68tl!k8r`ni&FGf-DAb%mC5jQ`Mgumu&Z>yJH3f2FXs|Z_(a*AYIUI@6WTOk80Br{NUP52 zv-Ck>w&aO^xqTCjPHI(57Wf?~uqeXL;?i01l?Wnrfzwl99FlB|v4Ht*5Qrh~#(~t58$_i8U;=pwa z4g|BJT_ftR`rE)&9@(bFEVYu=UPhOcbD5t=u6s)+m-l%UHm>%!X~4oBJ2zAyB=BXH z*03Lj)ra+5yHd6H)u!dtL(u+qIf;2v^7Ibbyc!$W!2I z4fO_aZRkXm;{xrU8pI4;2AP)9p04LBg=52tFP+veJxGVp2F|5-gocV6e2PG!Q6Umd z{LWaCVZXFtk?h#J3x7f*F6YS~X$$&pthp8ZOjMJ3C#tu$K!J36rT?PHJ^09ot=mZDZr{RsaJ%u2S0wS!?!(?2U_y^yL&SY}`LStnK(zo87pzu``Gbq9Ih^ z?2vT7mHY+Ii5Bim+oXriT&!uHNuv?J%bao=T!rAE&b7}X)CM4by_ljzp%Un_{Ji_b za1RpJ8Q*{MRU9j%=**!x+S~MAa{t0mJx9*S;?dq}^JO65=96<>Icrfi8OacQYur2g z_R`yc1{Y*roi!WPr}-Nvn8u`>o@*@LR~|?CW(4E@2*+U8%Bn4X_hX$nZgk~=IZ#YB zxhT5TAelPJ5sH$bE`m%J3NGG@rUjfLhB3OjB=G?`;POws3F=d8S88(XjD-i1Wl#2Y zCzUwzqvgSrxu-|HDYuOLviyAI8|qkJpe><2HHG_dsZ*uNq#>#u*#k-rAamL&24%1G zFGsdhvVPLX#~m5*8ntF{U61TT}@ROvhf)|vX#lZQDGs1*hh3LsHfl6kz1JB z(qvjwV;G5Kd9jKx5)SG<^p|7qOhr7NbrggXlI9267{|&9Z4!~P-RI_)5hLk!VtseN zdMN)ob{Bb;e(O3zh5LA!rfFzp&oOfp(($^>vBHSRSu+0Mb74R)qm$v#(RIdj(z3<# z0IjKlqw{yb4ikm(B1*p)o~yy%nBbjVpB4^xRd-33_?~Wm&ML`s92Z1MsM&W;V(_(t z?D+WJU#Y9ZdVv96d8BUG`~-NDaXGGUc>xq?-;c~$*=gAHCbI<-c5NKU^7+&IUww8) zt9D9cx3DC%_J-WI>g@l*`?|fhc7;To?|8^d(nwM+cfH!$bxKJ#3sfO;cog%wOro}R zUA8*Sv+4ILJyFzCvPTR%ht2zcga#^s+grEoZkpa)XN<%;&L5b*bjh%o>TU8eMc{6{ z|FnLh9_6d~Jz~lhSlS|bepXD1vb|hL6Yck$!VKQuqq4QWU=#J>3{LhBBbfxQLdis*$mzCYn8yo9ldU{b3Bf)Ht|EiI!$9`mnK*?k=?u zFDAAJLO2#qb(|{~r4$^i`sJ*D+pl+K^JyQqPuOpn4M{!klzf{fdv|l|5xYGBp8}>= z9S*rIesC9U)*5M86SvGk7-_R+%p6{mK94h;0cPoUXF%<6rZA^U9L$29Ld)2g2+c8b^4IGayb?M&wJ z8xM9VWGg(sVjj5uvL(OQV~_OHRQ*AK;DwQT&A&oJqqi)B%M%{xrPnq2b1nv zWL!axBgJgN(cv$8>ugm!s~p%DEi_Ig4!=3F?lcaxG#&tpn-iUC0cZP$UEE`3j##_2 zd(-oC3;SVWz*VayZC71@GVH?dPr2H37LQr7Wuqm za5!UxDHBw<>$4%A7Gvnd96qA8Zc2w}}_w7czV9o>aD~(qB zIM037+A%EONI==d3A zF3;dx?r2!mXI^OC*(tt7>1J~ky>W{LeHkXoz*1opxp zg@sKk>hH4`8~v;0d<`bNbW|GA3srjCo+}|a(jVI_h9oX9vzJAw0n4N(Wdy8 zhgxWK)@yem3SHd_uJQgF-k?GA7M3C}rl0(=YVR{=-&O~j7T}GsiNE4E=MP3&ZTV#I zN^aOJ$M}*;MAb|N4N*F<2yy@2r;c6(P&6vx|;>C#k7aRCYHWB}?&pDJ*rwmt(C<#CaGeUC1$A{;|+z;N& zv3uLK9DZu|8%UX^Km9{<%4vdh%Ml1tXL6b!AmJ_2O`v+kCm+O`F#b~Rz}?UFEZ`LC zIiHb(gnIW}2BL0ZaP)K4)Zf^_f!U;YG2L}KSfvEPj|7u(2-k&EEo->=>G+)C(Hy|7 z-!{>J7oKDSLVs*MXbYU|;gdxUYV9UQY8mm9Wqaw;SlLHSXJZHM+#`$`uG6kX?ol8( zu*oha6htMPE7}+Cjo{gLuV5u_vp`RW66s&8X*HrJH@%AVxB(OiXbfR#P2cDGqUcT~iSK z2JE`+d{{lH*!`+m(D|$*iRtzM%R3}YS;sHr8*XoXYs0dmCWr=zv%^=olFq#tdtI6# zTo0YiPdj8rH%SG31h)59#SbPU#$7tq4zLvgl;%6~KlcL0M}+S<8qXFVNgu>=WlL07 zoM8)mqpr;Ym)>x|sk%iVSGFz76Jstj?_2lK2V*Lifa70A*Z5>|7SYa*^{6m)NcK6i zWqSCz!VFYf^DE2?rgQEG4Eybg{hpd~CE~w#Z9) zQrV*3F}or8WIki{k5O-9Bi|S4#7*G3@AuN+=$K~Db+fP{7rg2#3~-YJxM?{E)p9ON zqvrCi%t!<;E`@eG{rn{3yxdj!;FE#h@XXDwp0B0)3sTp>gy?&f%NlmqTlYKL#{s10 ze#`eKwQCLz1ADrphSwmCxHi$@hV{nv>!1(DRpxfSBRMa~CcU5Yk2wzZw^w;cp4L15 zW-wjlEgl)at3kjUxE%+0B+Oe2rCVBkcnGSVl;{HW0rz{@jTIhXy-?%q13=p~v(48` zgAMCljT@6?RsAPF>qeYk*;rZj1f=K2)XKp$(po;?S#!Yd`F5gd_67EzzTLwu)xt`Z z?lu#=!cHQn_1E&u`TZl>_4=Kj&F|-FoK9X~)1zXVWv1Cy|GY3e{G}ff%I?Rncy`$9 zB6!^~;s6+A3$>)xZTGd3U(2-4irwcWBqNALFukUPXKekg)Q0Z6I zULbd6Z${B+DPjV@0_-Q9I}VOMM9zzzZHu;anxAeLofEIBu+4|;p--{s=3q*uX7>#I zWYO!g4gBW3XrMfN@iv;g#HUTjt9??|1+1)B1}5_+b;7$m-!710 z{f5d?tseNs;(RI%>4gilw0C-*kja}QH0niT<8x6b-CmtL+3Pzwq6h(#dLMDcQ`{`( zSDX+AD4e?2N~a#mel|W#7G@c}j(5Itwpf6x74_6u2+p17v9cc}mrO8kOU<*tQYy=3Ne6B`o)_gvquXv`cV-{SCBa@oyY?!Rl^iQ{Ay1v{ z3bi&|4R<6<2Yol&7C)-5F>A5K$Tw?Jm%c2xub7QQ7XTQ}s9qISg_}h}t{XC)5my;{8w=o$*NwL|KwI zfCO?_NwIjO#1ER^jdX{w{|b$cnKy7A`Rv>kdVlMj4pWYuZFan18_eAOn?-CcO1^ej zZ-dwu4KrX*8@O7F&Srv8M9n-kA2BlfVu^K;Y)rb zJ_J99N^=y6zMWLRD{iE$g|PX+9{9c(%z8z_3RYFTAl-d}zexyJt_whdubwXY48^hX zvZYmJ+&0(bxD?z-V}8`1(9GsNdDZOfXe1QH?(dWrT|&-ftY*s?MKX`H$sfy$iEU0W zx%I3(FjIPoO%b(T`5w`83pJ>rJff}ENeucy(fQ{besp|@amh2LXb8lG2Fk5@?Gbzk zGt(APpYAC0mmOOIlFRTS;`p@PUZ8 zG7(32Hb@mkPP>AyLp!wg{))#1=JcJ?E$=ftXApc|$b1Zm0B*W)kp3#^y?!?re%`~| z3Y_8zT@UZA>IjbJt@~a%sYMKFlcCWq5c@X!m&l7g!f>Z{#Z+<@rm}q6{!rS+AzH}e zrYq)|N?WO`HHf+s=|yKGcvhU_pV0HH8(f1JT?PrX(QeJhD`nhu8w>jItF|@fwM5qTrW?0^~ zqHU*suSL~r!`n&gXDY3BKfBL5#@*5MZ0dzK-;p~KpImptvZU~~eZyzb?_h-%VrfRk z<>I4K6L8{JL1H{({D+sFM|Njxa#2YZzHt!XA`$epn$Oz1q|*qUe1K@u%_!2N*f7DZ zFdf!=>>aGGn9q(&>JsB_5H=~Am*QKQRaJdzPjD264~8bFP`@N33b6*o$oKm8^vJdQ zLl%OgrFp(fyy6UqIrgajg52;Igm;f3pL%X|?0^N$XRk|g5vARP{4^O@gnv8}My?44 zZJt-&JWq@_Kv47b*()?>WA|J7Ttr*Of-2*(Rt|Rj8f(xolfQgIzgiKaG1ch#Lmx{^ zWA5G5+> z8|jTdr>j&<^hej%kcW;hynqXoiNblP%Y9P;cr51CW%jqz>y+j=NR7{ymCtqupLf8p z1l@MKY@_&bN5=6ZsdMl@=g+(eS%!(6Lyxb~Ze@A)NT$hljkjcPNyY}aE0mGY^5ojM zmQ02ul!xG`-v{Oa;w6KUvA!v(TaQD=-y3Nh5tFgnbxeV#pcOSf%r{7Yy3v3hK3% z$!$6sDxnq)JnZCYTgsgn+U-J(RyvL}6JqcN>M`aRuphKDkO>)QnQ%u-|TX_;KCjfH4dqp zfWDd@40imSl=KEQW;ZEFqddEF&taO95L`*#iwCg|IY)xR1Sw};kg;f{zEgP!Yytp4 z88_`2??fEC!qvXTlY!@>`f%6n7UD4hpI|eR($ww9ky~(*ybm(<-&DspMO;c58^WFy zhfl5bHQtjJI9nosOo0YxL zj_%*az$L8xpw_`2xKnzdCXSi0dFosVB2rAhU$6#VKkxs#Y}qnu#Bas(Z%Al{Jbd(F z+|zWg_B6)uV?V9-fP**gn6Tc*6?vZyug&1Hd*JHQm-y2<6l$m54O_^b0)f>ta^xDI&oXiyVTjUaLa3;U#uNO!20*zdgc_ukYL^M#j`X zwAvn4e%0_k#Y$Gknl%TL(P9Sh`+-y$)Q(l>>(xBlOWK(8ge@s|G64ha^1+b37i5Ok z3(yB3rA0mL4sBCWd_~^oj%@v~8azIT=Fiu@iq3b-0+WE}hK-U~>UOfu1W?UN2R~mn&0>B9_%!P~iPi z&+~Vln<`fRhJE-Y_Mr~Zy(s8qOyp4ygp9l{PqUgaYIdTPeRcD~+Ru(*N0GV}>U0ms zOLIRib5OYi?4-01I1ZH6G%7!J`%cDs17s#WM@%v3vlDbn$>~Z-@pXtpe0XIQaNiew zRQu%HvqTp3v%k*F2zmUbMf>6cF2Hf)kSq%lKnIp-=gPhbWQJ{EVMp#%Zf>4C1Y1W^ zZ^(Wy9yT(P_GBYqR(|=y{!jMk*|^){h`fjI=(musx4#&qfj#jN=Eqh(SV!|4Cu4+| zbvh$@%01~Zpb>^Dk)`GUZiGiPF`wcC&)zI0hGL*=aV2mY+#H`W3!%R368+gC4wF$H z=3@eDa{J%0KmgIpoky0g?B43s6z|;^%BQo+>56=F8K=t`gc-NQYJrhEIJMBI3_LQU z?kd;|rN{s-HbIs>#3d%M9$jR&37l#GR+PYrENzVNw(PPS(_Akwb|MN2oM zRKeRHXpI|nuYnuS@J)fG=Z@w0IEU9P^ma`C60U`|>`9U#yz};Y4fGk=GJf~qfCh}S z@By|hq^^QxK3kjsXXoD+vE)hqWsg#Z#TVE4h^a=NiVNGexqC8w*y9~iT|W=%dkr5} z|7p9de-mDR*zNK@U^ajj%&u5JvMHk$6xHo1j7;Avr#UfG<|!Tdt*|h7Lac4%^0;me z-|`M?_8Gvg5OQKjH0^i-w2cw20jl%c{Bb!;+1O!$XV}Z9phoM&4}aiUr zj}rc?2o0BWu$`a4@0y|YXJjnc&+MVmmRx4nAdmmf{zi$je-}Wj#}{h{4TW#}zmO>M ziZ}=5J}*3VP3|;tS)90a!x;R<0A;XM0p~}60aMspduJVf-m4hM+DpuWfuf@;=&?XK)p$xgBsKk1SOP$qQjr`Gb?b8P%{kqDy1)MVobEL^r+q%H z&Z*%q*b^+ziKBjXFSJONUbC<#L~YU9byhBDXa&Pc60@NUL~$_0pVQ@bPID`qpX12H|};KFuLy+=r$@Z*-z>gQuH69 z4Hx!1(Lqd;ie^JOh}4+C$2{8uBvZhsEg9O5R?Zd<2_z2Im%tGA96DuPkR;KdHGukr+(kA zF8$XvLXZI*vm-Djds&l<^!iq#ezLP6KTct;dY+$n28}CWUU?l0Y#sHhr#mFBj4m{U zMT(C!!{;+ROT62{yT;pg%i{Fq#v9i1*7UY&lNTztdd=& zlm#ZSxXgu@aaQ`wBoa5eiTWl84#d3&69|kAnvSEpX1PY7F+L+9k3rUsF)GM~x+e8& zKMY;W8%Vw?EAq|JP%i8X6PP2A?;x>p;haA+vZ-#|*%KA2@d_b!8;(G1w`bKojxEIkhrql(sCX<~8-dmpB5eTHq9GS$ z_R}q;QUsHN*N)P&-zR^BS;3SqHV|B7%j2Bd`*Pp5?UwvKf{W@YA?mK^BWbZ24`n_E z&*kZE5s&XTc2m`7apxcPT0=0R><)lX$>0!9%0qCR#FK4MBr;jFks{g7%JiKhD__Xs zdND>dM(Cy&xCKU)a3nphzi9Ce4`uYxyeU)e-dFiRE0;a9I$I=^KxZJ^5_60bfnh!XcV@u)p1G-*fBb&s3-y|Z0D<7?^ zR8LtkT;B{VzPH|>OybVCZ=MrVa0Ku#hjPsjeOe(k(JN%awu&g;>;eA7KyT~6g7KYn z7Iv&!(N}CkXd!IAA49$4i?Y3Z-Qs{(@&us3`a`vgBImAVTiB1ci}|*E;L`^n$_TUD zz*gzl>RYpT`;%0{Jrf`37?9(F6RX+zx7RQB*nDL#)Dw^~5+J;~be90+H-ZAxgOggk zo$9HIo%ZEifz7*M2NQ(BbD;QW1{7~Yp!EdM;^irT_cB(gt4gJhla=k9Z`il^ZY$%nab2|)#;k;HDHz~tiWUy`nX#HXk3~3Ps1ZPQYS9PI5 zfg~NhPw5~I{~pTqP+q39hDFZUutIxm=GBJ}dwm}7jwW%d)YFM*U&A|J9#eg61@%pG zq*l%JbtLFs1~YQ`Be%UZ%`Ncipvddxhio3T%0|-Ejf0co+ACZgE;= zX9-;wE$+L>xg{I&_%6>HXg;Sh?q1BQ!*8xn!%@3}AGI&{kv&QC3cbCcBA`nHr09G= zXh|-%c5@Kg@>t!|DDuqeCtcbQVSXrgLEHdw7O~#R|0|3&%_Rm0 z#lKj+9ycU9a#--Rh(*X>7q8yuM&A9n`=a+yt2_IW{rOXlC!_RVW--H)cpl?;aYdvh z;)QK%`QLv9l%1@Pr%K_oV*VKCtSwLS;CCd!`8%-k(&4uUcO*xqX}DcZG{AdE-EX!BO|Ju4jK*tDZ;Ccy;PP-S++X$zrM8b?Mp zBQM)^eo4eO**q9KDUo#Ab{g0k_;zb1AT7aJ%+Q~pJgX2a;R_W1B#*ol{<)YDp-n@c zo2+zj$JIreT7L+zZ*9X<<~1yYpLt%(Wn_b7%-JN!&nM~GVTS08kl1x*fv%#uen2(& z7B2eu z)IalDQGea*Wa*p0jq__j=J&Fyv?J>3jR*A;YcAAE(A0H%Xq^SY#ghy(mwzMxUw7L4RBx8yvW!V;O zj79LeK{yKpJoFr_B@|pgo!DL%+1xci9qs#Y7Q0PW|A}-FWhQ4Y2t$=LC3v-smawPY zOh549XsJ|D&SZ5Z8BZOJOINIX*gh3xOn4>KGd}uP@{4fg9imW1%lhT0y{l-61=MXV zH{pSX#bD2p^ue8Z4J|*a!9R_Mm%xC4Mv%f!#Kt#6_o_ez{rKi3aM^UiIRCqLg7%>E z2tUL-?)VDaHYb4q58;}F#yHszh&2YH zYwg(Sf2rD#B%KbpG$0!k04<+x)hK=g3kP|k4trTa;mFmO`WwEZC9ut+Dp{ag>00|% zISt6+^9?xr${$U7$CpCImM#gcOq&+KQleV?&|c6D(odkyG6#-d^C!3GO$biP#Lgh% z+ZXXetxO&e1!pfmRIqiC$=9JF?>2lHXm=Au3yP7`xGcTy+FDqd-nhq!9cxnGXB1u@e?)g_8t&Y5>pchd%JPWyMs+lH~M!TZ$?(n{-3Wy z2Nrjzc$!Z2`|ZKXCb(nQ{WbB`H+>!f9s{*k7FT}A)F8?1#q)^{0lC_-AHeSP-nyc9 z>lxK^y4P2IBGG^dY?%ekVd81fcK1!lIL@2-Z+aRr8}C{3N?C}8crNojELSN~lEIU| ziV7vd857i(aqpvYJrcuF8~Z_V>NUXbT`3DAg=i85l#N0L`~Bj7ce|t=sC-OrV);;+Cz`FmZ~f=jO+)y9-Tud{k&AzhGabR}f>){0*k>I&J^} literal 0 HcmV?d00001 diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 4438ab0da..b42f03a83 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -6757,6 +6757,21 @@ } ] }, + { + "name": "ImageRotate", + "description": "Rotate image by input angle in degrees (-359 to 359) ", + "returnType": "void", + "params": [ + { + "type": "Image *", + "name": "image" + }, + { + "type": "int", + "name": "degrees" + } + ] + }, { "name": "ImageRotateCW", "description": "Rotate image clockwise 90deg", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 79b3a9283..ed11ac08f 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -5266,6 +5266,15 @@ return { {type = "Image *", name = "image"} } }, + { + name = "ImageRotate", + description = "Rotate image by input angle in degrees (-359 to 359) ", + returnType = "void", + params = { + {type = "Image *", name = "image"}, + {type = "int", name = "degrees"} + } + }, { name = "ImageRotateCW", description = "Rotate image clockwise 90deg", diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index c53cb77ec..0d0ccccf1 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -964,7 +964,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 517 +Functions found: 518 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2606,93 +2606,99 @@ Function 272: ImageFlipHorizontal() (1 input parameters) Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 273: ImageRotateCW() (1 input parameters) +Function 273: ImageRotate() (2 input parameters) + Name: ImageRotate + Return type: void + Description: Rotate image by input angle in degrees (-359 to 359) + Param[1]: image (type: Image *) + Param[2]: degrees (type: int) +Function 274: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 274: ImageRotateCCW() (1 input parameters) +Function 275: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 275: ImageColorTint() (2 input parameters) +Function 276: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 276: ImageColorInvert() (1 input parameters) +Function 277: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 277: ImageColorGrayscale() (1 input parameters) +Function 278: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 278: ImageColorContrast() (2 input parameters) +Function 279: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 279: ImageColorBrightness() (2 input parameters) +Function 280: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 280: ImageColorReplace() (3 input parameters) +Function 281: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 281: LoadImageColors() (1 input parameters) +Function 282: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 282: LoadImagePalette() (3 input parameters) +Function 283: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 283: UnloadImageColors() (1 input parameters) +Function 284: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 284: UnloadImagePalette() (1 input parameters) +Function 285: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 285: GetImageAlphaBorder() (2 input parameters) +Function 286: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 286: GetImageColor() (3 input parameters) +Function 287: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 287: ImageClearBackground() (2 input parameters) +Function 288: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 288: ImageDrawPixel() (4 input parameters) +Function 289: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2700,14 +2706,14 @@ Function 288: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 289: ImageDrawPixelV() (3 input parameters) +Function 290: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 290: ImageDrawLine() (6 input parameters) +Function 291: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2717,7 +2723,7 @@ Function 290: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 291: ImageDrawLineV() (4 input parameters) +Function 292: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2725,7 +2731,7 @@ Function 291: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 292: ImageDrawCircle() (5 input parameters) +Function 293: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2734,7 +2740,7 @@ Function 292: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 293: ImageDrawCircleV() (4 input parameters) +Function 294: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2742,7 +2748,7 @@ Function 293: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 294: ImageDrawCircleLines() (5 input parameters) +Function 295: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2751,7 +2757,7 @@ Function 294: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 295: ImageDrawCircleLinesV() (4 input parameters) +Function 296: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2759,7 +2765,7 @@ Function 295: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 296: ImageDrawRectangle() (6 input parameters) +Function 297: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2769,7 +2775,7 @@ Function 296: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 297: ImageDrawRectangleV() (4 input parameters) +Function 298: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2777,14 +2783,14 @@ Function 297: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 298: ImageDrawRectangleRec() (3 input parameters) +Function 299: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 299: ImageDrawRectangleLines() (4 input parameters) +Function 300: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2792,7 +2798,7 @@ Function 299: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 300: ImageDraw() (5 input parameters) +Function 301: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2801,7 +2807,7 @@ Function 300: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 301: ImageDrawText() (6 input parameters) +Function 302: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2811,7 +2817,7 @@ Function 301: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 302: ImageDrawTextEx() (7 input parameters) +Function 303: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2822,79 +2828,79 @@ Function 302: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 303: LoadTexture() (1 input parameters) +Function 304: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 304: LoadTextureFromImage() (1 input parameters) +Function 305: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 305: LoadTextureCubemap() (2 input parameters) +Function 306: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 306: LoadRenderTexture() (2 input parameters) +Function 307: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 307: IsTextureReady() (1 input parameters) +Function 308: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 308: UnloadTexture() (1 input parameters) +Function 309: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 309: IsRenderTextureReady() (1 input parameters) +Function 310: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 310: UnloadRenderTexture() (1 input parameters) +Function 311: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 311: UpdateTexture() (2 input parameters) +Function 312: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 312: UpdateTextureRec() (3 input parameters) +Function 313: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 313: GenTextureMipmaps() (1 input parameters) +Function 314: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 314: SetTextureFilter() (2 input parameters) +Function 315: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 315: SetTextureWrap() (2 input parameters) +Function 316: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 316: DrawTexture() (4 input parameters) +Function 317: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2902,14 +2908,14 @@ Function 316: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 317: DrawTextureV() (3 input parameters) +Function 318: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 318: DrawTextureEx() (5 input parameters) +Function 319: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2918,7 +2924,7 @@ Function 318: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 319: DrawTextureRec() (4 input parameters) +Function 320: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2926,7 +2932,7 @@ Function 319: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 320: DrawTexturePro() (6 input parameters) +Function 321: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2936,7 +2942,7 @@ Function 320: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 321: DrawTextureNPatch() (6 input parameters) +Function 322: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2946,106 +2952,106 @@ Function 321: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 322: Fade() (2 input parameters) +Function 323: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 323: ColorToInt() (1 input parameters) +Function 324: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 324: ColorNormalize() (1 input parameters) +Function 325: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 325: ColorFromNormalized() (1 input parameters) +Function 326: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 326: ColorToHSV() (1 input parameters) +Function 327: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 327: ColorFromHSV() (3 input parameters) +Function 328: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 328: ColorTint() (2 input parameters) +Function 329: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 329: ColorBrightness() (2 input parameters) +Function 330: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 330: ColorContrast() (2 input parameters) +Function 331: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 331: ColorAlpha() (2 input parameters) +Function 332: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 332: ColorAlphaBlend() (3 input parameters) +Function 333: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 333: GetColor() (1 input parameters) +Function 334: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 334: GetPixelColor() (2 input parameters) +Function 335: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 335: SetPixelColor() (3 input parameters) +Function 336: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 336: GetPixelDataSize() (3 input parameters) +Function 337: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 337: GetFontDefault() (0 input parameters) +Function 338: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 338: LoadFont() (1 input parameters) +Function 339: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 339: LoadFontEx() (4 input parameters) +Function 340: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set @@ -3053,14 +3059,14 @@ Function 339: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: fontChars (type: int *) Param[4]: glyphCount (type: int) -Function 340: LoadFontFromImage() (3 input parameters) +Function 341: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 341: LoadFontFromMemory() (6 input parameters) +Function 342: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3070,12 +3076,12 @@ Function 341: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: fontChars (type: int *) Param[6]: glyphCount (type: int) -Function 342: IsFontReady() (1 input parameters) +Function 343: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 343: LoadFontData() (6 input parameters) +Function 344: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3085,7 +3091,7 @@ Function 343: LoadFontData() (6 input parameters) Param[4]: fontChars (type: int *) Param[5]: glyphCount (type: int) Param[6]: type (type: int) -Function 344: GenImageFontAtlas() (6 input parameters) +Function 345: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3095,30 +3101,30 @@ Function 344: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 345: UnloadFontData() (2 input parameters) +Function 346: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: chars (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 346: UnloadFont() (1 input parameters) +Function 347: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 347: ExportFontAsCode() (2 input parameters) +Function 348: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 348: DrawFPS() (2 input parameters) +Function 349: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 349: DrawText() (5 input parameters) +Function 350: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3127,7 +3133,7 @@ Function 349: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 350: DrawTextEx() (6 input parameters) +Function 351: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3137,7 +3143,7 @@ Function 350: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 351: DrawTextPro() (8 input parameters) +Function 352: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3149,7 +3155,7 @@ Function 351: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 352: DrawTextCodepoint() (5 input parameters) +Function 353: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3158,7 +3164,7 @@ Function 352: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 353: DrawTextCodepoints() (7 input parameters) +Function 354: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3169,13 +3175,13 @@ Function 353: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 354: MeasureText() (2 input parameters) +Function 355: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 355: MeasureTextEx() (4 input parameters) +Function 356: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3183,180 +3189,180 @@ Function 355: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 356: GetGlyphIndex() (2 input parameters) +Function 357: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 357: GetGlyphInfo() (2 input parameters) +Function 358: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 358: GetGlyphAtlasRec() (2 input parameters) +Function 359: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 359: LoadUTF8() (2 input parameters) +Function 360: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 360: UnloadUTF8() (1 input parameters) +Function 361: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 361: LoadCodepoints() (2 input parameters) +Function 362: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 362: UnloadCodepoints() (1 input parameters) +Function 363: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 363: GetCodepointCount() (1 input parameters) +Function 364: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 364: GetCodepoint() (2 input parameters) +Function 365: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 365: GetCodepointNext() (2 input parameters) +Function 366: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 366: GetCodepointPrevious() (2 input parameters) +Function 367: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 367: CodepointToUTF8() (2 input parameters) +Function 368: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 368: TextCopy() (2 input parameters) +Function 369: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 369: TextIsEqual() (2 input parameters) +Function 370: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 370: TextLength() (1 input parameters) +Function 371: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 371: TextFormat() (2 input parameters) +Function 372: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 372: TextSubtext() (3 input parameters) +Function 373: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 373: TextReplace() (3 input parameters) +Function 374: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 374: TextInsert() (3 input parameters) +Function 375: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 375: TextJoin() (3 input parameters) +Function 376: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 376: TextSplit() (3 input parameters) +Function 377: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 377: TextAppend() (3 input parameters) +Function 378: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 378: TextFindIndex() (2 input parameters) +Function 379: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 379: TextToUpper() (1 input parameters) +Function 380: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 380: TextToLower() (1 input parameters) +Function 381: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 381: TextToPascal() (1 input parameters) +Function 382: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 382: TextToInteger() (1 input parameters) +Function 383: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 383: DrawLine3D() (3 input parameters) +Function 384: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 384: DrawPoint3D() (2 input parameters) +Function 385: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 385: DrawCircle3D() (5 input parameters) +Function 386: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3365,7 +3371,7 @@ Function 385: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 386: DrawTriangle3D() (4 input parameters) +Function 387: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3373,14 +3379,14 @@ Function 386: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 387: DrawTriangleStrip3D() (3 input parameters) +Function 388: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 388: DrawCube() (5 input parameters) +Function 389: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3389,14 +3395,14 @@ Function 388: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 389: DrawCubeV() (3 input parameters) +Function 390: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 390: DrawCubeWires() (5 input parameters) +Function 391: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3405,21 +3411,21 @@ Function 390: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 391: DrawCubeWiresV() (3 input parameters) +Function 392: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 392: DrawSphere() (3 input parameters) +Function 393: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 393: DrawSphereEx() (5 input parameters) +Function 394: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3428,7 +3434,7 @@ Function 393: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 394: DrawSphereWires() (5 input parameters) +Function 395: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3437,7 +3443,7 @@ Function 394: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 395: DrawCylinder() (6 input parameters) +Function 396: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3447,7 +3453,7 @@ Function 395: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 396: DrawCylinderEx() (6 input parameters) +Function 397: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3457,7 +3463,7 @@ Function 396: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 397: DrawCylinderWires() (6 input parameters) +Function 398: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3467,7 +3473,7 @@ Function 397: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 398: DrawCylinderWiresEx() (6 input parameters) +Function 399: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3477,7 +3483,7 @@ Function 398: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 399: DrawCapsule() (6 input parameters) +Function 400: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3487,7 +3493,7 @@ Function 399: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 400: DrawCapsuleWires() (6 input parameters) +Function 401: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3497,51 +3503,51 @@ Function 400: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 401: DrawPlane() (3 input parameters) +Function 402: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 402: DrawRay() (2 input parameters) +Function 403: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 403: DrawGrid() (2 input parameters) +Function 404: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 404: LoadModel() (1 input parameters) +Function 405: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 405: LoadModelFromMesh() (1 input parameters) +Function 406: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 406: IsModelReady() (1 input parameters) +Function 407: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 407: UnloadModel() (1 input parameters) +Function 408: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 408: GetModelBoundingBox() (1 input parameters) +Function 409: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 409: DrawModel() (4 input parameters) +Function 410: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3549,7 +3555,7 @@ Function 409: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 410: DrawModelEx() (6 input parameters) +Function 411: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3559,7 +3565,7 @@ Function 410: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 411: DrawModelWires() (4 input parameters) +Function 412: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3567,7 +3573,7 @@ Function 411: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 412: DrawModelWiresEx() (6 input parameters) +Function 413: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3577,13 +3583,13 @@ Function 412: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 413: DrawBoundingBox() (2 input parameters) +Function 414: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 414: DrawBillboard() (5 input parameters) +Function 415: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3592,7 +3598,7 @@ Function 414: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 415: DrawBillboardRec() (6 input parameters) +Function 416: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3602,7 +3608,7 @@ Function 415: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 416: DrawBillboardPro() (9 input parameters) +Function 417: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3615,13 +3621,13 @@ Function 416: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 417: UploadMesh() (2 input parameters) +Function 418: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 418: UpdateMeshBuffer() (5 input parameters) +Function 419: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3630,19 +3636,19 @@ Function 418: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 419: UnloadMesh() (1 input parameters) +Function 420: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 420: DrawMesh() (3 input parameters) +Function 421: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 421: DrawMeshInstanced() (4 input parameters) +Function 422: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3650,29 +3656,29 @@ Function 421: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 422: ExportMesh() (2 input parameters) +Function 423: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 423: GetMeshBoundingBox() (1 input parameters) +Function 424: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 424: GenMeshTangents() (1 input parameters) +Function 425: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 425: GenMeshPoly() (2 input parameters) +Function 426: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 426: GenMeshPlane() (4 input parameters) +Function 427: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3680,42 +3686,42 @@ Function 426: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 427: GenMeshCube() (3 input parameters) +Function 428: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 428: GenMeshSphere() (3 input parameters) +Function 429: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 429: GenMeshHemiSphere() (3 input parameters) +Function 430: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 430: GenMeshCylinder() (3 input parameters) +Function 431: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 431: GenMeshCone() (3 input parameters) +Function 432: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 432: GenMeshTorus() (4 input parameters) +Function 433: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3723,7 +3729,7 @@ Function 432: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 433: GenMeshKnot() (4 input parameters) +Function 434: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3731,84 +3737,84 @@ Function 433: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 434: GenMeshHeightmap() (2 input parameters) +Function 435: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 435: GenMeshCubicmap() (2 input parameters) +Function 436: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 436: LoadMaterials() (2 input parameters) +Function 437: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 437: LoadMaterialDefault() (0 input parameters) +Function 438: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 438: IsMaterialReady() (1 input parameters) +Function 439: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 439: UnloadMaterial() (1 input parameters) +Function 440: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 440: SetMaterialTexture() (3 input parameters) +Function 441: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 441: SetModelMeshMaterial() (3 input parameters) +Function 442: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 442: LoadModelAnimations() (2 input parameters) +Function 443: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: unsigned int *) -Function 443: UpdateModelAnimation() (3 input parameters) +Function 444: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 444: UnloadModelAnimation() (1 input parameters) +Function 445: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 445: UnloadModelAnimations() (2 input parameters) +Function 446: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: count (type: unsigned int) -Function 446: IsModelAnimationValid() (2 input parameters) +Function 447: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 447: CheckCollisionSpheres() (4 input parameters) +Function 448: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3816,40 +3822,40 @@ Function 447: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 448: CheckCollisionBoxes() (2 input parameters) +Function 449: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 449: CheckCollisionBoxSphere() (3 input parameters) +Function 450: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 450: GetRayCollisionSphere() (3 input parameters) +Function 451: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 451: GetRayCollisionBox() (2 input parameters) +Function 452: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 452: GetRayCollisionMesh() (3 input parameters) +Function 453: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 453: GetRayCollisionTriangle() (4 input parameters) +Function 454: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3857,7 +3863,7 @@ Function 453: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 454: GetRayCollisionQuad() (5 input parameters) +Function 455: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3866,143 +3872,143 @@ Function 454: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 455: InitAudioDevice() (0 input parameters) +Function 456: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 456: CloseAudioDevice() (0 input parameters) +Function 457: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 457: IsAudioDeviceReady() (0 input parameters) +Function 458: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 458: SetMasterVolume() (1 input parameters) +Function 459: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 459: LoadWave() (1 input parameters) +Function 460: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 460: LoadWaveFromMemory() (3 input parameters) +Function 461: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 461: IsWaveReady() (1 input parameters) +Function 462: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 462: LoadSound() (1 input parameters) +Function 463: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 463: LoadSoundFromWave() (1 input parameters) +Function 464: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 464: IsSoundReady() (1 input parameters) +Function 465: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 465: UpdateSound() (3 input parameters) +Function 466: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 466: UnloadWave() (1 input parameters) +Function 467: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 467: UnloadSound() (1 input parameters) +Function 468: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 468: ExportWave() (2 input parameters) +Function 469: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 469: ExportWaveAsCode() (2 input parameters) +Function 470: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 470: PlaySound() (1 input parameters) +Function 471: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 471: StopSound() (1 input parameters) +Function 472: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 472: PauseSound() (1 input parameters) +Function 473: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 473: ResumeSound() (1 input parameters) +Function 474: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 474: IsSoundPlaying() (1 input parameters) +Function 475: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 475: SetSoundVolume() (2 input parameters) +Function 476: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 476: SetSoundPitch() (2 input parameters) +Function 477: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 477: SetSoundPan() (2 input parameters) +Function 478: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 478: WaveCopy() (1 input parameters) +Function 479: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 479: WaveCrop() (3 input parameters) +Function 480: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 480: WaveFormat() (4 input parameters) +Function 481: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4010,203 +4016,203 @@ Function 480: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 481: LoadWaveSamples() (1 input parameters) +Function 482: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 482: UnloadWaveSamples() (1 input parameters) +Function 483: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 483: LoadMusicStream() (1 input parameters) +Function 484: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 484: LoadMusicStreamFromMemory() (3 input parameters) +Function 485: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 485: IsMusicReady() (1 input parameters) +Function 486: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 486: UnloadMusicStream() (1 input parameters) +Function 487: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 487: PlayMusicStream() (1 input parameters) +Function 488: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 488: IsMusicStreamPlaying() (1 input parameters) +Function 489: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 489: UpdateMusicStream() (1 input parameters) +Function 490: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 490: StopMusicStream() (1 input parameters) +Function 491: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 491: PauseMusicStream() (1 input parameters) +Function 492: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 492: ResumeMusicStream() (1 input parameters) +Function 493: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 493: SeekMusicStream() (2 input parameters) +Function 494: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 494: SetMusicVolume() (2 input parameters) +Function 495: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 495: SetMusicPitch() (2 input parameters) +Function 496: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 496: SetMusicPan() (2 input parameters) +Function 497: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 497: GetMusicTimeLength() (1 input parameters) +Function 498: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 498: GetMusicTimePlayed() (1 input parameters) +Function 499: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 499: LoadAudioStream() (3 input parameters) +Function 500: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 500: IsAudioStreamReady() (1 input parameters) +Function 501: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 501: UnloadAudioStream() (1 input parameters) +Function 502: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 502: UpdateAudioStream() (3 input parameters) +Function 503: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 503: IsAudioStreamProcessed() (1 input parameters) +Function 504: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 504: PlayAudioStream() (1 input parameters) +Function 505: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 505: PauseAudioStream() (1 input parameters) +Function 506: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 506: ResumeAudioStream() (1 input parameters) +Function 507: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 507: IsAudioStreamPlaying() (1 input parameters) +Function 508: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 508: StopAudioStream() (1 input parameters) +Function 509: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 509: SetAudioStreamVolume() (2 input parameters) +Function 510: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 510: SetAudioStreamPitch() (2 input parameters) +Function 511: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 511: SetAudioStreamPan() (2 input parameters) +Function 512: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 512: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 513: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 513: SetAudioStreamCallback() (2 input parameters) +Function 514: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 514: AttachAudioStreamProcessor() (2 input parameters) +Function 515: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 515: DetachAudioStreamProcessor() (2 input parameters) +Function 516: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 516: AttachAudioMixedProcessor() (1 input parameters) +Function 517: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline Param[1]: processor (type: AudioCallback) -Function 517: DetachAudioMixedProcessor() (1 input parameters) +Function 518: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 85b3e3e4c..f450069cc 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -656,7 +656,7 @@ - + @@ -1685,6 +1685,10 @@ + + + + diff --git a/projects/Geany/raylib.c.tags b/projects/Geany/raylib.c.tags index 4cc6d4605..79a192a14 100644 --- a/projects/Geany/raylib.c.tags +++ b/projects/Geany/raylib.c.tags @@ -202,6 +202,7 @@ ImageDrawText|void|(Image *dst, Vector2 position, const char *text, int fontSize ImageDrawTextEx|void|(Image *dst, Vector2 position, Font font, const char *text, float fontSize, float spacing, Color color);| ImageFlipVertical|void|(Image *image);| ImageFlipHorizontal|void|(Image *image);| +ImageRotate|void|(Image *image, int degrees);| ImageRotateCW|void|(Image *image);| ImageRotateCCW|void|(Image *image);| ImageColorTint|void|(Image *image, Color color);| diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index 7e173a936..883183cef 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -1570,6 +1570,12 @@ + + + + + + diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index f20f065e3..c32cdb26e 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -346,6 +346,7 @@ RLAPI void ImageMipmaps(Image *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 RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally +RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint diff --git a/src/raylib.h b/src/raylib.h index 703e70c21..b2b825831 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1268,6 +1268,7 @@ RLAPI void ImageMipmaps(Image *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 RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally +RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint diff --git a/src/rtextures.c b/src/rtextures.c index eeeebba82..b5e2a7f7a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2118,6 +2118,65 @@ void ImageFlipHorizontal(Image *image) } } +// Rotate image in degrees +void ImageRotate(Image *image, int degrees) +{ + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + + if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level"); + if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats"); + else + { + float rad = degrees * PI / 180.0f; + float sinRadius = sin(rad); + float cosRadius = cos(rad); + + int width = abs(image->width * cosRadius) + abs(image->height * sinRadius); + int height = abs(image->height * cosRadius) + abs(image->width * sinRadius); + + int bytesPerPixel = GetPixelDataSize(1, 1, image->format); + unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width * height, bytesPerPixel); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + float oldX = ((x - width / 2.0f) * cosRadius + (y - height / 2.0f) * sinRadius) + image->width / 2.0f; + float oldY = ((y - height / 2.0f) * cosRadius - (x - width / 2.0f) * sinRadius) + image->height / 2.0f; + + if (oldX >= 0 && oldX < image->width && oldY >= 0 && oldY < image->height) + { + int x1 = (int)floor(oldX); + int y1 = (int)floor(oldY); + int x2 = MIN(x1 + 1, image->width - 1); + int y2 = MIN(y1 + 1, image->height - 1); + + float px = oldX - x1; + float py = oldY - y1; + + for (int i = 0; i < bytesPerPixel; i++) + { + float f1 = ((unsigned char *)image->data)[(y1 * image->width + x1) * bytesPerPixel + i]; + float f2 = ((unsigned char *)image->data)[(y1 * image->width + x2) * bytesPerPixel + i]; + float f3 = ((unsigned char *)image->data)[(y2 * image->width + x1) * bytesPerPixel + i]; + float f4 = ((unsigned char *)image->data)[(y2 * image->width + x2) * bytesPerPixel + i]; + + float val = f1 * (1 - px) * (1 - py) + f2 * px * (1 - py) + f3 * (1 - px) * py + f4 * px * py; + + rotatedData[(y * width + x) * bytesPerPixel + i] = (unsigned char)val; + } + } + } + } + + RL_FREE(image->data); + image->data = rotatedData; + image->width = width; + image->height = height; + } +} + // Rotate image clockwise 90deg void ImageRotateCW(Image *image) { From 144ae120ab0a0c2ebecd83a97c312740b5b46ce4 Mon Sep 17 00:00:00 2001 From: LuraMoth <85266594+Luramoth@users.noreply.github.com> Date: Thu, 25 May 2023 00:15:53 -0700 Subject: [PATCH 0455/1710] Add new file formats to FAQ (#3079) I noticed some file formats from the new release of raylib were missing so I decided to help out and update it! --- FAQ.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FAQ.md b/FAQ.md index b933bf615..2020162c7 100644 --- a/FAQ.md +++ b/FAQ.md @@ -119,8 +119,8 @@ raylib can load data from multiple standard file formats: - Image/Textures: PNG, BMP, TGA, JPG, GIF, QOI, PSD, DDS, HDR, KTX, ASTC, PKM, PVR - Fonts: FNT (sprite font), TTF, OTF - - Models/Meshes: OBJ, IQM, GLTF, VOX - - Audio: WAV, OGG, MP3, FLAC, XM, MOD + - Models/Meshes: OBJ, IQM, GLTF, VOX, M3D + - Audio: WAV, OGG, MP3, FLAC, XM, MOD, QOA ### Does raylib support the Vulkan API? From 5ef50ae139305c487e4cff0f49f3ced88345e445 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 26 May 2023 14:01:19 +0200 Subject: [PATCH 0456/1710] REVIEWED: `ImageRotate()` formatting --- src/rtextures.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index b5e2a7f7a..a1987ef51 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2128,27 +2128,27 @@ void ImageRotate(Image *image, int degrees) if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats"); else { - float rad = degrees * PI / 180.0f; + float rad = degrees*PI/180.0f; float sinRadius = sin(rad); float cosRadius = cos(rad); - int width = abs(image->width * cosRadius) + abs(image->height * sinRadius); - int height = abs(image->height * cosRadius) + abs(image->width * sinRadius); + int width = fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius); + int height = fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius); int bytesPerPixel = GetPixelDataSize(1, 1, image->format); - unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width * height, bytesPerPixel); + unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width*height, bytesPerPixel); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - float oldX = ((x - width / 2.0f) * cosRadius + (y - height / 2.0f) * sinRadius) + image->width / 2.0f; - float oldY = ((y - height / 2.0f) * cosRadius - (x - width / 2.0f) * sinRadius) + image->height / 2.0f; + float oldX = ((x - width/2.0f)*cosRadius + (y - height/2.0f)*sinRadius) + image->width/2.0f; + float oldY = ((y - height/2.0f)*cosRadius - (x - width/2.0f)*sinRadius) + image->height/2.0f; - if (oldX >= 0 && oldX < image->width && oldY >= 0 && oldY < image->height) + if ((oldX >= 0) && (oldX < image->width) && (oldY >= 0) && (oldY < image->height)) { - int x1 = (int)floor(oldX); - int y1 = (int)floor(oldY); + int x1 = (int)floorf(oldX); + int y1 = (int)floorf(oldY); int x2 = MIN(x1 + 1, image->width - 1); int y2 = MIN(y1 + 1, image->height - 1); @@ -2157,14 +2157,14 @@ void ImageRotate(Image *image, int degrees) for (int i = 0; i < bytesPerPixel; i++) { - float f1 = ((unsigned char *)image->data)[(y1 * image->width + x1) * bytesPerPixel + i]; - float f2 = ((unsigned char *)image->data)[(y1 * image->width + x2) * bytesPerPixel + i]; - float f3 = ((unsigned char *)image->data)[(y2 * image->width + x1) * bytesPerPixel + i]; - float f4 = ((unsigned char *)image->data)[(y2 * image->width + x2) * bytesPerPixel + i]; + float f1 = ((unsigned char *)image->data)[(y1*image->width + x1)*bytesPerPixel + i]; + float f2 = ((unsigned char *)image->data)[(y1*image->width + x2)*bytesPerPixel + i]; + float f3 = ((unsigned char *)image->data)[(y2*image->width + x1)*bytesPerPixel + i]; + float f4 = ((unsigned char *)image->data)[(y2*image->width + x2)*bytesPerPixel + i]; - float val = f1 * (1 - px) * (1 - py) + f2 * px * (1 - py) + f3 * (1 - px) * py + f4 * px * py; + float val = f1*(1 - px)*(1 - py) + f2*px*(1 - py) + f3*(1 - px)*py + f4*px*py; - rotatedData[(y * width + x) * bytesPerPixel + i] = (unsigned char)val; + rotatedData[(y*width + x)*bytesPerPixel + i] = (unsigned char)val; } } } From 20860e2ba05ee818d57b1fd2ec4a7f094a0f3783 Mon Sep 17 00:00:00 2001 From: Jason Liang Date: Sat, 27 May 2023 00:27:44 -0700 Subject: [PATCH 0457/1710] Fix a link in the FAQ (#3082) --- FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index 2020162c7..274236d47 100644 --- a/FAQ.md +++ b/FAQ.md @@ -14,7 +14,7 @@ - [What does raylib provide that other engines or libraries don't?](#what-does-raylib-provide-that-other-engines-or-libraries-dont) - [How does raylib compare to Unity/Unreal/Godot?](#how-does-raylib-compare-to-unityunrealgodot) - [What development tools are required for raylib?](#what-development-tools-are-required-for-raylib) -- [Which are raylib external dependencies?](#which-are-raylib-external-dependencies) +- [What are raylib's external dependencies?](#what-are-raylibs-external-dependencies) - [Can I use raylib with other technologies or libraries?](#can-i-use-raylib-with-other-technologies-or-libraries) - [What file formats are supported by raylib?](#what-file-formats-are-supported-by-raylib) - [Does raylib support the Vulkan API?](#does-raylib-support-the-vulkan-api) From aad51d47048f9eb5c13bbbd65dc5cd4d37aa68e5 Mon Sep 17 00:00:00 2001 From: Nikita K Date: Sat, 27 May 2023 14:11:33 +0300 Subject: [PATCH 0458/1710] BINDINGS.md: Janet bindings supported version update (#3083) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index a10e5860f..a60979fc4 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -31,6 +31,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | | raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | +| jaylib | 4.5 | [Janet](https://janet-lang.org/) | MIT | https://github.com/janet-lang/jaylib | | jaylib | 4.2 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | @@ -133,7 +134,6 @@ These are older raylib bindings that are more than 2 versions old or have not be | raylib-jai | 3.1-dev | [Jai](https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md) | https://github.com/kujukuju/raylib-jai | | ray.zig | 2.5 | [Zig](https://ziglang.org/) | https://github.com/BitPuffin/zig-raylib-experiments | | raylib-Ada | 3.0 | [Ada](https://www.adacore.com/about-ada) | https://github.com/mimo/raylib-Ada | -| jaylib | 3.0 | [Janet](https://janet-lang.org/) | https://github.com/janet-lang/jaylib | | raykit | ? | [Kit](https://www.kitlang.org/) | https://github.com/Gamerfiend/raykit | | ray.mod | 3.0 | [BlitzMax](https://blitzmax.org/) | https://github.com/bmx-ng/ray.mod | | raylib-mosaic | 3.0 | [Mosaic](https://github.com/sal55/langs/tree/master/Mosaic) | https://github.com/pluckyporcupine/raylib-mosaic | From 4a371a51975cf7e06bbf1d94284493f77a820113 Mon Sep 17 00:00:00 2001 From: RayIT Date: Sun, 28 May 2023 11:33:14 +0200 Subject: [PATCH 0459/1710] Fixed compile on OpenBSD (#3085) --- cmake/LibraryConfigurations.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index ffb1a047d..a54261d27 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -25,6 +25,18 @@ if (${PLATFORM} MATCHES "Desktop") add_definitions(-D_CRT_SECURE_NO_WARNINGS) find_package(OpenGL QUIET) set(LIBS_PRIVATE ${OPENGL_LIBRARIES} winmm) + elseif (UNIX) + find_library(pthread NAMES pthread) + find_package(OpenGL QUIET) + if ("${OPENGL_LIBRARIES}" STREQUAL "") + set(OPENGL_LIBRARIES "GL") + endif () + + if ("${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD") + find_library(OSS_LIBRARY ossaudio) + endif () + + set(LIBS_PRIVATE m pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) else () find_library(pthread NAMES pthread) find_package(OpenGL QUIET) From 15cbf313bb3f490a64c25b0d54a5635742b6de53 Mon Sep 17 00:00:00 2001 From: RayIT Date: Sun, 28 May 2023 14:49:33 +0200 Subject: [PATCH 0460/1710] Enhanced cmake part for OpenBSD (#3086) * Fixed compile on OpenBSD * Changed to not use seperate UNIX for cmake --- cmake/LibraryConfigurations.cmake | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index a54261d27..e9eee630a 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -43,14 +43,15 @@ if (${PLATFORM} MATCHES "Desktop") if ("${OPENGL_LIBRARIES}" STREQUAL "") set(OPENGL_LIBRARIES "GL") endif () + + set(LIBS_PRIVATE m atomic pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) if ("${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD") find_library(OSS_LIBRARY ossaudio) + set(LIBS_PRIVATE m pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) endif () - set(LIBS_PRIVATE m atomic pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) - - if (USE_AUDIO) + if (NOT "${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD" AND USE_AUDIO) set(LIBS_PRIVATE ${LIBS_PRIVATE} dl) endif () endif () From 924bb7226ba1aa5228479fab5f7e9b0f8b0837d1 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 May 2023 21:12:03 +0200 Subject: [PATCH 0461/1710] UPDATED: `sdefl` and `sinfl` DEFLATE compression libraries --- src/external/sdefl.h | 65 +++++++++------ src/external/sinfl.h | 187 ++++++++++++++++++++++++------------------- 2 files changed, 144 insertions(+), 108 deletions(-) diff --git a/src/external/sdefl.h b/src/external/sdefl.h index 5db76763a..56539a56e 100644 --- a/src/external/sdefl.h +++ b/src/external/sdefl.h @@ -38,10 +38,10 @@ this file implementation in *one* C or C++ file to prevent collisions. | zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | | zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | | zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | -| sdefl 1.0 -0 | 127 MB/s | 371 MB/s | 40004116 | 39.88 | -| sdefl 1.0 -1 | 111 MB/s | 398 MB/s | 38940674 | 38.82 | -| sdefl 1.0 -5 | 45 MB/s | 420 MB/s | 36577183 | 36.46 | -| sdefl 1.0 -7 | 38 MB/s | 423 MB/s | 36523781 | 36.41 | +| sdefl 1.0 -0 | 127 MB/s | 355 MB/s | 40004116 | 39.88 | +| sdefl 1.0 -1 | 111 MB/s | 413 MB/s | 38940674 | 38.82 | +| sdefl 1.0 -5 | 45 MB/s | 436 MB/s | 36577183 | 36.46 | +| sdefl 1.0 -7 | 38 MB/s | 432 MB/s | 36523781 | 36.41 | | libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | | libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | | libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | @@ -50,20 +50,20 @@ this file implementation in *one* C or C++ file to prevent collisions. ### Compression Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): -| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | -| :------ | ---------: | -----------------: | ---------: | ----------: | -| dickens | 10.192.446 | 4,260,187| 3,845,261| 3,833,657 | -| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | -| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | -| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | -| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | -| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | -| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | -| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | -| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | -| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | -| xml | 5.345.280 | 886,620| 674,009 | 662,141 | -| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | +| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | +| --------| -----------| -------------| ---------- | ------------| +| dickens | 10.192.446 | 4,260,187 | 3,845,261 | 3,833,657 | +| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | +| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | +| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | +| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | +| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | +| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | +| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | +| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | +| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | +| xml | 5.345.280 | 886,620 | 674,009 | 662,141 | +| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | ## License ``` @@ -462,8 +462,12 @@ sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 }; + assert(len <= 258); + assert(dist <= 32768); cod->ls = lslot[len]; cod->lc = 257 + cod->ls; + assert(cod->lc <= 285); + cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; } @@ -501,7 +505,9 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, sdefl_precode(&symcnt, freqs, items, s->cod.len.lit, s->cod.len.off); sdefl_huff(lens, codes, freqs, SDEFL_PRE_MAX, SDEFL_PRE_CODES); for (item_cnt = SDEFL_PRE_MAX; item_cnt > 4; item_cnt--) { - if (lens[perm[item_cnt - 1]]) break; + if (lens[perm[item_cnt - 1]]){ + break; + } } /* block header */ sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ @@ -509,8 +515,9 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, sdefl_put(dst, s, symcnt.lit - 257, 5); sdefl_put(dst, s, symcnt.off - 1, 5); sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) + for (i = 0; i < item_cnt; ++i) { sdefl_put(dst, s, lens[perm[i]], 3); + } for (i = 0; i < symcnt.items; ++i) { unsigned sym = items[i] & 0x1F; sdefl_put(dst, s, (int)codes[sym], lens[sym]); @@ -521,12 +528,14 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, } /* block sequences */ for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) + if (s->seq[i].off >= 0) { for (j = 0; j < s->seq[i].len; ++j) { int c = in[s->seq[i].off + j]; sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); } - else sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } else { + sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } } sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); memset(&s->freq, 0, sizeof(s->freq)); @@ -579,12 +588,13 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, for (n = 0; n < SDEFL_HASH_SIZ; ++n) { s->tbl[n] = SDEFL_NIL; } - do {int blk_end = i + SDEFL_BLK_MAX < in_len ? i + SDEFL_BLK_MAX : in_len; + do {int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; while (i < blk_end) { struct sdefl_match m = {0}; - int max_match = ((in_len-i)>SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH:(in_len-i); + int left = blk_end - i; + int max_match = (left >= SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; - int run = 1, inc = 1, run_inc; + int run = 1, inc = 1, run_inc = 0; if (max_match > SDEFL_MIN_MATCH) { sdefl_fnd(&m, s, max_chain, max_match, in, i); } @@ -615,9 +625,11 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, unsigned h = sdefl_hash32(&in[i]); s->prv[i&SDEFL_WIN_MSK] = s->tbl[h]; s->tbl[h] = i, i += inc; + assert(i <= blk_end); } } else { i += run_inc; + assert(i <= blk_end); } } if (litlen) { @@ -627,8 +639,9 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, sdefl_flush(&q, s, blk_end == in_len, in); } while (i < in_len); - if (s->bitcnt) + if (s->bitcnt > 0) sdefl_put(&q, s, 0x00, 8 - s->bitcnt); + return (int)(q - out); } extern int diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 09f50d2bc..915da9d23 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -10,7 +10,7 @@ as needed to keep the implementation as concise as possible. - Dual license with either MIT or public domain - Small implementation - Deflate: 525 LoC - - Inflate: 320 LoC + - Inflate: 500 LoC - Webassembly: - Deflate ~3.7 KB (~2.2KB compressed) - Inflate ~3.6 KB (~2.2KB compressed) @@ -39,10 +39,10 @@ this file implementation in *one* C or C++ file to prevent collisions. | zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | | zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | | zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | -| sdefl 1.0 -0 | 127 MB/s | 371 MB/s | 40004116 | 39.88 | -| sdefl 1.0 -1 | 111 MB/s | 398 MB/s | 38940674 | 38.82 | -| sdefl 1.0 -5 | 45 MB/s | 420 MB/s | 36577183 | 36.46 | -| sdefl 1.0 -7 | 38 MB/s | 423 MB/s | 36523781 | 36.41 | +| sdefl 1.0 -0 | 127 MB/s | 355 MB/s | 40004116 | 39.88 | +| sdefl 1.0 -1 | 111 MB/s | 413 MB/s | 38940674 | 38.82 | +| sdefl 1.0 -5 | 45 MB/s | 436 MB/s | 36577183 | 36.46 | +| sdefl 1.0 -7 | 38 MB/s | 432 MB/s | 36523781 | 36.41 | | libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | | libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | | libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | @@ -51,20 +51,20 @@ this file implementation in *one* C or C++ file to prevent collisions. ### Compression Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): -| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | -| :------ | ---------: | -----------------: | ---------: | ----------: | -| dickens | 10.192.446 | 4,260,187| 3,845,261| 3,833,657 | -| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | -| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | -| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | -| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | -| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | -| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | -| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | -| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | -| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | -| xml | 5.345.280 | 886,620| 674,009 | 662,141 | -| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | +| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | +| --------| -----------| -------------| ---------- | ------------| +| dickens | 10.192.446 | 4,260,187 | 3,845,261 | 3,833,657 | +| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | +| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | +| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | +| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | +| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | +| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | +| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | +| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | +| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | +| xml | 5.345.280 | 886,620 | 674,009 | 662,141 | +| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | ## License ``` @@ -151,7 +151,7 @@ extern int zsinflate(void *out, int cap, const void *in, int size); #endif #ifndef SINFL_NO_SIMD -#if __x86_64__ || defined(_WIN32) || defined(_WIN64) +#if defined(__x86_64__) || defined(_WIN32) || defined(_WIN64) #include #define sinfl_char16 __m128i #define sinfl_char16_ld(p) _mm_loadu_si128((const __m128i *)(void*)(p)) @@ -183,6 +183,18 @@ sinfl_read64(const void *p) { memcpy(&n, p, 8); return n; } +static void +sinfl_copy64(unsigned char **dst, unsigned char **src) { + unsigned long long n; + memcpy(&n, *src, 8); + memcpy(*dst, &n, 8); + *dst += 8, *src += 8; +} +static unsigned char* +sinfl_write64(unsigned char *dst, unsigned long long w) { + memcpy(dst, &w, 8); + return dst + 8; +} #ifndef SINFL_NO_SIMD static unsigned char* sinfl_write128(unsigned char *dst, sinfl_char16 w) { @@ -195,25 +207,12 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) { sinfl_char16_str(*dst, n); *dst += 16, *src += 16; } -#else -static unsigned char* -sinfl_write64(unsigned char *dst, unsigned long long w) { - memcpy(dst, &w, 8); - return dst + 8; -} -static void -sinfl_copy64(unsigned char **dst, unsigned char **src) { - unsigned long long n; - memcpy(&n, *src, 8); - memcpy(*dst, &n, 8); - *dst += 8, *src += 8; -} #endif static void sinfl_refill(struct sinfl *s) { s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; s->bitptr += (63 - s->bitcnt) >> 3; - s->bitcnt |= 56; /* bitcount is in range [56,63] */ + s->bitcnt |= 56; /* bitcount in range [56,63] */ } static int sinfl_peek(struct sinfl *s, int cnt) { @@ -222,7 +221,7 @@ sinfl_peek(struct sinfl *s, int cnt) { return s->bitbuf & ((1ull << cnt) - 1); } static void -sinfl_consume(struct sinfl *s, int cnt) { +sinfl_eat(struct sinfl *s, int cnt) { assert(cnt <= s->bitcnt); s->bitbuf >>= cnt; s->bitcnt -= cnt; @@ -230,7 +229,7 @@ sinfl_consume(struct sinfl *s, int cnt) { static int sinfl__get(struct sinfl *s, int cnt) { int res = sinfl_peek(s, cnt); - sinfl_consume(s, cnt); + sinfl_eat(s, cnt); return res; } static int @@ -285,7 +284,7 @@ sinfl_build_subtbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits, while (1) { unsigned entry; int bit, stride, i; - /* start new subtable */ + /* start new sub-table */ if ((gen->word & ((1 << tbl_bits)-1)) != sub_prefix) { int used = 0; sub_prefix = gen->word & ((1 << tbl_bits)-1); @@ -299,7 +298,7 @@ sinfl_build_subtbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits, tbl_end = sub_start + (1 << sub_bits); tbl[sub_prefix] = (sub_start << 16) | 0x10 | (sub_bits & 0xf); } - /* fill subtable */ + /* fill sub-table */ entry = (*gen->sorted << 16) | ((gen->len - tbl_bits) & 0xf); gen->sorted++; i = sub_start + (gen->word >> tbl_bits); @@ -353,18 +352,17 @@ sinfl_build(unsigned *tbl, unsigned char *lens, int tbl_bits, int maxlen, } static int sinfl_decode(struct sinfl *s, const unsigned *tbl, int bit_len) { - sinfl_refill(s); - {int idx = sinfl_peek(s, bit_len); + int idx = sinfl_peek(s, bit_len); unsigned key = tbl[idx]; if (key & 0x10) { /* sub-table lookup */ int len = key & 0x0f; - sinfl_consume(s, bit_len); + sinfl_eat(s, bit_len); idx = sinfl_peek(s, len); key = tbl[((key >> 16) & 0xffff) + (unsigned)idx]; } - sinfl_consume(s, key & 0x0f); - return (key >> 16) & 0x0fff;} + sinfl_eat(s, key & 0x0f); + return (key >> 16) & 0x0fff; } static int sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) { @@ -402,11 +400,11 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) } break; case stored: { /* uncompressed block */ - int len; + int len, nlen; sinfl_refill(&s); sinfl__get(&s,s.bitcnt & 7); len = sinfl__get(&s,16); - //int nlen = sinfl__get(&s,16); // @raysan5: Unused variable? + nlen = sinfl__get(&s,16); in -= 2; s.bitcnt = 0; if (len > (e-in) || !len) @@ -430,40 +428,62 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) state = blk; } break; case dyn: { - /* dynamic huffman codes */ - int n, i; - unsigned hlens[SINFL_PRE_TBL_SIZE]; - unsigned char nlens[19] = {0}, lens[288+32]; + /* dynamic huffman codes */ + int n, i; + unsigned hlens[SINFL_PRE_TBL_SIZE]; + unsigned char nlens[19] = {0}, lens[288+32]; + sinfl_refill(&s); + {int nlit = 257 + sinfl__get(&s,5); + int ndist = 1 + sinfl__get(&s,5); + int nlen = 4 + sinfl__get(&s,4); + for (n = 0; n < nlen; n++) + nlens[order[n]] = (unsigned char)sinfl_get(&s,3); + sinfl_build(hlens, nlens, 7, 7, 19); + + /* decode code lengths */ + for (n = 0; n < nlit + ndist;) { sinfl_refill(&s); - {int nlit = 257 + sinfl__get(&s,5); - int ndist = 1 + sinfl__get(&s,5); - int nlen = 4 + sinfl__get(&s,4); - for (n = 0; n < nlen; n++) - nlens[order[n]] = (unsigned char)sinfl_get(&s,3); - sinfl_build(hlens, nlens, 7, 7, 19); - - /* decode code lengths */ - for (n = 0; n < nlit + ndist;) { - int sym = sinfl_decode(&s, hlens, 7); - switch (sym) {default: lens[n++] = (unsigned char)sym; break; - case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; - case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; - case 18: for (i=11+sinfl_get(&s,7);i;i--,n++) lens[n]=0; break;} - } - /* build lit/dist tables */ - sinfl_build(s.lits, lens, 10, 15, nlit); - sinfl_build(s.dsts, lens + nlit, 8, 15, ndist); - state = blk;} + int sym = sinfl_decode(&s, hlens, 7); + switch (sym) {default: lens[n++] = (unsigned char)sym; break; + case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; + case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; + case 18: for (i=11+sinfl_get(&s,7);i;i--,n++) lens[n]=0; break;} + } + /* build lit/dist tables */ + sinfl_build(s.lits, lens, 10, 15, nlit); + sinfl_build(s.dsts, lens + nlit, 8, 15, ndist); + state = blk;} } break; case blk: { /* decompress block */ - int sym = sinfl_decode(&s, s.lits, 10); - if (sym < 256) { - /* literal */ - *out++ = (unsigned char)sym; - } else if (sym > 256) {sym -= 257; /* match symbol */ + while (1) { sinfl_refill(&s); + int sym = sinfl_decode(&s, s.lits, 10); + if (sym < 256) { + /* literal */ + if (sinfl_unlikely(out >= oe)) { + return (int)(out-o); + } + *out++ = (unsigned char)sym; + sym = sinfl_decode(&s, s.lits, 10); + if (sym < 256) { + *out++ = (unsigned char)sym; + continue; + } + } + if (sinfl_unlikely(sym == 256)) { + /* end of block */ + if (last) return (int)(out-o); + state = hdr; + break; + } + /* match */ + if (sym >= 286) { + /* length codes 286 and 287 must not appear in compressed data */ + return (int)(out-o); + } + sym -= 257; {int len = sinfl__get(&s, lbits[sym]) + lbase[sym]; int dsym = sinfl_decode(&s, s.dsts, 8); int offs = sinfl__get(&s, dbits[dsym]) + dbase[dsym]; @@ -476,11 +496,17 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) #ifndef SINFL_NO_SIMD if (sinfl_likely(oe - out >= 16 * 3)) { if (offs >= 16) { - /* copy match */ + /* simd copy match */ sinfl_copy128(&dst, &src); sinfl_copy128(&dst, &src); do sinfl_copy128(&dst, &src); while (dst < out); + } else if (offs >= 8) { + /* word copy match */ + sinfl_copy64(&dst, &src); + sinfl_copy64(&dst, &src); + do sinfl_copy64(&dst, &src); + while (dst < out); } else if (offs == 1) { /* rle match copying */ sinfl_char16 w = sinfl_char16_char(src[0]); @@ -489,6 +515,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) do dst = sinfl_write128(dst, w); while (dst < out); } else { + /* byte copy match */ *dst++ = *src++; *dst++ = *src++; do *dst++ = *src++; @@ -498,7 +525,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) #else if (sinfl_likely(oe - out >= 3 * 8 - 3)) { if (offs >= 8) { - /* copy match */ + /* word copy match */ sinfl_copy64(&dst, &src); sinfl_copy64(&dst, &src); do sinfl_copy64(&dst, &src); @@ -513,6 +540,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) do dst = sinfl_write64(dst, w); while (dst < out); } else { + /* byte copy match */ *dst++ = *src++; *dst++ = *src++; do *dst++ = *src++; @@ -524,13 +552,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) *dst++ = *src++; *dst++ = *src++; do *dst++ = *src++; - while (dst < out);} - } - } else { - /* end of block */ - if (last) return (int)(out-o); - state = hdr; - break; + while (dst < out); + }} } } break;} } From 45c00ab9d43982a841c549ce4c9d3bbe60a660b1 Mon Sep 17 00:00:00 2001 From: yujiri8 Date: Tue, 30 May 2023 15:33:01 -0400 Subject: [PATCH 0462/1710] build.zig: Fix cross-compiling from Linux (#3090) --- src/build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build.zig b/src/build.zig index 48e93c326..59d8241e7 100644 --- a/src/build.zig +++ b/src/build.zig @@ -45,6 +45,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("dl"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("X11"); + raylib.addIncludePath("/usr/include"); raylib.defineCMacro("PLATFORM_DESKTOP", null); }, From a18667c2e9a3a815ab37a915e582e8906ff9cace Mon Sep 17 00:00:00 2001 From: A Billy <47233777+TheLastBilly@users.noreply.github.com> Date: Tue, 30 May 2023 15:34:08 -0400 Subject: [PATCH 0463/1710] cross compilation for PLATFORM_DRM (#3091) * added cross compilation options for DRM * fixed identation --- src/Makefile | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5d7086d8a..c6cd30e81 100644 --- a/src/Makefile +++ b/src/Makefile @@ -95,12 +95,11 @@ USE_EXTERNAL_GLFW ?= FALSE USE_WAYLAND_DISPLAY ?= FALSE # Use cross-compiler for PLATFORM_RPI -ifeq ($(PLATFORM),PLATFORM_RPI) - USE_RPI_CROSS_COMPILER ?= FALSE - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry - RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/arm-linux-gnueabihf/sysroot - endif +USE_RPI_CROSS_COMPILER ?= FALSE +ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) + RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry + RPI_TOOLCHAIN_NAME ?= arm-linux-gnueabihf + RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/$(RPI_TOOLCHAIN_NAME)/sysroot endif # Determine if the file has root access (only required to install raylib) @@ -273,8 +272,16 @@ ifeq ($(PLATFORM),PLATFORM_RPI) ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) # Define RPI cross-compiler #CC = armv6j-hardfloat-linux-gnueabi-gcc - CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc - AR = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-ar + CC = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-gcc + AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar + endif +endif +ifeq ($(PLATFORM),PLATFORM_DRM) + ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) + # Define RPI cross-compiler + #CC = armv6j-hardfloat-linux-gnueabi-gcc + CC = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-gcc + AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar endif endif ifeq ($(PLATFORM),PLATFORM_WEB) @@ -451,6 +458,10 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm + ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) + INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/usr/include + INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include + endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) NATIVE_APP_GLUE = $(ANDROID_NDK)/sources/android/native_app_glue @@ -499,6 +510,9 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif ifeq ($(PLATFORM),PLATFORM_DRM) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) + ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) + INCLUDE_PATHS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib + endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) LDFLAGS += -Wl,-soname,libraylib.$(API_VERSION).so -Wl,--exclude-libs,libatomic.a From e497603678823dc93f01dd9686819a92d780ad67 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 May 2023 18:36:33 +0200 Subject: [PATCH 0464/1710] ADDED: Experimental support for OpenGL ES 3.0 -WIP- Just added the required flags to request the OpenGL ES 3.0 context but it has not been tested... --- src/rcore.c | 16 ++++++++++++++-- src/rlgl.h | 19 ++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 7a31b5ed6..af65cebf0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -291,6 +291,7 @@ #if defined(PLATFORM_WEB) #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) + //#define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Required for: timespec, nanosleep(), select() - POSIX @@ -2451,7 +2452,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) { VrStereoConfig config = { 0 }; - if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() == RL_OPENGL_ES_20)) + if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() >= RL_OPENGL_ES_20)) { // Compute aspect ratio float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution; @@ -4172,6 +4173,17 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); #else glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); +#if defined(PLATFORM_DESKTOP) + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); +#else + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); #endif } @@ -4543,7 +4555,7 @@ static bool InitGraphicsDevice(int width, int height) const EGLint framebufferAttribs[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI? + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support #if defined(PLATFORM_DRM) EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android! #endif diff --git a/src/rlgl.h b/src/rlgl.h index 85a8ec368..f0bdf3cd4 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -26,6 +26,7 @@ * #define GRAPHICS_API_OPENGL_33 * #define GRAPHICS_API_OPENGL_43 * #define GRAPHICS_API_OPENGL_ES2 +* #define GRAPHICS_API_OPENGL_ES3 * Use selected OpenGL graphics backend, should be supported by platform * Those preprocessor defines are only used on rlgl module, if OpenGL version is * required by any other module, use rlGetVersion() to check it @@ -178,6 +179,11 @@ #define GRAPHICS_API_OPENGL_33 #endif +// OpenGL ES 3.0 uses OpenGL ES 2.0 functionality (and more) +#if defined(GRAPHICS_API_OPENGL_ES3) + #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 @@ -382,7 +388,8 @@ typedef enum { 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) - RL_OPENGL_ES_20 // OpenGL ES 2.0 (GLSL 100) + RL_OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100) + RL_OPENGL_ES_30 // OpenGL ES 3.0 (GLSL 300 es) } rlGlVersion; // Trace log level @@ -2389,15 +2396,17 @@ int rlGetVersion(void) #endif #if defined(GRAPHICS_API_OPENGL_21) glVersion = RL_OPENGL_21; +#elif defined(GRAPHICS_API_OPENGL_43) + glVersion = RL_OPENGL_43; #elif defined(GRAPHICS_API_OPENGL_33) glVersion = RL_OPENGL_33; #endif -#if defined(GRAPHICS_API_OPENGL_43) - glVersion = RL_OPENGL_43; -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES3) + glVersion = RL_OPENGL_ES_30; +#elif defined(GRAPHICS_API_OPENGL_ES2) glVersion = RL_OPENGL_ES_20; #endif + return glVersion; } From 2dec56e7b772ce4734f415e03715d0712ae68b8a Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Thu, 1 Jun 2023 08:47:52 +0100 Subject: [PATCH 0465/1710] Add error if raylib.h is included in a C++98 program (#3093) The color macros don't work properly in C++98, because they require aggregate initialzation, which is a C++11 feature. So, explicitly state how to fix this issue, instead of letting the compiler give a more vague error message like: main.cpp:8:23: error: expected '(' for function-style cast or type construction ClearBackground(BLACK); ^~~~~ /opt/homebrew/Cellar/raylib/4.5.0/include/raylib.h:179:35: note: expanded from macro 'BLACK' #define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black NOTE: Don't use this check with MSVC because by default, it reports 199711L regardless of any C++ version passed on command line Only passing `/Zc:__cplusplus` will make MSVC set this correctly see: https://learn.microsoft.com/en-us/cpp/build/reference/zc-cplusplus --- src/raylib.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index b2b825831..56acd6ad1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -133,12 +133,20 @@ // NOTE: MSVC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized with { } +// This is called aggregate initialization (C++11 feature) #if defined(__cplusplus) #define CLITERAL(type) type #else #define CLITERAL(type) (type) #endif +// Some compilers (mostly macos clang) default to C++98, +// where aggregate initialization can't be used +// So, give a more clear error stating how to fix this +#if !defined(_MSC_VER) && (defined(__cplusplus) && __cplusplus < 201103L) + #error "C++11 or later is required. Add -std=c++11" +#endif + // NOTE: We 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) From b1b6ae3905512cbe3b39ad37109a494c10f5ca32 Mon Sep 17 00:00:00 2001 From: Pixel Phobic <65147646+PixelPhobicGames@users.noreply.github.com> Date: Fri, 2 Jun 2023 01:29:45 -0700 Subject: [PATCH 0466/1710] Full Movement Added to Right Analog Stick (#3095) I Added Analog Stick Support to the rcamera module, However this code only allowed for 4 Directions of Movement, This Changed adds the full range of Movement to the Right Analog Stick. --- src/rcamera.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index c1b048e01..2a20d5814 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -465,10 +465,10 @@ void UpdateCamera(Camera *camera, int mode) CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.75f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.75f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.75f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.75f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); } //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); From ba802fdd5e33f0e48406841c5aa9c97fd9fc715b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 3 Jun 2023 19:50:46 +0200 Subject: [PATCH 0467/1710] tweaks --- src/rcore.c | 2 +- src/rlgl.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index af65cebf0..ee682e8b0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3431,7 +3431,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa struct sdefl sdefl = { 0 }; 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 stbwi + *compDataSize = sdeflate(&sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbiw TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize); #endif diff --git a/src/rlgl.h b/src/rlgl.h index f0bdf3cd4..5ba2593f2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2204,8 +2204,9 @@ void rlLoadExtensions(void *loader) #if defined(GRAPHICS_API_OPENGL_ES2) #if defined(PLATFORM_DESKTOP) + // TODO: Support OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); - else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES2.0 loaded successfully"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); #endif // Get supported extensions list From f8b352f6d979bd7bfb960fc776bea0e9d39bf9da Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 3 Jun 2023 19:51:16 +0200 Subject: [PATCH 0468/1710] ADDED: `ExportImageToMemory()` Only PNG supported for now --- src/raylib.h | 1 + src/rtextures.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 56acd6ad1..f6a48bc73 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1243,6 +1243,7 @@ RLAPI Image LoadImageFromScreen(void); RLAPI bool IsImageReady(Image image); // Check if an image is ready 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 RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success // Image generation functions diff --git a/src/rtextures.c b/src/rtextures.c index a1987ef51..7d76616c1 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -605,6 +605,34 @@ bool ExportImage(Image image, const char *fileName) return success; } +// Export image to memory buffer +unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataSize) +{ + unsigned char *fileData = NULL; + *dataSize = 0; + + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL; + +#if defined(SUPPORT_IMAGE_EXPORT) + int channels = 4; + + if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1; + else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) channels = 2; + else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3; + else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4; + +#if defined(SUPPORT_FILEFORMAT_PNG) + if ((strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0)) + { + fileData = stbi_write_png_to_mem((const unsigned char *)image.data, image.width*channels, image.width, image.height, channels, dataSize); + } +#endif + +#endif + + return fileData; +} + // Export image as code file (.h) defining an array of bytes bool ExportImageAsCode(Image image, const char *fileName) { @@ -1013,7 +1041,7 @@ Image ImageCopy(Image image) if (height < 1) height = 1; } - newImage.data = RL_MALLOC(size); + newImage.data = RL_CALLOC(size, 1); if (newImage.data != NULL) { From 753c0b385380adff54803fb9ff90fa5b4763327b Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Mon, 5 Jun 2023 01:13:08 +0200 Subject: [PATCH 0469/1710] Addition of support for vox files in version 200. (#3097) --- src/external/vox_loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/vox_loader.h b/src/external/vox_loader.h index c1b26e6f3..6df10b518 100644 --- a/src/external/vox_loader.h +++ b/src/external/vox_loader.h @@ -555,7 +555,7 @@ int Vox_LoadFromMemory(unsigned char* pvoxData, unsigned int voxDataSize, VoxArr version = ((unsigned int*)fileDataPtr)[0]; fileDataPtr += 4; - if (version != 150) + if (version != 150 && version != 200) { return VOX_ERROR_FILE_VERSION_NOT_MATCH; //"MagicaVoxel version doesn't match" } From 6aada7d5ecc2c201665444dddec9debc2465be8f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jun 2023 18:07:25 +0200 Subject: [PATCH 0470/1710] Updated examples to `raygui 4.0-dev` --- examples/shapes/raygui.h | 2761 ++++++++++------- examples/shapes/shapes_draw_circle_sector.c | 16 +- .../shapes/shapes_draw_rectangle_rounded.c | 28 +- examples/shapes/shapes_draw_ring.c | 24 +- 4 files changed, 1641 insertions(+), 1188 deletions(-) diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index ea95e5c85..63a27fa90 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,22 +1,38 @@ /******************************************************************************************* * -* raygui v3.2 - A simple and easy-to-use immediate-mode gui library +* raygui v4.0-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. * -* 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, embeds a complete 1-bit icons pack +* - Standalone usage mode option (custom graphics backends) +* - Multiple tools provided for raygui development * -* Controls provided: +* POSSIBLE IMPROVEMENTS: +* - Redesign functions that require a value as parameter to be returned, pass by reference +* - Better standalone mode API for easy plug of custom backends +* - Externalize required inputs in some way, allow user customization * -* # Container/separators Controls +* LIMITATIONS: +* - No multi-line word-wraped text box support +* - No auto-layout mechanism provided, up to the user to define controls position and size +* - Standalone mode requires library modification and some user work to plug another backend +* +* CONTROLS PROVIDED: +* # Container/separators Controls * - WindowBox --> StatusBar, Panel * - GroupBox --> Line * - Line * - Panel --> StatusBar * - ScrollPanel --> StatusBar * -* # Basic Controls +* # Basic Controls * - Label * - Button * - LabelButton --> Label @@ -26,7 +42,6 @@ * - ComboBox * - DropdownBox * - TextBox -* - TextBoxMulti * - ValueBox --> TextBox * - Spinner --> Button, ValueBox * - Slider @@ -36,81 +51,124 @@ * - DummyRec * - Grid * -* # Advance Controls +* # 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). +* 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 * -* 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: * -* 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)]; * -* 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*4 = 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. * -* 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 * -* 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. * -* Custom control properties can be defined using the EXTENDED properties for each independent control. -* -* TOOL: rGuiStyler is a visual tool to customize raygui style. +* 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 * -* 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. * -* 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: * -* 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]; * -* 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 * -* 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 * -* TOOL: rGuiIcons is a visual tool to customize raygui icons. +* 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_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_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_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_CUSTOM_ICONS +* Includes custom ricons.h header defining a set of custom icons, +* this file can be generated using rGuiIcons tool * +* #define RAYGUI_DEBUG_TEXT_BOUNDS +* Draw text bounds rectangles for debug * * VERSIONS HISTORY: +* 4.0 (xx-Jun-2023) 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 +* +* 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 @@ -120,6 +178,7 @@ * 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 @@ -127,10 +186,12 @@ * 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 @@ -140,6 +201,7 @@ * 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 @@ -158,9 +220,13 @@ * 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 4.5 Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing +* +* 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. * * 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) @@ -174,7 +240,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 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. @@ -196,7 +262,10 @@ #ifndef RAYGUI_H #define RAYGUI_H -#define RAYGUI_VERSION "3.2" +#define RAYGUI_VERSION_MAJOR 4 +#define RAYGUI_VERSION_MINOR 0 +#define RAYGUI_VERSION_PATCH 0 +#define RAYGUI_VERSION "4.0-dev" #if !defined(RAYGUI_STANDALONE) #include "raylib.h" @@ -291,6 +360,15 @@ int format; // Data format (PixelFormat type) } 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) @@ -304,10 +382,11 @@ // It should be redesigned to be provided by user typedef struct Font { int baseSize; // Base size (default chars height) - int glyphCount; // Number of characters - Texture2D texture; // Characters texture atlas - Rectangle *recs; // Characters rectangles in texture - GlyphInfo *chars; // Characters info data + 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 @@ -442,6 +521,9 @@ typedef enum { typedef enum { TEXT_INNER_PADDING = 16, // TextBox/TextBoxMulti/ValueBox/Spinner inner text padding TEXT_LINES_SPACING, // TextBoxMulti lines separation + TEXT_ALIGNMENT_VERTICAL, // TextBoxMulti vertical alignment: 0-CENTERED, 1-UP, 2-DOWN + TEXT_MULTILINE, // TextBox supports multiple lines + TEXT_WRAP_MODE // TextBox wrap mode for multiline: 0-NO_WRAP, 1-CHAR_WRAP, 2-WORD_WRAP } GuiTextBoxProperty; // Spinner @@ -484,78 +566,86 @@ 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 GuiFade(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) +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 GuiFade(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) +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 - -// Container/separator controls, useful for controls organization -RAYGUIAPI bool GuiWindowBox(Rectangle bounds, const char *title); // Window Box control, shows a window that can be closed -RAYGUIAPI void GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name -RAYGUIAPI void GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text -RAYGUIAPI void GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls -RAYGUIAPI Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll); // Scroll Panel control - -// Basic controls set -RAYGUIAPI void GuiLabel(Rectangle bounds, const char *text); // Label control, shows text -RAYGUIAPI bool GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked -RAYGUIAPI bool GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked -RAYGUIAPI bool GuiToggle(Rectangle bounds, const char *text, bool active); // Toggle Button control, returns true when active -RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int active); // Toggle Group control, returns active toggle index -RAYGUIAPI bool 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, returns selected item index -RAYGUIAPI bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item -RAYGUIAPI bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value -RAYGUIAPI bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers -RAYGUIAPI bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text -RAYGUIAPI bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control with multiple lines -RAYGUIAPI float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider control, returns selected value -RAYGUIAPI float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider Bar control, returns selected value -RAYGUIAPI float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Progress Bar control, shows current progress value -RAYGUIAPI void GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text -RAYGUIAPI void GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders -RAYGUIAPI Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs); // Grid control, returns mouse cell position - -// Advance controls set -RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int active); // List View control, returns selected list item index -RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, int *scrollIndex, int active); // 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, int *secretViewActive); // Text Input Box control, ask for text, supports secret -RAYGUIAPI Color GuiColorPicker(Rectangle bounds, const char *text, Color color); // Color Picker control (multiple color controls) -RAYGUIAPI Color GuiColorPanel(Rectangle bounds, const char *text, Color color); // Color Panel control -RAYGUIAPI float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha); // Color Bar Alpha control -RAYGUIAPI float GuiColorBarHue(Rectangle bounds, const char *text, float value); // Color Bar Hue control +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 + + +// 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, shows text +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, show true when clicked +RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control, returns true when active +RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control, returns active toggle index +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, returns selected item index + +RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item +RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value +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 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, returns selected value +RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control, returns selected value +RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control, shows current progress value +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, returns mouse cell position + +// Advance controls set +RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control, returns selected list item index +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 +//---------------------------------------------------------------------------------------------------------- + #if !defined(RAYGUI_NO_ICONS) -RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); - -RAYGUIAPI unsigned int *GuiGetIcons(void); // Get full icons data pointer -RAYGUIAPI unsigned int *GuiGetIconData(int iconId); // Get icon bit data -RAYGUIAPI void GuiSetIconData(int iconId, unsigned int *data); // Set icon bit data -RAYGUIAPI void GuiSetIconScale(unsigned int scale); // Set icon scale (1 by default) - -RAYGUIAPI void GuiSetIconPixel(int iconId, int x, int y); // Set icon pixel value -RAYGUIAPI void GuiClearIconPixel(int iconId, int x, int y); // Clear icon pixel value -RAYGUIAPI bool GuiCheckIconPixel(int iconId, int x, int y); // Check icon pixel value #if !defined(RAYGUI_CUSTOM_ICONS) //---------------------------------------------------------------------------------- @@ -768,20 +858,20 @@ typedef enum { ICON_FILE_NEW = 203, ICON_FOLDER_ADD = 204, ICON_ALARM = 205, - ICON_206 = 206, - ICON_207 = 207, - ICON_208 = 208, - ICON_209 = 209, - ICON_210 = 210, - ICON_211 = 211, - ICON_212 = 212, - ICON_213 = 213, - ICON_214 = 214, - ICON_215 = 215, - ICON_216 = 216, - ICON_217 = 217, - ICON_218 = 218, - ICON_219 = 219, + 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_220 = 220, ICON_221 = 221, ICON_222 = 222, @@ -839,7 +929,7 @@ typedef enum { #include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()] #include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()] -#include // Required for: strlen() [GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()], memset(), memcpy() +#include // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy() #include // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()] #include // Required for: roundf() [GuiColorPicker()] @@ -849,6 +939,11 @@ typedef enum { #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) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height)) +#endif + #if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS) // Embedded icons, no external file provided @@ -875,264 +970,267 @@ typedef enum { // 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, 0x47fe483c, 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, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM - 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT - 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 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 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_206 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_207 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_208 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_209 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_210 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_211 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_212 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_213 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_214 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_215 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_216 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_217 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_218 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_219 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_223 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_224 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_225 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_226 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_227 - 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 - 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 + 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, 0x47fe483c, 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, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT + 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 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 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_223 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_224 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_225 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_226 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_227 + 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 + 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: We keep a pointer to the icons array, useful to point to other sets if required +static unsigned int *guiIconsPtr = guiIcons; + #endif // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS #ifndef RAYGUI_ICON_SIZE @@ -1152,13 +1250,24 @@ 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 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 element transpacency on drawing +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 element transpacency on drawing -static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) +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 guiSliderDragging = false; // Gui slider drag state (no inputs processed except dragged slider) +static Rectangle guiSliderActive = { 0 }; // Gui slider active bounds rectangle, used as an unique identifier + +static unsigned 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 //---------------------------------------------------------------------------------- // Style data array for all gui style properties (allocated on data segment by default) @@ -1203,13 +1312,12 @@ static bool IsMouseButtonReleased(int button); static bool IsKeyDown(int key); static bool IsKeyPressed(int key); -static int GetCharPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox() +static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() //------------------------------------------------------------------------------- // Drawing required functions //------------------------------------------------------------------------------- static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle(), GuiDrawIcon() - static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() //------------------------------------------------------------------------------- @@ -1221,9 +1329,6 @@ static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle() static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle() static char *LoadFileText(const char *fileName); // -- GuiLoadStyle() static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle() - -static Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // -- GetTextWidth(), GuiTextBoxMulti() -static void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // -- GuiDrawText() //------------------------------------------------------------------------------- // raylib functions already implemented in raygui @@ -1235,7 +1340,8 @@ static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if 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 int GetCodepoint(const char *text, int *bytesProcessed); // Get next codepoint in a UTF-8 encoded 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 @@ -1246,18 +1352,22 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static int GetTextWidth(const char *text); // Gui get text width using default font +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 static void GuiDrawText(const char *text, Rectangle bounds, 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, int *count, int *textRow); // Split controls text into multiple strings +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 + //---------------------------------------------------------------------------------- // Gui Setup Functions Definition @@ -1341,7 +1451,7 @@ int GuiGetStyle(int control, int property) //---------------------------------------------------------------------------------- // Window Box control -bool GuiWindowBox(Rectangle bounds, const char *title) +int GuiWindowBox(Rectangle bounds, const char *title) { // Window title bar height (including borders) // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox() @@ -1349,8 +1459,8 @@ bool GuiWindowBox(Rectangle bounds, const char *title) #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24 #endif + int result = 0; //GuiState state = guiState; - bool clicked = false; int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; @@ -1379,22 +1489,23 @@ bool GuiWindowBox(Rectangle bounds, const char *title) #if defined(RAYGUI_NO_ICONS) clicked = GuiButton(closeButtonRec, "x"); #else - clicked = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); + result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); #endif GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); //-------------------------------------------------------------------- - return clicked; + return result; // Window close button clicked: result = 1 } // Group Box control with text name -void GuiGroupBox(Rectangle bounds, const char *text) +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 @@ -1405,10 +1516,12 @@ void GuiGroupBox(Rectangle bounds, const char *text) 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 -void GuiLine(Rectangle bounds, const char *text) +int GuiLine(Rectangle bounds, const char *text) { #if !defined(RAYGUI_LINE_ORIGIN_SIZE) #define RAYGUI_LINE_MARGIN_TEXT 12 @@ -1417,6 +1530,7 @@ void GuiLine(Rectangle bounds, const char *text) #define RAYGUI_LINE_TEXT_PADDING 4 #endif + int result = 0; GuiState state = guiState; Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha); @@ -1427,7 +1541,7 @@ void GuiLine(Rectangle bounds, const char *text) else { Rectangle textBounds = { 0 }; - textBounds.width = (float)GetTextWidth(text); + textBounds.width = (float)GetTextWidth(text) + 2; textBounds.height = bounds.height; textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT; textBounds.y = bounds.y; @@ -1438,15 +1552,18 @@ void GuiLine(Rectangle bounds, const char *text) 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 -void GuiPanel(Rectangle bounds, const char *text) +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) @@ -1467,13 +1584,91 @@ void GuiPanel(Rectangle bounds, const char *text) GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha), Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha)); //-------------------------------------------------------------------- + + 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 160 + + 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, GuiIconText(12, text[i]), &toggle); + } + else + { + toggle = false; + GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle); + if (toggle) *active = i; + } + + GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); + + // Draw tab close button + // NOTE: Only draw close button for curren tab: if (CheckCollisionPointRec(mousePoint, 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 -Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll) +int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view) { + 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; @@ -1501,17 +1696,17 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, 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) }; // Calculate view area (area without the scrollbars) - Rectangle view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? + *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; + 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 : -1.0f; + 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 @@ -1541,8 +1736,8 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, #endif float wheelMove = GetMouseWheelMove(); - // Horizontal scroll (Shift + Mouse wheel) - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_SHIFT))) scrollPos.x += wheelMove*20; + // Horizontal scroll ((Left Control or Left Shift) + Mouse wheel) + if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*20; else scrollPos.y += wheelMove*20; // Vertical scroll } } @@ -1558,7 +1753,7 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, //-------------------------------------------------------------------- if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar - GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); // Draw background // Save size of the scrollbar slider const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); @@ -1597,34 +1792,37 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, if (scroll != NULL) *scroll = scrollPos; - return view; + return result; } // Label control -void GuiLabel(Rectangle bounds, const char *text) +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), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- + + return result; } // Button control, returns true when clicked -bool GuiButton(Rectangle bounds, const char *text) +int GuiButton(Rectangle bounds, const char *text) { + int result = 0; GuiState state = guiState; - bool pressed = false; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1634,7 +1832,7 @@ bool GuiButton(Rectangle bounds, const char *text) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; } } //-------------------------------------------------------------------- @@ -1643,24 +1841,26 @@ bool GuiButton(Rectangle bounds, const char *text) //-------------------------------------------------------------------- GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); + + if (state == STATE_FOCUSED) GuiTooltip(bounds); //------------------------------------------------------------------ - return pressed; + return result; // Button pressed: result = 1 } // Label button control -bool GuiLabelButton(Rectangle bounds, const char *text) +int GuiLabelButton(Rectangle bounds, const char *text) { GuiState state = guiState; bool pressed = false; // NOTE: We force bounds.width to be all text - float textWidth = MeasureTextEx(guiFont, text, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)).x; - if (bounds.width < textWidth) bounds.width = textWidth; + float textWidth = (float)GetTextWidth(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) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1684,13 +1884,17 @@ bool GuiLabelButton(Rectangle bounds, const char *text) } // Toggle Button control, returns true when active -bool GuiToggle(Rectangle bounds, const char *text, bool active) +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) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1701,7 +1905,7 @@ bool GuiToggle(Rectangle bounds, const char *text, bool active) else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { state = STATE_NORMAL; - active = !active; + *active = !(*active); } else state = STATE_FOCUSED; } @@ -1712,32 +1916,40 @@ bool GuiToggle(Rectangle bounds, const char *text, bool active) //-------------------------------------------------------------------- if (state == STATE_NORMAL) { - GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); - GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, (active? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha)); } else { GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state*3)), guiAlpha)); GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)), guiAlpha)); } + + if (state == STATE_FOCUSED) GuiTooltip(bounds); //-------------------------------------------------------------------- - return active; + return result; } -// Toggle Group control, returns toggled button index -int GuiToggleGroup(Rectangle bounds, const char *text, int active) +// Toggle Group control, returns toggled button codepointIndex +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); + const char **items = GuiTextSplit(text, ';', &itemCount, rows); int prevRow = rows[0]; @@ -1750,25 +1962,38 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int active) prevRow = rows[i]; } - if (i == active) GuiToggle(bounds, items[i], true); - else if (GuiToggle(bounds, items[i], false) == true) active = 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 active; + return result; } // Check Box control, returns true when active -bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) +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)GetTextWidth(text); + textBounds.width = (float)GetTextWidth(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; @@ -1777,7 +2002,7 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1794,7 +2019,7 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) checked = !checked; + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) *checked = !(*checked); } } //-------------------------------------------------------------------- @@ -1803,7 +2028,7 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) //-------------------------------------------------------------------- GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha), BLANK); - if (checked) + if (*checked) { Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), @@ -1815,14 +2040,18 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- - return checked; + return result; } -// Combo Box control, returns selected item index -int GuiComboBox(Rectangle bounds, const char *text, int active) +// Combo Box control, returns selected item codepointIndex +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), @@ -1830,14 +2059,14 @@ int GuiComboBox(Rectangle bounds, const char *text, int active) // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, &itemCount, NULL); + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); - if (active < 0) active = 0; - else if (active > itemCount - 1) active = itemCount - 1; + if (*active < 0) *active = 0; + else if (*active > (itemCount - 1)) *active = itemCount - 1; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1)) + if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1846,8 +2075,8 @@ int GuiComboBox(Rectangle bounds, const char *text, int active) { if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - active += 1; - if (active >= itemCount) active = 0; + *active += 1; + if (*active >= itemCount) *active = 0; // Cyclic combobox } if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; @@ -1860,7 +2089,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int active) //-------------------------------------------------------------------- // Draw combo box main GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))), guiAlpha)); - GuiDrawText(items[active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); // Draw selector using a custom button // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values @@ -1869,37 +2098,37 @@ int GuiComboBox(Rectangle bounds, const char *text, int active) GuiSetStyle(BUTTON, BORDER_WIDTH, 1); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); - GuiButton(selector, TextFormat("%i/%i", active + 1, itemCount)); + GuiButton(selector, TextFormat("%i/%i", *active + 1, itemCount)); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); //-------------------------------------------------------------------- - return active; + return result; } // Dropdown Box control // NOTE: Returns mouse click -bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) +int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) { + int result = 0; GuiState state = guiState; + int itemSelected = *active; int itemFocused = -1; // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, &itemCount, NULL); + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); Rectangle boundsOpen = bounds; boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); Rectangle itemBounds = bounds; - bool pressed = false; // Check mouse button pressed - // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1)) + if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1910,11 +2139,11 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo // Check if mouse has been pressed or released outside limits if (!CheckCollisionPointRec(mousePoint, boundsOpen)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + 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)) pressed = true; + if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; // Check focused and selected item for (int i = 0; i < itemCount; i++) @@ -1928,7 +2157,7 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { itemSelected = i; - pressed = true; // Item selected, change to editMode = false + result = 1; // Item selected } break; } @@ -1942,7 +2171,7 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo { if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - pressed = true; + result = 1; state = STATE_PRESSED; } else state = STATE_FOCUSED; @@ -1991,29 +2220,68 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo //-------------------------------------------------------------------- *active = itemSelected; - return pressed; + + // 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, updates input text -// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) -bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) +// Text Box control +// NOTE: Returns true on ENTER pressed (useful for data validation) +int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) { - GuiState state = guiState; - bool pressed = false; + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 40 // 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; + + Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); + int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex); + int textIndexOffset = 0; // Text index offset to start drawing in the box + + int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); + int multiline = GuiGetStyle(TEXTBOX, TEXT_MULTILINE); + + // Cursor rectangle + // NOTE: Position X value should be updated Rectangle cursor = { - bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text) + 2, - bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), - 4, + textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), + textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), + 2, (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2 }; + switch (alignmentVertical) + { + case 0: cursor.y = textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE); break; // CENTERED + case 1: cursor.y = textBounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; break; // UP + case 2: cursor.y = textBounds.y + textBounds.height; break; // DOWN + default: break; + } + 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); + // 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; + // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2021,51 +2289,158 @@ bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) { state = STATE_PRESSED; - int key = GetCharPressed(); // Returns codepoint as Unicode - int keyCount = (int)strlen(text); - int byteSize = 0; - const char *textUTF8 = CodepointToUTF8(key, &byteSize); - - // Only allow keys in range [32..125] - if ((keyCount + byteSize) < textSize) + // 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) { - float maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)*2)); + int nextCodepointSize = 0; + GetCodepointNext(text + textIndexOffset, &nextCodepointSize); - if ((GetTextWidth(text) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) && (key >= 32)) + textIndexOffset += nextCodepointSize; + + textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex); + } + + unsigned int textLength = (unsigned int)strlen(text); // Get current text length + int codepoint = GetCharPressed(); // Get Unicode codepoint + if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; + + if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; + + // Encode codepoint as UTF-8 + 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) < bufferSize)) + { + // 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 codepoint from text, after current cursor position + if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) + { + autoCursorDelayCounter++; + + if (IsKeyPressed(KEY_DELETE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames { - for (int i = 0; i < byteSize; i++) + 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]; + + textLength -= codepointSize; + + // Make sure text last character is EOL + text[textLength] = '\0'; + } + } + + // Delete codepoint from text, before current cursor position + if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) + { + autoCursorDelayCounter++; + + if (IsKeyPressed(KEY_BACKSPACE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames + { + int prevCodepointSize = 0; + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + // Move backward text from cursor position + for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize]; + + // Prevent cursor index from decrementing past 0 + if (textBoxCursorIndex > 0) { - text[keyCount] = textUTF8[i]; - keyCount++; + textBoxCursorIndex -= codepointSize; + textLength -= codepointSize; } - text[keyCount] = '\0'; + // Make sure text last character is EOL + text[textLength] = '\0'; } } - // Delete text - if (keyCount > 0) + // Move cursor position with keys + //if (IsKeyDown(KEY_LEFT) && autoCursorMode) + if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))) { - if (IsKeyPressed(KEY_BACKSPACE)) + autoCursorDelayCounter++; + + if (IsKeyPressed(KEY_LEFT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames { - while ((keyCount > 0) && ((text[--keyCount] & 0xc0) == 0x80)); - text[keyCount] = '\0'; + int prevCodepointSize = 0; + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize; + } + } + else if (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))) + { + autoCursorDelayCounter++; + + if (IsKeyPressed(KEY_RIGHT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames + { + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize; } } - if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true; + // TODO: Get cursor rectangle from mouse position + //cursor = GetCursorFromMousePosition(bounds, text, mouse); // Gui style considered internally, including wrapMode - // Check text alignment to position cursor properly - int textAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); - if (textAlignment == TEXT_ALIGN_CENTER) cursor.x = bounds.x + GetTextWidth(text)/2 + bounds.width/2 + 1; - else if (textAlignment == TEXT_ALIGN_RIGHT) cursor.x = bounds.x + bounds.width - GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING); + // TODO: Get cursor rectangle from buffer index + //cursor = GetCursorFromIndex(bounds, text, index); // Gui style considered internally, including wrapMode + + // 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); + //if (multiline) cursor.y = GetTextLines() + + // Finish text editing on ENTER (if not multiline mode) or mouse click outside bounds + if ((!multiline && IsKeyPressed(KEY_ENTER)) || + (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + { + textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index + result = 1; + } } else { if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + textBoxCursorIndex = (int)strlen(text); // GLOBAL: Place cursor index to the end of current text + result = 1; + } } } } @@ -2081,23 +2456,51 @@ bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) { GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); } - else GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); + else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); - GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + // Draw text considering index offset if required + // NOTE: Text index offset depends on cursor position + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); // Draw cursor - if (editMode) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + if (editMode) + { + //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) + GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } + else if (state == STATE_FOCUSED) GuiTooltip(bounds); //-------------------------------------------------------------------- + return result; // Mouse button pressed: result = 1 +} + +/* +// Text Box control with multiple lines +// NOTE: It's a regular GuiTextBox() but enabling multiline support, +// unfortunately cursor placement is not working properly so the function is removed +bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode) +{ + bool pressed = false; + + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1); + GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 1); + + // TODO: Implement methods to calculate cursor position properly + pressed = GuiTextBox(bounds, text, bufferSize, editMode); + + GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 0); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 0); + return pressed; } +*/ // Spinner control, returns selected value -bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) { + int result = 1; GuiState state = guiState; - bool pressed = false; int tempValue = *value; Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING), bounds.y, @@ -2108,7 +2511,7 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in Rectangle textBounds = { 0 }; if (text != NULL) { - textBounds.width = (float)GetTextWidth(text); + textBounds.width = (float)GetTextWidth(text) + 2; textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; @@ -2117,7 +2520,7 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2146,8 +2549,7 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in // Draw control //-------------------------------------------------------------------- - // TODO: Set Spinner properties for ValueBox - pressed = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode); + result = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode); // Draw value selector custom buttons // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values @@ -2164,19 +2566,19 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in //-------------------------------------------------------------------- *value = tempValue; - return pressed; + return result; } // Value Box control, updates input text with numbers // NOTE: Requires static variables: frameCounter -bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +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; - bool pressed = false; char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0"; sprintf(textValue, "%i", *value); @@ -2184,7 +2586,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i Rectangle textBounds = { 0 }; if (text != NULL) { - textBounds.width = (float)GetTextWidth(text); + textBounds.width = (float)GetTextWidth(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; @@ -2193,7 +2595,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2237,7 +2639,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i //if (*value > maxValue) *value = maxValue; //else if (*value < minValue) *value = minValue; - if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true; + if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1; } else { @@ -2247,7 +2649,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; } } } @@ -2267,7 +2669,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i if (editMode) { // NOTE: ValueBox internal text is always centered - Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 2, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) }; + 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) }; GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); } @@ -2275,190 +2677,20 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- - return pressed; -} - -// Text Box control with multiple lines -bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) -{ - GuiState state = guiState; - bool pressed = false; - - Rectangle textAreaBounds = { - bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), - bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), - bounds.width - 2*(GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)), - bounds.height - 2*(GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)) - }; - - // Cursor position, [x, y] values should be updated - Rectangle cursor = { 0, -1, 4, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) + 2 }; - - float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; // Character rectangle scaling factor - - // Update control - //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) - { - Vector2 mousePoint = GetMousePosition(); - - if (editMode) - { - state = STATE_PRESSED; - - // We get an Unicode codepoint - int codepoint = GetCharPressed(); - int textLength = (int)strlen(text); // Length in bytes (UTF-8 string) - - // Introduce characters - if (textLength < (textSize - 1)) - { - if (IsKeyPressed(KEY_ENTER)) - { - text[textLength] = '\n'; - textLength++; - } - else if (codepoint >= 32) - { - // Supports Unicode inputs -> Encoded to UTF-8 - int charUTF8Length = 0; - const char *charEncoded = CodepointToUTF8(codepoint, &charUTF8Length); - memcpy(text + textLength, charEncoded, charUTF8Length); - textLength += charUTF8Length; - } - } - - // Delete characters - if (textLength > 0) - { - if (IsKeyPressed(KEY_BACKSPACE)) - { - if ((unsigned char)text[textLength - 1] < 127) - { - // Remove ASCII equivalent character (1 byte) - textLength--; - text[textLength] = '\0'; - } - else - { - // Remove latest UTF-8 unicode character introduced (n bytes) - int charUTF8Length = 0; - while (((unsigned char)text[textLength - 1 - charUTF8Length] & 0b01000000) == 0) charUTF8Length++; - - textLength -= (charUTF8Length + 1); - text[textLength] = '\0'; - } - } - } - - // Exit edit mode - if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; - } - else - { - if (CheckCollisionPointRec(mousePoint, bounds)) - { - state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; - } - } - } - //-------------------------------------------------------------------- - - // Draw control - //-------------------------------------------------------------------- - if (state == STATE_PRESSED) - { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); - } - else if (state == STATE_DISABLED) - { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); - } - else GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); - - int wrapMode = 1; // 0-No wrap, 1-Char wrap, 2-Word wrap - Vector2 cursorPos = { textAreaBounds.x, textAreaBounds.y }; - - //int lastSpacePos = 0; - //int lastSpaceWidth = 0; - //int lastSpaceCursorPos = 0; - - for (int i = 0, codepointLength = 0; text[i] != '\0'; i += codepointLength) - { - int codepoint = GetCodepoint(text + i, &codepointLength); - int index = GetGlyphIndex(guiFont, codepoint); // If requested codepoint is not found, we get '?' (0x3f) - Rectangle atlasRec = guiFont.recs[index]; - GlyphInfo glyphInfo = guiFont.glyphs[index]; // Glyph measures - - if ((codepointLength == 1) && (codepoint == '\n')) - { - cursorPos.y += (guiFont.baseSize*scaleFactor + GuiGetStyle(TEXTBOX, TEXT_LINES_SPACING)); // Line feed - cursorPos.x = textAreaBounds.x; // Carriage return - } - else - { - if (wrapMode == 1) - { - int glyphWidth = 0; - if (glyphInfo.advanceX != 0) glyphWidth += glyphInfo.advanceX; - else glyphWidth += (int)(atlasRec.width + glyphInfo.offsetX); - - // Jump line if the end of the text box area has been reached - if ((cursorPos.x + (glyphWidth*scaleFactor)) > (textAreaBounds.x + textAreaBounds.width)) - { - cursorPos.y += (guiFont.baseSize*scaleFactor + GuiGetStyle(TEXTBOX, TEXT_LINES_SPACING)); // Line feed - cursorPos.x = textAreaBounds.x; // Carriage return - } - } - else if (wrapMode == 2) - { - /* - if ((codepointLength == 1) && (codepoint == ' ')) - { - lastSpacePos = i; - lastSpaceWidth = 0; - lastSpaceCursorPos = cursorPos.x; - } - - // Jump line if last word reaches end of text box area - if ((lastSpaceCursorPos + lastSpaceWidth) > (textAreaBounds.x + textAreaBounds.width)) - { - cursorPos.y += 12; // Line feed - cursorPos.x = textAreaBounds.x; // Carriage return - } - */ - } - - // Draw current character glyph - DrawTextCodepoint(guiFont, codepoint, cursorPos, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); - - int glyphWidth = 0; - if (glyphInfo.advanceX != 0) glyphWidth += glyphInfo.advanceX; - else glyphWidth += (int)(atlasRec.width + glyphInfo.offsetX); - - cursorPos.x += (glyphWidth*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - //if (i > lastSpacePos) lastSpaceWidth += (atlasRec.width + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - } - } - - cursor.x = cursorPos.x; - cursor.y = cursorPos.y; - - // Draw cursor position considering text glyphs - if (editMode) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); - //-------------------------------------------------------------------- - - return pressed; + return result; } // Slider control with pro parameters // NOTE: Other GuiSlider*() controls use this one -float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue, int sliderWidth) +int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth) { + int result = 0; GuiState state = guiState; - int sliderValue = (int)(((value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + + int sliderValue = (int)(((*value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_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) }; @@ -2480,14 +2712,32 @@ float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds)) + if (guiSliderDragging) // Keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + { + // Get equivalent value and slider position from mousePoint.x + *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + } + } + else + { + guiSliderDragging = false; + guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; + guiSliderDragging = true; + guiSliderActive = bounds; // Store bounds as an identifier when dragging starts // Get equivalent value and slider position from mousePoint.x - value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar @@ -2495,8 +2745,8 @@ float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight else state = STATE_FOCUSED; } - if (value > maxValue) value = maxValue; - else if (value < minValue) value = minValue; + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; } // Bar limits check @@ -2543,35 +2793,39 @@ float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight } //-------------------------------------------------------------------- - return value; + return result; } // Slider control extended, returns selected value and has text -float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) +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 -float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) +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); } // Progress Bar control extended, shows current progress value -float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) +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; + 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) }; // Update control //-------------------------------------------------------------------- - if (value > maxValue) value = maxValue; + if (*value > maxValue) *value = maxValue; - if (state != STATE_DISABLED) progress.width = ((float)(value/(maxValue - minValue))*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); + if (state != STATE_DISABLED) progress.width = ((float)(*value/(maxValue - minValue))*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); //-------------------------------------------------------------------- // Draw control @@ -2606,12 +2860,13 @@ float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRig } //-------------------------------------------------------------------- - return value; + return result; } // Status Bar control -void GuiStatusBar(Rectangle bounds, const char *text) +int GuiStatusBar(Rectangle bounds, const char *text) { + int result = 0; GuiState state = guiState; // Draw control @@ -2620,16 +2875,19 @@ void GuiStatusBar(Rectangle bounds, const char *text) Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); //-------------------------------------------------------------------- + + return result; } // Dummy rectangle control, intended for placeholding -void GuiDummyRec(Rectangle bounds, const char *text) +int GuiDummyRec(Rectangle bounds, const char *text) { + int result = 0; GuiState state = guiState; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2647,25 +2905,32 @@ void GuiDummyRec(Rectangle bounds, const char *text) GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); //------------------------------------------------------------------ + + return result; } // List View control -int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int active) +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); + if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); - return GuiListViewEx(bounds, items, itemCount, NULL, scrollIndex, active); + 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 *focus, int *scrollIndex, int active) +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; + int itemSelected = *active; // Check if we need a scroll bar bool useScrollBar = false; @@ -2689,7 +2954,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, in // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2800,19 +3065,21 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, in if (focus != NULL) *focus = itemFocused; if (scrollIndex != NULL) *scrollIndex = startIndex; - return itemSelected; + *active = itemSelected; + return result; } // Color Panel control -Color GuiColorPanel(Rectangle bounds, const char *text, Color color) +int GuiColorPanel(Rectangle bounds, const char *text, Color *color) { - const Color colWhite = { 255, 255, 255, 255 }; - const Color colBlack = { 0, 0, 0, 255 }; - + int result = 0; GuiState state = guiState; Vector2 pickerSelector = { 0 }; - Vector3 vcolor = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f }; + const Color colWhite = { 255, 255, 255, 255 }; + const Color colBlack = { 0, 0, 0, 255 }; + + Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f }; Vector3 hsv = ConvertRGBtoHSV(vcolor); pickerSelector.x = bounds.x + (float)hsv.y*bounds.width; // HSV: Saturation @@ -2827,7 +3094,7 @@ Color GuiColorPanel(Rectangle bounds, const char *text, Color color) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2850,10 +3117,10 @@ Color GuiColorPanel(Rectangle bounds, const char *text, Color color) Vector3 rgb = ConvertHSVtoRGB(hsv); // NOTE: Vector3ToColor() only available on raylib 1.8.1 - color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), + *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), (unsigned char)(255.0f*rgb.y), (unsigned char)(255.0f*rgb.z), - (unsigned char)(255.0f*(float)color.a/255.0f) }; + (unsigned char)(255.0f*(float)color->a/255.0f) }; } else state = STATE_FOCUSED; @@ -2880,19 +3147,20 @@ Color GuiColorPanel(Rectangle bounds, const char *text, Color color) GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); //-------------------------------------------------------------------- - return color; + return result; } // Color Bar Alpha control // NOTE: Returns alpha value normalized [0..1] -float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha) +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 }; + 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 //-------------------------------------------------------------------- @@ -2900,16 +3168,34 @@ float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha) { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds) || - CheckCollisionPointRec(mousePoint, selector)) + if (guiSliderDragging) // Keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + { + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; + } + } + else + { + guiSliderDragging = false; + guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; + guiSliderDragging = true; + guiSliderActive = 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; + *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; @@ -2945,7 +3231,7 @@ float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha) GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- - return alpha; + return result; } // Color Bar Hue control @@ -2954,10 +3240,11 @@ float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha) // Color GuiColorBarSat() [WHITE->color] // Color GuiColorBarValue() [BLACK->color], HSV/HSL // float GuiColorBarLuminance() [BLACK->WHITE] -float GuiColorBarHue(Rectangle bounds, const char *text, float hue) +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) }; + 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 //-------------------------------------------------------------------- @@ -2965,16 +3252,34 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue) { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds) || - CheckCollisionPointRec(mousePoint, selector)) + if (guiSliderDragging) // Keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + { + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; + } + } + else + { + guiSliderDragging = false; + guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; + guiSliderDragging = true; + guiSliderActive = 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; + *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; @@ -2998,12 +3303,12 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue) if (state != STATE_DISABLED) { // Draw hue bar:color bars - 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)); + 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)); @@ -3013,7 +3318,7 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue) GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- - return hue; + return result; } // Color Picker control @@ -3022,21 +3327,28 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue) // float GuiColorBarAlpha(Rectangle bounds, float alpha) // float GuiColorBarHue(Rectangle bounds, float value) // NOTE: bounds define GuiColorPanel() size -Color GuiColorPicker(Rectangle bounds, const char *text, Color color) +int GuiColorPicker(Rectangle bounds, const char *text, Color *color) { - color = GuiColorPanel(bounds, NULL, 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) }; - Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ color.r/255.0f, color.g/255.0f, color.b/255.0f }); - hsv.x = GuiColorBarHue(boundsHue, NULL, hsv.x); + 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 }; + *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 color; + return result; } // Message Box control @@ -3049,27 +3361,27 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons #define RAYGUI_MESSAGEBOX_BUTTON_PADDING 12 #endif - int clicked = -1; // Returns clicked button from buttons list, 0 refers to closed window button + 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); + 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; - Vector2 textSize = MeasureTextEx(guiFont, message, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), 1); + int textWidth = GetTextWidth(message); Rectangle textBounds = { 0 }; - textBounds.x = bounds.x + bounds.width/2 - textSize.x/2; + textBounds.x = bounds.x + bounds.width/2 - textWidth/2; textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING; - textBounds.width = textSize.x; + textBounds.width = (float)textWidth; textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; // Draw control //-------------------------------------------------------------------- - if (GuiWindowBox(bounds, title)) clicked = 0; + if (GuiWindowBox(bounds, title)) result = 0; int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); @@ -3081,37 +3393,37 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons for (int i = 0; i < buttonCount; i++) { - if (GuiButton(buttonBounds, buttonsText[i])) clicked = i + 1; + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); } GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment); //-------------------------------------------------------------------- - return clicked; + 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, int *secretViewActive) +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 28 + #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 28 + #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 btnIndex = -1; + int result = -1; int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, &buttonCount, NULL); + 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; @@ -3123,12 +3435,12 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co Rectangle textBounds = { 0 }; if (message != NULL) { - Vector2 textSize = MeasureTextEx(guiFont, message, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), 1); + int textSize = GetTextWidth(message) + 2; - textBounds.x = bounds.x + bounds.width/2 - textSize.x/2; - textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - textSize.y/2; - textBounds.width = textSize.x; - textBounds.height = textSize.y; + 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 }; @@ -3141,7 +3453,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co // Draw control //-------------------------------------------------------------------- - if (GuiWindowBox(bounds, title)) btnIndex = 0; + if (GuiWindowBox(bounds, title)) result = 0; // Draw message if available if (message != NULL) @@ -3155,10 +3467,10 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co if (secretViewActive != NULL) { static char stars[] = "****************"; - if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height }, + 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; - *secretViewActive = GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", *secretViewActive); + 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 { @@ -3170,52 +3482,53 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co for (int i = 0; i < buttonCount; i++) { - if (GuiButton(buttonBounds, buttonsText[i])) btnIndex = i + 1; + 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 btnIndex; + 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 -Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs) +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 currentCell = { -1, -1 }; + Vector2 currentMouseCell = { 0 }; int linesV = ((int)(bounds.width/spacing))*subdivs + 1; int linesH = ((int)(bounds.height/spacing))*subdivs + 1; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { if (CheckCollisionPointRec(mousePoint, bounds)) { - // NOTE: Cell values must be rounded to int - currentCell.x = (float)((mousePoint.x - bounds.x)/spacing); - currentCell.y = (float)((mousePoint.y - bounds.y)/spacing); + // 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 //-------------------------------------------------------------------- - - // TODO: Draw background panel? - switch (state) { case STATE_NORMAL: @@ -3240,9 +3553,24 @@ Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs) default: break; } - return currentCell; + 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 //---------------------------------------------------------------------------------- @@ -3338,125 +3666,24 @@ void GuiLoadStyle(const char *fileName) { rgsFile = fopen(fileName, "rb"); - if (rgsFile == NULL) return; - - char signature[5] = { 0 }; - short version = 0; - short reserved = 0; - int propertyCount = 0; - - fread(signature, 1, 4, rgsFile); - fread(&version, 1, sizeof(short), rgsFile); - fread(&reserved, 1, sizeof(short), rgsFile); - fread(&propertyCount, 1, sizeof(int), rgsFile); - - if ((signature[0] == 'r') && - (signature[1] == 'G') && - (signature[2] == 'S') && - (signature[3] == ' ')) + if (rgsFile != NULL) { - short controlId = 0; - short propertyId = 0; - unsigned int propertyValue = 0; + fseek(rgsFile, 0, SEEK_END); + int fileDataSize = ftell(rgsFile); + fseek(rgsFile, 0, SEEK_SET); - for (int i = 0; i < propertyCount; i++) + if (fileDataSize > 0) { - fread(&controlId, 1, sizeof(short), rgsFile); - fread(&propertyId, 1, sizeof(short), rgsFile); - fread(&propertyValue, 1, sizeof(unsigned int), rgsFile); + unsigned char *fileData = (unsigned char *)RL_MALLOC(fileDataSize*sizeof(unsigned char)); + fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); - 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); + GuiLoadStyleFromMemory(fileData, fileDataSize); - if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue); - } - else GuiSetStyle((int)controlId, (int)propertyId, propertyValue); + RL_FREE(fileData); } - // 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; - fread(&fontDataSize, 1, sizeof(int), rgsFile); - - if (fontDataSize > 0) - { - Font font = { 0 }; - int fontType = 0; // 0-Normal, 1-SDF - Rectangle whiteRec = { 0 }; - - fread(&font.baseSize, 1, sizeof(int), rgsFile); - fread(&font.glyphCount, 1, sizeof(int), rgsFile); - fread(&fontType, 1, sizeof(int), rgsFile); - - // Load font white rectangle - fread(&whiteRec, 1, sizeof(Rectangle), rgsFile); - - // Load font image parameters - int fontImageUncompSize = 0; - int fontImageCompSize = 0; - fread(&fontImageUncompSize, 1, sizeof(int), rgsFile); - fread(&fontImageCompSize, 1, sizeof(int), rgsFile); - - Image imFont = { 0 }; - imFont.mipmaps = 1; - fread(&imFont.width, 1, sizeof(int), rgsFile); - fread(&imFont.height, 1, sizeof(int), rgsFile); - fread(&imFont.format, 1, sizeof(int), rgsFile); - - if (fontImageCompSize < fontImageUncompSize) - { - // Compressed font atlas image data (DEFLATE), it requires DecompressData() - int dataUncompSize = 0; - unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize); - fread(compData, 1, fontImageCompSize, rgsFile); - 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_MALLOC(fontImageUncompSize); - fread(imFont.data, 1, fontImageUncompSize, rgsFile); - } - - if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); - font.texture = LoadTextureFromImage(imFont); - if (font.texture.id == 0) font = GetFontDefault(); - - RAYGUI_FREE(imFont.data); - - // Load font recs data - font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); - for (int i = 0; i < font.glyphCount; i++) fread(&font.recs[i], 1, sizeof(Rectangle), rgsFile); - - // Load font chars info data - font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); - for (int i = 0; i < font.glyphCount; i++) - { - fread(&font.glyphs[i].value, 1, sizeof(int), rgsFile); - fread(&font.glyphs[i].offsetX, 1, sizeof(int), rgsFile); - fread(&font.glyphs[i].offsetY, 1, sizeof(int), rgsFile); - fread(&font.glyphs[i].advanceX, 1, sizeof(int), rgsFile); - } - - GuiSetFont(font); - - // Set font texture source rectangle to be used as white texture to draw shapes - // NOTE: This way, all gui can be draw using a single draw call - if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec); - } -#endif + fclose(rgsFile); } - - fclose(rgsFile); } } @@ -3493,9 +3720,9 @@ void GuiLoadStyleDefault(void) GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); GuiSetStyle(TEXTBOX, TEXT_PADDING, 4); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); - GuiSetStyle(VALUEBOX, TEXT_PADDING, 4); + GuiSetStyle(VALUEBOX, TEXT_PADDING, 0); GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); - GuiSetStyle(SPINNER, TEXT_PADDING, 4); + GuiSetStyle(SPINNER, TEXT_PADDING, 0); GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(STATUSBAR, TEXT_PADDING, 8); GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); @@ -3515,7 +3742,7 @@ void GuiLoadStyleDefault(void) GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); - GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, 4); + GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, (int)((float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f)); GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24); GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2); @@ -3526,7 +3753,7 @@ void GuiLoadStyleDefault(void) GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16); GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0); GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12); - GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 24); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28); GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2); GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12); GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); @@ -3536,7 +3763,19 @@ void GuiLoadStyleDefault(void) GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8); GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2); - guiFont = GetFontDefault(); // Initialize default font + if (guiFont.texture.id != GetFontDefault().texture.id) + { + // Unload previous font texture + UnloadTexture(guiFont.texture); + + // 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 @@ -3554,28 +3793,27 @@ const char *GuiIconText(int iconId, const char *text) { memset(buffer, 0, 1024); sprintf(buffer, "#%03i#", iconId); - + for (int i = 5; i < 1024; i++) { buffer[i] = text[i - 5]; if (text[i - 5] == '\0') break; } - + return buffer; } - else + else { sprintf(iconBuffer, "#%03i#", iconId & 0x1ff); - + return iconBuffer; } #endif } #if !defined(RAYGUI_NO_ICONS) - // Get full icons data pointer -unsigned int *GuiGetIcons(void) { return guiIcons; } +unsigned int *GuiGetIcons(void) { return guiIconsPtr; } // Load raygui icons file (.rgi) // NOTE: In case nameIds are required, they can be requested with loadIconsName, @@ -3620,10 +3858,10 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName) short iconSize = 0; fread(signature, 1, 4, rgiFile); - fread(&version, 1, sizeof(short), rgiFile); - fread(&reserved, 1, sizeof(short), rgiFile); - fread(&iconCount, 1, sizeof(short), rgiFile); - fread(&iconSize, 1, sizeof(short), 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') && @@ -3636,13 +3874,13 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName) for (int i = 0; i < iconCount; i++) { guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH); - fread(guiIconsName[i], RAYGUI_ICON_MAX_NAME_LENGTH, 1, rgiFile); + 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 guiIcons data array - fread(guiIcons, iconCount*(iconSize*iconSize/32), sizeof(unsigned int), rgiFile); + // Read icons data directly over internal icons array + fread(guiIconsPtr, sizeof(unsigned int), iconCount*(iconSize*iconSize/32), rgiFile); } fclose(rgiFile); @@ -3660,7 +3898,7 @@ void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) { for (int k = 0; k < 32; k++) { - if (BIT_CHECK(guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) + if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) { #if !defined(RAYGUI_STANDALONE) DrawRectangle(posX + (k%RAYGUI_ICON_SIZE)*pixelSize, posY + y*pixelSize, pixelSize, pixelSize, color); @@ -3672,63 +3910,156 @@ void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) } } -// Get icon bit data -// NOTE: Bit data array grouped as unsigned int (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32 elements) -unsigned int *GuiGetIconData(int iconId) +// Set icon drawing size +void GuiSetIconScale(int scale) { - static unsigned int iconData[RAYGUI_ICON_DATA_ELEMENTS] = { 0 }; - memset(iconData, 0, RAYGUI_ICON_DATA_ELEMENTS*sizeof(unsigned int)); - - if (iconId < RAYGUI_ICON_MAX_ICONS) memcpy(iconData, &guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS], RAYGUI_ICON_DATA_ELEMENTS*sizeof(unsigned int)); - - return iconData; + if (scale >= 1) guiIconScale = scale; } -// Set icon bit data -// NOTE: Data must be provided as unsigned int array (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32 elements) -void GuiSetIconData(int iconId, unsigned int *data) -{ - if (iconId < RAYGUI_ICON_MAX_ICONS) memcpy(&guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS], data, RAYGUI_ICON_DATA_ELEMENTS*sizeof(unsigned int)); -} - -// Set icon scale (1 by default) -void GuiSetIconScale(unsigned int scale) -{ - guiIconScale = (scale < 1)? 1 : scale; -} - -// Set icon pixel value -void GuiSetIconPixel(int iconId, int x, int y) -{ - #define BIT_SET(a,b) ((a) |= (1u<<(b))) - - // This logic works for any RAYGUI_ICON_SIZE pixels icons, - // For example, in case of 16x16 pixels, every 2 lines fit in one unsigned int data element - BIT_SET(guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS + y/(sizeof(unsigned int)*8/RAYGUI_ICON_SIZE)], x + (y%(sizeof(unsigned int)*8/RAYGUI_ICON_SIZE)*RAYGUI_ICON_SIZE)); -} - -// Clear icon pixel value -void GuiClearIconPixel(int iconId, int x, int y) -{ - #define BIT_CLEAR(a,b) ((a) &= ~((1u)<<(b))) - - // This logic works for any RAYGUI_ICON_SIZE pixels icons, - // For example, in case of 16x16 pixels, every 2 lines fit in one unsigned int data element - BIT_CLEAR(guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS + y/(sizeof(unsigned int)*8/RAYGUI_ICON_SIZE)], x + (y%(sizeof(unsigned int)*8/RAYGUI_ICON_SIZE)*RAYGUI_ICON_SIZE)); -} - -// Check icon pixel value -bool GuiCheckIconPixel(int iconId, int x, int y) -{ - #define BIT_CHECK(a,b) ((a) & (1u<<(b))) - - return (BIT_CHECK(guiIcons[iconId*8 + y/2], x + (y%2*16))); -} #endif // !RAYGUI_NO_ICONS //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- + +// Load style from memory (binary 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 i = 1; i < RAYGUI_MAX_CONTROLS; i++) GuiSetStyle(i, (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 + Rectangle whiteRec = { 0 }; + + 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 + memcpy(&whiteRec, 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 < fontImageUncompSize) + { + // Compressed font atlas image data (DEFLATE), it requires DecompressData() + int dataUncompSize = 0; + unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize); + 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_MALLOC(fontImageUncompSize); + memcpy(imFont.data, fileDataPtr, fontImageUncompSize); + fileDataPtr += fontImageUncompSize; + } + + if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); + font.texture = LoadTextureFromImage(imFont); + if (font.texture.id == 0) font = GetFontDefault(); + + RAYGUI_FREE(imFont.data); + + // Load font recs data + 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 chars info data + font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); + 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; + } + + GuiSetFont(font); + + // Set font texture source rectangle to be used as white texture to draw shapes + // NOTE: This way, all gui can be draw using a single draw call + if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec); + } +#endif + } +} + // Gui get text width considering icon static int GetTextWidth(const char *text) { @@ -3736,7 +4067,7 @@ static int GetTextWidth(const char *text) #define ICON_TEXT_PADDING 4 #endif - Vector2 size = { 0 }; + Vector2 textSize = { 0 }; int textIconOffset = 0; if ((text != NULL) && (text[0] != '\0')) @@ -3752,15 +4083,43 @@ static int GetTextWidth(const char *text) } } } - + + text += textIconOffset; + // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); - - size = MeasureTextEx(guiFont, text + textIconOffset, fontSize, (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - if (textIconOffset > 0) size.x += (RAYGUI_ICON_SIZE - ICON_TEXT_PADDING); + + // 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 + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING)); + + textSize.x += glyphWidth; + } + } + + if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE - ICON_TEXT_PADDING); } - return (int)size.x; + return (int)textSize.x; } // Get text bounds considering control bounds @@ -3769,25 +4128,23 @@ 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); - textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH); - textBounds.height = bounds.height - 2*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); // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) + // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER switch (control) { - case COMBOBOX: bounds.width -= (GuiGetStyle(control, COMBO_BUTTON_WIDTH) + GuiGetStyle(control, COMBO_BUTTON_SPACING)); break; - case VALUEBOX: break; // NOTE: ValueBox text value always centered, text padding applies to label default: { if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); else textBounds.x += GuiGetStyle(control, TEXT_PADDING); - } break; + } + break; } - // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) - // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER - return textBounds; } @@ -3822,6 +4179,39 @@ static const char *GetTextIcon(const char *text, int *iconId) return text; } +// Get text divided into lines (by line-breaks '\n') +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; + 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; + *count += 1; + } + else len++; + } + + //lines[*count - 1].size = len; + + return lines; +} + // Gui draw text using default font static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint) { @@ -3831,70 +4221,120 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color #define ICON_TEXT_PADDING 4 #endif + int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); + + // We process the text lines one by one if ((text != NULL) && (text[0] != '\0')) { - int iconId = 0; - text = GetTextIcon(text, &iconId); // Check text for icon and move cursor + // Get text lines ('\n' delimiter) to process lines individually + // NOTE: We can't use GuiTextSplit() because it can be already used before calling + // GuiDrawText() and static buffer would be overriden :( + int lineCount = 0; + const char **lines = GetTextLines(text, &lineCount); - // Get text position depending on alignment and iconId - //--------------------------------------------------------------------------------- - Vector2 position = { bounds.x, bounds.y }; + //Rectangle textBounds = GetTextBounds(LABEL, bounds); + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); + float posOffsetY = 0; - // NOTE: We get text size after icon has been processed - // TODO: REVIEW: We consider text size in case of line breaks! -> MeasureTextEx() depends on raylib! - Vector2 textSize = MeasureTextEx(GuiGetFont(), text, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - //int textWidth = GetTextWidth(text); - //int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); - - // If text requires an icon, add size to measure - if (iconId >= 0) + for (int i = 0; i < lineCount; i++) { - textSize.x += RAYGUI_ICON_SIZE*guiIconScale; + int iconId = 0; + lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor - // WARNING: If only icon provided, text could be pointing to EOF character: '\0' - if ((text != NULL) && (text[0] != '\0')) textSize.x += ICON_TEXT_PADDING; - } + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + Vector2 position = { bounds.x, bounds.y }; - // Check guiTextAlign global variables - switch (alignment) - { - case TEXT_ALIGN_LEFT: + // 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]); + + // If text requires an icon, add size to measure + if (iconId >= 0) { - position.x = bounds.x; - position.y = bounds.y + bounds.height/2 - textSize.y/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); - } break; - case TEXT_ALIGN_CENTER: - { - position.x = bounds.x + bounds.width/2 - textSize.x/2; - position.y = bounds.y + bounds.height/2 - textSize.y/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); - } break; - case TEXT_ALIGN_RIGHT: - { - position.x = bounds.x + bounds.width - textSize.x; - position.y = bounds.y + bounds.height/2 - textSize.y/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); - } break; - default: break; - } + textSizeX += RAYGUI_ICON_SIZE*guiIconScale; - // NOTE: Make sure we get pixel-perfect coordinates, - // In case of decimals we got weird text positioning - position.x = (float)((int)position.x); - position.y = (float)((int)position.y); - //--------------------------------------------------------------------------------- + // WARNING: If only icon provided, text could be pointing to EOF character: '\0' + if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; + } - // Draw text (with icon if available) - //--------------------------------------------------------------------------------- + // Check guiTextAlign global variables + switch (alignment) + { + case TEXT_ALIGN_LEFT: position.x = bounds.x; break; + case TEXT_ALIGN_CENTER: position.x = bounds.x + bounds.width/2 - textSizeX/2; break; + case TEXT_ALIGN_RIGHT: position.x = bounds.x + bounds.width - textSizeX; break; + default: break; + } + + switch (alignmentVertical) + { + case 0: position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // CENTERED + case 1: position.y = bounds.y + posOffsetY; break; // UP + case 2: position.y = bounds.y + posOffsetY + bounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // DOWN + default: break; + } + + // NOTE: Make sure we get pixel-perfect coordinates, + // In case of decimals we got weird text positioning + position.x = (float)((int)position.x); + position.y = (float)((int)position.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)position.x, (int)(bounds.y + bounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)), guiIconScale, tint); - position.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); - } + if (iconId >= 0) + { + // NOTE: We consider icon height, probably different than text size + GuiDrawIcon(iconId, (int)position.x, (int)(bounds.y + bounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)), guiIconScale, tint); + position.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + } #endif - DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); - //--------------------------------------------------------------------------------- + //DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); + + // Get size in bytes of text, + // considering end of line and line break + int size = 0; + for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n'); c++, size++){ } + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + int textOffsetY = 0; + float textOffsetX = 0.0f; + for (int c = 0, codepointSize = 0; c < size; 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; + + if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint + else + { + if ((codepoint != ' ') && (codepoint != '\t')) + { + // Draw only required text glyphs fitting the bounds.width + if (textOffsetX < (bounds.width - guiFont.recs[index].width)) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ position.x + textOffsetX, position.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), tint); + } + } + + 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)); + } + } + + // TODO: Allow users to set line spacing for text: GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, x) + posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f; + //--------------------------------------------------------------------------------- + } } +#if defined(RAYGUI_DEBUG_TEXT_BOUNDS) + GuiDrawRectangle(bounds, 0, WHITE, Fade(RED, 0.4f)); +#endif } // Gui draw rectangle using default raygui plain style with borders @@ -3916,9 +4356,30 @@ static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, } } +// Draw tooltip using control bounds +static void GuiTooltip(Rectangle controlRec) +{ + if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiSliderDragging) + { + 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.f }, 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); + 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, int *count, int *textRow) +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, @@ -3927,15 +4388,18 @@ static const char **GuiTextSplit(const char *text, int *count, int *textRow) // 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 + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 #endif #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) - #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 + #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 }; + 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; @@ -3948,7 +4412,7 @@ static const char **GuiTextSplit(const char *text, int *count, int *textRow) { buffer[i] = text[i]; if (buffer[i] == '\0') break; - else if ((buffer[i] == ';') || (buffer[i] == '\n')) + else if ((buffer[i] == delimiter) || (buffer[i] == '\n')) { result[counter] = buffer + i + 1; @@ -4100,10 +4564,12 @@ 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; + 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; + 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 }; @@ -4119,25 +4585,40 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) if (value > maxValue) value = maxValue; if (value < minValue) value = minValue; - const int range = maxValue - minValue; + const int valueRange = maxValue - minValue; int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_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 }; + 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) }; - sliderSize = (sliderSize >= scrollbar.height) ? ((int)scrollbar.height - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar - slider = RAYGUI_CLITERAL(Rectangle) { (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), (float)scrollbar.y + (int)(((float)(value - minValue)/range)*(scrollbar.height - sliderSize)), (float)bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), (float)sliderSize }; + 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 + 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)) }; - sliderSize = (sliderSize >= scrollbar.width) ? ((int)scrollbar.width - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar - slider = RAYGUI_CLITERAL(Rectangle) { (float)scrollbar.x + (int)(((float)(value - minValue)/range)*(scrollbar.width - sliderSize)), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), (float)sliderSize, (float)bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) }; + 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 @@ -4146,34 +4627,66 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds)) + if (guiSliderDragging) // Keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + { + if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); + else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); + } + } + else + { + guiSliderDragging = false; + guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; + guiSliderDragging = true; + guiSliderActive = bounds; // Store bounds as an identifier when dragging starts // Handle mouse wheel int wheel = (int)GetMouseWheelMove(); if (wheel != 0) value += wheel; + // Handle mouse button down if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + // 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; } else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - if (!isVertical) - { - Rectangle scrollArea = { arrowUpLeft.x + arrowUpLeft.width, arrowUpLeft.y, scrollbar.width, bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; - if (CheckCollisionPointRec(mousePoint, scrollArea)) value = (int)(((float)(mousePoint.x - scrollArea.x - slider.width/2)*range)/(scrollArea.width - slider.width) + minValue); - } - else - { - Rectangle scrollArea = { arrowUpLeft.x, arrowUpLeft.y+arrowUpLeft.height, bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), scrollbar.height }; - if (CheckCollisionPointRec(mousePoint, scrollArea)) value = (int)(((float)(mousePoint.y - scrollArea.y - slider.height/2)*range)/(scrollArea.height - slider.height) + minValue); - } + if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); + else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); } + + // 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 @@ -4193,14 +4706,18 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) 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 }, + GuiDrawText(isVertical? "^" : "<", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); - GuiDrawText(isVertical ? "v" : ">", RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical ? bounds.width : bounds.height, isVertical ? bounds.width : bounds.height }, + GuiDrawText(isVertical? "v" : ">", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); #else - GuiDrawText(isVertical ? "#121#" : "#118#", RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical ? bounds.width : bounds.height, isVertical ? bounds.width : bounds.height }, + GuiDrawText(isVertical? "#121#" : "#118#", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // 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 }, + GuiDrawText(isVertical? "#120#" : "#119#", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL #endif } @@ -4286,7 +4803,7 @@ const char **TextSplit(const char *text, char delimiter, int *count) // 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 + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 #endif #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 @@ -4384,107 +4901,43 @@ static const char *CodepointToUTF8(int codepoint, int *byteSize) // 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 GetCodepoint(const char *text, int *bytesProcessed) +static int GetCodepointNext(const char *text, int *codepointSize) { -/* - UTF-8 specs from https://www.ietf.org/rfc/rfc3629.txt + const char *ptr = text; + int codepoint = 0x3f; // Codepoint (defaults to '?') + *codepointSize = 1; - Char. number range | UTF-8 octet sequence - (hexadecimal) | (binary) - --------------------+--------------------------------------------- - 0000 0000-0000 007F | 0xxxxxxx - 0000 0080-0000 07FF | 110xxxxx 10xxxxxx - 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 code = 0x3f; // Codepoint (defaults to '?') - int octet = (unsigned char)(text[0]); // The first UTF8 octet - *bytesProcessed = 1; - - if (octet <= 0x7f) + // Get current codepoint and bytes processed + if (0xf0 == (0xf8 & ptr[0])) { - // Only one octet (ASCII range x00-7F) - code = text[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 ((octet & 0xe0) == 0xc0) + else if (0xe0 == (0xf0 & ptr[0])) { - // Two octets - - // [0]xC2-DF [1]UTF8-tail(x80-BF) - unsigned char octet1 = text[1]; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - if ((octet >= 0xc2) && (octet <= 0xdf)) - { - code = ((octet & 0x1f) << 6) | (octet1 & 0x3f); - *bytesProcessed = 2; - } + // 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 ((octet & 0xf0) == 0xe0) + else if (0xc0 == (0xe0 & ptr[0])) { - // Three octets - unsigned char octet1 = text[1]; - unsigned char octet2 = '\0'; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - octet2 = text[2]; - - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence - - // [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) - // [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) - // [0]xED [1]x80-9F [2]UTF8-tail(x80-BF) - // [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF) - - if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) || - ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; } - - if ((octet >= 0xe0) && (0 <= 0xef)) - { - code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); - *bytesProcessed = 3; - } + // 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 ((octet & 0xf8) == 0xf0) + else if (0x00 == (0x80 & ptr[0])) { - // Four octets - if (octet > 0xf4) return code; - - unsigned char octet1 = text[1]; - unsigned char octet2 = '\0'; - unsigned char octet3 = '\0'; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - octet2 = text[2]; - - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence - - octet3 = text[3]; - - if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence - - // [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail - // [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail - // [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail - - if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) || - ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence - - if (octet >= 0xf0) - { - code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); - *bytesProcessed = 4; - } + // 1 byte UTF-8 codepoint + codepoint = ptr[0]; + *codepointSize = 1; } - if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid - return code; + return codepoint; } #endif // RAYGUI_STANDALONE diff --git a/examples/shapes/shapes_draw_circle_sector.c b/examples/shapes/shapes_draw_circle_sector.c index c95f43643..1c283e151 100644 --- a/examples/shapes/shapes_draw_circle_sector.c +++ b/examples/shapes/shapes_draw_circle_sector.c @@ -35,8 +35,8 @@ int main(void) float outerRadius = 180.0f; float startAngle = 0.0f; float endAngle = 180.0f; - int segments = 0; - int minSegments = 4; + float segments = 10.0f; + float minSegments = 4; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -58,16 +58,16 @@ int main(void) DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f)); DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f)); - DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, Fade(MAROON, 0.3f)); - DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, Fade(MAROON, 0.6f)); + 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 //------------------------------------------------------------------------------ - startAngle = GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", NULL, startAngle, 0, 720); - endAngle = GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", NULL, endAngle, 0, 720); + GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", NULL, &startAngle, 0, 720); + GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", NULL, &endAngle, 0, 720); - outerRadius = GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", NULL, outerRadius, 0, 200); - segments = (int)GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", NULL, (float)segments, 0, 100); + GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", NULL, &outerRadius, 0, 200); + GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", NULL, &segments, 0, 100); //------------------------------------------------------------------------------ minSegments = (int)ceilf((endAngle - startAngle) / 90); diff --git a/examples/shapes/shapes_draw_rectangle_rounded.c b/examples/shapes/shapes_draw_rectangle_rounded.c index 8150e0946..a10e3c848 100644 --- a/examples/shapes/shapes_draw_rectangle_rounded.c +++ b/examples/shapes/shapes_draw_rectangle_rounded.c @@ -31,10 +31,10 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw rectangle rounded"); float roundness = 0.2f; - int width = 200; - int height = 100; - int segments = 0; - int lineThick = 1; + float width = 200.0f; + float height = 100.0f; + float segments = 0.0f; + float lineThick = 1.0f; bool drawRect = false; bool drawRoundedRect = true; @@ -61,20 +61,20 @@ int main(void) DrawRectangle(560, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f)); if (drawRect) DrawRectangleRec(rec, Fade(GOLD, 0.6f)); - if (drawRoundedRect) DrawRectangleRounded(rec, roundness, segments, Fade(MAROON, 0.2f)); - if (drawRoundedLines) DrawRectangleRoundedLines(rec,roundness, segments, (float)lineThick, Fade(MAROON, 0.4f)); + if (drawRoundedRect) DrawRectangleRounded(rec, roundness, (int)segments, Fade(MAROON, 0.2f)); + if (drawRoundedLines) DrawRectangleRoundedLines(rec, roundness, (int)segments, lineThick, Fade(MAROON, 0.4f)); // Draw GUI controls //------------------------------------------------------------------------------ - width = (int)GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", NULL, (float)width, 0, (float)GetScreenWidth() - 300); - height = (int)GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", NULL, (float)height, 0, (float)GetScreenHeight() - 50); - roundness = GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", NULL, roundness, 0.0f, 1.0f); - lineThick = (int)GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", NULL, (float)lineThick, 0, 20); - segments = (int)GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", NULL, (float)segments, 0, 60); + GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", NULL, &width, 0, (float)GetScreenWidth() - 300); + GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", NULL, &height, 0, (float)GetScreenHeight() - 50); + GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", NULL, &roundness, 0.0f, 1.0f); + GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", NULL, &lineThick, 0, 20); + GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", NULL, &segments, 0, 60); - drawRoundedRect = GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", drawRoundedRect); - drawRoundedLines = GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", drawRoundedLines); - drawRect = GuiCheckBox((Rectangle){ 640, 380, 20, 20}, "DrawRect", drawRect); + 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); diff --git a/examples/shapes/shapes_draw_ring.c b/examples/shapes/shapes_draw_ring.c index b001b9216..47327b7ad 100644 --- a/examples/shapes/shapes_draw_ring.c +++ b/examples/shapes/shapes_draw_ring.c @@ -37,7 +37,7 @@ int main(void) float startAngle = 0.0f; float endAngle = 360.0f; - int segments = 0; + float segments = 0.0f; bool drawRing = true; bool drawRingLines = false; @@ -63,23 +63,23 @@ int main(void) 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, segments, Fade(MAROON, 0.3f)); - if (drawRingLines) DrawRingLines(center, innerRadius, outerRadius, startAngle, endAngle, segments, Fade(BLACK, 0.4f)); - if (drawCircleLines) DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, Fade(BLACK, 0.4f)); + 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 //------------------------------------------------------------------------------ - startAngle = GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", NULL, startAngle, -450, 450); - endAngle = GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", NULL, endAngle, -450, 450); + GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", NULL, &startAngle, -450, 450); + GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", NULL, &endAngle, -450, 450); - innerRadius = GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", NULL, innerRadius, 0, 100); - outerRadius = GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", NULL, outerRadius, 0, 200); + GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", NULL, &innerRadius, 0, 100); + GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", NULL, &outerRadius, 0, 200); - segments = (int)GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", NULL, (float)segments, 0, 100); + GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", NULL, &segments, 0, 100); - drawRing = GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", drawRing); - drawRingLines = GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", drawRingLines); - drawCircleLines = GuiCheckBox((Rectangle){ 600, 380, 20, 20 }, "Draw CircleLines", drawCircleLines); + 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); From 2e00d16f3d3808defe76d9af75df2dd714c66af2 Mon Sep 17 00:00:00 2001 From: Charles Date: Sat, 10 Jun 2023 16:15:24 -0400 Subject: [PATCH 0471/1710] GLTF: fix segfault in animNormals memcpy when mesh.normals == NULL (#3103) --- src/rmodels.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index c4073eed3..23a5d16a0 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5201,7 +5201,9 @@ static Model LoadGLTF(const char *fileName) 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)); - memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, 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)); + } meshIndex++; // Move to next mesh } From f385d0ce1cb71338c49025db43a78ac25ed0c35f Mon Sep 17 00:00:00 2001 From: Chema Guerra <33331554+chemaguerra@users.noreply.github.com> Date: Mon, 12 Jun 2023 08:18:31 +0200 Subject: [PATCH 0472/1710] Continuation of support for ES3/WebGL2 (#3107) * Continuation of support for ES3/WebGL2 * GetTouchPointState() * Amends to the WebGL2 PR --------- Co-authored-by: root Co-authored-by: chemguerra --- src/Makefile | 1 + src/rlgl.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index c6cd30e81..fc4c5b0c7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -246,6 +246,7 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 GRAPHICS = GRAPHICS_API_OPENGL_ES2 + #GRAPHICS = GRAPHICS_API_OPENGL_ES3 # Uncomment to use ES3/WebGL2 (preliminary support). endif ifeq ($(PLATFORM),PLATFORM_ANDROID) # By default use OpenGL ES 2.0 on Android diff --git a/src/rlgl.h b/src/rlgl.h index 5ba2593f2..8c101a79c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -148,7 +148,8 @@ !defined(GRAPHICS_API_OPENGL_21) && \ !defined(GRAPHICS_API_OPENGL_33) && \ !defined(GRAPHICS_API_OPENGL_43) && \ - !defined(GRAPHICS_API_OPENGL_ES2) + !defined(GRAPHICS_API_OPENGL_ES2) && \ + !defined(GRAPHICS_API_OPENGL_ES3) #define GRAPHICS_API_OPENGL_33 #endif @@ -1711,7 +1712,7 @@ void rlDisableFramebuffer(void) // NOTE: One color buffer is always active by default void rlActiveDrawBuffers(int count) { -#if (defined(GRAPHICS_API_OPENGL_33) && defined(RLGL_RENDER_TEXTURES_HINT)) +#if ((defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT)) // 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; @@ -1723,6 +1724,16 @@ void rlActiveDrawBuffers(int count) 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, @@ -1731,9 +1742,14 @@ void rlActiveDrawBuffers(int count) 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"); @@ -2201,7 +2217,29 @@ void rlLoadExtensions(void *loader) #endif // GRAPHICS_API_OPENGL_33 -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES3) + // Register supported extensions flags + // OpenGL ES 3.0 extensions supported by default + RLGL.ExtSupported.vao = true; + RLGL.ExtSupported.instancing = true; + RLGL.ExtSupported.texNPOT = true; + RLGL.ExtSupported.texFloat32 = true; + RLGL.ExtSupported.texDepth = true; + RLGL.ExtSupported.texDepthWebGL = true; + RLGL.ExtSupported.maxDepthBits = 24; + RLGL.ExtSupported.texAnisoFilter = true; + RLGL.ExtSupported.texMirrorClamp = true; + // TODO: Make sure that the ones above are actually present by default + // TODO: Check for these... + // RLGL.ExtSupported.texCompDXT + // RLGL.ExtSupported.texCompETC1 + // RLGL.ExtSupported.texCompETC2 + // RLGL.ExtSupported.texCompPVRT + // RLGL.ExtSupported.texCompASTC + // RLGL.ExtSupported.computeShader + // RLGL.ExtSupported.ssbo + // RLGL.ExtSupported.maxAnisotropyLevel +#elif defined(GRAPHICS_API_OPENGL_ES2) #if defined(PLATFORM_DESKTOP) // TODO: Support OpenGL ES 3.0 @@ -3057,7 +3095,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F unsigned int glInternalFormat = GL_DEPTH_COMPONENT; -#if defined(GRAPHICS_API_OPENGL_ES2) +#if (defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_ES3)) // WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT) // while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer) @@ -3214,10 +3252,16 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; #if !defined(GRAPHICS_API_OPENGL_11) + #if defined(GRAPHICS_API_OPENGL_ES3) + case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F_EXT; *glFormat = GL_RED_EXT; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F_EXT; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F_EXT; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + #else case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float #endif + #endif #elif defined(GRAPHICS_API_OPENGL_33) case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; From 7392c4b0c592b45d113c7c869fbfdfc8d8de3d59 Mon Sep 17 00:00:00 2001 From: iacore <74560659+iacore@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:46:10 +0000 Subject: [PATCH 0473/1710] Better examples/core_input_gamepad (#3110) * examples/core_input_gamepad: Add visuals for LT,RT * examples/core_input_gamepad: arrows left/right to choose gamepad * Style change --- examples/core/core_input_gamepad.c | 108 ++++++++++++++++------------- 1 file changed, 61 insertions(+), 47 deletions(-) diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index cd0c867d7..1eb516e85 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -49,6 +49,8 @@ int main(void) SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + int gamepad = 0; // which gamepad to display + // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { @@ -63,102 +65,114 @@ int main(void) ClearBackground(RAYWHITE); - if (IsGamepadAvailable(0)) - { - DrawText(TextFormat("GP1: %s", GetGamepadName(0)), 10, 10, 10, BLACK); + if (IsKeyPressed(KEY_LEFT) && gamepad > 0) gamepad--; + if (IsKeyPressed(KEY_RIGHT)) gamepad++; - if (TextIsEqual(GetGamepadName(0), XBOX360_NAME_ID) || TextIsEqual(GetGamepadName(0), XBOX360_LEGACY_NAME_ID)) + if (IsGamepadAvailable(gamepad)) + { + DrawText(TextFormat("GP%d: %s", gamepad, GetGamepadName(gamepad)), 10, 10, 10, BLACK); + + if (true) { DrawTexture(texXboxPad, 0, 0, DARKGRAY); // Draw buttons: xbox home - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(394, 89, 19, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(394, 89, 19, RED); // Draw buttons: basic - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawCircle(436, 150, 9, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawCircle(352, 150, 9, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(501, 151, 15, BLUE); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(536, 187, 15, LIME); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(572, 151, 15, MAROON); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(536, 115, 15, GOLD); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawCircle(436, 150, 9, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawCircle(352, 150, 9, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(501, 151, 15, BLUE); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(536, 187, 15, LIME); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(572, 151, 15, MAROON); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(536, 115, 15, GOLD); // Draw buttons: d-pad DrawRectangle(317, 202, 19, 71, BLACK); DrawRectangle(293, 228, 69, 19, BLACK); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(317, 202, 19, 26, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(317, 202 + 45, 19, 26, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(292, 228, 25, 19, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(292 + 44, 228, 26, 19, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(317, 202, 19, 26, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(317, 202 + 45, 19, 26, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(292, 228, 25, 19, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(292 + 44, 228, 26, 19, RED); // Draw buttons: left-right back - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawCircle(259, 61, 20, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawCircle(536, 61, 20, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawCircle(259, 61, 20, RED); + 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(0, GAMEPAD_AXIS_LEFT_X)*20), - 152 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y)*20), 25, BLACK); + DrawCircle(259 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X)*20), + 152 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y)*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(0, GAMEPAD_AXIS_RIGHT_X)*20), - 237 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y)*20), 25, BLACK); + DrawCircle(461 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X)*20), + 237 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y)*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(0, GAMEPAD_AXIS_LEFT_TRIGGER))/2)*70), RED); - DrawRectangle(604, 30, 15, (int)(((1 + GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER))/2)*70), RED); + 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); - //DrawText(TextFormat("Xbox axis LT: %02.02f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER)), 10, 40, 10, BLACK); - //DrawText(TextFormat("Xbox axis RT: %02.02f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER)), 10, 60, 10, BLACK); + //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(0), PS3_NAME_ID)) + else if (TextIsEqual(GetGamepadName(gamepad), PS3_NAME_ID)) { DrawTexture(texPs3Pad, 0, 0, DARKGRAY); // Draw buttons: ps - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(396, 222, 13, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(396, 222, 13, RED); // Draw buttons: basic - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawRectangle(328, 170, 32, 13, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawTriangle((Vector2){ 436, 168 }, (Vector2){ 436, 185 }, (Vector2){ 464, 177 }, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(557, 144, 13, LIME); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(586, 173, 13, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(557, 203, 13, VIOLET); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(527, 173, 13, PINK); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawRectangle(328, 170, 32, 13, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawTriangle((Vector2){ 436, 168 }, (Vector2){ 436, 185 }, (Vector2){ 464, 177 }, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(557, 144, 13, LIME); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(586, 173, 13, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(557, 203, 13, VIOLET); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(527, 173, 13, PINK); // Draw buttons: d-pad DrawRectangle(225, 132, 24, 84, BLACK); DrawRectangle(195, 161, 84, 25, BLACK); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(225, 132, 24, 29, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(225, 132 + 54, 24, 30, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(195, 161, 30, 25, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(195 + 54, 161, 30, 25, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(225, 132, 24, 29, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(225, 132 + 54, 24, 30, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(195, 161, 30, 25, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(195 + 54, 161, 30, 25, RED); // Draw buttons: left-right back buttons - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawCircle(239, 82, 20, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawCircle(557, 82, 20, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawCircle(239, 82, 20, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawCircle(557, 82, 20, RED); // Draw axis: left joystick - DrawCircle(319, 255, 35, BLACK); + Color leftGamepadColor = BLACK; + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED; + DrawCircle(319, 255, 35, leftGamepadColor); DrawCircle(319, 255, 31, LIGHTGRAY); - DrawCircle(319 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) * 20), - 255 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) * 20), 25, BLACK); + DrawCircle(319 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X) * 20), + 255 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y) * 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(0, GAMEPAD_AXIS_RIGHT_X) * 20), - 255 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 20), 25, BLACK); + DrawCircle(475 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X) * 20), + 255 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y) * 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(0, GAMEPAD_AXIS_LEFT_TRIGGER)) / 2) * 70), RED); - DrawRectangle(611, 48, 15, (int)(((1 - GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER)) / 2) * 70), RED); + 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); } else { @@ -179,7 +193,7 @@ int main(void) } else { - DrawText("GP1: NOT DETECTED", 10, 10, 10, GRAY); + DrawText(TextFormat("GP%d: NOT DETECTED", gamepad), 10, 10, 10, GRAY); DrawTexture(texXboxPad, 0, 0, LIGHTGRAY); } From a0a18384cb2505437695c60a9c917ca13a82f0d4 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 16 Jun 2023 16:34:47 +0200 Subject: [PATCH 0474/1710] Fix typo --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index ee682e8b0..2701fe0e3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -915,7 +915,7 @@ void InitWindow(int width, int height, const char *title) #endif #if defined(PLATFORM_WEB) - // Setup callback funtions for the DOM events + // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review From 2209e5b0caa0b8b55bfea104b7102a2990797297 Mon Sep 17 00:00:00 2001 From: lesleyrs <19632758+lesleyrs@users.noreply.github.com> Date: Fri, 16 Jun 2023 18:08:10 +0200 Subject: [PATCH 0475/1710] fix window flags order (#3114) --- src/rcore.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 2701fe0e3..27b60ae4a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1492,13 +1492,6 @@ void ClearWindowState(unsigned int flags) CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; } - // State change: FLAG_WINDOW_UNDECORATED - if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - } - // State change: FLAG_WINDOW_HIDDEN if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) { @@ -1518,6 +1511,13 @@ void ClearWindowState(unsigned int flags) 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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + } + // State change: FLAG_WINDOW_UNFOCUSED if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) { From 830e328df09bd597d9a0dbb974f5e7b54c6219f1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Jun 2023 16:48:18 +0200 Subject: [PATCH 0476/1710] Remove trailing spaces --- src/raylib.h | 2 +- src/rlgl.h | 2 +- src/rmodels.c | 2 +- src/rtextures.c | 14 +++++++------- src/utils.c | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index f6a48bc73..cceaa2101 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1277,7 +1277,7 @@ RLAPI void ImageMipmaps(Image *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 RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally -RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) +RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint diff --git a/src/rlgl.h b/src/rlgl.h index 8c101a79c..a104175c2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -390,7 +390,7 @@ typedef enum { RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) RL_OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100) - RL_OPENGL_ES_30 // OpenGL ES 3.0 (GLSL 300 es) + RL_OPENGL_ES_30 // OpenGL ES 3.0 (GLSL 300 es) } rlGlVersion; // Trace log level diff --git a/src/rmodels.c b/src/rmodels.c index 23a5d16a0..73fb02353 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5379,7 +5379,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in 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); animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); diff --git a/src/rtextures.c b/src/rtextures.c index 7d76616c1..b6186d9e8 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -610,7 +610,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS { unsigned char *fileData = NULL; *dataSize = 0; - + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL; #if defined(SUPPORT_IMAGE_EXPORT) @@ -629,7 +629,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS #endif #endif - + return fileData; } @@ -713,7 +713,7 @@ Image GenImageColor(int width, int height, Color color) #if defined(SUPPORT_IMAGE_GENERATION) // Generate image: linear gradient -// The direction value specifies the direction of the gradient (in degrees) +// The direction value specifies the direction of the gradient (in degrees) // with 0 being vertical (from top to bottom), 90 being horizontal (from left to right). // The gradient effectively rotates counter-clockwise by the specified amount. Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end) @@ -898,21 +898,21 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float { float nx = (float)(x + offsetX)*(scale/(float)width); float ny = (float)(y + offsetY)*(scale/(float)height); - + // Basic perlin noise implementation (not used) //float p = (stb_perlin_noise3(nx, ny, 0.0f, 0, 0, 0); - + // Calculate a better perlin noise using fbm (fractal brownian motion) // Typical values to start playing with: // lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) // gain = 0.5 -- relative weighting applied to each successive octave // octaves = 6 -- number of "octaves" of noise3() to sum float p = stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6); - + // Clamp between -1.0f and 1.0f if (p < -1.0f) p = -1.0f; if (p > 1.0f) p = 1.0f; - + // We need to normalize the data from [-1..1] to [0..1] float np = (p + 1.0f)/2.0f; diff --git a/src/utils.c b/src/utils.c index aa2bfc40e..ac57a2157 100644 --- a/src/utils.c +++ b/src/utils.c @@ -348,7 +348,7 @@ char *LoadFileText(const char *fileName) 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); From 3a90acf08ee97ba42bc21decfc52b41c7ef4d68d Mon Sep 17 00:00:00 2001 From: Dante Catalfamo <43040593+dantecatalfamo@users.noreply.github.com> Date: Sun, 18 Jun 2023 05:48:50 -0400 Subject: [PATCH 0477/1710] Add options to zig compile (#3115) * Add options to zig compile options Support for compiling with raygui, raymath, and physac. Also outputs the required headers. Raygui should be located `../raygui` relative to the repo root Physac should be located `../physac` relative to the repo root This behavior matches options in the Makefile * Move Options struct * Remove physac, explicit raymath, always copy rlgl.h and raymath.h * Remove unused options from build.zig * Add srcdir as include path for raygui.h --- src/build.zig | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/build.zig b/src/build.zig index 59d8241e7..8485322d5 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); // This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 -pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { +pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", "-D_GNU_SOURCE", @@ -28,6 +28,16 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built srcdir ++ "/utils.c", }, raylib_flags); + var gen_step = std.build.Step.WriteFile.create(b); + raylib.step.dependOn(&gen_step.step); + + if (options.raygui) { + _ = gen_step.add(srcdir ++ "/raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); + raylib.addCSourceFile(srcdir ++ "/raygui.c", raylib_flags); + raylib.addIncludePath(srcdir); + raylib.addIncludePath(srcdir ++ "/../../raygui/src"); + } + switch (target.getOsTag()) { .windows => { raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); @@ -105,6 +115,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built return raylib; } +const Options = struct { + raygui: bool = false, +}; + pub fn build(b: *std.Build) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which @@ -116,8 +130,20 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const lib = addRaylib(b, target, optimize); + const raygui = b.option(bool, "raygui", "Compile with raygui support"); + + const lib = addRaylib(b, target, optimize, .{ + .raygui = raygui orelse false, + }); + lib.installHeader("src/raylib.h", "raylib.h"); + lib.installHeader("src/raymath.h", "raymath.h"); + lib.installHeader("src/rlgl.h", "rlgl.h"); + + if (raygui orelse false) { + lib.installHeader("../raygui/src/raygui.h", "raygui.h"); + } + b.installArtifact(lib); } From 5e1a81555ca130e2c6544add0e2391a8763e7e2a Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 20 Jun 2023 11:01:57 +0200 Subject: [PATCH 0478/1710] Review `android_main()` in rcore.c (#3121) --- src/rcore.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 27b60ae4a..d02ea2b9b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -717,17 +717,17 @@ void android_main(struct android_app *app) char arg0[] = "raylib"; // NOTE: argv[] are mutable CORE.Android.app = app; - // NOTE: Return codes != 0 are skipped - (void)main(1, (char *[]) { arg0, NULL }); + // NOTE: We get the main return for exit() + int ret = main(1, (char *[]) { arg0, NULL }); - // Finish native activity - ANativeActivity_finish(CORE.Android.app->activity); + // Request to end the native activity + ANativeActivity_finish(app->activity); // Android ALooper_pollAll() variables int pollResult = 0; int pollEvents = 0; - // Wait for app events to close + // Waiting for application events before complete finishing while (!CORE.Android.app->destroyRequested) { while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) @@ -736,8 +736,11 @@ void android_main(struct android_app *app) } } - // WARNING: Check for deallocation and ensure no other processes are running from the application - exit(0); // Closes the application completely without going through Java + // WARNING: Make sure you free resources properly and no other process is running from Java code or other. + // NOTE: You can use JNI to call a NativeLoader method (which will call finish() from the UI thread) + // to handle the full close from Java, without using exit(0) like here. + + exit(ret); // Close the application directly, without going through Java } // NOTE: Add this to header (if apps really need it) From fa698fb05e7687c8a43b76d9e1ca9c6a12d99b59 Mon Sep 17 00:00:00 2001 From: Ian Rash Date: Sat, 24 Jun 2023 04:22:23 -0700 Subject: [PATCH 0479/1710] Update BINDINGS.md for Crystal (#3132) Just updated --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index a60979fc4..272bc2da5 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -13,7 +13,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | | claylib/wrap | **4.5** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | -| raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | +| raylib-cr | **4.6-dev (5e1a81)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | From 974460b0723c4ab550779fbc46fb88f4cf6cac41 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 24 Jun 2023 13:26:43 +0200 Subject: [PATCH 0480/1710] REVIEWED: `rlLoadShaderBuffer()` #3104 --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index a104175c2..470295b0a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4143,7 +4143,7 @@ unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHi glGenBuffers(1, &ssbo); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY); - glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, 0); + if (data == NULL) glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL); // Clear buffer data to 0 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); #endif From 2d518bfbcdaf673c813d6fc56218d906678f42be Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 24 Jun 2023 13:32:13 +0200 Subject: [PATCH 0481/1710] REVIEWED: `ProcessMaterialsOBJ()` #3125 --- src/rmodels.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 73fb02353..49f922695 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1857,35 +1857,34 @@ bool ExportMesh(Mesh mesh, const char *fileName) #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) // Process obj materials -static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount) +static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount) { - // Init model materials + // Init model mats for (int m = 0; m < materialCount; m++) { // Init material to default // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE - rayMaterials[m] = LoadMaterialDefault(); + materials[m] = LoadMaterialDefault(); // Get default texture, in case no texture is defined // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd + 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; - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; - rayMaterials[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 (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks - rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3]; - rayMaterials[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; - if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump - rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; - rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[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]; - rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3]; - - if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp + if (mats[m].displacement_texname != NULL) materials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(mats[m].displacement_texname); //char *displacement_texname; // disp } } #endif From 0c126af7171e51fff9f94c4f2e498f43f60d617b Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sat, 24 Jun 2023 14:38:09 -0700 Subject: [PATCH 0482/1710] casting warnings in rtextures (#3134) --- src/rtextures.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index b6186d9e8..2d422b179 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -721,8 +721,8 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start, Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); float radianDirection = (float)(90 - direction)/180.f*3.14159f; - float cosDir = cos(radianDirection); - float sinDir = sin(radianDirection); + float cosDir = cosf(radianDirection); + float sinDir = sinf(radianDirection); for (int i = 0; i < width; i++) { @@ -812,7 +812,7 @@ Image GenImageGradientSquare(int width, int height, float density, Color inner, float normalizedDistY = distY / centerY; // Calculate the total normalized Manhattan distance - float manhattanDist = fmax(normalizedDistX, normalizedDistY); + float manhattanDist = fmaxf(normalizedDistX, normalizedDistY); // Subtract the density from the manhattanDist, then divide by (1 - density) // This makes the gradient start from the center when density is 0, and from the edge when density is 1 @@ -2157,11 +2157,11 @@ void ImageRotate(Image *image, int degrees) else { float rad = degrees*PI/180.0f; - float sinRadius = sin(rad); - float cosRadius = cos(rad); + float sinRadius = sinf(rad); + float cosRadius = cosf(rad); - int width = fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius); - int height = fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius); + int width = (int)(fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius)); + int height = (int)(fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius)); int bytesPerPixel = GetPixelDataSize(1, 1, image->format); unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width*height, bytesPerPixel); From 5834e970ebb50edc95cb5b5dbc6dd772f99c366f Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:03:42 +0100 Subject: [PATCH 0483/1710] Update BINDINGS.md (#3138) Change C3 to Zlib Update Raylib-nelua's name as Raylib.nelua to reflect repo change. Update Raylib.nelua licence to Zlib --- BINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 272bc2da5..f040599ca 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -15,7 +15,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.6-dev (5e1a81)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | -| raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | +| raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | Zlib | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | 4.2 | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | @@ -40,7 +40,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | -| Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | +| Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | From ceafbcf9d2d722886c40d0ba12eaad2f232a2782 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 28 Jun 2023 00:37:24 +0200 Subject: [PATCH 0484/1710] REVIEWED: `SetShapesTexture()`, allow reseting --- src/rshapes.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 278886423..d726ff0e8 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -80,7 +80,7 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (usually a white pixel) +Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (white pixel loaded by rlgl) Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing //---------------------------------------------------------------------------------- @@ -97,8 +97,19 @@ static float EaseCubicInOut(float t, float b, float c, float d); // Cubic eas // defining a font char white rectangle would allow drawing everything in a single draw call void SetShapesTexture(Texture2D texture, Rectangle source) { - texShapes = texture; - texShapesRec = source; + // Reset texture to default pixel if required + // WARNING: Shapes texture should be probably better validated, + // it can break the rendering of all shapes if missused + if ((texture.id == 0) || (source.width == 0) || (source.height == 0)) + { + texShapes = (Texture2D){ 1, 1, 1, 1, 7 }; + texShapesRec = (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }; + } + else + { + texShapes = texture; + texShapesRec = source; + } } // Draw a pixel From e190b7eee9199b681a8c50fb69f2fce07e92c7af Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 30 Jun 2023 09:47:16 +0200 Subject: [PATCH 0485/1710] UPDATED: `sdefl` and `sinfl` compression libraries --- src/external/sdefl.h | 195 ++++++++++++++++++++++++++++++------------- src/external/sinfl.h | 28 ++++--- 2 files changed, 155 insertions(+), 68 deletions(-) diff --git a/src/external/sdefl.h b/src/external/sdefl.h index 56539a56e..36015b95b 100644 --- a/src/external/sdefl.h +++ b/src/external/sdefl.h @@ -71,7 +71,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke +Copyright (c) 2020-2023 Micha Mettke 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 @@ -125,7 +125,7 @@ extern "C" { #define SDEFL_MIN_MATCH 4 #define SDEFL_BLK_MAX (256*1024) -#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) +#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX+2)/3) #define SDEFL_SYM_MAX (288) #define SDEFL_OFF_MAX (32) @@ -185,6 +185,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_MAX_CODE_LEN (15) #define SDEFL_SYM_BITS (10u) #define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) +#define SDEFL_RAW_BLK_SIZE (65535) #define SDEFL_LIT_LEN_CODES (14) #define SDEFL_OFF_CODES (15) #define SDEFL_PRE_CODES (7) @@ -192,6 +193,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_EOB (256) #define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) +#define sdefl_div_round_up(n,d) (((n)+((d)-1))/(d)) static int sdefl_ilog2(int n) { @@ -438,12 +440,12 @@ sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items, } while (run_start != total); cnt->items = (int)(at - items); } -struct sdefl_match_codes { +struct sdefl_match_codest { int ls, lc; int dc, dx; }; static void -sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { +sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; static const unsigned char lslot[258+1] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, @@ -471,6 +473,44 @@ sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; } +enum sdefl_blk_type { + SDEFL_BLK_UCOMPR, + SDEFL_BLK_DYN +}; +static enum sdefl_blk_type +sdefl_blk_type(const struct sdefl *s, int blk_len, int pre_item_len, + const unsigned *pre_freq, const unsigned char *pre_len) { + static const unsigned char x_pre_bits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + static const unsigned char x_len_bits[] = {0,0,0,0,0,0,0,0, 1,1,1,1,2,2,2,2, + 3,3,3,3,4,4,4,4, 5,5,5,5,0}; + static const unsigned char x_off_bits[] = {0,0,0,0,1,1,2,2, 3,3,4,4,5,5,6,6, + 7,7,8,8,9,9,10,10, 11,11,12,12,13,13}; + + int dyn_cost = 0; + int fix_cost = 0; + int sym = 0; + + dyn_cost += 5 + 5 + 4 + (3 * pre_item_len); + for (sym = 0; sym < SDEFL_PRE_MAX; sym++) + dyn_cost += pre_freq[sym] * (x_pre_bits[sym] + pre_len[sym]); + for (sym = 0; sym < 256; sym++) + dyn_cost += s->freq.lit[sym] * s->cod.len.lit[sym]; + dyn_cost += s->cod.len.lit[SDEFL_EOB]; + for (sym = 257; sym < 286; sym++) + dyn_cost += s->freq.lit[sym] * (x_len_bits[sym - 257] + s->cod.len.lit[sym]); + for (sym = 0; sym < 30; sym++) + dyn_cost += s->freq.off[sym] * (x_off_bits[sym] + s->cod.len.off[sym]); + + fix_cost += 8*(5 * sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE) + blk_len + 1 + 2); + return (dyn_cost < fix_cost) ? SDEFL_BLK_DYN : SDEFL_BLK_UCOMPR; +} +static void +sdefl_put16(unsigned char **dst, unsigned short x) { + unsigned char *val = *dst; + val[0] = (unsigned char)(x & 0xff); + val[1] = (unsigned char)(x >> 8); + *dst = val + 2; +} static void sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; @@ -479,7 +519,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; - struct sdefl_match_codes cod; + struct sdefl_match_codest cod; sdefl_match_codes(&cod, dist, len); sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); @@ -488,7 +528,8 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { } static void sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, - const unsigned char *in) { + const unsigned char *in, int blk_begin, int blk_end) { + int blk_len = blk_end - blk_begin; int j, i = 0, item_cnt = 0; struct sdefl_symcnt symcnt = {0}; unsigned codes[SDEFL_PRE_MAX]; @@ -498,7 +539,7 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, 4,12,3,13,2,14,1,15}; - /* huffman codes */ + /* calculate huffman codes */ s->freq.lit[SDEFL_EOB]++; sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); @@ -509,35 +550,58 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, break; } } - /* block header */ - sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ - sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ - sdefl_put(dst, s, symcnt.lit - 257, 5); - sdefl_put(dst, s, symcnt.off - 1, 5); - sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) { - sdefl_put(dst, s, lens[perm[i]], 3); - } - for (i = 0; i < symcnt.items; ++i) { - unsigned sym = items[i] & 0x1F; - sdefl_put(dst, s, (int)codes[sym], lens[sym]); - if (sym < 16) continue; - if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); - else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); - else sdefl_put(dst, s, items[i] >> 5, 7); - } - /* block sequences */ - for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) { - for (j = 0; j < s->seq[i].len; ++j) { - int c = in[s->seq[i].off + j]; - sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + /* write block */ + switch (sdefl_blk_type(s, blk_len, item_cnt, freqs, lens)) { + case SDEFL_BLK_UCOMPR: { + /* uncompressed blocks */ + int n = sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE); + for (i = 0; i < n; ++i) { + int fin = is_last && (i + 1 == n); + int amount = blk_len < SDEFL_RAW_BLK_SIZE ? blk_len : SDEFL_RAW_BLK_SIZE; + sdefl_put(dst, s, !!fin, 1); /* block */ + sdefl_put(dst, s, 0x00, 2); /* stored block */ + if (s->bitcnt) { + sdefl_put(dst, s, 0x00, 8 - s->bitcnt); } - } else { - sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + assert(s->bitcnt == 0); + sdefl_put16(dst, (unsigned short)amount); + sdefl_put16(dst, ~(unsigned short)amount); + memcpy(*dst, in + blk_begin + i * SDEFL_RAW_BLK_SIZE, amount); + *dst = *dst + amount; + blk_len -= amount; } - } - sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + } break; + case SDEFL_BLK_DYN: { + /* dynamic huffman block */ + sdefl_put(dst, s, !!is_last, 1); /* block */ + sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ + sdefl_put(dst, s, symcnt.lit - 257, 5); + sdefl_put(dst, s, symcnt.off - 1, 5); + sdefl_put(dst, s, item_cnt - 4, 4); + for (i = 0; i < item_cnt; ++i) { + sdefl_put(dst, s, lens[perm[i]], 3); + } + for (i = 0; i < symcnt.items; ++i) { + unsigned sym = items[i] & 0x1F; + sdefl_put(dst, s, (int)codes[sym], lens[sym]); + if (sym < 16) continue; + if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); + else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); + else sdefl_put(dst, s, items[i] >> 5, 7); + } + /* block sequences */ + for (i = 0; i < s->seq_cnt; ++i) { + if (s->seq[i].off >= 0) { + for (j = 0; j < s->seq[i].len; ++j) { + int c = in[s->seq[i].off + j]; + sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + } + } else { + sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } + } + sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + } break;} memset(&s->freq, 0, sizeof(s->freq)); s->seq_cnt = 0; } @@ -550,8 +614,12 @@ sdefl_seq(struct sdefl *s, int off, int len) { } static void sdefl_reg_match(struct sdefl *s, int off, int len) { - struct sdefl_match_codes cod; + struct sdefl_match_codest cod; sdefl_match_codes(&cod, off, len); + + assert(cod.lc < SDEFL_SYM_MAX); + assert(cod.dc < SDEFL_OFF_MAX); + s->freq.lit[cod.lc]++; s->freq.off[cod.dc]++; } @@ -560,22 +628,35 @@ struct sdefl_match { int len; }; static void -sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, - int chain_len, int max_match, const unsigned char *in, int p) { - int i = s->tbl[sdefl_hash32(&in[p])]; - int limit = ((p-SDEFL_WIN_SIZ)tbl[sdefl_hash32(in + p)]; + int limit = ((p - SDEFL_WIN_SIZ) < SDEFL_NIL) ? SDEFL_NIL : (p-SDEFL_WIN_SIZ); + + assert(p < e); + assert(p + max_match <= e); while (i > limit) { - if (in[i+m->len] == in[p+m->len] && - (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))){ + assert(i + m->len < e); + assert(p + m->len < e); + assert(i + SDEFL_MIN_MATCH < e); + assert(p + SDEFL_MIN_MATCH < e); + + if (in[i + m->len] == in[p + m->len] && + (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))) { int n = SDEFL_MIN_MATCH; - while (n < max_match && in[i+n] == in[p+n]) n++; + while (n < max_match && in[i + n] == in[p + n]) { + assert(i + n < e); + assert(p + n < e); + n++; + } if (n > m->len) { m->len = n, m->off = p - i; - if (n == max_match) break; + if (n == max_match) + break; } } if (!(--chain_len)) break; - i = s->prv[i&SDEFL_WIN_MSK]; + i = s->prv[i & SDEFL_WIN_MSK]; } } static int @@ -588,19 +669,20 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, for (n = 0; n < SDEFL_HASH_SIZ; ++n) { s->tbl[n] = SDEFL_NIL; } - do {int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; + do {int blk_begin = i; + int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; while (i < blk_end) { struct sdefl_match m = {0}; int left = blk_end - i; - int max_match = (left >= SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; + int max_match = (left > SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; int run = 1, inc = 1, run_inc = 0; if (max_match > SDEFL_MIN_MATCH) { - sdefl_fnd(&m, s, max_chain, max_match, in, i); + sdefl_fnd(&m, s, max_chain, max_match, in, i, in_len); } - if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ + if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len + 1 < nice_match){ struct sdefl_match m2 = {0}; - sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); + sdefl_fnd(&m2, s, max_chain, m.len + 1, in, i + 1, in_len); m.len = (m2.len > m.len) ? 0 : m.len; } if (m.len >= SDEFL_MIN_MATCH) { @@ -636,12 +718,12 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, sdefl_seq(s, i - litlen, litlen); litlen = 0; } - sdefl_flush(&q, s, blk_end == in_len, in); + sdefl_flush(&q, s, blk_end == in_len, in, blk_begin, blk_end); } while (i < in_len); - - if (s->bitcnt > 0) + if (s->bitcnt) { sdefl_put(&q, s, 0x00, 8 - s->bitcnt); - + } + assert(s->bitcnt == 0); return (int)(q - out); } extern int @@ -701,9 +783,8 @@ zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { } extern int sdefl_bound(int len) { - int a = 128 + (len * 110) / 100; - int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; - return (a > b) ? a : b; + int max_blocks = 1 + sdefl_div_round_up(len, SDEFL_RAW_BLK_SIZE); + int bound = 5 * max_blocks + len + 1 + 4 + 8; + return bound; } #endif /* SDEFL_IMPLEMENTATION */ - diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 915da9d23..8979fcd7f 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -72,7 +72,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke +Copyright (c) 2020-2023 Micha Mettke 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 @@ -400,17 +400,21 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) } break; case stored: { /* uncompressed block */ - int len, nlen; - sinfl_refill(&s); + unsigned len, nlen; sinfl__get(&s,s.bitcnt & 7); - len = sinfl__get(&s,16); - nlen = sinfl__get(&s,16); - in -= 2; s.bitcnt = 0; + len = (unsigned short)sinfl__get(&s,16); + nlen = (unsigned short)sinfl__get(&s,16); + s.bitptr -= s.bitcnt / 8; + s.bitbuf = s.bitcnt = 0; - if (len > (e-in) || !len) + if ((unsigned short)len != (unsigned short)~nlen) return (int)(out-o); - memcpy(out, in, (size_t)len); - in += len, out += len; + if (len > (e - s.bitptr) || !len) + return (int)(out-o); + + memcpy(out, s.bitptr, (size_t)len); + s.bitptr += len, out += len; + if (last) return (int)(out-o); state = hdr; } break; case fixed: { @@ -443,8 +447,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) /* decode code lengths */ for (n = 0; n < nlit + ndist;) { + int sym = 0; sinfl_refill(&s); - int sym = sinfl_decode(&s, hlens, 7); + sym = sinfl_decode(&s, hlens, 7); switch (sym) {default: lens[n++] = (unsigned char)sym; break; case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; @@ -458,8 +463,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) case blk: { /* decompress block */ while (1) { + int sym; sinfl_refill(&s); - int sym = sinfl_decode(&s, s.lits, 10); + sym = sinfl_decode(&s, s.lits, 10); if (sym < 256) { /* literal */ if (sinfl_unlikely(out >= oe)) { From df90da0b3750b654c1ec660f83e461aae03a3b6b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 10:50:40 +0200 Subject: [PATCH 0486/1710] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index d02ea2b9b..bba8f5efe 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3219,7 +3219,7 @@ const char *GetApplicationDirectory(void) #if defined(_WIN32) int len = 0; -#if defined (UNICODE) +#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); From 48e2663d03a74b26d86e06057d659ddc9b1b7693 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 10:51:39 +0200 Subject: [PATCH 0487/1710] REVIEWED: Issue #3105 --- src/rtext.c | 2 +- src/rtextures.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index dce838c29..61f2e04cb 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1227,7 +1227,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; - textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure + textSize.x = (tempTextWidth + (float)((tempByteCounter - 1)*spacing))*scaleFactor; textSize.y = textHeight*scaleFactor; return textSize; diff --git a/src/rtextures.c b/src/rtextures.c index 2d422b179..bc53e1cec 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3290,8 +3290,7 @@ void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSiz if (GetFontDefault().texture.id == 0) LoadFontDefault(); Vector2 position = { (float)posX, (float)posY }; - // NOTE: For default font, spacing is set to desired font size / default font size (10) - ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color); // WARNING: Module required: rtext + ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, 1.0f, color); // WARNING: Module required: rtext #else TRACELOG(LOG_WARNING, "IMAGE: ImageDrawText() requires module: rtext"); #endif From 3e4e4b32fdcaa2cff8854e11ff2179af4ff2afe9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 10:52:20 +0200 Subject: [PATCH 0488/1710] WARNING: BREAKING: ADDED: `SetTextLineSpacing()` --- src/raylib.h | 1 + src/rtext.c | 32 +++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index cceaa2101..539a31355 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1381,6 +1381,7 @@ RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float f RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) // Text font info functions +RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found diff --git a/src/rtext.c b/src/rtext.c index 61f2e04cb..cb609cf62 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -111,7 +111,8 @@ static Font defaultFont = { 0 }; // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) -static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) +static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) +static int textLineSpacing = 15; // Text vertical line spacing in pixels #endif #if defined(SUPPORT_DEFAULT_FONT) @@ -123,7 +124,6 @@ extern void UnloadFontDefault(void); // Module Functions Definition //---------------------------------------------------------------------------------- #if defined(SUPPORT_DEFAULT_FONT) - // Load raylib default font extern void LoadFontDefault(void) { @@ -702,6 +702,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC totalWidth += chars[i].image.width + 2*padding; } +//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE +#if defined(SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE) int rowCount = 0; int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images @@ -711,6 +713,12 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC imageSize *= 2; // Double the size of image (to keep POT) rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size } +#else + // No need for a so-conservative atlas generation + float totalArea = totalWidth*fontSize*1.3f; + float imageMinSize = sqrtf(totalArea); + int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2))); +#endif atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height @@ -1071,9 +1079,8 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f if (codepoint == '\n') { - // NOTE: Fixed line spacing of 1.5 line-height - // TODO: Support custom line spacing defined by user - textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor); + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup + textOffsetY += textLineSpacing; textOffsetX = 0.0f; } else @@ -1143,9 +1150,8 @@ void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 pos if (codepoints[i] == '\n') { - // NOTE: Fixed line spacing of 1.5 line-height - // TODO: Support custom line spacing defined by user - textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor); + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup + textOffsetY += textLineSpacing; textOffsetX = 0.0f; } else @@ -1161,6 +1167,12 @@ void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 pos } } +// Set vertical line spacing when drawing with line-breaks +void SetTextLineSpacing(int spacing) +{ + textLineSpacing = spacing; +} + // Measure string width for default font int MeasureText(const char *text, int fontSize) { @@ -1219,7 +1231,9 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; byteCounter = 0; textWidth = 0; - textHeight += ((float)font.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines + + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup + textHeight += (float)textLineSpacing; } if (tempByteCounter < byteCounter) tempByteCounter = byteCounter; From 4fc5e82e3087b5a9df21db86f6dc4588e5d629b4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 11:10:42 +0200 Subject: [PATCH 0489/1710] REVIEWED: `TextToUpper()`, `TextToLower()`, `TextToPascal()` --- src/rtext.c | 48 ++++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index cb609cf62..0cdac6d10 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1588,7 +1588,8 @@ int TextFindIndex(const char *text, const char *find) } // Get upper case version of provided string -// REQUIRES: toupper() +// WARNING: Limited functionality, only basic characters set +// TODO: Support UTF-8 diacritics to upper-case, check codepoints const char *TextToUpper(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; @@ -1596,17 +1597,10 @@ const char *TextToUpper(const char *text) if (text != NULL) { - for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++) { - if (text[i] != '\0') - { - buffer[i] = (char)toupper(text[i]); - //if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32; - - // TODO: Support UTF-8 diacritics to upper-case - //if ((text[i] >= 'à') && (text[i] <= 'ý')) buffer[i] = text[i] - 32; - } - else { buffer[i] = '\0'; break; } + if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32; + else buffer[i] = text[i]; } } @@ -1614,7 +1608,7 @@ const char *TextToUpper(const char *text) } // Get lower case version of provided string -// REQUIRES: tolower() +// WARNING: Limited functionality, only basic characters set const char *TextToLower(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; @@ -1622,14 +1616,10 @@ const char *TextToLower(const char *text) if (text != NULL) { - for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++) { - if (text[i] != '\0') - { - buffer[i] = (char)tolower(text[i]); - //if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32; - } - else { buffer[i] = '\0'; break; } + if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32; + else buffer[i] = text[i]; } } @@ -1637,7 +1627,7 @@ const char *TextToLower(const char *text) } // Get Pascal case notation version of provided string -// REQUIRES: toupper() +// WARNING: Limited functionality, only basic characters set const char *TextToPascal(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; @@ -1645,20 +1635,18 @@ const char *TextToPascal(const char *text) if (text != NULL) { - buffer[0] = (char)toupper(text[0]); + // Upper case first character + if ((text[0] >= 'a') && (text[0] <= 'z')) buffer[0] = text[0] - 32; - for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++) + // Check for next separator to upper case another character + for (int i = 1, j = 1; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++) { - if (text[j] != '\0') + if (text[j] != '_') buffer[i] = text[j]; + else { - if (text[j] != '_') buffer[i] = text[j]; - else - { - j++; - buffer[i] = (char)toupper(text[j]); - } + j++; + if ((text[j] >= 'a') && (text[j] <= 'z')) buffer[i] = text[j] - 32; } - else { buffer[i] = '\0'; break; } } } From 5361d498c33deeb1e1ad44aed24a53a4f9e97618 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 17:27:38 +0200 Subject: [PATCH 0490/1710] WARNING: REDESIGN: `Vector2Angle()`<-->`Vector2LineAngle()` #2887 --- examples/others/raymath_vector_angle.c | 106 +++++++++++++++++++++++ examples/others/raymath_vector_angle.png | Bin 0 -> 16339 bytes src/raymath.h | 27 +++--- 3 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 examples/others/raymath_vector_angle.c create mode 100644 examples/others/raymath_vector_angle.png diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c new file mode 100644 index 000000000..86cd91a93 --- /dev/null +++ b/examples/others/raymath_vector_angle.c @@ -0,0 +1,106 @@ +/******************************************************************************************* +* +* raylib [shapes] example - Vector Angle +* +* 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) 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 = { 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 + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_SPACE)) angleMode = !angleMode; + + if (angleMode == 0) + { + // Calculate angle between two vectors, considering a common origin (v0) + v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); + v2 = GetMousePosition(); + angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; + } + else if (angleMode == 1) + { + // Calculate angle defined by a two vectors line, in reference to horizontal line + v1 = (Vector2){ screenWidth/2, screenHeight/2 }; + v2 = GetMousePosition(); + angle = Vector2LineAngle(v1, v2)*RAD2DEG; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (angleMode == 0) DrawText("v0", v0.x, v0.y, 10, DARKGRAY); + DrawText("v1", v1.x, v1.y, 10, DARKGRAY); + DrawText("v2", v2.x, v2.y, 10, DARKGRAY); + + if (angleMode == 0) + { + DrawText("MODE: Angle between V1 and V2", 10, 10, 20, BLACK); + + DrawLineEx(v0, v1, 2.0f, BLACK); + DrawLineEx(v0, v2, 2.0f, RED); + + // TODO: Properly draw circle sector + DrawCircleSector(v0, 40.0f, Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + } + else if (angleMode == 1) + { + DrawText("MODE: Angle formed by line V1 to V2", 10, 10, 20, BLACK); + + DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); + DrawLineEx(v1, v2, 2.0f, RED); + + DrawCircleSector(v1, 40.0f, 90.0f, 180 - angle - 90, 32, Fade(GREEN, 0.6f)); + } + + DrawText("Press SPACE to change MODE", 460, 10, 20, DARKGRAY); + DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 40, 20, LIME); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/others/raymath_vector_angle.png b/examples/others/raymath_vector_angle.png new file mode 100644 index 0000000000000000000000000000000000000000..29995e002f283e5bd9a1960085dc25da31d5e9eb GIT binary patch literal 16339 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 N8#8hAQNQ5m{{X8kT3rAD literal 0 HcmV?d00001 diff --git a/src/raymath.h b/src/raymath.h index 47728ff69..6a929cf6e 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -314,8 +314,19 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) // NOTE: Angle is calculated from origin point (0, 0) RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { - float result = atan2f(v2.y - v1.y, v2.x - v1.x); + float result = 0.0f; + + float dot = v1.x*v2.x + v1.y*v2.y; // Dot product + float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp + if (dotClamp > 1.0f) dotClamp = 1.0f; + result = acosf(dotClamp); + + // Alternative implementation, more costly + //float v1Length = sqrtf((v1.x*v1.x) + (v1.y*v1.y)); + //float v2Length = sqrtf((v2.x*v2.x) + (v2.y*v2.y)); + //float result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); + return result; } @@ -325,18 +336,8 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - - float dot = start.x*end.x + start.y*end.y; // Dot product - - float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp - if (dotClamp > 1.0f) dotClamp = 1.0f; - - result = acosf(dotClamp); - - // Alternative implementation, more costly - //float v1Length = sqrtf((start.x*start.x) + (start.y*start.y)); - //float v2Length = sqrtf((end.x*end.x) + (end.y*end.y)); - //float result = -acosf((start.x*end.x + start.y*end.y)/(v1Length*v2Length)); + + result = atan2f(end.y - start.y, end.x - start.x); return result; } From d1ab031a273f6d012c81f3ed637771b7f3c72ef4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 17:48:00 +0200 Subject: [PATCH 0491/1710] ADDED: `SetWindowFocused()` #3142 --- src/raylib.h | 1 + src/rcore.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 539a31355..f9f36626e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -958,6 +958,7 @@ RLAPI void SetWindowMonitor(int monitor); // Set monitor RLAPI void SetWindowMinSize(int width, int height); // Set window minimum 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 *GetWindowHandle(void); // Get native window handle RLAPI int GetScreenWidth(void); // Get current screen width RLAPI int GetScreenHeight(void); // Get current screen height diff --git a/src/rcore.c b/src/rcore.c index bba8f5efe..135ccb6cc 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1705,6 +1705,14 @@ void SetWindowOpacity(float opacity) #endif } +// Set window focused +void SetWindowFocused(void) +{ +#if defined(PLATFORM_DESKTOP) + glfwFocusWindow(CORE.Window.handle); +#endif +} + // Get current screen width int GetScreenWidth(void) { From 64bb2fe3ec4994037467e44525c85abadce892e5 Mon Sep 17 00:00:00 2001 From: Gisteron <56622101+Gisteron@users.noreply.github.com> Date: Sun, 2 Jul 2023 18:51:27 +0200 Subject: [PATCH 0492/1710] fix vector angle example mode 0 circle segment drawing (#3150) --- examples/Makefile | 11 ++++++++++- examples/others/raymath_vector_angle.c | 7 +++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index c2ea35bf4..15edf1b36 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -536,12 +536,20 @@ AUDIO = \ audio/audio_stream_effects \ audio/audio_mixed_processor +OTHERS = \ + others/easings_testbed \ + others/embedded_files_loading \ + others/raylib_opengl_interop \ + others/raymath_vector_angle \ + others/rlgl_compute_shader \ + others/rlgl_standalone + CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) # Define processes to execute #------------------------------------------------------------------------------------------------ # Default target entry -all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO) +all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO) $(OTHERS) core: $(CORE) shapes: $(SHAPES) @@ -550,6 +558,7 @@ text: $(TEXT) models: $(MODELS) shaders: $(SHADERS) audio: $(AUDIO) +others: $(OTHERS) # Generic compilation pattern # NOTE: Examples must be ready for Android compilation! diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 86cd91a93..56db373cd 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -49,14 +49,14 @@ int main(void) // Calculate angle between two vectors, considering a common origin (v0) v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); v2 = GetMousePosition(); - angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; + angle = 90 - Vector2LineAngle(v0, v2) * RAD2DEG; } else if (angleMode == 1) { // Calculate angle defined by a two vectors line, in reference to horizontal line v1 = (Vector2){ screenWidth/2, screenHeight/2 }; v2 = GetMousePosition(); - angle = Vector2LineAngle(v1, v2)*RAD2DEG; + angle = Vector2LineAngle(v1, v2) * RAD2DEG; } //---------------------------------------------------------------------------------- @@ -77,8 +77,7 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - // TODO: Properly draw circle sector - DrawCircleSector(v0, 40.0f, Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1) * RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { From e8af87575639d27fa5486ffe88458596d067c21a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 18:54:50 +0200 Subject: [PATCH 0493/1710] Minor format tweak, another issue introduced... --- examples/others/raymath_vector_angle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 56db373cd..457d69dc3 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -49,14 +49,14 @@ int main(void) // Calculate angle between two vectors, considering a common origin (v0) v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); v2 = GetMousePosition(); - angle = 90 - Vector2LineAngle(v0, v2) * RAD2DEG; + angle = 90 - Vector2LineAngle(v0, v2)*RAD2DEG; } else if (angleMode == 1) { // Calculate angle defined by a two vectors line, in reference to horizontal line v1 = (Vector2){ screenWidth/2, screenHeight/2 }; v2 = GetMousePosition(); - angle = Vector2LineAngle(v1, v2) * RAD2DEG; + angle = Vector2LineAngle(v1, v2)*RAD2DEG; } //---------------------------------------------------------------------------------- @@ -77,7 +77,7 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1) * RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { From fdc28fce80d6e9ae04e900079beba7d79673b25d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 20:05:15 +0200 Subject: [PATCH 0494/1710] Reviewed vector2angle example --- examples/others/raymath_vector_angle.c | 7 +++++-- src/raymath.h | 5 ++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 457d69dc3..5de6116f2 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -49,7 +49,7 @@ int main(void) // Calculate angle between two vectors, considering a common origin (v0) v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); v2 = GetMousePosition(); - angle = 90 - Vector2LineAngle(v0, v2)*RAD2DEG; + angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; } else if (angleMode == 1) { @@ -77,7 +77,10 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + float startangle = 90 - Vector2LineAngle(v0, v1)*RAD2DEG; + DrawCircleSector(v0, 40.0f, startangle, angle + startangle, 32, Fade(GREEN, 0.6f)); + + //DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { diff --git a/src/raymath.h b/src/raymath.h index 6a929cf6e..087410ef3 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -316,16 +316,15 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { float result = 0.0f; - float dot = v1.x*v2.x + v1.y*v2.y; // Dot product + float dot = v1.x*v2.x + v1.y*v2.y; // Dot product float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp if (dotClamp > 1.0f) dotClamp = 1.0f; - result = acosf(dotClamp); // Alternative implementation, more costly //float v1Length = sqrtf((v1.x*v1.x) + (v1.y*v1.y)); //float v2Length = sqrtf((v2.x*v2.x) + (v2.y*v2.y)); - //float result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); + //result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); return result; } From ffe4d36e0af86ee2d72abe7607b0525e1ffcf61f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:29:26 -0300 Subject: [PATCH 0495/1710] Fix swipe gestures for web (#3151) --- src/rcore.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 135ccb6cc..6ef25c0bf 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6131,6 +6131,10 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent { 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 From dd2d64e05804e749d470a9d252cea48b8b1d7e17 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 4 Jul 2023 06:08:58 -0300 Subject: [PATCH 0496/1710] Fix pinch gestures for web (#3153) --- src/rgestures.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rgestures.h b/src/rgestures.h index 0be2fbb5c..1461a1114 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -203,6 +203,8 @@ typedef struct { Vector2 downDragPosition; // Touch drag position Vector2 moveDownPositionA; // First touch down position on move Vector2 moveDownPositionB; // Second touch down position on move + Vector2 previousPositionA; // Previous position A to compare for pinch gestures + Vector2 previousPositionB; // Previous position B to compare for pinch gestures int tapCounter; // TAP counter (one tap implies TOUCH_ACTION_DOWN and TOUCH_ACTION_UP actions) } Touch; struct { @@ -364,6 +366,9 @@ void ProcessGestureEvent(GestureEvent event) GESTURES.Touch.downPositionA = event.position[0]; GESTURES.Touch.downPositionB = event.position[1]; + GESTURES.Touch.previousPositionA = GESTURES.Touch.downPositionA; + GESTURES.Touch.previousPositionB = GESTURES.Touch.downPositionB; + //GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.downPositionB); GESTURES.Pinch.vector.x = GESTURES.Touch.downPositionB.x - GESTURES.Touch.downPositionA.x; @@ -376,18 +381,15 @@ void ProcessGestureEvent(GestureEvent event) { GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB); - GESTURES.Touch.downPositionA = GESTURES.Touch.moveDownPositionA; - GESTURES.Touch.downPositionB = GESTURES.Touch.moveDownPositionB; - GESTURES.Touch.moveDownPositionA = event.position[0]; GESTURES.Touch.moveDownPositionB = event.position[1]; GESTURES.Pinch.vector.x = GESTURES.Touch.moveDownPositionB.x - GESTURES.Touch.moveDownPositionA.x; GESTURES.Pinch.vector.y = GESTURES.Touch.moveDownPositionB.y - GESTURES.Touch.moveDownPositionA.y; - if ((rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (rgVector2Distance(GESTURES.Touch.downPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH)) + if ((rgVector2Distance(GESTURES.Touch.previousPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (rgVector2Distance(GESTURES.Touch.previousPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH)) { - if ((rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) - GESTURES.Pinch.distance) < 0) GESTURES.current = GESTURE_PINCH_IN; + if ( rgVector2Distance(GESTURES.Touch.previousPositionA, GESTURES.Touch.previousPositionB) > rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) ) GESTURES.current = GESTURE_PINCH_IN; else GESTURES.current = GESTURE_PINCH_OUT; } else From 225b4fb3e2503d97a53d3aa746ddac3648cfebd8 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 4 Jul 2023 16:58:43 +0200 Subject: [PATCH 0497/1710] REVIEWED: `Vector2Angle()` --- examples/others/raymath_vector_angle.c | 4 +--- src/raymath.h | 12 +++--------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 5de6116f2..b193648fe 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -78,9 +78,7 @@ int main(void) DrawLineEx(v0, v2, 2.0f, RED); float startangle = 90 - Vector2LineAngle(v0, v1)*RAD2DEG; - DrawCircleSector(v0, 40.0f, startangle, angle + startangle, 32, Fade(GREEN, 0.6f)); - - //DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle + angle - 360.0f*(angle > 180.0f), 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { diff --git a/src/raymath.h b/src/raymath.h index 087410ef3..d7ec1d252 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -316,15 +316,9 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { float result = 0.0f; - float dot = v1.x*v2.x + v1.y*v2.y; // Dot product - float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp - if (dotClamp > 1.0f) dotClamp = 1.0f; - result = acosf(dotClamp); - - // Alternative implementation, more costly - //float v1Length = sqrtf((v1.x*v1.x) + (v1.y*v1.y)); - //float v2Length = sqrtf((v2.x*v2.x) + (v2.y*v2.y)); - //result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); + float dot = v1.x*v2.x + v1.y*v2.y; + float det = v1.x*v2.y - v1.y*v2.x; + result = -atan2f(det, dot); return result; } From 3c4ce9c99b74475cfc651ddc4db482a4ee86d57f Mon Sep 17 00:00:00 2001 From: Wytek01 <101928745+Wytekol@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:39:38 +0200 Subject: [PATCH 0498/1710] Update examples_template.c to raylib 4.5 (#3156) --- examples/examples_template.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples_template.c b/examples/examples_template.c index 3d8332e67..2b962d319 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -41,7 +41,7 @@ * * raylib [core] example - Basic window * -* Example originally created with raylib 4.2, last time updated with raylib 4.2 +* Example originally created with raylib 4.5, last time updated with raylib 4.5 * * Example contributed by (@) and reviewed by Ramon Santamaria (@raysan5) * From 13a26a0fa238b50621091ff686a6edf7014ab75f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:43:00 -0300 Subject: [PATCH 0499/1710] Fix degrees of swipe gestures for web (#3155) --- src/rgestures.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rgestures.h b/src/rgestures.h index 1461a1114..35cd7a602 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -311,10 +311,10 @@ void ProcessGestureEvent(GestureEvent event) // NOTE: Angle should be inverted in Y GESTURES.Drag.angle = 360.0f - rgVector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); - if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT; // Right - else if ((GESTURES.Drag.angle > 30) && (GESTURES.Drag.angle < 120)) GESTURES.current = GESTURE_SWIPE_UP; // Up - else if ((GESTURES.Drag.angle > 120) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT; // Left - else if ((GESTURES.Drag.angle > 210) && (GESTURES.Drag.angle < 300)) GESTURES.current = GESTURE_SWIPE_DOWN; // Down + if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT; // Right + else if ((GESTURES.Drag.angle >= 30) && (GESTURES.Drag.angle <= 150)) GESTURES.current = GESTURE_SWIPE_UP; // Up + else if ((GESTURES.Drag.angle > 150) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT; // Left + else if ((GESTURES.Drag.angle >= 210) && (GESTURES.Drag.angle <= 330)) GESTURES.current = GESTURE_SWIPE_DOWN; // Down else GESTURES.current = GESTURE_NONE; } else From 58bd10edb27e0e5dd8d835e2222239390c804d5e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Jul 2023 18:36:54 +0200 Subject: [PATCH 0500/1710] Update rtext.c --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 0cdac6d10..396275a8d 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -112,8 +112,8 @@ static Font defaultFont = { 0 }; //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) -static int textLineSpacing = 15; // Text vertical line spacing in pixels #endif +static int textLineSpacing = 15; // Text vertical line spacing in pixels #if defined(SUPPORT_DEFAULT_FONT) extern void LoadFontDefault(void); From 668b37e1113c653c38ce50757984b8fc074ea9b3 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Jul 2023 12:29:28 +0200 Subject: [PATCH 0501/1710] REVIEWED: `GenImageFontAtlas()`, make atlas size less conservative --- src/rtext.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 396275a8d..eb3ad5514 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -699,7 +699,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; - totalWidth += chars[i].image.width + 2*padding; + totalWidth += chars[i].image.width + 4*padding; } //#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE @@ -713,15 +713,28 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC imageSize *= 2; // Double the size of image (to keep POT) rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size } -#else - // No need for a so-conservative atlas generation - float totalArea = totalWidth*fontSize*1.3f; - float imageMinSize = sqrtf(totalArea); - int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2))); -#endif atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height +#else + // No need for a so-conservative atlas generation + float totalArea = totalWidth*fontSize*1.2f; + float imageMinSize = sqrtf(totalArea); + int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2))); + + if (totalArea < ((imageSize*imageSize)/2)) + { + atlas.width = imageSize; // Atlas bitmap width + atlas.height = imageSize/2; // Atlas bitmap height + } + else + { + atlas.width = imageSize; // Atlas bitmap width + atlas.height = imageSize; // Atlas bitmap height + } +#endif + + atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; atlas.mipmaps = 1; From 685d47938bf97ef5cb2ec094bfebd7de9bd9bef8 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Jul 2023 12:31:01 +0200 Subject: [PATCH 0502/1710] Reverted `MeasureTextEx()` change #3105 --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index eb3ad5514..e7bea9a00 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1254,7 +1254,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; - textSize.x = (tempTextWidth + (float)((tempByteCounter - 1)*spacing))*scaleFactor; + textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); textSize.y = textHeight*scaleFactor; return textSize; From 334e96d470c5cb09d154e1c849d04adb721a7a8c Mon Sep 17 00:00:00 2001 From: o3o Date: Thu, 6 Jul 2023 12:32:02 +0200 Subject: [PATCH 0503/1710] update bindbd-raylib3 to raylib 4.5 (#3157) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index f040599ca..bb73d73aa 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -17,7 +17,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | Zlib | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | -| bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | +| bindbc-raylib3 | **4.5** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | 4.2 | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | | raylib-d | **4.5** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | 4.0 | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | From 91e4eea52d1b92e1408fd129ca7b638f9cd4531d Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Mon, 10 Jul 2023 12:58:10 -0400 Subject: [PATCH 0504/1710] Add raylib-ffi to bindings list (#3164) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index bb73d73aa..8c5c8396b 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -59,6 +59,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | +| raylib-ffi | 4.5 | [Rust](https://www.rust-lang.org/) | GPLv3 | https://github.com/ewpratten/raylib-ffi | | raylib-rs | 3.5 | [Rust](https://www.rust-lang.org/) | Zlib | https://github.com/deltaphc/raylib-rs | | Relib | 3.5 | [ReCT](https://github.com/RedCubeDev-ByteSpace/ReCT) | ? | https://github.com/RedCubeDev-ByteSpace/Relib | | racket-raylib | 4.0 | [Racket](https://racket-lang.org/) | MIT/Apache-2.0 | https://github.com/eutro/racket-raylib | From 4b6cbd234040312091e24085ee929a7cec60faac Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:58:56 -0300 Subject: [PATCH 0505/1710] Fix Touch pointCount for web (#3163) --- src/rcore.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 6ef25c0bf..833400fcf 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6139,6 +6139,9 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent // 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 return 1; // The event was consumed by the callback handler From 0b9fae3c539161e850db9092ac7294f889583e82 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 10 Jul 2023 19:13:44 +0200 Subject: [PATCH 0506/1710] Reviewed `rcamera`/`rgestures` file-macros for consistency #3161 --- src/rcamera.h | 20 ++++++++++---------- src/rcore.c | 4 ++-- src/rgestures.h | 22 +++++++++++----------- src/rtext.c | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 2a20d5814..4e99c3e8c 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -3,12 +3,12 @@ * rcamera - Basic camera system with support for multiple camera modes * * CONFIGURATION: -* #define CAMERA_IMPLEMENTATION +* #define RCAMERA_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 CAMERA_STANDALONE +* #define RCAMERA_STANDALONE * If defined, the library can be used as standalone as a camera system but some * functions must be redefined to manage inputs accordingly. * @@ -50,7 +50,7 @@ #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) #endif -#if defined(CAMERA_STANDALONE) +#if defined(RCAMERA_STANDALONE) #define CAMERA_CULL_DISTANCE_NEAR 0.01 #define CAMERA_CULL_DISTANCE_FAR 1000.0 #else @@ -60,9 +60,9 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition -// NOTE: Below types are required for CAMERA_STANDALONE usage +// NOTE: Below types are required for standalone usage //---------------------------------------------------------------------------------- -#if defined(CAMERA_STANDALONE) +#if defined(RCAMERA_STANDALONE) // Vector2, 2 components typedef struct Vector2 { float x; // Vector x component @@ -138,7 +138,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); } #endif -#endif // CAMERA_H +#endif // RCAMERA_H /*********************************************************************************** @@ -147,7 +147,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); * ************************************************************************************/ -#if defined(CAMERA_IMPLEMENTATION) +#if defined(RCAMERA_IMPLEMENTATION) #include "raymath.h" // Required for vector maths: // Vector3Add() @@ -417,7 +417,7 @@ Matrix GetCameraProjectionMatrix(Camera *camera, float aspect) return MatrixIdentity(); } -#ifndef CAMERA_STANDALONE +#if !defined(RCAMERA_STANDALONE) // Update camera position for selected mode // Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM void UpdateCamera(Camera *camera, int mode) @@ -483,7 +483,7 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyPressed(KEY_KP_ADD)) CameraMoveToTarget(camera, -2.0f); } } -#endif // !CAMERA_STANDALONE +#endif // !RCAMERA_STANDALONE // Update camera movement, movement/rotation values should be provided by user void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom) @@ -516,4 +516,4 @@ void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float z CameraMoveToTarget(camera, zoom); } -#endif // CAMERA_IMPLEMENTATION +#endif // RCAMERA_IMPLEMENTATION diff --git a/src/rcore.c b/src/rcore.c index 6ef25c0bf..e98734ada 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -124,12 +124,12 @@ #include "raymath.h" // Vector3, Quaternion and Matrix functionality #if defined(SUPPORT_GESTURES_SYSTEM) - #define GESTURES_IMPLEMENTATION + #define RGESTURES_IMPLEMENTATION #include "rgestures.h" // Gestures detection functionality #endif #if defined(SUPPORT_CAMERA_SYSTEM) - #define CAMERA_IMPLEMENTATION + #define RCAMERA_IMPLEMENTATION #include "rcamera.h" // Camera system functionality #endif diff --git a/src/rgestures.h b/src/rgestures.h index 35cd7a602..749f440aa 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -3,12 +3,12 @@ * rgestures - Gestures system, gestures processing based on input events (touch/mouse) * * CONFIGURATION: -* #define GESTURES_IMPLEMENTATION +* #define RGESTURES_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 GESTURES_STANDALONE +* #define RGESTURES_STANDALONE * If defined, the library can be used as standalone to process gesture events with * no external dependencies. * @@ -56,7 +56,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition -// NOTE: Below types are required for GESTURES_STANDALONE usage +// NOTE: Below types are required for standalone usage //---------------------------------------------------------------------------------- // Boolean type #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) @@ -73,7 +73,7 @@ typedef struct Vector2 { } Vector2; #endif -#if defined(GESTURES_STANDALONE) +#if defined(RGESTURES_STANDALONE) // Gestures type // NOTE: It could be used as flags to enable only some gestures typedef enum { @@ -122,7 +122,7 @@ extern "C" { // Prevents name mangling of functions void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures void UpdateGestures(void); // Update gestures detected (must be called every frame) -#if defined(GESTURES_STANDALONE) +#if defined(RGESTURES_STANDALONE) void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags bool IsGestureDetected(int gesture); // Check if a gesture have been detected int GetGestureDetected(void); // Get latest detected gesture @@ -138,17 +138,17 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang } #endif -#endif // GESTURES_H +#endif // RGESTURES_H /*********************************************************************************** * -* GESTURES IMPLEMENTATION +* RGESTURES IMPLEMENTATION * ************************************************************************************/ -#if defined(GESTURES_IMPLEMENTATION) +#if defined(RGESTURES_IMPLEMENTATION) -#if defined(GESTURES_STANDALONE) +#if defined(RGESTURES_STANDALONE) #if defined(_WIN32) #if defined(__cplusplus) extern "C" { // Prevents name mangling of functions @@ -527,7 +527,7 @@ static double rgGetCurrentTime(void) { double time = 0; -#if !defined(GESTURES_STANDALONE) +#if !defined(RGESTURES_STANDALONE) time = GetTime(); #else #if defined(_WIN32) @@ -568,4 +568,4 @@ static double rgGetCurrentTime(void) return time; } -#endif // GESTURES_IMPLEMENTATION +#endif // RGESTURES_IMPLEMENTATION diff --git a/src/rtext.c b/src/rtext.c index e7bea9a00..3b21a93c9 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1244,7 +1244,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; byteCounter = 0; textWidth = 0; - + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup textHeight += (float)textLineSpacing; } From bc40012ca3d719ece17f8ba28865ccb6cdf8bbcf Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 10 Jul 2023 19:15:55 +0200 Subject: [PATCH 0507/1710] Added missing structure on standalone mode #3160 --- src/rcamera.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/rcamera.h b/src/rcamera.h index 4e99c3e8c..c5382684e 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -76,6 +76,14 @@ float z; // Vector z component } Vector3; + // Matrix, 4x4 components, column major, OpenGL style, right-handed + typedef struct Matrix { + float m0, m4, m8, m12; // Matrix first row (4 components) + float m1, m5, m9, m13; // Matrix second row (4 components) + float m2, m6, m10, m14; // Matrix third row (4 components) + float m3, m7, m11, m15; // Matrix fourth row (4 components) + } Matrix; + // Camera type, defines a camera position/orientation in 3d space typedef struct Camera3D { Vector3 position; // Camera position From 2061bfc5e814dd4e2b5ff629cb02bef4c972e9f8 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 10 Jul 2023 19:29:10 +0200 Subject: [PATCH 0508/1710] Reviewed parameter names to avoid issues #3159 --- src/raymath.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index d7ec1d252..9281691fa 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1509,11 +1509,11 @@ RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, // Get perspective projection matrix // NOTE: Fovy angle must be provided in radians -RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double far) +RMAPI Matrix MatrixPerspective(double fovY, double aspect, double nearPlane, double farPlane) { Matrix result = { 0 }; - double top = near*tan(fovy*0.5); + double top = nearPlane*tan(fovY*0.5); double bottom = -top; double right = top*aspect; double left = -right; @@ -1521,27 +1521,27 @@ RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double f // MatrixFrustum(-right, right, -top, top, near, far); float rl = (float)(right - left); float tb = (float)(top - bottom); - float fn = (float)(far - near); + float fn = (float)(farPlane - nearPlane); - result.m0 = ((float)near*2.0f)/rl; - result.m5 = ((float)near*2.0f)/tb; + result.m0 = ((float)nearPlane*2.0f)/rl; + result.m5 = ((float)nearPlane*2.0f)/tb; result.m8 = ((float)right + (float)left)/rl; result.m9 = ((float)top + (float)bottom)/tb; - result.m10 = -((float)far + (float)near)/fn; + result.m10 = -((float)farPlane + (float)nearPlane)/fn; result.m11 = -1.0f; - result.m14 = -((float)far*(float)near*2.0f)/fn; + result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; return result; } // Get orthographic projection matrix -RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far) +RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane) { Matrix result = { 0 }; float rl = (float)(right - left); float tb = (float)(top - bottom); - float fn = (float)(far - near); + float fn = (float)(farPlane - nearPlane); result.m0 = 2.0f/rl; result.m1 = 0.0f; @@ -1557,7 +1557,7 @@ RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, d result.m11 = 0.0f; result.m12 = -((float)left + (float)right)/rl; result.m13 = -((float)top + (float)bottom)/tb; - result.m14 = -((float)far + (float)near)/fn; + result.m14 = -((float)farPlane + (float)nearPlane)/fn; result.m15 = 1.0f; return result; From e8181a5ddf22cb6c4b3dd530b5b60e5c4bd24c86 Mon Sep 17 00:00:00 2001 From: Anand Swaroop <72886192+Anut-py@users.noreply.github.com> Date: Tue, 11 Jul 2023 09:21:49 -0400 Subject: [PATCH 0509/1710] Update latest h-raylib version (#3166) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 8c5c8396b..7fc88113e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -28,7 +28,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-go | 4.5 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | -| h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | +| h-raylib | **4.6-dev** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | | raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | 4.5 | [Janet](https://janet-lang.org/) | MIT | https://github.com/janet-lang/jaylib | From 7f21cf1dcf17225949cbfb0bdeb4677182e5564b Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 12 Jul 2023 10:37:10 +0200 Subject: [PATCH 0510/1710] Revert "UPDATED: `sdefl` and `sinfl` compression libraries" This reverts commit e190b7eee9199b681a8c50fb69f2fce07e92c7af. --- src/external/sdefl.h | 195 +++++++++++++------------------------------ src/external/sinfl.h | 28 +++---- 2 files changed, 68 insertions(+), 155 deletions(-) diff --git a/src/external/sdefl.h b/src/external/sdefl.h index 36015b95b..56539a56e 100644 --- a/src/external/sdefl.h +++ b/src/external/sdefl.h @@ -71,7 +71,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020-2023 Micha Mettke +Copyright (c) 2020 Micha Mettke 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 @@ -125,7 +125,7 @@ extern "C" { #define SDEFL_MIN_MATCH 4 #define SDEFL_BLK_MAX (256*1024) -#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX+2)/3) +#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) #define SDEFL_SYM_MAX (288) #define SDEFL_OFF_MAX (32) @@ -185,7 +185,6 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_MAX_CODE_LEN (15) #define SDEFL_SYM_BITS (10u) #define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) -#define SDEFL_RAW_BLK_SIZE (65535) #define SDEFL_LIT_LEN_CODES (14) #define SDEFL_OFF_CODES (15) #define SDEFL_PRE_CODES (7) @@ -193,7 +192,6 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_EOB (256) #define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) -#define sdefl_div_round_up(n,d) (((n)+((d)-1))/(d)) static int sdefl_ilog2(int n) { @@ -440,12 +438,12 @@ sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items, } while (run_start != total); cnt->items = (int)(at - items); } -struct sdefl_match_codest { +struct sdefl_match_codes { int ls, lc; int dc, dx; }; static void -sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { +sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; static const unsigned char lslot[258+1] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, @@ -473,44 +471,6 @@ sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; } -enum sdefl_blk_type { - SDEFL_BLK_UCOMPR, - SDEFL_BLK_DYN -}; -static enum sdefl_blk_type -sdefl_blk_type(const struct sdefl *s, int blk_len, int pre_item_len, - const unsigned *pre_freq, const unsigned char *pre_len) { - static const unsigned char x_pre_bits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; - static const unsigned char x_len_bits[] = {0,0,0,0,0,0,0,0, 1,1,1,1,2,2,2,2, - 3,3,3,3,4,4,4,4, 5,5,5,5,0}; - static const unsigned char x_off_bits[] = {0,0,0,0,1,1,2,2, 3,3,4,4,5,5,6,6, - 7,7,8,8,9,9,10,10, 11,11,12,12,13,13}; - - int dyn_cost = 0; - int fix_cost = 0; - int sym = 0; - - dyn_cost += 5 + 5 + 4 + (3 * pre_item_len); - for (sym = 0; sym < SDEFL_PRE_MAX; sym++) - dyn_cost += pre_freq[sym] * (x_pre_bits[sym] + pre_len[sym]); - for (sym = 0; sym < 256; sym++) - dyn_cost += s->freq.lit[sym] * s->cod.len.lit[sym]; - dyn_cost += s->cod.len.lit[SDEFL_EOB]; - for (sym = 257; sym < 286; sym++) - dyn_cost += s->freq.lit[sym] * (x_len_bits[sym - 257] + s->cod.len.lit[sym]); - for (sym = 0; sym < 30; sym++) - dyn_cost += s->freq.off[sym] * (x_off_bits[sym] + s->cod.len.off[sym]); - - fix_cost += 8*(5 * sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE) + blk_len + 1 + 2); - return (dyn_cost < fix_cost) ? SDEFL_BLK_DYN : SDEFL_BLK_UCOMPR; -} -static void -sdefl_put16(unsigned char **dst, unsigned short x) { - unsigned char *val = *dst; - val[0] = (unsigned char)(x & 0xff); - val[1] = (unsigned char)(x >> 8); - *dst = val + 2; -} static void sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; @@ -519,7 +479,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; - struct sdefl_match_codest cod; + struct sdefl_match_codes cod; sdefl_match_codes(&cod, dist, len); sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); @@ -528,8 +488,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { } static void sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, - const unsigned char *in, int blk_begin, int blk_end) { - int blk_len = blk_end - blk_begin; + const unsigned char *in) { int j, i = 0, item_cnt = 0; struct sdefl_symcnt symcnt = {0}; unsigned codes[SDEFL_PRE_MAX]; @@ -539,7 +498,7 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, 4,12,3,13,2,14,1,15}; - /* calculate huffman codes */ + /* huffman codes */ s->freq.lit[SDEFL_EOB]++; sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); @@ -550,58 +509,35 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, break; } } - /* write block */ - switch (sdefl_blk_type(s, blk_len, item_cnt, freqs, lens)) { - case SDEFL_BLK_UCOMPR: { - /* uncompressed blocks */ - int n = sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE); - for (i = 0; i < n; ++i) { - int fin = is_last && (i + 1 == n); - int amount = blk_len < SDEFL_RAW_BLK_SIZE ? blk_len : SDEFL_RAW_BLK_SIZE; - sdefl_put(dst, s, !!fin, 1); /* block */ - sdefl_put(dst, s, 0x00, 2); /* stored block */ - if (s->bitcnt) { - sdefl_put(dst, s, 0x00, 8 - s->bitcnt); + /* block header */ + sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ + sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ + sdefl_put(dst, s, symcnt.lit - 257, 5); + sdefl_put(dst, s, symcnt.off - 1, 5); + sdefl_put(dst, s, item_cnt - 4, 4); + for (i = 0; i < item_cnt; ++i) { + sdefl_put(dst, s, lens[perm[i]], 3); + } + for (i = 0; i < symcnt.items; ++i) { + unsigned sym = items[i] & 0x1F; + sdefl_put(dst, s, (int)codes[sym], lens[sym]); + if (sym < 16) continue; + if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); + else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); + else sdefl_put(dst, s, items[i] >> 5, 7); + } + /* block sequences */ + for (i = 0; i < s->seq_cnt; ++i) { + if (s->seq[i].off >= 0) { + for (j = 0; j < s->seq[i].len; ++j) { + int c = in[s->seq[i].off + j]; + sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); } - assert(s->bitcnt == 0); - sdefl_put16(dst, (unsigned short)amount); - sdefl_put16(dst, ~(unsigned short)amount); - memcpy(*dst, in + blk_begin + i * SDEFL_RAW_BLK_SIZE, amount); - *dst = *dst + amount; - blk_len -= amount; + } else { + sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); } - } break; - case SDEFL_BLK_DYN: { - /* dynamic huffman block */ - sdefl_put(dst, s, !!is_last, 1); /* block */ - sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ - sdefl_put(dst, s, symcnt.lit - 257, 5); - sdefl_put(dst, s, symcnt.off - 1, 5); - sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) { - sdefl_put(dst, s, lens[perm[i]], 3); - } - for (i = 0; i < symcnt.items; ++i) { - unsigned sym = items[i] & 0x1F; - sdefl_put(dst, s, (int)codes[sym], lens[sym]); - if (sym < 16) continue; - if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); - else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); - else sdefl_put(dst, s, items[i] >> 5, 7); - } - /* block sequences */ - for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) { - for (j = 0; j < s->seq[i].len; ++j) { - int c = in[s->seq[i].off + j]; - sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); - } - } else { - sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); - } - } - sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); - } break;} + } + sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); memset(&s->freq, 0, sizeof(s->freq)); s->seq_cnt = 0; } @@ -614,12 +550,8 @@ sdefl_seq(struct sdefl *s, int off, int len) { } static void sdefl_reg_match(struct sdefl *s, int off, int len) { - struct sdefl_match_codest cod; + struct sdefl_match_codes cod; sdefl_match_codes(&cod, off, len); - - assert(cod.lc < SDEFL_SYM_MAX); - assert(cod.dc < SDEFL_OFF_MAX); - s->freq.lit[cod.lc]++; s->freq.off[cod.dc]++; } @@ -628,35 +560,22 @@ struct sdefl_match { int len; }; static void -sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, int chain_len, - int max_match, const unsigned char *in, int p, int e) { - int i = s->tbl[sdefl_hash32(in + p)]; - int limit = ((p - SDEFL_WIN_SIZ) < SDEFL_NIL) ? SDEFL_NIL : (p-SDEFL_WIN_SIZ); - - assert(p < e); - assert(p + max_match <= e); +sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, + int chain_len, int max_match, const unsigned char *in, int p) { + int i = s->tbl[sdefl_hash32(&in[p])]; + int limit = ((p-SDEFL_WIN_SIZ) limit) { - assert(i + m->len < e); - assert(p + m->len < e); - assert(i + SDEFL_MIN_MATCH < e); - assert(p + SDEFL_MIN_MATCH < e); - - if (in[i + m->len] == in[p + m->len] && - (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))) { + if (in[i+m->len] == in[p+m->len] && + (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))){ int n = SDEFL_MIN_MATCH; - while (n < max_match && in[i + n] == in[p + n]) { - assert(i + n < e); - assert(p + n < e); - n++; - } + while (n < max_match && in[i+n] == in[p+n]) n++; if (n > m->len) { m->len = n, m->off = p - i; - if (n == max_match) - break; + if (n == max_match) break; } } if (!(--chain_len)) break; - i = s->prv[i & SDEFL_WIN_MSK]; + i = s->prv[i&SDEFL_WIN_MSK]; } } static int @@ -669,20 +588,19 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, for (n = 0; n < SDEFL_HASH_SIZ; ++n) { s->tbl[n] = SDEFL_NIL; } - do {int blk_begin = i; - int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; + do {int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; while (i < blk_end) { struct sdefl_match m = {0}; int left = blk_end - i; - int max_match = (left > SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; + int max_match = (left >= SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; int run = 1, inc = 1, run_inc = 0; if (max_match > SDEFL_MIN_MATCH) { - sdefl_fnd(&m, s, max_chain, max_match, in, i, in_len); + sdefl_fnd(&m, s, max_chain, max_match, in, i); } - if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len + 1 < nice_match){ + if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ struct sdefl_match m2 = {0}; - sdefl_fnd(&m2, s, max_chain, m.len + 1, in, i + 1, in_len); + sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); m.len = (m2.len > m.len) ? 0 : m.len; } if (m.len >= SDEFL_MIN_MATCH) { @@ -718,12 +636,12 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, sdefl_seq(s, i - litlen, litlen); litlen = 0; } - sdefl_flush(&q, s, blk_end == in_len, in, blk_begin, blk_end); + sdefl_flush(&q, s, blk_end == in_len, in); } while (i < in_len); - if (s->bitcnt) { + + if (s->bitcnt > 0) sdefl_put(&q, s, 0x00, 8 - s->bitcnt); - } - assert(s->bitcnt == 0); + return (int)(q - out); } extern int @@ -783,8 +701,9 @@ zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { } extern int sdefl_bound(int len) { - int max_blocks = 1 + sdefl_div_round_up(len, SDEFL_RAW_BLK_SIZE); - int bound = 5 * max_blocks + len + 1 + 4 + 8; - return bound; + int a = 128 + (len * 110) / 100; + int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; + return (a > b) ? a : b; } #endif /* SDEFL_IMPLEMENTATION */ + diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 8979fcd7f..915da9d23 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -72,7 +72,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020-2023 Micha Mettke +Copyright (c) 2020 Micha Mettke 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 @@ -400,21 +400,17 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) } break; case stored: { /* uncompressed block */ - unsigned len, nlen; + int len, nlen; + sinfl_refill(&s); sinfl__get(&s,s.bitcnt & 7); - len = (unsigned short)sinfl__get(&s,16); - nlen = (unsigned short)sinfl__get(&s,16); - s.bitptr -= s.bitcnt / 8; - s.bitbuf = s.bitcnt = 0; + len = sinfl__get(&s,16); + nlen = sinfl__get(&s,16); + in -= 2; s.bitcnt = 0; - if ((unsigned short)len != (unsigned short)~nlen) + if (len > (e-in) || !len) return (int)(out-o); - if (len > (e - s.bitptr) || !len) - return (int)(out-o); - - memcpy(out, s.bitptr, (size_t)len); - s.bitptr += len, out += len; - if (last) return (int)(out-o); + memcpy(out, in, (size_t)len); + in += len, out += len; state = hdr; } break; case fixed: { @@ -447,9 +443,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) /* decode code lengths */ for (n = 0; n < nlit + ndist;) { - int sym = 0; sinfl_refill(&s); - sym = sinfl_decode(&s, hlens, 7); + int sym = sinfl_decode(&s, hlens, 7); switch (sym) {default: lens[n++] = (unsigned char)sym; break; case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; @@ -463,9 +458,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) case blk: { /* decompress block */ while (1) { - int sym; sinfl_refill(&s); - sym = sinfl_decode(&s, s.lits, 10); + int sym = sinfl_decode(&s, s.lits, 10); if (sym < 256) { /* literal */ if (sinfl_unlikely(out >= oe)) { From 8096f142ecc9607df6b37a924b56202f8eb31657 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 12 Jul 2023 07:02:33 -0300 Subject: [PATCH 0511/1710] Add a new task the issue template about checking the wiki (#3169) --- .github/ISSUE_TEMPLATE/new-issue-template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/new-issue-template.md b/.github/ISSUE_TEMPLATE/new-issue-template.md index 096eb28cb..4bac86e82 100644 --- a/.github/ISSUE_TEMPLATE/new-issue-template.md +++ b/.github/ISSUE_TEMPLATE/new-issue-template.md @@ -21,6 +21,7 @@ Please, before submitting a new issue verify and check: - [ ] I tested it on latest raylib version from master branch - [ ] I checked there is no similar issue already reported + - [ ] I checked the documentation on the [wiki](https://github.com/raysan5/raylib/wiki) - [ ] My code has no errors or misuse of raylib ### Issue description From bc9c06325481c0b4b5a2db3b2a8281465569ba3e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 12 Jul 2023 15:49:38 +0200 Subject: [PATCH 0512/1710] Update external sdefl and sinfl --- src/external/sdefl.h | 195 ++++++++++++++++++++++++++++++------------- src/external/sinfl.h | 28 ++++--- 2 files changed, 155 insertions(+), 68 deletions(-) diff --git a/src/external/sdefl.h b/src/external/sdefl.h index 56539a56e..36015b95b 100644 --- a/src/external/sdefl.h +++ b/src/external/sdefl.h @@ -71,7 +71,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke +Copyright (c) 2020-2023 Micha Mettke 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 @@ -125,7 +125,7 @@ extern "C" { #define SDEFL_MIN_MATCH 4 #define SDEFL_BLK_MAX (256*1024) -#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) +#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX+2)/3) #define SDEFL_SYM_MAX (288) #define SDEFL_OFF_MAX (32) @@ -185,6 +185,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_MAX_CODE_LEN (15) #define SDEFL_SYM_BITS (10u) #define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) +#define SDEFL_RAW_BLK_SIZE (65535) #define SDEFL_LIT_LEN_CODES (14) #define SDEFL_OFF_CODES (15) #define SDEFL_PRE_CODES (7) @@ -192,6 +193,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_EOB (256) #define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) +#define sdefl_div_round_up(n,d) (((n)+((d)-1))/(d)) static int sdefl_ilog2(int n) { @@ -438,12 +440,12 @@ sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items, } while (run_start != total); cnt->items = (int)(at - items); } -struct sdefl_match_codes { +struct sdefl_match_codest { int ls, lc; int dc, dx; }; static void -sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { +sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; static const unsigned char lslot[258+1] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, @@ -471,6 +473,44 @@ sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; } +enum sdefl_blk_type { + SDEFL_BLK_UCOMPR, + SDEFL_BLK_DYN +}; +static enum sdefl_blk_type +sdefl_blk_type(const struct sdefl *s, int blk_len, int pre_item_len, + const unsigned *pre_freq, const unsigned char *pre_len) { + static const unsigned char x_pre_bits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + static const unsigned char x_len_bits[] = {0,0,0,0,0,0,0,0, 1,1,1,1,2,2,2,2, + 3,3,3,3,4,4,4,4, 5,5,5,5,0}; + static const unsigned char x_off_bits[] = {0,0,0,0,1,1,2,2, 3,3,4,4,5,5,6,6, + 7,7,8,8,9,9,10,10, 11,11,12,12,13,13}; + + int dyn_cost = 0; + int fix_cost = 0; + int sym = 0; + + dyn_cost += 5 + 5 + 4 + (3 * pre_item_len); + for (sym = 0; sym < SDEFL_PRE_MAX; sym++) + dyn_cost += pre_freq[sym] * (x_pre_bits[sym] + pre_len[sym]); + for (sym = 0; sym < 256; sym++) + dyn_cost += s->freq.lit[sym] * s->cod.len.lit[sym]; + dyn_cost += s->cod.len.lit[SDEFL_EOB]; + for (sym = 257; sym < 286; sym++) + dyn_cost += s->freq.lit[sym] * (x_len_bits[sym - 257] + s->cod.len.lit[sym]); + for (sym = 0; sym < 30; sym++) + dyn_cost += s->freq.off[sym] * (x_off_bits[sym] + s->cod.len.off[sym]); + + fix_cost += 8*(5 * sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE) + blk_len + 1 + 2); + return (dyn_cost < fix_cost) ? SDEFL_BLK_DYN : SDEFL_BLK_UCOMPR; +} +static void +sdefl_put16(unsigned char **dst, unsigned short x) { + unsigned char *val = *dst; + val[0] = (unsigned char)(x & 0xff); + val[1] = (unsigned char)(x >> 8); + *dst = val + 2; +} static void sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; @@ -479,7 +519,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; - struct sdefl_match_codes cod; + struct sdefl_match_codest cod; sdefl_match_codes(&cod, dist, len); sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); @@ -488,7 +528,8 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { } static void sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, - const unsigned char *in) { + const unsigned char *in, int blk_begin, int blk_end) { + int blk_len = blk_end - blk_begin; int j, i = 0, item_cnt = 0; struct sdefl_symcnt symcnt = {0}; unsigned codes[SDEFL_PRE_MAX]; @@ -498,7 +539,7 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, 4,12,3,13,2,14,1,15}; - /* huffman codes */ + /* calculate huffman codes */ s->freq.lit[SDEFL_EOB]++; sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); @@ -509,35 +550,58 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, break; } } - /* block header */ - sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ - sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ - sdefl_put(dst, s, symcnt.lit - 257, 5); - sdefl_put(dst, s, symcnt.off - 1, 5); - sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) { - sdefl_put(dst, s, lens[perm[i]], 3); - } - for (i = 0; i < symcnt.items; ++i) { - unsigned sym = items[i] & 0x1F; - sdefl_put(dst, s, (int)codes[sym], lens[sym]); - if (sym < 16) continue; - if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); - else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); - else sdefl_put(dst, s, items[i] >> 5, 7); - } - /* block sequences */ - for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) { - for (j = 0; j < s->seq[i].len; ++j) { - int c = in[s->seq[i].off + j]; - sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + /* write block */ + switch (sdefl_blk_type(s, blk_len, item_cnt, freqs, lens)) { + case SDEFL_BLK_UCOMPR: { + /* uncompressed blocks */ + int n = sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE); + for (i = 0; i < n; ++i) { + int fin = is_last && (i + 1 == n); + int amount = blk_len < SDEFL_RAW_BLK_SIZE ? blk_len : SDEFL_RAW_BLK_SIZE; + sdefl_put(dst, s, !!fin, 1); /* block */ + sdefl_put(dst, s, 0x00, 2); /* stored block */ + if (s->bitcnt) { + sdefl_put(dst, s, 0x00, 8 - s->bitcnt); } - } else { - sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + assert(s->bitcnt == 0); + sdefl_put16(dst, (unsigned short)amount); + sdefl_put16(dst, ~(unsigned short)amount); + memcpy(*dst, in + blk_begin + i * SDEFL_RAW_BLK_SIZE, amount); + *dst = *dst + amount; + blk_len -= amount; } - } - sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + } break; + case SDEFL_BLK_DYN: { + /* dynamic huffman block */ + sdefl_put(dst, s, !!is_last, 1); /* block */ + sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ + sdefl_put(dst, s, symcnt.lit - 257, 5); + sdefl_put(dst, s, symcnt.off - 1, 5); + sdefl_put(dst, s, item_cnt - 4, 4); + for (i = 0; i < item_cnt; ++i) { + sdefl_put(dst, s, lens[perm[i]], 3); + } + for (i = 0; i < symcnt.items; ++i) { + unsigned sym = items[i] & 0x1F; + sdefl_put(dst, s, (int)codes[sym], lens[sym]); + if (sym < 16) continue; + if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); + else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); + else sdefl_put(dst, s, items[i] >> 5, 7); + } + /* block sequences */ + for (i = 0; i < s->seq_cnt; ++i) { + if (s->seq[i].off >= 0) { + for (j = 0; j < s->seq[i].len; ++j) { + int c = in[s->seq[i].off + j]; + sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + } + } else { + sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } + } + sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + } break;} memset(&s->freq, 0, sizeof(s->freq)); s->seq_cnt = 0; } @@ -550,8 +614,12 @@ sdefl_seq(struct sdefl *s, int off, int len) { } static void sdefl_reg_match(struct sdefl *s, int off, int len) { - struct sdefl_match_codes cod; + struct sdefl_match_codest cod; sdefl_match_codes(&cod, off, len); + + assert(cod.lc < SDEFL_SYM_MAX); + assert(cod.dc < SDEFL_OFF_MAX); + s->freq.lit[cod.lc]++; s->freq.off[cod.dc]++; } @@ -560,22 +628,35 @@ struct sdefl_match { int len; }; static void -sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, - int chain_len, int max_match, const unsigned char *in, int p) { - int i = s->tbl[sdefl_hash32(&in[p])]; - int limit = ((p-SDEFL_WIN_SIZ)tbl[sdefl_hash32(in + p)]; + int limit = ((p - SDEFL_WIN_SIZ) < SDEFL_NIL) ? SDEFL_NIL : (p-SDEFL_WIN_SIZ); + + assert(p < e); + assert(p + max_match <= e); while (i > limit) { - if (in[i+m->len] == in[p+m->len] && - (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))){ + assert(i + m->len < e); + assert(p + m->len < e); + assert(i + SDEFL_MIN_MATCH < e); + assert(p + SDEFL_MIN_MATCH < e); + + if (in[i + m->len] == in[p + m->len] && + (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))) { int n = SDEFL_MIN_MATCH; - while (n < max_match && in[i+n] == in[p+n]) n++; + while (n < max_match && in[i + n] == in[p + n]) { + assert(i + n < e); + assert(p + n < e); + n++; + } if (n > m->len) { m->len = n, m->off = p - i; - if (n == max_match) break; + if (n == max_match) + break; } } if (!(--chain_len)) break; - i = s->prv[i&SDEFL_WIN_MSK]; + i = s->prv[i & SDEFL_WIN_MSK]; } } static int @@ -588,19 +669,20 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, for (n = 0; n < SDEFL_HASH_SIZ; ++n) { s->tbl[n] = SDEFL_NIL; } - do {int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; + do {int blk_begin = i; + int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; while (i < blk_end) { struct sdefl_match m = {0}; int left = blk_end - i; - int max_match = (left >= SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; + int max_match = (left > SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; int run = 1, inc = 1, run_inc = 0; if (max_match > SDEFL_MIN_MATCH) { - sdefl_fnd(&m, s, max_chain, max_match, in, i); + sdefl_fnd(&m, s, max_chain, max_match, in, i, in_len); } - if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ + if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len + 1 < nice_match){ struct sdefl_match m2 = {0}; - sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); + sdefl_fnd(&m2, s, max_chain, m.len + 1, in, i + 1, in_len); m.len = (m2.len > m.len) ? 0 : m.len; } if (m.len >= SDEFL_MIN_MATCH) { @@ -636,12 +718,12 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, sdefl_seq(s, i - litlen, litlen); litlen = 0; } - sdefl_flush(&q, s, blk_end == in_len, in); + sdefl_flush(&q, s, blk_end == in_len, in, blk_begin, blk_end); } while (i < in_len); - - if (s->bitcnt > 0) + if (s->bitcnt) { sdefl_put(&q, s, 0x00, 8 - s->bitcnt); - + } + assert(s->bitcnt == 0); return (int)(q - out); } extern int @@ -701,9 +783,8 @@ zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { } extern int sdefl_bound(int len) { - int a = 128 + (len * 110) / 100; - int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; - return (a > b) ? a : b; + int max_blocks = 1 + sdefl_div_round_up(len, SDEFL_RAW_BLK_SIZE); + int bound = 5 * max_blocks + len + 1 + 4 + 8; + return bound; } #endif /* SDEFL_IMPLEMENTATION */ - diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 915da9d23..8979fcd7f 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -72,7 +72,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke +Copyright (c) 2020-2023 Micha Mettke 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 @@ -400,17 +400,21 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) } break; case stored: { /* uncompressed block */ - int len, nlen; - sinfl_refill(&s); + unsigned len, nlen; sinfl__get(&s,s.bitcnt & 7); - len = sinfl__get(&s,16); - nlen = sinfl__get(&s,16); - in -= 2; s.bitcnt = 0; + len = (unsigned short)sinfl__get(&s,16); + nlen = (unsigned short)sinfl__get(&s,16); + s.bitptr -= s.bitcnt / 8; + s.bitbuf = s.bitcnt = 0; - if (len > (e-in) || !len) + if ((unsigned short)len != (unsigned short)~nlen) return (int)(out-o); - memcpy(out, in, (size_t)len); - in += len, out += len; + if (len > (e - s.bitptr) || !len) + return (int)(out-o); + + memcpy(out, s.bitptr, (size_t)len); + s.bitptr += len, out += len; + if (last) return (int)(out-o); state = hdr; } break; case fixed: { @@ -443,8 +447,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) /* decode code lengths */ for (n = 0; n < nlit + ndist;) { + int sym = 0; sinfl_refill(&s); - int sym = sinfl_decode(&s, hlens, 7); + sym = sinfl_decode(&s, hlens, 7); switch (sym) {default: lens[n++] = (unsigned char)sym; break; case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; @@ -458,8 +463,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) case blk: { /* decompress block */ while (1) { + int sym; sinfl_refill(&s); - int sym = sinfl_decode(&s, s.lits, 10); + sym = sinfl_decode(&s, s.lits, 10); if (sym < 256) { /* literal */ if (sinfl_unlikely(out >= oe)) { From 22895ba14fab982ca74ec1526b48b924d1174e60 Mon Sep 17 00:00:00 2001 From: Roy Qu Date: Fri, 14 Jul 2023 03:20:06 +0800 Subject: [PATCH 0513/1710] fix: cmake option "OPENGL_VERSION" doesn't work (#3170) --- cmake/LibraryConfigurations.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index e9eee630a..d12df3a00 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -106,7 +106,7 @@ elseif ("${PLATFORM}" MATCHES "DRM") endif () -if (NOT ${OPENGL_VERSION}) +if (NOT ${OPENGL_VERSION} MATCHES "OFF") set(${SUGGESTED_GRAPHICS} "${GRAPHICS}") if (${OPENGL_VERSION} MATCHES "4.3") set(GRAPHICS "GRAPHICS_API_OPENGL_43") From b980268ba7fd1fb5083546ca3f0b20f3390fe128 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 16 Jul 2023 08:07:29 -0300 Subject: [PATCH 0514/1710] [example] Core Input Gestures for Web (#3172) * [example] Core Input Gestures for Web * Fix Doubletap for web * Changes TAP_TIMEOUT and rgGetCurrentTime to seconds --- examples/Makefile | 1 + examples/Makefile.Web | 26 +- examples/core/core_input_gestures_web.c | 330 ++++++++++++++++++++++ examples/core/core_input_gestures_web.png | Bin 0 -> 8937 bytes src/rcore.c | 6 + src/rgestures.h | 16 +- 6 files changed, 360 insertions(+), 19 deletions(-) create mode 100644 examples/core/core_input_gestures_web.c create mode 100644 examples/core/core_input_gestures_web.png diff --git a/examples/Makefile b/examples/Makefile index 15edf1b36..1a4d5f06c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -405,6 +405,7 @@ CORE = \ core/core_input_gamepad \ core/core_input_multitouch \ core/core_input_gestures \ + core/core_input_gestures_web \ core/core_2d_camera \ core/core_2d_camera_platformer \ core/core_2d_camera_mouse_zoom \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 499eb3eaa..e65351e07 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -386,6 +386,7 @@ CORE = \ core/core_input_gamepad \ core/core_input_multitouch \ core/core_input_gestures \ + core/core_input_gestures_web \ core/core_2d_camera \ core/core_2d_camera_platformer \ core/core_2d_camera_mouse_zoom \ @@ -558,12 +559,15 @@ core/core_input_multitouch: core/core_input_multitouch.c core/core_input_gestures: core/core_input_gestures.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_input_gestures_web: core/core_input_gestures_web.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_2d_camera: core/core_2d_camera.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_2d_camera_platformer: core/core_2d_camera_platformer.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + core/core_2d_camera_mouse_zoom: core/core_2d_camera_mouse_zoom.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -618,10 +622,10 @@ core/core_custom_frame_control: core/core_custom_frame_control.c core/core_window_should_close: core/core_window_should_close.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + # NOTE: To use multi-threading raylib must be compiled with multi-theading support (-s USE_PTHREADS=1) # WARNING: For security reasons multi-threading is not supported on browsers, it requires cross-origin isolation (Oct.2021) -# WARNING: It requires raylib to be compiled using -pthread, so atomic operations and thread-local data (if any) +# WARNING: It requires raylib to be compiled using -pthread, so atomic operations and thread-local data (if any) # in its source were transformed to non-atomic operations and non-thread-local data core/core_loading_thread: core/core_loading_thread.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s USE_PTHREADS=1 @@ -745,7 +749,7 @@ textures/textures_sprite_explosion: textures/textures_sprite_explosion.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/explosion.png@resources/explosion.png \ --preload-file textures/resources/boom.wav@resources/boom.wav - + textures/textures_textured_curve: textures/textures_textured_curve.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/road.png@resources/road.png @@ -857,8 +861,8 @@ models/models_cubicmap: models/models_cubicmap.c models/models_draw_cube_texture: models/models_draw_cube_texture.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file models/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png - + --preload-file models/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png + models/models_first_person_maze: models/models_first_person_maze.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/cubicmap.png@resources/cubicmap.png \ @@ -889,7 +893,7 @@ models/models_loading_vox: models/models_loading_vox.c models/models_loading_gltf: models/models_loading_gltf.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb - + models/models_loading_m3d: models/models_loading_m3d.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file models/resources/models/m3d/cesium_man.m3d@resources/models/m3d/cesium_man.m3d @@ -1010,16 +1014,16 @@ shaders/shaders_texture_outline: shaders/shaders_texture_outline.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/outline.fs@resources/shaders/glsl100/outline.fs \ --preload-file shaders/resources/fudesumi.png@resources/fudesumi.png - + shaders/shaders_write_depth: shaders/shaders_write_depth.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/write_depth.fs@resources/shaders/glsl100/write_depth.fs - + shaders/shaders_hybrid_render: shaders/shaders_hybrid_render.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/hybrid_raymarch.fs@resources/shaders/glsl100/hybrid_raymarch.fs \ --preload-file shaders/resources/shaders/glsl100/hybrid_raster.fs@resources/shaders/glsl100/hybrid_raster.fs - + # Compile AUDIO examples audio/audio_module_playing: audio/audio_module_playing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1040,7 +1044,7 @@ audio/audio_sound_loading: audio/audio_sound_loading.c audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 - + audio/audio_mixed_processor: audio/audio_mixed_processor.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 \ diff --git a/examples/core/core_input_gestures_web.c b/examples/core/core_input_gestures_web.c new file mode 100644 index 000000000..0ee3d1c5e --- /dev/null +++ b/examples/core/core_input_gestures_web.c @@ -0,0 +1,330 @@ +/******************************************************************************************* +* +* 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 // Required for the Web/HTML5 +#endif + +//-------------------------------------------------------------------------------------- +// Global definitions and declarations +//-------------------------------------------------------------------------------------- + +// Common variables definitions +//-------------------------------------------------------------------------------------- +int screenWidth = 800; +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; + } +} +Color gestureColor = BLACK; +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 +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--; + TextCopy( gestureLog[gestureLogIndex], GetGestureName(currentGesture) ); // Copy the gesture respective name to the gesture log array + } + + // 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 + //-------------------------------------------------------------------------------------- + Vector2 touchPosition[touchCount]; + 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 + //-------------------------------------------------------------------------------------- + #if defined( PLATFORM_WEB ) + const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); // Using Emscripten EM_ASM_INT macro, get the page canvas width + if (canvasWidth > 400) + { + screenWidth = canvasWidth; + } + else + { + screenWidth = 400; // Set a minimum width for the screen + } + #endif + + 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/examples/core/core_input_gestures_web.png b/examples/core/core_input_gestures_web.png new file mode 100644 index 0000000000000000000000000000000000000000..dd604e84b9c831d32229a8bdefc81d4ae161189a GIT binary patch literal 8937 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 literal 0 HcmV?d00001 diff --git a/src/rcore.c b/src/rcore.c index 6fd7a4fe1..45495bb15 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5593,8 +5593,14 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int gestureEvent.position[0].y /= (float)GetScreenHeight(); // Gesture data is sent to gestures-system for processing +#if defined(PLATFORM_WEB) + // 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); +#else ProcessGestureEvent(gestureEvent); #endif + +#endif } // GLFW3 Cursor Position Callback, runs on mouse move diff --git a/src/rgestures.h b/src/rgestures.h index 749f440aa..78dde76ee 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -127,7 +127,7 @@ void SetGesturesEnabled(unsigned int flags); // Enable a set of gestu bool IsGestureDetected(int gesture); // Check if a gesture have been detected int GetGestureDetected(void); // Get latest detected gesture -float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds +float GetGestureHoldDuration(void); // Get gesture hold time in seconds Vector2 GetGestureDragVector(void); // Get gesture drag vector float GetGestureDragAngle(void); // Get gesture drag angle Vector2 GetGesturePinchVector(void); // Get gesture pinch delta @@ -181,8 +181,8 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang #define FORCE_TO_SWIPE 0.0005f // Swipe force, measured in normalized screen units/time #define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f) #define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f) -#define TAP_TIMEOUT 300 // Tap minimum time, measured in milliseconds -#define PINCH_TIMEOUT 300 // Pinch minimum time, measured in milliseconds +#define TAP_TIMEOUT 0.3f // Tap minimum time, measured in seconds +#define PINCH_TIMEOUT 0.3f // Pinch minimum time, measured in seconds #define DOUBLETAP_RANGE 0.03f // DoubleTap range, measured in normalized screen units (0.0f to 1.0f) //---------------------------------------------------------------------------------- @@ -209,7 +209,7 @@ typedef struct { } Touch; struct { bool resetRequired; // HOLD reset to get first touch point again - double timeDuration; // HOLD duration in milliseconds + double timeDuration; // HOLD duration in seconds } Hold; struct { Vector2 vector; // DRAG vector (between initial and current position) @@ -522,7 +522,7 @@ static float rgVector2Distance(Vector2 v1, Vector2 v2) return result; } -// Time measure returned are milliseconds +// Time measure returned are seconds static double rgGetCurrentTime(void) { double time = 0; @@ -536,7 +536,7 @@ static double rgGetCurrentTime(void) QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation! QueryPerformanceCounter(¤tTime); - time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds + time = (double)currentTime/clockFrequency; // Time in seconds #endif #if defined(__linux__) @@ -545,7 +545,7 @@ static double rgGetCurrentTime(void) clock_gettime(CLOCK_MONOTONIC, &now); unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds - time = ((double)nowTime/1000000.0); // Time in miliseconds + time = ((double)nowTime*1e-9); // Time in seconds #endif #if defined(__APPLE__) @@ -561,7 +561,7 @@ static double rgGetCurrentTime(void) mach_port_deallocate(mach_task_self(), cclock); unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds - time = ((double)nowTime/1000000.0); // Time in miliseconds + time = ((double)nowTime*1e-9); // Time in seconds #endif #endif From 70286c7cdc6d972c63704ad957c18065f6a44cfe Mon Sep 17 00:00:00 2001 From: Michael Anghelone Date: Sun, 16 Jul 2023 07:08:55 -0400 Subject: [PATCH 0515/1710] Makefile change for cross compiling. (#3176) Working from wsl and compiling for windows this change makes it much easier to compile a static library for windows on arm. To compile a static library for windows on arm: ``` make PLATFORM=PLATFORM_DESKTOP TARGET_OS=WINDOWS CROSS_CC=/llvm/bin/aarch64-w64-mingw32-gcc CROSS_AR=/llvm/bin/aarch64-w64-mingw32-ar ``` This does not work to compile a shared library yet, only static. --- src/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Makefile b/src/Makefile index fc4c5b0c7..30ac232f8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -308,6 +308,17 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) AR = $(ANDROID_TOOLCHAIN)/bin/llvm-ar endif +# This section is here to help handle cross compilation. eg build on Linux for Windows. If you are doing this, you better know what you are doing! +ifdef TARGET_OS + ifdef CROSS_CC + CC = ${CROSS_CC} + endif + ifdef CROSS_AR + AR = ${CROSS_AR} + endif + PLATFORM_OS = ${TARGET_OS} +endif + # Define compiler flags: CFLAGS #------------------------------------------------------------------------------------------------ # -O1 defines optimization level From 86f95d71502bb6099dca0490aa8216b1684acf0f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 16 Jul 2023 13:24:49 +0200 Subject: [PATCH 0516/1710] Reviewed C compilation issues and formatting --- examples/core/core_input_gestures_web.c | 124 +++++++++++++----------- 1 file changed, 66 insertions(+), 58 deletions(-) diff --git a/examples/core/core_input_gestures_web.c b/examples/core/core_input_gestures_web.c index 0ee3d1c5e..0129a983b 100644 --- a/examples/core/core_input_gestures_web.c +++ b/examples/core/core_input_gestures_web.c @@ -14,7 +14,8 @@ ********************************************************************************************/ #include "raylib.h" -#include "math.h" // Required for the protractor angle graphic drawing + +#include "math.h" // Required for the protractor angle graphic drawing #if defined(PLATFORM_WEB) #include // Required for the Web/HTML5 @@ -26,14 +27,14 @@ // Common variables definitions //-------------------------------------------------------------------------------------- -int screenWidth = 800; +int screenWidth = 800; // Update depending on web canvas const int screenHeight = 450; -Vector2 messagePosition = {160, 7}; +Vector2 messagePosition = { 160, 7 }; // Last gesture variables definitions //-------------------------------------------------------------------------------------- int lastGesture = 0; -Vector2 lastGesturePosition = {165, 130}; +Vector2 lastGesturePosition = { 165, 130 }; // Gesture log variables definitions and functions declarations //-------------------------------------------------------------------------------------- @@ -41,6 +42,7 @@ Vector2 lastGesturePosition = {165, 130}; 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) { @@ -58,6 +60,7 @@ char const *GetGestureName(int i) default: return "Unknown"; break; } } + Color GetGestureColor(int i) { switch (i) { @@ -75,19 +78,21 @@ Color GetGestureColor(int i) default: return BLACK; break; } } -Color gestureColor = BLACK; + 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 -Rectangle logButton1 = {53, 7, 48, 26}; -Rectangle logButton2 = {108, 7, 36, 26}; -Vector2 gestureLogPosition = {10, 10}; + +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 finalVector = { 0.0f, 0.0f }; char currentAngleStr[7] = ""; -Vector2 protractorPosition = {266.0f, 315.0f}; +Vector2 protractorPosition = { 266.0f, 315.0f }; // Update //-------------------------------------------------------------------------------------- @@ -103,39 +108,40 @@ void Update(void) // Handle last gesture //-------------------------------------------------------------------------------------- - if ( currentGesture != 0 && currentGesture != 4 && currentGesture != previousGesture ) lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display + 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 (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - if ( CheckCollisionPointRec(GetMousePosition(), logButton1 ) ) + 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; + case 3: logMode=2; break; + case 2: logMode=3; break; + case 1: logMode=0; break; + default: logMode=1; break; } } - else if ( CheckCollisionPointRec(GetMousePosition(), logButton2) ) + 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; + 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; + if (((currentGesture != 4) && (currentGesture != previousGesture)) || (currentGesture < 3)) fillLog = 1; } else if (logMode == 2) // 2 shows repeated events but hide hold events { @@ -150,13 +156,16 @@ void Update(void) 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--; - TextCopy( gestureLog[gestureLogIndex], GetGestureName(currentGesture) ); // Copy the gesture respective name to the gesture log array + + // Copy the gesture respective name to the gesture log array + TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture)); } // Handle protractor @@ -173,12 +182,15 @@ void Update(void) { 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 + + 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 //-------------------------------------------------------------------------------------- - Vector2 touchPosition[touchCount]; + #define MAX_TOUCH_COUNT 32 + + Vector2 touchPosition[MAX_TOUCH_COUNT] = { 0 }; Vector2 mousePosition = {0, 0}; if (currentGesture != GESTURE_NONE) { @@ -186,16 +198,14 @@ void Update(void) { for (i = 0; i < touchCount; i++) touchPosition[i] = GetTouchPosition(i); // Fill the touch positions } - else - { - mousePosition = GetMousePosition(); - } + else mousePosition = GetMousePosition(); } // Draw //-------------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(RAYWHITE); + + ClearBackground(RAYWHITE); // Draw common //-------------------------------------------------------------------------------------- @@ -216,29 +226,30 @@ void Update(void) 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); + 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; + 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); + 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); + DrawRectangleRec(logButton2, logButton2Color); DrawText("Hide", logButton1.x + 62, logButton1.y + 3, 10, WHITE); DrawText("Hold", logButton1.x + 62, logButton1.y + 13, 10, WHITE); @@ -250,10 +261,10 @@ void Update(void) 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); + 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); @@ -262,7 +273,7 @@ void Update(void) 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); + if (currentAngleDegrees != 0.0f) DrawLineEx(protractorPosition, finalVector, 3.0f, gestureColor); // Draw touch and mouse pointer points //-------------------------------------------------------------------------------------- @@ -275,7 +286,8 @@ void Update(void) 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); + + if (touchCount == 2) DrawLineEx(touchPosition[0], touchPosition[1], ((currentGesture == 512)? 8 : 12), gestureColor); } else { @@ -297,15 +309,11 @@ int main(void) // Initialization //-------------------------------------------------------------------------------------- #if defined( PLATFORM_WEB ) - const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); // Using Emscripten EM_ASM_INT macro, get the page canvas width - if (canvasWidth > 400) - { - screenWidth = canvasWidth; - } - else - { - screenWidth = 400; // Set a minimum width for the screen - } + // Using Emscripten EM_ASM_INT macro, get the page canvas width + const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); + + if (canvasWidth > 400) screenWidth = canvasWidth; + else screenWidth = 400; // Set a minimum width for the screen #endif InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures web"); From e0c80f5ddd9543a6f086f3871f886e275c331a53 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 16 Jul 2023 20:18:38 +0200 Subject: [PATCH 0517/1710] Revert "Makefile change for cross compiling. (#3176)" This reverts commit 70286c7cdc6d972c63704ad957c18065f6a44cfe. --- src/Makefile | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Makefile b/src/Makefile index 30ac232f8..fc4c5b0c7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -308,17 +308,6 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) AR = $(ANDROID_TOOLCHAIN)/bin/llvm-ar endif -# This section is here to help handle cross compilation. eg build on Linux for Windows. If you are doing this, you better know what you are doing! -ifdef TARGET_OS - ifdef CROSS_CC - CC = ${CROSS_CC} - endif - ifdef CROSS_AR - AR = ${CROSS_AR} - endif - PLATFORM_OS = ${TARGET_OS} -endif - # Define compiler flags: CFLAGS #------------------------------------------------------------------------------------------------ # -O1 defines optimization level From 52541b4a1fb04d66de30c2a1e5f44358b2c1aee7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 18 Jul 2023 17:57:10 +0200 Subject: [PATCH 0518/1710] ADDED: `SUPPORT_FONT_ATLAS_WHITE_REC` Support creating a 3x3 pixels white rectangle at the bottom-right corner of the generated font atlas image, useful for shapes+text drawing in a single draw call! --- CMakeOptions.txt | 1 + src/config.h | 5 +++++ src/rtext.c | 27 ++++++++++++++++++++++----- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 326ed44e6..a239f1143 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -73,6 +73,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_TTF "Support loading font in TTF/OTF format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_TEXT_MANIPULATION "Support text manipulation functions" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(#define SUPPORT_FONT_ATLAS_WHITE_REC "Support white rec on font atlas bottom-right corner" ON CUSTOMIZE_BUILD ON) # rmodels.c cmake_dependent_option(SUPPORT_MESH_GENERATION "Support procedural mesh generation functions, uses external par_shapes.h library. NOTE: Some generated meshes DO NOT include generated texture coordinates" ON CUSTOMIZE_BUILD ON) diff --git a/src/config.h b/src/config.h index fbc7a5b47..67c6060b8 100644 --- a/src/config.h +++ b/src/config.h @@ -181,6 +181,11 @@ // If not defined, still some functions are supported: TextLength(), TextFormat() #define SUPPORT_TEXT_MANIPULATION 1 +// 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()]. +#define SUPPORT_FONT_ATLAS_WHITE_REC 1 + // rtext: Configuration values //------------------------------------------------------------------------------------ #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: diff --git a/src/rtext.c b/src/rtext.c index 3b21a93c9..a7d9903a8 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -6,14 +6,19 @@ * #define SUPPORT_MODULE_RTEXT * 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. +* * #define SUPPORT_FILEFORMAT_FNT * #define SUPPORT_FILEFORMAT_TTF * Selected desired fileformats to be supported for loading. Some of those formats are * supported by default, to remove support, just comment unrequired #define in this module * -* #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. +* #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()]. * * #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH * TextSplit() function static buffer max size @@ -734,7 +739,6 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC } #endif - atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; atlas.mipmaps = 1; @@ -839,7 +843,20 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC RL_FREE(nodes); RL_FREE(context); } - + +#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++) + { + ((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 + // Convert image data from GRAYSCALE to GRAY_ALPHA unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels From a9ff13a367b7bd166436b12b741b8c8199f1c85f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 18 Jul 2023 18:07:49 +0200 Subject: [PATCH 0519/1710] Update CMakeOptions.txt --- CMakeOptions.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index a239f1143..c643427c6 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -73,7 +73,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_TTF "Support loading font in TTF/OTF format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_TEXT_MANIPULATION "Support text manipulation functions" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(#define SUPPORT_FONT_ATLAS_WHITE_REC "Support white rec on font atlas bottom-right corner" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FONT_ATLAS_WHITE_REC "Support white rec on font atlas bottom-right corner" ON CUSTOMIZE_BUILD ON) # rmodels.c cmake_dependent_option(SUPPORT_MESH_GENERATION "Support procedural mesh generation functions, uses external par_shapes.h library. NOTE: Some generated meshes DO NOT include generated texture coordinates" ON CUSTOMIZE_BUILD ON) From 954c60100f2411dcfb6b57a3a39e96c71e4d2e52 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 19 Jul 2023 06:33:10 -0300 Subject: [PATCH 0520/1710] Fix GESTURE_DRAG and GESTURE_SWIPE_* issues (mostly) for web (#3183) --- src/rgestures.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rgestures.h b/src/rgestures.h index 78dde76ee..d8bb3b1d9 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -178,8 +178,9 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define FORCE_TO_SWIPE 0.0005f // Swipe force, measured in normalized screen units/time +#define FORCE_TO_SWIPE 0.2f // Swipe force, measured in normalized screen units/time #define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f) +#define DRAG_TIMEOUT 0.2f // Drag minimum time for web, measured in seconds #define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f) #define TAP_TIMEOUT 0.3f // Tap minimum time, measured in seconds #define PINCH_TIMEOUT 0.3f // Pinch minimum time, measured in seconds @@ -297,7 +298,8 @@ void ProcessGestureEvent(GestureEvent event) } else if (event.touchAction == TOUCH_ACTION_UP) { - if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0]; + // A swipe can happen while the current gesture is drag, but (specially for web) also hold, so set upPosition for both cases + if (GESTURES.current == GESTURE_DRAG || GESTURES.current == GESTURE_HOLD) GESTURES.Touch.upPosition = event.position[0]; // NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); @@ -348,7 +350,12 @@ void ProcessGestureEvent(GestureEvent event) GESTURES.Hold.resetRequired = false; // Detect GESTURE_DRAG +#if defined(PLATFORM_WEB) + // An alternative check to detect gesture drag is necessary since moveDownPositionA on touch for web is always zero, causing the distance calculation to be inaccurate + if ((rgGetCurrentTime() - GESTURES.Touch.eventTime) > DRAG_TIMEOUT) +#else if (rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_DRAG) +#endif { GESTURES.Touch.eventTime = rgGetCurrentTime(); GESTURES.current = GESTURE_DRAG; From d6f16b76649944f65491633a75ad648a6ddfdacf Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Wed, 19 Jul 2023 14:46:14 +0300 Subject: [PATCH 0521/1710] Update usage of 'sinf()' and 'cosf()' to be correct (#3181) * Update usage of 'sinf()' and 'cosf()' to be correct * Update formatting of arithmetic operations --- src/rshapes.c | 216 +++++++++++++++++++++++++++----------------------- 1 file changed, 117 insertions(+), 99 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index d726ff0e8..86e998a6d 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -393,14 +393,14 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); angle += (stepLength*2); } @@ -413,11 +413,11 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); @@ -432,8 +432,8 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + 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; } @@ -475,15 +475,15 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); } for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); angle += stepLength; } @@ -492,7 +492,7 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); } rlEnd(); } @@ -507,9 +507,9 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2f((float)centerX, (float)centerY); rlColor4ub(color2.r, color2.g, color2.b, color2.a); - rlVertex2f((float)centerX + sinf(DEG2RAD*i)*radius, (float)centerY + cosf(DEG2RAD*i)*radius); + rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radius, (float)centerY + sinf(DEG2RAD*(i + 10))*radius); rlColor4ub(color2.r, color2.g, color2.b, color2.a); - rlVertex2f((float)centerX + sinf(DEG2RAD*(i + 10))*radius, (float)centerY + cosf(DEG2RAD*(i + 10))*radius); + rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radius, (float)centerY + sinf(DEG2RAD*i)*radius); } rlEnd(); } @@ -530,8 +530,8 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) for (int i = 0; i < 360; i += 10) { - rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius); - rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius); + rlVertex2f(centerX + cosf(DEG2RAD*i)*radius, centerY + sinf(DEG2RAD*i)*radius); + rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radius, centerY + sinf(DEG2RAD*(i + 10))*radius); } rlEnd(); } @@ -544,8 +544,8 @@ void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color c { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f((float)centerX, (float)centerY); - rlVertex2f((float)centerX + sinf(DEG2RAD*i)*radiusH, (float)centerY + cosf(DEG2RAD*i)*radiusV); - rlVertex2f((float)centerX + sinf(DEG2RAD*(i + 10))*radiusH, (float)centerY + cosf(DEG2RAD*(i + 10))*radiusV); + 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); } rlEnd(); } @@ -557,8 +557,8 @@ void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Co for (int i = 0; i < 360; i += 10) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(centerX + sinf(DEG2RAD*i)*radiusH, centerY + cosf(DEG2RAD*i)*radiusV); - rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radiusH, centerY + cosf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radiusH, centerY + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(centerX + cosf(DEG2RAD*i)*radiusH, centerY + sinf(DEG2RAD*i)*radiusV); } rlEnd(); } @@ -616,17 +616,17 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA { rlColor4ub(color.r, color.g, color.b, color.a); - rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); angle += stepLength; } @@ -639,13 +639,13 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); angle += stepLength; } @@ -702,19 +702,19 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); } for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); angle += stepLength; } @@ -722,8 +722,8 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); } rlEnd(); } @@ -982,7 +982,7 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co }; const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; - const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; + const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f }; #if defined(SUPPORT_QUADS_DRAW_MODE) rlSetTexture(texShapes.id); @@ -1000,12 +1000,16 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co rlColor4ub(color.r, color.g, color.b, color.a); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + angle += (stepLength*2); } @@ -1015,10 +1019,13 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co rlColor4ub(color.r, color.g, color.b, color.a); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); } @@ -1093,8 +1100,8 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + 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; } } @@ -1208,7 +1215,7 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19 }; - const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; + const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f }; if (lineThick > 1) { @@ -1225,14 +1232,18 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); angle += stepLength; } @@ -1297,13 +1308,13 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); angle += stepLength; } @@ -1361,8 +1372,8 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); angle += stepLength; } } @@ -1492,7 +1503,8 @@ void DrawTriangleStrip(Vector2 *points, int pointCount, Color color) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) { if (sides < 3) sides = 3; - float centralAngle = rotation; + float centralAngle = rotation*DEG2RAD; + float angleStep = 360.0f/(float)sides*DEG2RAD; #if defined(SUPPORT_QUADS_DRAW_MODE) rlSetTexture(texShapes.id); @@ -1501,19 +1513,21 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col for (int i = 0; i < sides; i++) { rlColor4ub(color.r, color.g, color.b, color.a); + float nextAngle = centralAngle + angleStep; rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + centralAngle = nextAngle; } rlEnd(); rlSetTexture(0); @@ -1524,10 +1538,10 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle + angleStep)*radius, center.y + sinf(centralAngle + angleStep)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + centralAngle += angleStep; } rlEnd(); #endif @@ -1537,16 +1551,18 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color) { if (sides < 3) sides = 3; - float centralAngle = rotation; + float centralAngle = rotation*DEG2RAD; + float angleStep = 360.0f/(float)sides*DEG2RAD; rlBegin(RL_LINES); for (int i = 0; i < sides; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle + angleStep)*radius, center.y + sinf(centralAngle + angleStep)*radius); + + centralAngle += angleStep; } rlEnd(); } @@ -1554,8 +1570,8 @@ void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Colo void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color) { if (sides < 3) sides = 3; - float centralAngle = rotation; - float exteriorAngle = 360.0f/(float)sides; + float centralAngle = rotation*DEG2RAD; + float exteriorAngle = 360.0f/(float)sides*DEG2RAD; float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f)); #if defined(SUPPORT_QUADS_DRAW_MODE) @@ -1565,19 +1581,21 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl for (int i = 0; i < sides; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - - rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); + float nextAngle = centralAngle + exteriorAngle; rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); - centralAngle += exteriorAngle; - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); + rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius); + + centralAngle = nextAngle; } rlEnd(); rlSetTexture(0); @@ -1588,13 +1606,13 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl rlColor4ub(color.r, color.g, color.b, color.a); float nextAngle = centralAngle + exteriorAngle; - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*radius, center.y + cosf(DEG2RAD*nextAngle)*radius); + rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*radius, center.y + cosf(DEG2RAD*nextAngle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*innerRadius, center.y + cosf(DEG2RAD*nextAngle)*innerRadius); + rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius); + rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius); + rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius); centralAngle = nextAngle; } From 7124a14a60a4bf7e4357030395750e03a1b976b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Gonz=C3=A1lez=20Palomo?= Date: Wed, 19 Jul 2023 23:11:29 +0200 Subject: [PATCH 0522/1710] Document buffer format for audio processors. (#3186) --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index f9f36626e..c20a69d2b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1591,7 +1591,7 @@ RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream 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 +RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as s RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline #if defined(__cplusplus) From 055fd752c273f246515375d79fca511eef8ecf1b Mon Sep 17 00:00:00 2001 From: Danil <61111955+localwhale20@users.noreply.github.com> Date: Thu, 20 Jul 2023 11:56:35 +0300 Subject: [PATCH 0523/1710] Fixed GetMonitorName description (#3184) (#3189) --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index c20a69d2b..e99d674e2 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -974,7 +974,7 @@ RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specifi RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor -RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor +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 void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling From 5635e4214cb31b0c83e2d65f32af1cc3d40ffdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Gonz=C3=A1lez=20Palomo?= Date: Thu, 20 Jul 2023 13:41:35 +0200 Subject: [PATCH 0524/1710] Add note about sample format to AttachAudioStreamProcessor() (#3188) --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index e99d674e2..108ab0245 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1588,7 +1588,7 @@ RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan 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 +RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as s 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 s From 1310617a922c12256259eca69da0d746f5d10b71 Mon Sep 17 00:00:00 2001 From: smalltimewizard <83366732+smalltimewizard@users.noreply.github.com> Date: Thu, 20 Jul 2023 07:50:56 -0400 Subject: [PATCH 0525/1710] Optimization of ImageDrawRectangleRec() (#3185) A significant performance increase can be had by copying the first row to all other rows. --- src/rtextures.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index bc53e1cec..ca341bc81 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3135,26 +3135,28 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) if (rec.height < 0) rec.height = 0; int sy = (int)rec.y; - int ey = sy + (int)rec.height; - int sx = (int)rec.x; int bytesPerPixel = GetPixelDataSize(1, 1, dst->format); - for (int y = sy; y < ey; y++) + // Fill in the first pixel of the first row based on image format + ImageDrawPixel(dst, sx, sy, color); + + int bytesOffset = ((sy*dst->width) + sx)*bytesPerPixel; + 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++) { - // Fill in the first pixel of the row based on image format - ImageDrawPixel(dst, sx, y, color); - - int bytesOffset = ((y*dst->width) + sx)*bytesPerPixel; - 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++) - { - memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel); - } + memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel); } + + // Repeat the first row data for all other rows + int bytesPerRow = bytesPerPixel * (int)rec.width; + for (int y = 1; y < (int)rec.height; y++) + { + memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow); + } } // Draw rectangle lines within an image From ad2338b994785c887528c91a843d6720cac2c2b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20V=C3=A1clav=20Flasar?= <109527915+jakubvf@users.noreply.github.com> Date: Fri, 21 Jul 2023 14:08:35 +0200 Subject: [PATCH 0526/1710] build.zig: Support for building with PLAFORM_DRM (#3191) - Adds an option -Dplatform_drm when using zig build - When building for linux, checks whether -Dplatform_drm is present and configures the build accordingly. --- src/build.zig | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/build.zig b/src/build.zig index 8485322d5..64480a4f1 100644 --- a/src/build.zig +++ b/src/build.zig @@ -16,7 +16,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }); raylib.linkLibC(); - raylib.addIncludePath(srcdir ++ "/external/glfw/include"); + // No GLFW required on PLATFORM_DRM + if (!options.platform_drm) { + raylib.addIncludePath(srcdir ++ "/external/glfw/include"); + } raylib.addCSourceFiles(&.{ srcdir ++ "/raudio.c", @@ -49,15 +52,32 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.defineCMacro("PLATFORM_DESKTOP", null); }, .linux => { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); - raylib.linkSystemLibrary("GL"); - raylib.linkSystemLibrary("rt"); - raylib.linkSystemLibrary("dl"); - raylib.linkSystemLibrary("m"); - raylib.linkSystemLibrary("X11"); - raylib.addIncludePath("/usr/include"); + if (!options.platform_drm) { + raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.linkSystemLibrary("GL"); + raylib.linkSystemLibrary("rt"); + raylib.linkSystemLibrary("dl"); + raylib.linkSystemLibrary("m"); + raylib.linkSystemLibrary("X11"); + raylib.addIncludePath("/usr/include"); - raylib.defineCMacro("PLATFORM_DESKTOP", null); + raylib.defineCMacro("PLATFORM_DESKTOP", null); + } else { + raylib.linkSystemLibrary("GLESv2"); + raylib.linkSystemLibrary("EGL"); + raylib.linkSystemLibrary("drm"); + raylib.linkSystemLibrary("gbm"); + raylib.linkSystemLibrary("pthread"); + raylib.linkSystemLibrary("rt"); + raylib.linkSystemLibrary("m"); + raylib.linkSystemLibrary("dl"); + raylib.addIncludePath("/usr/include/libdrm"); + + raylib.defineCMacro("PLATFORM_DRM", null); + raylib.defineCMacro("GRAPHICS_API_OPENGL_ES2", null); + raylib.defineCMacro("EGL_NO_X11", null); + raylib.defineCMacro("DEFAULT_BATCH_BUFFER_ELEMENT", "2048"); + } }, .freebsd, .openbsd, .netbsd, .dragonfly => { raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); @@ -115,8 +135,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built return raylib; } -const Options = struct { +pub const Options = struct { raygui: bool = false, + platform_drm: bool = false, }; pub fn build(b: *std.Build) void { @@ -131,9 +152,11 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const raygui = b.option(bool, "raygui", "Compile with raygui support"); + const platform_drm = b.option(bool, "platform_drm", "Compile raylib in native mode (no X11)"); const lib = addRaylib(b, target, optimize, .{ .raygui = raygui orelse false, + .platform_drm = platform_drm orelse false, }); lib.installHeader("src/raylib.h", "raylib.h"); From 295e8c2a2faa7b0c80a840695e37f3ed1740eb88 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 22 Jul 2023 15:50:04 -0300 Subject: [PATCH 0527/1710] Optimize and simplify the gesture system (#3190) * Optimize and simplify the gesture system * Decouples GESTURE_SWIPE_* from GESTURE_DRAG --- src/rgestures.h | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/rgestures.h b/src/rgestures.h index d8bb3b1d9..779dbed56 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -180,7 +180,7 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang //---------------------------------------------------------------------------------- #define FORCE_TO_SWIPE 0.2f // Swipe force, measured in normalized screen units/time #define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f) -#define DRAG_TIMEOUT 0.2f // Drag minimum time for web, measured in seconds +#define DRAG_TIMEOUT 0.3f // Drag minimum time for web, measured in seconds #define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f) #define TAP_TIMEOUT 0.3f // Tap minimum time, measured in seconds #define PINCH_TIMEOUT 0.3f // Pinch minimum time, measured in seconds @@ -219,8 +219,7 @@ typedef struct { float intensity; // DRAG intensity, how far why did the DRAG (pixels per frame) } Drag; struct { - bool start; // SWIPE used to define when start measuring GESTURES.Swipe.timeDuration - double timeDuration; // SWIPE time to calculate drag intensity + double startTime; // SWIPE start time to calculate drag intensity } Swipe; struct { Vector2 vector; // PINCH vector (between first and second touch points) @@ -292,7 +291,7 @@ void ProcessGestureEvent(GestureEvent event) GESTURES.Touch.upPosition = GESTURES.Touch.downPositionA; GESTURES.Touch.eventTime = rgGetCurrentTime(); - GESTURES.Touch.firstId = event.pointId[0]; + GESTURES.Swipe.startTime = rgGetCurrentTime(); GESTURES.Drag.vector = (Vector2){ 0.0f, 0.0f }; } @@ -303,12 +302,10 @@ void ProcessGestureEvent(GestureEvent event) // NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); - GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.timeDuration)); - - GESTURES.Swipe.start = false; + GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.startTime)); // Detect GESTURE_SWIPE - if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.Touch.firstId == event.pointId[0])) + if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.current != GESTURE_DRAG)) { // NOTE: Angle should be inverted in Y GESTURES.Drag.angle = 360.0f - rgVector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); @@ -333,14 +330,6 @@ void ProcessGestureEvent(GestureEvent event) } else if (event.touchAction == TOUCH_ACTION_MOVE) { - if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.eventTime = rgGetCurrentTime(); - - if (!GESTURES.Swipe.start) - { - GESTURES.Swipe.timeDuration = rgGetCurrentTime(); - GESTURES.Swipe.start = true; - } - GESTURES.Touch.moveDownPositionA = event.position[0]; if (GESTURES.current == GESTURE_HOLD) @@ -350,12 +339,7 @@ void ProcessGestureEvent(GestureEvent event) GESTURES.Hold.resetRequired = false; // Detect GESTURE_DRAG -#if defined(PLATFORM_WEB) - // An alternative check to detect gesture drag is necessary since moveDownPositionA on touch for web is always zero, causing the distance calculation to be inaccurate if ((rgGetCurrentTime() - GESTURES.Touch.eventTime) > DRAG_TIMEOUT) -#else - if (rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_DRAG) -#endif { GESTURES.Touch.eventTime = rgGetCurrentTime(); GESTURES.current = GESTURE_DRAG; @@ -436,13 +420,6 @@ void UpdateGestures(void) GESTURES.Hold.timeDuration = rgGetCurrentTime(); } - if (((rgGetCurrentTime() - GESTURES.Touch.eventTime) > TAP_TIMEOUT) && (GESTURES.current == GESTURE_DRAG) && (GESTURES.Touch.pointCount < 2)) - { - GESTURES.current = GESTURE_HOLD; - GESTURES.Hold.timeDuration = rgGetCurrentTime(); - GESTURES.Hold.resetRequired = true; - } - // Detect GESTURE_NONE if ((GESTURES.current == GESTURE_SWIPE_RIGHT) || (GESTURES.current == GESTURE_SWIPE_UP) || (GESTURES.current == GESTURE_SWIPE_LEFT) || (GESTURES.current == GESTURE_SWIPE_DOWN)) { From 090b857912c338f0021987d0a4d5b42f9844e2c4 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 23 Jul 2023 15:35:41 -0300 Subject: [PATCH 0528/1710] Fix mouse wheel not working in PLATFORM_RPI or PLATFORM_DRM (#3193) --- src/rcore.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 45495bb15..d6aeaec96 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -468,6 +468,7 @@ typedef struct CoreData { Vector2 currentWheelMove; // Registers current mouse wheel variation Vector2 previousWheelMove; // Registers previous mouse wheel variation #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) + 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 #endif @@ -5062,7 +5063,8 @@ void PollInputEvents(void) // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; + CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; + CORE.Input.Mouse.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]; @@ -6677,7 +6679,7 @@ static void *EventThread(void *arg) gestureUpdate = true; } - if (event.code == REL_WHEEL) CORE.Input.Mouse.currentWheelMove.y += event.value; + if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; } // Absolute movement parsing From 32b54be5cbd9eb77fc36a4c044dce69c5640a26a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 24 Jul 2023 12:32:22 +0200 Subject: [PATCH 0529/1710] Update FAQ.md --- FAQ.md | 1 + 1 file changed, 1 insertion(+) diff --git a/FAQ.md b/FAQ.md index 274236d47..be06657b9 100644 --- a/FAQ.md +++ b/FAQ.md @@ -20,6 +20,7 @@ - [Does raylib support the Vulkan API?](#does-raylib-support-the-vulkan-api) - [What could I expect to see in raylib in the future?](#what-could-i-expect-to-see-in-raylib-in-the-future) - [Who are the raylib developers?](#who-are-the-raylib-developers) +- [MORE QUESTIONS...](https://github.com/raysan5/raylib/wiki/Frequently-Asked-Questions) ### What is raylib? From 298f93ef50cbf976ed497714e7cf5fbc0c1193e4 Mon Sep 17 00:00:00 2001 From: bohonghuang <1281299809@qq.com> Date: Wed, 26 Jul 2023 23:46:57 +0800 Subject: [PATCH 0530/1710] Fix `DrawBillboardPro` to allow `source` of negative size (#3197) (#3203) --- src/rmodels.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 49f922695..8c7e088cb 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3492,7 +3492,7 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint) { // NOTE: Billboard size will maintain source rectangle aspect ratio, size will represent billboard width - Vector2 sizeRatio = { size.x*(float)source.width/source.height, size.y }; + Vector2 sizeRatio = { size.x*fabsf((float)source.width/source.height), size.y }; Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); @@ -3558,21 +3558,40 @@ void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, tint.a); - // Bottom-left corner for texture and quad - rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); - rlVertex3f(topLeft.x, topLeft.y, topLeft.z); + if (sizeRatio.x * sizeRatio.y >= 0.0f) + { + // Bottom-left corner for texture and quad + rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); + rlVertex3f(topLeft.x, topLeft.y, topLeft.z); - // Top-left corner for texture and quad - rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height); - rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); + // Top-left corner for texture and quad + rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height); + rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); - // Top-right corner for texture and quad - rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height); - rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); + // Top-right corner for texture and quad + rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height); + rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); + + // Bottom-right corner for texture and quad + rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); + rlVertex3f(topRight.x, topRight.y, topRight.z); + } + else + { + // Reverse vertex order if the size has only one negative dimension + rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); + rlVertex3f(topRight.x, topRight.y, topRight.z); + + rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height); + rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); + + rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height); + rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); + + rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); + rlVertex3f(topLeft.x, topLeft.y, topLeft.z); + } - // Bottom-right corner for texture and quad - rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); - rlVertex3f(topRight.x, topRight.y, topRight.z); rlEnd(); rlSetTexture(0); From ac6f889dfcaf4c195c13abed09522ecebd03b0f9 Mon Sep 17 00:00:00 2001 From: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Date: Wed, 26 Jul 2023 11:50:07 -0400 Subject: [PATCH 0531/1710] Fix misleading indentation in src/Makefile (#3202) --- src/Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Makefile b/src/Makefile index fc4c5b0c7..4f742a259 100644 --- a/src/Makefile +++ b/src/Makefile @@ -801,19 +801,19 @@ ifeq ($(ROOT),root) # and $(RAYLIB_H_INSTALL_PATH). Please confirm each item. ifeq ($(PLATFORM_OS),LINUX) ifeq ($(RAYLIB_LIBTYPE),SHARED) - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_API_VERSION) - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_VERSION) - # Uncomment to clean up the runtime linker cache. See install target. - ldconfig + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_API_VERSION) + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_VERSION) + # Uncomment to clean up the runtime linker cache. See install target. + ldconfig else - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.a + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.a endif rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/raylib.h rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/raymath.h rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/rlgl.h @echo "raylib development files removed!" - else + else @echo "This function currently works on GNU/Linux systems. Add yours today (^;" endif else From 5d28bad0adf2a85a48f6ce4644ce728ec9868208 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Thu, 27 Jul 2023 22:41:43 +0200 Subject: [PATCH 0532/1710] Fix LoadTextureCubemap for manual layouts (#3204) --- src/rtextures.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index ca341bc81..c13e331cd 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3369,10 +3369,16 @@ TextureCubemap LoadTextureCubemap(Image image, int layout) if ((image.height/6) == image.width) { layout = CUBEMAP_LAYOUT_LINE_VERTICAL; cubemap.width = image.height/6; } else if ((image.width/3) == (image.height/4)) { layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; } } - - cubemap.height = cubemap.width; + } else { + if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL) cubemap.width = image.height/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; + // Layout provided or already auto-detected if (layout != CUBEMAP_LAYOUT_AUTO_DETECT) { From 962030e70ad3dfcce5d4642ad65890fde93ad117 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:08:41 -0300 Subject: [PATCH 0533/1710] Changes SetWindowMonitor() to no longer force fullscreen (#3209) * Changes SetWindowMonitor() to no longer force fullscreen * Readds fullscreen support --- src/raylib.h | 2 +- src/rcore.c | 32 ++++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 108ab0245..b3f17bd53 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -954,7 +954,7 @@ RLAPI void SetWindowIcon(Image image); // Set icon fo 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) RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) -RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) +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 SetWindowSize(int width, int height); // Set window dimensions RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) diff --git a/src/rcore.c b/src/rcore.c index d6aeaec96..66a914ee7 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1661,7 +1661,7 @@ void SetWindowPosition(int x, int y) #endif } -// Set monitor for the current window (fullscreen mode) +// Set monitor for the current window void SetWindowMonitor(int monitor) { #if defined(PLATFORM_DESKTOP) @@ -1670,10 +1670,34 @@ void SetWindowMonitor(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + if (CORE.Window.fullscreen) + { + TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + } + else + { + TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + + const int screenWidth = CORE.Window.screen.width; + const int screenHeight = CORE.Window.screen.height; + int monitorWorkareaX = 0; + int monitorWorkareaY = 0; + int monitorWorkareaWidth = 0; + int monitorWorkareaHeight = 0; + glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); + + // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it + if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); + else + { + const int x = monitorWorkareaX + (monitorWorkareaWidth*0.5f) - (screenWidth*0.5f); + const int y = monitorWorkareaY + (monitorWorkareaHeight*0.5f) - (screenHeight*0.5f); + glfwSetWindowPos(CORE.Window.handle, x, y); + } + } } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); #endif From 44659b7ba8bd6d517d75fac8675ecd026f713240 Mon Sep 17 00:00:00 2001 From: ndytts Date: Sat, 29 Jul 2023 07:46:04 -0400 Subject: [PATCH 0534/1710] Fix android soname in src/Makefile (#3211) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 4f742a259..e9b68cd93 100644 --- a/src/Makefile +++ b/src/Makefile @@ -516,7 +516,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) - LDFLAGS += -Wl,-soname,libraylib.$(API_VERSION).so -Wl,--exclude-libs,libatomic.a + LDFLAGS += -Wl,-soname,libraylib.$(RAYLIB_API_VERSION).so -Wl,--exclude-libs,libatomic.a LDFLAGS += -Wl,--build-id -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings # Force linking of library module to define symbol LDFLAGS += -u ANativeActivity_onCreate From d3ea64983212f7451a9cfbf644da8a5c43dbc706 Mon Sep 17 00:00:00 2001 From: Alexander Klingenbeck Date: Sun, 30 Jul 2023 19:08:59 +0200 Subject: [PATCH 0535/1710] Update BINDINGS.md to include rayjs (#3212) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 7fc88113e..ec66d4ab5 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -75,7 +75,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | hare-raylib | **auto** | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | | raylib-sunder | **auto** | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | | rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | - +| rayjs | 4.6-dev | [QuickJS](https://bellard.org/quickjs/) | MIT | https://github.com/mode777/rayjs | ### Utility Wrapers These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. From 4fd40f0333e48ad0b10a853eacc367b8ac8cc02f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 1 Aug 2023 05:42:50 -0300 Subject: [PATCH 0536/1710] Fixes GetCurrentMonitor() detection inconsistency issue (#3215) --- src/rcore.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 66a914ee7..282fec17e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1835,20 +1835,24 @@ int GetCurrentMonitor(void) int mx = 0; int my = 0; - int width = 0; - int height = 0; - monitor = monitors[i]; - glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); - - if ((x >= mx) && - (x < (mx + width)) && - (y >= my) && - (y < (my + height))) + glfwGetMonitorPos(monitor, &mx, &my); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + if (mode) { - index = i; - break; + const int width = mode->width; + const int height = mode->height; + + if ((x >= mx) && + (x < (mx + width)) && + (y >= my) && + (y < (my + height))) + { + index = i; + break; + } } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } } } From e7664d5684f4b7c487d2a08645f23a1d0485f9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20C=2E=20Fran=C3=A7a?= Date: Tue, 1 Aug 2023 05:46:40 -0300 Subject: [PATCH 0537/1710] build change (#3214) ref.: https://github.com/ziglang/zig/pull/16446 --- src/build.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/build.zig b/src/build.zig index 64480a4f1..28a5d727d 100644 --- a/src/build.zig +++ b/src/build.zig @@ -18,7 +18,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built // No GLFW required on PLATFORM_DRM if (!options.platform_drm) { - raylib.addIncludePath(srcdir ++ "/external/glfw/include"); + raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } raylib.addCSourceFiles(&.{ @@ -36,9 +36,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built if (options.raygui) { _ = gen_step.add(srcdir ++ "/raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); - raylib.addCSourceFile(srcdir ++ "/raygui.c", raylib_flags); - raylib.addIncludePath(srcdir); - raylib.addIncludePath(srcdir ++ "/../../raygui/src"); + raylib.addCSourceFile(.{ .file = .{ .path = srcdir ++ "/raygui.c" }, .flags = raylib_flags }); + raylib.addIncludePath(.{ .path = srcdir }); + raylib.addIncludePath(.{ .path = srcdir ++ "/../../raygui/src" }); } switch (target.getOsTag()) { @@ -47,7 +47,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); - raylib.addIncludePath("external/glfw/deps/mingw"); + raylib.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); raylib.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -59,7 +59,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("dl"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("X11"); - raylib.addIncludePath("/usr/include"); + raylib.addIncludePath(.{ .path = "/usr/include" }); raylib.defineCMacro("PLATFORM_DESKTOP", null); } else { @@ -71,7 +71,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("dl"); - raylib.addIncludePath("/usr/include/libdrm"); + raylib.addIncludePath(.{ .path = "/usr/include/libdrm" }); raylib.defineCMacro("PLATFORM_DRM", null); raylib.defineCMacro("GRAPHICS_API_OPENGL_ES2", null); @@ -125,7 +125,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{ .access_sub_paths = true, .no_follow = true }) catch @panic("No emscripten cache. Generate it!"); dir.close(); - raylib.addIncludePath(cache_include); + raylib.addIncludePath(.{ .path = cache_include }); }, else => { @panic("Unsupported OS"); From b60c69181663a5e7f2cb0ec04c5e7c388cdcad3e Mon Sep 17 00:00:00 2001 From: Joseph Montanez Date: Wed, 2 Aug 2023 00:05:02 -0700 Subject: [PATCH 0538/1710] Update BINDINGS.md (#3217) Updated Raylib-PHP to support Raylib 4.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index ec66d4ab5..00672d9bf 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -56,7 +56,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylibpyctbg | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | | raylib-py | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | | raylib-python-ctypes | 4.6-dev | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | -| raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | +| raylib-php | **4.5** | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | | raylib-ffi | 4.5 | [Rust](https://www.rust-lang.org/) | GPLv3 | https://github.com/ewpratten/raylib-ffi | From 04678bc585b51bf218310387b048e846ca4a7718 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 2 Aug 2023 10:12:38 -0700 Subject: [PATCH 0539/1710] int math done with floats causes warnings. (#3218) --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 282fec17e..fc87e7354 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1693,8 +1693,8 @@ void SetWindowMonitor(int monitor) if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); else { - const int x = monitorWorkareaX + (monitorWorkareaWidth*0.5f) - (screenWidth*0.5f); - const int y = monitorWorkareaY + (monitorWorkareaHeight*0.5f) - (screenHeight*0.5f); + const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); + const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); glfwSetWindowPos(CORE.Window.handle, x, y); } } From 464e714a2e256e7501239e0a5a12a2e9ee4e7f04 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:16:35 -0300 Subject: [PATCH 0540/1710] Adds BORDERLESS_WINDOWED_MODE for PLATFORM_DESKTOP (#3216) --- src/raylib.h | 2 ++ src/rcore.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index b3f17bd53..45a6120f5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -526,6 +526,7 @@ typedef enum { FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer FLAG_WINDOW_HIGHDPI = 0x00002000, // Set to support HighDPI FLAG_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED + FLAG_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D) } ConfigFlags; @@ -947,6 +948,7 @@ RLAPI bool IsWindowState(unsigned int flag); // Check if on RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) 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) diff --git a/src/rcore.c b/src/rcore.c index fc87e7354..d5addad48 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -412,6 +412,8 @@ typedef struct CoreData { Size render; // Framebuffer width and height (render area, including black bars if required) Point renderOffset; // Offset from render area (must be divided by 2) Matrix screenScale; // Matrix to scale screen (framebuffer rendering) + Point previousPosition; // Previous screen position (required on borderless windowed toggle) + Size previousScreen; // Previous screen size (required on borderless windowed toggle) char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings @@ -1324,6 +1326,82 @@ void ToggleFullscreen(void) #endif } +// Toggle borderless windowed mode (only PLATFORM_DESKTOP) +void ToggleBorderlessWindowed(void) +{ +#if defined(PLATFORM_DESKTOP) + // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it + bool wasOnFullscreen = false; + if (CORE.Window.fullscreen) + { + CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + wasOnFullscreen = true; + } + + const int monitor = GetCurrentMonitor(); + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + if (mode) + { + if (!IsWindowState(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) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + CORE.Window.previousScreen = CORE.Window.screen; + + // Set undecorated and topmost modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + + // 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; + glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + + // Set screen position and size + glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); + glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + // Remove topmost and undecorated modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + 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(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +#endif +} + // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) void MaximizeWindow(void) { @@ -1373,6 +1451,13 @@ void SetWindowState(unsigned int flags) 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)) + { + ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function + } + // State change: FLAG_FULLSCREEN_MODE if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE)) { @@ -1483,6 +1568,13 @@ void ClearWindowState(unsigned int flags) 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)) + { + 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)) { From 5b4aaf4eb1b848d023b3bac0ce7a8e34faab9ba1 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 3 Aug 2023 05:29:45 -0300 Subject: [PATCH 0541/1710] Adds CMake option for SUPPORT_CUSTOM_FRAME_CONTROL (#3221) --- CMakeOptions.txt | 1 + cmake/CompileDefinitions.cmake | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index c643427c6..cc57a851f 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -46,6 +46,7 @@ cmake_dependent_option(SUPPORT_BUSY_WAIT_LOOP "Use busy wait loop for timing syn cmake_dependent_option(SUPPORT_EVENTS_WAITING "Wait for events passively (sleeping while no events) instead of polling them actively every frame" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_WINMM_HIGHRES_TIMER "Setting a higher resolution can improve the accuracy of time-out intervals in wait functions" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_COMPRESSION_API "Support for compression API" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_CUSTOM_FRAME_CONTROL "Enabling this flag allows manual control of the frame processes, use at your own risk" OFF CUSTOMIZE_BUILD OFF) # rshapes.c cmake_dependent_option(SUPPORT_QUADS_DRAW_MODE "Use QUADS instead of TRIANGLES for drawing when possible. Some lines-based shapes could still use lines" ON CUSTOMIZE_BUILD ON) diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake index 749222455..689f98ac4 100644 --- a/cmake/CompileDefinitions.cmake +++ b/cmake/CompileDefinitions.cmake @@ -28,6 +28,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_EVENTS_WAITING) define_if("raylib" SUPPORT_WINMM_HIGHRES_TIMER) define_if("raylib" SUPPORT_COMPRESSION_API) + define_if("raylib" SUPPORT_CUSTOM_FRAME_CONTROL) define_if("raylib" SUPPORT_QUADS_DRAW_MODE) define_if("raylib" SUPPORT_IMAGE_EXPORT) define_if("raylib" SUPPORT_IMAGE_GENERATION) @@ -69,17 +70,17 @@ if (${CUSTOMIZE_BUILD}) else () target_compile_definitions("raylib" PUBLIC "MAX_FILEPATH_LENGTH=512") endif () - + target_compile_definitions("raylib" PUBLIC "MAX_GAMEPADS=4") target_compile_definitions("raylib" PUBLIC "MAX_GAMEPAD_AXIS=8") target_compile_definitions("raylib" PUBLIC "MAX_GAMEPAD_BUTTONS=32") target_compile_definitions("raylib" PUBLIC "MAX_TOUCH_POINTS=10") target_compile_definitions("raylib" PUBLIC "MAX_KEY_PRESSED_QUEUE=16") - + target_compile_definitions("raylib" PUBLIC "STORAGE_DATA_FILE=\"storage.data\"") target_compile_definitions("raylib" PUBLIC "MAX_CHAR_PRESSED_QUEUE=16") target_compile_definitions("raylib" PUBLIC "MAX_DECOMPRESSION_SIZE=64") - + if (${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_33" OR ${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_11") target_compile_definitions("raylib" PUBLIC "DEFAULT_BATCH_BUFFER_ELEMENTS=8192") elseif (${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_ES2") From d3058fe58972b80684591237c6358104f9e28ba0 Mon Sep 17 00:00:00 2001 From: vitopigno <103512727+VitusVeit@users.noreply.github.com> Date: Fri, 4 Aug 2023 12:04:19 +0200 Subject: [PATCH 0542/1710] [CORE] Support for SetWindowTitle and InitWindow for web (#3222) * Update raylib.h Changed SetWindowTitle's description * Update rcore.c SetWindowTitle now works on web * Update rcore.c InitWindow title now works with web platform too. --- src/raylib.h | 2 +- src/rcore.c | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 45a6120f5..8b5a80700 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -954,7 +954,7 @@ RLAPI void MinimizeWindow(void); // Set window 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) +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 SetWindowMonitor(int monitor); // Set monitor for the current window RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) diff --git a/src/rcore.c b/src/rcore.c index d5addad48..344e851f3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1736,13 +1736,16 @@ void SetWindowIcons(Image *images, int count) #endif } -// Set title for window (only PLATFORM_DESKTOP) +// Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) void SetWindowTitle(const char *title) { CORE.Window.title = title; #if defined(PLATFORM_DESKTOP) glfwSetWindowTitle(CORE.Window.handle, title); #endif +#if defined(PLATFORM_WEB) + emscripten_set_window_title(title); +#endif } // Set window position on screen (windowed mode) @@ -4433,6 +4436,11 @@ static bool InitGraphicsDevice(int width, int height) return false; } +// glfwCreateWindow title doesn't work with emscripten. +#if defined(PLATFORM_WEB) + emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); +#endif + // Set window callback events glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! #if !defined(PLATFORM_WEB) From 601cadbae6a7561d2114de891f374a2d9f2f497e Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 4 Aug 2023 07:40:10 -0700 Subject: [PATCH 0543/1710] [AUDIO] Add a function to make an alias of a sound and share it's sample data (#3219) * Add a function to clone a sound and share data with another sound. * rename items based on feedback * PR Feedback, use custom unload for sound alias, not variant of normal sound unloading --- src/raudio.c | 37 +++++++++++++++++++++++++++++++++++++ src/raylib.h | 2 ++ 2 files changed, 39 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index 5e2ccf996..5f84688d9 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -916,6 +916,32 @@ Sound LoadSoundFromWave(Wave wave) return sound; } +// Clone sound from existing sound data, clone does not own wave data +// Wave data must +// NOTE: Wave data must be unallocated manually and will be shared across all clones +Sound LoadSoundAlias(Sound source) +{ + Sound sound = { 0 }; + + if (source.stream.buffer->data != NULL) + { + AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, source.frameCount, AUDIO_BUFFER_USAGE_STATIC); + if (audioBuffer == NULL) + { + TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); + return sound; // early return to avoid dereferencing the audioBuffer null pointer + } + audioBuffer->data = source.stream.buffer->data; + sound.frameCount = source.frameCount; + sound.stream.sampleRate = AUDIO.System.device.sampleRate; + sound.stream.sampleSize = 32; + sound.stream.channels = AUDIO_DEVICE_CHANNELS; + sound.stream.buffer = audioBuffer; + } + + return sound; +} + // Checks if a sound is ready bool IsSoundReady(Sound sound) { @@ -940,6 +966,17 @@ void UnloadSound(Sound sound) //TRACELOG(LOG_INFO, "SOUND: Unloaded sound data from RAM"); } +void UnloadSoundAlias(Sound alias) +{ + // untrack and unload just the sound buffer, not the sample data, it is shared with the source for the alias + if (alias.stream.buffer != NULL) + { + ma_data_converter_uninit(&alias.stream.buffer->converter, NULL); + UntrackAudioBuffer(alias.stream.buffer); + RL_FREE(alias.stream.buffer); + } +} + // Update sound buffer with new data void UpdateSound(Sound sound, const void *data, int sampleCount) { diff --git a/src/raylib.h b/src/raylib.h index 8b5a80700..a49e19a57 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1533,10 +1533,12 @@ RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileDat RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready 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 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 +RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data) RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success From 62f5382d560bde7fca0565dedfebcdded34ade96 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 4 Aug 2023 08:14:04 -0700 Subject: [PATCH 0544/1710] [AUDIO] Add an example of how to use LoadSoundAlias (#3223) * Add a function to clone a sound and share data with another sound. * rename items based on feedback * PR Feedback, use custom unload for sound alias, not variant of normal sound unloading * sound_multi example --- examples/Makefile | 1 + examples/audio/audio_sound_multi.c | 87 ++++ .../VS2022/examples/audio_sound_multi.vcxproj | 390 ++++++++++++++++++ projects/VS2022/raylib.sln | 35 +- 4 files changed, 497 insertions(+), 16 deletions(-) create mode 100644 examples/audio/audio_sound_multi.c create mode 100644 projects/VS2022/examples/audio_sound_multi.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 1a4d5f06c..ff5098948 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -534,6 +534,7 @@ AUDIO = \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ + audio/audio_sound_multi \ audio/audio_stream_effects \ audio/audio_mixed_processor diff --git a/examples/audio/audio_sound_multi.c b/examples/audio/audio_sound_multi.c new file mode 100644 index 000000000..d5472efab --- /dev/null +++ b/examples/audio/audio_sound_multi.c @@ -0,0 +1,87 @@ +/******************************************************************************************* +* +* raylib [audio] example - Playing sound multiple times +* +* Example originally created 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 Jeffery Myers (@JeffM2501) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MAX_SOUNDS 10 +Sound soundArray[MAX_SOUNDS] = { 0 }; +int currentSound; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - playing sound multiple times"); + + 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 + + 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)) + { + 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; + + // Note: a better way would be to look at the list for the first sound that is not playing and use that slot + } + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Press SPACE to PLAY a WAV sound!", 200, 180, 20, LIGHTGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + 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 + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/projects/VS2022/examples/audio_sound_multi.vcxproj b/projects/VS2022/examples/audio_sound_multi.vcxproj new file mode 100644 index 000000000..5bfed78fb --- /dev/null +++ b/projects/VS2022/examples/audio_sound_multi.vcxproj @@ -0,0 +1,390 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {F81C5819-85B4-4D2E-B6DC-104A7634461B} + Win32Proj + audio_sound_multi + 10.0 + audio_sound_multi + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index f58f2ed75..173637554 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -269,6 +269,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_write_depth", "exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hybrid_render", "examples\shaders_hybrid_render.vcxproj", "{3755E9F4-CB48-4EC3-B561-3B85964EBDEF}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "examples\audio_sound_multi.vcxproj", "{F81C5819-85B4-4D2E-B6DC-104A7634461B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -377,22 +379,6 @@ Global {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x64.Build.0 = Release|x64 {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x86.ActiveCfg = Release|Win32 {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x86.Build.0 = Release|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug|x64.ActiveCfg = Debug|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug|x64.Build.0 = Debug|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug|x86.ActiveCfg = Debug|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug|x86.Build.0 = Debug|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release|x64.ActiveCfg = Release|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release|x64.Build.0 = Release|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release|x86.ActiveCfg = Release|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release|x86.Build.0 = Release|Win32 {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 @@ -2277,6 +2263,22 @@ Global {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x64.Build.0 = Release|x64 {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.ActiveCfg = Release|Win32 {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.Build.0 = Release|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x64.ActiveCfg = Debug|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x64.Build.0 = Debug|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x86.ActiveCfg = Debug|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x86.Build.0 = Debug|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.ActiveCfg = Release|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.Build.0 = Release|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.ActiveCfg = Release|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2413,6 +2415,7 @@ Global {6D9E00D8-2893-45E4-9363-3F7F61D416BD} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {70B35F59-AFC2-4D8F-8833-5314D2047A81} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 6094869e3e845e90e1e8ae41b98e889fb3e13e78 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 4 Aug 2023 18:14:47 +0200 Subject: [PATCH 0545/1710] Fix material loading #3126 --- src/external/tinyobj_loader_c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index 6d34d25f7..502a55a7e 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -746,7 +746,7 @@ static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, (*materials_out) = NULL; (*num_materials_out) = 0; - fp = fopen(filename, "r"); + fp = fopen(filename, "rt"); if (!fp) { fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno); return TINYOBJ_ERROR_FILE_OPERATION; From dc621ca388077a8bd814c8d399dea09efbfc25ce Mon Sep 17 00:00:00 2001 From: Nikolas Date: Sat, 5 Aug 2023 23:16:26 +0200 Subject: [PATCH 0546/1710] Support 16-Bit HDR textures (#3220) * Support 16-Bit HDR textures * Fix build on emscripten * Move helper functions --- src/raylib.h | 3 + src/rlgl.h | 39 ++++++--- src/rtextures.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 238 insertions(+), 12 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index a49e19a57..f7b0df66b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -812,6 +812,9 @@ typedef enum { PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp diff --git a/src/rlgl.h b/src/rlgl.h index 470295b0a..717fa59fb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -419,6 +419,9 @@ typedef enum { RL_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + RL_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) RL_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp @@ -1006,6 +1009,7 @@ typedef struct rlglData { bool texDepth; // Depth textures supported (GL_ARB_depth_texture, GL_OES_depth_texture) bool texDepthWebGL; // Depth textures supported WebGL specific (GL_WEBGL_depth_texture) bool texFloat32; // float textures support (32 bit per channel) (GL_OES_texture_float) + bool texFloat16; // half float textures support (16 bit per channel) (GL_OES_texture_half_float) bool texCompDXT; // DDS texture compression support (GL_EXT_texture_compression_s3tc, GL_WEBGL_compressed_texture_s3tc, GL_WEBKIT_WEBGL_compressed_texture_s3tc) bool texCompETC1; // ETC1 texture compression support (GL_OES_compressed_ETC1_RGB8_texture, GL_WEBGL_compressed_texture_etc1) bool texCompETC2; // ETC2/EAC texture compression support (GL_ARB_ES3_compatibility) @@ -2189,6 +2193,7 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.instancing = (GLAD_GL_EXT_draw_instanced && GLAD_GL_ARB_instanced_arrays); RLGL.ExtSupported.texNPOT = GLAD_GL_ARB_texture_non_power_of_two; RLGL.ExtSupported.texFloat32 = GLAD_GL_ARB_texture_float; + RLGL.ExtSupported.texFloat16 = GLAD_GL_ARB_texture_float; RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture; RLGL.ExtSupported.maxDepthBits = 32; RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic; @@ -2200,6 +2205,7 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.instancing = true; RLGL.ExtSupported.texNPOT = true; RLGL.ExtSupported.texFloat32 = true; + RLGL.ExtSupported.texFloat16 = true; RLGL.ExtSupported.texDepth = true; RLGL.ExtSupported.maxDepthBits = 32; RLGL.ExtSupported.texAnisoFilter = true; @@ -2224,6 +2230,7 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.instancing = true; RLGL.ExtSupported.texNPOT = true; RLGL.ExtSupported.texFloat32 = true; + RLGL.ExtSupported.texFloat16 = true; RLGL.ExtSupported.texDepth = true; RLGL.ExtSupported.texDepthWebGL = true; RLGL.ExtSupported.maxDepthBits = 24; @@ -2320,6 +2327,7 @@ void rlLoadExtensions(void *loader) // Check texture float support if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) RLGL.ExtSupported.texFloat32 = true; + if (strcmp(extList[i], (const char *)"GL_OES_texture_half_float") == 0) RLGL.ExtSupported.texFloat16 = true; // Check depth texture support if (strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) RLGL.ExtSupported.texDepth = true; @@ -3163,13 +3171,9 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format) { if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) { - if (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32) - { - // Instead of using a sized internal texture format (GL_RGB16F, GL_RGB32F), we let the driver to choose the better format for us (GL_RGB) - if (RLGL.ExtSupported.texFloat32) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); - else TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); - } - else if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); + 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); } else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); @@ -3256,10 +3260,16 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F_EXT; *glFormat = GL_RED_EXT; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F_EXT; *glFormat = GL_RGB; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F_EXT; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_R16F_EXT; *glFormat = GL_RED_EXT; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB16F_EXT; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA16F_EXT; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT; break; #else - case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float #endif #endif #elif defined(GRAPHICS_API_OPENGL_33) @@ -3273,6 +3283,9 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_R16F; *glFormat = GL_RED; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB16F; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA16F; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT; break; #endif #if !defined(GRAPHICS_API_OPENGL_11) case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; @@ -4480,6 +4493,9 @@ const char *rlGetPixelFormatName(unsigned int format) case RL_PIXELFORMAT_UNCOMPRESSED_R32: return "R32"; break; // 32 bpp (1 channel - float) case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: return "R32G32B32"; break; // 32*3 bpp (3 channels - float) case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: return "R32G32B32A32"; break; // 32*4 bpp (4 channels - float) + case RL_PIXELFORMAT_UNCOMPRESSED_R16: return "R16"; break; // 16 bpp (1 channel - half float) + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: return "R16G16B16"; break; // 16*3 bpp (3 channels - half float) + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: return "R16G16B16A16"; break; // 16*4 bpp (4 channels - half float) case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: return "DXT1_RGB"; break; // 4 bpp (no alpha) case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: return "DXT1_RGBA"; break; // 4 bpp (1 bit alpha) case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: return "DXT3_RGBA"; break; // 8 bpp @@ -4721,6 +4737,9 @@ static int rlGetPixelDataSize(int width, int height, int format) case RL_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16: bpp = 16; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 16*3; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 16*4; break; case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: diff --git a/src/rtextures.c b/src/rtextures.c index c13e331cd..501e3fdea 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -223,6 +223,8 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- +static float HalfToFloat(unsigned short x); +static unsigned short FloatToHalf(float x); static Vector4 *LoadImageDataNormalized(Image image); // Load pixel data from image as Vector4 array (float normalized) //---------------------------------------------------------------------------------- @@ -1286,6 +1288,40 @@ void ImageFormat(Image *image, int newFormat) ((float *)image->data)[i + 3] = pixels[k].w; } } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + // WARNING: Image is converted to GRAYSCALE equivalent 16bit + + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short)); + + for (int i = 0; i < image->width*image->height; i++) + { + ((unsigned short *)image->data)[i] = FloatToHalf((float)(pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f)); + } + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*3*sizeof(unsigned short)); + + for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++) + { + ((unsigned short *)image->data)[i] = FloatToHalf(pixels[k].x); + ((unsigned short *)image->data)[i + 1] = FloatToHalf(pixels[k].y); + ((unsigned short *)image->data)[i + 2] = FloatToHalf(pixels[k].z); + } + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*4*sizeof(unsigned short)); + + for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++) + { + ((unsigned short *)image->data)[i] = FloatToHalf(pixels[k].x); + ((unsigned short *)image->data)[i + 1] = FloatToHalf(pixels[k].y); + ((unsigned short *)image->data)[i + 2] = FloatToHalf(pixels[k].z); + ((unsigned short *)image->data)[i + 3] = FloatToHalf(pixels[k].w); + } + } break; default: break; } @@ -1652,6 +1688,19 @@ void ImageAlphaClear(Image *image, Color color, float threshold) } } } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + for (int i = 3; i < image->width*image->height*4; i += 4) + { + if (HalfToFloat(((unsigned short *)image->data)[i]) <= threshold) + { + ((unsigned short *)image->data)[i - 3] = FloatToHalf((float)color.r/255.0f); + ((unsigned short *)image->data)[i - 2] = FloatToHalf((float)color.g/255.0f); + ((unsigned short *)image->data)[i - 1] = FloatToHalf((float)color.b/255.0f); + ((unsigned short *)image->data)[i] = FloatToHalf((float)color.a/255.0f); + } + } + } break; default: break; } } @@ -2493,6 +2542,10 @@ Color *LoadImageColors(Image image) (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) || (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 32bit to 8bit per channel"); + if ((image.format == PIXELFORMAT_UNCOMPRESSED_R16) || + (image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) || + (image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 16bit to 8bit per channel"); + for (int i = 0, k = 0; i < image.width*image.height; i++) { switch (image.format) @@ -2588,6 +2641,32 @@ Color *LoadImageColors(Image image) k += 4; } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].g = 0; + pixels[i].b = 0; + pixels[i].a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 1])*255.0f); + pixels[i].b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 2])*255.0f); + pixels[i].a = 255; + + k += 3; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + + k += 4; + } break; default: break; } } @@ -2799,6 +2878,30 @@ Color GetImageColor(Image image, int x, int y) color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f); color.a = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f); + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[y*image.width + x])*255.0f); + color.g = 0; + color.b = 0; + color.a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3 + 1])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3 + 2])*255.0f); + color.a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + color.a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + } break; default: TRACELOG(LOG_WARNING, "Compressed image format does not support color reading"); break; } @@ -2938,6 +3041,34 @@ void ImageDrawPixel(Image *dst, int x, int y, Color color) ((float *)dst->data)[(y*dst->width + x)*4 + 2] = coln.z; ((float *)dst->data)[(y*dst->width + x)*4 + 3] = coln.w; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + // NOTE: Calculate grayscale equivalent color (normalized to 32bit) + Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f }; + + ((unsigned short*)dst->data)[y*dst->width + x] = FloatToHalf(coln.x*0.299f + coln.y*0.587f + coln.z*0.114f); + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + // NOTE: Calculate R32G32B32 equivalent color (normalized to 32bit) + Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f }; + + ((unsigned short *)dst->data)[(y*dst->width + x)*3] = FloatToHalf(coln.x); + ((unsigned short *)dst->data)[(y*dst->width + x)*3 + 1] = FloatToHalf(coln.y); + ((unsigned short *)dst->data)[(y*dst->width + x)*3 + 2] = FloatToHalf(coln.z); + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + // NOTE: Calculate R32G32B32A32 equivalent color (normalized to 32bit) + Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f }; + + ((unsigned short *)dst->data)[(y*dst->width + x)*4] = FloatToHalf(coln.x); + ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 1] = FloatToHalf(coln.y); + ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 2] = FloatToHalf(coln.z); + ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 3] = FloatToHalf(coln.w); + } break; default: break; } @@ -3234,7 +3365,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color // [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend() // [ ] Support f32bit channels drawing - // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32, PIXELFORMAT_UNCOMPRESSED_R32G32B32, PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 + // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32, PIXELFORMAT_UNCOMPRESSED_R32G32B32, PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 and 16-bit equivalents Color colSrc, colDst, blend; bool blendRequired = true; @@ -4366,6 +4497,33 @@ Color GetPixelColor(void *srcPtr, int format) color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f); color.a = (unsigned char)(((float *)srcPtr)[3]*255.0f); + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + // NOTE: Pixel normalized float value is converted to [0..255] + color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + // NOTE: Pixel normalized float value is converted to [0..255] + color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[1])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[2])*255.0f); + color.a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + // NOTE: Pixel normalized float value is converted to [0..255] + color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[1])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[2])*255.0f); + color.a = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[3])*255.0f); + } break; default: break; } @@ -4473,6 +4631,9 @@ int GetPixelDataSize(int width, int height, int format) 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_UNCOMPRESSED_R16: bpp = 16; break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 16*3; break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 16*4; break; case PIXELFORMAT_COMPRESSED_DXT1_RGB: case PIXELFORMAT_COMPRESSED_DXT1_RGBA: case PIXELFORMAT_COMPRESSED_ETC1_RGB: @@ -4503,6 +4664,24 @@ int GetPixelDataSize(int width, int height, int format) //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- +// From https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308 + +static float HalfToFloat(unsigned short x) { + 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 + return *(float*)&r; +} + +static unsigned short FloatToHalf(float x) { + 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 + return (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 +} + // Get pixel data from image as Vector4 array (float normalized) static Vector4 *LoadImageDataNormalized(Image image) { @@ -4605,7 +4784,32 @@ static Vector4 *LoadImageDataNormalized(Image image) pixels[i].w = ((float *)image.data)[k + 3]; k += 4; - } + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]); + pixels[i].y = 0.0f; + pixels[i].z = 0.0f; + pixels[i].w = 1.0f; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]); + pixels[i].y = HalfToFloat(((unsigned short *)image.data)[k + 1]); + pixels[i].z = HalfToFloat(((unsigned short *)image.data)[k + 2]); + pixels[i].w = 1.0f; + + k += 3; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]); + pixels[i].y = HalfToFloat(((unsigned short *)image.data)[k + 1]); + pixels[i].z = HalfToFloat(((unsigned short *)image.data)[k + 2]); + pixels[i].w = HalfToFloat(((unsigned short *)image.data)[k + 3]); + + k += 4; + } break; default: break; } } From b82217eaaaabd4540a595958970b78f08cd62844 Mon Sep 17 00:00:00 2001 From: yujiri8 Date: Sun, 6 Aug 2023 03:25:37 -0400 Subject: [PATCH 0547/1710] Tweak build.zig to work with cross-compiling (#3225) --- src/build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build.zig b/src/build.zig index 28a5d727d..9aab4812a 100644 --- a/src/build.zig +++ b/src/build.zig @@ -59,6 +59,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("dl"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("X11"); + raylib.addLibraryPath(.{ .path = "/usr/lib" }); raylib.addIncludePath(.{ .path = "/usr/include" }); raylib.defineCMacro("PLATFORM_DESKTOP", null); From c9864d8ac1345819ce5520da578f96dffae4e967 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sun, 6 Aug 2023 22:04:20 +0200 Subject: [PATCH 0548/1710] Fixed Android app black screen issue when reopening after incomplete closing (#3227) * Fixed black screen issue when resuming the app on Android Partly explained here: https://github.com/raysan5/raylib/issues/3127 * Fix APP_CMD_TERM_WINDOW for Android --- src/rcore.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 344e851f3..732cc7e47 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -720,8 +720,8 @@ void android_main(struct android_app *app) char arg0[] = "raylib"; // NOTE: argv[] are mutable CORE.Android.app = app; - // NOTE: We get the main return for exit() - int ret = main(1, (char *[]) { arg0, NULL }); + // NOTE: Return from main is ignored + (void)main(1, (char *[]) { arg0, NULL }); // Request to end the native activity ANativeActivity_finish(app->activity); @@ -731,19 +731,13 @@ void android_main(struct android_app *app) int pollEvents = 0; // Waiting for application events before complete finishing - while (!CORE.Android.app->destroyRequested) + while (!app->destroyRequested) { while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) { - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); } } - - // WARNING: Make sure you free resources properly and no other process is running from Java code or other. - // NOTE: You can use JNI to call a NativeLoader method (which will call finish() from the UI thread) - // to handle the full close from Java, without using exit(0) like here. - - exit(ret); // Close the application directly, without going through Java } // NOTE: Add this to header (if apps really need it) @@ -5897,21 +5891,28 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_TERM_WINDOW: { // Dettach OpenGL context and destroy display surface - // NOTE 1: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) - // NOTE 2: In some cases (too many context loaded), OS could unload context automatically... :( - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroySurface(CORE.Window.device, CORE.Window.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 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 (CORE.Window.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + CORE.Android.contextRebindRequired = true; + } + // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' + // this means that the user has already called 'CloseWindow()' - CORE.Android.contextRebindRequired = true; } break; case APP_CMD_SAVE_STATE: break; case APP_CMD_STOP: break; - case APP_CMD_DESTROY: - { - // NOTE 1: Call ANativeActivity_finish again to free resources unconditionally. - // NOTE 2: You can deallocate other things that are NativeActivity related here. - ANativeActivity_finish(CORE.Android.app->activity); - } break; + case APP_CMD_DESTROY: break; case APP_CMD_CONFIG_CHANGED: { //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); From db55bed72b45c97616f0cfaf26448475f80174d5 Mon Sep 17 00:00:00 2001 From: mohad12211 <51754973+mohad12211@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:51:36 +0400 Subject: [PATCH 0549/1710] fix: check if ctrl modifier is among the currently set modifiers (#3230) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 732cc7e47..97d010a9f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5609,7 +5609,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) { #if defined(SUPPORT_GIF_RECORDING) - if (mods == GLFW_MOD_CONTROL) + if (mods & GLFW_MOD_CONTROL) { if (gifRecording) { From bef818e210f8fe83a0b5b9d80d6d2001654f07e2 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Tue, 8 Aug 2023 20:09:22 +0200 Subject: [PATCH 0550/1710] Fix build for OpenGL 2.1, where half floats are part of an extension (#3233) --- src/rlgl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index 717fa59fb..3756e5ac7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3267,11 +3267,17 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #if defined(GRAPHICS_API_OPENGL_21) + case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_HALF_FLOAT_ARB; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT_ARB; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT_ARB; break; + #else // defined(GRAPHICS_API_OPENGL_ES2) case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float #endif #endif + #endif #elif defined(GRAPHICS_API_OPENGL_33) case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; From 03ecf2202e61532bebaffc3b49941261bf967614 Mon Sep 17 00:00:00 2001 From: RadsammyT <32146976+RadsammyT@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:10:40 -0400 Subject: [PATCH 0551/1710] Fix typos in rcore.c comments (#3234) * Fix typos in rcore.c comments thought i corrected "dettach" Fix typos in rcore.c comments * 'fordward' > forward --- src/rcore.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 97d010a9f..c28834987 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3694,7 +3694,7 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (aprtially) avoid malicious code on PLATFORM_WEB + // Security check to (partially) avoid malicious code on PLATFORM_WEB if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { @@ -4277,9 +4277,9 @@ static bool InitGraphicsDevice(int width, int height) 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 #if defined(__APPLE__) - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires fordward compatibility + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility #else - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Fordward Compatibility Hint: Only 3.3 and above! + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! #endif //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context } @@ -5890,7 +5890,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } break; case APP_CMD_TERM_WINDOW: { - // Dettach OpenGL context and destroy display surface + // 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 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... :( @@ -6575,7 +6575,7 @@ static void ConfigureEvdevDevice(char *device) { ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); - // Check for absolute movement support (usualy touchscreens, but also joysticks) + // Check for absolute movement support (usually touchscreens, but also joysticks) if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) { hasAbs = true; @@ -6589,7 +6589,7 @@ static void ConfigureEvdevDevice(char *device) worker->absRange.height = absinfo.maximum - absinfo.minimum; } - // Check for multiple absolute movement support (usualy multitouch touchscreens) + // Check for multiple absolute movement support (usually multitouch touchscreens) if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) { hasAbsMulti = true; @@ -6604,7 +6604,7 @@ static void ConfigureEvdevDevice(char *device) } } - // Check for relative movement support (usualy mouse) + // Check for relative movement support (usually mouse) if (TEST_BIT(evBits, EV_REL)) { ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); @@ -6612,7 +6612,7 @@ static void ConfigureEvdevDevice(char *device) if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; } - // Check for button support to determine the device type(usualy on all input devices) + // Check for button support to determine the device type(usually on all input devices) if (TEST_BIT(evBits, EV_KEY)) { ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); @@ -6740,7 +6740,7 @@ static void PollKeyboardEvents(void) // Keyboard button parsing if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 { - keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode + keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the appropriate keycode // Make sure we got a valid keycode if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) @@ -6817,8 +6817,8 @@ static void *EventThread(void *arg) // Basic movement if (event.code == ABS_X) { - CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange - CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange + CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; @@ -6826,8 +6826,8 @@ static void *EventThread(void *arg) if (event.code == ABS_Y) { - CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange - CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange + CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; @@ -6838,12 +6838,12 @@ static void *EventThread(void *arg) if (event.code == ABS_MT_POSITION_X) { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange } if (event.code == ABS_MT_POSITION_Y) { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange } if (event.code == ABS_MT_TRACKING_ID) From 42cfabc67069fd01ee8c0b00d58862e32f0942fb Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 9 Aug 2023 10:00:26 +0200 Subject: [PATCH 0552/1710] REVIEWED: Old pragma formating --- src/raudio.c | 27 +++++++++++++++------------ src/rmodels.c | 17 ++++++++--------- src/rtextures.c | 15 ++++++++------- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 5f84688d9..31e21c6e4 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -227,17 +227,20 @@ typedef struct tagBITMAPINFOHEADER { #define QOA_MALLOC RL_MALLOC #define QOA_FREE RL_FREE -#if defined(_MSC_VER ) // par shapes has 2 warnings on windows, so disable them just fof this file -#pragma warning( push ) -#pragma warning( disable : 4018) -#pragma warning( disable : 4267) -#pragma warning( disable : 4244) -#endif - + #if defined(_MSC_VER) // Disable some MSVC warning + #pragma warning(push) + #pragma warning(disable : 4018) + #pragma warning(disable : 4267) + #pragma warning(disable : 4244) + #endif #define QOA_IMPLEMENTATION #include "external/qoa.h" // QOA loading and saving functions #include "external/qoaplay.c" // QOA stream playing helper functions + + #if defined(_MSC_VER) + #pragma warning(pop) // Disable MSVC warning suppression + #endif #endif #if defined(SUPPORT_FILEFORMAT_FLAC) @@ -254,16 +257,16 @@ typedef struct tagBITMAPINFOHEADER { #define JARXM_MALLOC RL_MALLOC #define JARXM_FREE RL_FREE - #if defined(_MSC_VER ) // jar_xm has warnings on windows, so disable them just for this file - #pragma warning( push ) - #pragma warning( disable : 4244) + #if defined(_MSC_VER) // Disable some MSVC warning + #pragma warning(push) + #pragma warning(disable : 4244) #endif #define JAR_XM_IMPLEMENTATION #include "external/jar_xm.h" // XM loading functions - #if defined(_MSC_VER ) - #pragma warning( pop ) + #if defined(_MSC_VER) + #pragma warning(pop) // Disable MSVC warning suppression #endif #endif diff --git a/src/rmodels.c b/src/rmodels.c index 8c7e088cb..294d13c5a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -101,19 +101,18 @@ #define PAR_REALLOC(T, BUF, N) ((T*)RL_REALLOC(BUF, sizeof(T)*(N))) #define PAR_FREE RL_FREE -#if defined(_MSC_VER ) // par shapes has 2 warnings on windows, so disable them just fof this file -#pragma warning( push ) -#pragma warning( disable : 4244) -#pragma warning( disable : 4305) -#endif + #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 ) // disable MSVC warning suppression for par shapes -#pragma warning( pop ) -#endif - + #if defined(_MSC_VER) + #pragma warning(pop) // Disable MSVC warning suppression + #endif #endif #if defined(_WIN32) diff --git a/src/rtextures.c b/src/rtextures.c index 501e3fdea..8a3914776 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -162,16 +162,17 @@ #define QOI_MALLOC RL_MALLOC #define QOI_FREE RL_FREE -#if defined(_MSC_VER ) // qoi has warnings on windows, so disable them just for this file -#pragma warning( push ) -#pragma warning( disable : 4267) -#endif + #if defined(_MSC_VER) // Disable some MSVC warning + #pragma warning(push) + #pragma warning(disable : 4267) + #endif + #define QOI_IMPLEMENTATION #include "external/qoi.h" -#if defined(_MSC_VER ) -#pragma warning( pop ) -#endif + #if defined(_MSC_VER) + #pragma warning(pop) // Disable MSVC warning suppression + #endif #endif From 90f17499658275ace1e4d1bed1aa231e34ac38b6 Mon Sep 17 00:00:00 2001 From: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:17:12 -0400 Subject: [PATCH 0553/1710] Ignore unused function warnings from external headers when compiling with GCC and Clang (#3235) --- src/rtext.c | 9 +++++++++ src/rtextures.c | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/rtext.c b/src/rtext.c index a7d9903a8..d6aa6d736 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -71,12 +71,21 @@ #include // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] #if defined(SUPPORT_FILEFORMAT_TTF) + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #endif + #define STB_RECT_PACK_IMPLEMENTATION #include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #include "external/stb_truetype.h" // Required for: ttf font data reading + + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic pop + #endif #endif //---------------------------------------------------------------------------------- diff --git a/src/rtextures.c b/src/rtextures.c index 8a3914776..f6e7e49c3 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -138,6 +138,11 @@ defined(SUPPORT_FILEFORMAT_PIC) || \ defined(SUPPORT_FILEFORMAT_PNM)) + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #endif + #define STBI_MALLOC RL_MALLOC #define STBI_FREE RL_FREE #define STBI_REALLOC RL_REALLOC @@ -145,6 +150,10 @@ #define STB_IMAGE_IMPLEMENTATION #include "external/stb_image.h" // Required for: stbi_load_from_file() // NOTE: Used to read image data (multiple formats support) + + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic pop + #endif #endif #if (defined(SUPPORT_FILEFORMAT_DDS) || \ @@ -153,9 +162,18 @@ defined(SUPPORT_FILEFORMAT_PVR) || \ defined(SUPPORT_FILEFORMAT_ASTC)) + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #endif + #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 #endif #if defined(SUPPORT_FILEFORMAT_QOI) From 0959f6ebf69f44d5bb0225cc94fd187ebedf8be5 Mon Sep 17 00:00:00 2001 From: RadsammyT <32146976+RadsammyT@users.noreply.github.com> Date: Wed, 9 Aug 2023 18:21:14 -0400 Subject: [PATCH 0554/1710] fix typos in rmodels, rshapes, rtext modules (#3236) --- src/rmodels.c | 8 ++++---- src/rshapes.c | 2 +- src/rtext.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 294d13c5a..a512cfc6f 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2457,7 +2457,7 @@ Mesh GenMeshCube(float width, float height, float length) // 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 (dyamond) +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 */ @@ -4244,7 +4244,7 @@ static Model LoadIQM(const char *fileName) model.meshes[i].triangleCount = imesh[i].num_triangles; model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); - // Animated verted data, what we actually process for rendering + // 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)); @@ -4787,7 +4787,7 @@ static Model LoadGLTF(const char *fileName) RESTRICTIONS: - Only triangle meshes supported - - Vertex attibute types and formats supported: + - Vertex attribute types and formats supported: > Vertices (position): vec3: float > Normals: vec3: float > Texcoords: vec2: float @@ -5286,7 +5286,7 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]}; Vector4 *r = data; - // Only v4 is for rotations, so we know it's a quat + // Only v4 is for rotations, so we know it's a quaternion *r = QuaternionSlerp(v1, v2, t); } diff --git a/src/rshapes.c b/src/rshapes.c index 86e998a6d..8b319ed15 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -99,7 +99,7 @@ void SetShapesTexture(Texture2D texture, Rectangle source) { // Reset texture to default pixel if required // WARNING: Shapes texture should be probably better validated, - // it can break the rendering of all shapes if missused + // it can break the rendering of all shapes if misused if ((texture.id == 0) || (source.width == 0) || (source.height == 0)) { texShapes = (Texture2D){ 1, 1, 1, 1, 7 }; diff --git a/src/rtext.c b/src/rtext.c index d6aa6d736..f5234ecf9 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1697,7 +1697,7 @@ const char *TextToPascal(const char *text) // WARNING: Allocated memory must be manually freed char *LoadUTF8(const int *codepoints, int length) { - // We allocate enough memory fo fit all possible codepoints + // 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; From f1c31bee279285ad941acc1140caeb3dca86867f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 10 Aug 2023 22:45:25 +0200 Subject: [PATCH 0555/1710] Fix #3177 #3109 --- src/rmodels.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index a512cfc6f..ebb454e4a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -110,7 +110,7 @@ #define PAR_SHAPES_IMPLEMENTATION #include "external/par_shapes.h" // Shapes 3d parametric generation - #if defined(_MSC_VER) + #if defined(_MSC_VER) #pragma warning(pop) // Disable MSVC warning suppression #endif #endif @@ -5576,7 +5576,7 @@ static Model LoadM3D(const char *fileName) m3dp_t *prop = NULL; unsigned int bytesRead = 0; unsigned char *fileData = LoadFileData(fileName, &bytesRead); - int i, j, k, l, n, mi = -2; + int i, j, k, l, n, mi = -2, vcolor = 0; if (fileData != NULL) { @@ -5606,10 +5606,13 @@ static Model LoadM3D(const char *fileName) } else { - model.meshCount = model.materialCount = 1; + 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++; + 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)); @@ -5634,7 +5637,14 @@ static Model LoadM3D(const char *fileName) k++; mi = m3d->face[i].materialid; - for (j = i, l = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++); + // 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; @@ -5642,9 +5652,9 @@ static Model LoadM3D(const char *fileName) 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, we allocate storage for vertex colors - // M3D specs only consider vertex colors if no material is provided - if (mi != M3D_UNDEF) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); + // 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 (m3d->numbone && m3d->numskin) { From 93f59a6f5912aa07fcfc2268ce119da09dff458a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 10 Aug 2023 22:47:17 +0200 Subject: [PATCH 0556/1710] Review tabs and trail-spaces --- src/raudio.c | 4 ++-- src/raymath.h | 6 +++--- src/rcore.c | 2 +- src/rtext.c | 4 ++-- src/rtextures.c | 16 ++++++++-------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 31e21c6e4..9d1683a1f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -237,7 +237,7 @@ typedef struct tagBITMAPINFOHEADER { #define QOA_IMPLEMENTATION #include "external/qoa.h" // QOA loading and saving functions #include "external/qoaplay.c" // QOA stream playing helper functions - + #if defined(_MSC_VER) #pragma warning(pop) // Disable MSVC warning suppression #endif @@ -920,7 +920,7 @@ Sound LoadSoundFromWave(Wave wave) } // Clone sound from existing sound data, clone does not own wave data -// Wave data must +// Wave data must // NOTE: Wave data must be unallocated manually and will be shared across all clones Sound LoadSoundAlias(Sound source) { diff --git a/src/raymath.h b/src/raymath.h index 9281691fa..86d2c8eb8 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -315,11 +315,11 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { float result = 0.0f; - + float dot = v1.x*v2.x + v1.y*v2.y; float det = v1.x*v2.y - v1.y*v2.x; result = -atan2f(det, dot); - + return result; } @@ -329,7 +329,7 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - + result = atan2f(end.y - start.y, end.x - start.x); return result; diff --git a/src/rcore.c b/src/rcore.c index c28834987..7d8811b05 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4434,7 +4434,7 @@ static bool InitGraphicsDevice(int width, int height) #if defined(PLATFORM_WEB) emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); #endif - + // Set window callback events glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! #if !defined(PLATFORM_WEB) diff --git a/src/rtext.c b/src/rtext.c index f5234ecf9..a66c58101 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -852,7 +852,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC RL_FREE(nodes); RL_FREE(context); } - + #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 @@ -865,7 +865,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC k -= atlas.width; } #endif - + // Convert image data from GRAYSCALE to GRAY_ALPHA unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels diff --git a/src/rtextures.c b/src/rtextures.c index f6e7e49c3..4b697f777 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -184,7 +184,7 @@ #pragma warning(push) #pragma warning(disable : 4267) #endif - + #define QOI_IMPLEMENTATION #include "external/qoi.h" @@ -3300,13 +3300,13 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) { memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel); } - - // Repeat the first row data for all other rows - int bytesPerRow = bytesPerPixel * (int)rec.width; - for (int y = 1; y < (int)rec.height; y++) - { - memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow); - } + + // Repeat the first row data for all other rows + int bytesPerRow = bytesPerPixel * (int)rec.width; + for (int y = 1; y < (int)rec.height; y++) + { + memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow); + } } // Draw rectangle lines within an image From c25b52b1b3f3e606ce2210e834c0e479aec7859e Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 11 Aug 2023 05:19:50 -0300 Subject: [PATCH 0557/1710] Fix rcamera.h issues (#3240) --- src/rcamera.h | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index c5382684e..de169fc39 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -184,9 +184,10 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); //---------------------------------------------------------------------------------- #define CAMERA_MOVE_SPEED 0.09f #define CAMERA_ROTATION_SPEED 0.03f +#define CAMERA_PAN_SPEED 0.2f // Camera mouse movement sensitivity -#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate #define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f #define CAMERA_ORBITAL_SPEED 0.5f // Radians per second @@ -435,7 +436,7 @@ void UpdateCamera(Camera *camera, int mode) bool moveInWorldPlane = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)); bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); bool lockView = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); - bool rotateUp = (mode == CAMERA_FREE); + bool rotateUp = false; if (mode == CAMERA_ORBITAL) { @@ -458,10 +459,23 @@ void UpdateCamera(Camera *camera, int mode) // Camera movement if (!IsGamepadAvailable(0)) { - // Mouse/Keyboard support - CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + // Camera pan (for CAMERA_FREE) + if ((mode == CAMERA_FREE) && (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))) + { + const Vector2 mouseDelta = GetMouseDelta(); + if (mouseDelta.x > 0.0f) CameraMoveRight(camera, CAMERA_PAN_SPEED, moveInWorldPlane); + if (mouseDelta.x < 0.0f) CameraMoveRight(camera, -CAMERA_PAN_SPEED, moveInWorldPlane); + if (mouseDelta.y > 0.0f) CameraMoveUp(camera, -CAMERA_PAN_SPEED); + if (mouseDelta.y < 0.0f) CameraMoveUp(camera, CAMERA_PAN_SPEED); + } + else + { + // Mouse support + CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + } + // Keyboard support if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); @@ -479,11 +493,14 @@ void UpdateCamera(Camera *camera, int mode) if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); } - //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); - //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + if (mode == CAMERA_FREE) + { + if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); + if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + } } - if ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)) + if ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL) || (mode == CAMERA_FREE)) { // Zoom target distance CameraMoveToTarget(camera, -GetMouseWheelMove()); From fc885180673d09e2eb074019c581ec200a480262 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 12 Aug 2023 14:00:50 +0200 Subject: [PATCH 0558/1710] ADDED: Spline drawing functions -> - `DrawLineBSpline()` - `DrawLineCatmullRom()` --- src/raylib.h | 2 + src/rshapes.c | 200 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 175 insertions(+), 27 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index f7b0df66b..abc8371bc 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1192,6 +1192,8 @@ RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points +RLAPI void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color); // Draw a B-Spline line, minimum 4 points +RLAPI void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color); // Draw a Catmull Rom spline line, minimum 4 points RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence 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 diff --git a/src/rshapes.c b/src/rshapes.c index 8b319ed15..6987fe9da 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -67,8 +67,8 @@ #ifndef SMOOTH_CIRCLE_ERROR_RATE #define SMOOTH_CIRCLE_ERROR_RATE 0.5f // Circle error rate #endif -#ifndef BEZIER_LINE_DIVISIONS - #define BEZIER_LINE_DIVISIONS 24 // Bezier line divisions +#ifndef SPLINE_LINE_DIVISIONS + #define SPLINE_LINE_DIVISIONS 24 // Spline lines segment divisions #endif @@ -208,14 +208,14 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) Vector2 previous = startPos; Vector2 current = { 0 }; - Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { // Cubic easing in-out // NOTE: Easing is calculated only for y position value - current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)BEZIER_LINE_DIVISIONS); - current.x = previous.x + (endPos.x - startPos.x)/ (float)BEZIER_LINE_DIVISIONS; + current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)SPLINE_LINE_DIVISIONS); + current.x = previous.x + (endPos.x - startPos.x)/(float)SPLINE_LINE_DIVISIONS; float dy = current.y-previous.y; float dx = current.x-previous.x; @@ -237,21 +237,21 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) previous = current; } - DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS+2, color); } // Draw line using quadratic bezier curves with a control point void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color) { - const float step = 1.0f/BEZIER_LINE_DIVISIONS; + const float step = 1.0f/SPLINE_LINE_DIVISIONS; Vector2 previous = startPos; Vector2 current = { 0 }; float t = 0.0f; - Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; float a = powf(1 - t, 2); @@ -282,52 +282,198 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl previous = current; } - DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS+2, color); } // Draw line using cubic bezier curves with 2 control points void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color) { - const float step = 1.0f/BEZIER_LINE_DIVISIONS; + const float step = 1.0f/SPLINE_LINE_DIVISIONS; Vector2 previous = startPos; Vector2 current = { 0 }; float t = 0.0f; - Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; float a = powf(1 - t, 3); float b = 3*powf(1 - t, 2)*t; - float c = 3*(1-t)*powf(t, 2); + float c = 3*(1 - t)*powf(t, 2); float d = powf(t, 3); current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y; current.x = a*startPos.x + b*startControlPos.x + c*endControlPos.x + d*endPos.x; - float dy = current.y-previous.y; - float dx = current.x-previous.x; + float dy = current.y - previous.y; + float dx = current.x - previous.x; float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - if (i==1) + if (i == 1) { - points[0].x = previous.x+dy*size; - points[0].y = previous.y-dx*size; - points[1].x = previous.x-dy*size; - points[1].y = previous.y+dx*size; + points[0].x = previous.x + dy*size; + points[0].y = previous.y - dx*size; + points[1].x = previous.x - dy*size; + points[1].y = previous.y + dx*size; } - points[2*i+1].x = current.x-dy*size; - points[2*i+1].y = current.y+dx*size; - points[2*i].x = current.x+dy*size; - points[2*i].y = current.y-dx*size; + points[2*i + 1].x = current.x - dy*size; + points[2*i + 1].y = current.y + dx*size; + points[2*i].x = current.x + dy*size; + points[2*i].y = current.y - dx*size; previous = current; } - DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color); +} + +// Draw a B-Spline line, minimum 4 points +void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color) +{ + if (pointCount < 4) return; + + float a[4] = { 0 }; + float b[4] = { 0 }; + float dy = 0.0f; + float dx = 0.0f; + float size = 0.0f; + + Vector2 currentPoint = { 0 }; + Vector2 nextPoint = { 0 }; + Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; + + for (int i = 0; i < (pointCount - 3); i++) + { + Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; + + a[0] = (-p1.x + 3*p2.x - 3*p3.x + p4.x)/6.0f; + a[1] = (3*p1.x - 6*p2.x + 3*p3.x)/6.0f; + a[2] = (-3*p1.x + 3*p3.x)/6.0f; + a[3] = (p1.x + 4*p2.x + p3.x)/6.0f; + + b[0] = (-p1.y + 3*p2.y - 3*p3.y + p4.y)/6.0f; + b[1] = (3*p1.y - 6*p2.y + 3*p3.y)/6.0f; + b[2] = (-3*p1.y + 3*p3.y)/6.0f; + b[3] = (p1.y + 4*p2.y + p3.y)/6.0f; + + currentPoint.x = a[3]; + currentPoint.y = b[3]; + + if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color); + + float t = 0.0f; + + if (i > 0) + { + vertices[0].x = currentPoint.x + dy*size; + vertices[0].y = currentPoint.y - dx*size; + vertices[1].x = currentPoint.x - dy*size; + vertices[1].y = currentPoint.y + dx*size; + } + + for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++) + { + t = ((float)j)/((float)SPLINE_LINE_DIVISIONS); + + nextPoint.x = a[3] + t*(a[2] + t*(a[1] + t*a[0])); + nextPoint.y = b[3] + t*(b[2] + t*(b[1] + t*b[0])); + + dy = nextPoint.y - currentPoint.y; + dx = nextPoint.x - currentPoint.x; + size = 0.5f*thick/sqrtf(dx*dx+dy*dy); + + if ((j == 1) && (i == 0)) + { + vertices[0].x = currentPoint.x + dy*size; + vertices[0].y = currentPoint.y - dx*size; + vertices[1].x = currentPoint.x - dy*size; + vertices[1].y = currentPoint.y + dx*size; + } + + vertices[2*j + 1].x = nextPoint.x - dy*size; + vertices[2*j + 1].y = nextPoint.y + dx*size; + vertices[2*j].x = nextPoint.x + dy*size; + vertices[2*j].y = nextPoint.y - dx*size; + + currentPoint = nextPoint; + } + + DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS+2, color); + } + + DrawCircleV(currentPoint, thick/2.0f, color); +} + +// Draw a Catmull Rom spline line, minimum 4 points +void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color) +{ + if (pointCount < 4) return; + + float dy = 0.0f; + float dx = 0.0f; + float size = 0.0f; + + Vector2 currentPoint = points[1]; + Vector2 nextPoint = { 0 }; + Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; + + DrawCircleV(currentPoint, thick/2.0f, color); + + for (int i = 0; i < (pointCount - 3); i++) + { + Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; + + float t = 0.0f; + currentPoint = points[i]; + + if (i > 0) + { + vertices[0].x = currentPoint.x + dy*size; + vertices[0].y = currentPoint.y - dx*size; + vertices[1].x = currentPoint.x - dy*size; + vertices[1].y = currentPoint.y + dx*size; + } + + for (int i = 0; i <= SPLINE_LINE_DIVISIONS; i++) + { + t = ((float)i)/((float)SPLINE_LINE_DIVISIONS); + + float q0 = (-1*t*t*t) + (2*t*t) + (-1*t); + float q1 = (3*t*t*t) + (-5*t*t) + 2; + float q2 = (-3*t*t*t) + (4*t*t) + t; + float q3 = t*t*t - t*t; + + nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3)); + nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3)); + + float dy = nextPoint.y - currentPoint.y; + float dx = nextPoint.x - currentPoint.x; + float size = (0.5f*thick)/sqrtf(dx*dx + dy*dy); + + if (i == 1) + { + vertices[0].x = currentPoint.x + dy*size; + vertices[0].y = currentPoint.y - dx*size; + vertices[1].x = currentPoint.x - dy*size; + vertices[1].y = currentPoint.y + dx*size; + } + + vertices[2*i + 1].x = nextPoint.x - dy*size; + vertices[2*i + 1].y = nextPoint.y + dx*size; + vertices[2*i].x = nextPoint.x + dy*size; + vertices[2*i].y = nextPoint.y - dx*size; + + currentPoint = nextPoint; + } + + DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS+2, color); + + // TODO: REVIEW: HACK: Drawing a circle at points intersection to hide broken strip + DrawCircleV(currentPoint, thick/2.0f, color); + } } // Draw lines sequence From 9161c55d5970f4e7a7fe2a21606f92cfcecf2cd3 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 12 Aug 2023 18:40:18 +0200 Subject: [PATCH 0559/1710] REVIEWED: Code formatting --- src/rshapes.c | 100 +++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 6987fe9da..655f683dd 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -190,6 +190,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) 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 }, @@ -217,27 +218,27 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)SPLINE_LINE_DIVISIONS); current.x = previous.x + (endPos.x - startPos.x)/(float)SPLINE_LINE_DIVISIONS; - float dy = current.y-previous.y; - float dx = current.x-previous.x; + float dy = current.y - previous.y; + float dx = current.x - previous.x; float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - if (i==1) + if (i == 1) { - points[0].x = previous.x+dy*size; - points[0].y = previous.y-dx*size; - points[1].x = previous.x-dy*size; - points[1].y = previous.y+dx*size; + points[0].x = previous.x + dy*size; + points[0].y = previous.y - dx*size; + points[1].x = previous.x - dy*size; + points[1].y = previous.y + dx*size; } - points[2*i+1].x = current.x-dy*size; - points[2*i+1].y = current.y+dx*size; - points[2*i].x = current.x+dy*size; - points[2*i].y = current.y-dx*size; + points[2*i + 1].x = current.x - dy*size; + points[2*i + 1].y = current.y + dx*size; + points[2*i].x = current.x + dy*size; + points[2*i].y = current.y - dx*size; previous = current; } - DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color); } // Draw line using quadratic bezier curves with a control point @@ -254,35 +255,36 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - float a = powf(1 - t, 2); - float b = 2*(1 - t)*t; + + float a = powf(1.0f - t, 2); + float b = 2.0f*(1.0f - t)*t; float c = powf(t, 2); // NOTE: The easing functions aren't suitable here because they don't take a control point current.y = a*startPos.y + b*controlPos.y + c*endPos.y; current.x = a*startPos.x + b*controlPos.x + c*endPos.x; - float dy = current.y-previous.y; - float dx = current.x-previous.x; + float dy = current.y - previous.y; + float dx = current.x - previous.x; float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - if (i==1) + if (i == 1) { - points[0].x = previous.x+dy*size; - points[0].y = previous.y-dx*size; - points[1].x = previous.x-dy*size; - points[1].y = previous.y+dx*size; + points[0].x = previous.x + dy*size; + points[0].y = previous.y - dx*size; + points[1].x = previous.x - dy*size; + points[1].y = previous.y + dx*size; } - points[2*i+1].x = current.x-dy*size; - points[2*i+1].y = current.y+dx*size; - points[2*i].x = current.x+dy*size; - points[2*i].y = current.y-dx*size; + points[2*i + 1].x = current.x - dy*size; + points[2*i + 1].y = current.y + dx*size; + points[2*i].x = current.x + dy*size; + points[2*i].y = current.y - dx*size; previous = current; } - DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color); } // Draw line using cubic bezier curves with 2 control points @@ -299,9 +301,10 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - float a = powf(1 - t, 3); - float b = 3*powf(1 - t, 2)*t; - float c = 3*(1 - t)*powf(t, 2); + + float a = powf(1.0f - t, 3); + float b = 3.0f*powf(1.0f - t, 2)*t; + float c = 3.0f*(1.0f - t)*powf(t, 2); float d = powf(t, 3); current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y; @@ -347,24 +350,23 @@ void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color) for (int i = 0; i < (pointCount - 3); i++) { + float t = 0.0f; Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; - a[0] = (-p1.x + 3*p2.x - 3*p3.x + p4.x)/6.0f; - a[1] = (3*p1.x - 6*p2.x + 3*p3.x)/6.0f; - a[2] = (-3*p1.x + 3*p3.x)/6.0f; - a[3] = (p1.x + 4*p2.x + p3.x)/6.0f; + a[0] = (-p1.x + 3.0f*p2.x - 3.0f*p3.x + p4.x)/6.0f; + a[1] = (3.0f*p1.x - 6.0f*p2.x + 3.0f*p3.x)/6.0f; + a[2] = (-3.0f*p1.x + 3.0f*p3.x)/6.0f; + a[3] = (p1.x + 4.0f*p2.x + p3.x)/6.0f; - b[0] = (-p1.y + 3*p2.y - 3*p3.y + p4.y)/6.0f; - b[1] = (3*p1.y - 6*p2.y + 3*p3.y)/6.0f; - b[2] = (-3*p1.y + 3*p3.y)/6.0f; - b[3] = (p1.y + 4*p2.y + p3.y)/6.0f; + b[0] = (-p1.y + 3.0f*p2.y - 3.0f*p3.y + p4.y)/6.0f; + b[1] = (3.0f*p1.y - 6.0f*p2.y + 3.0f*p3.y)/6.0f; + b[2] = (-3.0f*p1.y + 3.0f*p3.y)/6.0f; + b[3] = (p1.y + 4.0f*p2.y + p3.y)/6.0f; currentPoint.x = a[3]; currentPoint.y = b[3]; - if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color); - - float t = 0.0f; + if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color); // Draw init line circle-cap if (i > 0) { @@ -385,7 +387,7 @@ void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color) dx = nextPoint.x - currentPoint.x; size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - if ((j == 1) && (i == 0)) + if ((i == 0) && (j == 1)) { vertices[0].x = currentPoint.x + dy*size; vertices[0].y = currentPoint.y - dx*size; @@ -401,10 +403,10 @@ void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color) currentPoint = nextPoint; } - DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS+2, color); + DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color); } - DrawCircleV(currentPoint, thick/2.0f, color); + DrawCircleV(currentPoint, thick/2.0f, color); // Draw end line circle-cap } // Draw a Catmull Rom spline line, minimum 4 points @@ -420,13 +422,13 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo Vector2 nextPoint = { 0 }; Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; - DrawCircleV(currentPoint, thick/2.0f, color); + DrawCircleV(currentPoint, thick/2.0f, color); // Draw init line circle-cap for (int i = 0; i < (pointCount - 3); i++) { + float t = 0.0f; Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; - float t = 0.0f; currentPoint = points[i]; if (i > 0) @@ -441,9 +443,9 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo { t = ((float)i)/((float)SPLINE_LINE_DIVISIONS); - float q0 = (-1*t*t*t) + (2*t*t) + (-1*t); - float q1 = (3*t*t*t) + (-5*t*t) + 2; - float q2 = (-3*t*t*t) + (4*t*t) + t; + float q0 = (-1.0f*t*t*t) + (2.0f*t*t) + (-1.0f*t); + float q1 = (3.0f*t*t*t) + (-5.0f*t*t) + 2.0f; + float q2 = (-3.0f*t*t*t) + (4.0f*t*t) + t; float q3 = t*t*t - t*t; nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3)); @@ -469,7 +471,7 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo currentPoint = nextPoint; } - DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS+2, color); + DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color); // TODO: REVIEW: HACK: Drawing a circle at points intersection to hide broken strip DrawCircleV(currentPoint, thick/2.0f, color); From d873d0f173a2e12dce039132fe91fcd70923d98e Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 12 Aug 2023 18:45:59 +0200 Subject: [PATCH 0560/1710] ISSUE: `DrawLineCatmullRom()`, needs review --- src/rshapes.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 655f683dd..98552f26e 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -429,8 +429,6 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo float t = 0.0f; Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; - currentPoint = points[i]; - if (i > 0) { vertices[0].x = currentPoint.x + dy*size; @@ -439,7 +437,9 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo vertices[1].y = currentPoint.y + dx*size; } - for (int i = 0; i <= SPLINE_LINE_DIVISIONS; i++) + // TODO: Something is wrong with this implementation, + // it should use 'j' instead of 'i' but it does not work... + for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = ((float)i)/((float)SPLINE_LINE_DIVISIONS); From bf705a63de32a8fc8b6023e89cc10431a1babc9b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 12 Aug 2023 19:31:29 +0200 Subject: [PATCH 0561/1710] REVIEWED: `DrawLineCatmullRom()` --- src/rshapes.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 98552f26e..ea45aefc4 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -437,11 +437,9 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo vertices[1].y = currentPoint.y + dx*size; } - // TODO: Something is wrong with this implementation, - // it should use 'j' instead of 'i' but it does not work... - for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) + for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++) { - t = ((float)i)/((float)SPLINE_LINE_DIVISIONS); + t = ((float)j)/((float)SPLINE_LINE_DIVISIONS); float q0 = (-1.0f*t*t*t) + (2.0f*t*t) + (-1.0f*t); float q1 = (3.0f*t*t*t) + (-5.0f*t*t) + 2.0f; @@ -451,11 +449,11 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3)); nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3)); - float dy = nextPoint.y - currentPoint.y; - float dx = nextPoint.x - currentPoint.x; - float size = (0.5f*thick)/sqrtf(dx*dx + dy*dy); + dy = nextPoint.y - currentPoint.y; + dx = nextPoint.x - currentPoint.x; + size = (0.5f*thick)/sqrtf(dx*dx + dy*dy); - if (i == 1) + if ((i == 0) && (j == 1)) { vertices[0].x = currentPoint.x + dy*size; vertices[0].y = currentPoint.y - dx*size; @@ -463,19 +461,18 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo vertices[1].y = currentPoint.y + dx*size; } - vertices[2*i + 1].x = nextPoint.x - dy*size; - vertices[2*i + 1].y = nextPoint.y + dx*size; - vertices[2*i].x = nextPoint.x + dy*size; - vertices[2*i].y = nextPoint.y - dx*size; + vertices[2*j + 1].x = nextPoint.x - dy*size; + vertices[2*j + 1].y = nextPoint.y + dx*size; + vertices[2*j].x = nextPoint.x + dy*size; + vertices[2*j].y = nextPoint.y - dx*size; currentPoint = nextPoint; } DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color); - - // TODO: REVIEW: HACK: Drawing a circle at points intersection to hide broken strip - DrawCircleV(currentPoint, thick/2.0f, color); } + + DrawCircleV(currentPoint, thick/2.0f, color); // Draw end line circle-cap } // Draw lines sequence From 7a1c0d2547e836224277b85fe540447a05cd93a7 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 14 Aug 2023 21:10:24 +0200 Subject: [PATCH 0562/1710] Added fortran-raylib --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 00672d9bf..48cf0e64b 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -24,6 +24,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | +| fortran-raylib | **4.5** | [Fortran](https://www.fortran.com/) | ISC | https://github.com/interkosmos/fortran-raylib | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | | raylib-go | 4.5 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | From e0afb8942e0536b92e7da21ca177c804f698ebff Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:55:13 -0300 Subject: [PATCH 0563/1710] Fix examples/others/rlgl_standalone.c compilation issue (#3242) --- examples/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index ff5098948..9d0b7341b 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -261,6 +261,11 @@ ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm endif +# Include GLFW required for examples/others/rlgl_standalone.c +ifeq ($(USE_EXTERNAL_GLFW),FALSE) +all others: INCLUDE_PATHS += -I$(RAYLIB_PATH)/src/external/glfw/include +endif + # Define library paths containing required libs: LDFLAGS #------------------------------------------------------------------------------------------------ LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src From e2d4463886de84493ae2cebaa173ae402dc30701 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 15 Aug 2023 00:04:07 +0200 Subject: [PATCH 0564/1710] Update BINDINGS.md --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 48cf0e64b..d4bd8fc21 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -24,7 +24,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | -| fortran-raylib | **4.5** | [Fortran](https://www.fortran.com/) | ISC | https://github.com/interkosmos/fortran-raylib | +| fortran-raylib | **4.5** | [Fortran](https://fortran-lang.org/) | ISC | https://github.com/interkosmos/fortran-raylib | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | | raylib-go | 4.5 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | From a86c93ebc0095f6c2ffc14656bfc9e1e37070f72 Mon Sep 17 00:00:00 2001 From: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Date: Mon, 14 Aug 2023 18:09:27 -0400 Subject: [PATCH 0565/1710] Ignore unused return value of GetCodepointNext in GetCodepointCount (#3241) * Ignore unused return value of GetCodepointNext in GetCodepointCount Removes the last warning from non-external libraries when compiling with the default build configuration on x64 Linux. * Remove unnecessary void cast in GetCodepointCount --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index a66c58101..3fb2b8861 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1766,7 +1766,7 @@ int GetCodepointCount(const char *text) while (*ptr != '\0') { int next = 0; - int letter = GetCodepointNext(ptr, &next); + GetCodepointNext(ptr, &next); ptr += next; From e4dcbd518091a5854a517ea4cfc3f7e2d29de1a7 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 17 Aug 2023 15:05:52 +0200 Subject: [PATCH 0566/1710] Fix #3246 --- src/rlgl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rlgl.h b/src/rlgl.h index 3756e5ac7..9ca099acf 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2945,6 +2945,7 @@ bool rlCheckRenderBatchLimit(int vCount) unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) { unsigned int id = 0; + if (data == NULL) return id; glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding From 016b7d0a3a6d67e5d78a19d8c0adaa555712e67d Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 19 Aug 2023 08:43:00 -0300 Subject: [PATCH 0567/1710] Fix text_unicode.c example crashing (#3250) * Fix text_unicode.c example crashing * Adjust the text_unicode.c example crashing fix --- examples/text/text_unicode.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index eb2a7843c..b25e3273b 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -187,12 +187,11 @@ int main(void) // Add a new set of emojis when SPACE is pressed if (IsKeyPressed(KEY_SPACE)) RandomizeEmoji(); - // Set the selected emoji and copy its text to clipboard + // Set the selected emoji if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (hovered != -1) && (hovered != selected)) { selected = hovered; selectedPos = hoveredPos; - SetClipboardText(messages[emoji[selected].message].text); } Vector2 mouse = GetMousePosition(); @@ -267,7 +266,7 @@ int main(void) a = b; b = tmp; } - + if (msgRect.x + msgRect.width > screenWidth) msgRect.x -= (msgRect.x + msgRect.width) - screenWidth + 10; // Draw chat bubble @@ -287,11 +286,11 @@ int main(void) DrawText(info, (int)pos.x, (int)pos.y, 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(); //---------------------------------------------------------------------------------- } @@ -342,7 +341,7 @@ static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, { int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop - float textOffsetY = 0; // Offset between lines (on line break '\n') + 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 @@ -465,4 +464,4 @@ static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, textOffsetX += glyphWidth; } -} \ No newline at end of file +} From 5a33f19964c845fd891342a6291f082e82ed2bf4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 19 Aug 2023 20:09:43 +0200 Subject: [PATCH 0568/1710] Revert "Fix #3246" This reverts commit e4dcbd518091a5854a517ea4cfc3f7e2d29de1a7. --- src/rlgl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 9ca099acf..3756e5ac7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2945,7 +2945,6 @@ bool rlCheckRenderBatchLimit(int vCount) unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) { unsigned int id = 0; - if (data == NULL) return id; glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding From 820343e7ac56546af93066f89425a828156ea82b Mon Sep 17 00:00:00 2001 From: "actondev (Christos)" Date: Sun, 20 Aug 2023 21:27:39 +0200 Subject: [PATCH 0569/1710] add build.zig options for individual modules (#3254) --- src/build.zig | 58 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/build.zig b/src/build.zig index 9aab4812a..27250f5ff 100644 --- a/src/build.zig +++ b/src/build.zig @@ -6,7 +6,6 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built "-std=gnu99", "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; const raylib = b.addStaticLibrary(.{ @@ -22,15 +21,38 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } raylib.addCSourceFiles(&.{ - srcdir ++ "/raudio.c", srcdir ++ "/rcore.c", - srcdir ++ "/rmodels.c", - srcdir ++ "/rshapes.c", - srcdir ++ "/rtext.c", - srcdir ++ "/rtextures.c", srcdir ++ "/utils.c", }, raylib_flags); + if (options.raudio) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/raudio.c", + }, raylib_flags); + } + if (options.rmodels) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/rmodels.c", + }, &[_][]const u8{ + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 + } ++ raylib_flags); + } + if (options.rshapes) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/rshapes.c", + }, raylib_flags); + } + if (options.rtext) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/rtext.c", + }, raylib_flags); + } + if (options.rtextures) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/rtextures.c", + }, raylib_flags); + } + var gen_step = std.build.Step.WriteFile.create(b); raylib.step.dependOn(&gen_step.step); @@ -137,6 +159,11 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } pub const Options = struct { + raudio: bool = true, + rmodels: bool = true, + rshapes: bool = true, + rtext: bool = true, + rtextures: bool = true, raygui: bool = false, platform_drm: bool = false, }; @@ -152,19 +179,24 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const raygui = b.option(bool, "raygui", "Compile with raygui support"); - const platform_drm = b.option(bool, "platform_drm", "Compile raylib in native mode (no X11)"); + const defaults = Options{}; + const options = Options{ + .platform_drm = b.option(bool, "platform_drm", "Compile raylib in native mode (no X11)") orelse defaults.platform_drm, + .raudio = b.option(bool, "raudio", "Compile with audio support") orelse defaults.raudio, + .rmodels = b.option(bool, "rmodels", "Compile with models support") orelse defaults.rmodels, + .rtext = b.option(bool, "rtext", "Compile with text support") orelse defaults.rtext, + .rtextures = b.option(bool, "rtextures", "Compile with textures support") orelse defaults.rtextures, + .rshapes = b.option(bool, "rshapes", "Compile with shapes support") orelse defaults.rshapes, + .raygui = b.option(bool, "raygui", "Compile with raygui support") orelse defaults.raygui, + }; - const lib = addRaylib(b, target, optimize, .{ - .raygui = raygui orelse false, - .platform_drm = platform_drm orelse false, - }); + const lib = addRaylib(b, target, optimize, options); lib.installHeader("src/raylib.h", "raylib.h"); lib.installHeader("src/raymath.h", "raymath.h"); lib.installHeader("src/rlgl.h", "rlgl.h"); - if (raygui orelse false) { + if (options.raygui) { lib.installHeader("../raygui/src/raygui.h", "raygui.h"); } From 8189bddefbb91525386dfdd60e268cc33e956a1f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 20 Aug 2023 21:36:36 +0200 Subject: [PATCH 0570/1710] tweaks --- src/rshapes.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index ea45aefc4..f3061f8b3 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -498,6 +498,13 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color); } +// Draw a color-filled circle (Vector version) +// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues +void DrawCircleV(Vector2 center, float radius, Color color) +{ + DrawCircleSector(center, radius, 0, 360, 36, color); +} + // Draw a piece of a circle void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) { @@ -530,6 +537,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlSetTexture(texShapes.id); rlBegin(RL_QUADS); + // NOTE: Every QUAD actually represents two segments for (int i = 0; i < segments/2; i++) { @@ -539,7 +547,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlVertex2f(center.x, center.y); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); @@ -547,11 +555,11 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); - angle += (stepLength*2); + angle += (stepLength*2.0f); } // NOTE: In case number of segments is odd, we add one last piece to the cake - if (segments%2) + if ((segments%2) == 1) { rlColor4ub(color.r, color.g, color.b, color.a); @@ -567,6 +575,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); } + rlEnd(); rlSetTexture(0); @@ -659,13 +668,6 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co rlEnd(); } -// Draw a color-filled circle (Vector version) -// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues -void DrawCircleV(Vector2 center, float radius, Color color) -{ - DrawCircleSector(center, radius, 0, 360, 36, color); -} - // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) { From b3f82a148ac59ae0615597e553fb64b836616d63 Mon Sep 17 00:00:00 2001 From: "actondev (Christos)" Date: Sun, 20 Aug 2023 23:53:20 +0200 Subject: [PATCH 0571/1710] Add `IsKeyPressedRepeat` (desktop only) (#3245) Since the key pressed are handle by comparing current vs previous state (ie frame), a special way is needed to handle key repeats. --- src/raylib.h | 1 + src/rcore.c | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index abc8371bc..d932e3a34 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1115,6 +1115,7 @@ RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize // 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 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 diff --git a/src/rcore.c b/src/rcore.c index 7d8811b05..52a58d6d6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -438,6 +438,8 @@ typedef struct CoreData { 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 @@ -3755,6 +3757,13 @@ bool IsKeyPressed(int key) return pressed; } +// Check if a key has been pressed again (only PLATFORM_DESKTOP) +bool IsKeyPressedRepeat(int key) +{ + if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) return true; + else return true; +} + // Check if a key is being pressed (key held down) bool IsKeyDown(int key) { @@ -5170,6 +5179,8 @@ 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; #if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) // Reset last gamepad button/axis registered state @@ -5179,7 +5190,11 @@ void PollInputEvents(void) #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) // Register previous keys states - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + 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(); @@ -5586,7 +5601,8 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // 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 CORE.Input.Keyboard.currentKeyState[key] = 1; + else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; + else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; #if !defined(PLATFORM_WEB) // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys @@ -6357,7 +6373,11 @@ static void ProcessKeyboard(void) 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; + 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++) From dfd0436428f95c31885646183d64678f05a80494 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 21 Aug 2023 00:34:35 +0200 Subject: [PATCH 0572/1710] Reviewed `IsKeyPressedRepeat()` #3248 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 52a58d6d6..1cae7f582 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3761,7 +3761,7 @@ bool IsKeyPressed(int key) bool IsKeyPressedRepeat(int key) { if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) return true; - else return true; + else return false; } // Check if a key is being pressed (key held down) From 5ed83dfa2928bae7e7f53076a6edca789e513173 Mon Sep 17 00:00:00 2001 From: vitopigno <103512727+VitusVeit@users.noreply.github.com> Date: Mon, 21 Aug 2023 00:36:00 +0200 Subject: [PATCH 0573/1710] Update rcore.c (#3255) --- src/rcore.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 1cae7f582..10303fe94 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4470,13 +4470,16 @@ static bool InitGraphicsDevice(int width, int height) #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 + // 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 !defined(PLATFORM_WEB) if (CORE.Window.flags & FLAG_VSYNC_HINT) { // WARNING: It seems to hit a critical render path in Intel HD Graphics glfwSwapInterval(1); TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); } +#endif int fbWidth = CORE.Window.screen.width; int fbHeight = CORE.Window.screen.height; From 8f228626a2cdeb0749818b235f06b94e804bd873 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sat, 26 Aug 2023 09:35:53 -0300 Subject: [PATCH 0574/1710] Match CMakeOptions.txt options default values (#3258) --- CMakeOptions.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index cc57a851f..5cb73bbdd 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -38,13 +38,13 @@ cmake_dependent_option(SUPPORT_MODULE_RAUDIO "Include module: raudio" ON CUSTOMI cmake_dependent_option(SUPPORT_CAMERA_SYSTEM "Provide camera module (rcamera.h) with multiple predefined cameras: free, 1st/3rd person, orbital" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_GESTURES_SYSTEM "Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_MOUSE_GESTURES "Mouse gestures are directly mapped like touches and processed by gestures system" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" OFF CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_DEFAULT_FONT "Default font is loaded on window initialization to be available for the user to render simple text. If enabled, uses external module functions to load default raylib font (module: text)" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_SCREEN_CAPTURE "Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_GIF_RECORDING "Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_BUSY_WAIT_LOOP "Use busy wait loop for timing sync instead of a high-resolution timer" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_EVENTS_WAITING "Wait for events passively (sleeping while no events) instead of polling them actively every frame" OFF CUSTOMIZE_BUILD OFF) -cmake_dependent_option(SUPPORT_WINMM_HIGHRES_TIMER "Setting a higher resolution can improve the accuracy of time-out intervals in wait functions" OFF CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_WINMM_HIGHRES_TIMER "Setting a higher resolution can improve the accuracy of time-out intervals in wait functions" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_COMPRESSION_API "Support for compression API" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_CUSTOM_FRAME_CONTROL "Enabling this flag allows manual control of the frame processes, use at your own risk" OFF CUSTOMIZE_BUILD OFF) @@ -58,14 +58,14 @@ cmake_dependent_option(SUPPORT_IMAGE_MANIPULATION "Support multiple image editin cmake_dependent_option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_HDR "Support loading HDR as textures" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(SUPPORT_FILEFORMAT_PNM "Support loading PNM as textures" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_PNM "Support loading PNM as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_BMP "Support loading BMP as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_TGA "Support loading TGA as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_JPG "Support loading JPG as textures" ${OFF} CUSTOMIZE_BUILD OFF) -cmake_dependent_option(SUPPORT_FILEFORMAT_GIF "Support loading GIF as textures" ${OFF} CUSTOMIZE_BUILD OFF) -cmake_dependent_option(SUPPORT_FILEFORMAT_QOI "Support loading QOI as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_GIF "Support loading GIF as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_QOI "Support loading QOI as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_PSD "Support loading PSD as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PKM "Support loading PKM as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" ${OFF} CUSTOMIZE_BUILD OFF) From 4fa66f26359ba21068db15b1268cf7fb03422afa Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 26 Aug 2023 09:40:30 -0300 Subject: [PATCH 0575/1710] Fix SetClipboardText for web (#3257) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 10303fe94..8413df2a0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2150,7 +2150,7 @@ void SetClipboardText(const char *text) #if defined(PLATFORM_WEB) // Security check to (partially) avoid malicious code if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character"); - else emscripten_run_script(TextFormat("navigator.clipboard.writeText('%s')", text)); + else EM_ASM( { navigator.clipboard.writeText(UTF8ToString($0)); }, text); #endif } From 21f5482e0d6e935e69118f6028da472065ea8317 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sat, 26 Aug 2023 05:43:14 -0700 Subject: [PATCH 0576/1710] [Image] Validate that ImageDrawRectangleRec is drawing entirely inside the image (#3264) * Add a function to clone a sound and share data with another sound. * rename items based on feedback * PR Feedback, use custom unload for sound alias, not variant of normal sound unloading * sound_multi example * Validate that image rect drawing is inside the image so we don't overflow a buffer * remove files that should not have been added. * remove changes that should not have been * revert * adsfasdfsdfsdf --- src/rtextures.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index 4b697f777..f32a45d1e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3284,6 +3284,20 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) if (rec.width < 0) rec.width = 0; if (rec.height < 0) rec.height = 0; + // clamp the size the the image bounds + if (rec.x + rec.width >= dst->width) + rec.width = dst->width - rec.x; + + if (rec.y + rec.height >= dst->height) + rec.height = dst->height - rec.y; + + // check if the rect is even inside the image + if (rec.x > dst->width || rec.y > dst->height) + return; + + if (rec.x + rec.width < 0 || rec.y + rec.height < 0) + return; + int sy = (int)rec.y; int sx = (int)rec.x; From 828d2736987c280883c9a5082bfd4d1a834de8a9 Mon Sep 17 00:00:00 2001 From: Ethan Simpson Date: Sun, 27 Aug 2023 00:44:52 +1200 Subject: [PATCH 0577/1710] Add Vector3 Projecting and Rejection to Raymath (#3263) * Update raymath.h * formatting --- src/raymath.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/raymath.h b/src/raymath.h index 86d2c8eb8..def8bfd42 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -713,6 +713,32 @@ RMAPI Vector3 Vector3Normalize(Vector3 v) return result; } +//Calculate the projection of the vector v1 on to v2 +RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) +{ + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); + + float mag = v1dv2/v2dv2; + + Vector3 result = { v2.x*mag , v2.y*mag, v2.z*mag }; + + return result; +} + +//Calculate the rejection of the vector v1 on to v2 +RMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2) +{ + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); + + float mag = v1dv2/v2dv2; + + Vector3 result = { v1.x - (v2.x*mag) , v1.y - (v2.y*mag), v1.z - (v2.z*mag) }; + + return result; +} + // Orthonormalize provided vectors // Makes vectors normalized and orthogonal to each other // Gram-Schmidt function implementation From 9c9fba6a6c1f23e98ecb55ed257960e94c57f765 Mon Sep 17 00:00:00 2001 From: Nickolas McDonald <43690021+n77y@users.noreply.github.com> Date: Sat, 26 Aug 2023 08:48:28 -0400 Subject: [PATCH 0578/1710] [Feature] IsKey... safety checks and more (#3256) * [Feature] Add GetKeyRepeat * Update rcore.c * Simpler design, only one repeat per frame * Update config.h * Update rcore.c * Add KEYBOARD_KEYS_MASK * Update config.h * reversions * Update rcore.c * Update rcore.c * change docs * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update raylib.h * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c --- src/rcore.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8413df2a0..cb3dfe82a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3750,6 +3750,7 @@ void OpenURL(const char *url) // Check if a key has been pressed once bool IsKeyPressed(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; bool pressed = false; if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true; @@ -3760,6 +3761,7 @@ bool IsKeyPressed(int key) // Check if a key has been pressed again (only PLATFORM_DESKTOP) bool IsKeyPressedRepeat(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) return true; else return false; } @@ -3767,6 +3769,7 @@ bool IsKeyPressedRepeat(int key) // Check if a key is being pressed (key held down) bool IsKeyDown(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; if (CORE.Input.Keyboard.currentKeyState[key] == 1) return true; else return false; } @@ -3774,6 +3777,7 @@ bool IsKeyDown(int key) // Check if a key has been released once bool IsKeyReleased(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; bool released = false; if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true; @@ -3784,6 +3788,7 @@ bool IsKeyReleased(int key) // Check if a key is NOT being pressed (key not held down) bool IsKeyUp(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; if (CORE.Input.Keyboard.currentKeyState[key] == 0) return true; else return false; } @@ -5226,7 +5231,11 @@ void PollInputEvents(void) // 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]; + 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]; @@ -5408,7 +5417,11 @@ void PollInputEvents(void) #if defined(PLATFORM_ANDROID) // 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]; + for (int i = 0; i < 260; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } // Android ALooper_pollAll() variables int pollResult = 0; @@ -6073,6 +6086,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; CORE.Input.Keyboard.keyPressedQueueCount++; } + else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1; else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up if (keycode == AKEYCODE_POWER) @@ -6496,7 +6510,11 @@ static void InitEvdevInput(void) } // Reset keyboard key state - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0; + 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); From b27e98a428a1732b2f146770e96631aa13aec8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Ri=C4=8Dko?= Date: Sat, 26 Aug 2023 18:55:57 +0200 Subject: [PATCH 0579/1710] Fix bug where default shaders was not linking. (#3261) --- src/rlgl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rlgl.h b/src/rlgl.h index 3756e5ac7..f274931e2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4550,6 +4550,7 @@ static void rlLoadShaderDefault(void) #endif #if defined(GRAPHICS_API_OPENGL_ES2) "#version 100 \n" + "precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL) (on some browsers) "attribute vec3 vertexPosition; \n" "attribute vec2 vertexTexCoord; \n" "attribute vec4 vertexColor; \n" From 96464972168e32221298fef24af7c24b292e55f7 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 27 Aug 2023 00:30:56 +0200 Subject: [PATCH 0580/1710] Formating review --- src/raymath.h | 13 +++++++++++-- src/rtextures.c | 18 ++++++------------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index def8bfd42..1fab43aad 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -15,6 +15,7 @@ * - Functions use always a "result" variable for return * - Functions are always defined inline * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) +* - No compound literals used to make sure libray is compatible with C++ * * CONFIGURATION: * #define RAYMATH_IMPLEMENTATION @@ -716,12 +717,16 @@ RMAPI Vector3 Vector3Normalize(Vector3 v) //Calculate the projection of the vector v1 on to v2 RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) { + Vector3 result = { 0 }; + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); float mag = v1dv2/v2dv2; - Vector3 result = { v2.x*mag , v2.y*mag, v2.z*mag }; + result.x = v2.x*mag; + result.y = v2.y*mag; + result.z = v2.z*mag; return result; } @@ -729,12 +734,16 @@ RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) //Calculate the rejection of the vector v1 on to v2 RMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2) { + Vector3 result = { 0 }; + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); float mag = v1dv2/v2dv2; - Vector3 result = { v1.x - (v2.x*mag) , v1.y - (v2.y*mag), v1.z - (v2.z*mag) }; + result.x = v1.x - (v2.x*mag); + result.y = v1.y - (v2.y*mag); + result.z = v1.z - (v2.z*mag); return result; } diff --git a/src/rtextures.c b/src/rtextures.c index f32a45d1e..b88aeb154 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3284,19 +3284,13 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) if (rec.width < 0) rec.width = 0; if (rec.height < 0) rec.height = 0; - // clamp the size the the image bounds - if (rec.x + rec.width >= dst->width) - rec.width = dst->width - rec.x; + // Clamp the size the the image bounds + if ((rec.x + rec.width) >= dst->width) rec.width = dst->width - rec.x; + if ((rec.y + rec.height) >= dst->height) rec.height = dst->height - rec.y; - if (rec.y + rec.height >= dst->height) - rec.height = dst->height - rec.y; - - // check if the rect is even inside the image - if (rec.x > dst->width || rec.y > dst->height) - return; - - if (rec.x + rec.width < 0 || rec.y + rec.height < 0) - return; + // Check if the rect is even inside the image + if ((rec.x > dst->width) || (rec.y > dst->height)) return; + if (((rec.x + rec.width) < 0) || (rec.y + rec.height < 0)) return; int sy = (int)rec.y; int sx = (int)rec.x; From 71a8d09a6384837f273a1dd84e5a02d0df2cccdb Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sun, 27 Aug 2023 10:42:45 -0300 Subject: [PATCH 0581/1710] Add missing cmake options (#3267) --- CMakeOptions.txt | 4 ++++ cmake/CompileDefinitions.cmake | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 5cb73bbdd..83faa5637 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -46,6 +46,7 @@ cmake_dependent_option(SUPPORT_BUSY_WAIT_LOOP "Use busy wait loop for timing syn cmake_dependent_option(SUPPORT_EVENTS_WAITING "Wait for events passively (sleeping while no events) instead of polling them actively every frame" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_WINMM_HIGHRES_TIMER "Setting a higher resolution can improve the accuracy of time-out intervals in wait functions" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_COMPRESSION_API "Support for compression API" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_EVENTS_AUTOMATION "Support automatic generated events, loading and recording of those events when required" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_CUSTOM_FRAME_CONTROL "Enabling this flag allows manual control of the frame processes, use at your own risk" OFF CUSTOMIZE_BUILD OFF) # rshapes.c @@ -58,6 +59,7 @@ cmake_dependent_option(SUPPORT_IMAGE_MANIPULATION "Support multiple image editin cmake_dependent_option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_HDR "Support loading HDR as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_PIC "Support loading PIC as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PNM "Support loading PNM as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ${OFF} CUSTOMIZE_BUILD OFF) @@ -83,6 +85,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_MTL "Support loading MTL file format" cmake_dependent_option(SUPPORT_FILEFORMAT_IQM "Support loading IQM file format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_GLTF "Support loading GLTF file format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_VOX "Support loading VOX file format" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_M3D "Support loading M3D file format" ON CUSTOMIZE_BUILD ON) # raudio.c cmake_dependent_option(SUPPORT_FILEFORMAT_WAV "Support loading WAV for sound" ON CUSTOMIZE_BUILD ON) @@ -90,6 +93,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_OGG "Support loading OGG for sound" O cmake_dependent_option(SUPPORT_FILEFORMAT_XM "Support loading XM for sound" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_MOD "Support loading MOD for sound" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_MP3 "Support loading MP3 for sound" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_QOA "Support loading QOA for sound" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_FLAC "Support loading FLAC for sound" ${OFF} CUSTOMIZE_BUILD OFF) # utils.c diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake index 689f98ac4..1458e2f3c 100644 --- a/cmake/CompileDefinitions.cmake +++ b/cmake/CompileDefinitions.cmake @@ -28,6 +28,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_EVENTS_WAITING) define_if("raylib" SUPPORT_WINMM_HIGHRES_TIMER) define_if("raylib" SUPPORT_COMPRESSION_API) + define_if("raylib" SUPPORT_EVENTS_AUTOMATION) define_if("raylib" SUPPORT_CUSTOM_FRAME_CONTROL) define_if("raylib" SUPPORT_QUADS_DRAW_MODE) define_if("raylib" SUPPORT_IMAGE_EXPORT) @@ -36,6 +37,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_FILEFORMAT_PNG) define_if("raylib" SUPPORT_FILEFORMAT_DDS) define_if("raylib" SUPPORT_FILEFORMAT_HDR) + define_if("raylib" SUPPORT_FILEFORMAT_PIC) define_if("raylib" SUPPORT_FILEFORMAT_PNM) define_if("raylib" SUPPORT_FILEFORMAT_KTX) define_if("raylib" SUPPORT_FILEFORMAT_ASTC) @@ -56,11 +58,13 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_FILEFORMAT_IQM) define_if("raylib" SUPPORT_FILEFORMAT_GLTF) define_if("raylib" SUPPORT_FILEFORMAT_VOX) + define_if("raylib" SUPPORT_FILEFORMAT_M3D) define_if("raylib" SUPPORT_FILEFORMAT_WAV) define_if("raylib" SUPPORT_FILEFORMAT_OGG) define_if("raylib" SUPPORT_FILEFORMAT_XM) define_if("raylib" SUPPORT_FILEFORMAT_MOD) define_if("raylib" SUPPORT_FILEFORMAT_MP3) + define_if("raylib" SUPPORT_FILEFORMAT_QOA) define_if("raylib" SUPPORT_FILEFORMAT_FLAC) define_if("raylib" SUPPORT_STANDARD_FILEIO) define_if("raylib" SUPPORT_TRACELOG) From 76adf883fd7462e9a61aa6f6448a32099f8f29b0 Mon Sep 17 00:00:00 2001 From: iacore <74560659+iacore@users.noreply.github.com> Date: Sun, 27 Aug 2023 13:43:39 +0000 Subject: [PATCH 0582/1710] Fix CMake extraneous -lglfw (#3266) Closes #3265. The problem: LIBS_PRIVATE is a list of library names (used by pkg-config), but the shared library of the same name doesn't always exist. --- cmake/InstallConfigurations.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/InstallConfigurations.cmake b/cmake/InstallConfigurations.cmake index 6a89ad55c..6c606e565 100644 --- a/cmake/InstallConfigurations.cmake +++ b/cmake/InstallConfigurations.cmake @@ -9,8 +9,7 @@ install( # PKG_CONFIG_LIBS_PRIVATE is used in raylib.pc.in if (NOT BUILD_SHARED_LIBS) include(LibraryPathToLinkerFlags) - library_path_to_linker_flags(__PKG_CONFIG_LIBS_PRIVATE "${LIBS_PRIVATE}") - set(PKG_CONFIG_LIBS_PRIVATE ${__PKG_CONFIG_LIBS_PRIVATE} ${GLFW_PKG_LIBS}) + set(PKG_CONFIG_LIBS_PRIVATE ${GLFW_PKG_LIBS}) string(REPLACE ";" " " PKG_CONFIG_LIBS_PRIVATE "${PKG_CONFIG_LIBS_PRIVATE}") elseif (BUILD_SHARED_LIBS) set(PKG_CONFIG_LIBS_EXTRA "") From 9393500bff8731c744f0693c7f6bc9db6c612b57 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sun, 27 Aug 2023 16:14:20 -0300 Subject: [PATCH 0583/1710] Fix example/models/models_loading_gltf.c controls (#3268) --- examples/models/models_loading_gltf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index d5ebff237..28847316a 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -4,9 +4,9 @@ * * 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 +* - 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, +* - Only supports translation/rotation/scale animation channel.path, * weights not considered (i.e. morph targets) * * Example originally created with raylib 3.7, last time updated with raylib 4.2 @@ -42,7 +42,7 @@ int main(void) // Load gltf model Model model = LoadModel("resources/models/gltf/robot.glb"); - + // Load gltf model animations unsigned int animsCount = 0; unsigned int animIndex = 0; @@ -63,9 +63,9 @@ int main(void) //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_THIRD_PERSON); // Select current animation - if (IsKeyPressed(KEY_UP)) animIndex = (animIndex + 1)%animsCount; - else if (IsKeyPressed(KEY_DOWN)) animIndex = (animIndex + animsCount - 1)%animsCount; - + if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount; + else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount; + // Update model animation ModelAnimation anim = modelAnimations[animIndex]; animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; @@ -85,7 +85,7 @@ int main(void) EndMode3D(); - DrawText("Use the UP/DOWN arrow keys to switch animation", 10, 10, 20, GRAY); + DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY); DrawText(TextFormat("Animation: %s", anim.name), 10, GetScreenHeight() - 20, 10, DARKGRAY); EndDrawing(); From de3dc94d5ba135f9e179d1513814134e850471b3 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sun, 27 Aug 2023 16:15:02 -0300 Subject: [PATCH 0584/1710] Fix example/models/models_loading_m3d.c controls (#3269) --- examples/models/models_loading_m3d.c | 35 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index b9674b6f6..1e56d51ca 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -40,7 +40,7 @@ int main(void) camera.projection = CAMERA_PERSPECTIVE; // Camera projection type Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - + char modelFileName[128] = "resources/models/m3d/cesium_man.m3d"; bool drawMesh = 1; bool drawSkeleton = 1; @@ -72,27 +72,27 @@ int main(void) if (IsKeyDown(KEY_SPACE) || IsKeyPressed(KEY_N)) { animFrameCounter++; - + if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0; - + UpdateModelAnimation(model, anims[animId], animFrameCounter); animPlaying = true; } - - // Select animation by pressing A - if (IsKeyPressed(KEY_A)) + + // Select animation by pressing C + if (IsKeyPressed(KEY_C)) { animFrameCounter = 0; animId++; - + if (animId >= animsCount) animId = 0; UpdateModelAnimation(model, anims[animId], 0); animPlaying = true; } } - + // Toggle skeleton drawing - if (IsKeyPressed(KEY_S)) drawSkeleton ^= 1; + if (IsKeyPressed(KEY_B)) drawSkeleton ^= 1; // Toggle mesh drawing if (IsKeyPressed(KEY_M)) drawMesh ^= 1; @@ -112,19 +112,19 @@ int main(void) // Draw the animated skeleton if (drawSkeleton) { - // Loop to (boneCount - 1) because the last one is a special "no bone" bone, + // Loop to (boneCount - 1) because the last one is a special "no bone" bone, // needed to workaround buggy models // 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(). - // But if UpdateModelAnimation() has been called at least once + // 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) { // Display the bind-pose skeleton DrawCube(model.bindPose[i].translation, 0.04f, 0.04f, 0.04f, RED); - + if (model.bones[i].parent >= 0) { DrawLine3D(model.bindPose[i].translation, @@ -135,7 +135,7 @@ int main(void) { // Display the frame-pose skeleton DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED); - + if (anims[animId].bones[i].parent >= 0) { DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation, @@ -149,9 +149,10 @@ int main(void) EndMode3D(); - DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 60, 10, MAROON); - DrawText("PRESS A to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY); - DrawText("PRESS M to toggle MESH, S to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY); + DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 80, 10, MAROON); + DrawText("PRESS N to STEP ONE ANIMATION FRAME", 10, GetScreenHeight() - 60, 10, DARKGRAY); + DrawText("PRESS C to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY); + DrawText("PRESS M to toggle MESH, B to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY); DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY); EndDrawing(); From fc0d13256626e18de8fbcdefb262d8655005b561 Mon Sep 17 00:00:00 2001 From: Ethan Conneely Date: Mon, 28 Aug 2023 21:49:45 +0100 Subject: [PATCH 0585/1710] Remove e from secondes (#3270) --- examples/shaders/resources/shaders/glsl100/wave.fs | 6 +++--- examples/shaders/resources/shaders/glsl330/wave.fs | 6 +++--- examples/shaders/shaders_texture_waves.c | 2 +- src/rcore.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/wave.fs b/examples/shaders/resources/shaders/glsl100/wave.fs index 50c4e02f3..cd4ba9de4 100644 --- a/examples/shaders/resources/shaders/glsl100/wave.fs +++ b/examples/shaders/resources/shaders/glsl100/wave.fs @@ -10,7 +10,7 @@ varying vec4 fragColor; uniform sampler2D texture0; uniform vec4 colDiffuse; -uniform float secondes; +uniform float seconds; uniform vec2 size; @@ -29,8 +29,8 @@ void main() { float boxTop = 0.0; vec2 p = fragTexCoord; - p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (secondes * speedX)) * ampX * pixelWidth; - p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (secondes * speedY)) * ampY * pixelHeight; + p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (seconds * speedX)) * ampX * pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (seconds * speedY)) * ampY * pixelHeight; gl_FragColor = texture2D(texture0, p)*colDiffuse*fragColor; } diff --git a/examples/shaders/resources/shaders/glsl330/wave.fs b/examples/shaders/resources/shaders/glsl330/wave.fs index 43efee234..1f22bee09 100644 --- a/examples/shaders/resources/shaders/glsl330/wave.fs +++ b/examples/shaders/resources/shaders/glsl330/wave.fs @@ -11,7 +11,7 @@ uniform vec4 colDiffuse; // Output fragment color out vec4 finalColor; -uniform float secondes; +uniform float seconds; uniform vec2 size; @@ -30,8 +30,8 @@ void main() { float boxTop = 0.0; vec2 p = fragTexCoord; - p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (secondes * speedX)) * ampX * pixelWidth; - p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (secondes * speedY)) * ampY * pixelHeight; + p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (seconds * speedX)) * ampX * pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (seconds * speedY)) * ampY * pixelHeight; finalColor = texture(texture0, p)*colDiffuse*fragColor; } diff --git a/examples/shaders/shaders_texture_waves.c b/examples/shaders/shaders_texture_waves.c index a087ec4d0..27ad1f6e4 100644 --- a/examples/shaders/shaders_texture_waves.c +++ b/examples/shaders/shaders_texture_waves.c @@ -46,7 +46,7 @@ int main(void) // Load shader and setup location points and values Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/wave.fs", GLSL_VERSION)); - int secondsLoc = GetShaderLocation(shader, "secondes"); + int secondsLoc = GetShaderLocation(shader, "seconds"); int freqXLoc = GetShaderLocation(shader, "freqX"); int freqYLoc = GetShaderLocation(shader, "freqY"); int ampXLoc = GetShaderLocation(shader, "ampX"); diff --git a/src/rcore.c b/src/rcore.c index cb3dfe82a..6e5d94964 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2999,7 +2999,7 @@ int GetFPS(void) #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL) #define FPS_CAPTURE_FRAMES_COUNT 30 // 30 captures - #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 millisecondes + #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 milliseconds #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT) static int index = 0; From d047597244017ba81ab12d9fd714d001899a992e Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Tue, 29 Aug 2023 14:04:27 -0300 Subject: [PATCH 0586/1710] Fix example/audio/audio_module_player.c help instructions and small bug (#3272) * Fix example/audio/audio_module_player.c help instructions and small bug * Update example/audio/audio_module_player.png screenshot --- examples/audio/audio_module_playing.c | 11 ++++++++++- examples/audio/audio_module_playing.png | Bin 47970 -> 45137 bytes 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/audio/audio_module_playing.c b/examples/audio/audio_module_playing.c index b8482aa57..bf5530fdc 100644 --- a/examples/audio/audio_module_playing.c +++ b/examples/audio/audio_module_playing.c @@ -79,6 +79,7 @@ int main(void) { StopMusicStream(music); PlayMusicStream(music); + pause = false; } // Pause/Resume music playing @@ -134,6 +135,14 @@ int main(void) DrawRectangle(20, screenHeight - 20 - 12, (int)timePlayed, 12, MAROON); DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, GRAY); + // Draw help instructions + DrawRectangle(20, 20, 425, 145, WHITE); + DrawRectangleLines(20, 20, 425, 145, GRAY); + DrawText("PRESS SPACE TO RESTART MUSIC", 40, 40, 20, BLACK); + DrawText("PRESS P TO PAUSE/RESUME", 40, 70, 20, BLACK); + DrawText("PRESS UP/DOWN TO CHANGE SPEED", 40, 100, 20, BLACK); + DrawText(TextFormat("SPEED: %f", pitch), 40, 130, 20, MAROON); + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -148,4 +157,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/audio/audio_module_playing.png b/examples/audio/audio_module_playing.png index 8bde9879d4aa7c8f57c9bb334888b0fd52c1102e..63003e0472eccf6c7861ffee50dad0215c88cab0 100644 GIT binary patch literal 45137 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 Date: Tue, 29 Aug 2023 19:25:44 +0200 Subject: [PATCH 0587/1710] Use type name instead of valid specifier long long --> long long int --- src/rcore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 6e5d94964..210100fbf 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -506,7 +506,7 @@ typedef struct CoreData { double frame; // Time measure for one frame double target; // Desired time for one frame, if 0 not applied #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) - unsigned long long base; // Base time measure for hi-res timer + unsigned long long int base; // Base time measure for hi-res timer #endif unsigned int frameCounter; // Frame counter } Time; @@ -3495,10 +3495,10 @@ bool ChangeDirectory(const char *dir) // Check if a given path point to a file bool IsPathFile(const char *path) { - struct stat pathStat = { 0 }; - stat(path, &pathStat); + struct stat result = { 0 }; + stat(path, &result); - return S_ISREG(pathStat.st_mode); + return S_ISREG(result.st_mode); } // Check if a file has been dropped into window From 8157d4283e5afc5e7822e5885aa7b27cd36ddf65 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 29 Aug 2023 19:26:15 +0200 Subject: [PATCH 0588/1710] REVIEWED: `GetFileLength()`, added comment #3262 --- src/rcore.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 210100fbf..2c53eb5e6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3192,13 +3192,24 @@ bool DirectoryExists(const char *dirPath) 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); - size = (int)ftell(file); + 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); } From 752baac00c92a10bebd8b97da4bbf83e8a8345e3 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Tue, 29 Aug 2023 14:28:03 -0300 Subject: [PATCH 0589/1710] Update examples/models/models_loading_gltf.png;m3d.png screenshots (#3273) --- examples/models/models_loading_gltf.png | Bin 25896 -> 26632 bytes examples/models/models_loading_m3d.png | Bin 23682 -> 25881 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/models/models_loading_gltf.png b/examples/models/models_loading_gltf.png index 34748a22e587a86dc297238c939fbbe148fb5153..ce2a85284077b0cce9fbfb8b63f326ca48013be8 100644 GIT binary patch literal 26632 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 literal 25896 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 diff --git a/examples/models/models_loading_m3d.png b/examples/models/models_loading_m3d.png index 2214b8e0152194319b3cb88f763d85df9019e5a0..9220419ad1c9124b887dc019f0ecdf776eac1f31 100644 GIT binary patch literal 25881 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 l%o%Vu0{0rQ56G!+dTcYHd8`V{|m|TrIG*u literal 23682 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 Date: Wed, 30 Aug 2023 17:39:44 -0300 Subject: [PATCH 0590/1710] Remove a duplicated screenshot and add missing one (#3275) --- examples/audio/audio_sound_multi.png | Bin 0 -> 15106 bytes .../core/core_2d_camera_smooth_pixelperfect.png | Bin 15832 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/audio/audio_sound_multi.png delete mode 100644 examples/core/core_2d_camera_smooth_pixelperfect.png diff --git a/examples/audio/audio_sound_multi.png b/examples/audio/audio_sound_multi.png new file mode 100644 index 0000000000000000000000000000000000000000..d60138d0782e24f41049582c0554db33e293f090 GIT binary patch literal 15106 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!Rn-^i(^PdT=Jh^Utia^ z1sX^(Emp)qlyEREyBHwH+jvnx0xQ8-)p{U5u-U`G)Ii)2=qSoXt^+2 zE{v87qvgVAxqxl_bF_3EPxC0)NIeA)ku!@kEtZ{d@rpT~vFZd#-q)a! zYQ{T?4zDz3u3Pu6gTt@raO#fN$|64v{$IA98zA-W$gb(vzVU?oIQVF4$nT3+FI&sG zc~zQ3e7>7;*9v5IgEfm=x`SKracs#3B*t*pDWQZd#RNF!%^;}gkfBnzWSzQNdCSy0 zA7pZVwM9K!7;Yt(`Lp%P%!wC2rEIw+2_C(Fsc2xuY&47A%{jq@1sm}~jA^m4!s5aXUP5#}vo)pV-6gl*`xitbF=B z!wzDF4a5p5cOtCl;9xBJUNOOHlV3)Qz}=NsjOO2Qad^jJyz`;w`@Lo@Z^44ZJZ%?sRwkf&NpSg$yRV(}|4*>`?2uV? zXliTwD<9@vyAvNW3#*$LJLK71&N`OAWfH`@yo_H2z@`f0o5$#I zUJ<2fEM+fecP-XpJ+OI@bk*lXCVI;Ycj`ja%( zsNa+0Ue1sE==x@!K+@ypYd@Kv^&J#CKjoZ6Z`drbC#JL?c)@cbLknm<_9>SI(HsVK ze}8@bpHb0|T(_YnRt8&05}k{4TIPZ-i<>nhwP8z)5YOF$L>Z0=Do}Pb!6m0?@CV|A zP@K*jbpj{?M?+yW6ktuX(X=p{7Dm$oa#c2(BSv$?XpR`o5uncMXef+^0$MTvb4Tll z(K-TFIEYM(c>tI%2erK$M9H)@bQCS~`xF qj-#dHXz7S36A`S@(h*uZGRzZIR1w_UTm{_Q%HZkh=d#Wzp$P!(&(8<| literal 0 HcmV?d00001 diff --git a/examples/core/core_2d_camera_smooth_pixelperfect.png b/examples/core/core_2d_camera_smooth_pixelperfect.png deleted file mode 100644 index ba8d89b7c1e7d68f069a4c25488a2e0a8a698d22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15832 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 Date: Wed, 30 Aug 2023 17:40:24 -0300 Subject: [PATCH 0591/1710] Add examples/shaders/shaders_lightmap.c to Makefiles (#3276) --- examples/Makefile | 1 + examples/Makefile.Web | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index 9d0b7341b..7028a9542 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -529,6 +529,7 @@ SHADERS = \ shaders/shaders_simple_mask \ shaders/shaders_spotlight \ shaders/shaders_hot_reloading \ + shaders/shaders_lightmap \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index e65351e07..4574e928e 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -504,6 +504,7 @@ SHADERS = \ shaders/shaders_simple_mask \ shaders/shaders_spotlight \ shaders/shaders_hot_reloading \ + shaders/shaders_lightmap \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ From fefe8fcda96db357ab4b93490d0453f4b3058cc6 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Wed, 30 Aug 2023 17:40:49 -0300 Subject: [PATCH 0592/1710] Fix examples/others/easings_testbed.c help instructions and small tweak (#3277) --- examples/others/easings_testbed.c | 22 +++++++++++----------- examples/others/easings_testbed.png | Bin 17338 -> 17443 bytes 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/others/easings_testbed.c b/examples/others/easings_testbed.c index 227efd231..45f0efd0b 100644 --- a/examples/others/easings_testbed.c +++ b/examples/others/easings_testbed.c @@ -108,7 +108,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [easings] example - easings testbed"); - Vector2 ballPosition = { 100.0f, 200.0f }; + Vector2 ballPosition = { 100.0f, 100.0f }; float t = 0.0f; // Current time (in any unit measure, but same unit as duration) float d = 300.0f; // Total time it should take to complete (duration) @@ -180,8 +180,8 @@ int main(void) // Movement computation if (!paused && ((boundedT && t < d) || !boundedT)) { - ballPosition.x = Easings[easingX].func(t, 100.0f, 700.0f - 100.0f, d); - ballPosition.y = Easings[easingY].func(t, 100.0f, 400.0f - 100.0f, d); + ballPosition.x = Easings[easingX].func(t, 100.0f, 700.0f - 170.0f, d); + ballPosition.y = Easings[easingY].func(t, 100.0f, 400.0f - 170.0f, d); t += 1.0f; } //---------------------------------------------------------------------------------- @@ -193,15 +193,15 @@ int main(void) ClearBackground(RAYWHITE); // Draw information text - DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); - DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); - DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), 0, FONT_SIZE*4, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 20, FONT_SIZE, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 20, FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), 20, FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); // Draw instructions text - DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, GetScreenHeight() - FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); - DrawText("Use D and W or A and S keys to change duration", 0, GetScreenHeight() - FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); - DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, GetScreenHeight() - FONT_SIZE*4, FONT_SIZE, LIGHTGRAY); - DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, GetScreenHeight() - FONT_SIZE*5, FONT_SIZE, LIGHTGRAY); + DrawText("Use ENTER to play or pause movement, use SPACE to restart", 20, GetScreenHeight() - FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); + DrawText("Use Q and W or A and S keys to change duration", 20, GetScreenHeight() - FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); + DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 20, GetScreenHeight() - FONT_SIZE*4, FONT_SIZE, LIGHTGRAY); + DrawText("Use UP or DOWN keys to choose easing for the y axis", 20, GetScreenHeight() - FONT_SIZE*5, FONT_SIZE, LIGHTGRAY); // Draw ball DrawCircleV(ballPosition, 16.0f, MAROON); @@ -226,4 +226,4 @@ static float NoEase(float t, float b, float c, float d) d += burn; return b; -} \ No newline at end of file +} diff --git a/examples/others/easings_testbed.png b/examples/others/easings_testbed.png index fb0538531057f5e78e25186ba6023d4956f1db0c..05f63acdda92d4497df752a7768e01c7fbcb781e 100644 GIT binary patch literal 17443 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 TZV3GO2gKT9xA`qoeCWRc4{Unk literal 17338 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 From 3d0d54e0708a71ad40bec3f312ebf26d5fc2a28f Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Wed, 30 Aug 2023 17:42:01 -0300 Subject: [PATCH 0593/1710] Fix examples/shaders/shaders_texture_outline.c help instructions (#3278) --- examples/shaders/shaders_texture_outline.c | 18 +++++++++--------- examples/shaders/shaders_texture_outline.png | Bin 264137 -> 264739 bytes 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/shaders/shaders_texture_outline.c b/examples/shaders/shaders_texture_outline.c index a28ab80e4..09f625bb5 100644 --- a/examples/shaders/shaders_texture_outline.c +++ b/examples/shaders/shaders_texture_outline.c @@ -37,18 +37,18 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Apply an outline to a texture"); Texture2D texture = LoadTexture("resources/fudesumi.png"); - + Shader shdrOutline = LoadShader(0, TextFormat("resources/shaders/glsl%i/outline.fs", GLSL_VERSION)); float outlineSize = 2.0f; - float outlineColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; // Normalized RED color + float outlineColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; // Normalized RED color float textureSize[2] = { (float)texture.width, (float)texture.height }; - + // Get shader locations int outlineSizeLoc = GetShaderLocation(shdrOutline, "outlineSize"); int outlineColorLoc = GetShaderLocation(shdrOutline, "outlineColor"); int textureSizeLoc = GetShaderLocation(shdrOutline, "textureSize"); - + // Set shader values (they can be changed later) SetShaderValue(shdrOutline, outlineSizeLoc, &outlineSize, SHADER_UNIFORM_FLOAT); SetShaderValue(shdrOutline, outlineColorLoc, outlineColor, SHADER_UNIFORM_VEC4); @@ -64,7 +64,7 @@ int main(void) //---------------------------------------------------------------------------------- outlineSize += GetMouseWheelMove(); if (outlineSize < 1.0f) outlineSize = 1.0f; - + SetShaderValue(shdrOutline, outlineSizeLoc, &outlineSize, SHADER_UNIFORM_FLOAT); //---------------------------------------------------------------------------------- @@ -75,13 +75,13 @@ int main(void) ClearBackground(RAYWHITE); BeginShaderMode(shdrOutline); - + DrawTexture(texture, GetScreenWidth()/2 - texture.width/2, -30, WHITE); - + EndShaderMode(); DrawText("Shader-based\ntexture\noutline", 10, 10, 20, GRAY); - + DrawText("Scroll mouse wheel to\nchange outline size", 10, 72, 20, GRAY); DrawText(TextFormat("Outline size: %i px", (int)outlineSize), 10, 120, 20, MAROON); DrawFPS(710, 10); @@ -99,4 +99,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/shaders/shaders_texture_outline.png b/examples/shaders/shaders_texture_outline.png index 85c69b1808f756ca6969b53669d850293c53e799..badd388a5167830275b20fb07e75c95b449edb11 100644 GIT binary patch delta 240586 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

    Mzx#iqzyz}QX7=)GsKx|Z5g8VIlc0YU`9vj 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�!-_I*1<4gQLPta_^w6XxqF8VMb!>JM)SF+D}a*Vk~f^EGN()uKr8z%sGp)l|qyNubo6CmkAq?Ty( zRp!P$_%Dxg8i?9rm5Z8MafXs8Pr#IB%kh%WO*ntBo)f~kYCeKC)BxW z(CVJl*ACQtah?O@lZx$qCp&t7c|qD{ZTcl23bXU3wdixCRrCRzz@E%=cJLS1Iwo!e z0`K!DjmhC0z~9nU55Js#=Z=o(_R-U#w{E;6SH8fpT0Gs;v@qWOhg9FHI~0qH86=!& zPclDq6PmabhZ8i*irndmPvPz!F#<|4sGR?fuK5&|NBZ;C7IJ%82QU-7r>Uq(oi3t` zTr2^qy(L7Mj#wJKxkv#mbyRRa;SgKj{|I8vXBpet%YLJ3?uX5cm`9f?h9^uc{655K z82>BYHdOnin%bzDx^?(YfGK5e*$q;f15uWO>V{>JHZGHl(738MPWvcA-KS0W#O)$CeO;UlimyzDP&tF zQA%`sP}~gDg90U_rhRXlo{{I~djQ|bfo!Dv$Law#3xq+Mf;xcc!)&F zn-K&{utyna%jJx(fnvnD{mZqN2*p%855<27Mqn|sQxaMK6pZAwl0$Gfj~0of#PNR? z!SH!lLHOv<8aQH(x4($`WF$N)pkr}HNi=j;0dM4_zNBc z9XuE*>ct(T`=y-+Jo5J%BY@+bBsr3sY7B$t&oLGgvR=F_8)NoCMYugeDEl`3DQvIV z2d9sjnyd!QizkW9j|;@aE9qWlo)xQ?k>UFkVklP5MS!TX@%;QYjrC6hV)6 z5O^-r+qP|kUEYL;M{p=QMu3@GZFL%-J)KeE1Lm>c|%LrV^sx0N3fjiv}usRAx08 zUQuek8@{eCQMn7?cD$>k0zV7C&ueh@_NQ<%5eGz?1X()XRh`x88`MS+edB(kA2in%~^s-zodV$n;s`rpGN%Z z(-Pjn0Fna&Z_kj_0P4)LbcQOL5xP6fmrz8(c?>i(B0Rny*!!tgPC=ti`0ZOOVsvDp zopO*SC zGY|5Q+V#TW2gKgr5b`I3?aErjmuj^mQCgA&_49a(;3{YdX@Bj97Ft@c!qYpr!_@&d zftE*VRY`ZTW!Iovybwg0z}uDc5;gBj+<>uWg7XI3z7a4cd;|gkCQ6RlR1&v5j}GK6 zqips{x68}P%i}_S;a+L+3DV|RR2L4Vh={m&n4NA5nZ<_~@>Ct%14fD`c4>-AfD&`HUO z!YL*t-k64|!&+GxgWh6W>$okEv`(+CKS#z^?Mz}0d_+bmKu=Vu>8?*~cMJee7aeno|s1~IxT)2a^~yhTGoW!vQZP1Trr&3&z|INtXB`|tmYoLrQnKY8*b zge2*JZDFe8a~<8ti7F#F0Fr#Tbeou)h*)Z;TzG$%twliJKnhEf>vjz>9nQ; zV0hH2;m)2tEtK51hrsK+;^ysO7PBmpWvSGjO#JdRkHSZUtbdqelFDFvG@nNp1gx!FwYD}sFc`FUdQedJq?WjS}Y2(0jV*B1L=J(nP( z3J}Lp4l)ry*D(Mpda5ANoKGeP8rak`00y%zP9?^RvZgFgTqT6Jz zX(Q6@jz{c}9rvz@7B?$X9YDXwN1Ay7A@F@4NZ%WxvqUqt+)c(R+y@sCHEO~~{(M-b zb2kixc_k8iU@xhH_Yj%2x%>`C6J7AT7Tx0lxb$pw$x*^D5!1}GGK@>}a9(rn`X@+a z+5q(9n5do9hi5srF0WuCJN`T{1U9e6))=s`ZW?9(tb-|XJlT}u>nQYyb7TM{TvbBG zdEwlLC1R@Hip4?^vgJ>cc(=tx>7FW`x6JR&XN8rr4#Gc1%>>f&v7X{P?=y-(@~0m(4V*$MhLnbHB)gTO$3>0e=2M`B6;l^@rb^yFY{uIZjDo? zeL;oPF@7GFvLpW>)V!X{V=Kt+5$bK>?N7C4=P#;fs}r(4Q*F2WeFZVvJcJ2(fHR!x zAMy`CJR`GrG&{u3g6$k9i*SY$z4<|#O>wkD_v1vE?^Jqb*hh z)xQP^s_*W?GCir$4%FyFRE@Mp(kZA(+^Yt<|2@3t+S&#XWmtz%@3bK)@xJa)rJ`!cGdPRC$#&pn^)R7*^Oqb&{C8pJL1+m})4&wsq#ZIZnu>RLaA3CZqM z7`aHdun@?M_mL+YSs2e-GsHE@doG$lYp`Drp*-UvWLVu>7%8#^9}&MEKD>ZNGsKNw zLc!%&qd2rLF5apsI(iGz>Vg)+C~b+*YyUR{#fYw$F6YOLcox)A96>~nB^bNw6H2`? z?Wd_%jZ-x&En$Vy$nik-7}^Jw1%y<{&{n*rZ8L^Hkc%w~ZmX%u=x^?XTnI!hJ4}{z z#nuEJyY`g+H|M26-?|4>=LgR)i~J5ROrdLZZEbJd(`oOPcASkWJ^bHQuU{Rd8z}jC z#H!IVjOn8Gflh=nok#Svj->m2SK;EJTH<<9lXwm2$7&}md?O`tEkc^@@o6_>osb$x$s0pyUkEg9AT;TuTLED#*7D}}8y0!wP) z_lS;I9iTO7JU5gaDY}YpKPIl$H$QE%4Uhf;s^R!3!;YjXF+58_tBrxbHnI=ygK_o+ zXv7HvuJc6bSE!A*LBCmBR}ZLL`lW`PEiEAc9nh+z?8%J2*xxn&5|%%U7UoLP=;%do zb-Wej;<8zEp9{w3{)kg=#Kf^7_ANzl>mp%24&u6A`|_UR4@!GP)*_-dqvvrfcYRr<>!sPL8txAlse2Ki0KOOQz89wp_ zLYiO7{@;A(kz*!6!J-*+L5lG`J}pgwm2$zXNFL(QB&}Vhuw|ZCR9F`gy5ah(5dA`1 zqkEoD_tme2bA1+MZMC0blMS2SqOCqXHudDQl=xFiwxYz@m#~beKG6o283vWNZ4*Hk zlO+S`h)J9ertKEnMmrrc_Qp@NfkYI*VRZVnVJ4{6uFI&iYT=6+r8h95oqb0M7i2Yj zL?R{-ay}wlMk^~v0^#NUI_foy$L#&bk7arij9QAKCv|WEN&!VjQtx|iB^bGWhVx@z znhgyHA?Gxv%+>`wFl|2kEb)%a#@Nwp`=+AM7FFDpk&*WUC!qh^=Oso+B;5w2*dPD8 zE&UT#JZwdnrb4qlv57yea?R|ZRSwd82~oZu6Mjw^ zl9s+#f>J#vCVtgLo)o>g`u=>5cAbPqJN~`)^J|>%gsY!N0dem+;3tr(7db7bcs7Fd zEJ5+(sL(#J1E#ugZ?j+Ka!0b}dQEp;oZbPYwQ#~n|Lq}ZKZP$oJOa74Z!9G&-`3DnwSRf|EthKiB!gp-kOU@5q@RO7@x@XlpN;kN{ENp+fLYe;EohKL*#-2ILr$_#$iUit{ZdcM1be+=?@ z0f*sO>!Kpmcs^_4mlC>wvfj`T`5FJ~%Do%bthYB460GOfFL=XxUcAmt+p*-tPw>)K zYTOzX1$-2ZI`yXh9j?dI7@3WWb+WcS$8EZ zwMva8+H2;eJp*wreh_ak>Btez!_-rqb@V`A1R=^nZ?)GNz}iO0>?4TY%{oTw36(UBZ7zsO#I@^+*a>?%e>vp=v{AQj0G(}l60pSs z0|nS~N$|HF+Rd1r$A|$6azWct0ex80S^-A=i9gYCqF^^5j*w7ur>WY5E^Lu~wT}={`g!?KEXyt;K@( zYqYt5ig~mm&HsYwIO;bK%`92XSIVX4@MI+$k2!8xfbYOKyHlg~Ov=)P+8p5Kr*A3W zyxUCTOtFcmm|`kIsSU$RSTfKk@0f^LMtKF!AXy7*=d}^M$?9}9lvRFH(_2&!t(Cik z1`)+8v|GDqmY2n7T*@;bKdZo}aOQ4MZ@uEE&d0;jQWhL2otBF=`iW}!#qOD%9oGjT z4Q%+*!sP>Cl02*h%nrU^u$f#S62VXM_5j3u|H! z#!p@LBq{jamd=boX>KQ!99>#In<%Yjbv4bih6II6#9$|Mth>IpfTAw-jU?-_`P`SM zq}5kX9@MAe`9IepSnZEeZ)@mH6zKU}UB2ME9WV%y%Z+hOOgwB!)MhE-7hseWk8vC-5%Lvw*zy`i_T zbod2zX6L7;ePUiibFrCS?9`<@u^%)OwW@&B!sRB_F@3 z-lC{@@RqVOfhRgA*&~#>OmyGzLq_DVMxZJ0zT-Pw?ML9T5`ue!KyXmX&fb$`5h-Q^SF(7Ir1fsuiEwHP-xp(;hia{78#xeWEU>vA zdNqiyFGyQ?=^=dMF$IX!%a6r(dZTRClSbSHZ{358?1SW#g@NwZR4`2tN3{{u-192t zn+5i-x zHta$hPwz)Y)@xH_gnAxuR;5mZwD>&Ua6=MxE{J`L$_Y&q|Hk(m#^q4;B(f`5b2Dl+ zGY^f)*`NHYqa8?~dvd6Q69S38;cH$zoCGCqt9-TJXDA&gw|B+Z3F&94*jB2#?OP`1 zVzr7c%58cLt}VXNB-=g;Y1fVFT9%PISXowcGxAMR~0Iz3GGb_g}U)aj&zXXV#GLyUDD$A?UN#Ctq4eNdtVoh#|aWYxCpK)G}!{FAp| zd{b}8A#+PeeQ%hkNh*r50)&Dml?gtHZ2`QT=xs`L26aMtAB^89Ncd|NQWRvBy~Y9x-j`>BWTBNg0PL6H`cbs4-yHcRIvBhDJ^P(<`WK&f?<~=-VmGxCs+SBj5$TL) z&x+&I&vIP1>__*m-1+M+Ty$b)J@X+VEY(OC$R742rKfMM;o{p!nszZLt1I&7Y{+2< z_HyCVhi^f}$39UX*3F^itQ8zT%scXEvJZ0P-i&vBg=(=pawOAqtm{lFva}87ujo%E zQ@DldBe#e0g!!>Fmo5;OBUf(RWTI|jwpH@v_p`s9T^{`N9s+;7__u0sI^}P<2H}yM z_45--Tz5}(?kWdOND_pxYP*Zgz3edm(19fnO7i`70t;=9W&lU z$4zK}tYu{nvFwfzU!)_OIW;eRDwwN!*_*BMopXz^>!LI=NUE%wg6GxSF^(t|3|0e( zjS2N&AKU5#SM^y@z}6zYgSKwcp|;lvW8dD zxXnsroQ`mfh2W2G_(^*|Yw@xM*OXb6L=gFI6?jTnf2O(s&E0m0ljG>#(Sr^t#(!;< z9i8T=F7jH&dC5~?6^ZZdF={~cj(E0+N;qNQX-!duMl?qQr{EVm zmcHF=tI;(~%jz=fLB`RSnuwsxaBz^?#{2fCmScsVaNs*KG!P%*MSb}^w4+R>tj7%i zI5Tt$(%xdmQ{!!J?clhSs;iH~IgrjS;q~GfysYVbntxX!kdACe7*me zKcKzx_lIx&=)WI6dDLVt->2+WP7A&7b3KgbzgPeI{^1{gki$4K?MA}&ULnFuPO2xQ zV4NkBYf;oMapHEos+dfi*4 zXWVyS%zsu(^$u5~D!Wnb&*=W~**4e{dsdf-uVC32M;y9zKW6m6auB91aQQ;yL9mnK z-S^TXKOs1m7YmbzwILJWFhXwOB|45gkriBYEa0A=u@w2%M)i>ml84wD*huCuI)-4( zJg(9RJ=g*o+@9rA>K%w6Ztyvr$1IVRZCr4AWol4AacOQ6?8V~H)>jZcDTvaG3as6? zNJWEp?mv>U!|qn+j}hA0fKowkXxlpLv5d5;)lqvyhFy|?%sEcH+9vw;GZci&qFr~LM#+8SJbg<=9iP(;F;AO?FB!*{GdjoLg zSVjFk+H)v<4^70RmAsNt0OC|8-th6F6u;En-JRXvHFK1}bG?*M209RuiYzx=fd$b# zLB`q%>){$PGio8sl_SsZ^7!y!fH2HaQH>usbkO-S^2HI&@O#Du++x4sI0&}`uO6+2eGz?BQ28!J&I&XCoVs{K&SQO7ljX3}K1JBp zIG*jSeUa%GZ1zkMs`n0a`ja%yDb_;hKqQOK#R*iS-O&og>n?-JS2~U{t+h^eRD$jL zC+cMYEq@V~#THy!kOz6lWFGg{Ne=ML1i8Xcj%4F6E$5N?unx1$mcnZ#JiT3ZJw3p- z&IhL%n#+s%1#3=;{up7IuTCZK_hVQ~GhpR@afG@bhj$U4=k`HnuxlG?>4#%*i50^b z07IQ-jBB2qaf5Y#M+Qm}co}knXH^72tQ@bU-ImxJXnnR{9?gKHg1I(eLks~_hKFE} z4CA9+fx?-et)~)OvR;}LdROK4w|?y=Y>khBqiOSSQ$6X9#x1dMqXtg&;DlhOJ)=ru zB&^ac4-j^27T$!k-LX2mx9@g#;C<)2>(|Bb5SR5UH@k(+A*m-kg%E>E>!=VJ==p6|+Tu;}> zmfn*L|MlhNq$8;^ZUxCFZoAem=pTd!uT65?Q0P#_3$A2NJIA$nU}z|iCE?{zN0>B! z^{T@x+ZG~^)aRu(;W!$(fS8UB;wF))QEDN5&n_T`4l)}CAV=#>q`SIK;LK!OBO0jN zjPq;|#(z*?JjyB0TvJo+w4*xfy^hPX!<>l~OzshGv&h7hb+(+6Z5x$=Y?sGqQQ=yG zL+2GD%jWijax<_$?1u50?7Qac*4_5eRXB08up&gG=A_W<&HJ_g%8AS|iAVq0(mnVA zUS}W|Mbkhvs2KU1?B3%KL_c!fGIk4_$YnpyzHL#111vtkuXXQn#yun5k1AJglMGi~ zLz!+oDv)&HeY5A?rx64=E8XA~1*W7jV@RWYmq3t;xB5U!-XAFXc2-QLM`7Z-{}PgA zoNip_8s!xsL*K?N56N)vKSMdWW^15>U#SG|w`i2% z1<QpdCYAz!rjJN)Y$ zHS>??AKK5@Zn3DZ2T&kdZgXz7^(!76Z#9sXNyaF*voIn;zW;mHjo zQGKQ{W!IKxa~j~I16Amt6I39d7S=BV!U(5iQxv(kA{xz?{ze8Z=BFfwI2JM3zba^c zdI2yh&uFnmm85G{(nHC$9GO!VLNm}SAnWTWJL5vWiwSNV7lA8sQsR}&SW+x*VrE{- zvuZ}NZ2G5_019=4;sC087_S*>Kyc8L;wxUcpw=^0c6!iK6t>e#Sa?eUU3jKeBFAQp zf&$iSD=Pz}WHDy>=&_!iyLKatuf#}B+-Cgsn4SJ^1>g2!8Q<#JXi|9W_4(@dc8Rj( z%UMV>=~-g1 z)x#1{T28;uxvs>x)R9|(hv$k4P;)%?tyk5SGu$_^D~YEFFJ2rJf~lCxw+Epeun;lk zE{#n0<%|s=b48KPVBM!|{o2Z7&wnWcrqn+^-*cM(Ec0&YAZ%1;g9G%qXZ&>0(B_eX zL+cL3VV-1!ethq<(>^Y{UwY6Hcj}Mr!t*h2gaJmpK$`+xL`Xu32`$A1n^U?(jL9}E zxIBT@kbO9V(VY=aJ!GxJfri=z=qd%mefIT%Gwg#E~O)5?+C|h~t@H zmGTay^;EjINrdldT=}gMo@C5|t|_t{5sdIDU8kQAMgGm{N~ED*Z1b=TTda3r7;mNs1zw)?7Cq# z@c+b_?!WFM#NgENqW1qC1!9Y_xaGwD&krXFW<9}#@9^tGL!6Se``~~xW^h(_9cK(g zzD6;i{(diK?RBJlM zVcIuKl2wcmK>xff(b7`j$5$d`DVWmkjjK*s$U> zx@^ml%QPicp^*x+=C}8VB6i-4D%_{+&Xo_tjF-rW7cRo?!>{|`& zs{;X8P4Z(90Csqz#K+(ZBzxxZ7NcbH9gS?UutJ#^;K_>yt30T&P-?`wOH3$oHf2;{ z3O@dt{&AAN;~U;77RWET*`?b4M7-}wWSJA%L5jp8h@O=Y*%a$Sg0sy&0&i&>wP9kF z`#%M)=1tH^ER7phO3V++Lm0nTKv~gg6jYI|BRxlL8+XBx^+~zlBevHOS(>HPA;g~= z#eMZdNeLgi<#FfC#~hi8)_%aHIsSSMpuj0EJl%6NeU@~^+25)x)lV)=KlQX<-Rq&K zq~O;x3H<|)QwW<-c7zakKfjdIAi;H=S_A?|eGDlSkcr3O-0Sxcjx|Aabw0|n{J?=f zwI3cp8lqZM*@27@l&wDRTZjSdDh+;Bf1wM4B)V#lHsvbeA8qxS6q|yN_+Oi~w1`FM zcW;;?;c48lV}IZ}4AyL@{PVy~Bhsvv9iUp))FFJiuCi!-OZq&CGy*U@daWCyH?!##1#I(8|1E};R2DpI74NqE+Ex764R(8=`N6dgrx+CvzUb;Qn@3q~ zaYXhWz4PP=Kvx`+;HMz(;XaG-5hU%o{mWRWZ6{lGi1$aQFhkz?a03@v?4#C*k8q$S z4@2S%cK+}i0#7~?{J7zr+Kj+{$8ZP{5Wqz}unlWPQPP_;arL5d{NGL|Sv8Ohmf|q}zU)mK`gkM00-+o$^Y0l-`MxdpO z4$Z2wehfa;g0y#xqJomNS22-e)bFSupIo%9c3ANT`2NA*)Nco2S5krQv_{7iCg(Et zL_0TdGu4;3q6mj*73Eh;Ih>fcH8}-;AZ(aW8VZ0Po~elat?5>_%?-)VmhQQPh}`#$ zO8zD0AVruM(%UI13>kEMTx z^d;)AoZPpM5H>rTulMP{L21^%_r>r3ynK*EP7ti8pZ>a>q{xnnW@uqU#IV^hj!MiilN5h%_8~N5U1%WTcwBN z+y&siR2BJn%XF?=J?d&%3Cij^*Yz+ed3Om??JDn^6R<= zFSH@P?rfHrXlIXMb41g)GRY-9X2yP>>u7HN%}q6K9cE_EBdtW9L#qAD$*RCItV6A` z^JT(^HOs;kP2LZ(J09Qjik=W`8-(l~o^6F{17cLd$y2m^aK59MYe}5 zYqO68EV5}=g(?ux{EGHgC&@w2%qZxTYv25Z{ohehqhR`l;~!YosBsb6t835j`F4b& zBTAwib!v(k%IW|&Fu5{j;T=NKF_QUkWOm#ZfKqZ28}cemO~i|Ja)tkcVIp5`2n}cn zYdRL@X9ra_glWI55??S3<<9>$6li#}AEs^Gw1Ew!8*s+J>=idkm_n$1Psy`G8s#mb zvX9IA{mtPNZLQKG@a^~nZO8Z?bf(P0%1`~C|MBJz{-0I{&FD)+FmGWMBY7(%UVuBq zUfO?E!?A_#cM9n}iW`Nckj74b{+Swd+ZNQJ8@(iPr}xSyIzsX)X7Pd6R1F!(%PTJN zipmo(;x_<`N3gob8mFViSCD!;1#ueg9$h6-j$Aa8th}LwV-qgCqOh<4P-d@Wd#?P> zsP3)4g*5Nf)6;uf6g;>d=SbI2lFUSSqbBXHNl23a-y7^7+RFdE&e$L1<|WnNJ4lYP z3Le}yg%}!kjWD*S_Dab2EwQnQcEWQ@Ya@A}THapx6)>rdpW-sc5u(Vh|W)HLWP zm#a%O;recf5rp|b4@9KYQV_Olie$Q);g5HzFFD*4F4povwM!I<#8dgjseewcPWJC8 zR=eF@iFRWw$~f#({_w*mA#xMFM0S$>&@e53U=8#~Y-4T|T_3{|zQ3>520ZU`+d48g zQrIB}#4E#EW!&G`vSrEe23)g)yRK@-1>f71q75~U&XG6`LoDdfR1d&1y?58f!!p15 z@DYMwqi2usk57CU1ceoftl14^@qrH1Rc`k6I>hm5v ziBSf73Z40OfIdtwi}-EXk+&7v3S!=qphK~AgxZfSZ%a_?*oC~m*mUOv>$m|cD5wFw~cL^?swU3g-q^RZ>q&yx}E}^IY*nB9V3ER`K<qesJg|{HSH@n(WIbPlY2CvX)wvu3 zl$Khc^rJ4Fh(lW?_u-Az_U}UhiY6cbog(tOS|uP~(Spe;etsH4neW8E31BAki!77n zl_`bnu}VbB;>=W}-(g9<&M|UXpVN+`(lLCswAt_H;9^eQ&*uZXDuCEOtuHJ%rkdtHE5R|63_yKy#CdnC$!XoP!;eo%(V<{ZFB z0gkiP5AWa84?hdDuSw2&3Ci&QBT?}i=I2=y^$z|`8adN+8>hq})Qe9~#kEQ$L$|K| z*X2^*N}twDeIIW3&!$LQhw1xH5vOL5lcEoJ1YCV4!8vi1u)NF{ z)*EUu!`01g+Unk7H6!qb75(1_{Dmq)bgPsEg&l|m&@vM1)t2fVg7`@W!sugs#9^w; zna27lZ=qiRt%MBmq<_L12*$}}$LY0>9ls0(_s?q+MpI+ZxiJ`zcUUu(SNo?HXQC5r zVZW0g+HjlhlF0N{zzIeym(R&CffF@s#g8>VZO^@^t-`%yIsMB{FpWd|uOEwmYGqHuN5l|m$%aL{$5v5b=io~?uk&O_6XQW>6QDV#kNNHZ17bbNK}7=%h0s;W97*|N%GC%eoQ8&*+9B_2UR z4emHQkGZ-3Jtj!~^?{Ii7wF=3nvRp;{2JlM|B-ZD*rYq@_;}STJm>yA$0We|sS02l zbT1HdH4qWX)DoqlZb(2&J_A5MMGDubIZ`5fIdL39ZEg$T z@yEz~Wd|N$CwEuD6&iC|c6cj3t%Ts!x=1*!b4z%%m6r81L;K_tlJ;+_ zW)g``Sr4{+SD&6YoI&N3V@|ZkzmaQSKpZoWAhMisxDFgMm1L`iMuXVBVCoI;Dmgq= zOMQZ;Q00J_^JLGSgKw1rPD>!ywHlS9h|M&WlS5oxMyK4s8Xh_~`(24;U&uGkE(ST#38r0MWaWU7A9-@g}E^mJ=h$bl_K>JcaAxq!u3OY28M z)=C(nFciMY9Y3tWG?r-m%5&brfFuGKX||1aa)zkvi1_g3)%f(B;^nu>B2b_sK|dDOU>abgXT_fU+C4mN3Uzp~^8+dJS1;=O2N72d!6%8EEC7;#%&zJD$+9dDyb93YH zU5@zyx~FyE%?8aPO_pQ433EN>o}Y&3;`yM3n$s!L1d=nuB%ykF7cXtv7%&q z8OCCSmX%%&Q7}BeP|m!uh;&q-i4nrq=mH(d@{yDb$>RHDKuPqH{hfl1X*O^ynv(Vi znOXc4o%jXzJ3`BP?xS{{=5n8!e9|4QZoWz81U=r>1oev8ej5XhG z8G-y$f-vps5uqKc#41zuiE#HI=|2&hz7=JbNsX~>{3h(WY1pO(CA_YLe)H1TyR^RBjX{?94+b}3<9g(!2nv6 zTMRC@8Plta3VsxSOC9-IBK0ax(5eFOowbe9q71FGrEF*fXRmMMJG6Ha6Z+KFbnuP* zhO$@`J6V<1pfx+7VqAm=9F@WTdiX-St2R@Wjr zZ^#_EjO*-+_sHfZAs3WNFD;9qj(wXYWTf^BE*)r$CR3B6JM^xmCOX_3M*4BzsPiAG zF~B5oTTwSBSa95*msSg_EpqyQPkgma1au7c;LbyC>5P7|XnL~?h(HiE^nkB3n^46$ zJ*QSCxuPagd^ATWB=pf9UPsme0;7T@&w9f|6|$uQ-*fV}WZBu}pI?j#JX>>mi3~j& zJgQ-~BMiD=Ut!5`D}|5j-MI=-03&9z&ow`mo5|#pSZceZ*+X{9pK=d7FEg$ zG)(EQ$vRn0+qcz{q<(!QgE&d@kaR9mo@_!)Ob%58K0!9|@<6=i|t1vw$kS1h{DvyXVH zqn%u3fx+n1heMOSK1{2<0qh))T8YYTG-D2G+`3_Bv}SmE1N0}}bgSwNFL6C16kQqm?T z?vu#R?s!8})VP+s@&GoEISua5)cheg|h~YqeUG;mddnAx*7b z$K^6FXAQk!*s6$vo?dGxRY!W_8j|~~z@x`=-nV3>7 zJ)8hTH`Y{mRka`u*DeVrk46V##H*B;D*Oh;49=1fH4XsV9MJ{+vYlU$ufAz~ULmlyq6fG51t0yAlzIQV6H`d{{S4k31dmoZMT< zY8G9g5m;6dCXffUL#(92x#`O};;|)PTr}d77EIiM6r) z`N@}@tmZybR#`WNJ%O8=i}Jf&MOUghAS_IH0zFYiGjLMoXk4a7SXimg;n$J6P97&v z=)(PoFa%>)Ms~KHQ=s#jnrZTH5;aF#54#2DenF+Zb4i38i4v~Lh|KIn*Ndybj@P~G zI==M;+om*>lVdJu83)w&?PC0-?Ep?QXSu&7nt7Z1fnzQr^)BtX=z8Crxqe8~JrBaT zXZt9EF7iLKQ#m5%JvsILy8rHY2pg=l|1&vJB7-d49yZwv>U!*yb=88BmeghZ{bXn@e$z0twJ45*(QuU{5>C;E$@{1`lt?}-#mWHB`JUT zvF~9aA(1#E*+|@Bs7B3fKqgE>cmA+9FxY>Y^oFm6y9#$5jTfdpqR#fdT1uc@%;V}f zG)wicKt|UuLH&&MgbAAuHzKy2`qAc%>!^$014e^3D4?waPYczx#}{FhtL1REp(w5a zE1Y_=575|b4GzN;YA}Hp;KQqTEO;+_mo^u}4rEe607F@{&pc4baG;MS@U`%Zc&iGh2`3+%7QXr74JoSbg7{8D6wQx={G5k+b!gi*iI{=gCaK@C zmM>nh@e)O7t36GPJGr4o_?CF^_-cT$czY2fGdARj4RH|H!OfzTImbjTxM&_R#^`~t zmY>}!SWQJ*EGBG9=iB2hpDuZ3!k7v(JB)B^F+)xql1D{u?FH$m>{s)R2;28kW{<#l z&3rR1Gj{L6y@9Od5y=`0;U3k3DcOmg@fxdIeEDn*R@BA6GHtkW%opP=-BE;-U&iUy z(GzC#AL<$}B82i~a8@73rC2-a0c66p-Kg!jsjZ^=Bul|_OF2CDpoV<45N1a8@jQ1HScW$O#bE{s1_IhFYv4&e6@x_&%-<&|(>WOhsKy`&8 z%Z&%VgbL!_AD0@(3_`{n*U?2=F+W-}@-6TJgCb3# zXgOLKzy*V3U;W037&V?85&B3G{)Pg@LJKV8uLQGYKaUiXNCaLk5NP)C<1?poto_@5r5$q z$U||dRek&wi60WCRL~l2QKe1T`XrPOJEF(JO%Gp?EIyA}OWdL%9bRZmLF?O6(8Rxy zN@n*P+`QQ7c^eU-M0}2y-lkv3m|Z#}2?o(SY!h`aTs7%AyTa*LDN36H)=xPky(U|U zbag8X*;87Y%_f4uWFcZ^^C~I zLS$y{-k{KaIQ{x9U!ZM6&0{tS9vw*%Yd3wQhTzXj7L+`=^Q4-&vS>kT*Mull1xDDh`-bsY8G`g!4Do_-CIe zu*V48s>8aTaql#AB?8^(X5s!sdek39D7AZFPos^06DUPhOQcb>MDJr7*$y2J(JHyA=wS*3;iFENK(2U7Q*9(uaEvUYb5 zNR&BJ5iCz=ld-7S7Abo6Xbes?8LN*-J!*8UvVnf`$vdI{|EfhAF^@EcrZ;vp<1ga+ z`yhFW7H@w!TmmW_(~z?>Zm1Ps;P(bu>Sx9L@hSPWF-JcN)LhJ{IY!RHLX-l!Ko{2~ zl=aw$&FrTK1}2Q>1hNgLeA!wvO@!{-M9)AhGj+tAC=^M{ZIKvDXk*AS z+0Kl~Qi-YV4OyD57InL&a!Vp3g|ba?i$WuctP`>`zfbpm@9+2h{R2I!IdkUodB0z; z=QExW8!v+m_s_nx3~TjaGbek5+m?R%v^fiFvN8c*@e~u~DpT*fL)jlW5O0e_Y{Vn$ z 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

    $ueU;V`e22L2u2kzab_M1 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ⅇuDal7kq+3z9 z*iV?x-*xb#qNS=8QrePIFCu~S+v8iMM5L$|qZlju>`Nl0`|3y!K3u+I%-X+S(q$VQZEdeQXfMMWz~2VdB_IDm6QXR7L{(}+vpCybNxH?N<7LDB!-q+R zym9fo6c&8GvkSXt?paPBT0Wht1m{&X9Ow*3sj zyn6l5wHSJ>VsCrgR^YZ?0jV_c8pplU_71_@A3UaQzd9aYRvxY&6XFm#V9*X2r#F@S z7h!t_V7=RfSZAaA20;1~`%=73;n(9(tPRsNfN#6;5Ul%Ni~_Zujt`qf|tY_ zN_8vbbI2x?$MYBlbPQu0c)N^EgLcoGgxD?2A3?geSRNe#1ltfcD&7&eb_ZC$PAPla>Mv4w zRb6%KGb0O&W`O?CtNzEShsnEu%gH%9H?3LQsq|1hyLt<+aI+@HDSc;T-VTW}IyI!B zK}~>aJM`>p!(wODfO&iq=GaAjvT~UB=vV~x9d={fWn7mXCg-ovwNLixXH7>&+y6JI zm>~Y#LVU%zt_m%nTi_sK+>T9+FmorgoD*3Fv&N zJ;pyLcK*Y1VH(Kj=CSk%k_5%g(m%I@;29!1Gs0Fr#<{B1QAB7t##`IX0_`h#7|Dwg zaE+B5Tz;6{uNCt}J@POn<_H1kr7;?J&t%YK!7j@HsRnMp2g*4Sj6<+?`_XBo%9c4p zZs60v$Ruq0ZdkuU=`#S0^M<)|)41sg^Nx~Ra#Z5UVivCAhp%Na78lc0yszrCV?A76 z|Aj9L{bg!$U&_X60)Hy^A|9)fSHnDVRd(qTgIIB#@cNr7_v-}$r}HgNy2VbSZzigD zF(VlbdqLg&0RYeDna9r(9_Y452jlkt8X@f2O6W4hoOY90*UOe{PLV!wEPK>hJ(Wa+ z>?+(Gt<%rSK}?o7PklnGY@E1U}s>ZCqJ&j<}~dQKxRTI3$>Lb*ftoA`;Ip zEm+UF?Rh z2*=D>FGtkfZl&6~smP{xIZ3w|lcoD6I%+=aaQfxi(tI6#wogj<-~V$vDf9bOF;}%! zQ~wzF=SMJ?$g|5t>?Utqj&*qr>!SC>XFnc1y@eEf<2(r+>p{c0Q2A7l{FN!mwQUX) zI8E3SmzXI}KCK8kvY)Ze21_cj9SK-(fz}EPr9_}`tf|LNnx_heji5l0$(%K3Dh~mr zAC9aC`je`b)`^s_0F($CXIfxz=<;YgcGQPAmC6gZVqTq=dO^j9WCZ*7WXFTrB}vE% z)D6nN3E-vrm?r)$VO~0JPmy5KTby~!nq}3C5B$>5NUVISQVPlo0lfL;y?2@9w;GkY z9pm6i?>UI+39KQm3QMN!HgT|SO3~zQwJv$QOB`=w^rVwt=*M?dRGH#`j9^+4tPTc4 z`q@e@b&6ffdChZkVNmQMDu3!K5tT8JsYAtJ!f#dFspM^nRnt~^%S@2=Ewn&i2L6#F zQ_b-Eygos@c9ARXa0h3MBJch)_oOmCAi)m|+`q~6oS%+1sWB&R)E}R9+i&ygfuakVZf2Y`fuO=#@E~&$5~VS}T;|gSFfWZm zc81kw-}OrBZR|J#`?(WK@pzfSMkCB?8DV5OWKo5A=RtbR4n&OlLy~dD z$0GGirblz)(qFS4Vz5h5Fs3nzCl?#C@2iVe8H&yh0*oqEI=@s!Q(Dj9I%pMtgZHrI zKfY_*WMj6@et&K!Q?rnek4nOl9Fj|1zuYJa9et}leZ9ZD&UrNyc$Bg70-Mb(m83U=OLP9aJf{zt|YtK^8qs<4oUIz zCJ+YXa+0_f;_0Urhy@VSH2vP8VwROfQcOG3vzyA#Q;>YMN^ohsj4^T&dsgxuz7b5G z=b@=C3)7!v$M!|-s^G;NW8MLWrV~(3{3WTke{?6vaT8BaVk{2-Lf9tMDY=hpu88>{H zj8|@C@JEvLHY#mFzHoUS@A>bIrua^jbOkJ4atpM3sI5~f;-jQT7gg-LDBahU_cRK^ zeZ;qi<>?uDY{nAr5wjrNP`4gof7!#NXLPs_!^|@bwrn^KInV$4z2BoO`_qNrsiUxy zjYz$Q$*>h&-(jYO(XS?vev>H6T+w-?ZFM|+SkX0p0bj1mL8xWlk`esS|Z9M5TAb$q(!Ak#SyPCVrYSn+ zV@fH#c4kw07E6VzM_)%;aR{5^4(~FWnV+?i-K%otn{wv)yNC+I3jFs-D4|IG`{y?Y zgptyZ6Z7KV?p)(dCGh5+;me)JaI(-saA83s|Idd}yZonavFsn@IETp7X~^Q6w4yw_ z3pYW#(xta@7793s+jB8-?Nb(GnJATq8gcFt2Q^G(cn-&^ z&$Twg)QZZ~eWk42PHX1n8KrlE-BDICX9#-0uaO;Z!Zyfa^3DM4mw~w%rOv&WQ59<& zCgpA7cW0yha(sC@CDf(p9?3CQDQ3d5Xq?n_9MhGC*{L>3i3!!{M>!EVhd|R(l3qUU3bP2=Tbe#@2SgRz*M7&$k=hIn*VHJ@f?<^{dQbWuPE{gSSy)Twz$?;e{ zw3s(*bJ3Sd^WgLuKMXidB6(Udg(W7i?=(VT7xRzFd+wcA9V_%{Z zr9B$@4A(ggM*4dbJc(R~%PO*Kcd9_8tMjpm^L;IEH%a^BS^An1lDe6f(LE^F6RvML z!2a~!@K!%DMSnXx=aF)Eoa-jfGncLKT`=t1NcR6@<<50K<%XZ97}8x82)g=J`Rd-} S_5}!ht#|iYb7wU>@&5r6549%% delta 240155 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

    Vna_0JF@gcVt_T)oBB(b@^c-!AE=l4Rk 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 a27o_@|3UK1k7EGz2X*Oe{M(h4{Qm%Ab|a1e From 927a030bbf419f518374449609a0f85ecf161596 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Wed, 30 Aug 2023 17:43:19 -0300 Subject: [PATCH 0594/1710] Fix examples/shapes/shapes_collision_area.c help instructions (#3279) --- examples/shapes/shapes_collision_area.c | 5 ++++- examples/shapes/shapes_collision_area.png | Bin 15319 -> 15595 bytes 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/shapes/shapes_collision_area.c b/examples/shapes/shapes_collision_area.c index 34d04811d..752d4e79a 100644 --- a/examples/shapes/shapes_collision_area.c +++ b/examples/shapes/shapes_collision_area.c @@ -99,6 +99,9 @@ int main(void) DrawText(TextFormat("Collision Area: %i", (int)boxCollision.width*(int)boxCollision.height), GetScreenWidth()/2 - 100, screenUpperLimit + 10, 20, BLACK); } + // Draw help instructions + DrawText("Press SPACE to PAUSE/RESUME", 20, screenHeight - 35, 20, LIGHTGRAY); + DrawFPS(10, 10); EndDrawing(); @@ -111,4 +114,4 @@ int main(void) //---------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/shapes/shapes_collision_area.png b/examples/shapes/shapes_collision_area.png index 760e1bc1d94f9f11f21993fa5193a9ec8850ba2e..049e6fb00213631953a26d5457e9768e64f77a77 100644 GIT binary patch literal 15595 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!Df@Ei(^PdTyo8_%zyI} zMS9u}WN=_77-XFqGEJtqJ13a1xM3w4;uH;TF-rPoFIF^=V!}r7ES8;c(FKPjP{SLi zgc7zClfZsl&bXk<;%2R|xHA{08}cA-$iZekL=5Nz6WmVt193tqm7UNa!W4Af->Bh3 z#sWm}UQh%3Ml&0aZw@%E@eNvP-x2g{&1~j1eIZ|4c+{`i%&=Utt2yeI+vRuA=sMuk za3%0d`mARYJ_%be`Fm+BdfohybK2^FYLC$VJiV=Ve6@=?fBJVFDUk4ehzOf0tp{H4 zoXFsk;%&SrAc2Kg5Y4e+LY8n&T4O}*jw_R`7Bg<{5Bc4Bwf(g1lUwGq+MvmSA?}|; zx>-Rh$GVx5(ofmxjrIS&nI9YUm>6Wom#B_~{*VvIeNELL>b zE|4I}`x@b-Mqb9CbRM_I-CG1C?mjl0l>|$H1rQH($zX9i*grrIOt4{g%6QMV_Pfu7 zZ2O(nek*EcUC+2B2i5UVj_Js}f3E9i$`|=_O8c%v1kM&th%-&`ICH@*&4?%G3?{q^ zj+)!l`i?K@?ZWB%-9I0JMhnk3fdyae1fzcEm#C$f@b^N~^#cxw$rgA_mat{bHrWW% z!O(u-g{JS9UIr)g?fvt-%%W7F$%rA>8JuTYVJb25FEG!nsN)jbb-MkPT<68TvWs^X zfYMt-l%m6%l>rh@pMRa4V%a#M_J#7FBdgrQ+wF>-b?YC^5>&cg6Vup{t(H>amSyrD znjeU=PNGrS;lKOgYFx=0Sc1F|WLj*ju=r&TE;S7VGja(q74e?&zd&(T#F5|4A4t#(VDShheF<&5NU;xOnoDE9>L; zPkL%P!}i55Pf_)2MKRC#LiSwHp2Hr!%lW&x&c9Axey^*Kz1BDD*7i)B0WIVlGC6wA z0W--$)R;iz8Bm;nDhOyMD^hgGNNc_#SmVXqaA&62jJ;D{Ee~bk+g7lw)JfH(*)gN7 z>Bf&G<-cTfAHfRf4G;rn;VYm)Zh)qXL~*7a-|_@LX>9%0GhugnLtHgqhUmjDx3nE| zZ2I2KmC(HdGq|7)?6Olt8Z0QxbY$JbR;|sJmCw8L zq1i6;lr093YC%)cz>3*u)-^<(1+x}fSiBGe7as72NQMYgi`y3V*Du32JXmmDGvY|! zAO5Y9Oi7Q+m&`RYi+TX7^g)3sg{`86RaU5B!Cd?6;)@QmOwX>cYBx9kgHmMsUs0Ut zi&ZMebq=eA6HBw+hDvBISDLshyzNH8!f$fYL64JHyl0fsm4bS>Aq)}#+t9p?-~t1n z=YZfMnwJXTG9Gt`Nz_3cx(aVh0h@eB$S;^iod78wfR*iNM1YzcqoFVw3gF(-Xj%YO zTce>c8VayJ*l1c9O$(!G0nvdPEgeVOj);NPVNp8jEKY=tuqrY!q1Yh`)20Qq|qH@rKa@dlBdc^(N!#5S4EvKgIy0?w9>&cT7^j7LL( z)}a7gl)&)#`uh0){cYbds#HYgyr9hDmJVDnaub&thPzG)TYxck12LzG5b;r|w0i(?5&x62S+H|3c9*7R=$XKMsb#V@XQaf@a|7=G9i z(*MUfMgZ#nAMiW4Xd1t#tfy*e%ETz;iAVa@JlfPBX3TzNp5jH>wC}rkz7@!?C}BOl zf62L;178;Io*hx_(aZ03xIOBr$l5!$QQ>{adldtCz#F?ttdUD{gp;5<+g>I5zB!?! zSo*@4amgzA%*<(bCceM8$JWNF;nqk0cX6E;znGxu{{q>ScooGm%GP<==u@i-R5a4T8*o0E?Li=Bo)79-cKbHVW)M~C(r6L zPo0-BwcqXaHRymRcq4)Y4`f+AA4-Bm-Y5lH{~y1Tsx-Lq8<;({ZX9 lo!|f!ZKI(;Y$z}=6s6|I&Rdh)1l+{T;OXk;vd$@?2>_Q(o@D?4 literal 15319 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!Fryji(^PdTyo8_%zyI} zMS9u}WN=_77-XFqGEJtqJ13a1xM3w4;uH;TF-rPoFIF^=V!}r7ES8;c(FKPjP{SLi zgc7zClfZsl&bXk<;%2R|xHA{08}cA-$iZekL=5Nz6WmVt193tqm7UNa!W4Af->Bh3 z#sWm}UQh%3Ml&0aZw@%E@eNvP-x2g{&1~j1eIZ|4c+{`i%&=Utt2yeI+vRuA=sMuk za3%0d`mARYJ_%be`Fm+BdfohybK2^FYLC$VJiV=Ve6@=?fBJVFDUk4ehzOf0tp{H4 zoXFsk;%&SrAc2Kg5Y4e+LY8n&T4O}*jw_R`7Bg<{5Bc4Bwf(g1lUwGq+MvmSA?}|; zx>-Rh$GVx5(ofmxjrIS&nI9YUm>6Wom#B_~{*VvIeNELL>b zE|4I}`x@b-Mqb9CbRM_I-CG1C?mjl0l>|$H1rQH($zX9i*grrIOt4{g%6QMV_Pfu7 zZ2O(nek*EcUC+2B2i5UVj_Js}f3E9i$`|=_O8c%v1kM&th%-&`ICH@*&4?%G3?{q^ zj+)!l`i?K@?ZWB%-9I0JMhnk3fdyae1fzcEm#C$f@b^N~^#cxw$rgA_mat{bHrWW% z!O(u-g{JS9UIr)g?fvt-%%W7F$%rA>8JuTYVJb25FEG!nsN)jbb-MkPT<68TvWs^X zfYMt-l%m6%l>rh@pMRa4V%a#M_J#7FBdgrQ+wF>-b?YC^5>&cg6Vup{t(H>amSyrD znjeU=PNGrS;lKOgYFx=0Sc1F|WLj*ju=r&TE;S7VGja(q74e?&zd&(T#F5|4A4t#(VDShheF<&5NU;xOnoDE9>L; zPkL%P!}i55Pf_)2MKRC#LiSwHp2Hr!%lW&x&c9C4<4@&do(TlmM;XD2iUMHH0hYsC z@CMjiV2uu`O`Y{vIL_uwuxj$pWSC%mnQ8O%pubzX4jdFax5aP8l5-Ukr0sTmxoCa2 zPplSJhz2McSTP&T`UWemF|yhVF>oe?R_Hfc8P@HUZn5!NYpZ;uFJ*E6j>)^j8$y0( zh(3JsYxa$bg(flXn|~qF1ySWF!UM``EHCzcn|DW2G@@m~tGKM+{da6Rm;9O?Q9gOs z^QKE5)TbD_eb4C;n+J8iLmWrXIbazw2Q{H0iWE?i1=XLhGKf*Cm0_V^QB?nh;8(gq z=`ts__TQMeH4=PSUF>wRTZ;RoWuv@$#q;`kf13?+qD-iPZP}2Zc#9_}7?cZIUi{CffO!)UPjD84Eu?uh z7nTI!GO%n-u!Wbf1?UYnoC&He9^%kfcpOTgm>G2fsF^Yv3ZtPgnkkXXr_mfSnj=PY z#AuE{A0Qho7e>p4(Q;w5To|F{0>kNy2ymB=XTQLMUB_&t)`Gjw7ETXlU%P&B1EP~E zaX>%<)T^z7jSgURe@BP(Kr<(!p)eW>unDTsv@n_$M$-avMKPKqMsoxebA-;~iB`zT zYYQLavD3er)hAs4yKrapzH2>U>lg3Lf{ucNW}Ai9T-;)atOL0BjofN=n@ss z7%ZeH8eJs=3X0KC7!8Hd9DzJ0J(?p%bHr$l7|jvL9hT7?F`6SrbHr$lK=d9EtkL$u tXz4gwI*yi(qopIFOhmBgP&zU&oM#T?66lzs0o<_5;OXk;vd$@?2>@k)q Date: Fri, 1 Sep 2023 23:23:01 +0200 Subject: [PATCH 0595/1710] RENAMED: LoadFont*() parameter names for consistency and coherence --- src/raylib.h | 12 ++++----- src/rtext.c | 72 ++++++++++++++++++++++++++-------------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index d932e3a34..9043a11e9 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1371,13 +1371,13 @@ RLAPI int GetPixelDataSize(int width, int height, int format); // G // 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 *fontChars, int glyphCount); // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount 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 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 *fontChars, int glyphCount); // 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, 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 GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type); // Load font data for further use -RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info -RLAPI void UnloadFontData(GlyphInfo *chars, int glyphCount); // Unload font chars info data (RAM) +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) RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success @@ -1387,7 +1387,7 @@ RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color co RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation) RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) -RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) +RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) // Text font info functions RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks diff --git a/src/rtext.c b/src/rtext.c index 3fb2b8861..9017c8b26 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -358,7 +358,7 @@ Font LoadFont(const char *fileName) // Load Font from TTF 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 *fontChars, int glyphCount) +Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount) { Font font = { 0 }; @@ -369,7 +369,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCou if (fileData != NULL) { // Loading font from memory data - font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, fontChars, glyphCount); + font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, codepoints, codepointCount); UnloadFileData(fileData); } @@ -504,7 +504,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) } // 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 *fontChars, int glyphCount) +Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount) { Font font = { 0 }; @@ -516,9 +516,9 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int TextIsEqual(fileExtLower, ".otf")) { font.baseSize = fontSize; - font.glyphCount = (glyphCount > 0)? glyphCount : 95; + font.glyphCount = (codepointCount > 0)? codepointCount : 95; font.glyphPadding = 0; - font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, fontChars, font.glyphCount, FONT_DEFAULT); + font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT); if (font.glyphs != NULL) { @@ -562,7 +562,7 @@ bool IsFontReady(Font font) // 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 *fontChars, int glyphCount, int type) +GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type) { // NOTE: Using some SDF generation default values, // trades off precision with ability to handle *smaller* sizes @@ -600,25 +600,25 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap); // In case no chars count provided, default to 95 - glyphCount = (glyphCount > 0)? glyphCount : 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 (fontChars == NULL) + if (codepoints == NULL) { - fontChars = (int *)RL_MALLOC(glyphCount*sizeof(int)); - for (int i = 0; i < glyphCount; i++) fontChars[i] = i + 32; + codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); + for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32; genFontChars = true; } - chars = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo)); + chars = (GlyphInfo *)RL_MALLOC(codepointCount*sizeof(GlyphInfo)); // NOTE: Using simple packaging, one char after another - for (int i = 0; i < glyphCount; i++) + for (int i = 0; i < codepointCount; i++) { int chw = 0, chh = 0; // Character width and height (on generation) - int ch = fontChars[i]; // Character value to get info for + int ch = codepoints[i]; // Character value to get info for chars[i].value = ch; // Render a unicode codepoint to a bitmap @@ -678,7 +678,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz } else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data"); - if (genFontChars) RL_FREE(fontChars); + if (genFontChars) RL_FREE(codepoints); } #endif @@ -688,17 +688,17 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz // Generate image font atlas using chars info // NOTE: Packing method: 0-Default, 1-Skyline #if defined(SUPPORT_FILEFORMAT_TTF) -Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphCount, int fontSize, int padding, int packMethod) +Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod) { Image atlas = { 0 }; - if (chars == NULL) + if (glyphs == NULL) { TRACELOG(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas"); return atlas; } - *charRecs = NULL; + *glyphRecs = NULL; // In case no chars count provided we suppose default of 95 glyphCount = (glyphCount > 0)? glyphCount : 95; @@ -712,8 +712,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { - if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; - totalWidth += chars[i].image.width + 4*padding; + if (glyphs[i].image.width > maxGlyphWidth) maxGlyphWidth = glyphs[i].image.width; + totalWidth += glyphs[i].image.width + 4*padding; } //#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE @@ -764,7 +764,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { // Check remaining space for glyph - if (offsetX >= (atlas.width - chars[i].image.width - 2*padding)) + if (offsetX >= (atlas.width - glyphs[i].image.width - 2*padding)) { offsetX = padding; @@ -789,22 +789,22 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC } // Copy pixel data from fc.data to atlas - for (int y = 0; y < chars[i].image.height; y++) + for (int y = 0; y < glyphs[i].image.height; y++) { - for (int x = 0; x < chars[i].image.width; x++) + for (int x = 0; x < glyphs[i].image.width; x++) { - ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[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]; } } // Fill chars rectangles in atlas info recs[i].x = (float)offsetX; recs[i].y = (float)offsetY; - recs[i].width = (float)chars[i].image.width; - recs[i].height = (float)chars[i].image.height; + recs[i].width = (float)glyphs[i].image.width; + recs[i].height = (float)glyphs[i].image.height; // Move atlas position X for next character drawing - offsetX += (chars[i].image.width + 2*padding); + offsetX += (glyphs[i].image.width + 2*padding); } } else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect) @@ -819,8 +819,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { rects[i].id = i; - rects[i].w = chars[i].image.width + 2*padding; - rects[i].h = chars[i].image.height + 2*padding; + rects[i].w = glyphs[i].image.width + 2*padding; + rects[i].h = glyphs[i].image.height + 2*padding; } // Package rectangles into atlas @@ -831,17 +831,17 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // It returns char rectangles in atlas recs[i].x = rects[i].x + (float)padding; recs[i].y = rects[i].y + (float)padding; - recs[i].width = (float)chars[i].image.width; - recs[i].height = (float)chars[i].image.height; + recs[i].width = (float)glyphs[i].image.width; + recs[i].height = (float)glyphs[i].image.height; if (rects[i].was_packed) { // Copy pixel data from fc.data to atlas - for (int y = 0; y < chars[i].image.height; y++) + for (int y = 0; y < glyphs[i].image.height; y++) { - for (int x = 0; x < chars[i].image.width; x++) + 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 *)chars[i].image.data)[y*chars[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]; } } } @@ -879,7 +879,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC atlas.data = dataGrayAlpha; atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; - *charRecs = recs; + *glyphRecs = recs; return atlas; } @@ -1176,14 +1176,14 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz } // Draw multiple character (codepoints) -void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint) +void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint) { int textOffsetY = 0; // Offset between lines (on linebreak '\n') float textOffsetX = 0.0f; // Offset X to next character to draw float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor - for (int i = 0; i < count; i++) + for (int i = 0; i < codepointCount; i++) { int index = GetGlyphIndex(font, codepoints[i]); From 0f39051562909875d97a0156eef5d760f34d6390 Mon Sep 17 00:00:00 2001 From: Johannes Barthelmes <615914+jbarthelmes@users.noreply.github.com> Date: Sat, 2 Sep 2023 11:41:33 +0200 Subject: [PATCH 0596/1710] Fix uninitialized thread-locals in stbi #3282 (#3283) --- src/rtextures.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index b88aeb154..a97cb0b37 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -147,6 +147,8 @@ #define STBI_FREE RL_FREE #define STBI_REALLOC RL_REALLOC + #define STBI_NO_THREAD_LOCALS + #define STB_IMAGE_IMPLEMENTATION #include "external/stb_image.h" // Required for: stbi_load_from_file() // NOTE: Used to read image data (multiple formats support) From d41e3141f194d82ad0f6667aadac2d96d69bb770 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 11:48:19 +0200 Subject: [PATCH 0597/1710] REVIEWED: Added `SetTextLineSpacing()` to multiline examples --- examples/text/text_codepoints_loading.c | 2 ++ examples/text/text_font_loading.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index 1f3db0cf3..376b4972a 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -52,6 +52,8 @@ int main(void) // Set bilinear scale filter for better font scaling SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); + SetTextLineSpacing(54); // Set line spacing for multiline text (when line breaks are included '\n') + // Free codepoints, atlas has already been generated free(codepointsNoDups); diff --git a/examples/text/text_font_loading.c b/examples/text/text_font_loading.c index 2ded64f09..a3ff988e9 100644 --- a/examples/text/text_font_loading.c +++ b/examples/text/text_font_loading.c @@ -47,6 +47,8 @@ int main(void) // NOTE: We define a font base size of 32 pixels tall and up-to 250 characters Font fontTtf = LoadFontEx("resources/pixantiqua.ttf", 32, 0, 250); + SetTextLineSpacing(48); // Set line spacing for multiline text (when line breaks are included '\n') + bool useTtf = false; SetTargetFPS(60); // Set our game to run at 60 frames-per-second From 0f447f1fb6aeed5e3e807d123978b7feca240d1a Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 12:05:34 +0200 Subject: [PATCH 0598/1710] REVIEWED: Data size type consistency between functions #3168 --- src/raudio.c | 22 ++++++++++---------- src/raylib.h | 8 ++++---- src/rmodels.c | 54 +++++++++++++++++++++++++------------------------ src/rtext.c | 6 +++--- src/rtextures.c | 10 ++++----- src/utils.c | 32 ++++++++++++++--------------- 6 files changed, 67 insertions(+), 65 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 9d1683a1f..4627cdf5c 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -413,8 +413,8 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr static bool IsFileExtension(const char *fileName, const char *ext); // Check file extension static const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes the dot: .png) -static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) -static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write) +static unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read) +static bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write) static bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated #endif @@ -732,11 +732,11 @@ Wave LoadWave(const char *fileName) Wave wave = { 0 }; // Loading file to memory - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); // Loading wave from memory data - if (fileData != NULL) wave = LoadWaveFromMemory(GetFileExtension(fileName), fileData, fileSize); + if (fileData != NULL) wave = LoadWaveFromMemory(GetFileExtension(fileName), fileData, dataSize); RL_FREE(fileData); @@ -2619,10 +2619,10 @@ static const char *GetFileExtension(const char *fileName) } // Load data from file into a buffer -static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) +static unsigned char *LoadFileData(const char *fileName, int *dataSize) { unsigned char *data = NULL; - *bytesRead = 0; + *dataSize = 0; if (fileName != NULL) { @@ -2642,7 +2642,7 @@ static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); - *bytesRead = count; + *dataSize = count; if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); @@ -2659,7 +2659,7 @@ static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead } // Save data to file from buffer -static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) +static bool SaveFileData(const char *fileName, void *data, int dataSize) { if (fileName != NULL) { @@ -2667,10 +2667,10 @@ static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToW if (file != NULL) { - unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), bytesToWrite, file); + unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), dataSize, file); if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); - else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); + else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); fclose(file); diff --git a/src/raylib.h b/src/raylib.h index 9043a11e9..084fb4dc0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1075,10 +1075,10 @@ RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver // Files management functions -RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) +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 bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success -RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success +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 @@ -1506,7 +1506,7 @@ RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture 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, unsigned int *animCount); // Load model animations from file +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 UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data diff --git a/src/rmodels.c b/src/rmodels.c index ebb454e4a..66de97273 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -151,18 +151,18 @@ 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, unsigned int *animCount); // Load IQM animation 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, unsigned int *animCount); // Load GLTF animation 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, unsigned int *animCount); // Load M3D animation 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 @@ -1979,7 +1979,7 @@ void SetModelMeshMaterial(Model *model, int meshId, int materialId) } // Load model animations from file -ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount) +ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount) { ModelAnimation *animations = NULL; @@ -4081,8 +4081,8 @@ static Model LoadIQM(const char *fileName) #define MESH_NAME_LENGTH 32 // Mesh name string length #define MATERIAL_NAME_LENGTH 32 // Material name string length - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); unsigned char *fileDataPtr = fileData; // IQM file structs @@ -4090,7 +4090,7 @@ static Model LoadIQM(const char *fileName) typedef struct IQMHeader { char magic[16]; unsigned int version; - unsigned int filesize; + unsigned int dataSize; unsigned int flags; unsigned int num_text, ofs_text; unsigned int num_meshes, ofs_meshes; @@ -4443,19 +4443,19 @@ static Model LoadIQM(const char *fileName) } // Load IQM animation data -static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int *animCount) +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 - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); unsigned char *fileDataPtr = fileData; typedef struct IQMHeader { char magic[16]; unsigned int version; - unsigned int filesize; + unsigned int dataSize; unsigned int flags; unsigned int num_text, ofs_text; unsigned int num_meshes, ofs_meshes; @@ -4815,7 +4815,7 @@ static Model LoadGLTF(const char *fileName) Model model = { 0 }; // glTF file loading - unsigned int dataSize = 0; + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData == NULL) return model; @@ -5295,10 +5295,10 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo #define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) -static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned int *animCount) +static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount) { // glTF file loading - unsigned int dataSize = 0; + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); ModelAnimation *animations = NULL; @@ -5466,11 +5466,11 @@ static Model LoadVOX(const char *fileName) int nbvertices = 0; int meshescount = 0; - unsigned int fileSize = 0; - unsigned char *fileData = NULL; - + // Read vox file into buffer - fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); + if (fileData == 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName); @@ -5479,7 +5479,7 @@ static Model LoadVOX(const char *fileName) // Read and build voxarray description VoxArray3D voxarray = { 0 }; - int ret = Vox_LoadFromMemory(fileData, fileSize, &voxarray); + int ret = Vox_LoadFromMemory(fileData, dataSize, &voxarray); if (ret != VOX_SUCCESS) { @@ -5574,9 +5574,10 @@ static Model LoadM3D(const char *fileName) m3d_t *m3d = NULL; m3dp_t *prop = NULL; - unsigned int bytesRead = 0; - unsigned char *fileData = LoadFileData(fileName, &bytesRead); int i, j, k, l, n, mi = -2, vcolor = 0; + + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData != NULL) { @@ -5878,16 +5879,17 @@ static Model LoadM3D(const char *fileName) #define M3D_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) // Load M3D animation data -static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount) +static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount) { - m3d_t *m3d = NULL; - unsigned int bytesRead = 0; - unsigned char *fileData = LoadFileData(fileName, &bytesRead); 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); diff --git a/src/rtext.c b/src/rtext.c index 9017c8b26..facf24c24 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -363,13 +363,13 @@ Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepoi Font font = { 0 }; // Loading file to memory - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData != NULL) { // Loading font from memory data - font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, codepoints, codepointCount); + font = LoadFontFromMemory(GetFileExtension(fileName), fileData, dataSize, fontSize, codepoints, codepointCount); UnloadFileData(fileData); } diff --git a/src/rtextures.c b/src/rtextures.c index a97cb0b37..566371112 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -271,11 +271,11 @@ Image LoadImage(const char *fileName) #endif // Loading file to memory - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); // Loading image from memory data - if (fileData != NULL) image = LoadImageFromMemory(GetFileExtension(fileName), fileData, fileSize); + if (fileData != NULL) image = LoadImageFromMemory(GetFileExtension(fileName), fileData, dataSize); RL_FREE(fileData); @@ -287,7 +287,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int { Image image = { 0 }; - unsigned int dataSize = 0; + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData != NULL) @@ -323,7 +323,7 @@ Image LoadImageAnim(const char *fileName, int *frames) #if defined(SUPPORT_FILEFORMAT_GIF) if (IsFileExtension(fileName, ".gif")) { - unsigned int dataSize = 0; + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData != NULL) diff --git a/src/utils.c b/src/utils.c index ac57a2157..27747b757 100644 --- a/src/utils.c +++ b/src/utils.c @@ -180,16 +180,16 @@ void MemFree(void *ptr) } // Load data from file into a buffer -unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) +unsigned char *LoadFileData(const char *fileName, int *dataSize) { unsigned char *data = NULL; - *bytesRead = 0; + *dataSize = 0; if (fileName != NULL) { if (loadFileData) { - data = loadFileData(fileName, bytesRead); + data = loadFileData(fileName, dataSize); return data; } #if defined(SUPPORT_STANDARD_FILEIO) @@ -211,7 +211,7 @@ unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) { // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); - *bytesRead = count; + *dataSize = count; if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); @@ -239,7 +239,7 @@ void UnloadFileData(unsigned char *data) } // Save data to file from buffer -bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) +bool SaveFileData(const char *fileName, void *data, int dataSize) { bool success = false; @@ -247,17 +247,17 @@ bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) { if (saveFileData) { - return saveFileData(fileName, data, bytesToWrite); + return saveFileData(fileName, data, dataSize); } #if defined(SUPPORT_STANDARD_FILEIO) FILE *file = fopen(fileName, "wb"); if (file != NULL) { - unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), bytesToWrite, file); + unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), dataSize, file); if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); - else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", 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); @@ -274,7 +274,7 @@ bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) } // Export data to code (.h), returns true on success -bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName) +bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName) { bool success = false; @@ -284,7 +284,7 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * // 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(size*6 + 2000, sizeof(char)); + char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char)); int byteCount = 0; byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n"); @@ -303,11 +303,11 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * strcpy(varFileName, GetFileNameWithoutExt(fileName)); for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } - byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, size); + 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 (unsigned int i = 0; i < size - 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[size - 1]); + 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); @@ -465,12 +465,12 @@ FILE *android_fopen(const char *fileName, const char *mode) // Module specific Functions Definition //---------------------------------------------------------------------------------- #if defined(PLATFORM_ANDROID) -static int android_read(void *cookie, char *buf, int size) +static int android_read(void *cookie, char *data, int dataSize) { - return AAsset_read((AAsset *)cookie, buf, size); + return AAsset_read((AAsset *)cookie, data, dataSize); } -static int android_write(void *cookie, const char *buf, int size) +static int android_write(void *cookie, const char *data, int dataSize) { TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK"); From 6e18d96e7ad45e63c15b5e1f6f2ca52d2e9fab4a Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 12:54:36 +0200 Subject: [PATCH 0599/1710] Some tweaks --- src/raylib.h | 2 +- src/rcore.c | 2 +- src/rmodels.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 084fb4dc0..6f67d839e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1509,7 +1509,7 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); 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 UnloadModelAnimation(ModelAnimation anim); // Unload animation data -RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data +RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match // Collision detection functions diff --git a/src/rcore.c b/src/rcore.c index 2c53eb5e6..912f85c0d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -601,7 +601,7 @@ static const char *autoEventTypeName[] = { "ACTION_SETTARGETFPS" }; -// Automation Event (24 bytes) +// Automation event (24 bytes) typedef struct AutomationEvent { unsigned int frame; // Event frame unsigned int type; // Event type (AutomationEventType) diff --git a/src/rmodels.c b/src/rmodels.c index 66de97273..5a6d98ac6 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2099,9 +2099,9 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) } // Unload animation array data -void UnloadModelAnimations(ModelAnimation *animations, unsigned int count) +void UnloadModelAnimations(ModelAnimation *animations, int animCount) { - for (unsigned int i = 0; i < count; i++) UnloadModelAnimation(animations[i]); + for (int i = 0; i < animCount; i++) UnloadModelAnimation(animations[i]); RL_FREE(animations); } From 75e5cd86d7d1c4c6e0f266f3ae68f3af34fa78ac Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 12:58:47 +0200 Subject: [PATCH 0600/1710] Use internal default allocators, instead of user-exposed ones --- src/rmodels.c | 4 ++-- src/rtext.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 5a6d98ac6..9616f0ceb 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1904,7 +1904,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount) 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 = MemAlloc(count*sizeof(Material)); + materials = RL_MALLOC(count*sizeof(Material)); ProcessMaterialsOBJ(materials, mats, count); tinyobj_materials_free(mats, count); @@ -4704,7 +4704,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat if (result == cgltf_result_success) { image = LoadImageFromMemory(".png", (unsigned char *)data, outSize); - MemFree(data); + RL_FREE(data); } } } diff --git a/src/rtext.c b/src/rtext.c index facf24c24..5ca97d06d 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -975,7 +975,7 @@ bool ExportFontAsCode(Font font, const char *fileName) byteCount += sprintf(txtData + byteCount, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", fileNamePascal, TextToUpper(fileNamePascal)); for (int i = 0; i < compDataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), compData[i]); byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", compData[compDataSize - 1]); - MemFree(compData); + RL_FREE(compData); #else // Save font image data (uncompressed) byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n"); From c03ab03627232047c63b11c27567ef3d0a74ca3c Mon Sep 17 00:00:00 2001 From: bXi Date: Sat, 2 Sep 2023 07:00:18 -0400 Subject: [PATCH 0601/1710] Added rudimentary SVG support. (#2738) * Added rudimentary SVG support. Added 2 functions ImageLoadSvg and ImageLoadSvgWithSize. * Added an example on how to use ImageLoadSvgWithSize and adjusted Makefiles accordingly. * Added actual correct example file. * Reviewed the code to keep the raylib coding conventions in mind. Moved the LoadImageSvg() code into LoadImage() guarded by SUPPORT_FILEFORMAT_SVG. Renamed LoadImageSvgWithSize() to LoadImageSvg(). Added a LoadImageSvgFromString() function to parse the loaded SVG into an actual image. This does the bulk of the work. * Fixed typo. --------- Co-authored-by: Ray --- examples/Makefile | 3 +- examples/Makefile.Web | 3 +- examples/textures/resources/test.svg | 1 + examples/textures/textures_svg_loading.c | 72 + examples/textures/textures_svg_loading.png | Bin 0 -> 13850 bytes src/config.h | 30 +- src/external/nanosvg.h | 3053 ++++++++++++++++++++ src/external/nanosvgrast.h | 1458 ++++++++++ src/raylib.h | 2 + src/rtextures.c | 95 + 10 files changed, 4700 insertions(+), 17 deletions(-) create mode 100644 examples/textures/resources/test.svg create mode 100644 examples/textures/textures_svg_loading.c create mode 100644 examples/textures/textures_svg_loading.png create mode 100644 src/external/nanosvg.h create mode 100644 src/external/nanosvgrast.h diff --git a/examples/Makefile b/examples/Makefile index 7028a9542..4ba4f1720 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -475,7 +475,8 @@ TEXTURES = \ textures/textures_draw_tiled \ textures/textures_polygon \ textures/textures_gif_player \ - textures/textures_fog_of_war + textures/textures_fog_of_war \ + textures/textures_svg_loading TEXT = \ text/text_raylib_fonts \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 4574e928e..ccb4550f9 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -450,7 +450,8 @@ TEXTURES = \ textures/textures_draw_tiled \ textures/textures_polygon \ textures/textures_gif_player \ - textures/textures_fog_of_war + textures/textures_fog_of_war \ + textures/textures_svg_loading TEXT = \ text/text_raylib_fonts \ diff --git a/examples/textures/resources/test.svg b/examples/textures/resources/test.svg new file mode 100644 index 000000000..3d9bbb113 --- /dev/null +++ b/examples/textures/resources/test.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/textures/textures_svg_loading.c b/examples/textures/textures_svg_loading.c new file mode 100644 index 000000000..434ec7602 --- /dev/null +++ b/examples/textures/textures_svg_loading.c @@ -0,0 +1,72 @@ +/******************************************************************************************* +* +* raylib [textures] example - SVG loading and texture creation +* +* NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) +* +* 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 Dennis Meinen (@bixxy#4258 on Discord) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - svg loading"); + + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + + Image image = LoadImageSvg("resources/test.svg", 400, 350); // Loaded in CPU memory (RAM) + Texture2D texture = LoadTextureFromImage(image); // Image converted to texture, GPU memory (VRAM) + UnloadImage(image); // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM + + 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); + + DrawTexture(texture, screenWidth/2 - texture.width/2, screenHeight/2 - texture.height/2, WHITE); + + //Red border to illustrate how the SVG is centered within the specified dimensions + DrawRectangleLines((screenWidth / 2 - texture.width / 2) - 1, (screenHeight / 2 - texture.height / 2) - 1, texture.width + 2, texture.height + 2, RED); + + DrawText("this IS a texture loaded from an SVG file!", 300, 410, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(texture); // Texture unloading + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/textures/textures_svg_loading.png b/examples/textures/textures_svg_loading.png new file mode 100644 index 0000000000000000000000000000000000000000..fa98605fa8508a068537defae8a8fc960a6a6c08 GIT binary patch literal 13850 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, image->height); + // Use... + for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { + for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { + for (int i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } + } + } + // Delete + nsvgDelete(image); +*/ + +enum NSVGpaintType { + NSVG_PAINT_NONE = 0, + NSVG_PAINT_COLOR = 1, + NSVG_PAINT_LINEAR_GRADIENT = 2, + NSVG_PAINT_RADIAL_GRADIENT = 3 +}; + +enum NSVGspreadType { + NSVG_SPREAD_PAD = 0, + NSVG_SPREAD_REFLECT = 1, + NSVG_SPREAD_REPEAT = 2 +}; + +enum NSVGlineJoin { + NSVG_JOIN_MITER = 0, + NSVG_JOIN_ROUND = 1, + NSVG_JOIN_BEVEL = 2 +}; + +enum NSVGlineCap { + NSVG_CAP_BUTT = 0, + NSVG_CAP_ROUND = 1, + NSVG_CAP_SQUARE = 2 +}; + +enum NSVGfillRule { + NSVG_FILLRULE_NONZERO = 0, + NSVG_FILLRULE_EVENODD = 1 +}; + +enum NSVGflags { + NSVG_FLAGS_VISIBLE = 0x01 +}; + +typedef struct NSVGgradientStop { + unsigned int color; + float offset; +} NSVGgradientStop; + +typedef struct NSVGgradient { + float xform[6]; + char spread; + float fx, fy; + int nstops; + NSVGgradientStop stops[1]; +} NSVGgradient; + +typedef struct NSVGpaint { + char type; + union { + unsigned int color; + NSVGgradient* gradient; + }; +} NSVGpaint; + +typedef struct NSVGpath +{ + float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... + int npts; // Total number of bezier points. + char closed; // Flag indicating if shapes should be treated as closed. + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + struct NSVGpath* next; // Pointer to next path, or NULL if last element. +} NSVGpath; + +typedef struct NSVGshape +{ + char id[64]; // Optional 'id' attr of the shape or its group + NSVGpaint fill; // Fill paint + NSVGpaint stroke; // Stroke paint + float opacity; // Opacity of the shape. + float strokeWidth; // Stroke width (scaled). + float strokeDashOffset; // Stroke dash offset (scaled). + float strokeDashArray[8]; // Stroke dash array (scaled). + char strokeDashCount; // Number of dash values in dash array. + char strokeLineJoin; // Stroke join type. + char strokeLineCap; // Stroke cap type. + float miterLimit; // Miter limit + char fillRule; // Fill rule, see NSVGfillRule. + unsigned char flags; // Logical or of NSVG_FLAGS_* flags + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + NSVGpath* paths; // Linked list of paths in the image. + struct NSVGshape* next; // Pointer to next shape, or NULL if last element. +} NSVGshape; + +typedef struct NSVGimage +{ + float width; // Width of the image. + float height; // Height of the image. + NSVGshape* shapes; // Linked list of shapes in the image. +} NSVGimage; + +// Parses SVG file from a file, returns SVG image as paths. +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); + +// Parses SVG file from a null terminated string, returns SVG image as paths. +// Important note: changes the string. +NSVGimage* nsvgParse(char* input, const char* units, float dpi); + +// Duplicates a path. +NSVGpath* nsvgDuplicatePath(NSVGpath* p); + +// Deletes an image. +void nsvgDelete(NSVGimage* image); + +#ifndef NANOSVG_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#ifdef NANOSVG_IMPLEMENTATION + +#include +#include +#include +#include + +#define NSVG_PI (3.14159265358979323846264338327f) +#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. + +#define NSVG_ALIGN_MIN 0 +#define NSVG_ALIGN_MID 1 +#define NSVG_ALIGN_MAX 2 +#define NSVG_ALIGN_NONE 0 +#define NSVG_ALIGN_MEET 1 +#define NSVG_ALIGN_SLICE 2 + +#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) +#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) + +#ifdef _MSC_VER + #pragma warning (disable: 4996) // Switch off security warnings + #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings + #ifdef __cplusplus + #define NSVG_INLINE inline + #else + #define NSVG_INLINE + #endif +#else + #define NSVG_INLINE inline +#endif + + +static int nsvg__isspace(char c) +{ + return strchr(" \t\n\v\f\r", c) != 0; +} + +static int nsvg__isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } +static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } + + +// Simple XML parser + +#define NSVG_XML_TAG 1 +#define NSVG_XML_CONTENT 2 +#define NSVG_XML_MAX_ATTRIBS 256 + +static void nsvg__parseContent(char* s, + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + // Trim start white spaces + while (*s && nsvg__isspace(*s)) s++; + if (!*s) return; + + if (contentCb) + (*contentCb)(ud, s); +} + +static void nsvg__parseElement(char* s, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void* ud) +{ + const char* attr[NSVG_XML_MAX_ATTRIBS]; + int nattr = 0; + char* name; + int start = 0; + int end = 0; + char quote; + + // Skip white space after the '<' + while (*s && nsvg__isspace(*s)) s++; + + // Check if the tag is end tag + if (*s == '/') { + s++; + end = 1; + } else { + start = 1; + } + + // Skip comments, data and preprocessor stuff. + if (!*s || *s == '?' || *s == '!') + return; + + // Get tag name + name = s; + while (*s && !nsvg__isspace(*s)) s++; + if (*s) { *s++ = '\0'; } + + // Get attribs + while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { + char* name = NULL; + char* value = NULL; + + // Skip white space before the attrib name + while (*s && nsvg__isspace(*s)) s++; + if (!*s) break; + if (*s == '/') { + end = 1; + break; + } + name = s; + // Find end of the attrib name. + while (*s && !nsvg__isspace(*s) && *s != '=') s++; + if (*s) { *s++ = '\0'; } + // Skip until the beginning of the value. + while (*s && *s != '\"' && *s != '\'') s++; + if (!*s) break; + quote = *s; + s++; + // Store value and find the end of it. + value = s; + while (*s && *s != quote) s++; + if (*s) { *s++ = '\0'; } + + // Store only well formed attributes + if (name && value) { + attr[nattr++] = name; + attr[nattr++] = value; + } + } + + // List terminator + attr[nattr++] = 0; + attr[nattr++] = 0; + + // Call callbacks. + if (start && startelCb) + (*startelCb)(ud, name, attr); + if (end && endelCb) + (*endelCb)(ud, name); +} + +int nsvg__parseXML(char* input, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + char* s = input; + char* mark = s; + int state = NSVG_XML_CONTENT; + while (*s) { + if (*s == '<' && state == NSVG_XML_CONTENT) { + // Start of a tag + *s++ = '\0'; + nsvg__parseContent(mark, contentCb, ud); + mark = s; + state = NSVG_XML_TAG; + } else if (*s == '>' && state == NSVG_XML_TAG) { + // Start of a content or new tag. + *s++ = '\0'; + nsvg__parseElement(mark, startelCb, endelCb, ud); + mark = s; + state = NSVG_XML_CONTENT; + } else { + s++; + } + } + + return 1; +} + + +/* Simple SVG parser. */ + +#define NSVG_MAX_ATTR 128 + +enum NSVGgradientUnits { + NSVG_USER_SPACE = 0, + NSVG_OBJECT_SPACE = 1 +}; + +#define NSVG_MAX_DASHES 8 + +enum NSVGunits { + NSVG_UNITS_USER, + NSVG_UNITS_PX, + NSVG_UNITS_PT, + NSVG_UNITS_PC, + NSVG_UNITS_MM, + NSVG_UNITS_CM, + NSVG_UNITS_IN, + NSVG_UNITS_PERCENT, + NSVG_UNITS_EM, + NSVG_UNITS_EX +}; + +typedef struct NSVGcoordinate { + float value; + int units; +} NSVGcoordinate; + +typedef struct NSVGlinearData { + NSVGcoordinate x1, y1, x2, y2; +} NSVGlinearData; + +typedef struct NSVGradialData { + NSVGcoordinate cx, cy, r, fx, fy; +} NSVGradialData; + +typedef struct NSVGgradientData +{ + char id[64]; + char ref[64]; + char type; + union { + NSVGlinearData linear; + NSVGradialData radial; + }; + char spread; + char units; + float xform[6]; + int nstops; + NSVGgradientStop* stops; + struct NSVGgradientData* next; +} NSVGgradientData; + +typedef struct NSVGattrib +{ + char id[64]; + float xform[6]; + unsigned int fillColor; + unsigned int strokeColor; + float opacity; + float fillOpacity; + float strokeOpacity; + char fillGradient[64]; + char strokeGradient[64]; + float strokeWidth; + float strokeDashOffset; + float strokeDashArray[NSVG_MAX_DASHES]; + int strokeDashCount; + char strokeLineJoin; + char strokeLineCap; + float miterLimit; + char fillRule; + float fontSize; + unsigned int stopColor; + float stopOpacity; + float stopOffset; + char hasFill; + char hasStroke; + char visible; +} NSVGattrib; + +typedef struct NSVGparser +{ + NSVGattrib attr[NSVG_MAX_ATTR]; + int attrHead; + float* pts; + int npts; + int cpts; + NSVGpath* plist; + NSVGimage* image; + NSVGgradientData* gradients; + NSVGshape* shapesTail; + float viewMinx, viewMiny, viewWidth, viewHeight; + int alignX, alignY, alignType; + float dpi; + char pathFlag; + char defsFlag; +} NSVGparser; + +static void nsvg__xformIdentity(float* t) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetTranslation(float* t, float tx, float ty) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = tx; t[5] = ty; +} + +static void nsvg__xformSetScale(float* t, float sx, float sy) +{ + t[0] = sx; t[1] = 0.0f; + t[2] = 0.0f; t[3] = sy; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewX(float* t, float a) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = tanf(a); t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewY(float* t, float a) +{ + t[0] = 1.0f; t[1] = tanf(a); + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetRotation(float* t, float a) +{ + float cs = cosf(a), sn = sinf(a); + t[0] = cs; t[1] = sn; + t[2] = -sn; t[3] = cs; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformMultiply(float* t, float* s) +{ + float t0 = t[0] * s[0] + t[1] * s[2]; + float t2 = t[2] * s[0] + t[3] * s[2]; + float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; + t[1] = t[0] * s[1] + t[1] * s[3]; + t[3] = t[2] * s[1] + t[3] * s[3]; + t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; + t[0] = t0; + t[2] = t2; + t[4] = t4; +} + +static void nsvg__xformInverse(float* inv, float* t) +{ + double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; + if (det > -1e-6 && det < 1e-6) { + nsvg__xformIdentity(t); + return; + } + invdet = 1.0 / det; + inv[0] = (float)(t[3] * invdet); + inv[2] = (float)(-t[2] * invdet); + inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); + inv[1] = (float)(-t[1] * invdet); + inv[3] = (float)(t[0] * invdet); + inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); +} + +static void nsvg__xformPremultiply(float* t, float* s) +{ + float s2[6]; + memcpy(s2, s, sizeof(float)*6); + nsvg__xformMultiply(s2, t); + memcpy(t, s2, sizeof(float)*6); +} + +static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2] + t[4]; + *dy = x*t[1] + y*t[3] + t[5]; +} + +static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2]; + *dy = x*t[1] + y*t[3]; +} + +#define NSVG_EPSILON (1e-12) + +static int nsvg__ptInBounds(float* pt, float* bounds) +{ + return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; +} + + +static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) +{ + double it = 1.0-t; + return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; +} + +static void nsvg__curveBounds(float* bounds, float* curve) +{ + int i, j, count; + double roots[2], a, b, c, b2ac, t, v; + float* v0 = &curve[0]; + float* v1 = &curve[2]; + float* v2 = &curve[4]; + float* v3 = &curve[6]; + + // Start the bounding box by end points + bounds[0] = nsvg__minf(v0[0], v3[0]); + bounds[1] = nsvg__minf(v0[1], v3[1]); + bounds[2] = nsvg__maxf(v0[0], v3[0]); + bounds[3] = nsvg__maxf(v0[1], v3[1]); + + // Bezier curve fits inside the convex hull of it's control points. + // If control points are inside the bounds, we're done. + if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) + return; + + // Add bezier curve inflection points in X and Y. + for (i = 0; i < 2; i++) { + a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; + b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; + c = 3.0 * v1[i] - 3.0 * v0[i]; + count = 0; + if (fabs(a) < NSVG_EPSILON) { + if (fabs(b) > NSVG_EPSILON) { + t = -c / b; + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } else { + b2ac = b*b - 4.0*c*a; + if (b2ac > NSVG_EPSILON) { + t = (-b + sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + t = (-b - sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } + for (j = 0; j < count; j++) { + v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); + bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); + bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); + } + } +} + +static NSVGparser* nsvg__createParser(void) +{ + NSVGparser* p; + p = (NSVGparser*)malloc(sizeof(NSVGparser)); + if (p == NULL) goto error; + memset(p, 0, sizeof(NSVGparser)); + + p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); + if (p->image == NULL) goto error; + memset(p->image, 0, sizeof(NSVGimage)); + + // Init style + nsvg__xformIdentity(p->attr[0].xform); + memset(p->attr[0].id, 0, sizeof p->attr[0].id); + p->attr[0].fillColor = NSVG_RGB(0,0,0); + p->attr[0].strokeColor = NSVG_RGB(0,0,0); + p->attr[0].opacity = 1; + p->attr[0].fillOpacity = 1; + p->attr[0].strokeOpacity = 1; + p->attr[0].stopOpacity = 1; + p->attr[0].strokeWidth = 1; + p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; + p->attr[0].strokeLineCap = NSVG_CAP_BUTT; + p->attr[0].miterLimit = 4; + p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; + p->attr[0].hasFill = 1; + p->attr[0].visible = 1; + + return p; + +error: + if (p) { + if (p->image) free(p->image); + free(p); + } + return NULL; +} + +static void nsvg__deletePaths(NSVGpath* path) +{ + while (path) { + NSVGpath *next = path->next; + if (path->pts != NULL) + free(path->pts); + free(path); + path = next; + } +} + +static void nsvg__deletePaint(NSVGpaint* paint) +{ + if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) + free(paint->gradient); +} + +static void nsvg__deleteGradientData(NSVGgradientData* grad) +{ + NSVGgradientData* next; + while (grad != NULL) { + next = grad->next; + free(grad->stops); + free(grad); + grad = next; + } +} + +static void nsvg__deleteParser(NSVGparser* p) +{ + if (p != NULL) { + nsvg__deletePaths(p->plist); + nsvg__deleteGradientData(p->gradients); + nsvgDelete(p->image); + free(p->pts); + free(p); + } +} + +static void nsvg__resetPath(NSVGparser* p) +{ + p->npts = 0; +} + +static void nsvg__addPoint(NSVGparser* p, float x, float y) +{ + if (p->npts+1 > p->cpts) { + p->cpts = p->cpts ? p->cpts*2 : 8; + p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); + if (!p->pts) return; + } + p->pts[p->npts*2+0] = x; + p->pts[p->npts*2+1] = y; + p->npts++; +} + +static void nsvg__moveTo(NSVGparser* p, float x, float y) +{ + if (p->npts > 0) { + p->pts[(p->npts-1)*2+0] = x; + p->pts[(p->npts-1)*2+1] = y; + } else { + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__lineTo(NSVGparser* p, float x, float y) +{ + float px,py, dx,dy; + if (p->npts > 0) { + px = p->pts[(p->npts-1)*2+0]; + py = p->pts[(p->npts-1)*2+1]; + dx = x - px; + dy = y - py; + nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); + nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) +{ + if (p->npts > 0) { + nsvg__addPoint(p, cpx1, cpy1); + nsvg__addPoint(p, cpx2, cpy2); + nsvg__addPoint(p, x, y); + } +} + +static NSVGattrib* nsvg__getAttr(NSVGparser* p) +{ + return &p->attr[p->attrHead]; +} + +static void nsvg__pushAttr(NSVGparser* p) +{ + if (p->attrHead < NSVG_MAX_ATTR-1) { + p->attrHead++; + memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); + } +} + +static void nsvg__popAttr(NSVGparser* p) +{ + if (p->attrHead > 0) + p->attrHead--; +} + +static float nsvg__actualOrigX(NSVGparser* p) +{ + return p->viewMinx; +} + +static float nsvg__actualOrigY(NSVGparser* p) +{ + return p->viewMiny; +} + +static float nsvg__actualWidth(NSVGparser* p) +{ + return p->viewWidth; +} + +static float nsvg__actualHeight(NSVGparser* p) +{ + return p->viewHeight; +} + +static float nsvg__actualLength(NSVGparser* p) +{ + float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); + return sqrtf(w*w + h*h) / sqrtf(2.0f); +} + +static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) +{ + NSVGattrib* attr = nsvg__getAttr(p); + switch (c.units) { + case NSVG_UNITS_USER: return c.value; + case NSVG_UNITS_PX: return c.value; + case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; + case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; + case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; + case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; + case NSVG_UNITS_IN: return c.value * p->dpi; + case NSVG_UNITS_EM: return c.value * attr->fontSize; + case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. + case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; + default: return c.value; + } + return c.value; +} + +static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) +{ + NSVGgradientData* grad = p->gradients; + if (id == NULL || *id == '\0') + return NULL; + while (grad != NULL) { + if (strcmp(grad->id, id) == 0) + return grad; + grad = grad->next; + } + return NULL; +} + +static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGgradientData* data = NULL; + NSVGgradientData* ref = NULL; + NSVGgradientStop* stops = NULL; + NSVGgradient* grad; + float ox, oy, sw, sh, sl; + int nstops = 0; + int refIter; + + data = nsvg__findGradientData(p, id); + if (data == NULL) return NULL; + + // TODO: use ref to fill in all unset values too. + ref = data; + refIter = 0; + while (ref != NULL) { + NSVGgradientData* nextRef = NULL; + if (stops == NULL && ref->stops != NULL) { + stops = ref->stops; + nstops = ref->nstops; + break; + } + nextRef = nsvg__findGradientData(p, ref->ref); + if (nextRef == ref) break; // prevent infite loops on malformed data + ref = nextRef; + refIter++; + if (refIter > 32) break; // prevent infite loops on malformed data + } + if (stops == NULL) return NULL; + + grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); + if (grad == NULL) return NULL; + + // The shape width and height. + if (data->units == NSVG_OBJECT_SPACE) { + ox = localBounds[0]; + oy = localBounds[1]; + sw = localBounds[2] - localBounds[0]; + sh = localBounds[3] - localBounds[1]; + } else { + ox = nsvg__actualOrigX(p); + oy = nsvg__actualOrigY(p); + sw = nsvg__actualWidth(p); + sh = nsvg__actualHeight(p); + } + sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); + + if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { + float x1, y1, x2, y2, dx, dy; + x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); + y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); + x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); + y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); + // Calculate transform aligned to the line + dx = x2 - x1; + dy = y2 - y1; + grad->xform[0] = dy; grad->xform[1] = -dx; + grad->xform[2] = dx; grad->xform[3] = dy; + grad->xform[4] = x1; grad->xform[5] = y1; + } else { + float cx, cy, fx, fy, r; + cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); + cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); + fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); + fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); + r = nsvg__convertToPixels(p, data->radial.r, 0, sl); + // Calculate transform aligned to the circle + grad->xform[0] = r; grad->xform[1] = 0; + grad->xform[2] = 0; grad->xform[3] = r; + grad->xform[4] = cx; grad->xform[5] = cy; + grad->fx = fx / r; + grad->fy = fy / r; + } + + nsvg__xformMultiply(grad->xform, data->xform); + nsvg__xformMultiply(grad->xform, attr->xform); + + grad->spread = data->spread; + memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); + grad->nstops = nstops; + + *paintType = data->type; + + return grad; +} + +static float nsvg__getAverageScale(float* t) +{ + float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); + float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); + return (sx + sy) * 0.5f; +} + +static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) +{ + NSVGpath* path; + float curve[4*2], curveBounds[4]; + int i, first = 1; + for (path = shape->paths; path != NULL; path = path->next) { + nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); + for (i = 0; i < path->npts-1; i += 3) { + nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); + nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); + nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); + nsvg__curveBounds(curveBounds, curve); + if (first) { + bounds[0] = curveBounds[0]; + bounds[1] = curveBounds[1]; + bounds[2] = curveBounds[2]; + bounds[3] = curveBounds[3]; + first = 0; + } else { + bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); + bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); + bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); + bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); + } + curve[0] = curve[6]; + curve[1] = curve[7]; + } + } +} + +static void nsvg__addShape(NSVGparser* p) +{ + NSVGattrib* attr = nsvg__getAttr(p); + float scale = 1.0f; + NSVGshape* shape; + NSVGpath* path; + int i; + + if (p->plist == NULL) + return; + + shape = (NSVGshape*)malloc(sizeof(NSVGshape)); + if (shape == NULL) goto error; + memset(shape, 0, sizeof(NSVGshape)); + + memcpy(shape->id, attr->id, sizeof shape->id); + scale = nsvg__getAverageScale(attr->xform); + shape->strokeWidth = attr->strokeWidth * scale; + shape->strokeDashOffset = attr->strokeDashOffset * scale; + shape->strokeDashCount = (char)attr->strokeDashCount; + for (i = 0; i < attr->strokeDashCount; i++) + shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; + shape->strokeLineJoin = attr->strokeLineJoin; + shape->strokeLineCap = attr->strokeLineCap; + shape->miterLimit = attr->miterLimit; + shape->fillRule = attr->fillRule; + shape->opacity = attr->opacity; + + shape->paths = p->plist; + p->plist = NULL; + + // Calculate shape bounds + shape->bounds[0] = shape->paths->bounds[0]; + shape->bounds[1] = shape->paths->bounds[1]; + shape->bounds[2] = shape->paths->bounds[2]; + shape->bounds[3] = shape->paths->bounds[3]; + for (path = shape->paths->next; path != NULL; path = path->next) { + shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); + shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); + shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); + shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); + } + + // Set fill + if (attr->hasFill == 0) { + shape->fill.type = NSVG_PAINT_NONE; + } else if (attr->hasFill == 1) { + shape->fill.type = NSVG_PAINT_COLOR; + shape->fill.color = attr->fillColor; + shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; + } else if (attr->hasFill == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type); + if (shape->fill.gradient == NULL) { + shape->fill.type = NSVG_PAINT_NONE; + } + } + + // Set stroke + if (attr->hasStroke == 0) { + shape->stroke.type = NSVG_PAINT_NONE; + } else if (attr->hasStroke == 1) { + shape->stroke.type = NSVG_PAINT_COLOR; + shape->stroke.color = attr->strokeColor; + shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; + } else if (attr->hasStroke == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type); + if (shape->stroke.gradient == NULL) + shape->stroke.type = NSVG_PAINT_NONE; + } + + // Set flags + shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); + + // Add to tail + if (p->image->shapes == NULL) + p->image->shapes = shape; + else + p->shapesTail->next = shape; + p->shapesTail = shape; + + return; + +error: + if (shape) free(shape); +} + +static void nsvg__addPath(NSVGparser* p, char closed) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGpath* path = NULL; + float bounds[4]; + float* curve; + int i; + + if (p->npts < 4) + return; + + if (closed) + nsvg__lineTo(p, p->pts[0], p->pts[1]); + + // Expect 1 + N*3 points (N = number of cubic bezier segments). + if ((p->npts % 3) != 1) + return; + + path = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (path == NULL) goto error; + memset(path, 0, sizeof(NSVGpath)); + + path->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (path->pts == NULL) goto error; + path->closed = closed; + path->npts = p->npts; + + // Transform path. + for (i = 0; i < p->npts; ++i) + nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); + + // Find bounds + for (i = 0; i < path->npts-1; i += 3) { + curve = &path->pts[i*2]; + nsvg__curveBounds(bounds, curve); + if (i == 0) { + path->bounds[0] = bounds[0]; + path->bounds[1] = bounds[1]; + path->bounds[2] = bounds[2]; + path->bounds[3] = bounds[3]; + } else { + path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); + path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); + path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); + path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); + } + } + + path->next = p->plist; + p->plist = path; + + return; + +error: + if (path != NULL) { + if (path->pts != NULL) free(path->pts); + free(path); + } +} + +// We roll our own string to float because the std library one uses locale and messes things up. +static double nsvg__atof(const char* s) +{ + char* cur = (char*)s; + char* end = NULL; + double res = 0.0, sign = 1.0; + long long intPart = 0, fracPart = 0; + char hasIntPart = 0, hasFracPart = 0; + + // Parse optional sign + if (*cur == '+') { + cur++; + } else if (*cur == '-') { + sign = -1; + cur++; + } + + // Parse integer part + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + intPart = strtoll(cur, &end, 10); + if (cur != end) { + res = (double)intPart; + hasIntPart = 1; + cur = end; + } + } + + // Parse fractional part. + if (*cur == '.') { + cur++; // Skip '.' + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + fracPart = strtoll(cur, &end, 10); + if (cur != end) { + res += (double)fracPart / pow(10.0, (double)(end - cur)); + hasFracPart = 1; + cur = end; + } + } + } + + // A valid number should have integer or fractional part. + if (!hasIntPart && !hasFracPart) + return 0.0; + + // Parse optional exponent + if (*cur == 'e' || *cur == 'E') { + long expPart = 0; + cur++; // skip 'E' + expPart = strtol(cur, &end, 10); // Parse digit sequence with sign + if (cur != end) { + res *= pow(10.0, (double)expPart); + } + } + + return res * sign; +} + + +static const char* nsvg__parseNumber(const char* s, char* it, const int size) +{ + const int last = size-1; + int i = 0; + + // sign + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + // integer part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + if (*s == '.') { + // decimal point + if (i < last) it[i++] = *s; + s++; + // fraction part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + // exponent + if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { + if (i < last) it[i++] = *s; + s++; + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + it[i] = '\0'; + + return s; +} + +static const char* nsvg__getNextPathItem(const char* s, char* it) +{ + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + if (!*s) return s; + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { + s = nsvg__parseNumber(s, it, 64); + } else { + // Parse command + it[0] = *s++; + it[1] = '\0'; + return s; + } + + return s; +} + +static unsigned int nsvg__parseColorHex(const char* str) +{ + unsigned int r=0, g=0, b=0; + if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex + return NSVG_RGB(r, g, b); + if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa + return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), .. + return NSVG_RGB(128, 128, 128); +} + +// Parse rgb color. The pointer 'str' must point at "rgb(" (4+ characters). +// This function returns gray (rgb(128, 128, 128) == '#808080') on parse errors +// for backwards compatibility. Note: other image viewers return black instead. + +static unsigned int nsvg__parseColorRGB(const char* str) +{ + int i; + unsigned int rgbi[3]; + float rgbf[3]; + // try decimal integers first + if (sscanf(str, "rgb(%u, %u, %u)", &rgbi[0], &rgbi[1], &rgbi[2]) != 3) { + // integers failed, try percent values (float, locale independent) + const char delimiter[3] = {',', ',', ')'}; + str += 4; // skip "rgb(" + for (i = 0; i < 3; i++) { + while (*str && (nsvg__isspace(*str))) str++; // skip leading spaces + if (*str == '+') str++; // skip '+' (don't allow '-') + if (!*str) break; + rgbf[i] = nsvg__atof(str); + + // Note 1: it would be great if nsvg__atof() returned how many + // bytes it consumed but it doesn't. We need to skip the number, + // the '%' character, spaces, and the delimiter ',' or ')'. + + // Note 2: The following code does not allow values like "33.%", + // i.e. a decimal point w/o fractional part, but this is consistent + // with other image viewers, e.g. firefox, chrome, eog, gimp. + + while (*str && nsvg__isdigit(*str)) str++; // skip integer part + if (*str == '.') { + str++; + if (!nsvg__isdigit(*str)) break; // error: no digit after '.' + while (*str && nsvg__isdigit(*str)) str++; // skip fractional part + } + if (*str == '%') str++; else break; + while (nsvg__isspace(*str)) str++; + if (*str == delimiter[i]) str++; + else break; + } + if (i == 3) { + rgbi[0] = roundf(rgbf[0] * 2.55f); + rgbi[1] = roundf(rgbf[1] * 2.55f); + rgbi[2] = roundf(rgbf[2] * 2.55f); + } else { + rgbi[0] = rgbi[1] = rgbi[2] = 128; + } + } + // clip values as the CSS spec requires + for (i = 0; i < 3; i++) { + if (rgbi[i] > 255) rgbi[i] = 255; + } + return NSVG_RGB(rgbi[0], rgbi[1], rgbi[2]); +} + +typedef struct NSVGNamedColor { + const char* name; + unsigned int color; +} NSVGNamedColor; + +NSVGNamedColor nsvg__colors[] = { + + { "red", NSVG_RGB(255, 0, 0) }, + { "green", NSVG_RGB( 0, 128, 0) }, + { "blue", NSVG_RGB( 0, 0, 255) }, + { "yellow", NSVG_RGB(255, 255, 0) }, + { "cyan", NSVG_RGB( 0, 255, 255) }, + { "magenta", NSVG_RGB(255, 0, 255) }, + { "black", NSVG_RGB( 0, 0, 0) }, + { "grey", NSVG_RGB(128, 128, 128) }, + { "gray", NSVG_RGB(128, 128, 128) }, + { "white", NSVG_RGB(255, 255, 255) }, + +#ifdef NANOSVG_ALL_COLOR_KEYWORDS + { "aliceblue", NSVG_RGB(240, 248, 255) }, + { "antiquewhite", NSVG_RGB(250, 235, 215) }, + { "aqua", NSVG_RGB( 0, 255, 255) }, + { "aquamarine", NSVG_RGB(127, 255, 212) }, + { "azure", NSVG_RGB(240, 255, 255) }, + { "beige", NSVG_RGB(245, 245, 220) }, + { "bisque", NSVG_RGB(255, 228, 196) }, + { "blanchedalmond", NSVG_RGB(255, 235, 205) }, + { "blueviolet", NSVG_RGB(138, 43, 226) }, + { "brown", NSVG_RGB(165, 42, 42) }, + { "burlywood", NSVG_RGB(222, 184, 135) }, + { "cadetblue", NSVG_RGB( 95, 158, 160) }, + { "chartreuse", NSVG_RGB(127, 255, 0) }, + { "chocolate", NSVG_RGB(210, 105, 30) }, + { "coral", NSVG_RGB(255, 127, 80) }, + { "cornflowerblue", NSVG_RGB(100, 149, 237) }, + { "cornsilk", NSVG_RGB(255, 248, 220) }, + { "crimson", NSVG_RGB(220, 20, 60) }, + { "darkblue", NSVG_RGB( 0, 0, 139) }, + { "darkcyan", NSVG_RGB( 0, 139, 139) }, + { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, + { "darkgray", NSVG_RGB(169, 169, 169) }, + { "darkgreen", NSVG_RGB( 0, 100, 0) }, + { "darkgrey", NSVG_RGB(169, 169, 169) }, + { "darkkhaki", NSVG_RGB(189, 183, 107) }, + { "darkmagenta", NSVG_RGB(139, 0, 139) }, + { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, + { "darkorange", NSVG_RGB(255, 140, 0) }, + { "darkorchid", NSVG_RGB(153, 50, 204) }, + { "darkred", NSVG_RGB(139, 0, 0) }, + { "darksalmon", NSVG_RGB(233, 150, 122) }, + { "darkseagreen", NSVG_RGB(143, 188, 143) }, + { "darkslateblue", NSVG_RGB( 72, 61, 139) }, + { "darkslategray", NSVG_RGB( 47, 79, 79) }, + { "darkslategrey", NSVG_RGB( 47, 79, 79) }, + { "darkturquoise", NSVG_RGB( 0, 206, 209) }, + { "darkviolet", NSVG_RGB(148, 0, 211) }, + { "deeppink", NSVG_RGB(255, 20, 147) }, + { "deepskyblue", NSVG_RGB( 0, 191, 255) }, + { "dimgray", NSVG_RGB(105, 105, 105) }, + { "dimgrey", NSVG_RGB(105, 105, 105) }, + { "dodgerblue", NSVG_RGB( 30, 144, 255) }, + { "firebrick", NSVG_RGB(178, 34, 34) }, + { "floralwhite", NSVG_RGB(255, 250, 240) }, + { "forestgreen", NSVG_RGB( 34, 139, 34) }, + { "fuchsia", NSVG_RGB(255, 0, 255) }, + { "gainsboro", NSVG_RGB(220, 220, 220) }, + { "ghostwhite", NSVG_RGB(248, 248, 255) }, + { "gold", NSVG_RGB(255, 215, 0) }, + { "goldenrod", NSVG_RGB(218, 165, 32) }, + { "greenyellow", NSVG_RGB(173, 255, 47) }, + { "honeydew", NSVG_RGB(240, 255, 240) }, + { "hotpink", NSVG_RGB(255, 105, 180) }, + { "indianred", NSVG_RGB(205, 92, 92) }, + { "indigo", NSVG_RGB( 75, 0, 130) }, + { "ivory", NSVG_RGB(255, 255, 240) }, + { "khaki", NSVG_RGB(240, 230, 140) }, + { "lavender", NSVG_RGB(230, 230, 250) }, + { "lavenderblush", NSVG_RGB(255, 240, 245) }, + { "lawngreen", NSVG_RGB(124, 252, 0) }, + { "lemonchiffon", NSVG_RGB(255, 250, 205) }, + { "lightblue", NSVG_RGB(173, 216, 230) }, + { "lightcoral", NSVG_RGB(240, 128, 128) }, + { "lightcyan", NSVG_RGB(224, 255, 255) }, + { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, + { "lightgray", NSVG_RGB(211, 211, 211) }, + { "lightgreen", NSVG_RGB(144, 238, 144) }, + { "lightgrey", NSVG_RGB(211, 211, 211) }, + { "lightpink", NSVG_RGB(255, 182, 193) }, + { "lightsalmon", NSVG_RGB(255, 160, 122) }, + { "lightseagreen", NSVG_RGB( 32, 178, 170) }, + { "lightskyblue", NSVG_RGB(135, 206, 250) }, + { "lightslategray", NSVG_RGB(119, 136, 153) }, + { "lightslategrey", NSVG_RGB(119, 136, 153) }, + { "lightsteelblue", NSVG_RGB(176, 196, 222) }, + { "lightyellow", NSVG_RGB(255, 255, 224) }, + { "lime", NSVG_RGB( 0, 255, 0) }, + { "limegreen", NSVG_RGB( 50, 205, 50) }, + { "linen", NSVG_RGB(250, 240, 230) }, + { "maroon", NSVG_RGB(128, 0, 0) }, + { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, + { "mediumblue", NSVG_RGB( 0, 0, 205) }, + { "mediumorchid", NSVG_RGB(186, 85, 211) }, + { "mediumpurple", NSVG_RGB(147, 112, 219) }, + { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, + { "mediumslateblue", NSVG_RGB(123, 104, 238) }, + { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, + { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, + { "mediumvioletred", NSVG_RGB(199, 21, 133) }, + { "midnightblue", NSVG_RGB( 25, 25, 112) }, + { "mintcream", NSVG_RGB(245, 255, 250) }, + { "mistyrose", NSVG_RGB(255, 228, 225) }, + { "moccasin", NSVG_RGB(255, 228, 181) }, + { "navajowhite", NSVG_RGB(255, 222, 173) }, + { "navy", NSVG_RGB( 0, 0, 128) }, + { "oldlace", NSVG_RGB(253, 245, 230) }, + { "olive", NSVG_RGB(128, 128, 0) }, + { "olivedrab", NSVG_RGB(107, 142, 35) }, + { "orange", NSVG_RGB(255, 165, 0) }, + { "orangered", NSVG_RGB(255, 69, 0) }, + { "orchid", NSVG_RGB(218, 112, 214) }, + { "palegoldenrod", NSVG_RGB(238, 232, 170) }, + { "palegreen", NSVG_RGB(152, 251, 152) }, + { "paleturquoise", NSVG_RGB(175, 238, 238) }, + { "palevioletred", NSVG_RGB(219, 112, 147) }, + { "papayawhip", NSVG_RGB(255, 239, 213) }, + { "peachpuff", NSVG_RGB(255, 218, 185) }, + { "peru", NSVG_RGB(205, 133, 63) }, + { "pink", NSVG_RGB(255, 192, 203) }, + { "plum", NSVG_RGB(221, 160, 221) }, + { "powderblue", NSVG_RGB(176, 224, 230) }, + { "purple", NSVG_RGB(128, 0, 128) }, + { "rosybrown", NSVG_RGB(188, 143, 143) }, + { "royalblue", NSVG_RGB( 65, 105, 225) }, + { "saddlebrown", NSVG_RGB(139, 69, 19) }, + { "salmon", NSVG_RGB(250, 128, 114) }, + { "sandybrown", NSVG_RGB(244, 164, 96) }, + { "seagreen", NSVG_RGB( 46, 139, 87) }, + { "seashell", NSVG_RGB(255, 245, 238) }, + { "sienna", NSVG_RGB(160, 82, 45) }, + { "silver", NSVG_RGB(192, 192, 192) }, + { "skyblue", NSVG_RGB(135, 206, 235) }, + { "slateblue", NSVG_RGB(106, 90, 205) }, + { "slategray", NSVG_RGB(112, 128, 144) }, + { "slategrey", NSVG_RGB(112, 128, 144) }, + { "snow", NSVG_RGB(255, 250, 250) }, + { "springgreen", NSVG_RGB( 0, 255, 127) }, + { "steelblue", NSVG_RGB( 70, 130, 180) }, + { "tan", NSVG_RGB(210, 180, 140) }, + { "teal", NSVG_RGB( 0, 128, 128) }, + { "thistle", NSVG_RGB(216, 191, 216) }, + { "tomato", NSVG_RGB(255, 99, 71) }, + { "turquoise", NSVG_RGB( 64, 224, 208) }, + { "violet", NSVG_RGB(238, 130, 238) }, + { "wheat", NSVG_RGB(245, 222, 179) }, + { "whitesmoke", NSVG_RGB(245, 245, 245) }, + { "yellowgreen", NSVG_RGB(154, 205, 50) }, +#endif +}; + +static unsigned int nsvg__parseColorName(const char* str) +{ + int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); + + for (i = 0; i < ncolors; i++) { + if (strcmp(nsvg__colors[i].name, str) == 0) { + return nsvg__colors[i].color; + } + } + + return NSVG_RGB(128, 128, 128); +} + +static unsigned int nsvg__parseColor(const char* str) +{ + size_t len = 0; + while(*str == ' ') ++str; + len = strlen(str); + if (len >= 1 && *str == '#') + return nsvg__parseColorHex(str); + else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') + return nsvg__parseColorRGB(str); + return nsvg__parseColorName(str); +} + +static float nsvg__parseOpacity(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + if (val > 1.0f) val = 1.0f; + return val; +} + +static float nsvg__parseMiterLimit(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + return val; +} + +static int nsvg__parseUnits(const char* units) +{ + if (units[0] == 'p' && units[1] == 'x') + return NSVG_UNITS_PX; + else if (units[0] == 'p' && units[1] == 't') + return NSVG_UNITS_PT; + else if (units[0] == 'p' && units[1] == 'c') + return NSVG_UNITS_PC; + else if (units[0] == 'm' && units[1] == 'm') + return NSVG_UNITS_MM; + else if (units[0] == 'c' && units[1] == 'm') + return NSVG_UNITS_CM; + else if (units[0] == 'i' && units[1] == 'n') + return NSVG_UNITS_IN; + else if (units[0] == '%') + return NSVG_UNITS_PERCENT; + else if (units[0] == 'e' && units[1] == 'm') + return NSVG_UNITS_EM; + else if (units[0] == 'e' && units[1] == 'x') + return NSVG_UNITS_EX; + return NSVG_UNITS_USER; +} + +static int nsvg__isCoordinate(const char* s) +{ + // optional sign + if (*s == '-' || *s == '+') + s++; + // must have at least one digit, or start by a dot + return (nsvg__isdigit(*s) || *s == '.'); +} + +static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) +{ + NSVGcoordinate coord = {0, NSVG_UNITS_USER}; + char buf[64]; + coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); + coord.value = nsvg__atof(buf); + return coord; +} + +static NSVGcoordinate nsvg__coord(float v, int units) +{ + NSVGcoordinate coord = {v, units}; + return coord; +} + +static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) +{ + NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); + return nsvg__convertToPixels(p, coord, orig, length); +} + +static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) +{ + const char* end; + const char* ptr; + char it[64]; + + *na = 0; + ptr = str; + while (*ptr && *ptr != '(') ++ptr; + if (*ptr == 0) + return 1; + end = ptr; + while (*end && *end != ')') ++end; + if (*end == 0) + return 1; + + while (ptr < end) { + if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { + if (*na >= maxNa) return 0; + ptr = nsvg__parseNumber(ptr, it, 64); + args[(*na)++] = (float)nsvg__atof(it); + } else { + ++ptr; + } + } + return (int)(end - str); +} + + +static int nsvg__parseMatrix(float* xform, const char* str) +{ + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, t, 6, &na); + if (na != 6) return len; + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseTranslate(float* xform, const char* str) +{ + float args[2]; + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = 0.0; + + nsvg__xformSetTranslation(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseScale(float* xform, const char* str) +{ + float args[2]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = args[0]; + nsvg__xformSetScale(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewX(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewY(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseRotate(float* xform, const char* str) +{ + float args[3]; + int na = 0; + float m[6]; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 3, &na); + if (na == 1) + args[1] = args[2] = 0.0f; + nsvg__xformIdentity(m); + + if (na > 1) { + nsvg__xformSetTranslation(t, -args[1], -args[2]); + nsvg__xformMultiply(m, t); + } + + nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); + nsvg__xformMultiply(m, t); + + if (na > 1) { + nsvg__xformSetTranslation(t, args[1], args[2]); + nsvg__xformMultiply(m, t); + } + + memcpy(xform, m, sizeof(float)*6); + + return len; +} + +static void nsvg__parseTransform(float* xform, const char* str) +{ + float t[6]; + int len; + nsvg__xformIdentity(xform); + while (*str) + { + if (strncmp(str, "matrix", 6) == 0) + len = nsvg__parseMatrix(t, str); + else if (strncmp(str, "translate", 9) == 0) + len = nsvg__parseTranslate(t, str); + else if (strncmp(str, "scale", 5) == 0) + len = nsvg__parseScale(t, str); + else if (strncmp(str, "rotate", 6) == 0) + len = nsvg__parseRotate(t, str); + else if (strncmp(str, "skewX", 5) == 0) + len = nsvg__parseSkewX(t, str); + else if (strncmp(str, "skewY", 5) == 0) + len = nsvg__parseSkewY(t, str); + else{ + ++str; + continue; + } + if (len != 0) { + str += len; + } else { + ++str; + continue; + } + + nsvg__xformPremultiply(xform, t); + } +} + +static void nsvg__parseUrl(char* id, const char* str) +{ + int i = 0; + str += 4; // "url("; + if (*str && *str == '#') + str++; + while (i < 63 && *str && *str != ')') { + id[i] = *str++; + i++; + } + id[i] = '\0'; +} + +static char nsvg__parseLineCap(const char* str) +{ + if (strcmp(str, "butt") == 0) + return NSVG_CAP_BUTT; + else if (strcmp(str, "round") == 0) + return NSVG_CAP_ROUND; + else if (strcmp(str, "square") == 0) + return NSVG_CAP_SQUARE; + // TODO: handle inherit. + return NSVG_CAP_BUTT; +} + +static char nsvg__parseLineJoin(const char* str) +{ + if (strcmp(str, "miter") == 0) + return NSVG_JOIN_MITER; + else if (strcmp(str, "round") == 0) + return NSVG_JOIN_ROUND; + else if (strcmp(str, "bevel") == 0) + return NSVG_JOIN_BEVEL; + // TODO: handle inherit. + return NSVG_JOIN_MITER; +} + +static char nsvg__parseFillRule(const char* str) +{ + if (strcmp(str, "nonzero") == 0) + return NSVG_FILLRULE_NONZERO; + else if (strcmp(str, "evenodd") == 0) + return NSVG_FILLRULE_EVENODD; + // TODO: handle inherit. + return NSVG_FILLRULE_NONZERO; +} + +static const char* nsvg__getNextDashItem(const char* s, char* it) +{ + int n = 0; + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + // Advance until whitespace, comma or end. + while (*s && (!nsvg__isspace(*s) && *s != ',')) { + if (n < 63) + it[n++] = *s; + s++; + } + it[n++] = '\0'; + return s; +} + +static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) +{ + char item[64]; + int count = 0, i; + float sum = 0.0f; + + // Handle "none" + if (str[0] == 'n') + return 0; + + // Parse dashes + while (*str) { + str = nsvg__getNextDashItem(str, item); + if (!*item) break; + if (count < NSVG_MAX_DASHES) + strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); + } + + for (i = 0; i < count; i++) + sum += strokeDashArray[i]; + if (sum <= 1e-6f) + count = 0; + + return count; +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str); + +static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) +{ + float xform[6]; + NSVGattrib* attr = nsvg__getAttr(p); + if (!attr) return 0; + + if (strcmp(name, "style") == 0) { + nsvg__parseStyle(p, value); + } else if (strcmp(name, "display") == 0) { + if (strcmp(value, "none") == 0) + attr->visible = 0; + // Don't reset ->visible on display:inline, one display:none hides the whole subtree + + } else if (strcmp(name, "fill") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasFill = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasFill = 2; + nsvg__parseUrl(attr->fillGradient, value); + } else { + attr->hasFill = 1; + attr->fillColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "opacity") == 0) { + attr->opacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "fill-opacity") == 0) { + attr->fillOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasStroke = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasStroke = 2; + nsvg__parseUrl(attr->strokeGradient, value); + } else { + attr->hasStroke = 1; + attr->strokeColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "stroke-width") == 0) { + attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-dasharray") == 0) { + attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); + } else if (strcmp(name, "stroke-dashoffset") == 0) { + attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-opacity") == 0) { + attr->strokeOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke-linecap") == 0) { + attr->strokeLineCap = nsvg__parseLineCap(value); + } else if (strcmp(name, "stroke-linejoin") == 0) { + attr->strokeLineJoin = nsvg__parseLineJoin(value); + } else if (strcmp(name, "stroke-miterlimit") == 0) { + attr->miterLimit = nsvg__parseMiterLimit(value); + } else if (strcmp(name, "fill-rule") == 0) { + attr->fillRule = nsvg__parseFillRule(value); + } else if (strcmp(name, "font-size") == 0) { + attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "transform") == 0) { + nsvg__parseTransform(xform, value); + nsvg__xformPremultiply(attr->xform, xform); + } else if (strcmp(name, "stop-color") == 0) { + attr->stopColor = nsvg__parseColor(value); + } else if (strcmp(name, "stop-opacity") == 0) { + attr->stopOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "offset") == 0) { + attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); + } else if (strcmp(name, "id") == 0) { + strncpy(attr->id, value, 63); + attr->id[63] = '\0'; + } else { + return 0; + } + return 1; +} + +static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) +{ + const char* str; + const char* val; + char name[512]; + char value[512]; + int n; + + str = start; + while (str < end && *str != ':') ++str; + + val = str; + + // Right Trim + while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; + ++str; + + n = (int)(str - start); + if (n > 511) n = 511; + if (n) memcpy(name, start, n); + name[n] = 0; + + while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; + + n = (int)(end - val); + if (n > 511) n = 511; + if (n) memcpy(value, val, n); + value[n] = 0; + + return nsvg__parseAttr(p, name, value); +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str) +{ + const char* start; + const char* end; + + while (*str) { + // Left Trim + while(*str && nsvg__isspace(*str)) ++str; + start = str; + while(*str && *str != ';') ++str; + end = str; + + // Right Trim + while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; + ++end; + + nsvg__parseNameValue(p, start, end); + if (*str) ++str; + } +} + +static void nsvg__parseAttribs(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) + { + if (strcmp(attr[i], "style") == 0) + nsvg__parseStyle(p, attr[i + 1]); + else + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } +} + +static int nsvg__getArgsPerElement(char cmd) +{ + switch (cmd) { + case 'v': + case 'V': + case 'h': + case 'H': + return 1; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + return 2; + case 'q': + case 'Q': + case 's': + case 'S': + return 4; + case 'c': + case 'C': + return 6; + case 'a': + case 'A': + return 7; + case 'z': + case 'Z': + return 0; + } + return -1; +} + +static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__moveTo(p, *cpx, *cpy); +} + +static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpx += args[0]; + else + *cpx = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpy += args[0]; + else + *cpy = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x2, y2, cx1, cy1, cx2, cy2; + + if (rel) { + cx1 = *cpx + args[0]; + cy1 = *cpy + args[1]; + cx2 = *cpx + args[2]; + cy2 = *cpy + args[3]; + x2 = *cpx + args[4]; + y2 = *cpy + args[5]; + } else { + cx1 = args[0]; + cy1 = args[1]; + cx2 = args[2]; + cy2 = args[3]; + x2 = args[4]; + y2 = args[5]; + } + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx2 = *cpx + args[0]; + cy2 = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx2 = args[0]; + cy2 = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + cx1 = 2*x1 - *cpx2; + cy1 = 2*y1 - *cpy2; + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx = *cpx + args[0]; + cy = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx = args[0]; + cy = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + // Convert to cubic bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + x2 = *cpx + args[0]; + y2 = *cpy + args[1]; + } else { + x2 = args[0]; + y2 = args[1]; + } + + cx = 2*x1 - *cpx2; + cy = 2*y1 - *cpy2; + + // Convert to cubix bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static float nsvg__sqr(float x) { return x*x; } +static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } + +static float nsvg__vecrat(float ux, float uy, float vx, float vy) +{ + return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); +} + +static float nsvg__vecang(float ux, float uy, float vx, float vy) +{ + float r = nsvg__vecrat(ux,uy, vx,vy); + if (r < -1.0f) r = -1.0f; + if (r > 1.0f) r = 1.0f; + return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); +} + +static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + // Ported from canvg (https://code.google.com/p/canvg/) + float rx, ry, rotx; + float x1, y1, x2, y2, cx, cy, dx, dy, d; + float x1p, y1p, cxp, cyp, s, sa, sb; + float ux, uy, vx, vy, a1, da; + float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; + float sinrx, cosrx; + int fa, fs; + int i, ndivs; + float hda, kappa; + + rx = fabsf(args[0]); // y radius + ry = fabsf(args[1]); // x radius + rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle + fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc + fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction + x1 = *cpx; // start point + y1 = *cpy; + if (rel) { // end point + x2 = *cpx + args[5]; + y2 = *cpy + args[6]; + } else { + x2 = args[5]; + y2 = args[6]; + } + + dx = x1 - x2; + dy = y1 - y2; + d = sqrtf(dx*dx + dy*dy); + if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { + // The arc degenerates to a line + nsvg__lineTo(p, x2, y2); + *cpx = x2; + *cpy = y2; + return; + } + + sinrx = sinf(rotx); + cosrx = cosf(rotx); + + // Convert to center point parameterization. + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + // 1) Compute x1', y1' + x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; + y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; + d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); + if (d > 1) { + d = sqrtf(d); + rx *= d; + ry *= d; + } + // 2) Compute cx', cy' + s = 0.0f; + sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); + sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); + if (sa < 0.0f) sa = 0.0f; + if (sb > 0.0f) + s = sqrtf(sa / sb); + if (fa == fs) + s = -s; + cxp = s * rx * y1p / ry; + cyp = s * -ry * x1p / rx; + + // 3) Compute cx,cy from cx',cy' + cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; + cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; + + // 4) Calculate theta1, and delta theta. + ux = (x1p - cxp) / rx; + uy = (y1p - cyp) / ry; + vx = (-x1p - cxp) / rx; + vy = (-y1p - cyp) / ry; + a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle + da = nsvg__vecang(ux,uy, vx,vy); // Delta angle + +// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; +// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; + + if (fs == 0 && da > 0) + da -= 2 * NSVG_PI; + else if (fs == 1 && da < 0) + da += 2 * NSVG_PI; + + // Approximate the arc using cubic spline segments. + t[0] = cosrx; t[1] = sinrx; + t[2] = -sinrx; t[3] = cosrx; + t[4] = cx; t[5] = cy; + + // Split arc into max 90 degree segments. + // The loop assumes an iteration per end point (including start and end), this +1. + ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); + hda = (da / (float)ndivs) / 2.0f; + // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) + if ((hda < 1e-3f) && (hda > -1e-3f)) + hda *= 0.5f; + else + hda = (1.0f - cosf(hda)) / sinf(hda); + kappa = fabsf(4.0f / 3.0f * hda); + if (da < 0.0f) + kappa = -kappa; + + for (i = 0; i <= ndivs; i++) { + a = a1 + da * ((float)i/(float)ndivs); + dx = cosf(a); + dy = sinf(a); + nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position + nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent + if (i > 0) + nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); + px = x; + py = y; + ptanx = tanx; + ptany = tany; + } + + *cpx = x2; + *cpy = y2; +} + +static void nsvg__parsePath(NSVGparser* p, const char** attr) +{ + const char* s = NULL; + char cmd = '\0'; + float args[10]; + int nargs; + int rargs = 0; + char initPoint; + float cpx, cpy, cpx2, cpy2; + const char* tmp[4]; + char closedFlag; + int i; + char item[64]; + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "d") == 0) { + s = attr[i + 1]; + } else { + tmp[0] = attr[i]; + tmp[1] = attr[i + 1]; + tmp[2] = 0; + tmp[3] = 0; + nsvg__parseAttribs(p, tmp); + } + } + + if (s) { + nsvg__resetPath(p); + cpx = 0; cpy = 0; + cpx2 = 0; cpy2 = 0; + initPoint = 0; + closedFlag = 0; + nargs = 0; + + while (*s) { + s = nsvg__getNextPathItem(s, item); + if (!*item) break; + if (cmd != '\0' && nsvg__isCoordinate(item)) { + if (nargs < 10) + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= rargs) { + switch (cmd) { + case 'm': + case 'M': + nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); + // Moveto can be followed by multiple coordinate pairs, + // which should be treated as linetos. + cmd = (cmd == 'm') ? 'l' : 'L'; + rargs = nsvg__getArgsPerElement(cmd); + cpx2 = cpx; cpy2 = cpy; + initPoint = 1; + break; + case 'l': + case 'L': + nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'H': + case 'h': + nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'V': + case 'v': + nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'C': + case 'c': + nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); + break; + case 'S': + case 's': + nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); + break; + case 'Q': + case 'q': + nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); + break; + case 'T': + case 't': + nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); + break; + case 'A': + case 'a': + nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + default: + if (nargs >= 2) { + cpx = args[nargs-2]; + cpy = args[nargs-1]; + cpx2 = cpx; cpy2 = cpy; + } + break; + } + nargs = 0; + } + } else { + cmd = item[0]; + if (cmd == 'M' || cmd == 'm') { + // Commit path. + if (p->npts > 0) + nsvg__addPath(p, closedFlag); + // Start new subpath. + nsvg__resetPath(p); + closedFlag = 0; + nargs = 0; + } else if (initPoint == 0) { + // Do not allow other commands until initial point has been set (moveTo called once). + cmd = '\0'; + } + if (cmd == 'Z' || cmd == 'z') { + closedFlag = 1; + // Commit path. + if (p->npts > 0) { + // Move current point to first point + cpx = p->pts[0]; + cpy = p->pts[1]; + cpx2 = cpx; cpy2 = cpy; + nsvg__addPath(p, closedFlag); + } + // Start new subpath. + nsvg__resetPath(p); + nsvg__moveTo(p, cpx, cpy); + closedFlag = 0; + nargs = 0; + } + rargs = nsvg__getArgsPerElement(cmd); + if (rargs == -1) { + // Command not recognized + cmd = '\0'; + rargs = 0; + } + } + } + // Commit path. + if (p->npts) + nsvg__addPath(p, closedFlag); + } + + nsvg__addShape(p); +} + +static void nsvg__parseRect(NSVGparser* p, const char** attr) +{ + float x = 0.0f; + float y = 0.0f; + float w = 0.0f; + float h = 0.0f; + float rx = -1.0f; // marks not set + float ry = -1.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); + if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx < 0.0f && ry > 0.0f) rx = ry; + if (ry < 0.0f && rx > 0.0f) ry = rx; + if (rx < 0.0f) rx = 0.0f; + if (ry < 0.0f) ry = 0.0f; + if (rx > w/2.0f) rx = w/2.0f; + if (ry > h/2.0f) ry = h/2.0f; + + if (w != 0.0f && h != 0.0f) { + nsvg__resetPath(p); + + if (rx < 0.00001f || ry < 0.0001f) { + nsvg__moveTo(p, x, y); + nsvg__lineTo(p, x+w, y); + nsvg__lineTo(p, x+w, y+h); + nsvg__lineTo(p, x, y+h); + } else { + // Rounded rectangle + nsvg__moveTo(p, x+rx, y); + nsvg__lineTo(p, x+w-rx, y); + nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); + nsvg__lineTo(p, x+w, y+h-ry); + nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); + nsvg__lineTo(p, x+rx, y+h); + nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); + nsvg__lineTo(p, x, y+ry); + nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); + } + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseCircle(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float r = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); + } + } + + if (r > 0.0f) { + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+r, cy); + nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); + nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); + nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); + nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseEllipse(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float rx = 0.0f; + float ry = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx > 0.0f && ry > 0.0f) { + + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+rx, cy); + nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); + nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); + nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); + nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseLine(NSVGparser* p, const char** attr) +{ + float x1 = 0.0; + float y1 = 0.0; + float x2 = 0.0; + float y2 = 0.0; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + } + } + + nsvg__resetPath(p); + + nsvg__moveTo(p, x1, y1); + nsvg__lineTo(p, x2, y2); + + nsvg__addPath(p, 0); + + nsvg__addShape(p); +} + +static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) +{ + int i; + const char* s; + float args[2]; + int nargs, npts = 0; + char item[64]; + + nsvg__resetPath(p); + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "points") == 0) { + s = attr[i + 1]; + nargs = 0; + while (*s) { + s = nsvg__getNextPathItem(s, item); + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= 2) { + if (npts == 0) + nsvg__moveTo(p, args[0], args[1]); + else + nsvg__lineTo(p, args[0], args[1]); + nargs = 0; + npts++; + } + } + } + } + } + + nsvg__addPath(p, (char)closeFlag); + + nsvg__addShape(p); +} + +static void nsvg__parseSVG(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "width") == 0) { + p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "height") == 0) { + p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "viewBox") == 0) { + const char *s = attr[i + 1]; + char buf[64]; + s = nsvg__parseNumber(s, buf, 64); + p->viewMinx = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewMiny = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewWidth = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewHeight = nsvg__atof(buf); + } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { + if (strstr(attr[i + 1], "none") != 0) { + // No uniform scaling + p->alignType = NSVG_ALIGN_NONE; + } else { + // Parse X align + if (strstr(attr[i + 1], "xMin") != 0) + p->alignX = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "xMid") != 0) + p->alignX = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "xMax") != 0) + p->alignX = NSVG_ALIGN_MAX; + // Parse X align + if (strstr(attr[i + 1], "yMin") != 0) + p->alignY = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "yMid") != 0) + p->alignY = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "yMax") != 0) + p->alignY = NSVG_ALIGN_MAX; + // Parse meet/slice + p->alignType = NSVG_ALIGN_MEET; + if (strstr(attr[i + 1], "slice") != 0) + p->alignType = NSVG_ALIGN_SLICE; + } + } + } + } +} + +static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) +{ + int i; + NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); + if (grad == NULL) return; + memset(grad, 0, sizeof(NSVGgradientData)); + grad->units = NSVG_OBJECT_SPACE; + grad->type = type; + if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { + grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); + grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { + grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + } + + nsvg__xformIdentity(grad->xform); + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "id") == 0) { + strncpy(grad->id, attr[i+1], 63); + grad->id[63] = '\0'; + } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "gradientUnits") == 0) { + if (strcmp(attr[i+1], "objectBoundingBox") == 0) + grad->units = NSVG_OBJECT_SPACE; + else + grad->units = NSVG_USER_SPACE; + } else if (strcmp(attr[i], "gradientTransform") == 0) { + nsvg__parseTransform(grad->xform, attr[i + 1]); + } else if (strcmp(attr[i], "cx") == 0) { + grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "cy") == 0) { + grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "r") == 0) { + grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fx") == 0) { + grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fy") == 0) { + grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x1") == 0) { + grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y1") == 0) { + grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x2") == 0) { + grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y2") == 0) { + grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "spreadMethod") == 0) { + if (strcmp(attr[i+1], "pad") == 0) + grad->spread = NSVG_SPREAD_PAD; + else if (strcmp(attr[i+1], "reflect") == 0) + grad->spread = NSVG_SPREAD_REFLECT; + else if (strcmp(attr[i+1], "repeat") == 0) + grad->spread = NSVG_SPREAD_REPEAT; + } else if (strcmp(attr[i], "xlink:href") == 0) { + const char *href = attr[i+1]; + strncpy(grad->ref, href+1, 62); + grad->ref[62] = '\0'; + } + } + } + + grad->next = p->gradients; + p->gradients = grad; +} + +static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) +{ + NSVGattrib* curAttr = nsvg__getAttr(p); + NSVGgradientData* grad; + NSVGgradientStop* stop; + int i, idx; + + curAttr->stopOffset = 0; + curAttr->stopColor = 0; + curAttr->stopOpacity = 1.0f; + + for (i = 0; attr[i]; i += 2) { + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } + + // Add stop to the last gradient. + grad = p->gradients; + if (grad == NULL) return; + + grad->nstops++; + grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); + if (grad->stops == NULL) return; + + // Insert + idx = grad->nstops-1; + for (i = 0; i < grad->nstops-1; i++) { + if (curAttr->stopOffset < grad->stops[i].offset) { + idx = i; + break; + } + } + if (idx != grad->nstops-1) { + for (i = grad->nstops-1; i > idx; i--) + grad->stops[i] = grad->stops[i-1]; + } + + stop = &grad->stops[idx]; + stop->color = curAttr->stopColor; + stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; + stop->offset = curAttr->stopOffset; +} + +static void nsvg__startElement(void* ud, const char* el, const char** attr) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (p->defsFlag) { + // Skip everything but gradients in defs + if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } + return; + } + + if (strcmp(el, "g") == 0) { + nsvg__pushAttr(p); + nsvg__parseAttribs(p, attr); + } else if (strcmp(el, "path") == 0) { + if (p->pathFlag) // Do not allow nested paths. + return; + nsvg__pushAttr(p); + nsvg__parsePath(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "rect") == 0) { + nsvg__pushAttr(p); + nsvg__parseRect(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "circle") == 0) { + nsvg__pushAttr(p); + nsvg__parseCircle(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "ellipse") == 0) { + nsvg__pushAttr(p); + nsvg__parseEllipse(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "line") == 0) { + nsvg__pushAttr(p); + nsvg__parseLine(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "polyline") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 0); + nsvg__popAttr(p); + } else if (strcmp(el, "polygon") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 1); + nsvg__popAttr(p); + } else if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 1; + } else if (strcmp(el, "svg") == 0) { + nsvg__parseSVG(p, attr); + } +} + +static void nsvg__endElement(void* ud, const char* el) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (strcmp(el, "g") == 0) { + nsvg__popAttr(p); + } else if (strcmp(el, "path") == 0) { + p->pathFlag = 0; + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 0; + } +} + +static void nsvg__content(void* ud, const char* s) +{ + NSVG_NOTUSED(ud); + NSVG_NOTUSED(s); + // empty +} + +static void nsvg__imageBounds(NSVGparser* p, float* bounds) +{ + NSVGshape* shape; + shape = p->image->shapes; + if (shape == NULL) { + bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; + return; + } + bounds[0] = shape->bounds[0]; + bounds[1] = shape->bounds[1]; + bounds[2] = shape->bounds[2]; + bounds[3] = shape->bounds[3]; + for (shape = shape->next; shape != NULL; shape = shape->next) { + bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); + bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); + bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); + bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); + } +} + +static float nsvg__viewAlign(float content, float container, int type) +{ + if (type == NSVG_ALIGN_MIN) + return 0; + else if (type == NSVG_ALIGN_MAX) + return container - content; + // mid + return (container - content) * 0.5f; +} + +static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) +{ + float t[6]; + nsvg__xformSetTranslation(t, tx, ty); + nsvg__xformMultiply (grad->xform, t); + + nsvg__xformSetScale(t, sx, sy); + nsvg__xformMultiply (grad->xform, t); +} + +static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) +{ + NSVGshape* shape; + NSVGpath* path; + float tx, ty, sx, sy, us, bounds[4], t[6], avgs; + int i; + float* pt; + + // Guess image size if not set completely. + nsvg__imageBounds(p, bounds); + + if (p->viewWidth == 0) { + if (p->image->width > 0) { + p->viewWidth = p->image->width; + } else { + p->viewMinx = bounds[0]; + p->viewWidth = bounds[2] - bounds[0]; + } + } + if (p->viewHeight == 0) { + if (p->image->height > 0) { + p->viewHeight = p->image->height; + } else { + p->viewMiny = bounds[1]; + p->viewHeight = bounds[3] - bounds[1]; + } + } + if (p->image->width == 0) + p->image->width = p->viewWidth; + if (p->image->height == 0) + p->image->height = p->viewHeight; + + tx = -p->viewMinx; + ty = -p->viewMiny; + sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; + sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; + // Unit scaling + us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); + + // Fix aspect ratio + if (p->alignType == NSVG_ALIGN_MEET) { + // fit whole image into viewbox + sx = sy = nsvg__minf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } else if (p->alignType == NSVG_ALIGN_SLICE) { + // fill whole viewbox with image + sx = sy = nsvg__maxf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } + + // Transform + sx *= us; + sy *= us; + avgs = (sx+sy) / 2.0f; + for (shape = p->image->shapes; shape != NULL; shape = shape->next) { + shape->bounds[0] = (shape->bounds[0] + tx) * sx; + shape->bounds[1] = (shape->bounds[1] + ty) * sy; + shape->bounds[2] = (shape->bounds[2] + tx) * sx; + shape->bounds[3] = (shape->bounds[3] + ty) * sy; + for (path = shape->paths; path != NULL; path = path->next) { + path->bounds[0] = (path->bounds[0] + tx) * sx; + path->bounds[1] = (path->bounds[1] + ty) * sy; + path->bounds[2] = (path->bounds[2] + tx) * sx; + path->bounds[3] = (path->bounds[3] + ty) * sy; + for (i =0; i < path->npts; i++) { + pt = &path->pts[i*2]; + pt[0] = (pt[0] + tx) * sx; + pt[1] = (pt[1] + ty) * sy; + } + } + + if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); + memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->fill.gradient->xform, t); + } + if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); + memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->stroke.gradient->xform, t); + } + + shape->strokeWidth *= avgs; + shape->strokeDashOffset *= avgs; + for (i = 0; i < shape->strokeDashCount; i++) + shape->strokeDashArray[i] *= avgs; + } +} + +NSVGimage* nsvgParse(char* input, const char* units, float dpi) +{ + NSVGparser* p; + NSVGimage* ret = 0; + + p = nsvg__createParser(); + if (p == NULL) { + return NULL; + } + p->dpi = dpi; + + nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); + + // Scale to viewBox + nsvg__scaleToViewbox(p, units); + + ret = p->image; + p->image = NULL; + + nsvg__deleteParser(p); + + return ret; +} + +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + NSVGimage* image = NULL; + + fp = fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size+1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + image = nsvgParse(data, units, dpi); + free(data); + + return image; + +error: + if (fp) fclose(fp); + if (data) free(data); + if (image) nsvgDelete(image); + return NULL; +} + +NSVGpath* nsvgDuplicatePath(NSVGpath* p) +{ + NSVGpath* res = NULL; + + if (p == NULL) + return NULL; + + res = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (res == NULL) goto error; + memset(res, 0, sizeof(NSVGpath)); + + res->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (res->pts == NULL) goto error; + memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); + res->npts = p->npts; + + memcpy(res->bounds, p->bounds, sizeof(p->bounds)); + + res->closed = p->closed; + + return res; + +error: + if (res != NULL) { + free(res->pts); + free(res); + } + return NULL; +} + +void nsvgDelete(NSVGimage* image) +{ + NSVGshape *snext, *shape; + if (image == NULL) return; + shape = image->shapes; + while (shape != NULL) { + snext = shape->next; + nsvg__deletePaths(shape->paths); + nsvg__deletePaint(&shape->fill); + nsvg__deletePaint(&shape->stroke); + free(shape); + shape = snext; + } + free(image); +} + +#endif // NANOSVG_IMPLEMENTATION + +#endif // NANOSVG_H diff --git a/src/external/nanosvgrast.h b/src/external/nanosvgrast.h new file mode 100644 index 000000000..6e23acb7b --- /dev/null +++ b/src/external/nanosvgrast.h @@ -0,0 +1,1458 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The polygon rasterization is heavily based on stb_truetype rasterizer + * by Sean Barrett - http://nothings.org/ + * + */ + +#ifndef NANOSVGRAST_H +#define NANOSVGRAST_H + +#include "nanosvg.h" + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +typedef struct NSVGrasterizer NSVGrasterizer; + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + + // Create rasterizer (can be used to render multiple images). + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + // Allocate memory for image + unsigned char* img = malloc(w*h*4); + // Rasterize + nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); +*/ + +// Allocated rasterizer context. +NSVGrasterizer* nsvgCreateRasterizer(void); + +// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) +// r - pointer to rasterizer context +// image - pointer to image to rasterize +// tx,ty - image offset (applied after scaling) +// scale - image scale +// dst - pointer to destination image data, 4 bytes per pixel (RGBA) +// w - width of the image to render +// h - height of the image to render +// stride - number of bytes per scaleline in the destination buffer +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride); + +// Deletes rasterizer context. +void nsvgDeleteRasterizer(NSVGrasterizer*); + + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#ifdef NANOSVGRAST_IMPLEMENTATION + +#include +#include +#include + +#define NSVG__SUBSAMPLES 5 +#define NSVG__FIXSHIFT 10 +#define NSVG__FIX (1 << NSVG__FIXSHIFT) +#define NSVG__FIXMASK (NSVG__FIX-1) +#define NSVG__MEMPAGE_SIZE 1024 + +typedef struct NSVGedge { + float x0,y0, x1,y1; + int dir; + struct NSVGedge* next; +} NSVGedge; + +typedef struct NSVGpoint { + float x, y; + float dx, dy; + float len; + float dmx, dmy; + unsigned char flags; +} NSVGpoint; + +typedef struct NSVGactiveEdge { + int x,dx; + float ey; + int dir; + struct NSVGactiveEdge *next; +} NSVGactiveEdge; + +typedef struct NSVGmemPage { + unsigned char mem[NSVG__MEMPAGE_SIZE]; + int size; + struct NSVGmemPage* next; +} NSVGmemPage; + +typedef struct NSVGcachedPaint { + char type; + char spread; + float xform[6]; + unsigned int colors[256]; +} NSVGcachedPaint; + +struct NSVGrasterizer +{ + float px, py; + + float tessTol; + float distTol; + + NSVGedge* edges; + int nedges; + int cedges; + + NSVGpoint* points; + int npoints; + int cpoints; + + NSVGpoint* points2; + int npoints2; + int cpoints2; + + NSVGactiveEdge* freelist; + NSVGmemPage* pages; + NSVGmemPage* curpage; + + unsigned char* scanline; + int cscanline; + + unsigned char* bitmap; + int width, height, stride; +}; + +NSVGrasterizer* nsvgCreateRasterizer(void) +{ + NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); + if (r == NULL) goto error; + memset(r, 0, sizeof(NSVGrasterizer)); + + r->tessTol = 0.25f; + r->distTol = 0.01f; + + return r; + +error: + nsvgDeleteRasterizer(r); + return NULL; +} + +void nsvgDeleteRasterizer(NSVGrasterizer* r) +{ + NSVGmemPage* p; + + if (r == NULL) return; + + p = r->pages; + while (p != NULL) { + NSVGmemPage* next = p->next; + free(p); + p = next; + } + + if (r->edges) free(r->edges); + if (r->points) free(r->points); + if (r->points2) free(r->points2); + if (r->scanline) free(r->scanline); + + free(r); +} + +static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) +{ + NSVGmemPage *newp; + + // If using existing chain, return the next page in chain + if (cur != NULL && cur->next != NULL) { + return cur->next; + } + + // Alloc new page + newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); + if (newp == NULL) return NULL; + memset(newp, 0, sizeof(NSVGmemPage)); + + // Add to linked list + if (cur != NULL) + cur->next = newp; + else + r->pages = newp; + + return newp; +} + +static void nsvg__resetPool(NSVGrasterizer* r) +{ + NSVGmemPage* p = r->pages; + while (p != NULL) { + p->size = 0; + p = p->next; + } + r->curpage = r->pages; +} + +static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) +{ + unsigned char* buf; + if (size > NSVG__MEMPAGE_SIZE) return NULL; + if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { + r->curpage = nsvg__nextPage(r, r->curpage); + } + buf = &r->curpage->mem[r->curpage->size]; + r->curpage->size += size; + return buf; +} + +static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) +{ + float dx = x2 - x1; + float dy = y2 - y1; + return dx*dx + dy*dy < tol*tol; +} + +static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) +{ + NSVGpoint* pt; + + if (r->npoints > 0) { + pt = &r->points[r->npoints-1]; + if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { + pt->flags = (unsigned char)(pt->flags | flags); + return; + } + } + + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + + pt = &r->points[r->npoints]; + pt->x = x; + pt->y = y; + pt->flags = (unsigned char)flags; + r->npoints++; +} + +static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) +{ + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + r->points[r->npoints] = pt; + r->npoints++; +} + +static void nsvg__duplicatePoints(NSVGrasterizer* r) +{ + if (r->npoints > r->cpoints2) { + r->cpoints2 = r->npoints; + r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); + if (r->points2 == NULL) return; + } + + memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); + r->npoints2 = r->npoints; +} + +static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) +{ + NSVGedge* e; + + // Skip horizontal edges + if (y0 == y1) + return; + + if (r->nedges+1 > r->cedges) { + r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; + r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); + if (r->edges == NULL) return; + } + + e = &r->edges[r->nedges]; + r->nedges++; + + if (y0 < y1) { + e->x0 = x0; + e->y0 = y0; + e->x1 = x1; + e->y1 = y1; + e->dir = 1; + } else { + e->x0 = x1; + e->y0 = y1; + e->x1 = x0; + e->y1 = y0; + e->dir = -1; + } +} + +static float nsvg__normalize(float *x, float* y) +{ + float d = sqrtf((*x)*(*x) + (*y)*(*y)); + if (d > 1e-6f) { + float id = 1.0f / d; + *x *= id; + *y *= id; + } + return d; +} + +static float nsvg__absf(float x) { return x < 0 ? -x : x; } + +static void nsvg__flattenCubicBez(NSVGrasterizer* r, + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + int level, int type) +{ + float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; + float dx,dy,d2,d3; + + if (level > 10) return; + + x12 = (x1+x2)*0.5f; + y12 = (y1+y2)*0.5f; + x23 = (x2+x3)*0.5f; + y23 = (y2+y3)*0.5f; + x34 = (x3+x4)*0.5f; + y34 = (y3+y4)*0.5f; + x123 = (x12+x23)*0.5f; + y123 = (y12+y23)*0.5f; + + dx = x4 - x1; + dy = y4 - y1; + d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); + d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); + + if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { + nsvg__addPathPoint(r, x4, y4, type); + return; + } + + x234 = (x23+x34)*0.5f; + y234 = (y23+y34)*0.5f; + x1234 = (x123+x234)*0.5f; + y1234 = (y123+y234)*0.5f; + + nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); + nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); +} + +static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j; + NSVGpath* path; + + for (path = shape->paths; path != NULL; path = path->next) { + r->npoints = 0; + // Flatten path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); + } + // Close path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + // Build edges + for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) + nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); + } +} + +enum NSVGpointFlags +{ + NSVG_PT_CORNER = 0x01, + NSVG_PT_BEVEL = 0x02, + NSVG_PT_LEFT = 0x04 +}; + +static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + float len = nsvg__normalize(&dx, &dy); + float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x - dx*w, py = p->y - dy*w; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +#ifndef NSVG_PI +#define NSVG_PI (3.14159265358979323846264338327f) +#endif + +static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) +{ + int i; + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; + + for (i = 0; i < ncap; i++) { + float a = (float)i/(float)(ncap-1)*NSVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + float x = px - dlx*ax - dx*ay; + float y = py - dly*ax - dy*ay; + + if (i > 0) + nsvg__addEdge(r, prevx, prevy, x, y); + + prevx = x; + prevy = y; + + if (i == 0) { + lx = x; ly = y; + } else if (i == ncap-1) { + rx = x; ry = y; + } + } + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); + float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); + float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); + float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); + + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0, rx0, lx1, rx1; + float ly0, ry0, ly1, ry1; + + if (p1->flags & NSVG_PT_LEFT) { + lx0 = lx1 = p1->x - p1->dmx * w; + ly0 = ly1 = p1->y - p1->dmy * w; + nsvg__addEdge(r, lx1, ly1, left->x, left->y); + + rx0 = p1->x + (dlx0 * w); + ry0 = p1->y + (dly0 * w); + rx1 = p1->x + (dlx1 * w); + ry1 = p1->y + (dly1 * w); + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + } else { + lx0 = p1->x - (dlx0 * w); + ly0 = p1->y - (dly0 * w); + lx1 = p1->x - (dlx1 * w); + ly1 = p1->y - (dly1 * w); + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + rx0 = rx1 = p1->x + p1->dmx * w; + ry0 = ry1 = p1->y + p1->dmy * w; + nsvg__addEdge(r, right->x, right->y, rx1, ry1); + } + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) +{ + int i, n; + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float a0 = atan2f(dly0, dlx0); + float a1 = atan2f(dly1, dlx1); + float da = a1 - a0; + float lx, ly, rx, ry; + + if (da < NSVG_PI) da += NSVG_PI*2; + if (da > NSVG_PI) da -= NSVG_PI*2; + + n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); + if (n < 2) n = 2; + if (n > ncap) n = ncap; + + lx = left->x; + ly = left->y; + rx = right->x; + ry = right->y; + + for (i = 0; i < n; i++) { + float u = (float)i/(float)(n-1); + float a = a0 + u*da; + float ax = cosf(a) * w, ay = sinf(a) * w; + float lx1 = p1->x - ax, ly1 = p1->y - ay; + float rx1 = p1->x + ax, ry1 = p1->y + ay; + + nsvg__addEdge(r, lx1, ly1, lx, ly); + nsvg__addEdge(r, rx, ry, rx1, ry1); + + lx = lx1; ly = ly1; + rx = rx1; ry = ry1; + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); + float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); + + nsvg__addEdge(r, lx, ly, left->x, left->y); + nsvg__addEdge(r, right->x, right->y, rx, ry); + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static int nsvg__curveDivs(float r, float arc, float tol) +{ + float da = acosf(r / (r + tol)) * 2.0f; + int divs = (int)ceilf(arc / da); + if (divs < 2) divs = 2; + return divs; +} + +static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) +{ + int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. + NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; + NSVGpoint* p0, *p1; + int j, s, e; + + // Build stroke edges + if (closed) { + // Looping + p0 = &points[npoints-1]; + p1 = &points[0]; + s = 0; + e = npoints; + } else { + // Add cap + p0 = &points[0]; + p1 = &points[1]; + s = 1; + e = npoints-1; + } + + if (closed) { + nsvg__initClosed(&left, &right, p0, p1, lineWidth); + firstLeft = left; + firstRight = right; + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); + } + + for (j = s; j < e; ++j) { + if (p1->flags & NSVG_PT_CORNER) { + if (lineJoin == NSVG_JOIN_ROUND) + nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); + else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) + nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); + else + nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); + } else { + nsvg__straightJoin(r, &left, &right, p1, lineWidth); + } + p0 = p1++; + } + + if (closed) { + // Loop it + nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); + nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); + } +} + +static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) +{ + int i, j; + NSVGpoint* p0, *p1; + + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (i = 0; i < r->npoints; i++) { + // Calculate segment direction and length + p0->dx = p1->x - p0->x; + p0->dy = p1->y - p0->y; + p0->len = nsvg__normalize(&p0->dx, &p0->dy); + // Advance + p0 = p1++; + } + + // calculate joins + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (j = 0; j < r->npoints; j++) { + float dlx0, dly0, dlx1, dly1, dmr2, cross; + dlx0 = p0->dy; + dly0 = -p0->dx; + dlx1 = p1->dy; + dly1 = -p1->dx; + // Calculate extrusions + p1->dmx = (dlx0 + dlx1) * 0.5f; + p1->dmy = (dly0 + dly1) * 0.5f; + dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; + if (dmr2 > 0.000001f) { + float s2 = 1.0f / dmr2; + if (s2 > 600.0f) { + s2 = 600.0f; + } + p1->dmx *= s2; + p1->dmy *= s2; + } + + // Clear flags, but keep the corner. + p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; + + // Keep track of left turns. + cross = p1->dx * p0->dy - p0->dx * p1->dy; + if (cross > 0.0f) + p1->flags |= NSVG_PT_LEFT; + + // Check to see if the corner needs to be beveled. + if (p1->flags & NSVG_PT_CORNER) { + if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { + p1->flags |= NSVG_PT_BEVEL; + } + } + + p0 = p1++; + } +} + +static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j, closed; + NSVGpath* path; + NSVGpoint* p0, *p1; + float miterLimit = shape->miterLimit; + int lineJoin = shape->strokeLineJoin; + int lineCap = shape->strokeLineCap; + float lineWidth = shape->strokeWidth * scale; + + for (path = shape->paths; path != NULL; path = path->next) { + // Flatten path + r->npoints = 0; + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER); + } + if (r->npoints < 2) + continue; + + closed = path->closed; + + // If the first and last points are the same, remove the last, mark as closed path. + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { + r->npoints--; + p0 = &r->points[r->npoints-1]; + closed = 1; + } + + if (shape->strokeDashCount > 0) { + int idash = 0, dashState = 1; + float totalDist = 0, dashLen, allDashLen, dashOffset; + NSVGpoint cur; + + if (closed) + nsvg__appendPathPoint(r, r->points[0]); + + // Duplicate points -> points2. + nsvg__duplicatePoints(r); + + r->npoints = 0; + cur = r->points2[0]; + nsvg__appendPathPoint(r, cur); + + // Figure out dash offset. + allDashLen = 0; + for (j = 0; j < shape->strokeDashCount; j++) + allDashLen += shape->strokeDashArray[j]; + if (shape->strokeDashCount & 1) + allDashLen *= 2.0f; + // Find location inside pattern + dashOffset = fmodf(shape->strokeDashOffset, allDashLen); + if (dashOffset < 0.0f) + dashOffset += allDashLen; + + while (dashOffset > shape->strokeDashArray[idash]) { + dashOffset -= shape->strokeDashArray[idash]; + idash = (idash + 1) % shape->strokeDashCount; + } + dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; + + for (j = 1; j < r->npoints2; ) { + float dx = r->points2[j].x - cur.x; + float dy = r->points2[j].y - cur.y; + float dist = sqrtf(dx*dx + dy*dy); + + if ((totalDist + dist) > dashLen) { + // Calculate intermediate point + float d = (dashLen - totalDist) / dist; + float x = cur.x + dx * d; + float y = cur.y + dy * d; + nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); + + // Stroke + if (r->npoints > 1 && dashState) { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } + // Advance dash pattern + dashState = !dashState; + idash = (idash+1) % shape->strokeDashCount; + dashLen = shape->strokeDashArray[idash] * scale; + // Restart + cur.x = x; + cur.y = y; + cur.flags = NSVG_PT_CORNER; + totalDist = 0.0f; + r->npoints = 0; + nsvg__appendPathPoint(r, cur); + } else { + totalDist += dist; + cur = r->points2[j]; + nsvg__appendPathPoint(r, cur); + j++; + } + } + // Stroke any leftover path + if (r->npoints > 1 && dashState) + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } else { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); + } + } +} + +static int nsvg__cmpEdge(const void *p, const void *q) +{ + const NSVGedge* a = (const NSVGedge*)p; + const NSVGedge* b = (const NSVGedge*)q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + + +static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) +{ + NSVGactiveEdge* z; + + if (r->freelist != NULL) { + // Restore from freelist. + z = r->freelist; + r->freelist = z->next; + } else { + // Alloc new edge. + z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); + if (z == NULL) return NULL; + } + + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); +// STBTT_assert(e->y0 <= start_point); + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); + else + z->dx = (int)floorf(NSVG__FIX * dxdy); + z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); +// z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->dir = e->dir; + + return z; +} + +static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) +{ + z->next = r->freelist; + r->freelist = z; +} + +static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) +{ + int i = x0 >> NSVG__FIXSHIFT; + int j = x1 >> NSVG__FIXSHIFT; + if (i < *xmin) *xmin = i; + if (j > *xmax) *xmax = j; + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = (unsigned char)(scanline[i] + maxWeight); + } + } +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) +{ + // non-zero winding fill + int x0 = 0, w = 0; + + if (fillRule == NSVG_FILLRULE_NONZERO) { + // Non-zero + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->dir; + } else { + int x1 = e->x; w += e->dir; + // if we went to zero, we need to draw + if (w == 0) + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } else if (fillRule == NSVG_FILLRULE_EVENODD) { + // Even-odd + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w = 1; + } else { + int x1 = e->x; w = 0; + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } +} + +static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } + +static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24); +} + +static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; + int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; + int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; + int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static unsigned int nsvg__applyOpacity(unsigned int c, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (c) & 0xff; + int g = (c>>8) & 0xff; + int b = (c>>16) & 0xff; + int a = (((c>>24) & 0xff)*iu) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static inline int nsvg__div255(int x) +{ + return ((x+1) * 257) >> 16; +} + +static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, + float tx, float ty, float scale, NSVGcachedPaint* cache) +{ + + if (cache->type == NSVG_PAINT_COLOR) { + int i, cr, cg, cb, ca; + cr = cache->colors[0] & 0xff; + cg = (cache->colors[0] >> 8) & 0xff; + cb = (cache->colors[0] >> 16) & 0xff; + ca = (cache->colors[0] >> 24) & 0xff; + + for (i = 0; i < count; i++) { + int r,g,b; + int a = nsvg__div255((int)cover[0] * ca); + int ia = 255 - a; + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + } + } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + float fx, fy, dx, gy; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gy = fx*t[1] + fy*t[3] + t[5]; + c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + // TODO: focus (fx,fy) + float fx, fy, dx, gx, gy, gd; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gx = fx*t[0] + fy*t[2] + t[4]; + gy = fx*t[1] + fy*t[3] + t[5]; + gd = sqrtf(gx*gx + gy*gy); + c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } +} + +static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) +{ + NSVGactiveEdge *active = NULL; + int y, s; + int e = 0; + int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline + int xmin, xmax; + + for (y = 0; y < r->height; y++) { + memset(r->scanline, 0, r->width); + xmin = r->width; + xmax = 0; + for (s = 0; s < NSVG__SUBSAMPLES; ++s) { + // find center of pixel for this scanline + float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; + NSVGactiveEdge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + NSVGactiveEdge *z = *step; + if (z->ey <= scany) { + *step = z->next; // delete from list +// NSVG__assert(z->valid); + nsvg__freeActive(r, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for (;;) { + int changed = 0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + NSVGactiveEdge* t = *step; + NSVGactiveEdge* q = t->next; + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e < r->nedges && r->edges[e].y0 <= scany) { + if (r->edges[e].y1 > scany) { + NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); + if (z == NULL) break; + // find insertion point + if (active == NULL) { + active = z; + } else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + NSVGactiveEdge* p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + e++; + } + + // now process all active edges in non-zero fashion + if (active != NULL) + nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); + } + // Blit + if (xmin < 0) xmin = 0; + if (xmax > r->width-1) xmax = r->width-1; + if (xmin <= xmax) { + nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); + } + } + +} + +static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) +{ + int x,y; + + // Unpremultiply + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = row[0], g = row[1], b = row[2], a = row[3]; + if (a != 0) { + row[0] = (unsigned char)(r*255/a); + row[1] = (unsigned char)(g*255/a); + row[2] = (unsigned char)(b*255/a); + } + row += 4; + } + } + + // Defringe + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = 0, g = 0, b = 0, a = row[3], n = 0; + if (a == 0) { + if (x-1 > 0 && row[-1] != 0) { + r += row[-4]; + g += row[-3]; + b += row[-2]; + n++; + } + if (x+1 < w && row[7] != 0) { + r += row[4]; + g += row[5]; + b += row[6]; + n++; + } + if (y-1 > 0 && row[-stride+3] != 0) { + r += row[-stride]; + g += row[-stride+1]; + b += row[-stride+2]; + n++; + } + if (y+1 < h && row[stride+3] != 0) { + r += row[stride]; + g += row[stride+1]; + b += row[stride+2]; + n++; + } + if (n > 0) { + row[0] = (unsigned char)(r/n); + row[1] = (unsigned char)(g/n); + row[2] = (unsigned char)(b/n); + } + } + row += 4; + } + } +} + + +static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) +{ + int i, j; + NSVGgradient* grad; + + cache->type = paint->type; + + if (paint->type == NSVG_PAINT_COLOR) { + cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); + return; + } + + grad = paint->gradient; + + cache->spread = grad->spread; + memcpy(cache->xform, grad->xform, sizeof(float)*6); + + if (grad->nstops == 0) { + for (i = 0; i < 256; i++) + cache->colors[i] = 0; + } if (grad->nstops == 1) { + for (i = 0; i < 256; i++) + cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); + } else { + unsigned int ca, cb = 0; + float ua, ub, du, u; + int ia, ib, count; + + ca = nsvg__applyOpacity(grad->stops[0].color, opacity); + ua = nsvg__clampf(grad->stops[0].offset, 0, 1); + ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + for (i = 0; i < ia; i++) { + cache->colors[i] = ca; + } + + for (i = 0; i < grad->nstops-1; i++) { + ca = nsvg__applyOpacity(grad->stops[i].color, opacity); + cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); + ua = nsvg__clampf(grad->stops[i].offset, 0, 1); + ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + count = ib - ia; + if (count <= 0) continue; + u = 0; + du = 1.0f / (float)count; + for (j = 0; j < count; j++) { + cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); + u += du; + } + } + + for (i = ib; i < 256; i++) + cache->colors[i] = cb; + } + +} + +/* +static void dumpEdges(NSVGrasterizer* r, const char* name) +{ + float xmin = 0, xmax = 0, ymin = 0, ymax = 0; + NSVGedge *e = NULL; + int i; + if (r->nedges == 0) return; + FILE* fp = fopen(name, "w"); + if (fp == NULL) return; + + xmin = xmax = r->edges[0].x0; + ymin = ymax = r->edges[0].y0; + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + xmin = nsvg__minf(xmin, e->x0); + xmin = nsvg__minf(xmin, e->x1); + xmax = nsvg__maxf(xmax, e->x0); + xmax = nsvg__maxf(xmax, e->x1); + ymin = nsvg__minf(ymin, e->y0); + ymin = nsvg__minf(ymin, e->y1); + ymax = nsvg__maxf(ymax, e->y0); + ymax = nsvg__maxf(ymax, e->y1); + } + + fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); + + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); + } + + for (i = 0; i < r->npoints; i++) { + if (i+1 < r->npoints) + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); + } + + fprintf(fp, ""); + fclose(fp); +} +*/ + +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride) +{ + NSVGshape *shape = NULL; + NSVGedge *e = NULL; + NSVGcachedPaint cache; + int i; + + r->bitmap = dst; + r->width = w; + r->height = h; + r->stride = stride; + + if (w > r->cscanline) { + r->cscanline = w; + r->scanline = (unsigned char*)realloc(r->scanline, w); + if (r->scanline == NULL) return; + } + + for (i = 0; i < h; i++) + memset(&dst[i*stride], 0, w*4); + + for (shape = image->shapes; shape != NULL; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) + continue; + + if (shape->fill.type != NSVG_PAINT_NONE) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShape(r, shape, scale); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + if (r->nedges != 0) + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->fill, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); + } + if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShapeStroke(r, shape, scale); + +// dumpEdges(r, "edge.svg"); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + if (r->nedges != 0) + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->stroke, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); + } + } + + nsvg__unpremultiplyAlpha(dst, w, h, stride); + + r->bitmap = NULL; + r->width = 0; + r->height = 0; + r->stride = 0; +} + +#endif // NANOSVGRAST_IMPLEMENTATION + +#endif // NANOSVGRAST_H diff --git a/src/raylib.h b/src/raylib.h index 6f67d839e..198c6c497 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1245,6 +1245,8 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // 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 *fileName, int width, int height); // Load image from SVG file data with specified size +RLAPI Image LoadImageSvgFromString(const char *string, int width, int height); // Load an image from a SVG string with custom size RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) 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 diff --git a/src/rtextures.c b/src/rtextures.c index 566371112..09202ef77 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -215,6 +215,14 @@ #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()] +#if defined(SUPPORT_FILEFORMAT_SVG) + #define NANOSVG_IMPLEMENTATION // Expands implementation + #include "external/nanosvg.h" + + #define NANOSVGRAST_IMPLEMENTATION + #include "external/nanosvgrast.h" +#endif + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -310,6 +318,72 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int return image; } +// Load an image from SVG file data with a custom size +Image LoadImageSvg(const char *fileName, int width, int height) +{ + Image image = { 0 }; + + unsigned int dataSize = 0; + unsigned char *string = LoadFileData(fileName, &dataSize); + + if (string != NULL) + { + image = LoadImageSvgFromString(string, width, height); + RL_FREE(string); + } + + return image; +} + +// Load an image from a SVG string with custom size +Image LoadImageSvgFromString(const char *string, int width, int height) +{ + Image image = { 0 }; + + if (string != NULL) + { + struct NSVGimage *svgImage = nsvgParse(string, "px", 96.0f); + + // Allocate memory for image + unsigned char *img = malloc(width*height*4); + + // Calculate scales for both the width and the height + const float scaleWidth = width/svgImage->width; + const float scaleHeight = height/svgImage->height; + + // Set the largest of the 2 scales to be the scale to use + const float scale = (scaleHeight > scaleWidth) ? scaleWidth : scaleHeight; + + int offsetX = 0; + int offsetY = 0; + + if (scaleHeight > scaleWidth) + { + offsetY = (height - svgImage->height*scale) / 2; + } + else + { + offsetX = (width - svgImage->width*scale) / 2; + } + + // Rasterize + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + nsvgRasterize(rast, svgImage, (int)offsetX, (int)offsetY, scale, img, width, height, width*4); + + // Populate image struct with all data + image.data = img; + image.width = width; + image.height = height; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + // Delete + nsvgDelete(svgImage); + } + + return image; +} + // Load animated image data // - Image.data buffer includes all frames: [image#0][image#1][image#2][...] // - Number of frames is returned through 'frames' parameter @@ -470,6 +544,27 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i { image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } +#endif +#if defined(SUPPORT_FILEFORMAT_SVG) + else if (strcmp(fileType, ".svg") == 0) + { + if (fileData != NULL) + { + // Creating a duplicate svg to read sizes from due to nsvgParse modifiying the string buffer. + unsigned char *duplicate = (unsigned char*)RL_MALLOC(dataSize); + memcpy(duplicate, fileData, dataSize); + struct NSVGimage *svgImage = nsvgParse(duplicate, "px", 96.0f); + RL_FREE(duplicate); + + const int width = (int)svgImage->width; + const int height = (int)svgImage->height; + // Delete + nsvgDelete(svgImage); + + + image = LoadImageSvgFromString(fileData, width, height); + } + } #endif else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported"); From d6f3891009bbd582ceb4728682ffb6e1a89bd721 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 13:16:44 +0200 Subject: [PATCH 0602/1710] REVIEWED: `LoadImageSvg()` --- src/raylib.h | 3 +- src/rtextures.c | 136 +++++++++++++++++++++++------------------------- 2 files changed, 66 insertions(+), 73 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 198c6c497..4f3e35380 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1245,8 +1245,7 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // 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 *fileName, int width, int height); // Load image from SVG file data with specified size -RLAPI Image LoadImageSvgFromString(const char *string, int width, int height); // Load an image from a SVG string with custom size +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 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 diff --git a/src/rtextures.c b/src/rtextures.c index 09202ef77..6b7875b08 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -318,67 +318,60 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int return image; } -// Load an image from SVG file data with a custom size -Image LoadImageSvg(const char *fileName, int width, int height) +// Load an image from a SVG file or string with custom size +Image LoadImageSvg(const char *fileNameOrString, int width, int height) { Image image = { 0 }; - - unsigned int dataSize = 0; - unsigned char *string = LoadFileData(fileName, &dataSize); - - if (string != NULL) + bool isSvgStringValid = false; + + // TODO: Validate fileName or string + if (fileNameOrString != NULL) { - image = LoadImageSvgFromString(string, width, height); - RL_FREE(string); - } - - return image; -} - -// Load an image from a SVG string with custom size -Image LoadImageSvgFromString(const char *string, int width, int height) -{ - Image image = { 0 }; - - if (string != NULL) - { - struct NSVGimage *svgImage = nsvgParse(string, "px", 96.0f); - - // Allocate memory for image - unsigned char *img = malloc(width*height*4); - - // Calculate scales for both the width and the height - const float scaleWidth = width/svgImage->width; - const float scaleHeight = height/svgImage->height; - - // Set the largest of the 2 scales to be the scale to use - const float scale = (scaleHeight > scaleWidth) ? scaleWidth : scaleHeight; - - int offsetX = 0; - int offsetY = 0; - - if (scaleHeight > scaleWidth) + if (FileExists(fileNameOrString)) { - offsetY = (height - svgImage->height*scale) / 2; + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileNameOrString, &dataSize); + isSvgStringValid = true; } else { - offsetX = (width - svgImage->width*scale) / 2; + // TODO: Validate it's a valid SVG string + isSvgStringValid = true; } - // Rasterize - struct NSVGrasterizer* rast = nsvgCreateRasterizer(); - nsvgRasterize(rast, svgImage, (int)offsetX, (int)offsetY, scale, img, width, height, width*4); + if (isSvgStringValid != NULL) + { + struct NSVGimage *svgImage = nsvgParse(fileNameOrString, "px", 96.0f); + + unsigned char *img = RL_MALLOC(width*height*4); - // Populate image struct with all data - image.data = img; - image.width = width; - image.height = height; - image.mipmaps = 1; - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + // Calculate scales for both the width and the height + const float scaleWidth = width/svgImage->width; + const float scaleHeight = height/svgImage->height; - // Delete - nsvgDelete(svgImage); + // Set the largest of the 2 scales to be the scale to use + const float scale = (scaleHeight > scaleWidth)? scaleWidth : scaleHeight; + + int offsetX = 0; + int offsetY = 0; + + if (scaleHeight > scaleWidth) offsetY = (height - svgImage->height*scale) / 2; + else offsetX = (width - svgImage->width*scale) / 2; + + // Rasterize + struct NSVGrasterizer *rast = nsvgCreateRasterizer(); + nsvgRasterize(rast, svgImage, (int)offsetX, (int)offsetY, scale, img, width, height, width*4); + + // Populate image struct with all data + image.data = img; + image.width = width; + image.height = height; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + // Free used memory + nsvgDelete(svgImage); + } } return image; @@ -515,6 +508,28 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i image.mipmaps = 1; } #endif +#if defined(SUPPORT_FILEFORMAT_SVG) + else if ((strcmp(fileType, ".svg") == 0) || (strcmp(fileType, ".SVG") == 0)) + { + // TODO: Validate fileData as valid SVG string data + + struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); + unsigned char *img = RL_MALLOC(svgImage->width*svgImage->height*4); + + // Rasterize + struct NSVGrasterizer *rast = nsvgCreateRasterizer(); + nsvgRasterize(rast, svgImage, 0, 0, 1.0f, img, svgImage->width, svgImage->height, svgImage->width*4); + + // Populate image struct with all data + image.data = img; + image.width = svgImage->width; + image.height = svgImage->height; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + nsvgDelete(svgImage); + } +#endif #if defined(SUPPORT_FILEFORMAT_DDS) else if ((strcmp(fileType, ".dds") == 0) || (strcmp(fileType, ".DDS") == 0)) { @@ -544,27 +559,6 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i { image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } -#endif -#if defined(SUPPORT_FILEFORMAT_SVG) - else if (strcmp(fileType, ".svg") == 0) - { - if (fileData != NULL) - { - // Creating a duplicate svg to read sizes from due to nsvgParse modifiying the string buffer. - unsigned char *duplicate = (unsigned char*)RL_MALLOC(dataSize); - memcpy(duplicate, fileData, dataSize); - struct NSVGimage *svgImage = nsvgParse(duplicate, "px", 96.0f); - RL_FREE(duplicate); - - const int width = (int)svgImage->width; - const int height = (int)svgImage->height; - // Delete - nsvgDelete(svgImage); - - - image = LoadImageSvgFromString(fileData, width, height); - } - } #endif else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported"); From 67a693fc5be8cc8bdde3cf2e83dfffd031e84667 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 19:20:56 +0200 Subject: [PATCH 0603/1710] REVIEWED: `LoadImageSvg()` --- src/rtextures.c | 79 +++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 6b7875b08..3092071fd 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -324,24 +324,35 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) Image image = { 0 }; bool isSvgStringValid = false; - // TODO: Validate fileName or string + // Validate fileName or string if (fileNameOrString != NULL) { + int dataSize = 0; + unsigned char *fileData = NULL; + if (FileExists(fileNameOrString)) { - int dataSize = 0; - unsigned char *fileData = LoadFileData(fileNameOrString, &dataSize); + fileData = LoadFileData(fileNameOrString, &dataSize); isSvgStringValid = true; } else { - // TODO: Validate it's a valid SVG string - isSvgStringValid = true; + // Validate fileData as valid SVG string data + // + if ((fileNameOrString != NULL) && + (fileNameOrString[0] == '<') && + (fileNameOrString[1] == 's') && + (fileNameOrString[2] == 'v') && + (fileNameOrString[3] == 'g')) + { + fileData = fileNameOrString; + isSvgStringValid = true; + } } - if (isSvgStringValid != NULL) + if (isSvgStringValid) { - struct NSVGimage *svgImage = nsvgParse(fileNameOrString, "px", 96.0f); + struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); unsigned char *img = RL_MALLOC(width*height*4); @@ -372,6 +383,8 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) // Free used memory nsvgDelete(svgImage); } + + if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); } return image; @@ -500,34 +513,44 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #if defined(SUPPORT_FILEFORMAT_QOI) else if ((strcmp(fileType, ".qoi") == 0) || (strcmp(fileType, ".QOI") == 0)) { - qoi_desc desc = { 0 }; - image.data = qoi_decode(fileData, dataSize, &desc, 4); - image.width = desc.width; - image.height = desc.height; - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - image.mipmaps = 1; + if (fileData != NULL) + { + qoi_desc desc = { 0 }; + image.data = qoi_decode(fileData, dataSize, &desc, 4); + image.width = desc.width; + image.height = desc.height; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + image.mipmaps = 1; + } } #endif #if defined(SUPPORT_FILEFORMAT_SVG) else if ((strcmp(fileType, ".svg") == 0) || (strcmp(fileType, ".SVG") == 0)) { - // TODO: Validate fileData as valid SVG string data - - struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); - unsigned char *img = RL_MALLOC(svgImage->width*svgImage->height*4); - - // Rasterize - struct NSVGrasterizer *rast = nsvgCreateRasterizer(); - nsvgRasterize(rast, svgImage, 0, 0, 1.0f, img, svgImage->width, svgImage->height, svgImage->width*4); + // Validate fileData as valid SVG string data + // + if ((fileData != NULL) && + (fileData[0] == '<') && + (fileData[1] == 's') && + (fileData[2] == 'v') && + (fileData[3] == 'g')) + { + struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); + unsigned char *img = RL_MALLOC(svgImage->width*svgImage->height*4); - // Populate image struct with all data - image.data = img; - image.width = svgImage->width; - image.height = svgImage->height; - image.mipmaps = 1; - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + // Rasterize + struct NSVGrasterizer *rast = nsvgCreateRasterizer(); + nsvgRasterize(rast, svgImage, 0, 0, 1.0f, img, svgImage->width, svgImage->height, svgImage->width*4); - nsvgDelete(svgImage); + // Populate image struct with all data + image.data = img; + image.width = svgImage->width; + image.height = svgImage->height; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + nsvgDelete(svgImage); + } } #endif #if defined(SUPPORT_FILEFORMAT_DDS) From 8bd6bb622c53add567564fe567dcc1a23c7f7a3c Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sat, 2 Sep 2023 17:38:10 -0300 Subject: [PATCH 0604/1710] Add SUPPORT_FILEFORMAT_SVG to cmake (#3284) --- CMakeOptions.txt | 1 + cmake/CompileDefinitions.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 83faa5637..260522340 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -71,6 +71,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_QOI "Support loading QOI as textures" cmake_dependent_option(SUPPORT_FILEFORMAT_PSD "Support loading PSD as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PKM "Support loading PKM as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_SVG "Support loading SVG as textures" ON CUSTOMIZE_BUILD ON) # rtext.c cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake index 1458e2f3c..68c53d7a1 100644 --- a/cmake/CompileDefinitions.cmake +++ b/cmake/CompileDefinitions.cmake @@ -49,6 +49,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_FILEFORMAT_PSD) define_if("raylib" SUPPORT_FILEFORMAT_PKM) define_if("raylib" SUPPORT_FILEFORMAT_PVR) + define_if("raylib" SUPPORT_FILEFORMAT_SVG) define_if("raylib" SUPPORT_FILEFORMAT_FNT) define_if("raylib" SUPPORT_FILEFORMAT_TTF) define_if("raylib" SUPPORT_TEXT_MANIPULATION) From a69d4014339ed77ce531858503cf6b96c4cf708b Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sat, 2 Sep 2023 17:50:59 -0300 Subject: [PATCH 0605/1710] Fix examples/textures/textures_fog_of_war.c help instructions (#3285) --- examples/textures/textures_fog_of_war.c | 3 ++- examples/textures/textures_fog_of_war.png | Bin 38477 -> 37736 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/textures/textures_fog_of_war.c b/examples/textures/textures_fog_of_war.c index 433f364a6..516055aeb 100644 --- a/examples/textures/textures_fog_of_war.c +++ b/examples/textures/textures_fog_of_war.c @@ -134,7 +134,8 @@ int main(void) (Vector2){ 0, 0 }, 0.0f, WHITE); // Draw player current tile - DrawText(TextFormat("Current tile: [%i,%i]", playerTileX, playerTileY), 10, 10, 20, LIME); + DrawText(TextFormat("Current tile: [%i,%i]", playerTileX, playerTileY), 10, 10, 20, RAYWHITE); + DrawText("ARROW KEYS to move", 10, screenHeight-25, 20, RAYWHITE); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/textures/textures_fog_of_war.png b/examples/textures/textures_fog_of_war.png index 6a91316ee53a91cc63262e31e3b1bf615c6220de..01563c3906f256433bc11af25d2a2a95517bd22b 100644 GIT binary patch literal 37736 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 O&$r*q2{_}&PyRpTk3qHo literal 38477 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 Date: Sun, 3 Sep 2023 06:03:00 -0300 Subject: [PATCH 0606/1710] Fix examples/textures/textures_image_rotate.c help instructions (#3286) --- examples/textures/textures_image_rotate.c | 4 +++- examples/textures/textures_image_rotate.png | Bin 24060 -> 35476 bytes 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/textures/textures_image_rotate.c b/examples/textures/textures_image_rotate.c index a590e1e0a..e5c735732 100644 --- a/examples/textures/textures_image_rotate.c +++ b/examples/textures/textures_image_rotate.c @@ -64,6 +64,8 @@ int main(void) DrawTexture(textures[currentTexture], screenWidth/2 - textures[currentTexture].width/2, screenHeight/2 - textures[currentTexture].height/2, WHITE); + DrawText("Press LEFT MOUSE BUTTON to rotate the image clockwise", 250, 420, 10, DARKGRAY); + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -76,4 +78,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/textures/textures_image_rotate.png b/examples/textures/textures_image_rotate.png index 64bbc54e496f4587e2609a58cea3ceee637123d1..862612e5ababd8a7c0034d173983f0f5c2931311 100644 GIT binary patch literal 35476 zcmeFZdpy(q|3AK&!{#`LoDZAH;R=m83v+0Q5M8BG%^{~G

      -PYomGl#oMQSGlHC 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 leEhsT8j7@l^pb#uA|Qewy(v{dKsuo+Eh0^N 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 From 8a82e17a794ac5ac8c073f9a96cf5e4ae0c1ebf2 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Sep 2023 18:43:03 +0200 Subject: [PATCH 0607/1710] Update rtextures.c --- src/rtextures.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index 3092071fd..4e2fdbd91 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -324,6 +324,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) Image image = { 0 }; bool isSvgStringValid = false; +#if defined(SUPPORT_FILEFORMAT_SVG) // Validate fileName or string if (fileNameOrString != NULL) { @@ -386,6 +387,9 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); } +#else + TRACELOG(LOG_WARNING, "SVG image support not enabled, image can not be loaded"); +#endif return image; } From bdda1efd44d7a1dce1ce5ad03f7de14370642807 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Sep 2023 18:43:25 +0200 Subject: [PATCH 0608/1710] Fix #3247 --- src/rcore.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 912f85c0d..67907c367 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -887,9 +887,19 @@ void InitWindow(int width, int height, const char *title) // 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]; - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes + 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 #else #if defined(SUPPORT_MODULE_RSHAPES) From c104a975904f23d011cb6072f98945b8d38eff17 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Sep 2023 18:51:44 +0200 Subject: [PATCH 0609/1710] Update config.h --- src/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index 2c7d5e2d0..370837610 100644 --- a/src/config.h +++ b/src/config.h @@ -156,7 +156,7 @@ //#define SUPPORT_FILEFORMAT_ASTC 1 //#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PVR 1 -#define SUPPORT_FILEFORMAT_SVG 1 +//#define SUPPORT_FILEFORMAT_SVG 1 // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi) #define SUPPORT_IMAGE_EXPORT 1 From a316f9e7fc7f8e06852a40544e57f89018672ee4 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 5 Sep 2023 05:02:25 -0400 Subject: [PATCH 0610/1710] Disable UBSAN in zig builds. (#3292) Zig debug builds automatically enable ubsan. As the fix for #1891 had to be reverted, debug builds using zig will crash like so: ``` Illegal instruction at address 0x3237d2 raylib/src/rlgl.h:3690:91: 0x3237d2 in rlDrawVertexArrayElements (/home/rcorre/src/raylib-zig-template/raylib/src/rcore.c) glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); ``` This disables UBSAN when using zig to build raylib. --- src/build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build.zig b/src/build.zig index 27250f5ff..09d954bf5 100644 --- a/src/build.zig +++ b/src/build.zig @@ -6,6 +6,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built "-std=gnu99", "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; const raylib = b.addStaticLibrary(.{ From 97a8fe1e15b0904231373dbe6a2918ecd0c5068d Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 5 Sep 2023 18:03:32 +0900 Subject: [PATCH 0611/1710] Update README.md (#3290) specially -> especially --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90de36ca5..bff6980b0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **raylib is a simple and easy-to-use library to enjoy videogames programming.** -raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's specially well suited for prototyping, tooling, graphical applications, embedded systems and education. +raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's especially well suited for prototyping, tooling, graphical applications, embedded systems and education. *NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.* From 76fe16259d6cf8711d771ffb66b21e1673efa785 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Tue, 5 Sep 2023 06:03:59 -0300 Subject: [PATCH 0612/1710] Update cmake SUPPORT_FILEFORMAT_SVG default value (#3291) --- CMakeOptions.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 260522340..799530951 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -71,7 +71,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_QOI "Support loading QOI as textures" cmake_dependent_option(SUPPORT_FILEFORMAT_PSD "Support loading PSD as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PKM "Support loading PKM as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" ${OFF} CUSTOMIZE_BUILD OFF) -cmake_dependent_option(SUPPORT_FILEFORMAT_SVG "Support loading SVG as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_SVG "Support loading SVG as textures" ${OFF} CUSTOMIZE_BUILD OFF) # rtext.c cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) From 9cce5a93fffe6e297c004480b9b941d70bf4cd9e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Sep 2023 20:04:21 +0200 Subject: [PATCH 0613/1710] Fix #3293 --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 5ca97d06d..04d7a2a14 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1075,7 +1075,7 @@ void DrawFPS(int posX, int posY) if ((fps < 30) && (fps >= 15)) color = ORANGE; // Warning FPS else if (fps < 15) color = RED; // Low FPS - DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color); + DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, color); } // Draw text (using default font) From d7debba2224b638a3429fcceffeade28dfa7a4cd Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Sep 2023 23:16:31 +0200 Subject: [PATCH 0614/1710] Mouse offset and scaling must be considered also on web! --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 67907c367..138d3e4ad 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4051,7 +4051,7 @@ Vector2 GetMousePosition(void) { Vector2 position = { 0 }; -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) +#if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) position = GetTouchPosition(0); #else position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; From b0c6972e5cadb7c9e6680b8674fe378952efe668 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Sep 2023 23:33:29 +0200 Subject: [PATCH 0615/1710] Update rcore.c --- src/rcore.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 138d3e4ad..c93038239 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -49,9 +49,6 @@ * #define SUPPORT_MOUSE_GESTURES * Mouse gestures are directly mapped like touches and processed by gestures system. * -* #define SUPPORT_TOUCH_AS_MOUSE -* Touch input and mouse input are shared. Mouse functions also return touch information. -* * #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 From 8cf76ec113a049d60aa5d221543d35b8ddf25dcc Mon Sep 17 00:00:00 2001 From: SuperUserNameMan Date: Thu, 7 Sep 2023 17:35:02 +0200 Subject: [PATCH 0616/1710] Update Makefile : clean raygui.c & physac.c (#3296) --- src/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index e9b68cd93..ed7594938 100644 --- a/src/Makefile +++ b/src/Makefile @@ -827,7 +827,7 @@ clean: clean_shell_$(PLATFORM_SHELL) @echo "removed all generated files!" clean_shell_sh: - rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* + rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c physac.c ifeq ($(PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif @@ -839,4 +839,6 @@ clean_shell_cmd: cd $(RAYLIB_RELEASE_PATH) & \ del lib$(RAYLIB_LIB_NAME).a /s & \ del lib$(RAYLIB_LIB_NAME)dll.a /s & \ - del $(RAYLIB_LIB_NAME).dll /s + del $(RAYLIB_LIB_NAME).dll /s & \ + del raygui.c /s & \ + del physac.c /s From 18e9784c6d2f05ea82d5be0b86913b870e4fcf88 Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:42:28 -0400 Subject: [PATCH 0617/1710] Remove PLATFORM_RPI (#3232) * Remove PLATFORM_RPI * remove build artifacts --------- Co-authored-by: MichaelFiber Co-authored-by: Ray --- cmake/LibraryConfigurations.cmake | 13 -- examples/Makefile | 50 +------ examples/Makefile.Web | 50 +------ examples/core/core_basic_window_web.c | 2 +- examples/core/core_input_gamepad.c | 9 +- examples/core/core_vr_simulator.c | 2 +- examples/models/models_skybox.c | 2 +- examples/others/raylib_opengl_interop.c | 2 +- examples/shaders/shaders_basic_lighting.c | 2 +- examples/shaders/shaders_custom_uniform.c | 2 +- examples/shaders/shaders_eratosthenes.c | 2 +- examples/shaders/shaders_fog.c | 2 +- examples/shaders/shaders_hot_reloading.c | 2 +- examples/shaders/shaders_hybrid_render.c | 2 +- examples/shaders/shaders_julia_set.c | 2 +- examples/shaders/shaders_lightmap.c | 2 +- examples/shaders/shaders_mesh_instancing.c | 2 +- examples/shaders/shaders_model_shader.c | 2 +- examples/shaders/shaders_multi_sample2d.c | 2 +- examples/shaders/shaders_palette_switch.c | 2 +- examples/shaders/shaders_postprocessing.c | 2 +- examples/shaders/shaders_raymarching.c | 2 +- examples/shaders/shaders_shapes_textures.c | 2 +- examples/shaders/shaders_simple_mask.c | 2 +- examples/shaders/shaders_spotlight.c | 2 +- examples/shaders/shaders_texture_drawing.c | 2 +- examples/shaders/shaders_texture_outline.c | 2 +- examples/shaders/shaders_texture_waves.c | 2 +- examples/shaders/shaders_write_depth.c | 2 +- examples/text/text_font_sdf.c | 2 +- projects/CMake/core_basic_window.c | 2 +- projects/VSCode/Makefile | 2 +- src/Makefile | 55 +------- src/rcore.c | 150 +++++---------------- src/rlgl.h | 2 +- 35 files changed, 70 insertions(+), 315 deletions(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index d12df3a00..1debfeb42 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -73,19 +73,6 @@ elseif (${PLATFORM} MATCHES "Android") find_library(OPENGL_LIBRARY OpenGL) set(LIBS_PRIVATE m log android EGL GLESv2 OpenSLES atomic c) -elseif (${PLATFORM} MATCHES "Raspberry Pi") - set(PLATFORM_CPP "PLATFORM_RPI") - set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") - - add_definitions(-D_DEFAULT_SOURCE) - - find_library(GLESV2 brcmGLESv2 HINTS /opt/vc/lib) - find_library(EGL brcmEGL HINTS /opt/vc/lib) - find_library(BCMHOST bcm_host HINTS /opt/vc/lib) - include_directories(/opt/vc/include /opt/vc/include/interface/vmcs_host/linux /opt/vc/include/interface/vcos/pthreads) - link_directories(/opt/vc/lib) - set(LIBS_PRIVATE ${GLESV2} ${EGL} ${BCMHOST} pthread rt m dl) - elseif ("${PLATFORM}" MATCHES "DRM") set(PLATFORM_CPP "PLATFORM_DRM") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") diff --git a/examples/Makefile b/examples/Makefile index 4ba4f1720..338fe168c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -25,7 +25,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables @@ -58,15 +58,6 @@ BUILD_WEB_HEAP_SIZE ?= 134217728 BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources -# Use cross-compiler for PLATFORM_RPI -ifeq ($(PLATFORM),PLATFORM_RPI) - USE_RPI_CROSS_COMPILER ?= FALSE - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry - RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/arm-linux-gnueabihf/sysroot - endif -endif - # Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! @@ -95,12 +86,6 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) @@ -118,9 +103,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif # Default path for raylib on Raspberry Pi -ifeq ($(PLATFORM),PLATFORM_RPI) - RAYLIB_PATH ?= /home/pi/raylib -endif ifeq ($(PLATFORM),PLATFORM_DRM) RAYLIB_PATH ?= /home/pi/raylib endif @@ -154,13 +136,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - # Define RPI cross-compiler - #CC = armv6j-hardfloat-linux-gnueabi-gcc - CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc - endif -endif ifeq ($(PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler # WARNING: To compile to HTML5, code must be redesigned @@ -231,9 +206,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - CFLAGS += -std=gnu99 -endif ifeq ($(PLATFORM),PLATFORM_DRM) CFLAGS += -std=gnu99 -DEGL_NO_X11 endif @@ -252,11 +224,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vmcs_host/linux - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vcos/pthreads -endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm endif @@ -328,9 +295,6 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # logic to a self contained function: UpdateDrawFrame(), check core_basic_window_web.c for reference. endif -ifeq ($(PLATFORM),PLATFORM_RPI) - LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -endif # Define libraries required on linking: LDLIBS # NOTE: To link libraries (lib.so or lib.a), use -l @@ -381,14 +345,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - # Libraries for Raspberry Pi compiling - # NOTE: Required packages: libasound2-dev (ALSA) - LDLIBS = -lraylib -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl -latomic - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - LDLIBS += -lvchiq_arm -lvcos - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) @@ -593,10 +549,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) rm -f *.o endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - find . -type f -executable -delete - rm -fv *.o -endif ifeq ($(PLATFORM),PLATFORM_DRM) find . -type f -executable -delete rm -fv *.o diff --git a/examples/Makefile.Web b/examples/Makefile.Web index ccb4550f9..518fab918 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -25,7 +25,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_WEB # Define required raylib variables @@ -51,15 +51,6 @@ USE_EXTERNAL_GLFW ?= FALSE # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE -# Use cross-compiler for PLATFORM_RPI -ifeq ($(PLATFORM),PLATFORM_RPI) - USE_RPI_CROSS_COMPILER ?= FALSE - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry - RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/arm-linux-gnueabihf/sysroot - endif -endif - # Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! @@ -88,12 +79,6 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) @@ -111,9 +96,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif # Default path for raylib on Raspberry Pi -ifeq ($(PLATFORM),PLATFORM_RPI) - RAYLIB_PATH ?= /home/pi/raylib -endif ifeq ($(PLATFORM),PLATFORM_DRM) RAYLIB_PATH ?= /home/pi/raylib endif @@ -147,13 +129,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - # Define RPI cross-compiler - #CC = armv6j-hardfloat-linux-gnueabi-gcc - CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc - endif -endif ifeq ($(PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler # WARNING: To compile to HTML5, code must be redesigned @@ -224,9 +199,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - CFLAGS += -std=gnu99 -endif ifeq ($(PLATFORM),PLATFORM_DRM) CFLAGS += -std=gnu99 -DEGL_NO_X11 endif @@ -245,11 +217,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vmcs_host/linux - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vcos/pthreads -endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm endif @@ -304,9 +271,6 @@ ifeq ($(PLATFORM),PLATFORM_WEB) LDFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif -ifeq ($(PLATFORM),PLATFORM_RPI) - LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -endif # Define libraries required on linking: LDLIBS # NOTE: To link libraries (lib.so or lib.a), use -l @@ -357,14 +321,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - # Libraries for Raspberry Pi compiling - # NOTE: Required packages: libasound2-dev (ALSA) - LDLIBS = -lraylib -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl -latomic - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - LDLIBS += -lvchiq_arm -lvcos - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) @@ -1067,10 +1023,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) rm -f *.o endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - find . -type f -executable -delete - rm -fv *.o -endif ifeq ($(PLATFORM),PLATFORM_DRM) find . -type f -executable -delete rm -fv *.o diff --git a/examples/core/core_basic_window_web.c b/examples/core/core_basic_window_web.c index 7d83254a1..e0e1f9fb1 100644 --- a/examples/core/core_basic_window_web.c +++ b/examples/core/core_basic_window_web.c @@ -2,7 +2,7 @@ * * raylib [core] example - Basic window (adapted for HTML5 platform) * -* NOTE: This example is prepared to compile for PLATFORM_WEB, PLATFORM_DESKTOP and PLATFORM_RPI +* 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 * diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index 1eb516e85..c28ff1f9e 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -21,13 +21,8 @@ // NOTE: Gamepad name ID depends on drivers and OS #define XBOX360_LEGACY_NAME_ID "Xbox Controller" -#if defined(PLATFORM_RPI) - #define XBOX360_NAME_ID "Microsoft X-Box 360 pad" - #define PS3_NAME_ID "PLAYSTATION(R)3 Controller" -#else - #define XBOX360_NAME_ID "Xbox 360 Controller" - #define PS3_NAME_ID "PLAYSTATION(R)3 Controller" -#endif +#define XBOX360_NAME_ID "Xbox 360 Controller" +#define PS3_NAME_ID "PLAYSTATION(R)3 Controller" //------------------------------------------------------------------------------------ // Program main entry point diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index bc69cc698..fc2dee6b9 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -15,7 +15,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 34616de59..7a500e04d 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -18,7 +18,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/others/raylib_opengl_interop.c b/examples/others/raylib_opengl_interop.c index bcdbbbdd5..09b548c60 100644 --- a/examples/others/raylib_opengl_interop.c +++ b/examples/others/raylib_opengl_interop.c @@ -42,7 +42,7 @@ #endif #define GLSL_VERSION 330 #endif -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 61cec2e80..3fbe60b83 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -27,7 +27,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index eaeca5e51..0a1a76428 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_eratosthenes.c b/examples/shaders/shaders_eratosthenes.c index a481f3008..005a97aea 100644 --- a/examples/shaders/shaders_eratosthenes.c +++ b/examples/shaders/shaders_eratosthenes.c @@ -29,7 +29,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index 24a1c1068..cd36936d6 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -27,7 +27,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_hot_reloading.c b/examples/shaders/shaders_hot_reloading.c index 395d44cfe..2b1175e4b 100644 --- a/examples/shaders/shaders_hot_reloading.c +++ b/examples/shaders/shaders_hot_reloading.c @@ -21,7 +21,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_hybrid_render.c b/examples/shaders/shaders_hybrid_render.c index f07917fb0..53e14b884 100644 --- a/examples/shaders/shaders_hybrid_render.c +++ b/examples/shaders/shaders_hybrid_render.c @@ -20,7 +20,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_julia_set.c b/examples/shaders/shaders_julia_set.c index ee1988058..aebb287a1 100644 --- a/examples/shaders/shaders_julia_set.c +++ b/examples/shaders/shaders_julia_set.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_lightmap.c b/examples/shaders/shaders_lightmap.c index b636c8b28..c5ed6094c 100644 --- a/examples/shaders/shaders_lightmap.c +++ b/examples/shaders/shaders_lightmap.c @@ -25,7 +25,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_mesh_instancing.c b/examples/shaders/shaders_mesh_instancing.c index 7789f2cb0..7d603a6bf 100644 --- a/examples/shaders/shaders_mesh_instancing.c +++ b/examples/shaders/shaders_mesh_instancing.c @@ -24,7 +24,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index e84ecbfbe..739a33b7e 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_multi_sample2d.c b/examples/shaders/shaders_multi_sample2d.c index d6f8803b7..f7c369bc4 100644 --- a/examples/shaders/shaders_multi_sample2d.c +++ b/examples/shaders/shaders_multi_sample2d.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_palette_switch.c b/examples/shaders/shaders_palette_switch.c index bb1eda4e0..6b108b092 100644 --- a/examples/shaders/shaders_palette_switch.c +++ b/examples/shaders/shaders_palette_switch.c @@ -24,7 +24,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index 7f6bd0092..1a7c621e5 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index 7b34a5233..e9b7755ad 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -18,7 +18,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB -> Not supported at this moment +#else // PLATFORM_ANDROID, PLATFORM_WEB -> Not supported at this moment #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_shapes_textures.c b/examples/shaders/shaders_shapes_textures.c index d3ec8daf0..cca4959ed 100644 --- a/examples/shaders/shaders_shapes_textures.c +++ b/examples/shaders/shaders_shapes_textures.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_simple_mask.c b/examples/shaders/shaders_simple_mask.c index 6283ccbcc..7ed315b0a 100644 --- a/examples/shaders/shaders_simple_mask.c +++ b/examples/shaders/shaders_simple_mask.c @@ -25,7 +25,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_spotlight.c b/examples/shaders/shaders_spotlight.c index c96c983d0..067f9622d 100644 --- a/examples/shaders/shaders_spotlight.c +++ b/examples/shaders/shaders_spotlight.c @@ -34,7 +34,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_texture_drawing.c b/examples/shaders/shaders_texture_drawing.c index 006168d57..43ffa21ef 100644 --- a/examples/shaders/shaders_texture_drawing.c +++ b/examples/shaders/shaders_texture_drawing.c @@ -19,7 +19,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_texture_outline.c b/examples/shaders/shaders_texture_outline.c index 09f625bb5..83f2820e7 100644 --- a/examples/shaders/shaders_texture_outline.c +++ b/examples/shaders/shaders_texture_outline.c @@ -20,7 +20,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_texture_waves.c b/examples/shaders/shaders_texture_waves.c index 27ad1f6e4..5bdaada44 100644 --- a/examples/shaders/shaders_texture_waves.c +++ b/examples/shaders/shaders_texture_waves.c @@ -24,7 +24,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_write_depth.c b/examples/shaders/shaders_write_depth.c index 048e297ad..d9e40d0dd 100644 --- a/examples/shaders/shaders_write_depth.c +++ b/examples/shaders/shaders_write_depth.c @@ -19,7 +19,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index 6a223ae5c..cba47b438 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -15,7 +15,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/projects/CMake/core_basic_window.c b/projects/CMake/core_basic_window.c index 0e5542967..86709f7a9 100644 --- a/projects/CMake/core_basic_window.c +++ b/projects/CMake/core_basic_window.c @@ -2,7 +2,7 @@ * * raylib [core] example - Basic window (adapted for HTML5 platform) * -* This example is prepared to compile for PLATFORM_WEB, PLATFORM_DESKTOP and PLATFORM_RPI +* This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP * As you will notice, code structure is slightly different to the other examples... * To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning * diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 9ab042e8f..a2a26f092 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -32,7 +32,7 @@ RAYLIB_PATH ?= ..\.. COMPILER_PATH ?= C:/raylib/w64devkit/bin # Define default options -# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +# One of PLATFORM_DESKTOP, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Locations of your newly installed library and associated headers. See ../src/Makefile diff --git a/src/Makefile b/src/Makefile index ed7594938..63cbe2a41 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,7 +8,6 @@ # PLATFORM_DESKTOP: OSX/macOS (arm64, x86_64) # PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly # PLATFORM_ANDROID: Android (arm, i686, arm64, x86_64) -# PLATFORM_RPI: Raspberry Pi (deprecated - RPI OS Buster only) # PLATFORM_DRM: Linux native mode, including Raspberry Pi (RPI OS Bullseye) # PLATFORM_WEB: HTML5 (Chrome, Firefox) # @@ -41,7 +40,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables @@ -94,14 +93,6 @@ USE_EXTERNAL_GLFW ?= FALSE # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE -# Use cross-compiler for PLATFORM_RPI -USE_RPI_CROSS_COMPILER ?= FALSE -ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry - RPI_TOOLCHAIN_NAME ?= arm-linux-gnueabihf - RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/$(RPI_TOOLCHAIN_NAME)/sysroot -endif - # Determine if the file has root access (only required to install raylib) # "whoami" prints the name of the user that calls him (so, if it is the root user, "whoami" prints "root") ROOT = $(shell whoami) @@ -144,15 +135,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif - ifndef PLATFORM_SHELL - PLATFORM_SHELL = sh - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) @@ -235,10 +217,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif -ifeq ($(PLATFORM),PLATFORM_RPI) - # On RPI OpenGL ES 2.0 must be used - GRAPHICS = GRAPHICS_API_OPENGL_ES2 -endif + ifeq ($(PLATFORM),PLATFORM_DRM) # On DRM OpenGL ES 2.0 must be used GRAPHICS = GRAPHICS_API_OPENGL_ES2 @@ -269,14 +248,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - # Define RPI cross-compiler - #CC = armv6j-hardfloat-linux-gnueabi-gcc - CC = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-gcc - AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) # Define RPI cross-compiler @@ -452,11 +423,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I/usr/local/include endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vmcs_host/linux - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vcos/pthreads -endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) @@ -506,9 +472,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -endif ifeq ($(PLATFORM),PLATFORM_DRM) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) @@ -557,12 +520,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS = -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - LDLIBS = -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl - ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) - LDLIBS += -latomic - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) @@ -647,14 +604,6 @@ else cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so lib$(RAYLIB_LIB_NAME).so endif endif - ifeq ($(PLATFORM),PLATFORM_RPI) - # Compile raylib shared library version $(RAYLIB_VERSION). - # WARNING: you should type "make clean" before doing this target - $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) $(OBJS) $(LDFLAGS) $(LDLIBS) - @echo "raylib shared library generated (lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION)) in $(RAYLIB_RELEASE_PATH)!" - cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) - cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) lib$(RAYLIB_LIB_NAME).so - endif ifeq ($(PLATFORM),PLATFORM_DRM) # Compile raylib shared library version $(RAYLIB_VERSION). # WARNING: you should type "make clean" before doing this target diff --git a/src/rcore.c b/src/rcore.c index c93038239..fcb222bb3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -8,7 +8,6 @@ * - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) * - PLATFORM_DESKTOP: OSX/macOS * - PLATFORM_ANDROID: Android (ARM, ARM64) -* - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian, native mode) * - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver * - PLATFORM_WEB: HTML5 with WebAssembly * @@ -21,12 +20,6 @@ * Windowing and input system configured for Android device, app activity managed internally in this module. * NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL * -* #define PLATFORM_RPI (deprecated - RPI OS Buster only) -* Windowing and input system configured for Raspberry Pi in native mode (no XWindow required), -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ -* WARNING: This platform is deprecated, since RPI OS Bullseye, the old Dispmanx libraries are not -* supported and you must be using PLATFORM_DRM -* * #define PLATFORM_DRM * Windowing and input system configured for DRM native mode (RPI4 and other devices) * graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ @@ -260,7 +253,7 @@ //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) #include // POSIX file control definitions - open(), creat(), fcntl() #include // POSIX standard function definitions - read(), close(), STDIN_FILENO #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() @@ -272,9 +265,6 @@ #include // Linux: Keycodes constants definition (KEY_A, ...) #include // Linux: Joystick support library -#if defined(PLATFORM_RPI) - #include "bcm_host.h" // Raspberry Pi VideoCore IV access functions -#endif #if defined(PLATFORM_DRM) #include // Generic Buffer Management (native platform for EGL on DRM) #include // Direct Rendering Manager user-level library interface @@ -299,7 +289,7 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) @@ -351,7 +341,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) typedef struct { pthread_t threadId; // Event reading thread id int fd; // File descriptor to the device it is assigned to @@ -375,10 +365,7 @@ typedef struct CoreData { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) GLFWwindow *handle; // GLFW window handle (graphic device) #endif -#if defined(PLATFORM_RPI) - EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device) -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) #if defined(PLATFORM_DRM) int fd; // File descriptor for /dev/dri/... drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector @@ -428,7 +415,7 @@ typedef struct CoreData { const char *basePath; // Base path for data storage } Storage; struct { -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" #endif struct { @@ -444,7 +431,7 @@ typedef struct CoreData { int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) int charPressedQueueCount; // Input characters queue count -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) int defaultMode; // Default keyboard mode #if defined(SUPPORT_SSH_KEYBOARD_RPI) bool evtMode; // Keyboard in event mode @@ -468,7 +455,7 @@ typedef struct CoreData { char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state Vector2 currentWheelMove; // Registers current mouse wheel variation Vector2 previousWheelMove; // Registers previous mouse wheel variation -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) 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 @@ -489,7 +476,7 @@ typedef struct CoreData { 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 -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) pthread_t threadId; // Gamepad reading thread id int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor #endif @@ -502,7 +489,7 @@ typedef struct CoreData { 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 -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) unsigned long long int base; // Base time measure for hi-res timer #endif unsigned int frameCounter; // Frame counter @@ -667,7 +654,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) static void InitKeyboard(void); // Initialize raw keyboard system static void RestoreKeyboard(void); // Restore keyboard system #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -682,13 +669,11 @@ static void *EventThread(void *arg); // Input device events r static void InitGamepad(void); // Initialize raw gamepad input static void *GamepadThread(void *arg); // Mouse reading thread -#if defined(PLATFORM_DRM) 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 -#endif -#endif // PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_DRM #if defined(SUPPORT_EVENTS_AUTOMATION) static void LoadAutomationEvents(const char *fileName); // Load automation events from file @@ -857,7 +842,7 @@ void InitWindow(int width, int height, const char *title) } } #endif -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) // Initialize graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); @@ -916,7 +901,7 @@ void InitWindow(int width, int height, const char *title) } #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) // Initialize raw input system InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init @@ -958,7 +943,7 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif -#endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_DRM } // Close window and unload OpenGL context @@ -988,7 +973,7 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) +#if defined(PLATFORM_ANDROID) // Close surface, context and display if (CORE.Window.device != EGL_NO_DISPLAY) { @@ -1076,7 +1061,7 @@ void CloseWindow(void) } #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) // Wait for mouse and gamepad threads to finish before closing // NOTE: Those threads should already have finished at this point // because they are controlled by CORE.Window.shouldClose variable @@ -1138,7 +1123,7 @@ bool WindowShouldClose(void) else return true; #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) if (CORE.Window.ready) return CORE.Window.shouldClose; else return true; #endif @@ -1324,7 +1309,7 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode"); #endif } @@ -3047,7 +3032,7 @@ double GetTime(void) time = glfwGetTime(); // Elapsed time since glfwInit() #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) 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; @@ -3883,7 +3868,7 @@ const char *GetGamepadName(int gamepad) if (CORE.Input.Gamepad.ready[gamepad]) return glfwGetJoystickName(gamepad); else return NULL; #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); return CORE.Input.Gamepad.name[gamepad]; #endif @@ -3896,7 +3881,7 @@ const char *GetGamepadName(int gamepad) // Get gamepad axis count int GetGamepadAxisCount(int gamepad) { -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) int axisCount = 0; if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; @@ -4138,7 +4123,7 @@ int GetTouchX(void) { #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) return (int)CORE.Input.Touch.position[0].x; -#else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM +#else // PLATFORM_DESKTOP, PLATFORM_DRM return GetMouseX(); #endif } @@ -4148,7 +4133,7 @@ int GetTouchY(void) { #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) return (int)CORE.Input.Touch.position[0].y; -#else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM +#else // PLATFORM_DESKTOP, PLATFORM_DRM return GetMouseY(); #endif } @@ -4165,7 +4150,7 @@ Vector2 GetTouchPosition(int index) // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages if (index == 0) position = GetMousePosition(); #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) 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); #endif @@ -4537,21 +4522,10 @@ static bool InitGraphicsDevice(int width, int height) #endif // PLATFORM_DESKTOP || PLATFORM_WEB -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; -#if defined(PLATFORM_RPI) - bcm_host_init(); - - DISPMANX_ELEMENT_HANDLE_T dispmanElement = { 0 }; - DISPMANX_DISPLAY_HANDLE_T dispmanDisplay = { 0 }; - DISPMANX_UPDATE_HANDLE_T dispmanUpdate = { 0 }; - - VC_RECT_T dstRect = { 0 }; - VC_RECT_T srcRect = { 0 }; -#endif - #if defined(PLATFORM_DRM) CORE.Window.fd = -1; CORE.Window.connector = NULL; @@ -4748,7 +4722,7 @@ static bool InitGraphicsDevice(int width, int height) EGL_NONE }; -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) EGLint numConfigs = 0; // Get an EGL device connection @@ -4863,58 +4837,6 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); #endif // PLATFORM_ANDROID -#if defined(PLATFORM_RPI) - graphics_get_display_size(0, &CORE.Window.display.width, &CORE.Window.display.height); - - // Screen size security check - 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; - - // 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); - - dstRect.x = 0; - dstRect.y = 0; - dstRect.width = CORE.Window.display.width; - dstRect.height = CORE.Window.display.height; - - srcRect.x = 0; - srcRect.y = 0; - srcRect.width = CORE.Window.render.width << 16; - srcRect.height = CORE.Window.render.height << 16; - - // NOTE: RPI dispmanx windowing system takes care of source rectangle scaling to destination rectangle by hardware (no cost) - // Take care that renderWidth/renderHeight fit on displayWidth/displayHeight aspect ratio - - VC_DISPMANX_ALPHA_T alpha = { 0 }; - alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; - //alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE; // TODO: Allow transparent framebuffer! -> FLAG_WINDOW_TRANSPARENT - alpha.opacity = 255; // Set transparency level for framebuffer, requires EGLAttrib: EGL_TRANSPARENT_TYPE - alpha.mask = 0; - - dispmanDisplay = vc_dispmanx_display_open(0); // LCD - dispmanUpdate = vc_dispmanx_update_start(0); - - dispmanElement = vc_dispmanx_element_add(dispmanUpdate, dispmanDisplay, 0/*layer*/, &dstRect, 0/*src*/, - &srcRect, DISPMANX_PROTECTION_NONE, &alpha, 0/*clamp*/, DISPMANX_NO_ROTATE); - - CORE.Window.handle.element = dispmanElement; - CORE.Window.handle.width = CORE.Window.render.width; - CORE.Window.handle.height = CORE.Window.render.height; - vc_dispmanx_update_submit_sync(dispmanUpdate); - - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, &CORE.Window.handle, NULL); - - const unsigned char *const renderer = glGetString(GL_RENDERER); - if (renderer) TRACELOG(LOG_INFO, "DISPLAY: Renderer name is: %s", renderer); - else TRACELOG(LOG_WARNING, "DISPLAY: Failed to get renderer name"); - //--------------------------------------------------------------------------------- -#endif // PLATFORM_RPI - #if defined(PLATFORM_DRM) CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); if (EGL_NO_SURFACE == CORE.Window.surface) @@ -4952,7 +4874,7 @@ static bool InitGraphicsDevice(int width, int 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 // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_ANDROID || PLATFORM_DRM // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions @@ -5096,7 +5018,7 @@ static void InitTimer(void) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) struct timespec now = { 0 }; if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success @@ -5160,7 +5082,7 @@ void SwapScreenBuffer(void) glfwSwapBuffers(CORE.Window.handle); #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) eglSwapBuffers(CORE.Window.device, CORE.Window.surface); #if defined(PLATFORM_DRM) @@ -5190,7 +5112,7 @@ void SwapScreenBuffer(void) CORE.Window.prevBO = bo; #endif // PLATFORM_DRM -#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_ANDROID || PLATFORM_DRM } // Register all input events @@ -5208,13 +5130,11 @@ void PollInputEvents(void) // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; -#if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Input.Gamepad.axisCount = 0; -#endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) // Register previous keys states for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) { @@ -5461,7 +5381,7 @@ void PollInputEvents(void) } #endif -#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI) +#if defined(PLATFORM_DRM) && 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 @@ -6336,7 +6256,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent } #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { @@ -6977,7 +6897,7 @@ static void *EventThread(void *arg) if (CORE.Input.Touch.position[2].x >= 0) CORE.Input.Touch.pointCount++; if (CORE.Input.Touch.position[3].x >= 0) CORE.Input.Touch.pointCount++; -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_RPI, PLATFORM_DRM +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM if (gestureUpdate) { GestureEvent gestureEvent = { 0 }; @@ -7094,7 +7014,7 @@ static void *GamepadThread(void *arg) return NULL; } -#endif // PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_DRM #if defined(PLATFORM_DRM) // Search matching DRM mode in connector's mode list diff --git a/src/rlgl.h b/src/rlgl.h index f274931e2..dee4d501f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -815,7 +815,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad // 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) - #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) + #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); typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); From 30a9a24db9d2561335a2a42d423cf9cfeab6e769 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Sep 2023 18:00:10 +0200 Subject: [PATCH 0618/1710] Review to avoid UBSAN complaining #1891 --- src/rlgl.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index dee4d501f..6dee60f95 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3687,7 +3687,11 @@ void rlDrawVertexArray(int offset, int count) // Draw vertex array elements void rlDrawVertexArrayElements(int offset, int count, const void *buffer) { - glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned short *bufferPtr = (unsigned short *)buffer; + if (offset > 0) bufferPtr += offset; + + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr); } // Draw vertex array instanced @@ -3702,7 +3706,11 @@ void rlDrawVertexArrayInstanced(int offset, int count, int instances) void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset, instances); + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned short *bufferPtr = (unsigned short *)buffer; + if (offset > 0) bufferPtr += offset; + + glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr, instances); #endif } From ecc80bbceaaa90e24fff2ce956ce0730991bd552 Mon Sep 17 00:00:00 2001 From: Dan Vu Date: Fri, 8 Sep 2023 11:13:00 +0200 Subject: [PATCH 0619/1710] added raylib-raku to bindings (#3299) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index d4bd8fc21..bdd5e2558 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -77,6 +77,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-sunder | **auto** | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | | rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | | rayjs | 4.6-dev | [QuickJS](https://bellard.org/quickjs/) | MIT | https://github.com/mode777/rayjs | +| raylib-raku | **auto** | [Raku](https://www.raku.org/) | Artistic License 2.0 | https://github.com/vushu/raylib-raku | ### Utility Wrapers These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. From 2d5d0c2999717ea28924d6cdd47bab014478cc32 Mon Sep 17 00:00:00 2001 From: Gabriel dos Santos Sanches Date: Fri, 8 Sep 2023 12:22:12 +0200 Subject: [PATCH 0620/1710] examples: core: adds 2D camera two player split screen (#3298) --- examples/Makefile | 1 + examples/Makefile.Web | 1 + examples/README.md | 1 + examples/core/core_camera_2d_split_screen.c | 137 +++++++ examples/core/core_camera_2d_split_screen.png | Bin 0 -> 20010 bytes .../core_camera_2d_split_screen.vcxproj | 387 ++++++++++++++++++ 6 files changed, 527 insertions(+) create mode 100644 examples/core/core_camera_2d_split_screen.c create mode 100644 examples/core/core_camera_2d_split_screen.png create mode 100644 projects/VS2022/examples/core_camera_2d_split_screen.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 338fe168c..911743938 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -386,6 +386,7 @@ CORE = \ core/core_window_letterbox \ core/core_window_should_close \ core/core_split_screen \ + core/core_camera_2d_split_screen \ core/core_smooth_pixelperfect \ core/core_custom_frame_control diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 518fab918..44ace4ee4 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -361,6 +361,7 @@ CORE = \ core/core_window_letterbox \ core/core_window_should_close \ core/core_split_screen \ + core/core_camera_2d_split_screen \ core/core_smooth_pixelperfect \ core/core_custom_frame_control \ core/core_loading_thread diff --git a/examples/README.md b/examples/README.md index 8399d677d..35acaa25a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -54,6 +54,7 @@ Examples using raylib core platform functionality like window creation, inputs, | 28 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | **4.0** | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | | 29 | [core_split_screen](core/core_split_screen.c) | core_split_screen | ⭐️⭐️⭐️⭐️ | 3.7 | **4.0** | [Jeffery Myers](https://github.com/JeffM2501) | | 30 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | +| 31 | [core_camera_2d_split_screen](core/core_camera_2d_split_screen.c) | core_camera_2d_split_screen | ⭐️⭐️⭐️⭐️ | **4.5** | **4.5** | [Jeffery Myers](https://github.com/JeffM2501) | ### category: shapes diff --git a/examples/core/core_camera_2d_split_screen.c b/examples/core/core_camera_2d_split_screen.c new file mode 100644 index 000000000..5f5059569 --- /dev/null +++ b/examples/core/core_camera_2d_split_screen.c @@ -0,0 +1,137 @@ +/******************************************************************************************* +* +* raylib [core] example - split screen +* +* Addapted from the Split Screen example (https://github.com/raysan5/raylib/blob/master/examples/core/core_split_screen.c) +* +* Example originally created with raylib 4.5, last time updated with raylib 4.5 +* +* 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) 2021-2023 Jeffery Myers (@JeffM2501) +* +********************************************************************************************/ + +#include "raylib.h" + +#include + +#define PLAYER_SIZE 40 + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 440; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - camera 2D split screen"); + + Rectangle player1 = { 200, 200, PLAYER_SIZE, PLAYER_SIZE }; + Rectangle player2 = { 250, 200, PLAYER_SIZE, PLAYER_SIZE }; + + Camera2D camera1 = { 0 }; + camera1.target = (Vector2){ player1.x, player1.y }; + camera1.offset = (Vector2){ 200.0f, 200.0f }; + camera1.rotation = 0.0f; + camera1.zoom = 1.0f; + + Camera2D camera2 = { 0 }; + camera2.target = (Vector2){ player2.x, player2.y }; + camera2.offset = (Vector2){ 200.0f, 200.0f }; + camera2.rotation = 0.0f; + camera2.zoom = 1.0f; + + RenderTexture screenCamera1 = LoadRenderTexture(screenWidth / 2, screenHeight); + RenderTexture screenCamera2 = 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)screenCamera1.texture.width, (float)-screenCamera1.texture.height }; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + void DrawScene(void) { + for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){PLAYER_SIZE*i, 0}, (Vector2){PLAYER_SIZE*i, screenHeight}, LIGHTGRAY); + } + + for (int i = 0; i < screenHeight/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){0, PLAYER_SIZE*i}, (Vector2){screenWidth, PLAYER_SIZE*i}, LIGHTGRAY); + } + + for (int i = 0; i < screenWidth/PLAYER_SIZE; i++) + { + for (int j = 0; j < screenHeight/PLAYER_SIZE; j++) + { + char coordinate_str[8]; + snprintf(coordinate_str, sizeof(coordinate_str), "%d,%d", i, j); + DrawText(coordinate_str, 10 + PLAYER_SIZE*i, 10 + PLAYER_SIZE*j, 10, LIGHTGRAY); + } + } + + DrawRectangleRec(player1, RED); + DrawRectangleRec(player2, BLUE); + } + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyDown(KEY_S)) player1.y += 3; + else if (IsKeyDown(KEY_W)) player1.y -= 3; + if (IsKeyDown(KEY_D)) player1.x += 3; + else if (IsKeyDown(KEY_A)) player1.x -= 3; + + if (IsKeyDown(KEY_UP)) player2.y += 3; + else if (IsKeyDown(KEY_DOWN)) player2.y -= 3; + if (IsKeyDown(KEY_RIGHT)) player2.x += 3; + else if (IsKeyDown(KEY_LEFT)) player2.x -= 3; + + camera1.target = (Vector2){ player1.x, player1.y }; + camera2.target = (Vector2){ player2.x, player2.y }; + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(screenCamera1); + ClearBackground(RAYWHITE); + BeginMode2D(camera1); + DrawScene(); + EndMode2D(); + DrawText("PLAYER1 W/S/A/D to move", 10, 10, 15, RED); + EndTextureMode(); + + BeginTextureMode(screenCamera2); + ClearBackground(RAYWHITE); + BeginMode2D(camera2); + DrawScene(); + EndMode2D(); + DrawText("PLAYER2 UP/DOWN/LEFT/RIGHT to move", 10, 10, 15, BLUE); + 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); + EndDrawing(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(screenCamera1); // Unload render texture + UnloadRenderTexture(screenCamera2); // Unload render texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_camera_2d_split_screen.png b/examples/core/core_camera_2d_split_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..ed5aaa58bfc2bb8124098d2b5c583961414bc218 GIT binary patch literal 20010 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 literal 0 HcmV?d00001 diff --git a/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj b/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj new file mode 100644 index 000000000..1efc58a65 --- /dev/null +++ b/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj @@ -0,0 +1,387 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {946A1700-C7AA-46F0-AEF2-67C98B5722AC} + Win32Proj + core_camera_2d_split_screen + 10.0 + core_camera_2d_split_screen + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + From 1896268775e59d00b2b4f3fea0bc244eee1a1cb4 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Sep 2023 13:27:13 +0200 Subject: [PATCH 0621/1710] Reviewed examples for consistency --- examples/Makefile | 4 +- examples/Makefile.Web | 13 +- examples/README.md | 42 ++--- examples/core/core_2d_camera_split_screen.c | 167 ++++++++++++++++++ examples/core/core_2d_camera_split_screen.png | Bin 0 -> 21543 bytes examples/core/core_3d_camera_free.c | 2 +- ...screen.c => core_3d_camera_split_screen.c} | 87 +++++---- examples/core/core_3d_camera_split_screen.png | Bin 0 -> 16165 bytes examples/core/core_camera_2d_split_screen.c | 137 -------------- examples/core/core_camera_2d_split_screen.png | Bin 20010 -> 0 bytes examples/core/core_split_screen.png | Bin 21609 -> 0 bytes examples/shapes/shapes_bouncing_ball.c | 6 +- examples/shapes/shapes_lines_bezier.c | 24 ++- examples/shapes/shapes_lines_splines.c | 155 ++++++++++++++++ ...oj => core_2d_camera_split_screen.vcxproj} | 8 +- ...oj => core_3d_camera_split_screen.vcxproj} | 8 +- projects/VS2022/raylib.sln | 57 ++++-- 17 files changed, 481 insertions(+), 229 deletions(-) create mode 100644 examples/core/core_2d_camera_split_screen.c create mode 100644 examples/core/core_2d_camera_split_screen.png rename examples/core/{core_split_screen.c => core_3d_camera_split_screen.c} (68%) create mode 100644 examples/core/core_3d_camera_split_screen.png delete mode 100644 examples/core/core_camera_2d_split_screen.c delete mode 100644 examples/core/core_camera_2d_split_screen.png delete mode 100644 examples/core/core_split_screen.png create mode 100644 examples/shapes/shapes_lines_splines.c rename projects/VS2022/examples/{core_split_screen.vcxproj => core_2d_camera_split_screen.vcxproj} (98%) rename projects/VS2022/examples/{core_camera_2d_split_screen.vcxproj => core_3d_camera_split_screen.vcxproj} (99%) diff --git a/examples/Makefile b/examples/Makefile index 911743938..cd079c312 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -370,9 +370,11 @@ CORE = \ core/core_2d_camera \ core/core_2d_camera_platformer \ core/core_2d_camera_mouse_zoom \ + core/core_2d_camera_split_screen \ core/core_3d_camera_mode \ core/core_3d_camera_free \ core/core_3d_camera_first_person \ + core/core_3d_camera_split_screen \ core/core_3d_picking \ core/core_world_screen \ core/core_custom_logging \ @@ -385,8 +387,6 @@ CORE = \ core/core_window_flags \ core/core_window_letterbox \ core/core_window_should_close \ - core/core_split_screen \ - core/core_camera_2d_split_screen \ core/core_smooth_pixelperfect \ core/core_custom_frame_control diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 44ace4ee4..267e02396 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -346,9 +346,11 @@ CORE = \ core/core_2d_camera \ core/core_2d_camera_platformer \ core/core_2d_camera_mouse_zoom \ + core/core_2d_camera_split_screen \ core/core_3d_camera_mode \ core/core_3d_camera_free \ core/core_3d_camera_first_person \ + core/core_3d_camera_split_screen \ core/core_3d_picking \ core/core_world_screen \ core/core_custom_logging \ @@ -360,8 +362,6 @@ CORE = \ core/core_window_flags \ core/core_window_letterbox \ core/core_window_should_close \ - core/core_split_screen \ - core/core_camera_2d_split_screen \ core/core_smooth_pixelperfect \ core/core_custom_frame_control \ core/core_loading_thread @@ -529,6 +529,9 @@ core/core_2d_camera_platformer: core/core_2d_camera_platformer.c core/core_2d_camera_mouse_zoom: core/core_2d_camera_mouse_zoom.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +core/core_2d_camera_split_screen: core/core_2d_camera_split_screen.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_3d_camera_mode: core/core_3d_camera_mode.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -538,6 +541,9 @@ core/core_3d_camera_free: core/core_3d_camera_free.c core/core_3d_camera_first_person: core/core_3d_camera_first_person.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_3d_picking: core/core_3d_picking.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -570,9 +576,6 @@ core/core_vr_simulator: core/core_vr_simulator.c core/core_window_flags: core/core_window_flags.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -core/core_split_screen: core/core_split_screen.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - core/core_smooth_pixelperfect: core/core_smooth_pixelperfect.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/examples/README.md b/examples/README.md index 35acaa25a..82e4b7825 100644 --- a/examples/README.md +++ b/examples/README.md @@ -34,27 +34,27 @@ Examples using raylib core platform functionality like window creation, inputs, | 08 | [core_2d_camera](core/core_2d_camera.c) | core_2d_camera | ⭐️⭐️☆☆ | 1.5 | 3.0 | [Ray](https://github.com/raysan5) | | 09 | [core_2d_camera_mouse_zoom](core/core_2d_camera_mouse_zoom.c) | core_2d_camera_mouse_zoom | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Jeffery Myers](https://github.com/JeffM2501) | | 10 | [core_2d_camera_platformer](core/core_2d_camera_platformer.c) | core_2d_camera_platformer | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [avyy](https://github.com/avyy) | -| 11 | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 12 | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 13 | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 14 | [core_3d_picking](core/core_3d_picking.c) | core_3d_picking | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 15 | [core_world_screen](core/core_world_screen.c) | core_world_screen | ⭐️⭐️☆☆ | 1.3 | 1.4 | [Ray](https://github.com/raysan5) | -| 16 | [core_custom_logging](core/core_custom_logging.c) | core_custom_logging | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Pablo Marcos Oltra](https://github.com/pamarcos) | -| 17 | [core_window_flags](core/core_window_flags.c) | core_window_flags | ⭐️⭐️⭐️☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 18 | [core_window_letterbox](core/core_window_letterbox.c) | core_window_letterbox | ⭐️⭐️☆☆ | 2.5 | **4.0** | [Anata](https://github.com/anatagawa) | -| 19 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️☆☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | -| 20 | [core_drop_files](core/core_drop_files.c) | core_drop_files | ⭐️⭐️☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 21 | [core_random_values](core/core_random_values.c) | core_random_values | ⭐️☆☆☆ | 1.1 | 1.1 | [Ray](https://github.com/raysan5) | -| 22 | [core_storage_values](core/core_storage_values.c) | core_storage_values | ⭐️⭐️☆☆ | 1.4 | **4.2** | [Ray](https://github.com/raysan5) | -| 23 | [core_vr_simulator](core/core_vr_simulator.c) | core_vr_simulator | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | -| 24 | [core_loading_thread](core/core_loading_thread.c) | core_loading_thread | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Ray](https://github.com/raysan5) | -| 25 | [core_scissor_test](core/core_scissor_test.c) | core_scissor_test | ⭐️☆☆☆ | 2.5 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | -| 26 | [core_basic_screen_manager](core/core_basic_screen_manager.c) | core_basic_screen_manager | ⭐️☆☆☆ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | -| 27 | [core_custom_frame_control](core/core_custom_frame_control.c) | core_custom_frame_control | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | -| 28 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | **4.0** | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | -| 29 | [core_split_screen](core/core_split_screen.c) | core_split_screen | ⭐️⭐️⭐️⭐️ | 3.7 | **4.0** | [Jeffery Myers](https://github.com/JeffM2501) | -| 30 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | -| 31 | [core_camera_2d_split_screen](core/core_camera_2d_split_screen.c) | core_camera_2d_split_screen | ⭐️⭐️⭐️⭐️ | **4.5** | **4.5** | [Jeffery Myers](https://github.com/JeffM2501) | +| 11 | [core_2d_camera_split_screen](core/core_2d_camera_split_screen.c) | core_2d_camera_split_screen | ⭐️⭐️⭐️⭐️ | **4.5** | **4.5** | [Gabriel dos Santos Sanches](https://github.com/gabrielssanches) | +| 12 | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 13 | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 14 | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 15 | [core_3d_camera:split_screen](core/core_3d_camera_split_screen.c) | core_3d_camera_split_screen | ⭐️⭐️⭐️⭐️ | 3.7 | **4.0** | [Jeffery Myers](https://github.com/JeffM2501) | +| 16 | [core_3d_picking](core/core_3d_picking.c) | core_3d_picking | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 17 | [core_world_screen](core/core_world_screen.c) | core_world_screen | ⭐️⭐️☆☆ | 1.3 | 1.4 | [Ray](https://github.com/raysan5) | +| 18 | [core_custom_logging](core/core_custom_logging.c) | core_custom_logging | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Pablo Marcos Oltra](https://github.com/pamarcos) | +| 19 | [core_window_flags](core/core_window_flags.c) | core_window_flags | ⭐️⭐️⭐️☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 20 | [core_window_letterbox](core/core_window_letterbox.c) | core_window_letterbox | ⭐️⭐️☆☆ | 2.5 | **4.0** | [Anata](https://github.com/anatagawa) | +| 21 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️☆☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | +| 22 | [core_drop_files](core/core_drop_files.c) | core_drop_files | ⭐️⭐️☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 23 | [core_random_values](core/core_random_values.c) | core_random_values | ⭐️☆☆☆ | 1.1 | 1.1 | [Ray](https://github.com/raysan5) | +| 24 | [core_storage_values](core/core_storage_values.c) | core_storage_values | ⭐️⭐️☆☆ | 1.4 | **4.2** | [Ray](https://github.com/raysan5) | +| 25 | [core_vr_simulator](core/core_vr_simulator.c) | core_vr_simulator | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | +| 26 | [core_loading_thread](core/core_loading_thread.c) | core_loading_thread | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Ray](https://github.com/raysan5) | +| 27 | [core_scissor_test](core/core_scissor_test.c) | core_scissor_test | ⭐️☆☆☆ | 2.5 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | +| 28 | [core_basic_screen_manager](core/core_basic_screen_manager.c) | core_basic_screen_manager | ⭐️☆☆☆ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | +| 29 | [core_custom_frame_control](core/core_custom_frame_control.c) | core_custom_frame_control | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | +| 30 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | **4.0** | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | +| 31 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | ### category: shapes diff --git a/examples/core/core_2d_camera_split_screen.c b/examples/core/core_2d_camera_split_screen.c new file mode 100644 index 000000000..57a0dfd3a --- /dev/null +++ b/examples/core/core_2d_camera_split_screen.c @@ -0,0 +1,167 @@ +/******************************************************************************************* +* +* raylib [core] example - 2d camera split screen +* +* 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 +* +* Example contributed by Gabriel dos Santos Sanches (@gabrielssanches) 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 Gabriel dos Santos Sanches (@gabrielssanches) +* +********************************************************************************************/ + +#include "raylib.h" + +#define PLAYER_SIZE 40 + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 440; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera split screen"); + + Rectangle player1 = { 200, 200, PLAYER_SIZE, PLAYER_SIZE }; + Rectangle player2 = { 250, 200, PLAYER_SIZE, PLAYER_SIZE }; + + Camera2D camera1 = { 0 }; + camera1.target = (Vector2){ player1.x, player1.y }; + camera1.offset = (Vector2){ 200.0f, 200.0f }; + camera1.rotation = 0.0f; + camera1.zoom = 1.0f; + + Camera2D camera2 = { 0 }; + camera2.target = (Vector2){ player2.x, player2.y }; + camera2.offset = (Vector2){ 200.0f, 200.0f }; + camera2.rotation = 0.0f; + camera2.zoom = 1.0f; + + RenderTexture screenCamera1 = LoadRenderTexture(screenWidth/2, screenHeight); + RenderTexture screenCamera2 = 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)screenCamera1.texture.width, (float)-screenCamera1.texture.height }; + + 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 (IsKeyDown(KEY_S)) player1.y += 3.0f; + else if (IsKeyDown(KEY_W)) player1.y -= 3.0f; + if (IsKeyDown(KEY_D)) player1.x += 3.0f; + else if (IsKeyDown(KEY_A)) player1.x -= 3.0f; + + if (IsKeyDown(KEY_UP)) player2.y -= 3.0f; + else if (IsKeyDown(KEY_DOWN)) player2.y += 3.0f; + if (IsKeyDown(KEY_RIGHT)) player2.x += 3.0f; + else if (IsKeyDown(KEY_LEFT)) player2.x -= 3.0f; + + camera1.target = (Vector2){ player1.x, player1.y }; + camera2.target = (Vector2){ player2.x, player2.y }; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(screenCamera1); + ClearBackground(RAYWHITE); + + BeginMode2D(camera1); + + // Draw full scene with first camera + for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){PLAYER_SIZE*i, 0}, (Vector2){PLAYER_SIZE*i, screenHeight}, LIGHTGRAY); + } + + for (int i = 0; i < screenHeight/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){0, PLAYER_SIZE*i}, (Vector2){screenWidth, PLAYER_SIZE*i}, LIGHTGRAY); + } + + for (int i = 0; i < screenWidth/PLAYER_SIZE; i++) + { + for (int j = 0; j < screenHeight/PLAYER_SIZE; j++) + { + DrawText(TextFormat("[%i,%i]", i, j), 10 + PLAYER_SIZE*i, 15 + PLAYER_SIZE*j, 10, LIGHTGRAY); + } + } + + 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++) + { + DrawLineV((Vector2){PLAYER_SIZE*i, 0}, (Vector2){PLAYER_SIZE*i, screenHeight}, LIGHTGRAY); + } + + for (int i = 0; i < screenHeight/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){0, PLAYER_SIZE*i}, (Vector2){screenWidth, PLAYER_SIZE*i}, LIGHTGRAY); + } + + for (int i = 0; i < screenWidth/PLAYER_SIZE; i++) + { + for (int j = 0; j < screenHeight/PLAYER_SIZE; j++) + { + DrawText(TextFormat("[%i,%i]", i, j), 10 + PLAYER_SIZE*i, 15 + PLAYER_SIZE*j, 10, LIGHTGRAY); + } + } + + 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(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(screenCamera1); // Unload render texture + UnloadRenderTexture(screenCamera2); // Unload render texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_2d_camera_split_screen.png b/examples/core/core_2d_camera_split_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..a441e39298f2b7962e187f6b5918915a8babff20 GIT binary patch literal 21543 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 F{~tJM=}Z6s literal 0 HcmV?d00001 diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index 59bd158a2..78200a642 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -72,7 +72,7 @@ int main(void) DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, DARKGRAY); DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, DARKGRAY); DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, DARKGRAY); - DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, DARKGRAY); + //DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, DARKGRAY); DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, DARKGRAY); EndDrawing(); diff --git a/examples/core/core_split_screen.c b/examples/core/core_3d_camera_split_screen.c similarity index 68% rename from examples/core/core_split_screen.c rename to examples/core/core_3d_camera_split_screen.c index 50cfcf7ac..d625e1c0e 100644 --- a/examples/core/core_split_screen.c +++ b/examples/core/core_3d_camera_split_screen.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [core] example - split screen +* raylib [core] example - 3d cmaera split screen * * Example originally created with raylib 3.7, last time updated with raylib 4.0 * @@ -15,32 +15,6 @@ #include "raylib.h" -Camera cameraPlayer1 = { 0 }; -Camera cameraPlayer2 = { 0 }; - -// Scene drawing -void DrawScene(void) -{ - int count = 5; - float spacing = 4; - - // Grid of cube trees on a plane to make a "world" - DrawPlane((Vector3){ 0, 0, 0 }, (Vector2){ 50, 50 }, BEIGE); // Simple world plane - - for (float x = -count*spacing; x <= count*spacing; x += spacing) - { - for (float z = -count*spacing; z <= count*spacing; z += spacing) - { - DrawCube((Vector3) { x, 1.5f, z }, 1, 1, 1, LIME); - DrawCube((Vector3) { x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN); - } - } - - // Draw a cube at each player's position - DrawCube(cameraPlayer1.position, 1, 1, 1, RED); - DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE); -} - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -51,9 +25,10 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - split screen"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera split screen"); // Setup player 1 camera and screen + Camera cameraPlayer1 = { 0 }; cameraPlayer1.fovy = 45.0f; cameraPlayer1.up.y = 1.0f; cameraPlayer1.target.y = 1.0f; @@ -63,6 +38,7 @@ int main(void) RenderTexture screenPlayer1 = LoadRenderTexture(screenWidth/2, screenHeight); // Setup player two camera and screen + Camera cameraPlayer2 = { 0 }; cameraPlayer2.fovy = 45.0f; cameraPlayer2.up.y = 1.0f; cameraPlayer2.target.y = 3.0f; @@ -73,6 +49,10 @@ int main(void) // 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; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -116,26 +96,69 @@ int main(void) // Draw Player1 view to the render texture BeginTextureMode(screenPlayer1); ClearBackground(SKYBLUE); + BeginMode3D(cameraPlayer1); - DrawScene(); + + // 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 + + for (float x = -count*spacing; x <= count*spacing; x += spacing) + { + for (float z = -count*spacing; z <= count*spacing; z += spacing) + { + DrawCube((Vector3) { x, 1.5f, z }, 1, 1, 1, LIME); + DrawCube((Vector3) { x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN); + } + } + + // Draw a cube at each player's position + DrawCube(cameraPlayer1.position, 1, 1, 1, RED); + DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE); + EndMode3D(); - DrawText("PLAYER1 W/S to move", 10, 10, 20, RED); + + 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); - DrawScene(); + + // 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 + + for (float x = -count*spacing; x <= count*spacing; x += spacing) + { + for (float z = -count*spacing; z <= count*spacing; z += spacing) + { + DrawCube((Vector3) { x, 1.5f, z }, 1, 1, 1, LIME); + DrawCube((Vector3) { x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN); + } + } + + // Draw a cube at each player's position + DrawCube(cameraPlayer1.position, 1, 1, 1, RED); + DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE); + EndMode3D(); - DrawText("PLAYER2 UP/DOWN to move", 10, 10, 20, BLUE); + + 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/examples/core/core_3d_camera_split_screen.png b/examples/core/core_3d_camera_split_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..bc323d6adbb22a04677a9435398234601dd45429 GIT binary patch literal 16165 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 D;+@s` literal 0 HcmV?d00001 diff --git a/examples/core/core_camera_2d_split_screen.c b/examples/core/core_camera_2d_split_screen.c deleted file mode 100644 index 5f5059569..000000000 --- a/examples/core/core_camera_2d_split_screen.c +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************************************* -* -* raylib [core] example - split screen -* -* Addapted from the Split Screen example (https://github.com/raysan5/raylib/blob/master/examples/core/core_split_screen.c) -* -* Example originally created with raylib 4.5, last time updated with raylib 4.5 -* -* 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) 2021-2023 Jeffery Myers (@JeffM2501) -* -********************************************************************************************/ - -#include "raylib.h" - -#include - -#define PLAYER_SIZE 40 - -//------------------------------------------------------------------------------------ -// Program main entry point -//------------------------------------------------------------------------------------ -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 440; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - camera 2D split screen"); - - Rectangle player1 = { 200, 200, PLAYER_SIZE, PLAYER_SIZE }; - Rectangle player2 = { 250, 200, PLAYER_SIZE, PLAYER_SIZE }; - - Camera2D camera1 = { 0 }; - camera1.target = (Vector2){ player1.x, player1.y }; - camera1.offset = (Vector2){ 200.0f, 200.0f }; - camera1.rotation = 0.0f; - camera1.zoom = 1.0f; - - Camera2D camera2 = { 0 }; - camera2.target = (Vector2){ player2.x, player2.y }; - camera2.offset = (Vector2){ 200.0f, 200.0f }; - camera2.rotation = 0.0f; - camera2.zoom = 1.0f; - - RenderTexture screenCamera1 = LoadRenderTexture(screenWidth / 2, screenHeight); - RenderTexture screenCamera2 = 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)screenCamera1.texture.width, (float)-screenCamera1.texture.height }; - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //-------------------------------------------------------------------------------------- - void DrawScene(void) { - for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) - { - DrawLineV((Vector2){PLAYER_SIZE*i, 0}, (Vector2){PLAYER_SIZE*i, screenHeight}, LIGHTGRAY); - } - - for (int i = 0; i < screenHeight/PLAYER_SIZE + 1; i++) - { - DrawLineV((Vector2){0, PLAYER_SIZE*i}, (Vector2){screenWidth, PLAYER_SIZE*i}, LIGHTGRAY); - } - - for (int i = 0; i < screenWidth/PLAYER_SIZE; i++) - { - for (int j = 0; j < screenHeight/PLAYER_SIZE; j++) - { - char coordinate_str[8]; - snprintf(coordinate_str, sizeof(coordinate_str), "%d,%d", i, j); - DrawText(coordinate_str, 10 + PLAYER_SIZE*i, 10 + PLAYER_SIZE*j, 10, LIGHTGRAY); - } - } - - DrawRectangleRec(player1, RED); - DrawRectangleRec(player2, BLUE); - } - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyDown(KEY_S)) player1.y += 3; - else if (IsKeyDown(KEY_W)) player1.y -= 3; - if (IsKeyDown(KEY_D)) player1.x += 3; - else if (IsKeyDown(KEY_A)) player1.x -= 3; - - if (IsKeyDown(KEY_UP)) player2.y += 3; - else if (IsKeyDown(KEY_DOWN)) player2.y -= 3; - if (IsKeyDown(KEY_RIGHT)) player2.x += 3; - else if (IsKeyDown(KEY_LEFT)) player2.x -= 3; - - camera1.target = (Vector2){ player1.x, player1.y }; - camera2.target = (Vector2){ player2.x, player2.y }; - - // Draw - //---------------------------------------------------------------------------------- - BeginTextureMode(screenCamera1); - ClearBackground(RAYWHITE); - BeginMode2D(camera1); - DrawScene(); - EndMode2D(); - DrawText("PLAYER1 W/S/A/D to move", 10, 10, 15, RED); - EndTextureMode(); - - BeginTextureMode(screenCamera2); - ClearBackground(RAYWHITE); - BeginMode2D(camera2); - DrawScene(); - EndMode2D(); - DrawText("PLAYER2 UP/DOWN/LEFT/RIGHT to move", 10, 10, 15, BLUE); - 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); - EndDrawing(); - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadRenderTexture(screenCamera1); // Unload render texture - UnloadRenderTexture(screenCamera2); // Unload render texture - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} diff --git a/examples/core/core_camera_2d_split_screen.png b/examples/core/core_camera_2d_split_screen.png deleted file mode 100644 index ed5aaa58bfc2bb8124098d2b5c583961414bc218..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20010 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 diff --git a/examples/core/core_split_screen.png b/examples/core/core_split_screen.png deleted file mode 100644 index eace9027b0710f3a743641e6ca7d4930295d5603..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21609 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 0) + { + points[selectedPoint] = GetMousePosition(); + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedPoint = -1; + } + + // TODO: Cubic Bezier spline control points logic + + + // Spline selection logic + if (IsKeyPressed(KEY_ONE)) splineType = 0; + else if (IsKeyPressed(KEY_TWO)) splineType = 1; + else if (IsKeyPressed(KEY_THREE)) splineType = 2; + else if (IsKeyPressed(KEY_FOUR)) splineType = 3; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (splineType == 0) // Linear + { + // Draw linear spline + for (int i = 0; i < pointCount - 1; i++) + { + DrawLineEx(points[i], points[i + 1], 2.0f, RED); + } + } + else if (splineType == 1) // B-Spline + { + // Draw b-spline + DrawLineBSpline(points, pointCount, 2.0f, RED); + //for (int i = 0; i < (pointCount - 3); i++) DrawLineBSplineSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, BLUE); + } + else if (splineType == 2) // CatmullRom Spline + { + // Draw spline: catmull-rom + DrawLineCatmullRom(points, pointCount, 2.0f, RED); + //for (int i = 0; i < (pointCount - 3); i++) DrawLineCatmullRomSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, Fade(BLUE, 0.4f)); + } + else if (splineType == 3) // Cubic Bezier + { + // Draw line bezier cubic (with control points) + for (int i = 0; i < pointCount - 1; i++) + { + DrawLineBezierCubic(points[i], points[i + 1], control[i].start, control[i + 1].end, 2.0f, RED); + + // TODO: Every cubic bezier point should have two control points + DrawCircleV(control[i].start, 4, GOLD); + DrawCircleV(control[i].end, 4, GOLD); + DrawLineEx(points[i], control[i].start, 1.0, LIGHTGRAY); + DrawLineEx(points[i + 1], control[i].end, 1.0, LIGHTGRAY); + } + } + + // Draw control points + for (int i = 0; i < pointCount; i++) + { + DrawCircleV(points[i], 6.0f, RED); + if ((splineType != 0) && (i < pointCount - 1)) DrawLineV(points[i], points[i + 1], GRAY); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/projects/VS2022/examples/core_split_screen.vcxproj b/projects/VS2022/examples/core_2d_camera_split_screen.vcxproj similarity index 98% rename from projects/VS2022/examples/core_split_screen.vcxproj rename to projects/VS2022/examples/core_2d_camera_split_screen.vcxproj index 1005e2eaf..98ddc9ed3 100644 --- a/projects/VS2022/examples/core_split_screen.vcxproj +++ b/projects/VS2022/examples/core_2d_camera_split_screen.vcxproj @@ -35,11 +35,11 @@ - {946A1700-C7AA-46F0-AEF2-67C98B5722AC} + {CC62F7DB-D089-4677-8575-CAB7A7815C43} Win32Proj - core_split_screen + core_2d_camera_split_screen 10.0 - core_split_screen + core_2d_camera_split_screen @@ -374,7 +374,7 @@ - + diff --git a/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj b/projects/VS2022/examples/core_3d_camera_split_screen.vcxproj similarity index 99% rename from projects/VS2022/examples/core_camera_2d_split_screen.vcxproj rename to projects/VS2022/examples/core_3d_camera_split_screen.vcxproj index 1efc58a65..f39ccc205 100644 --- a/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj +++ b/projects/VS2022/examples/core_3d_camera_split_screen.vcxproj @@ -37,9 +37,9 @@ {946A1700-C7AA-46F0-AEF2-67C98B5722AC} Win32Proj - core_camera_2d_split_screen + core_3d_camera_split_screen 10.0 - core_camera_2d_split_screen + core_3d_camera_split_screen @@ -374,7 +374,7 @@ - + @@ -384,4 +384,4 @@ - + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 173637554..007f796f9 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -33,6 +33,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_free", "exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_mode", "examples\core_3d_camera_mode.vcxproj", "{6D1CA2F1-7FCA-4249-9220-075C2DF4F965}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_split_screen", "examples\core_3d_camera_split_screen.vcxproj", "{946A1700-C7AA-46F0-AEF2-67C98B5722AC}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_picking", "examples\core_3d_picking.vcxproj", "{FD193822-3D5C-4161-A147-884C2ABDE483}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_custom_logging", "examples\core_custom_logging.vcxproj", "{20AD0AC9-9159-4744-99CC-6AC5779D6B87}" @@ -233,8 +235,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "easings_testbed", "examples EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_standalone", "examples\rlgl_standalone.vcxproj", "{C8765523-58F8-4C8E-9914-693396F6F0FF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_split_screen", "examples\core_split_screen.vcxproj", "{946A1700-C7AA-46F0-AEF2-67C98B5722AC}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_vox", "examples\models_loading_vox.vcxproj", "{2F1B955B-275E-4D8E-8864-06FEC44D7912}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_gltf", "examples\models_loading_gltf.vcxproj", "{F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}" @@ -271,6 +271,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hybrid_render", "ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "examples\audio_sound_multi.vcxproj", "{F81C5819-85B4-4D2E-B6DC-104A7634461B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_split_screen", "examples\core_2d_camera_split_screen.vcxproj", "{CC62F7DB-D089-4677-8575-CAB7A7815C43}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -507,6 +509,22 @@ Global {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x64.Build.0 = Release|x64 {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x86.ActiveCfg = Release|Win32 {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x86.Build.0 = Release|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.ActiveCfg = Debug|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.Build.0 = Debug|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.ActiveCfg = Debug|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.Build.0 = Debug|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.ActiveCfg = Release|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.Build.0 = Release|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.ActiveCfg = Release|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.Build.0 = Release|Win32 {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 @@ -1975,22 +1993,6 @@ Global {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x64.Build.0 = Release|x64 {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.ActiveCfg = Release|Win32 {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.Build.0 = Release|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.ActiveCfg = Debug|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.Build.0 = Debug|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.ActiveCfg = Debug|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.Build.0 = Debug|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.ActiveCfg = Release|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.Build.0 = Release|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.ActiveCfg = Release|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.Build.0 = Release|Win32 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 @@ -2279,6 +2281,22 @@ Global {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.Build.0 = Release|x64 {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.ActiveCfg = Release|Win32 {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.Build.0 = Release|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x64.ActiveCfg = Debug|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x64.Build.0 = Debug|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x86.ActiveCfg = Debug|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x86.Build.0 = Debug|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.ActiveCfg = Release|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.Build.0 = Release|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.ActiveCfg = Release|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2297,6 +2315,7 @@ Global {557138B0-7BE2-4392-B2E2-B45734031A62} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {9EED87BB-527F-4D05-9384-6D16CFD627A8} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {6D1CA2F1-7FCA-4249-9220-075C2DF4F965} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {946A1700-C7AA-46F0-AEF2-67C98B5722AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {FD193822-3D5C-4161-A147-884C2ABDE483} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {20AD0AC9-9159-4744-99CC-6AC5779D6B87} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {0199E349-0701-40BC-8A7F-06A54FFA3E7C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -2397,7 +2416,6 @@ Global {FDE6080B-E203-4066-910D-AD0302566008} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {E1B6D565-9D7C-46B7-9202-ECF54974DE50} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {C8765523-58F8-4C8E-9914-693396F6F0FF} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {946A1700-C7AA-46F0-AEF2-67C98B5722AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {2F1B955B-275E-4D8E-8864-06FEC44D7912} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {F2DB2E59-76BF-4D81-859A-AFC289C046C0} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} @@ -2416,6 +2434,7 @@ Global {70B35F59-AFC2-4D8F-8833-5314D2047A81} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {CC62F7DB-D089-4677-8575-CAB7A7815C43} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 10e4aa32f86cb40e72b3ab076d87de6bf649f215 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Sep 2023 20:01:19 +0200 Subject: [PATCH 0622/1710] Update rtext.c --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 04d7a2a14..146fc68f4 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -788,7 +788,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp } } - // Copy pixel data from fc.data to atlas + // Copy pixel data from glyph image to atlas for (int y = 0; y < glyphs[i].image.height; y++) { for (int x = 0; x < glyphs[i].image.width; x++) From b68d0850b1dee42d818e67d99f4affe1d890dba1 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Sep 2023 20:01:52 +0200 Subject: [PATCH 0623/1710] Some code restructuring for input functions, consistency review --- src/rcore.c | 100 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index fcb222bb3..2f112aee5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -389,15 +389,15 @@ typedef struct CoreData { bool resizedLastFrame; // Check if window has been resized last frame bool eventWaiting; // Wait for events before ending frame - Point position; // Window position on screen (required on fullscreen toggle) + 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) Matrix screenScale; // Matrix to scale screen (framebuffer rendering) - Point previousPosition; // Previous screen position (required on borderless windowed toggle) - Size previousScreen; // Previous screen size (required on borderless windowed toggle) char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings @@ -3753,10 +3753,12 @@ void OpenURL(const char *url) // Check if a key has been pressed once bool IsKeyPressed(int key) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; bool pressed = false; - if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true; + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) + { + if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true; + } return pressed; } @@ -3764,26 +3766,38 @@ bool IsKeyPressed(int key) // Check if a key has been pressed again (only PLATFORM_DESKTOP) bool IsKeyPressedRepeat(int key) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; - if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) return true; - else return false; + 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) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; - if (CORE.Input.Keyboard.currentKeyState[key] == 1) return true; - else return false; + 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) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; bool released = false; - - if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true; + + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) + { + if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true; + } return released; } @@ -3791,9 +3805,14 @@ bool IsKeyReleased(int key) // Check if a key is NOT being pressed (key not held down) bool IsKeyUp(int key) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; - if (CORE.Input.Keyboard.currentKeyState[key] == 0) return true; - else return false; + 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 @@ -3806,7 +3825,7 @@ int GetKeyPressed(void) // Get character from the queue head value = CORE.Input.Keyboard.keyPressedQueue[0]; - // Shift elements 1 step toward the head. + // 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]; @@ -3828,7 +3847,7 @@ int GetCharPressed(void) // Get character from the queue head value = CORE.Input.Keyboard.charPressedQueue[0]; - // Shift elements 1 step toward the head. + // 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]; @@ -3864,18 +3883,23 @@ bool IsGamepadAvailable(int gamepad) // Get gamepad internal name id const char *GetGamepadName(int gamepad) { + const char *name = NULL; + #if defined(PLATFORM_DESKTOP) - if (CORE.Input.Gamepad.ready[gamepad]) return glfwGetJoystickName(gamepad); - else return NULL; + if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); #endif #if defined(PLATFORM_DRM) - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); - return CORE.Input.Gamepad.name[gamepad]; + if (CORE.Input.Gamepad.ready[gamepad]) + { + ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); + name = CORE.Input.Gamepad.name[gamepad]; + } #endif #if defined(PLATFORM_WEB) - return CORE.Input.Gamepad.name[gamepad]; + name = CORE.Input.Gamepad.name[gamepad]; #endif - return NULL; + + return name; } // Get gamepad axis count @@ -3915,12 +3939,12 @@ bool IsGamepadButtonPressed(int gamepad, int button) // Check if a gamepad button is being pressed bool IsGamepadButtonDown(int gamepad, int button) { - bool result = false; + bool down = false; if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && - (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) result = true; + (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) down = true; - return result; + return down; } // Check if a gamepad button has NOT been pressed once @@ -3937,12 +3961,12 @@ bool IsGamepadButtonReleased(int gamepad, int button) // Check if a gamepad button is NOT being pressed bool IsGamepadButtonUp(int gamepad, int button) { - bool result = false; + bool up = false; if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && - (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) result = true; + (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) up = true; - return result; + return up; } // Get the last gamepad button pressed @@ -3983,7 +4007,7 @@ bool IsMouseButtonDown(int button) if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true; - // Map touches to mouse buttons checking + // NOTE: Touches are considered like mouse buttons if (CORE.Input.Touch.currentTouchState[button] == 1) down = true; return down; @@ -4005,7 +4029,14 @@ bool IsMouseButtonReleased(int button) // Check if a mouse button is NOT being pressed bool IsMouseButtonUp(int button) { - return !IsMouseButtonDown(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 @@ -4032,10 +4063,13 @@ int GetMouseY(void) Vector2 GetMousePosition(void) { Vector2 position = { 0 }; + + // TODO: Review touch position on PLATFORM_WEB #if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) position = GetTouchPosition(0); #else + // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned 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; #endif From 37f60e75e72eebd5ff687b8857bf9043d291b0df Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Sat, 9 Sep 2023 03:47:07 -0400 Subject: [PATCH 0624/1710] Remove unneeded #if (#3301) Co-authored-by: MichaelFiber --- src/rcore.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 2f112aee5..8df087f83 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -265,11 +265,9 @@ #include // Linux: Keycodes constants definition (KEY_A, ...) #include // Linux: Joystick support library -#if defined(PLATFORM_DRM) #include // Generic Buffer Management (native platform for EGL on DRM) #include // Direct Rendering Manager user-level library interface #include // Direct Rendering Manager mode setting (KMS) interface -#endif #include "EGL/egl.h" // Native platform windowing system interface #include "EGL/eglext.h" // EGL extensions From b8cd10264b6d34ff4b09ccdd0b0f7b254cf3b122 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Sat, 9 Sep 2023 11:32:28 -0400 Subject: [PATCH 0625/1710] Revert "Disable UBSAN in zig builds. (#3292)" (#3303) This reverts commit a316f9e7fc7f8e06852a40544e57f89018672ee4. Issue #1891 was fixed again, so this is no longer needed. --- src/build.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/build.zig b/src/build.zig index 09d954bf5..27250f5ff 100644 --- a/src/build.zig +++ b/src/build.zig @@ -6,7 +6,6 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built "-std=gnu99", "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; const raylib = b.addStaticLibrary(.{ From 30f8dd6e377ba022a70ebdbac78a10f5b27af0eb Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Mon, 11 Sep 2023 13:00:30 -0400 Subject: [PATCH 0626/1710] rtextures: Fix ImageDraw() source clipping when drawing beyond top left (#3306) --- src/rtextures.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 4e2fdbd91..c86ebac39 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3481,7 +3481,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color // Destination rectangle out-of-bounds security checks if (dstRec.x < 0) { - srcRec.x = -dstRec.x; + srcRec.x -= dstRec.x; srcRec.width += dstRec.x; dstRec.x = 0; } @@ -3489,7 +3489,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color if (dstRec.y < 0) { - srcRec.y = -dstRec.y; + srcRec.y -= dstRec.y; srcRec.height += dstRec.y; dstRec.y = 0; } From e75f85ce58ccc73a54ec8116f4890247587727ab Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 11 Sep 2023 19:01:24 +0200 Subject: [PATCH 0627/1710] REVIEWED: `TextToPascal()` issue when first char is uppercase --- src/rtext.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rtext.c b/src/rtext.c index 146fc68f4..fb8440131 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1676,6 +1676,7 @@ const char *TextToPascal(const char *text) { // Upper case first character if ((text[0] >= 'a') && (text[0] <= 'z')) buffer[0] = text[0] - 32; + else buffer[0] = text[0]; // Check for next separator to upper case another character for (int i = 1, j = 1; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++) From 9d230d753b5dfb9c4f56b065833ac5ddaf265d8c Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Mon, 11 Sep 2023 17:03:33 +0000 Subject: [PATCH 0628/1710] Implement FLAG_WINDOW_RESIZABLE for web (#3305) Fixes #3231 --- src/rcore.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8df087f83..beb611040 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -914,9 +914,9 @@ void InitWindow(int width, int height, const char *title) // 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) - //emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); // Trigger this once to get initial window sizing - //EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); + 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); @@ -6165,8 +6165,8 @@ static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUi return 1; // The event was consumed by the callback handler } -EM_JS(int, GetCanvasWidth, (), { return canvas.clientWidth; }); -EM_JS(int, GetCanvasHeight, (), { return canvas.clientHeight; }); +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) @@ -6176,8 +6176,8 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent * // This event is called whenever the window changes sizes, // so the size of the canvas object is explicitly retrieved below - int width = GetCanvasWidth(); - int height = GetCanvasHeight(); + int width = GetWindowInnerWidth(); + int height = GetWindowInnerHeight(); emscripten_set_canvas_element_size("#canvas",width,height); SetupViewport(width, height); // Reset viewport and projection matrix for new size From 3ab7f70e3129be963812a9b4133278e8a5e41797 Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:19:43 +0100 Subject: [PATCH 0629/1710] Update BINDINGS.md (#3307) Fix Kaylib binding. Reroute to a new repository. Binding renamed. --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index bdd5e2558..c26fa873e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -37,7 +37,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | -| kaylib | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Soutaisei/Kaylib | +| KaylibKit | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/KaylibKit | | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | From 2e7a7877a51ca33487642bd7ed0367b017b54486 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 12 Sep 2023 15:11:16 +0200 Subject: [PATCH 0630/1710] Update webassembly.yml --- .github/workflows/webassembly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index b50cd8c46..9374cb9eb 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@master - name: Setup emsdk - uses: mymindstorm/setup-emsdk@v11 + uses: mymindstorm/setup-emsdk@v12 with: version: 3.1.30 actions-cache-folder: 'emsdk-cache' From 2b1849e57d23f3ba53a8ee5200303694f7a3a63a Mon Sep 17 00:00:00 2001 From: bohonghuang <1281299809@qq.com> Date: Wed, 13 Sep 2023 22:35:37 +0800 Subject: [PATCH 0631/1710] Add claw-raylib to BINDINGS.md (#3310) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index c26fa873e..28e2c542e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -12,6 +12,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | | claylib/wrap | **4.5** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | +| claw-raylib | **auto** | [Common Lisp](https://common-lisp.net/) | Apache-2.0 | https://github.com/bohonghuang/claw-raylib | | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.6-dev (5e1a81)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | From 719365f209d046f3fccefce2974b3dcb2e75cc8f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:37:11 -0300 Subject: [PATCH 0632/1710] Add SetWindowMaxSize for desktop and web (#3309) * Add SetWindowMaxSize for desktop and web * Remove SizeInt and respective adjustments --- src/raylib.h | 1 + src/rcore.c | 72 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 4f3e35380..94e79a5f7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -961,6 +961,7 @@ RLAPI void SetWindowTitle(const char *title); // Set title f RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) 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) diff --git a/src/rcore.c b/src/rcore.c index beb611040..771a8380d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -395,9 +395,11 @@ typedef struct CoreData { 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 windowMin; // Window minimum width and height (for resizable window) + Size windowMax; // Window 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) + char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings } Window; @@ -1787,9 +1789,36 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; #if defined(PLATFORM_DESKTOP) - const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - glfwSetWindowSizeLimits(CORE.Window.handle, width, height, mode->width, mode->height); + int minWidth = (CORE.Window.windowMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.width; + int minHeight = (CORE.Window.windowMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.height; + int maxWidth = (CORE.Window.windowMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.width; + int maxHeight = (CORE.Window.windowMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +#endif +#if defined(PLATFORM_WEB) + // 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); +#endif +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +#if defined(PLATFORM_DESKTOP) + int minWidth = (CORE.Window.windowMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.width; + int minHeight = (CORE.Window.windowMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.height; + int maxWidth = (CORE.Window.windowMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.width; + int maxHeight = (CORE.Window.windowMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +#endif +#if defined(PLATFORM_WEB) + // 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); #endif } @@ -3182,7 +3211,7 @@ bool DirectoryExists(const char *dirPath) 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 }; @@ -3195,11 +3224,11 @@ int GetFileLength(const char *fileName) { 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); } @@ -3765,12 +3794,12 @@ bool IsKeyPressed(int key) 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; } @@ -3778,12 +3807,12 @@ bool IsKeyPressedRepeat(int key) 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; } @@ -3791,7 +3820,7 @@ bool IsKeyDown(int key) 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; @@ -3804,12 +3833,12 @@ bool IsKeyReleased(int key) 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; } @@ -3887,7 +3916,7 @@ const char *GetGamepadName(int gamepad) if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); #endif #if defined(PLATFORM_DRM) - if (CORE.Input.Gamepad.ready[gamepad]) + if (CORE.Input.Gamepad.ready[gamepad]) { ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); name = CORE.Input.Gamepad.name[gamepad]; @@ -4061,7 +4090,7 @@ int GetMouseY(void) Vector2 GetMousePosition(void) { Vector2 position = { 0 }; - + // TODO: Review touch position on PLATFORM_WEB #if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) @@ -4220,6 +4249,12 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.screen.height = height; // User desired height CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + // Set the window minimum and maximum default values to 0 + CORE.Window.windowMin.width = 0; + CORE.Window.windowMin.height = 0; + CORE.Window.windowMax.width = 0; + CORE.Window.windowMax.height = 0; + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) @@ -6178,6 +6213,13 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent * // so the size of the canvas object is explicitly retrieved below int width = GetWindowInnerWidth(); int height = GetWindowInnerHeight(); + + if (width < CORE.Window.windowMin.width) width = CORE.Window.windowMin.width; + else if (width > CORE.Window.windowMax.width && CORE.Window.windowMax.width > 0) width = CORE.Window.windowMax.width; + + if (height < CORE.Window.windowMin.height) height = CORE.Window.windowMin.height; + else if (height > CORE.Window.windowMax.height && CORE.Window.windowMax.height > 0) height = CORE.Window.windowMax.height; + emscripten_set_canvas_element_size("#canvas",width,height); SetupViewport(width, height); // Reset viewport and projection matrix for new size From 528b8799550f8a9d759ef5db8c86d19923a79794 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 13 Sep 2023 17:05:22 +0200 Subject: [PATCH 0633/1710] Update rtextures.c --- src/rtextures.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index c86ebac39..a6741ee2a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -322,9 +322,10 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int Image LoadImageSvg(const char *fileNameOrString, int width, int height) { Image image = { 0 }; + +#if defined(SUPPORT_FILEFORMAT_SVG) bool isSvgStringValid = false; -#if defined(SUPPORT_FILEFORMAT_SVG) // Validate fileName or string if (fileNameOrString != NULL) { From 97ef81914ce5fe34b79e263814b65292a31edf42 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 13 Sep 2023 17:05:38 +0200 Subject: [PATCH 0634/1710] Reviewed parameters for consistency --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 94e79a5f7..07b64aeb6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -918,8 +918,8 @@ typedef enum { // Callbacks to hook some internal functions // WARNING: These callbacks are intended for advance users typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages -typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data -typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data +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 From 8a1779b2ad2d316532fb2ca32ed9939439b492b9 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 14 Sep 2023 06:57:23 -0300 Subject: [PATCH 0635/1710] Rename windowM* to screenM* (#3312) --- src/rcore.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 771a8380d..0d3c6e12a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -395,8 +395,8 @@ typedef struct CoreData { 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 windowMin; // Window minimum width and height (for resizable window) - Size windowMax; // Window maximum width and height (for resizable window) + 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) @@ -1789,13 +1789,13 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = height; + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; #if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.windowMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.width; - int minHeight = (CORE.Window.windowMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.height; - int maxWidth = (CORE.Window.windowMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.width; - int maxHeight = (CORE.Window.windowMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.height; + int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); #endif #if defined(PLATFORM_WEB) @@ -1807,13 +1807,13 @@ void SetWindowMinSize(int width, int height) // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { - CORE.Window.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; #if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.windowMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.width; - int minHeight = (CORE.Window.windowMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.height; - int maxWidth = (CORE.Window.windowMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.width; - int maxHeight = (CORE.Window.windowMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.height; + int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); #endif #if defined(PLATFORM_WEB) @@ -4249,11 +4249,11 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.screen.height = height; // User desired height CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - // Set the window minimum and maximum default values to 0 - CORE.Window.windowMin.width = 0; - CORE.Window.windowMin.height = 0; - CORE.Window.windowMax.width = 0; - CORE.Window.windowMax.height = 0; + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) @@ -6214,11 +6214,11 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent * int width = GetWindowInnerWidth(); int height = GetWindowInnerHeight(); - if (width < CORE.Window.windowMin.width) width = CORE.Window.windowMin.width; - else if (width > CORE.Window.windowMax.width && CORE.Window.windowMax.width > 0) width = CORE.Window.windowMax.width; + if (width < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; + else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; - if (height < CORE.Window.windowMin.height) height = CORE.Window.windowMin.height; - else if (height > CORE.Window.windowMax.height && CORE.Window.windowMax.height > 0) height = CORE.Window.windowMax.height; + if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; + else if (height > CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height; emscripten_set_canvas_element_size("#canvas",width,height); From 18b22523cc8cbc5c6ef4ab090f3284095e78c7dc Mon Sep 17 00:00:00 2001 From: turborium <45082001+turborium@users.noreply.github.com> Date: Fri, 15 Sep 2023 14:32:50 +0400 Subject: [PATCH 0636/1710] Update BINDINGS.md (#3317) Update TurboRaylib bindings --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 28e2c542e..b02bba8ab 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -50,7 +50,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-odin | **4.5** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | | raylib-ocaml | 4.2 | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | -| TurboRaylib | 4.2 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | +| TurboRaylib | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | From 06986f36b3ef80894e207725077cfe396fe839ef Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 15 Sep 2023 17:04:07 +0200 Subject: [PATCH 0637/1710] Update rmodels.c --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 9616f0ceb..1f8568625 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5564,7 +5564,7 @@ static Model LoadVOX(const char *fileName) #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, len); } +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 From f9c6ca468a32d5e9c33bd8cc040d33731356c8ce Mon Sep 17 00:00:00 2001 From: Wilson Silva Date: Sun, 17 Sep 2023 00:56:38 +0700 Subject: [PATCH 0638/1710] Update BINDINGS.md with vaiorabbit/raylib-bindings (#3318) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index b02bba8ab..0eec1dc24 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -114,6 +114,7 @@ These are older raylib bindings that are more than 2 versions old or have not be | raylib-pas | 3.0 | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) | https://github.com/tazdij/raylib-pas | | raylib-pascal | 2.0 | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) | https://github.com/drezgames/raylib-pascal | | Graphics-Raylib | 1.4 | [Perl](https://www.perl.org/) | https://github.com/athreef/Graphics-Raylib | +| raylib-bindings | 4.5 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/vaiorabbit/raylib-bindings | | raylib-ruby | 2.6 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/a0/raylib-ruby | | raylib-ruby-ffi | 2.0 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/D3nX/raylib-ruby-ffi | | raylib-mruby | 2.5-dev | [mruby](https://github.com/mruby/mruby) | https://github.com/lihaochen910/raylib-mruby | From 33cc18ed51ae9279dab4eac9f98bf303feebebe5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 17 Sep 2023 13:32:44 +0200 Subject: [PATCH 0639/1710] Update BINDINGS.md --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 0eec1dc24..118c0ab9d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -44,6 +44,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | +| raylib-bindings | **4.5** | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/vaiorabbit/raylib-bindings | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | @@ -114,7 +115,6 @@ These are older raylib bindings that are more than 2 versions old or have not be | raylib-pas | 3.0 | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) | https://github.com/tazdij/raylib-pas | | raylib-pascal | 2.0 | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) | https://github.com/drezgames/raylib-pascal | | Graphics-Raylib | 1.4 | [Perl](https://www.perl.org/) | https://github.com/athreef/Graphics-Raylib | -| raylib-bindings | 4.5 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/vaiorabbit/raylib-bindings | | raylib-ruby | 2.6 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/a0/raylib-ruby | | raylib-ruby-ffi | 2.0 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/D3nX/raylib-ruby-ffi | | raylib-mruby | 2.5-dev | [mruby](https://github.com/mruby/mruby) | https://github.com/lihaochen910/raylib-mruby | From acf211a5fa1f90cae7a8f8832e3a675720b02f73 Mon Sep 17 00:00:00 2001 From: Brian E <72316548+Brian-ED@users.noreply.github.com> Date: Sun, 17 Sep 2023 19:16:14 +0100 Subject: [PATCH 0640/1710] general syntax fixes (#3319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prettified a comment * fixed broken indentation caused by another commit. the commit renamed a bool to int and broke indentation: 233cf3970c9148c4963cfdea20e49e761104f9de * Changed 0.001 and 0.00001 to EPSILON This commit is untested. I don't know what consequences this has. Since the commits that added these numbers were before epsilon was added, I have assumed that epsilon could replace them. * Prettied up indentation in a few places * removed spacing around *, standardizing it. * I may have gotten overboard with indentation * removed a few useless parenthesis * Added fortran-raylib * Fix examples/others/rlgl_standalone.c compilation issue (#3242) * Update BINDINGS.md * Ignore unused return value of GetCodepointNext in GetCodepointCount (#3241) * Ignore unused return value of GetCodepointNext in GetCodepointCount Removes the last warning from non-external libraries when compiling with the default build configuration on x64 Linux. * Remove unnecessary void cast in GetCodepointCount * Fix #3246 * Revert "Fix #3246" This reverts commit e4dcbd518091a5854a517ea4cfc3f7e2d29de1a7. * Fix text_unicode.c example crashing (#3250) * Fix text_unicode.c example crashing * Adjust the text_unicode.c example crashing fix * tweaks * add build.zig options for individual modules (#3254) * Add `IsKeyPressedRepeat` (desktop only) (#3245) Since the key pressed are handle by comparing current vs previous state (ie frame), a special way is needed to handle key repeats. * Reviewed `IsKeyPressedRepeat()` #3248 * Update rcore.c (#3255) * Match CMakeOptions.txt options default values (#3258) * Fix SetClipboardText for web (#3257) * [Image] Validate that ImageDrawRectangleRec is drawing entirely inside the image (#3264) * Add a function to clone a sound and share data with another sound. * rename items based on feedback * PR Feedback, use custom unload for sound alias, not variant of normal sound unloading * sound_multi example * Validate that image rect drawing is inside the image so we don't overflow a buffer * remove files that should not have been added. * remove changes that should not have been * revert * adsfasdfsdfsdf * Add Vector3 Projecting and Rejection to Raymath (#3263) * Update raymath.h * formatting * [Feature] IsKey... safety checks and more (#3256) * [Feature] Add GetKeyRepeat * Update rcore.c * Simpler design, only one repeat per frame * Update config.h * Update rcore.c * Add KEYBOARD_KEYS_MASK * Update config.h * reversions * Update rcore.c * Update rcore.c * change docs * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update raylib.h * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Fix bug where default shaders was not linking. (#3261) * Formating review * Add missing cmake options (#3267) * Fix CMake extraneous -lglfw (#3266) Closes #3265. The problem: LIBS_PRIVATE is a list of library names (used by pkg-config), but the shared library of the same name doesn't always exist. * Fix example/models/models_loading_gltf.c controls (#3268) * Fix example/models/models_loading_m3d.c controls (#3269) * Remove e from secondes (#3270) * Fix example/audio/audio_module_player.c help instructions and small bug (#3272) * Fix example/audio/audio_module_player.c help instructions and small bug * Update example/audio/audio_module_player.png screenshot * Use type name instead of valid specifier long long --> long long int * REVIEWED: `GetFileLength()`, added comment #3262 * Update examples/models/models_loading_gltf.png;m3d.png screenshots (#3273) * Remove a duplicated screenshot and add missing one (#3275) * Add examples/shaders/shaders_lightmap.c to Makefiles (#3276) * Fix examples/others/easings_testbed.c help instructions and small tweak (#3277) * Fix examples/shaders/shaders_texture_outline.c help instructions (#3278) * Fix examples/shapes/shapes_collision_area.c help instructions (#3279) * RENAMED: LoadFont*() parameter names for consistency and coherence * Fix uninitialized thread-locals in stbi #3282 (#3283) * REVIEWED: Added `SetTextLineSpacing()` to multiline examples * REVIEWED: Data size type consistency between functions #3168 * Some tweaks * Use internal default allocators, instead of user-exposed ones * Added rudimentary SVG support. (#2738) * Added rudimentary SVG support. Added 2 functions ImageLoadSvg and ImageLoadSvgWithSize. * Added an example on how to use ImageLoadSvgWithSize and adjusted Makefiles accordingly. * Added actual correct example file. * Reviewed the code to keep the raylib coding conventions in mind. Moved the LoadImageSvg() code into LoadImage() guarded by SUPPORT_FILEFORMAT_SVG. Renamed LoadImageSvgWithSize() to LoadImageSvg(). Added a LoadImageSvgFromString() function to parse the loaded SVG into an actual image. This does the bulk of the work. * Fixed typo. --------- Co-authored-by: Ray * REVIEWED: `LoadImageSvg()` * REVIEWED: `LoadImageSvg()` * Add SUPPORT_FILEFORMAT_SVG to cmake (#3284) * Fix examples/textures/textures_fog_of_war.c help instructions (#3285) * Fix examples/textures/textures_image_rotate.c help instructions (#3286) * Update rtextures.c * Fix #3247 * Update config.h * Fix #3293 * Disable UBSAN in zig builds. (#3292) Zig debug builds automatically enable ubsan. As the fix for #1891 had to be reverted, debug builds using zig will crash like so: ``` Illegal instruction at address 0x3237d2 raylib/src/rlgl.h:3690:91: 0x3237d2 in rlDrawVertexArrayElements (/home/rcorre/src/raylib-zig-template/raylib/src/rcore.c) glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); ``` This disables UBSAN when using zig to build raylib. * Update README.md (#3290) specially -> especially * Update cmake SUPPORT_FILEFORMAT_SVG default value (#3291) * Mouse offset and scaling must be considered also on web! * Update rcore.c * Update Makefile : clean raygui.c & physac.c (#3296) * Remove PLATFORM_RPI (#3232) * Remove PLATFORM_RPI * remove build artifacts --------- Co-authored-by: MichaelFiber Co-authored-by: Ray * Review to avoid UBSAN complaining #1891 * added raylib-raku to bindings (#3299) * examples: core: adds 2D camera two player split screen (#3298) * Reviewed examples for consistency * Update rtext.c * Some code restructuring for input functions, consistency review * Remove unneeded #if (#3301) Co-authored-by: MichaelFiber * Revert "Disable UBSAN in zig builds. (#3292)" (#3303) This reverts commit a316f9e7fc7f8e06852a40544e57f89018672ee4. Issue #1891 was fixed again, so this is no longer needed. * rtextures: Fix ImageDraw() source clipping when drawing beyond top left (#3306) * REVIEWED: `TextToPascal()` issue when first char is uppercase * Implement FLAG_WINDOW_RESIZABLE for web (#3305) Fixes #3231 * Update BINDINGS.md (#3307) Fix Kaylib binding. Reroute to a new repository. Binding renamed. * Update webassembly.yml * Add claw-raylib to BINDINGS.md (#3310) * Add SetWindowMaxSize for desktop and web (#3309) * Add SetWindowMaxSize for desktop and web * Remove SizeInt and respective adjustments * Update rtextures.c * Reviewed parameters for consistency * Rename windowM* to screenM* (#3312) * Update BINDINGS.md (#3317) Update TurboRaylib bindings * Update rmodels.c * Update BINDINGS.md with vaiorabbit/raylib-bindings (#3318) * fixed spelling mistake * put back parenthesis * reverted major allignment changes * reverted parser output changes * reverted one more indentation change --------- Co-authored-by: Brian-E Co-authored-by: Ray Co-authored-by: ubkp <118854183+ubkp@users.noreply.github.com> Co-authored-by: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Co-authored-by: actondev (Christos) Co-authored-by: vitopigno <103512727+VitusVeit@users.noreply.github.com> Co-authored-by: Asdqwe Co-authored-by: Jeffery Myers Co-authored-by: Ethan Simpson Co-authored-by: Nickolas McDonald <43690021+n77y@users.noreply.github.com> Co-authored-by: Branimir Ričko Co-authored-by: iacore <74560659+iacore@users.noreply.github.com> Co-authored-by: Ethan Conneely Co-authored-by: Johannes Barthelmes <615914+jbarthelmes@users.noreply.github.com> Co-authored-by: bXi Co-authored-by: Ryan Roden-Corrent Co-authored-by: Ikko Eltociear Ashimine Co-authored-by: SuperUserNameMan Co-authored-by: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Co-authored-by: MichaelFiber Co-authored-by: Dan Vu Co-authored-by: Gabriel dos Santos Sanches Co-authored-by: Rob Loach Co-authored-by: Peter0x44 Co-authored-by: Kenta <106167071+Its-Kenta@users.noreply.github.com> Co-authored-by: bohonghuang <1281299809@qq.com> Co-authored-by: turborium <45082001+turborium@users.noreply.github.com> Co-authored-by: Wilson Silva --- src/raylib.h | 2 +- src/raymath.h | 67 +++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 07b64aeb6..98e5c48a0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1093,7 +1093,7 @@ RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filenam 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 if the running application (uses static string) +RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string) 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 FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths diff --git a/src/raymath.h b/src/raymath.h index 1fab43aad..48bae01a9 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -820,7 +820,7 @@ RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) Vector3 result = v; // Vector3Normalize(axis); - float length = sqrtf(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z); + float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); if (length == 0.0f) length = 1.0f; float ilength = 1.0f / length; axis.x *= ilength; @@ -829,19 +829,19 @@ RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) angle /= 2.0f; float a = sinf(angle); - float b = axis.x * a; - float c = axis.y * a; - float d = axis.z * a; + float b = axis.x*a; + float c = axis.y*a; + float d = axis.z*a; a = cosf(angle); Vector3 w = { b, c, d }; // Vector3CrossProduct(w, v) - Vector3 wv = { w.y * v.z - w.z * v.y, w.z * v.x - w.x * v.z, w.x * v.y - w.y * v.x }; + Vector3 wv = { w.y*v.z - w.z*v.y, w.z*v.x - w.x*v.z, w.x*v.y - w.y*v.x }; // Vector3CrossProduct(w, wv) - Vector3 wwv = { w.y * wv.z - w.z * wv.y, w.z * wv.x - w.x * wv.z, w.x * wv.y - w.y * wv.x }; + Vector3 wwv = { w.y*wv.z - w.z*wv.y, w.z*wv.x - w.x*wv.z, w.x*wv.y - w.y*wv.x }; - // Vector3Scale(wv, 2 * a) + // Vector3Scale(wv, 2*a) a *= 2; wv.x *= a; wv.y *= a; @@ -1091,18 +1091,17 @@ RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) RMAPI int Vector3Equals(Vector3 p, Vector3 q) { int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); return result; } -// Compute the direction of a refracted ray where v specifies the -// normalized direction of the incoming ray, n specifies the -// normalized normal vector of the interface of two optical media, -// and r specifies the ratio of the refractive index of the medium -// from where the ray comes to the refractive index of the medium -// on the other side of the surface +// Compute the direction of a refracted ray +// v: normalized direction of the incoming ray +// n: normalized normal vector of the interface of two optical media +// r: ratio of the refractive index of the medium from where the ray comes +// to the refractive index of the medium on the other side of the surface RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) { Vector3 result = { 0 }; @@ -1862,7 +1861,7 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) float halfTheta = acosf(cosHalfTheta); float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); - if (fabsf(sinHalfTheta) < 0.001f) + if (fabsf(sinHalfTheta) < EPSILON) { result.x = (q1.x*0.5f + q2.x*0.5f); result.y = (q1.y*0.5f + q2.y*0.5f); @@ -1917,9 +1916,9 @@ RMAPI Quaternion QuaternionFromMatrix(Matrix mat) { Quaternion result = { 0 }; - float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; - float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; - float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; + float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; + float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; + float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; int biggestIndex = 0; @@ -1942,34 +1941,34 @@ RMAPI Quaternion QuaternionFromMatrix(Matrix mat) biggestIndex = 3; } - float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f) * 0.5f; + float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f)*0.5f; float mult = 0.25f / biggestVal; switch (biggestIndex) { case 0: result.w = biggestVal; - result.x = (mat.m6 - mat.m9) * mult; - result.y = (mat.m8 - mat.m2) * mult; - result.z = (mat.m1 - mat.m4) * mult; + result.x = (mat.m6 - mat.m9)*mult; + result.y = (mat.m8 - mat.m2)*mult; + result.z = (mat.m1 - mat.m4)*mult; break; case 1: result.x = biggestVal; - result.w = (mat.m6 - mat.m9) * mult; - result.y = (mat.m1 + mat.m4) * mult; - result.z = (mat.m8 + mat.m2) * mult; + result.w = (mat.m6 - mat.m9)*mult; + result.y = (mat.m1 + mat.m4)*mult; + result.z = (mat.m8 + mat.m2)*mult; break; case 2: result.y = biggestVal; - result.w = (mat.m8 - mat.m2) * mult; - result.x = (mat.m1 + mat.m4) * mult; - result.z = (mat.m6 + mat.m9) * mult; + result.w = (mat.m8 - mat.m2)*mult; + result.x = (mat.m1 + mat.m4)*mult; + result.z = (mat.m6 + mat.m9)*mult; break; case 3: result.z = biggestVal; - result.w = (mat.m1 - mat.m4) * mult; - result.x = (mat.m8 + mat.m2) * mult; - result.y = (mat.m6 + mat.m9) * mult; + result.w = (mat.m1 - mat.m4)*mult; + result.x = (mat.m8 + mat.m2)*mult; + result.y = (mat.m6 + mat.m9)*mult; break; } @@ -2075,7 +2074,7 @@ RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle float resAngle = 2.0f*acosf(q.w); float den = sqrtf(1.0f - q.w*q.w); - if (den > 0.0001f) + if (den > EPSILON) { resAxis.x = q.x/den; resAxis.y = q.y/den; @@ -2158,7 +2157,7 @@ RMAPI int QuaternionEquals(Quaternion p, Quaternion q) ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || - (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); From 97c4333803234845c0535d4c53985268aeaf4798 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 17 Sep 2023 20:42:45 +0200 Subject: [PATCH 0641/1710] REVIEWED: `UnloadRenderTexture()`, additional check --- src/rtextures.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index a6741ee2a..8dc49fac5 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3793,8 +3793,11 @@ void UnloadRenderTexture(RenderTexture2D target) { if (target.id > 0) { - // Color texture attached to FBO is deleted - rlUnloadTexture(target.texture.id); + if (target.texture.id > 0) + { + // Color texture attached to FBO is deleted + rlUnloadTexture(target.texture.id); + } // NOTE: Depth texture/renderbuffer is automatically // queried and deleted before deleting framebuffer From 421ae5bbd8da0e52b32b943232def3c291145338 Mon Sep 17 00:00:00 2001 From: Tobias Mock Date: Sun, 17 Sep 2023 22:52:34 +0200 Subject: [PATCH 0642/1710] Update raylib-ocaml bindings to 4.5 version (#3322) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 118c0ab9d..9fe86919d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -50,7 +50,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | | raylib-odin | **4.5** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | -| raylib-ocaml | 4.2 | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | +| raylib-ocaml | **4.5** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | | TurboRaylib | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | From b9acbbbc66abfa7ffb26ece2f3c860743693f6a6 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 18 Sep 2023 11:50:37 +0200 Subject: [PATCH 0643/1710] Fix #3323 --- src/rcore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 0d3c6e12a..cba3bbc12 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6966,10 +6966,10 @@ static void *EventThread(void *arg) // Update touch point count CORE.Input.Touch.pointCount = 0; - if (CORE.Input.Touch.position[0].x >= 0) CORE.Input.Touch.pointCount++; - if (CORE.Input.Touch.position[1].x >= 0) CORE.Input.Touch.pointCount++; - if (CORE.Input.Touch.position[2].x >= 0) CORE.Input.Touch.pointCount++; - if (CORE.Input.Touch.position[3].x >= 0) CORE.Input.Touch.pointCount++; + 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) // PLATFORM_DRM if (gestureUpdate) From 5c9cc3f9f77d64a9fe9cc0d374da8ed4741fcaa5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 18 Sep 2023 12:07:40 +0200 Subject: [PATCH 0644/1710] Reviewed #3323 --- src/rcore.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index cba3bbc12..0cfaed70f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6979,15 +6979,11 @@ static void *EventThread(void *arg) gestureEvent.touchAction = touchAction; gestureEvent.pointCount = CORE.Input.Touch.pointCount; - gestureEvent.pointId[0] = 0; - gestureEvent.pointId[1] = 1; - gestureEvent.pointId[2] = 2; - gestureEvent.pointId[3] = 3; - - gestureEvent.position[0] = CORE.Input.Touch.position[0]; - gestureEvent.position[1] = CORE.Input.Touch.position[1]; - gestureEvent.position[2] = CORE.Input.Touch.position[2]; - gestureEvent.position[3] = CORE.Input.Touch.position[3]; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + gestureEvent.pointId[i] = i; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + } ProcessGestureEvent(gestureEvent); } From 4d2906b0a5766823cbd9808431656140dae881b9 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Mon, 18 Sep 2023 18:50:22 +0200 Subject: [PATCH 0645/1710] Move mutex initialization before `ma_device_start()` (#3325) --- src/raudio.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 4627cdf5c..61e00a386 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -474,6 +474,16 @@ void InitAudioDevice(void) return; } + // Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may + // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. + if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS) + { + TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing"); + ma_device_uninit(&AUDIO.System.device); + ma_context_uninit(&AUDIO.System.context); + return; + } + // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running // while there's at least one sound being played. result = ma_device_start(&AUDIO.System.device); @@ -485,16 +495,6 @@ void InitAudioDevice(void) return; } - // Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may - // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. - if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS) - { - TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing"); - ma_device_uninit(&AUDIO.System.device); - ma_context_uninit(&AUDIO.System.context); - return; - } - TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully"); TRACELOG(LOG_INFO, " > Backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend)); TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat)); From eb461512a7191896acda33330e643b0dd60365b3 Mon Sep 17 00:00:00 2001 From: Christopher Odom Date: Mon, 18 Sep 2023 13:39:12 -0400 Subject: [PATCH 0646/1710] Added UBSAN complaint fix to rLoadTexture #1891 (#3321) --- src/rlgl.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 6dee60f95..bef4fdc2d 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3009,11 +3009,15 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned char *dataPtr = (unsigned char*)data; + if (mipOffset > 0) dataPtr = (unsigned char*)data + mipOffset; + if (glInternalFormat != -1) { - if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, (unsigned char *)data + mipOffset); + if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, dataPtr); #if !defined(GRAPHICS_API_OPENGL_11) - else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, (unsigned char *)data + mipOffset); + else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, dataPtr); #endif #if defined(GRAPHICS_API_OPENGL_33) From b3359276654c410033c8d194c0188d2257029f1d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 18 Sep 2023 19:43:10 +0200 Subject: [PATCH 0647/1710] Reviewed PR #3321 --- src/rlgl.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index bef4fdc2d..fe69afd6f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2997,7 +2997,10 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipWidth = width; int mipHeight = height; - int mipOffset = 0; // Mipmap data offset + int mipOffset = 0; // Mipmap data offset, only used for tracelog + + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned char *dataPtr = (unsigned char *)data; // Load the different mipmap levels for (int i = 0; i < mipmapCount; i++) @@ -3009,10 +3012,6 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); - // NOTE: Added pointer math separately from function to avoid UBSAN complaining - unsigned char *dataPtr = (unsigned char*)data; - if (mipOffset > 0) dataPtr = (unsigned char*)data + mipOffset; - if (glInternalFormat != -1) { if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, dataPtr); @@ -3040,7 +3039,8 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, mipWidth /= 2; mipHeight /= 2; - mipOffset += mipSize; + mipOffset += mipSize; // Increment offset position to next mipmap + dataPtr += mipSize; // Increment data pointer to next mipmap // Security check for NPOT textures if (mipWidth < 1) mipWidth = 1; From c9020ece5da5dcb4760b32080fdeddbba96d885c Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 19 Sep 2023 18:52:40 +0200 Subject: [PATCH 0648/1710] Update linux.yml --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 3c1ef62b8..d740250e0 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -22,7 +22,7 @@ jobs: build: permissions: contents: write # for actions/upload-release-asset to upload release asset - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: fail-fast: false max-parallel: 1 From 36e99860ee0c09b87d110f3def6997930d80232e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Sep 2023 21:22:04 +0200 Subject: [PATCH 0649/1710] Added note about WebGL warning --- src/rlgl.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index fe69afd6f..9ed484408 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3403,7 +3403,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) // Attach our texture to FBO 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)); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); @@ -3541,11 +3541,14 @@ bool rlFramebufferComplete(unsigned int id) void rlUnloadFramebuffer(unsigned int id) { #if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) - // Query depth attachment to automatically delete texture/renderbuffer int depthType = 0, depthId = 0; glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType); + + // 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/ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId); unsigned int depthIdU = (unsigned int)depthId; From a2b3b1ebe43cdf394b49f901cbaedb2c87959168 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Sep 2023 21:23:43 +0200 Subject: [PATCH 0650/1710] REVIEWED: `CompressData()`, possible stack overflow `struct sdefl` is almost 1MB, in some platforms it could generated a stack overflow, for example WebAssembly, where by default stack is only 65KB! --- src/rcore.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 0cfaed70f..26ff79d9a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3589,11 +3589,13 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa #if defined(SUPPORT_COMPRESSION_API) // Compress data and generate a valid DEFLATE stream - struct sdefl sdefl = { 0 }; - int bounds = sdefl_bound(dataSize); + struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB + int bounds = dataSize*2;//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 - + + *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 From 577a8de7c059504178a6e5a749d52f543ba199fc Mon Sep 17 00:00:00 2001 From: Brian E <72316548+Brian-ED@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:48:48 +0100 Subject: [PATCH 0651/1710] [raymath] Added macros for EPSILON on each function it's used in (#3330) * Added macros for EPSILON This is so the functions can be easily copied and used. * used `#if !defined()` instead of `#ifndef` --------- Co-authored-by: Brian-E --- src/raymath.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/raymath.h b/src/raymath.h index 48bae01a9..db04c51ee 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -214,6 +214,10 @@ RMAPI float Wrap(float value, float min, float max) // Check whether two given floats are almost equal RMAPI int FloatEquals(float x, float y) { +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); return result; @@ -506,6 +510,10 @@ RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) // Check whether two given vectors are almost equal RMAPI int Vector2Equals(Vector2 p, Vector2 q) { +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); @@ -1090,6 +1098,10 @@ RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) // Check whether two given vectors are almost equal RMAPI int Vector3Equals(Vector3 p, Vector3 q) { +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); @@ -1846,6 +1858,10 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) { Quaternion result = { 0 }; +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; if (cosHalfTheta < 0) @@ -2153,6 +2169,10 @@ RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) // Check whether two given quaternions are almost equal RMAPI int QuaternionEquals(Quaternion p, Quaternion q) { +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && From 477f5e5436e57f4dc4cadd4b8a2c9d78ff3d0c0e Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 21 Sep 2023 23:54:59 +0200 Subject: [PATCH 0652/1710] Update miniaudio v0.11.16 --> v0.11.18 --- src/external/miniaudio.h | 22009 ++++++++++++++++++------------------- 1 file changed, 10572 insertions(+), 11437 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 35eaafdcf..181f45289 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.16 - 2023-05-15 +miniaudio - v0.11.18 - 2023-08-07 David Reid - mackron@gmail.com @@ -538,6 +538,20 @@ you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link wi The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use `-std=c*` compiler flags, nor `-ansi`. +You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling +with the following options: + + -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY + +An example for compiling with AudioWorklet support might look like this: + + emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY + +To run locally, you'll need to use emrun: + + emrun bin/program.html + + 2.7. Build Options ------------------ @@ -2460,37 +2474,18 @@ used. The same general process applies to detachment. See `ma_node_attach_output 8. Decoding =========== The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from -devices and can be used independently. The following formats are supported: +devices and can be used independently. Built-in support is included for the following formats: - +---------+------------------+----------+ - | Format | Decoding Backend | Built-In | - +---------+------------------+----------+ - | WAV | dr_wav | Yes | - | MP3 | dr_mp3 | Yes | - | FLAC | dr_flac | Yes | - | Vorbis | stb_vorbis | No | - +---------+------------------+----------+ + +---------+ + | Format | + +---------+ + | WAV | + | MP3 | + | FLAC | + +---------+ -Vorbis is supported via stb_vorbis which can be enabled by including the header section before the -implementation of miniaudio, like the following: - - ```c - #define STB_VORBIS_HEADER_ONLY - #include "extras/stb_vorbis.c" // Enables Vorbis decoding. - - #define MINIAUDIO_IMPLEMENTATION - #include "miniaudio.h" - - // The stb_vorbis implementation must come after the implementation of miniaudio. - #undef STB_VORBIS_HEADER_ONLY - #include "extras/stb_vorbis.c" - ``` - -A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio). - -Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the -built-in decoders by specifying one or more of the following options before the miniaudio -implementation: +You can disable the built-in decoders by specifying one or more of the following options before the +miniaudio implementation: ```c #define MA_NO_WAV @@ -2498,8 +2493,8 @@ implementation: #define MA_NO_FLAC ``` -Disabling built-in decoding libraries is useful if you use these libraries independently of the -`ma_decoder` API. +miniaudio supports the ability to plug in custom decoders. See the section below for details on how +to use custom decoders. A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is @@ -2640,8 +2635,7 @@ opportunity to clean up and internal data. 9. Encoding =========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV -which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio. +The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. This can be disabled by specifying the following option before the implementation of miniaudio: ```c @@ -3722,7 +3716,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 16 +#define MA_VERSION_REVISION 18 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3914,6 +3908,13 @@ typedef ma_uint16 wchar_t; #ifdef _MSC_VER #define MA_INLINE __forceinline + + /* noinline was introduced in Visual Studio 2005. */ + #if _MSC_VER >= 1400 + #define MA_NO_INLINE __declspec(noinline) + #else + #define MA_NO_INLINE + #endif #elif defined(__GNUC__) /* I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when @@ -3930,45 +3931,59 @@ typedef ma_uint16 wchar_t; #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) + #define MA_NO_INLINE __attribute__((noinline)) #else #define MA_INLINE MA_GNUC_INLINE_HINT + #define MA_NO_INLINE __attribute__((noinline)) #endif #elif defined(__WATCOMC__) #define MA_INLINE __inline + #define MA_NO_INLINE #else #define MA_INLINE + #define MA_NO_INLINE +#endif + +/* MA_DLL is not officially supported. You're on your own if you want to use this. */ +#if defined(MA_DLL) + #if defined(_WIN32) + #define MA_DLL_IMPORT __declspec(dllimport) + #define MA_DLL_EXPORT __declspec(dllexport) + #define MA_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define MA_DLL_IMPORT __attribute__((visibility("default"))) + #define MA_DLL_EXPORT __attribute__((visibility("default"))) + #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define MA_DLL_IMPORT + #define MA_DLL_EXPORT + #define MA_DLL_PRIVATE static + #endif + #endif #endif #if !defined(MA_API) #if defined(MA_DLL) - #if defined(_WIN32) - #define MA_DLL_IMPORT __declspec(dllimport) - #define MA_DLL_EXPORT __declspec(dllexport) - #define MA_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define MA_DLL_IMPORT __attribute__((visibility("default"))) - #define MA_DLL_EXPORT __attribute__((visibility("default"))) - #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define MA_DLL_IMPORT - #define MA_DLL_EXPORT - #define MA_DLL_PRIVATE static - #endif - #endif - #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) #define MA_API MA_DLL_EXPORT #else #define MA_API MA_DLL_IMPORT #endif - #define MA_PRIVATE MA_DLL_PRIVATE #else #define MA_API extern + #endif +#endif + +#if !defined(MA_STATIC) + #if defined(MA_DLL) + #define MA_PRIVATE MA_DLL_PRIVATE + #else #define MA_PRIVATE static #endif #endif + /* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ #define MA_SIMD_ALIGNMENT 32 @@ -4166,28 +4181,31 @@ typedef enum MA_CANCELLED = -51, MA_MEMORY_ALREADY_MAPPED = -52, + /* General non-standard errors. */ + MA_CRC_MISMATCH = -100, + /* General miniaudio-specific errors. */ - MA_FORMAT_NOT_SUPPORTED = -100, - MA_DEVICE_TYPE_NOT_SUPPORTED = -101, - MA_SHARE_MODE_NOT_SUPPORTED = -102, - MA_NO_BACKEND = -103, - MA_NO_DEVICE = -104, - MA_API_NOT_FOUND = -105, - MA_INVALID_DEVICE_CONFIG = -106, - MA_LOOP = -107, - MA_BACKEND_NOT_ENABLED = -108, + MA_FORMAT_NOT_SUPPORTED = -200, + MA_DEVICE_TYPE_NOT_SUPPORTED = -201, + MA_SHARE_MODE_NOT_SUPPORTED = -202, + MA_NO_BACKEND = -203, + MA_NO_DEVICE = -204, + MA_API_NOT_FOUND = -205, + MA_INVALID_DEVICE_CONFIG = -206, + MA_LOOP = -207, + MA_BACKEND_NOT_ENABLED = -208, /* State errors. */ - MA_DEVICE_NOT_INITIALIZED = -200, - MA_DEVICE_ALREADY_INITIALIZED = -201, - MA_DEVICE_NOT_STARTED = -202, - MA_DEVICE_NOT_STOPPED = -203, + MA_DEVICE_NOT_INITIALIZED = -300, + MA_DEVICE_ALREADY_INITIALIZED = -301, + MA_DEVICE_NOT_STARTED = -302, + MA_DEVICE_NOT_STOPPED = -303, /* Operation errors. */ - MA_FAILED_TO_INIT_BACKEND = -300, - MA_FAILED_TO_OPEN_BACKEND_DEVICE = -301, - MA_FAILED_TO_START_BACKEND_DEVICE = -302, - MA_FAILED_TO_STOP_BACKEND_DEVICE = -303 + MA_FAILED_TO_INIT_BACKEND = -400, + MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, + MA_FAILED_TO_START_BACKEND_DEVICE = -402, + MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 } ma_result; @@ -5039,13 +5057,14 @@ typedef struct float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ float volumeEnd; ma_uint64 lengthInFrames; /* The total length of the fade. */ - ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */ + ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ } ma_fader; MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); +MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); MA_API float ma_fader_get_current_volume(const ma_fader* pFader); @@ -7090,7 +7109,7 @@ struct ma_device_config /* -The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`. +The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. Parameters @@ -7664,6 +7683,8 @@ struct ma_context ma_proc RegOpenKeyExA; ma_proc RegCloseKey; ma_proc RegQueryValueExA; + + /*HRESULT*/ long CoInitializeResult; } win32; #endif #ifdef MA_POSIX @@ -7943,21 +7964,12 @@ struct ma_device struct { /* AudioWorklets path. */ - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextPlayback; - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextCapture; - /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodePlayback; - /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodeCapture; - size_t intermediaryBufferSizeInFramesPlayback; - size_t intermediaryBufferSizeInFramesCapture; - float* pIntermediaryBufferPlayback; - float* pIntermediaryBufferCapture; - void* pStackBufferPlayback; - void* pStackBufferCapture; - ma_bool32 isInitialized; - - /* ScriptProcessorNode path. */ - int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */ - int indexCapture; + /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; + /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; + float* pIntermediaryBuffer; + void* pStackBuffer; + ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ + int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ } webaudio; #endif #ifdef MA_SUPPORT_NULL @@ -10045,7 +10057,7 @@ struct ma_encoder ma_encoder_uninit_proc onUninit; ma_encoder_write_pcm_frames_proc onWritePCMFrames; void* pUserData; - void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */ + void* pInternalEncoder; union { struct @@ -10110,6 +10122,33 @@ MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double freque MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double dutyCycle; + double amplitude; + double frequency; +} ma_pulsewave_config; + +MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); + +typedef struct +{ + ma_waveform waveform; + ma_pulsewave_config config; +} ma_pulsewave; + +MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); +MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); +MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); +MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); +MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); +MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); +MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); + typedef enum { ma_noise_type_white, @@ -11035,6 +11074,15 @@ typedef struct MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ + /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ + struct + { + ma_atomic_float volumeBeg; + ma_atomic_float volumeEnd; + ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ + ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ + } fadeSettings; + /* Memory management. */ ma_bool8 _ownsHeap; void* _pHeap; @@ -11115,6 +11163,8 @@ typedef ma_sound ma_sound_group; MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ +typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); + typedef struct { #if !defined(MA_NO_RESOURCE_MANAGER) @@ -11124,6 +11174,7 @@ typedef struct ma_context* pContext; ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ + ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ ma_device_notification_proc notificationCallback; #endif ma_log* pLog; /* When set to NULL, will use the context's log. */ @@ -11140,6 +11191,8 @@ typedef struct ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ + ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ + void* pProcessUserData; /* User data that's passed into onProcess. */ } ma_engine_config; MA_API ma_engine_config ma_engine_config_init(void); @@ -11167,6 +11220,8 @@ struct ma_engine ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ ma_uint32 defaultVolumeSmoothTimeInPCMFrames; ma_mono_expansion_mode monoExpansionMode; + ma_engine_process_proc onProcess; + void* pProcessUserData; }; MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); @@ -11191,7 +11246,9 @@ MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); MA_API ma_result ma_engine_start(ma_engine* pEngine); MA_API ma_result ma_engine_stop(ma_engine* pEngine); MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); +MA_API float ma_engine_get_volume(ma_engine* pEngine); MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); +MA_API float ma_engine_get_gain_db(ma_engine* pEngine); MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); @@ -11225,6 +11282,8 @@ MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); MA_API ma_result ma_sound_start(ma_sound* pSound); MA_API ma_result ma_sound_stop(ma_sound* pSound); +MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ +MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); MA_API float ma_sound_get_volume(const ma_sound* pSound); MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); @@ -11267,13 +11326,18 @@ MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); +MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); +MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); @@ -11419,6 +11483,7 @@ IMPLEMENTATION #endif +/* Architecture Detection */ #if !defined(MA_64BIT) && !defined(MA_32BIT) #ifdef _WIN32 #ifdef _WIN64 @@ -11448,12 +11513,18 @@ IMPLEMENTATION #endif #endif -/* Architecture Detection */ +#if defined(__arm__) || defined(_M_ARM) +#define MA_ARM32 +#endif +#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#define MA_ARM64 +#endif + #if defined(__x86_64__) || defined(_M_X64) #define MA_X64 #elif defined(__i386) || defined(_M_IX86) #define MA_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#elif defined(MA_ARM32) || defined(MA_ARM64) #define MA_ARM #endif @@ -11547,7 +11618,7 @@ IMPLEMENTATION What's basically happening is that we're saving and restoring the ebx register manually. */ - #if defined(DRFLAC_X86) && defined(__PIC__) + #if defined(MA_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" @@ -12296,8 +12367,11 @@ Return Values: 34: ERANGE Not using symbolic constants for errors because I want to avoid #including errno.h + +These are marked as no-inline because of some bad code generation by Clang. None of these functions +are used in any performance-critical code within miniaudio. */ -MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) +MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) { size_t i; @@ -12325,7 +12399,7 @@ MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) return 34; } -MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) +MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) { size_t i; @@ -12354,7 +12428,7 @@ MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) } -MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { size_t maxcount; size_t i; @@ -12388,7 +12462,7 @@ MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_ return 34; } -MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) +MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) { char* dstorig; @@ -12430,7 +12504,7 @@ MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) return 0; } -MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { char* dstorig; @@ -12476,7 +12550,7 @@ MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_ return 0; } -MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) +MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) { int sign; unsigned int valueU; @@ -12545,7 +12619,7 @@ MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) return 0; } -MA_API int ma_strcmp(const char* str1, const char* str2) +MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) { if (str1 == str2) return 0; @@ -12568,7 +12642,7 @@ MA_API int ma_strcmp(const char* str1, const char* str2) return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } -MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) +MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) { int result; @@ -12585,7 +12659,7 @@ MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* return result; } -MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz; char* dst; @@ -12605,7 +12679,7 @@ MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAll return dst; } -MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz = wcslen(src)+1; wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); @@ -13944,9 +14018,8 @@ static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 dith Atomics **************************************************************************************************************************************************************/ -/* c89atomic.h begin */ -#ifndef c89atomic_h -#define c89atomic_h +/* ma_atomic.h begin */ +#ifndef ma_atomic_h #if defined(__cplusplus) extern "C" { #endif @@ -13957,149 +14030,83 @@ extern "C" { #pragma GCC diagnostic ignored "-Wc++11-long-long" #endif #endif -typedef signed char c89atomic_int8; -typedef unsigned char c89atomic_uint8; -typedef signed short c89atomic_int16; -typedef unsigned short c89atomic_uint16; -typedef signed int c89atomic_int32; -typedef unsigned int c89atomic_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 c89atomic_int64; - typedef unsigned __int64 c89atomic_uint64; -#else - typedef signed long long c89atomic_int64; - typedef unsigned long long c89atomic_uint64; -#endif -typedef int c89atomic_memory_order; -typedef unsigned char c89atomic_bool; -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#ifdef _WIN32 -#ifdef _WIN64 -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#endif -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#ifdef __GNUC__ -#ifdef __LP64__ -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#endif -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#include -#if INTPTR_MAX == INT64_MAX -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#if defined(__arm__) || defined(_M_ARM) -#define C89ATOMIC_ARM32 -#endif -#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define C89ATOMIC_ARM64 -#endif -#if defined(__x86_64__) || defined(_M_X64) -#define C89ATOMIC_X64 -#elif defined(__i386) || defined(_M_IX86) -#define C89ATOMIC_X86 -#elif defined(C89ATOMIC_ARM32) || defined(C89ATOMIC_ARM64) -#define C89ATOMIC_ARM -#endif -#if defined(_MSC_VER) - #define C89ATOMIC_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline)) - #else - #define C89ATOMIC_INLINE inline __attribute__((always_inline)) - #endif -#elif defined(__WATCOMC__) || defined(__DMC__) - #define C89ATOMIC_INLINE __inline -#else - #define C89ATOMIC_INLINE -#endif -#define C89ATOMIC_HAS_8 -#define C89ATOMIC_HAS_16 -#define C89ATOMIC_HAS_32 -#define C89ATOMIC_HAS_64 +typedef int ma_atomic_memory_order; +#define MA_ATOMIC_HAS_8 +#define MA_ATOMIC_HAS_16 +#define MA_ATOMIC_HAS_32 +#define MA_ATOMIC_HAS_64 #if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) - #define C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, c89atomicType, msvcType) \ - c89atomicType result; \ + #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ + ma_atomicType result; \ switch (order) \ { \ - case c89atomic_memory_order_relaxed: \ + case ma_atomic_memory_order_relaxed: \ { \ - result = (c89atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case c89atomic_memory_order_consume: \ - case c89atomic_memory_order_acquire: \ + case ma_atomic_memory_order_consume: \ + case ma_atomic_memory_order_acquire: \ { \ - result = (c89atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case c89atomic_memory_order_release: \ + case ma_atomic_memory_order_release: \ { \ - result = (c89atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case c89atomic_memory_order_acq_rel: \ - case c89atomic_memory_order_seq_cst: \ + case ma_atomic_memory_order_acq_rel: \ + case ma_atomic_memory_order_seq_cst: \ default: \ { \ - result = (c89atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ + result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ } break; \ } \ return result; - #define C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, c89atomicType, msvcType) \ - c89atomicType result; \ + #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ + ma_atomicType result; \ switch (order) \ { \ - case c89atomic_memory_order_relaxed: \ + case ma_atomic_memory_order_relaxed: \ { \ - result = (c89atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case c89atomic_memory_order_consume: \ - case c89atomic_memory_order_acquire: \ + case ma_atomic_memory_order_consume: \ + case ma_atomic_memory_order_acquire: \ { \ - result = (c89atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case c89atomic_memory_order_release: \ + case ma_atomic_memory_order_release: \ { \ - result = (c89atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case c89atomic_memory_order_acq_rel: \ - case c89atomic_memory_order_seq_cst: \ + case ma_atomic_memory_order_acq_rel: \ + case ma_atomic_memory_order_seq_cst: \ default: \ { \ - result = (c89atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ } \ return result; - #define c89atomic_memory_order_relaxed 0 - #define c89atomic_memory_order_consume 1 - #define c89atomic_memory_order_acquire 2 - #define c89atomic_memory_order_release 3 - #define c89atomic_memory_order_acq_rel 4 - #define c89atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(C89ATOMIC_X86) - #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY + #define ma_atomic_memory_order_relaxed 0 + #define ma_atomic_memory_order_consume 1 + #define ma_atomic_memory_order_acquire 2 + #define ma_atomic_memory_order_release 3 + #define ma_atomic_memory_order_acq_rel 4 + #define ma_atomic_memory_order_seq_cst 5 + #if _MSC_VER < 1600 && defined(MA_X86) + #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY #endif #if _MSC_VER < 1600 - #undef C89ATOMIC_HAS_8 - #undef C89ATOMIC_HAS_16 + #undef MA_ATOMIC_HAS_8 + #undef MA_ATOMIC_HAS_16 #endif - #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) #include #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) { - c89atomic_uint8 result = 0; + ma_uint8 result = 0; __asm { mov ecx, dst mov al, expected @@ -14110,10 +14117,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) { - c89atomic_uint16 result = 0; + ma_uint16 result = 0; __asm { mov ecx, dst mov ax, expected @@ -14124,10 +14131,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) { - c89atomic_uint32 result = 0; + ma_uint32 result = 0; __asm { mov ecx, dst mov eax, expected @@ -14138,11 +14145,11 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { - c89atomic_uint32 resultEAX = 0; - c89atomic_uint32 resultEDX = 0; + ma_uint32 resultEAX = 0; + ma_uint32 resultEDX = 0; __asm { mov esi, dst mov eax, dword ptr expected @@ -14153,28 +14160,28 @@ typedef unsigned char c89atomic_bool; mov resultEAX, eax mov resultEDX, edx } - return ((c89atomic_uint64)resultEDX << 32) | resultEAX; + return ((ma_uint64)resultEDX << 32) | resultEAX; } #endif #else - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile c89atomic_int64*)dst, (c89atomic_int64)desired, (c89atomic_int64)expected) + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) #endif #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 result = 0; + ma_uint8 result = 0; (void)order; __asm { mov ecx, dst @@ -14185,10 +14192,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 result = 0; + ma_uint16 result = 0; (void)order; __asm { mov ecx, dst @@ -14199,10 +14206,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 result = 0; + ma_uint32 result = 0; (void)order; __asm { mov ecx, dst @@ -14214,68 +14221,68 @@ typedef unsigned char c89atomic_bool; } #endif #else - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); #else (void)order; - return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); + return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); #else (void)order; - return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); + return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); #else (void)order; - return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src); + return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); #endif } #endif - #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); #else (void)order; - return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); + return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); #endif } #else #endif #endif - #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; + ma_uint64 oldValue; do { oldValue = *dst; - } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 result = 0; + ma_uint8 result = 0; (void)order; __asm { mov ecx, dst @@ -14286,10 +14293,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 result = 0; + ma_uint16 result = 0; (void)order; __asm { mov ecx, dst @@ -14300,10 +14307,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 result = 0; + ma_uint32 result = 0; (void)order; __asm { mov ecx, dst @@ -14315,67 +14322,67 @@ typedef unsigned char c89atomic_bool; } #endif #else - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); #else (void)order; - return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); + return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); #else (void)order; - return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); + return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); #else (void)order; - return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); + return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); #endif } #endif - #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); #else (void)order; - return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); #endif } #else #endif #endif - #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue + src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) { (void)order; __asm { @@ -14383,1067 +14390,1067 @@ typedef unsigned char c89atomic_bool; } } #else - #if defined(C89ATOMIC_X64) - #define c89atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(C89ATOMIC_ARM64) - #define c89atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order + #if defined(MA_X64) + #define ma_atomic_thread_fence(order) __faststorefence(), (void)order + #elif defined(MA_ARM64) + #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order #else - static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order) + static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) { - volatile c89atomic_uint32 barrier = 0; - c89atomic_fetch_add_explicit_32(&barrier, 0, order); + volatile ma_uint32 barrier = 0; + ma_atomic_fetch_add_explicit_32(&barrier, 0, order); } #endif #endif - #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst) - #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) + #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); #else (void)order; - return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0); + return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); #else (void)order; - return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0); + return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); #else (void)order; - return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0); + return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); #endif } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); #else (void)order; - return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0); + return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); #endif } #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue - src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue - src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); #else - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue & src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); #else - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue & src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); #else - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); #else - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); #else - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue ^ src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); #else - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue ^ src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); #else - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); #else - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); #else - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue | src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); #else - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue | src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); #else - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); #else - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) #endif - #if defined(C89ATOMIC_HAS_8) - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) + #if defined(MA_ATOMIC_HAS_8) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #else - typedef c89atomic_uint32 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order) + typedef ma_uint32 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED - #define c89atomic_memory_order_consume __ATOMIC_CONSUME - #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define c89atomic_memory_order_release __ATOMIC_RELEASE - #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define c89atomic_thread_fence(order) __atomic_thread_fence(order) - #define c89atomic_signal_fence(order) __atomic_signal_fence(order) - #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) - #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) - #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) - #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) - #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE + #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE + #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED + #define ma_atomic_memory_order_consume __ATOMIC_CONSUME + #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE + #define ma_atomic_memory_order_release __ATOMIC_RELEASE + #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL + #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) + #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) + #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) + #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) + #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) + #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) + #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order) - #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) + #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #else - #define c89atomic_memory_order_relaxed 1 - #define c89atomic_memory_order_consume 2 - #define c89atomic_memory_order_acquire 3 - #define c89atomic_memory_order_release 4 - #define c89atomic_memory_order_acq_rel 5 - #define c89atomic_memory_order_seq_cst 6 - #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") #if defined(__GNUC__) - #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - if (order > c89atomic_memory_order_acquire) { + if (order > ma_atomic_memory_order_acquire) { __sync_synchronize(); } return __sync_lock_test_and_set(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; + ma_uint16 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; + ma_uint32 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; + ma_uint64 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #else - #if defined(C89ATOMIC_X86) - #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(C89ATOMIC_X64) - #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") + #if defined(MA_X86) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") + #elif defined(MA_X64) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") #else #error Unsupported architecture. Please submit a feature request. #endif - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) { - c89atomic_uint8 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + ma_uint8 result; + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) { - c89atomic_uint16 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + ma_uint16 result; + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) { - c89atomic_uint32 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + ma_uint32 result; + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { - volatile c89atomic_uint64 result; - #if defined(C89ATOMIC_X86) - c89atomic_uint32 resultEAX; - c89atomic_uint32 resultEDX; + volatile ma_uint64 result; + #if defined(MA_X86) + ma_uint32 resultEAX; + ma_uint32 resultEDX; __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((c89atomic_uint64)resultEDX << 32) | resultEAX; - #elif defined(C89ATOMIC_X64) + result = ((ma_uint64)resultEDX << 32) | resultEAX; + #elif defined(MA_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 result = 0; + ma_uint8 result = 0; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 result = 0; + ma_uint16 result = 0; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 result; + ma_uint32 result; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 result; + ma_uint64 result; (void)order; - #if defined(C89ATOMIC_X86) + #if defined(MA_X86) do { result = *dst; - } while (c89atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(C89ATOMIC_X64) + } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); + #elif defined(MA_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 result; + ma_uint8 result; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 result; + ma_uint16 result; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 result; + ma_uint32 result; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_X86) - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + #if defined(MA_X86) + ma_uint64 oldValue; + ma_uint64 newValue; (void)order; do { oldValue = *dst; newValue = oldValue + src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); return oldValue; - #elif defined(C89ATOMIC_X64) - c89atomic_uint64 result; + #elif defined(MA_X64) + ma_uint64 result; (void)order; __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); return result; #endif } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue - src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue - src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue & src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue & src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue ^ src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue ^ src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue | src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue | src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) { (void)order; - return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0); + return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) { (void)order; - return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0); + return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) { (void)order; - return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0); + return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) { (void)order; - return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0); + return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); } - #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) - #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) - #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) - #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) - #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) - #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) - #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) - #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) - #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) - #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) - #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) - #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #endif -#if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_uint8 expectedValue; - c89atomic_uint8 result; + ma_uint8 expectedValue; + ma_uint8 result; (void)successOrder; (void)failureOrder; - expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_8(dst, expectedValue, desired); + expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - c89atomic_store_explicit_8(expected, result, failureOrder); + ma_atomic_store_explicit_8(expected, result, failureOrder); return 0; } } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_uint16 expectedValue; - c89atomic_uint16 result; + ma_uint16 expectedValue; + ma_uint16 result; (void)successOrder; (void)failureOrder; - expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_16(dst, expectedValue, desired); + expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - c89atomic_store_explicit_16(expected, result, failureOrder); + ma_atomic_store_explicit_16(expected, result, failureOrder); return 0; } } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_uint32 expectedValue; - c89atomic_uint32 result; + ma_uint32 expectedValue; + ma_uint32 result; (void)successOrder; (void)failureOrder; - expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_32(dst, expectedValue, desired); + expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - c89atomic_store_explicit_32(expected, result, failureOrder); + ma_atomic_store_explicit_32(expected, result, failureOrder); return 0; } } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_uint64 expectedValue; - c89atomic_uint64 result; + ma_uint64 expectedValue; + ma_uint64 result; (void)successOrder; (void)failureOrder; - expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_64(dst, expectedValue, desired); + expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - c89atomic_store_explicit_64(expected, result, failureOrder); + ma_atomic_store_explicit_64(expected, result, failureOrder); return 0; } } #endif - #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) #endif -#if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr) +#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) { (void)ptr; return 1; } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) { (void)ptr; return 1; } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) { (void)ptr; return 1; } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) { (void)ptr; - #if defined(C89ATOMIC_64BIT) + #if defined(MA_64BIT) return 1; #else - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) return 1; #else return 0; @@ -15451,432 +15458,432 @@ typedef unsigned char c89atomic_bool; #endif } #endif -#if defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) +#if defined(MA_64BIT) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) { - return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr); + return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); } - static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) + static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) { - return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order); + return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); } - static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) { - c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); + ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); } - static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) { - return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); + return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { - return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired); + return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); } -#elif defined(C89ATOMIC_32BIT) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) +#elif defined(MA_32BIT) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) { - return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr); + return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); } - static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) + static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) { - return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order); + return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); } - static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) { - c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); + ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); } - static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) { - return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); + return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { - return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired); + return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); } #else #error Unsupported architecture. #endif -#define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_store_explicit_i8( dst, src, order) c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_store_explicit_i16(dst, src, order) c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_store_explicit_i32(dst, src, order) c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_store_explicit_i64(dst, src, order) c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) -#define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i16(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_and_swap_i8( dst, expected, dedsired) (c89atomic_int8 )c89atomic_compare_and_swap_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )expected, (c89atomic_uint8 )dedsired) -#define c89atomic_compare_and_swap_i16(dst, expected, dedsired) (c89atomic_int16)c89atomic_compare_and_swap_16((c89atomic_uint16*)dst, (c89atomic_uint16)expected, (c89atomic_uint16)dedsired) -#define c89atomic_compare_and_swap_i32(dst, expected, dedsired) (c89atomic_int32)c89atomic_compare_and_swap_32((c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)dedsired) -#define c89atomic_compare_and_swap_i64(dst, expected, dedsired) (c89atomic_int64)c89atomic_compare_and_swap_64((c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)dedsired) +#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) +#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) +#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) +#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) typedef union { - c89atomic_uint32 i; + ma_uint32 i; float f; -} c89atomic_if32; +} ma_atomic_if32; typedef union { - c89atomic_uint64 i; + ma_uint64 i; double f; -} c89atomic_if64; -#define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) -static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +} ma_atomic_if64; +#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) +static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 x; + ma_atomic_if32 x; x.f = src; - c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); } -static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 x; + ma_atomic_if64 x; x.f = src; - c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); } -static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile const float* ptr, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) { - c89atomic_if32 r; - r.i = c89atomic_load_explicit_32((volatile const c89atomic_uint32*)ptr, order); + ma_atomic_if32 r; + r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile const double* ptr, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) { - c89atomic_if64 r; - r.i = c89atomic_load_explicit_64((volatile const c89atomic_uint64*)ptr, order); + ma_atomic_if64 r; + r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_if32 d; + ma_atomic_if32 d; d.f = desired; - return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); } -static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_if64 d; + ma_atomic_if64 d; d.f = desired; - return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); } -static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_if32 d; + ma_atomic_if32 d; d.f = desired; - return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); } -static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_if64 d; + ma_atomic_if64 d; d.f = desired; - return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); } -static C89ATOMIC_INLINE float c89atomic_fetch_add_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_add_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_add_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_add_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_fetch_sub_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_sub_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_sub_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_sub_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_fetch_or_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_or_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_or_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_or_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_fetch_xor_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_xor_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_xor_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_xor_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_fetch_and_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_and_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_and_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_and_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -#define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_f32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_f64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_f32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_f64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_f32(dst, src) c89atomic_fetch_add_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_f64(dst, src) c89atomic_fetch_add_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_f32(dst, src) c89atomic_fetch_sub_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_f64(dst, src) c89atomic_fetch_sub_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_f32(dst, src) c89atomic_fetch_or_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_f64(dst, src) c89atomic_fetch_or_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_f32(dst, src) c89atomic_fetch_xor_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_f64(dst, src) c89atomic_fetch_xor_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_f32(dst, src) c89atomic_fetch_and_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_f64(dst, src) c89atomic_fetch_and_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -static C89ATOMIC_INLINE float c89atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) +#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) { - c89atomic_if32 r; - c89atomic_if32 e, d; + ma_atomic_if32 r; + ma_atomic_if32 e, d; e.f = expected; d.f = desired; - r.i = c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, e.i, d.i); + r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); return r.f; } -static C89ATOMIC_INLINE double c89atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) +static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) { - c89atomic_if64 r; - c89atomic_if64 e, d; + ma_atomic_if64 r; + ma_atomic_if64 e, d; e.f = expected; d.f = desired; - r.i = c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, e.i, d.i); + r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); return r.f; } -typedef c89atomic_flag c89atomic_spinlock; -static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock) +typedef ma_atomic_flag ma_atomic_spinlock; +static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) { for (;;) { - if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) { + if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { break; } - while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) { + while (c89atoimc_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { } } } -static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock) +static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) { - c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); + ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop @@ -15885,70 +15892,70 @@ static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlo } #endif #endif -/* c89atomic.h end */ +/* ma_atomic.h end */ #define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ { \ - return (ma_##type)c89atomic_load_##c89TypeExtension(&x->value); \ + return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ } \ static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ { \ - c89atomic_store_##c89TypeExtension(&x->value, value); \ + ma_atomic_store_##c89TypeExtension(&x->value, value); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ { \ - return (ma_##type)c89atomic_exchange_##c89TypeExtension(&x->value, value); \ + return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ } \ static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ { \ - return c89atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ + return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_add_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_or_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_and_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ { \ - return (ma_##type)c89atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ + return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ } \ #define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ { \ - return c89atomic_load_ptr((void**)&x->value); \ + return ma_atomic_load_ptr((void**)&x->value); \ } \ static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ { \ - c89atomic_store_ptr((void**)&x->value, (void*)value); \ + ma_atomic_store_ptr((void**)&x->value, (void*)value); \ } \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ { \ - return c89atomic_exchange_ptr((void**)&x->value, (void*)value); \ + return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ } \ static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ { \ - return c89atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ } \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ { \ - return (ma_##type*)c89atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ } \ MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) @@ -16031,11 +16038,11 @@ static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, } for (;;) { - if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) { + if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { break; } - while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) { + while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { if (yield) { ma_yield(); } @@ -16061,7 +16068,7 @@ MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) return MA_INVALID_ARGS; } - c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release); + ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); return MA_SUCCESS; } @@ -16808,7 +16815,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence) } for (;;) { - ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); + ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); ma_uint32 newCounter = oldCounter + 1; /* Make sure we're not about to exceed our maximum value. */ @@ -16817,7 +16824,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence) return MA_OUT_OF_RANGE; } - if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { return MA_SUCCESS; } else { if (oldCounter == MA_FENCE_COUNTER_MAX) { @@ -16838,7 +16845,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence) } for (;;) { - ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); + ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); ma_uint32 newCounter = oldCounter - 1; if (oldCounter == 0) { @@ -16846,7 +16853,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence) return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ } - if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { #ifndef MA_NO_THREADING { if (newCounter == 0) { @@ -16877,7 +16884,7 @@ MA_API ma_result ma_fence_wait(ma_fence* pFence) for (;;) { ma_uint32 counter; - counter = c89atomic_load_32(&pFence->counter); + counter = ma_atomic_load_32(&pFence->counter); if (counter == 0) { /* Counter has hit zero. By the time we get here some other thread may have acquired the @@ -17210,7 +17217,7 @@ MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint6 ma_uint32 newBitfield; ma_uint32 bitOffset; - oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ /* Fast check to see if anything is available. */ if (oldBitfield == 0xFFFFFFFF) { @@ -17222,11 +17229,11 @@ MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint6 newBitfield = oldBitfield | (1 << bitOffset); - if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { ma_uint32 slotIndex; /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ - c89atomic_fetch_add_32(&pAllocator->count, 1); + ma_atomic_fetch_add_32(&pAllocator->count, 1); /* The slot index is required for constructing the output value. */ slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ @@ -17275,12 +17282,12 @@ MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ - while (c89atomic_load_32(&pAllocator->count) > 0) { + while (ma_atomic_load_32(&pAllocator->count) > 0) { /* CAS */ ma_uint32 oldBitfield; ma_uint32 newBitfield; - oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ newBitfield = oldBitfield & ~(1 << iBit); /* Debugging for checking for double-frees. */ @@ -17292,8 +17299,8 @@ MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 } #endif - if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - c89atomic_fetch_sub_32(&pAllocator->count, 1); + if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + ma_atomic_fetch_sub_32(&pAllocator->count, 1); return MA_SUCCESS; } } @@ -17624,7 +17631,7 @@ MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callba static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { /* The new counter is taken from the expected value. */ - return c89atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; + return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; } MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) @@ -17662,10 +17669,10 @@ MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) { /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ for (;;) { - tail = c89atomic_load_64(&pQueue->tail); - next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); + tail = ma_atomic_load_64(&pQueue->tail); + next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); - if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) { + if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { if (ma_job_extract_slot(next) == 0xFFFF) { if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { break; @@ -17736,11 +17743,11 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) /* Now we need to remove the root item from the list. */ for (;;) { - head = c89atomic_load_64(&pQueue->head); - tail = c89atomic_load_64(&pQueue->tail); - next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); + head = ma_atomic_load_64(&pQueue->head); + tail = ma_atomic_load_64(&pQueue->tail); + next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); - if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) { + if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { if (ma_job_extract_slot(next) == 0xFFFF) { #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE @@ -17779,6 +17786,112 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) +/******************************************************************************* + +Dynamic Linking + +*******************************************************************************/ +#ifdef MA_POSIX + /* No need for dlfcn.h if we're not using runtime linking. */ + #ifndef MA_NO_RUNTIME_LINKING + #include + #endif +#endif + +MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_handle handle; + + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); + + #ifdef MA_WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(MA_WIN32_UWP) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif + #else + handle = (ma_handle)dlopen(filename, RTLD_NOW); + #endif + + /* + I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority + backend is a deliberate design choice. Instead I'm logging it as an informational message. + */ + if (handle == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); + } + + return handle; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)filename; + return NULL; +#endif +} + +MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) +{ +#ifndef MA_NO_RUNTIME_LINKING + #ifdef MA_WIN32 + FreeLibrary((HMODULE)handle); + #else + dlclose((void*)handle); + #endif + + (void)pLog; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; +#endif +} + +MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_proc proc; + + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); + +#ifdef _WIN32 + proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); +#else +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" +#endif + proc = (ma_proc)dlsym((void*)handle, symbol); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic pop +#endif +#endif + + if (proc == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); + } + + (void)pLog; /* It's possible for pContext to be unused. */ + return proc; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; + (void)symbol; + return NULL; +#endif +} + + /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* @@ -18443,105 +18556,6 @@ Timing #endif -/******************************************************************************* - -Dynamic Linking - -*******************************************************************************/ -MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_handle handle; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) - handle = (ma_handle)LoadLibraryA(filename); - #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } - #endif - #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); - #endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - (void)pContext; /* It's possible for pContext to be unused. */ - return handle; -#else - /* Runtime linking is disabled. */ - (void)pContext; - (void)filename; - return NULL; -#endif -} - -MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) -{ -#ifndef MA_NO_RUNTIME_LINKING - #ifdef MA_WIN32 - FreeLibrary((HMODULE)handle); - #else - dlclose((void*)handle); - #endif - - (void)pContext; -#else - /* Runtime linking is disabled. */ - (void)pContext; - (void)handle; -#endif -} - -MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_proc proc; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #pragma GCC diagnostic pop -#endif -#endif - - if (proc == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pContext; /* It's possible for pContext to be unused. */ - return proc; -#else - /* Runtime linking is disabled. */ - (void)pContext; - (void)handle; - (void)symbol; - return NULL; -#endif -} - #if 0 static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) @@ -20676,12 +20690,12 @@ static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_com static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) { - return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; + return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; } static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) { - ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; + ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; if (newRefCount == 0) { return 0; /* We don't free anything here because we never allocate the object on the heap. */ } @@ -20753,12 +20767,12 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMN static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) { - return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; + return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; } static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) { - ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; + ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; if (newRefCount == 0) { return 0; /* We don't free anything here because we never allocate the object on the heap. */ } @@ -22148,7 +22162,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); } - + result = MA_SUCCESS; } @@ -23404,14 +23418,14 @@ static ma_result ma_context_uninit__wasapi(ma_context* pContext) ma_thread_wait(&pContext->wasapi.commandThread); if (pContext->wasapi.hAvrt) { - ma_dlclose(pContext, pContext->wasapi.hAvrt); + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; } #if defined(MA_WIN32_UWP) { if (pContext->wasapi.hMMDevapi) { - ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); pContext->wasapi.hMMDevapi = NULL; } } @@ -23445,15 +23459,15 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; ma_PFNVerSetConditionMask _VerSetConditionMask; - kernel32DLL = ma_dlopen(pContext, "kernel32.dll"); + kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); if (kernel32DLL == NULL) { return MA_NO_BACKEND; } - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask"); + _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); + _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(pContext, kernel32DLL); + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); return MA_NO_BACKEND; } @@ -23468,7 +23482,7 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ result = MA_NO_BACKEND; } - ma_dlclose(pContext, kernel32DLL); + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); } #endif @@ -23525,13 +23539,13 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ #if defined(MA_WIN32_UWP) { /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ - pContext->wasapi.hMMDevapi = ma_dlopen(pContext, "mmdevapi.dll"); + pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); if (pContext->wasapi.hMMDevapi) { - pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(pContext, pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); + pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); - ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ } } else { @@ -23543,16 +23557,16 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ #endif /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ - pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); + pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); - pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); + pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); + pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); /* If either function could not be found, disable use of avrt entirely. */ if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; - ma_dlclose(pContext, pContext->wasapi.hAvrt); + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; } } @@ -24173,7 +24187,7 @@ static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ MA_ASSERT(pData != NULL); - pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData); + pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); if (pData->terminated) { return FALSE; /* Stop enumeration. */ } else { @@ -25283,7 +25297,7 @@ static ma_result ma_context_uninit__dsound(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_dsound); - ma_dlclose(pContext, pContext->dsound.hDSoundDLL); + ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); return MA_SUCCESS; } @@ -25294,15 +25308,15 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_ (void)pConfig; - pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll"); + pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); if (pContext->dsound.hDSoundDLL == NULL) { return MA_API_NOT_FOUND; } - pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); + pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); + pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); + pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); + pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); /* We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too @@ -26382,7 +26396,7 @@ static ma_result ma_context_uninit__winmm(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_winmm); - ma_dlclose(pContext, pContext->winmm.hWinMM); + ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); return MA_SUCCESS; } @@ -26392,28 +26406,28 @@ static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_c (void)pConfig; - pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll"); + pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); if (pContext->winmm.hWinMM == NULL) { return MA_NO_BACKEND; } - pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset"); + pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); + pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); + pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); + pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); + pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); + pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); + pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); + pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); + pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); + pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); + pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); + pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); + pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); + pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); + pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); + pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); + pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); pCallbacks->onContextInit = ma_context_init__winmm; pCallbacks->onContextUninit = ma_context_uninit__winmm; @@ -28031,6 +28045,12 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) static ma_result ma_device_stop__alsa(ma_device* pDevice) { + /* + The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is + a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. + */ + int resultPoll; + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); @@ -28043,6 +28063,13 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); } + + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); + } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { @@ -28057,6 +28084,14 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); } + + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); + } + } return MA_SUCCESS; @@ -28069,7 +28104,7 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st int resultALSA; int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed."); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.\n"); return ma_result_from_errno(errno); } @@ -28082,7 +28117,7 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st ma_uint64 t; int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ if (resultRead < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed."); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); return ma_result_from_errno(errno); } @@ -28096,13 +28131,17 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st */ resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed."); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); return ma_result_from_errno(-resultALSA); } if ((revents & POLLERR) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected."); - return ma_result_from_errno(errno); + ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); + if (state == MA_SND_PCM_STATE_XRUN) { + /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); + } } if ((revents & requiredEvent) == requiredEvent) { @@ -28277,7 +28316,7 @@ static ma_result ma_context_uninit__alsa(ma_context* pContext) ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->alsa.asoundSO); + ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); #endif ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); @@ -28296,7 +28335,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co size_t i; for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]); + pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); if (pContext->alsa.asoundSO != NULL) { break; } @@ -28307,72 +28346,72 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co return MA_NO_BACKEND; } - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global"); + pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); + pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); + pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); + pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); + pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); + pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); + pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); + pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); + pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); + pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); + pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); + pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); + pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); + pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); + pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); + pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); + pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); + pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); + pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); + pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); + pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); + pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); + pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); + pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); + pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); + pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); + pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); + pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); + pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); + pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); + pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); + pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); + pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); + pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); + pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); + pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); + pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); + pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); + pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); + pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); + pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); + pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); + pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); + pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); + pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); + pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); + pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); + pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); + pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); + pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); + pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); + pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); #else /* The system below is just for type safety. */ ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; @@ -30228,11 +30267,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Notes for PulseAudio: - - We're always using native format/channels/rate regardless of whether or not PulseAudio - supports the format directly through their own data conversion system. I'm doing this to - reduce as much variability from the PulseAudio side as possible because it's seems to be - extremely unreliable at everything it does. - - When both the period size in frames and milliseconds are 0, we default to miniaudio's default buffer sizes rather than leaving it up to PulseAudio because I don't trust PulseAudio to give us any kind of reasonable latency by default. @@ -30321,6 +30355,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi if (pDescriptorCapture->sampleRate != 0) { ss.rate = pDescriptorCapture->sampleRate; } + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { @@ -30328,14 +30363,17 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } + streamFlags |= MA_PA_STREAM_FIX_FORMAT; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; + streamFlags |= MA_PA_STREAM_FIX_RATE; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; + streamFlags |= MA_PA_STREAM_FIX_CHANNELS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } @@ -30364,7 +30402,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Connect after we've got all of our internal state set up. */ - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; if (devCapture != NULL) { streamFlags |= MA_PA_STREAM_DONT_MOVE; } @@ -30467,20 +30504,24 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss.rate = pDescriptorPlayback->sampleRate; } + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } + streamFlags |= MA_PA_STREAM_FIX_FORMAT; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; + streamFlags |= MA_PA_STREAM_FIX_RATE; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; + streamFlags |= MA_PA_STREAM_FIX_CHANNELS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } @@ -30513,7 +30554,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Connect after we've got all of our internal state set up. */ - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; if (devPlayback != NULL) { streamFlags |= MA_PA_STREAM_DONT_MOVE; } @@ -30792,7 +30832,7 @@ static ma_result ma_context_uninit__pulse(ma_context* pContext) ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); + ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); #endif return MA_SUCCESS; @@ -30809,7 +30849,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c size_t i; for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]); + pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); if (pContext->pulse.pulseSO != NULL) { break; } @@ -30819,67 +30859,67 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c return MA_NO_BACKEND; } - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size"); + pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); + pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); + pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); + pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); + pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); + pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); + pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); + pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); + pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); + pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); + pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); + pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); + pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); + pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); + pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); + pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); + pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); + pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); + pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); + pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); + pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); + pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); + pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); + pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); + pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); + pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); + pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); + pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); + pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); + pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); + pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); + pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); + pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); + pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); + pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); + pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); + pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); + pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); + pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); + pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); + pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); + pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); + pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); + pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); + pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); + pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); + pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); + pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); + pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); + pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); + pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); + pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); + pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); + pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); + pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); + pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); + pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); + pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); + pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); + pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); #else /* This strange assignment system is just for type safety. */ ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; @@ -31024,7 +31064,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); + ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); #endif return result; } @@ -31588,7 +31628,7 @@ static ma_result ma_context_uninit__jack(ma_context* pContext) pContext->jack.pClientName = NULL; #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->jack.jackSO); + ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); #endif return MA_SUCCESS; @@ -31610,7 +31650,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co size_t i; for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]); + pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); if (pContext->jack.jackSO != NULL) { break; } @@ -31620,22 +31660,22 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co return MA_NO_BACKEND; } - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free"); + pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); + pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); + pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); + pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); + pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); + pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); + pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); + pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); + pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); + pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); + pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); + pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); + pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); + pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); + pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); + pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); #else /* This strange assignment system is here just to ensure type safety of miniaudio's function pointer @@ -31691,7 +31731,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co if (result != MA_SUCCESS) { ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->jack.jackSO); + ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); #endif return MA_NO_BACKEND; } @@ -34703,9 +34743,9 @@ static ma_result ma_context_uninit__coreaudio(ma_context* pContext) #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); #endif #if !defined(MA_APPLE_MOBILE) @@ -34794,26 +34834,26 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation"); + pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } - pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease"); + pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); + pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio"); + pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); + pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); + pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); + pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); + pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); + pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); /* It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still @@ -34821,35 +34861,35 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to AudioToolbox. */ - pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit"); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioUnit.framework/AudioUnit"); if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { + if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox"); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioToolbox.framework/AudioToolbox"); if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } } - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender"); + pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); + pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); + pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); + pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); + pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); + pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); + pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); + pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); + pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); + pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); #else pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; pContext->coreaudio.CFRelease = (ma_proc)CFRelease; @@ -34891,9 +34931,9 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); if (pContext->coreaudio.component == NULL) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); #endif return MA_FAILED_TO_INIT_BACKEND; } @@ -34903,9 +34943,9 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte result = ma_context__init_device_tracking__coreaudio(pContext); if (result != MA_SUCCESS) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); #endif return result; } @@ -35726,7 +35766,7 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c size_t i; for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]); + pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); if (pContext->sndio.sndioSO != NULL) { break; } @@ -35736,16 +35776,16 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c return MA_NO_BACKEND; } - pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar"); + pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); + pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); + pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); + pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); + pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); + pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); + pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); + pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); + pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); + pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); #else pContext->sndio.sio_open = sio_open; pContext->sndio.sio_close = sio_close; @@ -37614,7 +37654,7 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* anything from Android 11 and earlier. Suggestions welcome on how we might be able to make this more targetted. */ - if (pConfig->aaudio.enableCompatibilityWorkarounds && ma_android_sdk_version() > 30) { + if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) { /* AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you retrieve the actual sample rate until after you've opened the stream. But you need to configure @@ -38187,7 +38227,7 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext) ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - ma_dlclose(pContext, pContext->aaudio.hAAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return MA_SUCCESS; @@ -38201,7 +38241,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ }; for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]); + pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); if (pContext->aaudio.hAAudio != NULL) { break; } @@ -38211,35 +38251,35 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ return MA_FAILED_TO_INIT_BACKEND; } - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop"); + pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); + pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); + pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); + pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); + pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); + pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); + pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); + pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); + pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); + pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); + pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); + pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); + pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); + pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); + pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); + pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); + pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); + pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); + pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); + pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); + pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); + pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); + pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); + pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); + pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); + pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); pCallbacks->onContextInit = ma_context_init__aaudio; @@ -38265,7 +38305,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->aaudio.hAAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return result; } @@ -39281,7 +39321,7 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) return ma_result_from_OpenSL(resultSL); } - /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */ + /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ if (pDevice->type == ma_device_type_duplex) { MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); } else { @@ -39402,7 +39442,7 @@ static ma_result ma_context_uninit__opensl(ma_context* pContext) static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) { /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName); + ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); if (p == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); return MA_NO_BACKEND; @@ -39460,7 +39500,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ references to the symbols and will hopefully skip the checks. */ for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]); + pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); if (pContext->opensl.libOpenSLES != NULL) { break; } @@ -39473,49 +39513,49 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine"); + pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); return MA_NO_BACKEND; } @@ -39539,7 +39579,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ ma_spinlock_unlock(&g_maOpenSLSpinlock); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); return result; } @@ -39702,95 +39742,65 @@ static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_d return MA_SUCCESS; } -#if !defined(MA_USE_AUDIO_WORKLETS) -static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex) -{ - MA_ASSERT(pDevice != NULL); - - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - var pAllocationCallbacks = $3; - - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - - /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */ - if (device.intermediaryBuffer !== undefined) { - _ma_free_emscripten(device.intermediaryBuffer, pAllocationCallbacks); - device.intermediaryBuffer = undefined; - device.intermediaryBufferView = undefined; - device.intermediaryBufferSizeInBytes = undefined; - } - - /* Make sure the device is untracked so the slot can be reused later. */ - miniaudio.untrack_device_by_index($0); - }, deviceIndex, deviceType, &pDevice->pContext->allocationCallbacks); -} -#endif - -static void ma_device_uninit_by_type__webaudio(ma_device* pDevice, ma_device_type deviceType) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - -#if defined(MA_USE_AUDIO_WORKLETS) - if (deviceType == ma_device_type_capture) { - ma_free(pDevice->webaudio.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->webaudio.pStackBufferCapture, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContextCapture); - } else { - ma_free(pDevice->webaudio.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->webaudio.pStackBufferPlayback, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContextPlayback); - } -#else - if (deviceType == ma_device_type_capture) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); - } else { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); - } -#endif -} - static ma_result ma_device_uninit__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture); - } + #if defined(MA_USE_AUDIO_WORKLETS) + { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_playback); + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + }, pDevice->webaudio.deviceIndex); + + emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); } + #else + { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + + /* Make sure all nodes are disconnected and marked for collection. */ + if (device.scriptNode !== undefined) { + device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ + device.scriptNode.disconnect(); + device.scriptNode = undefined; + } + + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + + /* + Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want + to clear the callback before closing. + */ + device.webaudio.close(); + device.webaudio = undefined; + }, pDevice->webaudio.deviceIndex); + } + #endif + + /* Clean up the device on the JS side. */ + EM_ASM({ + miniaudio.untrack_device_by_index($0); + }, pDevice->webaudio.deviceIndex); + + ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); return MA_SUCCESS; } +#if !defined(MA_USE_AUDIO_WORKLETS) static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { -#if defined(MA_USE_AUDIO_WORKLETS) - (void)pDescriptor; - (void)nativeSampleRate; - (void)performanceProfile; - - return 256; -#else /* There have been reports of the default buffer size being too small on some browsers. If we're using the default buffer size, we'll make sure the period size is bigger than our standard defaults. @@ -39821,8 +39831,8 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(co } return periodSizeInFrames; -#endif } +#endif #if defined(MA_USE_AUDIO_WORKLETS) @@ -39830,20 +39840,22 @@ typedef struct { ma_device* pDevice; const ma_device_config* pConfig; - ma_device_descriptor* pDescriptor; - ma_device_type deviceType; - ma_uint32 channels; + ma_device_descriptor* pDescriptorPlayback; + ma_device_descriptor* pDescriptorCapture; } ma_audio_worklet_thread_initialized_data; static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; ma_uint32 frameCount; - ma_uint32 framesProcessed; (void)paramCount; (void)pParams; + if (ma_device_get_state(pDevice) != ma_device_state_started) { + return EM_TRUE; + } + /* The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer @@ -39854,38 +39866,31 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const */ frameCount = 128; - /* Run the conversion logic in a loop for robustness. */ - framesProcessed = 0; - while (framesProcessed < frameCount) { - ma_uint32 framesToProcessThisIteration = frameCount - framesProcessed; - - if (inputCount > 0) { - if (framesToProcessThisIteration > pDevice->webaudio.intermediaryBufferSizeInFramesPlayback) { - framesToProcessThisIteration = pDevice->webaudio.intermediaryBufferSizeInFramesPlayback; + if (inputCount > 0) { + /* Input data needs to be interleaved before we hand it to the client. */ + for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { + for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; } - - /* Input data needs to be interleaved before we hand it to the client. */ - for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) { - for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { - pDevice->webaudio.pIntermediaryBufferCapture[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + framesProcessed + iFrame]; - } - } - - ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferCapture); } - if (outputCount > 0) { - ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferPlayback); + ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + } + + if (outputCount > 0) { + /* If it's a capture-only device, we'll need to output silence. */ + if (pDevice->type == ma_device_type_capture) { + MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); + } else { + ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ - for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) { - for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { - pOutputs[0].data[frameCount*iChannel + framesProcessed + iFrame] = pDevice->webaudio.pIntermediaryBufferPlayback[iFrame*pDevice->playback.internalChannels + iChannel]; + for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { + for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; } } } - - framesProcessed += framesToProcessThisIteration; } return EM_TRUE; @@ -39895,76 +39900,135 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) { ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - EmscriptenAudioWorkletNodeCreateOptions workletNodeOptions; - EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletNode; - int outputChannelCount = 0; + EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; + int channels = 0; + size_t intermediaryBufferSizeInFrames; + int sampleRate; if (success == EM_FALSE) { - pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + pParameters->pDevice->webaudio.initResult = MA_ERROR; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); return; } - MA_ZERO_OBJECT(&workletNodeOptions); - - if (pParameters->deviceType == ma_device_type_capture) { - workletNodeOptions.numberOfInputs = 1; - } else { - outputChannelCount = (int)pParameters->channels; /* Safe cast. */ - - workletNodeOptions.numberOfOutputs = 1; - workletNodeOptions.outputChannelCounts = &outputChannelCount; - } - - /* Here is where we create the node that will do our processing. */ - workletNode = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &workletNodeOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); - - if (pParameters->deviceType == ma_device_type_capture) { - pParameters->pDevice->webaudio.workletNodeCapture = workletNode; - } else { - pParameters->pDevice->webaudio.workletNodePlayback = workletNode; - } + /* The next step is to initialize the audio worklet node. */ + MA_ZERO_OBJECT(&audioWorkletOptions); /* - With the worklet node created we can now attach it to the graph. This is done differently depending on whether or not - it's capture or playback mode. + The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel + count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an + output channel count on the capture side. This is slightly confusing for capture mode because intuitively you + wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have + proper control over the channel count. In the capture case, we'll have to output silence to it's output node. */ - if (pParameters->deviceType == ma_device_type_capture) { - EM_ASM({ - var workletNode = emscriptenGetAudioObject($0); + if (pParameters->pConfig->deviceType == ma_device_type_capture) { + channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); + audioWorkletOptions.numberOfInputs = 1; + } else { + channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); + + if (pParameters->pConfig->deviceType == ma_device_type_duplex) { + audioWorkletOptions.numberOfInputs = 1; + } else { + audioWorkletOptions.numberOfInputs = 0; + } + } + + audioWorkletOptions.numberOfOutputs = 1; + audioWorkletOptions.outputChannelCounts = &channels; + + + /* + Now that we know the channel count to use we can allocate the intermediary buffer. The + intermediary buffer is used for interleaving and deinterleaving. + */ + intermediaryBufferSizeInFrames = 128; + + pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); + if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { + pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + + + pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); + + /* With the audio worklet initialized we can now attach it to the graph. */ + if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { + ma_result attachmentResult = EM_ASM_INT({ + var getUserMediaResult = 0; + var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); navigator.mediaDevices.getUserMedia({audio:true, video:false}) .then(function(stream) { audioContext.streamNode = audioContext.createMediaStreamSource(stream); - audioContext.streamNode.connect(workletNode); - - /* - Now that the worklet node has been connected, do we need to inspect workletNode.channelCount - to check the actual channel count, or is it safe to assume it's always 2? - */ + audioContext.streamNode.connect(audioWorklet); + audioWorklet.connect(audioContext.destination); + getUserMediaResult = 0; /* 0 = MA_SUCCESS */ }) .catch(function(error) { - + console.log("navigator.mediaDevices.getUserMedia Failed: " + error); + getUserMediaResult = -1; /* -1 = MA_ERROR */ }); - }, workletNode, audioContext); - } else { - EM_ASM({ - var workletNode = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - workletNode.connect(audioContext.destination); - }, workletNode, audioContext); + + return getUserMediaResult; + }, pParameters->pDevice->webaudio.audioWorklet, audioContext); + + if (attachmentResult != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); + emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); + pParameters->pDevice->webaudio.initResult = attachmentResult; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } } - pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ + if (pParameters->pConfig->deviceType == ma_device_type_playback) { + ma_result attachmentResult = EM_ASM_INT({ + var audioWorklet = emscriptenGetAudioObject($0); + var audioContext = emscriptenGetAudioObject($1); + audioWorklet.connect(audioContext.destination); + return 0; /* 0 = MA_SUCCESS */ + }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", workletNode); + if (attachmentResult != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); + pParameters->pDevice->webaudio.initResult = attachmentResult; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + } - /* Our parameter data is no longer needed. */ + /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ + sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); + + if (pParameters->pDescriptorCapture != NULL) { + pParameters->pDescriptorCapture->format = ma_format_f32; + pParameters->pDescriptorCapture->channels = (ma_uint32)channels; + pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); + pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; + pParameters->pDescriptorCapture->periodCount = 1; + } + + if (pParameters->pDescriptorPlayback != NULL) { + pParameters->pDescriptorPlayback->format = ma_format_f32; + pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; + pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); + pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; + pParameters->pDescriptorPlayback->periodCount = 1; + } + + /* At this point we're done and we can return. */ + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); + pParameters->pDevice->webaudio.initResult = MA_SUCCESS; ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); } - - static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) { ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; @@ -39973,7 +40037,7 @@ static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T MA_ASSERT(pParameters != NULL); if (success == EM_FALSE) { - pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + pParameters->pDevice->webaudio.initResult = MA_ERROR; return; } @@ -39984,327 +40048,8 @@ static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T } #endif -static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ -#if defined(MA_USE_AUDIO_WORKLETS) - EMSCRIPTEN_WEBAUDIO_T audioContext; - void* pStackBuffer; - size_t intermediaryBufferSizeInFrames; - float* pIntermediaryBuffer; -#endif - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { - return MA_NO_DEVICE; - } - - /* We're going to calculate some stuff in C just to simplify the JS code. */ - channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; - sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames); - -#if defined(MA_USE_AUDIO_WORKLETS) - { - ma_audio_worklet_thread_initialized_data* pInitParameters; - EmscriptenWebAudioCreateAttributes audioContextAttributes; - - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; - audioContextAttributes.sampleRate = sampleRate; - - /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ - audioContext = emscripten_create_audio_context(&audioContextAttributes); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: AUDIO CONTEXT CREATED\n"); - - /* - We now need to create a worker thread. This is a bit weird because we need to allocate our - own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to - allocate this on the heap to keep it simple. - */ - pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); - if (pStackBuffer == NULL) { - emscripten_destroy_audio_context(audioContext); - return MA_OUT_OF_MEMORY; - } - - /* - We need an intermediary buffer for data conversion. WebAudio reports data in uninterleaved - format whereas we require it to be interleaved. We'll do this in chunks of 128 frames. - */ - intermediaryBufferSizeInFrames = 128; - pIntermediaryBuffer = ma_malloc(intermediaryBufferSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); - if (pIntermediaryBuffer == NULL) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(audioContext); - return MA_OUT_OF_MEMORY; - } - - pInitParameters = ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); - if (pInitParameters == NULL) { - ma_free(pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(audioContext); - return MA_OUT_OF_MEMORY; - } - - pInitParameters->pDevice = pDevice; - pInitParameters->pConfig = pConfig; - pInitParameters->pDescriptor = pDescriptor; - pInitParameters->deviceType = deviceType; - pInitParameters->channels = channels; - - /* - We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of - the Emscripten WebAudio stuff is asynchronous. - */ - pDevice->webaudio.isInitialized = MA_FALSE; - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: CREATING WORKLET\n"); - - emscripten_start_wasm_audio_worklet_thread_async(audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); - - /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ - while (pDevice->webaudio.isInitialized == MA_FALSE) { - emscripten_sleep(1); - } - - /* - Now that initialization is finished we can go ahead and extract our channel count so that - miniaudio can set up a data converter at a higher level. - */ - if (deviceType == ma_device_type_capture) { - /* - For capture we won't actually know what the channel count is. Everything I've seen seems - to indicate that the default channel count is 2, so I'm sticking with that. - */ - channels = 2; - } else { - /* Get the channel count from the audio context. */ - channels = (ma_uint32)EM_ASM_INT({ - return emscriptenGetAudioObject($0).destination.channelCount; - }, audioContext); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: INITIALIZED. channels = %u\n", channels); - } -#else - /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ - int deviceIndex = EM_ASM_INT({ - var channels = $0; - var sampleRate = $1; - var bufferSize = $2; /* In PCM frames. */ - var isCapture = $3; - var pDevice = $4; - var pAllocationCallbacks = $5; - - if (typeof(window.miniaudio) === 'undefined') { - return -1; /* Context not initialized. */ - } - - var device = {}; - - /* The AudioContext must be created in a suspended state. */ - device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate}); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - - /* We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. */ - device.intermediaryBufferSizeInBytes = channels * bufferSize * 4; - device.intermediaryBuffer = _ma_malloc_emscripten(device.intermediaryBufferSizeInBytes, pAllocationCallbacks); - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - - /* - Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations. - - ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback - that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of - something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to - work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL - implementation. I'll be avoiding that insane AudioWorklet API like the plague... - - For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the - playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the - MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've - been unable to figure out how to get this as raw PCM. The closest I can think is to use the MIME type for WAV files and just parse it, but I don't know - how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like - this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know! - */ - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels); - - if (isCapture) { - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBuffer === undefined) { - return; /* This means the device has been uninitialized. */ - } - - if (device.intermediaryBufferView.length == 0) { - /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - } - - /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */ - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - - /* There are some situations where we may want to send silence to the client. */ - var sendSilence = false; - if (device.streamNode === undefined) { - sendSilence = true; - } - - /* Sanity check. This will never happen, right? */ - if (e.inputBuffer.numberOfChannels != channels) { - console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence."); - sendSilence = true; - } - - /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */ - var totalFramesProcessed = 0; - while (totalFramesProcessed < e.inputBuffer.length) { - var framesRemaining = e.inputBuffer.length - totalFramesProcessed; - var framesToProcess = framesRemaining; - if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { - framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); - } - - /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */ - if (sendSilence) { - device.intermediaryBufferView.fill(0.0); - } else { - for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { - for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) { - device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame]; - } - } - } - - /* Send data to the client from our intermediary buffer. */ - _ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); - - totalFramesProcessed += framesToProcess; - } - }; - - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - device.streamNode = device.webaudio.createMediaStreamSource(stream); - device.streamNode.connect(device.scriptNode); - device.scriptNode.connect(device.webaudio.destination); - }) - .catch(function(error) { - /* I think this should output silence... */ - device.scriptNode.connect(device.webaudio.destination); - }); - } else { - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBuffer === undefined) { - return; /* This means the device has been uninitialized. */ - } - - if(device.intermediaryBufferView.length == 0) { - /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - } - - var outputSilence = false; - - /* Sanity check. This will never happen, right? */ - if (e.outputBuffer.numberOfChannels != channels) { - console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence."); - outputSilence = true; - return; - } - - /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */ - var totalFramesProcessed = 0; - while (totalFramesProcessed < e.outputBuffer.length) { - var framesRemaining = e.outputBuffer.length - totalFramesProcessed; - var framesToProcess = framesRemaining; - if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { - framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); - } - - /* Read data from the client into our intermediary buffer. */ - _ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); - - /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */ - if (outputSilence) { - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - } else { - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - var outputBuffer = e.outputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { - outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; - } - } - } - - totalFramesProcessed += framesToProcess; - } - }; - - device.scriptNode.connect(device.webaudio.destination); - } - - return miniaudio.track_device(device); - }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice, &pDevice->pContext->allocationCallbacks); - - if (deviceIndex < 0) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } -#endif - -#if defined(MA_USE_AUDIO_WORKLETS) - if (deviceType == ma_device_type_capture) { - pDevice->webaudio.audioContextCapture = audioContext; - pDevice->webaudio.pStackBufferCapture = pStackBuffer; - pDevice->webaudio.intermediaryBufferSizeInFramesCapture = intermediaryBufferSizeInFrames; - pDevice->webaudio.pIntermediaryBufferCapture = pIntermediaryBuffer; - } else { - pDevice->webaudio.audioContextPlayback = audioContext; - pDevice->webaudio.pStackBufferPlayback = pStackBuffer; - pDevice->webaudio.intermediaryBufferSizeInFramesPlayback = intermediaryBufferSizeInFrames; - pDevice->webaudio.pIntermediaryBufferPlayback = pIntermediaryBuffer; - } -#else - if (deviceType == ma_device_type_capture) { - pDevice->webaudio.indexCapture = deviceIndex; - } else { - pDevice->webaudio.indexPlayback = deviceIndex; - } -#endif - - pDescriptor->format = ma_format_f32; - pDescriptor->channels = channels; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->periodSizeInFrames = periodSizeInFrames; - pDescriptor->periodCount = 1; - -#if defined(MA_USE_AUDIO_WORKLETS) - pDescriptor->sampleRate = sampleRate; /* Is this good enough to be used in the general case? */ -#else - pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); -#endif - - return MA_SUCCESS; -} - static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { - ma_result result; - if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } @@ -40315,55 +40060,269 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co return MA_SHARE_MODE_NOT_SUPPORTED; } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } + /* + With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so + it might be worthwhile to look into that as well. + */ + #if defined(MA_USE_AUDIO_WORKLETS) + { + EmscriptenWebAudioCreateAttributes audioContextAttributes; + ma_audio_worklet_thread_initialized_data* pInitParameters; + void* pStackBuffer; - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture); + if (pConfig->performanceProfile == ma_performance_profile_conservative) { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; + } else { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; + } + + /* + In my testing, Firefox does not seem to capture audio data properly if the sample rate is set + to anything other than 48K. This does not seem to be the case for other browsers. For this reason, + if the device type is anything other than playback, we'll leave the sample rate as-is and let the + browser pick the appropriate rate for us. + */ + if (pConfig->deviceType == ma_device_type_playback) { + audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; + } else { + audioContextAttributes.sampleRate = 0; + } + + /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ + pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); + + + /* + With the context created we can now create the worklet. We can only have a single worklet per audio + context which means we'll need to craft this appropriately to handle duplex devices correctly. + */ + + /* + We now need to create a worker thread. This is a bit weird because we need to allocate our + own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to + allocate this on the heap to keep it simple. + */ + pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); + if (pStackBuffer == NULL) { + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return MA_OUT_OF_MEMORY; + } + + /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ + pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); + if (pInitParameters == NULL) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return MA_OUT_OF_MEMORY; + } + + pInitParameters->pDevice = pDevice; + pInitParameters->pConfig = pConfig; + pInitParameters->pDescriptorPlayback = pDescriptorPlayback; + pInitParameters->pDescriptorCapture = pDescriptorCapture; + + /* + We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of + the Emscripten WebAudio stuff is asynchronous. + */ + pDevice->webaudio.initResult = MA_BUSY; + { + emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); + } + while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ + + /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ + if (pDevice->webaudio.initResult != MA_SUCCESS) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return pDevice->webaudio.initResult; + } + + /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ + pDevice->webaudio.deviceIndex = EM_ASM_INT({ + return miniaudio.track_device({ + webaudio: emscriptenGetAudioObject($0), + state: 1 /* 1 = ma_device_state_stopped */ + }); + }, pDevice->webaudio.audioContext); + + return MA_SUCCESS; + } + #else + { + /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ + ma_uint32 deviceIndex; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; + + /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */ + if (pConfig->deviceType == ma_device_type_capture) { + channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; + } else { + channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; + } + + /* + When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's + native rate. For this reason we're leaving the sample rate untouched for capture devices. + */ + if (pConfig->deviceType == ma_device_type_playback) { + sampleRate = pDescriptorPlayback->sampleRate; + } else { + sampleRate = 0; /* Let the browser decide when capturing. */ + } + + /* The period size needs to be a power of 2. */ + if (pConfig->deviceType == ma_device_type_capture) { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); + } else { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); + } + + /* We need an intermediary buffer for doing interleaving and deinterleaving. */ + pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); + if (pDevice->webaudio.pIntermediaryBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + deviceIndex = EM_ASM_INT({ + var deviceType = $0; + var channels = $1; + var sampleRate = $2; + var bufferSize = $3; + var pIntermediaryBuffer = $4; + var pDevice = $5; + + if (typeof(window.miniaudio) === 'undefined') { + return -1; /* Context not initialized. */ } - return result; - } - } - return MA_SUCCESS; + var device = {}; + + /* First thing we need is an AudioContext. */ + var audioContextOptions = {}; + if (deviceType == window.miniaudio.device_type.playback) { + audioContextOptions.sampleRate = sampleRate; + } + + device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); + device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ + device.state = window.miniaudio.device_state.stopped; + + /* + We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we + need to specify an output and configure the channel count there. + */ + var channelCountIn = 0; + var channelCountOut = channels; + if (deviceType != window.miniaudio.device_type.playback) { + channelCountIn = channels; + } + + device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); + + /* The node processing callback. */ + device.scriptNode.onaudioprocess = function(e) { + if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { + device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); + } + + /* Do the capture side first. */ + if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + /* The data must be interleaved before being processed miniaudio. */ + for (var iChannel = 0; iChannel < channels; iChannel += 1) { + var inputBuffer = e.inputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + + for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { + intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; + } + } + + _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + } + + if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) { + _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + var outputBuffer = e.outputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + + for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { + outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; + } + } + } else { + /* It's a capture-only device. Make sure the output is silenced. */ + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + e.outputBuffer.getChannelData(iChannel).fill(0.0); + } + } + }; + + /* Now we need to connect our node to the graph. */ + if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + device.streamNode = device.webaudio.createMediaStreamSource(stream); + device.streamNode.connect(device.scriptNode); + device.scriptNode.connect(device.webaudio.destination); + }) + .catch(function(error) { + console.log("Failed to get user media: " + error); + }); + } + + if (deviceType == miniaudio.device_type.playback) { + device.scriptNode.connect(device.webaudio.destination); + } + + return miniaudio.track_device(device); + }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); + + if (deviceIndex < 0) { + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + pDevice->webaudio.deviceIndex = deviceIndex; + + /* Grab the sample rate from the audio context directly. */ + sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); + + if (pDescriptorCapture != NULL) { + pDescriptorCapture->format = ma_format_f32; + pDescriptorCapture->channels = channels; + pDescriptorCapture->sampleRate = sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = 1; + } + + if (pDescriptorPlayback != NULL) { + pDescriptorPlayback->format = ma_format_f32; + pDescriptorPlayback->channels = channels; + pDescriptorPlayback->sampleRate = sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = 1; + } + + return MA_SUCCESS; + } + #endif } static ma_result ma_device_start__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); -#if defined(MA_USE_AUDIO_WORKLETS) - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextPlayback); - } -#else - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = 2; /* ma_device_state_started */ - }, pDevice->webaudio.indexCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = 2; /* ma_device_state_started */ - }, pDevice->webaudio.indexPlayback); - } -#endif + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.resume(); + device.state = miniaudio.device_state.started; + }, pDevice->webaudio.deviceIndex); return MA_SUCCESS; } @@ -40381,37 +40340,11 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to do any kind of explicit draining. */ - -#if defined(MA_USE_AUDIO_WORKLETS) - /* I can't seem to find a way to suspend an AudioContext via the C Emscripten API. Is this an oversight? */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - emscriptenGetAudioObject($0).suspend(); - }, pDevice->webaudio.audioContextCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - emscriptenGetAudioObject($0).suspend(); - }, pDevice->webaudio.audioContextPlayback); - } -#else - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - }, pDevice->webaudio.indexCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - }, pDevice->webaudio.indexPlayback); - } -#endif + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.suspend(); + device.state = miniaudio.device_state.stopped; + }, pDevice->webaudio.deviceIndex); ma_device__on_notification_stopped(pDevice); @@ -40428,7 +40361,7 @@ static ma_result ma_context_uninit__webaudio(ma_context* pContext) /* Remove the global miniaudio object from window if there are no more references to it. */ EM_ASM({ if (typeof(window.miniaudio) !== 'undefined') { - window.miniaudio.referenceCount--; + window.miniaudio.referenceCount -= 1; if (window.miniaudio.referenceCount === 0) { delete window.miniaudio; } @@ -40456,7 +40389,20 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex window.miniaudio = { referenceCount: 0 }; - miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + + /* Device types. */ + window.miniaudio.device_type = {}; + window.miniaudio.device_type.playback = $0; + window.miniaudio.device_type.capture = $1; + window.miniaudio.device_type.duplex = $2; + + /* Device states. */ + window.miniaudio.device_state = {}; + window.miniaudio.device_state.stopped = $3; + window.miniaudio.device_state.started = $4; + + /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + miniaudio.devices = []; miniaudio.track_device = function(device) { /* Try inserting into a free slot first. */ @@ -40519,10 +40465,10 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex }); } - window.miniaudio.referenceCount++; + window.miniaudio.referenceCount += 1; return 1; - }, 0); /* Must pass in a dummy argument for C99 compatibility. */ + }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); if (resultFromJS != 1) { return MA_FAILED_TO_INIT_BACKEND; @@ -40857,10 +40803,14 @@ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceTy static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) { ma_device* pDevice = (ma_device*)pData; +#ifdef MA_WIN32 + HRESULT CoInitializeResult; +#endif + MA_ASSERT(pDevice != NULL); #ifdef MA_WIN32 - ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); + CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); #endif /* @@ -40946,7 +40896,9 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) } #ifdef MA_WIN32 - ma_CoUninitialize(pDevice->pContext); + if (CoInitializeResult == S_OK) { + ma_CoUninitialize(pDevice->pContext); + } #endif return (ma_thread_result)0; @@ -40969,14 +40921,16 @@ static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - ma_CoUninitialize(pContext); + if (pContext->win32.CoInitializeResult == S_OK) { + ma_CoUninitialize(pContext); + } #if defined(MA_WIN32_DESKTOP) - ma_dlclose(pContext, pContext->win32.hUser32DLL); - ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); #endif - ma_dlclose(pContext, pContext->win32.hOle32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); #else (void)pContext; #endif @@ -40989,44 +40943,44 @@ static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #if defined(MA_WIN32_DESKTOP) /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); + pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); if (pContext->win32.hUser32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); + pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); if (pContext->win32.hAdvapi32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #endif /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll"); + pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); if (pContext->win32.hOle32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2"); + pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); + pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); #else (void)pContext; /* Unused. */ #endif - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); + pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); return MA_SUCCESS; } #else @@ -41901,7 +41855,6 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } - /* If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to be done after post_init_setup() because we'll need access to the sample rate. @@ -44682,13 +44635,14 @@ static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uin d1 = vmovq_n_f32(0); } else if (ditherMode == ma_dither_mode_rectangle) { float d0v[4]; + float d1v[4]; + d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0 = vld1q_f32(d0v); - float d1v[4]; d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); @@ -44696,13 +44650,14 @@ static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uin d1 = vld1q_f32(d1v); } else { float d0v[4]; + float d1v[4]; + d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); d0 = vld1q_f32(d0v); - float d1v[4]; d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); @@ -49315,48 +49270,65 @@ MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, return MA_INVALID_ARGS; } - /* - For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for - the conversion to a float which we use for the linear interpolation. This might be changed later. - */ - if (frameCount + pFader->cursorInFrames > UINT_MAX) { - frameCount = UINT_MAX - pFader->cursorInFrames; + /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ + if (pFader->cursorInFrames < 0) { + ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; + if (absCursorInFrames > frameCount) { + absCursorInFrames = frameCount; + } + + ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); + + pFader->cursorInFrames += absCursorInFrames; + frameCount -= absCursorInFrames; + pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); + pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); } - /* Optimized path if volumeBeg and volumeEnd are equal. */ - if (pFader->volumeBeg == pFader->volumeEnd) { - if (pFader->volumeBeg == 1) { - /* Straight copy. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); - } else { - /* Copy with volume. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); + if (pFader->cursorInFrames >= 0) { + /* + For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for + the conversion to a float which we use for the linear interpolation. This might be changed later. + */ + if (frameCount + pFader->cursorInFrames > UINT_MAX) { + frameCount = UINT_MAX - pFader->cursorInFrames; } - } else { - /* Slower path. Volumes are different, so may need to do an interpolation. */ - if (pFader->cursorInFrames >= pFader->lengthInFrames) { - /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } else { - /* Slow path. This is where we do the actual fading. */ - ma_uint64 iFrame; - ma_uint32 iChannel; - /* For now we only support f32. Support for other formats will be added later. */ - if (pFader->config.format == ma_format_f32) { - const float* pFramesInF32 = (const float*)pFramesIn; - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ - float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); - - for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; - } - } + /* Optimized path if volumeBeg and volumeEnd are equal. */ + if (pFader->volumeBeg == pFader->volumeEnd) { + if (pFader->volumeBeg == 1) { + /* Straight copy. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); } else { - return MA_NOT_IMPLEMENTED; + /* Copy with volume. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); + } + } else { + /* Slower path. Volumes are different, so may need to do an interpolation. */ + if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { + /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); + } else { + /* Slow path. This is where we do the actual fading. */ + ma_uint64 iFrame; + ma_uint32 iChannel; + + /* For now we only support f32. Support for other formats might be added later. */ + if (pFader->config.format == ma_format_f32) { + const float* pFramesInF32 = (const float*)pFramesIn; + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ + float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); + + for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; + } + } + } else { + return MA_NOT_IMPLEMENTED; + } } } } @@ -49386,6 +49358,11 @@ MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, } MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) +{ + ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); +} + +MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) { if (pFader == NULL) { return; @@ -49404,10 +49381,15 @@ MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd lengthInFrames = UINT_MAX; } + /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ + if (startOffsetInFrames > INT_MAX) { + startOffsetInFrames = INT_MAX; + } + pFader->volumeBeg = volumeBeg; pFader->volumeEnd = volumeEnd; pFader->lengthInFrames = lengthInFrames; - pFader->cursorInFrames = 0; /* Reset cursor. */ + pFader->cursorInFrames = -startOffsetInFrames; } MA_API float ma_fader_get_current_volume(const ma_fader* pFader) @@ -49416,10 +49398,15 @@ MA_API float ma_fader_get_current_volume(const ma_fader* pFader) return 0.0f; } + /* Any frames prior to the start of the fade period will be at unfaded volume. */ + if (pFader->cursorInFrames < 0) { + return 1.0f; + } + /* The current volume depends on the position of the cursor. */ if (pFader->cursorInFrames == 0) { return pFader->volumeBeg; - } else if (pFader->cursorInFrames >= pFader->lengthInFrames) { + } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ return pFader->volumeEnd; } else { /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ @@ -50297,7 +50284,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, } /* If we're not spatializing we need to run an optimized path. */ - if (c89atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { + if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { if (ma_spatializer_listener_is_enabled(pListener)) { /* No attenuation is required, but we'll need to do some channel conversion. */ if (pSpatializer->channelsIn == pSpatializer->channelsOut) { @@ -50638,7 +50625,7 @@ MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, m return; } - c89atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); + ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); } MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) @@ -50647,7 +50634,7 @@ MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatia return ma_attenuation_model_none; } - return (ma_attenuation_model)c89atomic_load_i32(&pSpatializer->attenuationModel); + return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); } MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) @@ -50656,7 +50643,7 @@ MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_posi return; } - c89atomic_exchange_i32(&pSpatializer->positioning, positioning); + ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); } MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) @@ -50665,7 +50652,7 @@ MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpat return ma_positioning_absolute; } - return (ma_positioning)c89atomic_load_i32(&pSpatializer->positioning); + return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); } MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) @@ -50674,7 +50661,7 @@ MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rollo return; } - c89atomic_exchange_f32(&pSpatializer->rolloff, rolloff); + ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); } MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) @@ -50683,7 +50670,7 @@ MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->rolloff); + return ma_atomic_load_f32(&pSpatializer->rolloff); } MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) @@ -50692,7 +50679,7 @@ MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minG return; } - c89atomic_exchange_f32(&pSpatializer->minGain, minGain); + ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); } MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) @@ -50701,7 +50688,7 @@ MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->minGain); + return ma_atomic_load_f32(&pSpatializer->minGain); } MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) @@ -50710,7 +50697,7 @@ MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxG return; } - c89atomic_exchange_f32(&pSpatializer->maxGain, maxGain); + ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); } MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) @@ -50719,7 +50706,7 @@ MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->maxGain); + return ma_atomic_load_f32(&pSpatializer->maxGain); } MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) @@ -50728,7 +50715,7 @@ MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float return; } - c89atomic_exchange_f32(&pSpatializer->minDistance, minDistance); + ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); } MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) @@ -50737,7 +50724,7 @@ MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->minDistance); + return ma_atomic_load_f32(&pSpatializer->minDistance); } MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) @@ -50746,7 +50733,7 @@ MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float return; } - c89atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); + ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); } MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) @@ -50755,7 +50742,7 @@ MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->maxDistance); + return ma_atomic_load_f32(&pSpatializer->maxDistance); } MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) @@ -50764,9 +50751,9 @@ MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAng return; } - c89atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); - c89atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); - c89atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); + ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); + ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); + ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); } MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) @@ -50776,15 +50763,15 @@ MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* p } if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = c89atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); + *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); } if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = c89atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); + *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); } if (pOuterGain != NULL) { - *pOuterGain = c89atomic_load_f32(&pSpatializer->coneOuterGain); + *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); } } @@ -50794,7 +50781,7 @@ MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, floa return; } - c89atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); + ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); } MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) @@ -50803,7 +50790,7 @@ MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatialize return 1; } - return c89atomic_load_f32(&pSpatializer->dopplerFactor); + return ma_atomic_load_f32(&pSpatializer->dopplerFactor); } MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) @@ -50812,7 +50799,7 @@ MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pS return; } - c89atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); + ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); } MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) @@ -50821,7 +50808,7 @@ MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatiali return 1; } - return c89atomic_load_f32(&pSpatializer->directionalAttenuationFactor); + return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); } MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) @@ -51343,8 +51330,10 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear } } - /* Filter. */ - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); + } framesProcessedIn += 1; pResampler->inTimeInt -= 1; @@ -51430,8 +51419,10 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_r MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - /* Filter. */ - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); + } pFramesOutS16 += pResampler->config.channels; } @@ -51503,8 +51494,10 @@ static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear } } - /* Filter. */ - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); + } framesProcessedIn += 1; pResampler->inTimeInt -= 1; @@ -51590,8 +51583,10 @@ static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_r MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - /* Filter. */ - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); + } pFramesOutF32 += pResampler->config.channels; } @@ -51661,7 +51656,7 @@ MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResamp return MA_INVALID_ARGS; } - d = 1000; + d = 1000000; n = (ma_uint32)(ratioInOut * d); if (n == 0) { @@ -56061,13 +56056,13 @@ static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffs static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset))); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); } static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset))); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); } static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) @@ -56160,8 +56155,8 @@ MA_API void ma_rb_reset(ma_rb* pRB) return; } - c89atomic_exchange_32(&pRB->encodedReadOffset, 0); - c89atomic_exchange_32(&pRB->encodedWriteOffset, 0); + ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); } MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) @@ -56180,10 +56175,10 @@ MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppB } /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* @@ -56219,7 +56214,7 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) return MA_INVALID_ARGS; } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ @@ -56235,7 +56230,7 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) newReadOffsetLoopFlag ^= 0x80000000; } - c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); if (ma_rb_pointer_distance(pRB) == 0) { return MA_AT_END; @@ -56260,10 +56255,10 @@ MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** pp } /* The returned buffer should never overtake the read buffer. */ - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* @@ -56305,7 +56300,7 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) return MA_INVALID_ARGS; } - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ @@ -56321,7 +56316,7 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) newWriteOffsetLoopFlag ^= 0x80000000; } - c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); if (ma_rb_pointer_distance(pRB) == 0) { return MA_AT_END; @@ -56345,10 +56340,10 @@ MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) return MA_INVALID_ARGS; } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); newReadOffsetLoopFlag = readOffsetLoopFlag; @@ -56370,7 +56365,7 @@ MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) } } - c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); return MA_SUCCESS; } @@ -56389,10 +56384,10 @@ MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) return MA_INVALID_ARGS; } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); newWriteOffsetLoopFlag = writeOffsetLoopFlag; @@ -56414,7 +56409,7 @@ MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) } } - c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); return MA_SUCCESS; } @@ -56431,10 +56426,10 @@ MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) return 0; } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); if (readOffsetLoopFlag == writeOffsetLoopFlag) { @@ -56529,7 +56524,7 @@ static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, voi if (framesToRead > 0xFFFFFFFF) { framesToRead = 0xFFFFFFFF; } - + mappedFrameCount = (ma_uint32)framesToRead; result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); if (result != MA_SUCCESS) { @@ -56579,7 +56574,7 @@ static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pData return MA_SUCCESS; } -static ma_data_source_vtable ma_gRBDataSourceVTable = +static ma_data_source_vtable ma_gRBDataSourceVTable = { ma_pcm_rb_data_source__on_read, NULL, /* onSeek */ @@ -57573,7 +57568,7 @@ MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool return MA_INVALID_ARGS; } - c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); + ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); /* If there's no callback for this just treat it as a successful no-op. */ if (pDataSourceBase->vtable->onSetLooping == NULL) { @@ -57591,7 +57586,7 @@ MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) return MA_FALSE; } - return c89atomic_load_32(&pDataSourceBase->isLooping); + return ma_atomic_load_32(&pDataSourceBase->isLooping); } MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) @@ -57643,7 +57638,7 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou pDataSourceBase->loopBegInFrames = 0; pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - + /* Seek to within range. Note that our seek positions here are relative to the new range. We don't want do do this if we failed to retrieve the cursor earlier on because it probably means the data source @@ -58357,9 +58352,9 @@ MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, } /* All pages need to be freed. */ - pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); + pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); while (pPage != NULL) { - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext); + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); ma_free(pPage, pAllocationCallbacks); pPage = pNext; @@ -58399,7 +58394,7 @@ MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_au } /* Calculate the length from the linked list. */ - for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { + for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { *pLength += pPage->sizeInFrames; } @@ -58465,12 +58460,12 @@ MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_da /* First thing to do is update the tail. */ for (;;) { - ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail); + ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); ma_paged_audio_buffer_page* pNewTail = pPage; - if (c89atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { + if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ - c89atomic_exchange_ptr(&pOldTail->pNext, pPage); + ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); break; /* Done. */ } } @@ -58627,7 +58622,7 @@ MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pP if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); if (pNext == NULL) { result = MA_AT_END; break; /* We've reached the end. */ @@ -58669,12 +58664,12 @@ MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* ma_paged_audio_buffer_page* pPage; ma_uint64 runningCursor = 0; - for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { + for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { ma_uint64 pageRangeBeg = runningCursor; ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; if (frameIndex >= pageRangeBeg) { - if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ + if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ /* We found the page. */ pPagedAudioBuffer->pCurrent = pPage; pPagedAudioBuffer->absoluteCursor = frameIndex; @@ -58887,85 +58882,36 @@ MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo } -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_vfs_file file; - ma_file_info info; - void* pData; - size_t bytesRead; - - if (ppData != NULL) { - *ppData = NULL; - } - if (pSize != NULL) { - *pSize = 0; - } - - if (ppData == NULL) { - return MA_INVALID_ARGS; - } - - if (pFilePath != NULL) { - result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - } else { - result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); - } - if (result != MA_SUCCESS) { - return result; - } - - result = ma_vfs_info(pVFS, file, &info); - if (result != MA_SUCCESS) { - ma_vfs_close(pVFS, file); - return result; - } - - if (info.sizeInBytes > MA_SIZE_MAX) { - ma_vfs_close(pVFS, file); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ - if (pData == NULL) { - ma_vfs_close(pVFS, file); - return result; - } - - result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ - ma_vfs_close(pVFS, file); - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - if (pSize != NULL) { - *pSize = bytesRead; - } - - MA_ASSERT(ppData != NULL); - *ppData = pData; - - return MA_SUCCESS; -} - -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); -} - -MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); -} - - #if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) #define MA_USE_WIN32_FILEIO #endif #if defined(MA_USE_WIN32_FILEIO) +/* +We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do +not have the Ex version. We therefore need to do some dynamic branching depending on what's available. + +We load these when we load our first file from the default VFS. It's left open for the life of the +program and is left to the OS to uninitialize when the program terminates. +*/ +typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); +typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); + +static ma_handle hKernel32DLL = NULL; +static ma_SetFilePointer_proc ma_SetFilePointer = NULL; +static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; + +static void ma_win32_fileio_init(void) +{ + if (hKernel32DLL == NULL) { + hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); + if (hKernel32DLL != NULL) { + ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); + ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); + } + } +} + static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) { *pDesiredAccess = 0; @@ -58997,6 +58943,9 @@ static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, (void)pVFS; + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); @@ -59017,6 +58966,9 @@ static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFile (void)pVFS; + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); @@ -59142,16 +59094,19 @@ static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i dwMoveMethod = FILE_BEGIN; } -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - /* No SetFilePointerEx() so restrict to 31 bits. */ - if (origin > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; + if (ma_SetFilePointerEx != NULL) { + result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); + } else if (ma_SetFilePointer != NULL) { + /* No SetFilePointerEx() so restrict to 31 bits. */ + if (origin > 0x7FFFFFFF) { + return MA_OUT_OF_RANGE; + } + + result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); + } else { + return MA_NOT_IMPLEMENTED; } - result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); -#else - result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); -#endif if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } @@ -59164,20 +59119,22 @@ static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i LARGE_INTEGER liZero; LARGE_INTEGER liTell; BOOL result; -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - LONG tell; -#endif (void)pVFS; liZero.QuadPart = 0; -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); - liTell.QuadPart = tell; -#else - result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); -#endif + if (ma_SetFilePointerEx != NULL) { + result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); + } else if (ma_SetFilePointer != NULL) { + LONG tell; + + result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); + liTell.QuadPart = tell; + } else { + return MA_NOT_IMPLEMENTED; + } + if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } @@ -59653,6 +59610,81 @@ MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_ +static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_vfs_file file; + ma_file_info info; + void* pData; + size_t bytesRead; + + if (ppData != NULL) { + *ppData = NULL; + } + if (pSize != NULL) { + *pSize = 0; + } + + if (ppData == NULL) { + return MA_INVALID_ARGS; + } + + if (pFilePath != NULL) { + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + } else { + result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); + } + if (result != MA_SUCCESS) { + return result; + } + + result = ma_vfs_or_default_info(pVFS, file, &info); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + if (info.sizeInBytes > MA_SIZE_MAX) { + ma_vfs_or_default_close(pVFS, file); + return MA_TOO_BIG; + } + + pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ + if (pData == NULL) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ + ma_vfs_or_default_close(pVFS, file); + + if (result != MA_SUCCESS) { + ma_free(pData, pAllocationCallbacks); + return result; + } + + if (pSize != NULL) { + *pSize = bytesRead; + } + + MA_ASSERT(ppData != NULL); + *ppData = pData; + + return MA_SUCCESS; +} + +MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); +} + +MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); +} + + + /************************************************************************************************************************************************************** Decoding and Encoding Headers. These are auto-generated from a tool. @@ -59660,195 +59692,76 @@ Decoding and Encoding Headers. These are auto-generated from a tool. **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) /* dr_wav_h begin */ -#ifndef dr_wav_h -#define dr_wav_h +#ifndef ma_dr_wav_h +#define ma_dr_wav_h #ifdef __cplusplus extern "C" { #endif -#define DRWAV_STRINGIFY(x) #x -#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) -#define DRWAV_VERSION_MAJOR 0 -#define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 8 -#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) +#define MA_DR_WAV_STRINGIFY(x) #x +#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) +#define MA_DR_WAV_VERSION_MAJOR 0 +#define MA_DR_WAV_VERSION_MINOR 13 +#define MA_DR_WAV_VERSION_REVISION 12 +#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) #include -typedef signed char drwav_int8; -typedef unsigned char drwav_uint8; -typedef signed short drwav_int16; -typedef unsigned short drwav_uint16; -typedef signed int drwav_int32; -typedef unsigned int drwav_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drwav_int64; - typedef unsigned __int64 drwav_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drwav_int64; - typedef unsigned long long drwav_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #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__) - typedef drwav_uint64 drwav_uintptr; -#else - typedef drwav_uint32 drwav_uintptr; -#endif -typedef drwav_uint8 drwav_bool8; -typedef drwav_uint32 drwav_bool32; -#define DRWAV_TRUE 1 -#define DRWAV_FALSE 0 -#if !defined(DRWAV_API) - #if defined(DRWAV_DLL) - #if defined(_WIN32) - #define DRWAV_DLL_IMPORT __declspec(dllimport) - #define DRWAV_DLL_EXPORT __declspec(dllexport) - #define DRWAV_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRWAV_DLL_IMPORT __attribute__((visibility("default"))) - #define DRWAV_DLL_EXPORT __attribute__((visibility("default"))) - #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRWAV_DLL_IMPORT - #define DRWAV_DLL_EXPORT - #define DRWAV_DLL_PRIVATE static - #endif - #endif - #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) - #define DRWAV_API DRWAV_DLL_EXPORT - #else - #define DRWAV_API DRWAV_DLL_IMPORT - #endif - #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE - #else - #define DRWAV_API extern - #define DRWAV_PRIVATE static - #endif -#endif -typedef drwav_int32 drwav_result; -#define DRWAV_SUCCESS 0 -#define DRWAV_ERROR -1 -#define DRWAV_INVALID_ARGS -2 -#define DRWAV_INVALID_OPERATION -3 -#define DRWAV_OUT_OF_MEMORY -4 -#define DRWAV_OUT_OF_RANGE -5 -#define DRWAV_ACCESS_DENIED -6 -#define DRWAV_DOES_NOT_EXIST -7 -#define DRWAV_ALREADY_EXISTS -8 -#define DRWAV_TOO_MANY_OPEN_FILES -9 -#define DRWAV_INVALID_FILE -10 -#define DRWAV_TOO_BIG -11 -#define DRWAV_PATH_TOO_LONG -12 -#define DRWAV_NAME_TOO_LONG -13 -#define DRWAV_NOT_DIRECTORY -14 -#define DRWAV_IS_DIRECTORY -15 -#define DRWAV_DIRECTORY_NOT_EMPTY -16 -#define DRWAV_END_OF_FILE -17 -#define DRWAV_NO_SPACE -18 -#define DRWAV_BUSY -19 -#define DRWAV_IO_ERROR -20 -#define DRWAV_INTERRUPT -21 -#define DRWAV_UNAVAILABLE -22 -#define DRWAV_ALREADY_IN_USE -23 -#define DRWAV_BAD_ADDRESS -24 -#define DRWAV_BAD_SEEK -25 -#define DRWAV_BAD_PIPE -26 -#define DRWAV_DEADLOCK -27 -#define DRWAV_TOO_MANY_LINKS -28 -#define DRWAV_NOT_IMPLEMENTED -29 -#define DRWAV_NO_MESSAGE -30 -#define DRWAV_BAD_MESSAGE -31 -#define DRWAV_NO_DATA_AVAILABLE -32 -#define DRWAV_INVALID_DATA -33 -#define DRWAV_TIMEOUT -34 -#define DRWAV_NO_NETWORK -35 -#define DRWAV_NOT_UNIQUE -36 -#define DRWAV_NOT_SOCKET -37 -#define DRWAV_NO_ADDRESS -38 -#define DRWAV_BAD_PROTOCOL -39 -#define DRWAV_PROTOCOL_UNAVAILABLE -40 -#define DRWAV_PROTOCOL_NOT_SUPPORTED -41 -#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRWAV_SOCKET_NOT_SUPPORTED -44 -#define DRWAV_CONNECTION_RESET -45 -#define DRWAV_ALREADY_CONNECTED -46 -#define DRWAV_NOT_CONNECTED -47 -#define DRWAV_CONNECTION_REFUSED -48 -#define DRWAV_NO_HOST -49 -#define DRWAV_IN_PROGRESS -50 -#define DRWAV_CANCELLED -51 -#define DRWAV_MEMORY_ALREADY_MAPPED -52 -#define DRWAV_AT_END -53 -#define DR_WAVE_FORMAT_PCM 0x1 -#define DR_WAVE_FORMAT_ADPCM 0x2 -#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define DR_WAVE_FORMAT_ALAW 0x6 -#define DR_WAVE_FORMAT_MULAW 0x7 -#define DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE -#define DRWAV_SEQUENTIAL 0x00000001 -DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); -DRWAV_API const char* drwav_version_string(void); +#define MA_DR_WAVE_FORMAT_PCM 0x1 +#define MA_DR_WAVE_FORMAT_ADPCM 0x2 +#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 +#define MA_DR_WAVE_FORMAT_ALAW 0x6 +#define MA_DR_WAVE_FORMAT_MULAW 0x7 +#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 +#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE +#define MA_DR_WAV_SEQUENTIAL 0x00000001 +#define MA_DR_WAV_WITH_METADATA 0x00000002 +MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_wav_version_string(void); typedef enum { - drwav_seek_origin_start, - drwav_seek_origin_current -} drwav_seek_origin; + ma_dr_wav_seek_origin_start, + ma_dr_wav_seek_origin_current +} ma_dr_wav_seek_origin; typedef enum { - drwav_container_riff, - drwav_container_w64, - drwav_container_rf64 -} drwav_container; + ma_dr_wav_container_riff, + ma_dr_wav_container_rifx, + ma_dr_wav_container_w64, + ma_dr_wav_container_rf64, + ma_dr_wav_container_aiff +} ma_dr_wav_container; typedef struct { union { - drwav_uint8 fourcc[4]; - drwav_uint8 guid[16]; + ma_uint8 fourcc[4]; + ma_uint8 guid[16]; } id; - drwav_uint64 sizeInBytes; + ma_uint64 sizeInBytes; unsigned int paddingSize; -} drwav_chunk_header; +} ma_dr_wav_chunk_header; typedef struct { - drwav_uint16 formatTag; - drwav_uint16 channels; - drwav_uint32 sampleRate; - drwav_uint32 avgBytesPerSec; - drwav_uint16 blockAlign; - drwav_uint16 bitsPerSample; - drwav_uint16 extendedSize; - drwav_uint16 validBitsPerSample; - drwav_uint32 channelMask; - drwav_uint8 subFormat[16]; -} drwav_fmt; -DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT); -typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); -typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); -typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT); + ma_uint16 formatTag; + ma_uint16 channels; + ma_uint32 sampleRate; + ma_uint32 avgBytesPerSec; + ma_uint16 blockAlign; + ma_uint16 bitsPerSample; + ma_uint16 extendedSize; + ma_uint16 validBitsPerSample; + ma_uint32 channelMask; + ma_uint8 subFormat[16]; +} ma_dr_wav_fmt; +MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); +typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); +typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); +typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); typedef struct { - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drwav_allocation_callbacks; -typedef struct -{ - const drwav_uint8* data; + const ma_uint8* data; size_t dataSize; size_t currentReadPos; -} drwav__memory_stream; +} ma_dr_wav__memory_stream; typedef struct { void** ppData; @@ -59856,129 +59769,129 @@ typedef struct size_t dataSize; size_t dataCapacity; size_t currentWritePos; -} drwav__memory_stream_write; +} ma_dr_wav__memory_stream_write; typedef struct { - drwav_container container; - drwav_uint32 format; - drwav_uint32 channels; - drwav_uint32 sampleRate; - drwav_uint32 bitsPerSample; -} drwav_data_format; + ma_dr_wav_container container; + ma_uint32 format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 bitsPerSample; +} ma_dr_wav_data_format; typedef enum { - drwav_metadata_type_none = 0, - drwav_metadata_type_unknown = 1 << 0, - drwav_metadata_type_smpl = 1 << 1, - drwav_metadata_type_inst = 1 << 2, - drwav_metadata_type_cue = 1 << 3, - drwav_metadata_type_acid = 1 << 4, - drwav_metadata_type_bext = 1 << 5, - drwav_metadata_type_list_label = 1 << 6, - drwav_metadata_type_list_note = 1 << 7, - drwav_metadata_type_list_labelled_cue_region = 1 << 8, - drwav_metadata_type_list_info_software = 1 << 9, - drwav_metadata_type_list_info_copyright = 1 << 10, - drwav_metadata_type_list_info_title = 1 << 11, - drwav_metadata_type_list_info_artist = 1 << 12, - drwav_metadata_type_list_info_comment = 1 << 13, - drwav_metadata_type_list_info_date = 1 << 14, - 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_all_info_strings = drwav_metadata_type_list_info_software - | drwav_metadata_type_list_info_copyright - | drwav_metadata_type_list_info_title - | drwav_metadata_type_list_info_artist - | drwav_metadata_type_list_info_comment - | 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_all_adtl = drwav_metadata_type_list_label - | drwav_metadata_type_list_note - | drwav_metadata_type_list_labelled_cue_region, - drwav_metadata_type_all = -2, - drwav_metadata_type_all_including_unknown = -1 -} drwav_metadata_type; + ma_dr_wav_metadata_type_none = 0, + ma_dr_wav_metadata_type_unknown = 1 << 0, + ma_dr_wav_metadata_type_smpl = 1 << 1, + ma_dr_wav_metadata_type_inst = 1 << 2, + ma_dr_wav_metadata_type_cue = 1 << 3, + ma_dr_wav_metadata_type_acid = 1 << 4, + ma_dr_wav_metadata_type_bext = 1 << 5, + ma_dr_wav_metadata_type_list_label = 1 << 6, + ma_dr_wav_metadata_type_list_note = 1 << 7, + ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, + ma_dr_wav_metadata_type_list_info_software = 1 << 9, + ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, + ma_dr_wav_metadata_type_list_info_title = 1 << 11, + ma_dr_wav_metadata_type_list_info_artist = 1 << 12, + ma_dr_wav_metadata_type_list_info_comment = 1 << 13, + ma_dr_wav_metadata_type_list_info_date = 1 << 14, + ma_dr_wav_metadata_type_list_info_genre = 1 << 15, + ma_dr_wav_metadata_type_list_info_album = 1 << 16, + ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, + ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software + | ma_dr_wav_metadata_type_list_info_copyright + | ma_dr_wav_metadata_type_list_info_title + | ma_dr_wav_metadata_type_list_info_artist + | ma_dr_wav_metadata_type_list_info_comment + | ma_dr_wav_metadata_type_list_info_date + | ma_dr_wav_metadata_type_list_info_genre + | ma_dr_wav_metadata_type_list_info_album + | ma_dr_wav_metadata_type_list_info_tracknumber, + ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label + | ma_dr_wav_metadata_type_list_note + | ma_dr_wav_metadata_type_list_labelled_cue_region, + ma_dr_wav_metadata_type_all = -2, + ma_dr_wav_metadata_type_all_including_unknown = -1 +} ma_dr_wav_metadata_type; typedef enum { - drwav_smpl_loop_type_forward = 0, - drwav_smpl_loop_type_pingpong = 1, - drwav_smpl_loop_type_backward = 2 -} drwav_smpl_loop_type; + ma_dr_wav_smpl_loop_type_forward = 0, + ma_dr_wav_smpl_loop_type_pingpong = 1, + ma_dr_wav_smpl_loop_type_backward = 2 +} ma_dr_wav_smpl_loop_type; typedef struct { - drwav_uint32 cuePointId; - drwav_uint32 type; - drwav_uint32 firstSampleByteOffset; - drwav_uint32 lastSampleByteOffset; - drwav_uint32 sampleFraction; - drwav_uint32 playCount; -} drwav_smpl_loop; + ma_uint32 cuePointId; + ma_uint32 type; + ma_uint32 firstSampleByteOffset; + ma_uint32 lastSampleByteOffset; + ma_uint32 sampleFraction; + ma_uint32 playCount; +} ma_dr_wav_smpl_loop; typedef struct { - drwav_uint32 manufacturerId; - drwav_uint32 productId; - drwav_uint32 samplePeriodNanoseconds; - drwav_uint32 midiUnityNote; - drwav_uint32 midiPitchFraction; - drwav_uint32 smpteFormat; - drwav_uint32 smpteOffset; - drwav_uint32 sampleLoopCount; - drwav_uint32 samplerSpecificDataSizeInBytes; - drwav_smpl_loop* pLoops; - drwav_uint8* pSamplerSpecificData; -} drwav_smpl; + ma_uint32 manufacturerId; + ma_uint32 productId; + ma_uint32 samplePeriodNanoseconds; + ma_uint32 midiUnityNote; + ma_uint32 midiPitchFraction; + ma_uint32 smpteFormat; + ma_uint32 smpteOffset; + ma_uint32 sampleLoopCount; + ma_uint32 samplerSpecificDataSizeInBytes; + ma_dr_wav_smpl_loop* pLoops; + ma_uint8* pSamplerSpecificData; +} ma_dr_wav_smpl; typedef struct { - drwav_int8 midiUnityNote; - drwav_int8 fineTuneCents; - drwav_int8 gainDecibels; - drwav_int8 lowNote; - drwav_int8 highNote; - drwav_int8 lowVelocity; - drwav_int8 highVelocity; -} drwav_inst; + ma_int8 midiUnityNote; + ma_int8 fineTuneCents; + ma_int8 gainDecibels; + ma_int8 lowNote; + ma_int8 highNote; + ma_int8 lowVelocity; + ma_int8 highVelocity; +} ma_dr_wav_inst; typedef struct { - drwav_uint32 id; - drwav_uint32 playOrderPosition; - drwav_uint8 dataChunkId[4]; - drwav_uint32 chunkStart; - drwav_uint32 blockStart; - drwav_uint32 sampleByteOffset; -} drwav_cue_point; + ma_uint32 id; + ma_uint32 playOrderPosition; + ma_uint8 dataChunkId[4]; + ma_uint32 chunkStart; + ma_uint32 blockStart; + ma_uint32 sampleByteOffset; +} ma_dr_wav_cue_point; typedef struct { - drwav_uint32 cuePointCount; - drwav_cue_point *pCuePoints; -} drwav_cue; + ma_uint32 cuePointCount; + ma_dr_wav_cue_point *pCuePoints; +} ma_dr_wav_cue; typedef enum { - drwav_acid_flag_one_shot = 1, - drwav_acid_flag_root_note_set = 2, - drwav_acid_flag_stretch = 4, - drwav_acid_flag_disk_based = 8, - drwav_acid_flag_acidizer = 16 -} drwav_acid_flag; + ma_dr_wav_acid_flag_one_shot = 1, + ma_dr_wav_acid_flag_root_note_set = 2, + ma_dr_wav_acid_flag_stretch = 4, + ma_dr_wav_acid_flag_disk_based = 8, + ma_dr_wav_acid_flag_acidizer = 16 +} ma_dr_wav_acid_flag; typedef struct { - drwav_uint32 flags; - drwav_uint16 midiUnityNote; - drwav_uint16 reserved1; + ma_uint32 flags; + ma_uint16 midiUnityNote; + ma_uint16 reserved1; float reserved2; - drwav_uint32 numBeats; - drwav_uint16 meterDenominator; - drwav_uint16 meterNumerator; + ma_uint32 numBeats; + ma_uint16 meterDenominator; + ma_uint16 meterNumerator; float tempo; -} drwav_acid; +} ma_dr_wav_acid; typedef struct { - drwav_uint32 cuePointId; - drwav_uint32 stringLength; + ma_uint32 cuePointId; + ma_uint32 stringLength; char* pString; -} drwav_list_label_or_note; +} ma_dr_wav_list_label_or_note; typedef struct { char* pDescription; @@ -59986,206 +59899,210 @@ typedef struct char* pOriginatorReference; char pOriginationDate[10]; char pOriginationTime[8]; - drwav_uint64 timeReference; - drwav_uint16 version; + ma_uint64 timeReference; + ma_uint16 version; char* pCodingHistory; - drwav_uint32 codingHistorySize; - drwav_uint8* pUMID; - drwav_uint16 loudnessValue; - drwav_uint16 loudnessRange; - drwav_uint16 maxTruePeakLevel; - drwav_uint16 maxMomentaryLoudness; - drwav_uint16 maxShortTermLoudness; -} drwav_bext; + ma_uint32 codingHistorySize; + ma_uint8* pUMID; + ma_uint16 loudnessValue; + ma_uint16 loudnessRange; + ma_uint16 maxTruePeakLevel; + ma_uint16 maxMomentaryLoudness; + ma_uint16 maxShortTermLoudness; +} ma_dr_wav_bext; typedef struct { - drwav_uint32 stringLength; + ma_uint32 stringLength; char* pString; -} drwav_list_info_text; +} ma_dr_wav_list_info_text; typedef struct { - drwav_uint32 cuePointId; - drwav_uint32 sampleLength; - drwav_uint8 purposeId[4]; - drwav_uint16 country; - drwav_uint16 language; - drwav_uint16 dialect; - drwav_uint16 codePage; - drwav_uint32 stringLength; + ma_uint32 cuePointId; + ma_uint32 sampleLength; + ma_uint8 purposeId[4]; + ma_uint16 country; + ma_uint16 language; + ma_uint16 dialect; + ma_uint16 codePage; + ma_uint32 stringLength; char* pString; -} drwav_list_labelled_cue_region; +} ma_dr_wav_list_labelled_cue_region; typedef enum { - drwav_metadata_location_invalid, - drwav_metadata_location_top_level, - drwav_metadata_location_inside_info_list, - drwav_metadata_location_inside_adtl_list -} drwav_metadata_location; + ma_dr_wav_metadata_location_invalid, + ma_dr_wav_metadata_location_top_level, + ma_dr_wav_metadata_location_inside_info_list, + ma_dr_wav_metadata_location_inside_adtl_list +} ma_dr_wav_metadata_location; typedef struct { - drwav_uint8 id[4]; - drwav_metadata_location chunkLocation; - drwav_uint32 dataSizeInBytes; - drwav_uint8* pData; -} drwav_unknown_metadata; + ma_uint8 id[4]; + ma_dr_wav_metadata_location chunkLocation; + ma_uint32 dataSizeInBytes; + ma_uint8* pData; +} ma_dr_wav_unknown_metadata; typedef struct { - drwav_metadata_type type; + ma_dr_wav_metadata_type type; union { - drwav_cue cue; - drwav_smpl smpl; - drwav_acid acid; - drwav_inst inst; - drwav_bext bext; - drwav_list_label_or_note labelOrNote; - drwav_list_labelled_cue_region labelledCueRegion; - drwav_list_info_text infoText; - drwav_unknown_metadata unknown; + ma_dr_wav_cue cue; + ma_dr_wav_smpl smpl; + ma_dr_wav_acid acid; + ma_dr_wav_inst inst; + ma_dr_wav_bext bext; + ma_dr_wav_list_label_or_note labelOrNote; + ma_dr_wav_list_labelled_cue_region labelledCueRegion; + ma_dr_wav_list_info_text infoText; + ma_dr_wav_unknown_metadata unknown; } data; -} drwav_metadata; +} ma_dr_wav_metadata; typedef struct { - drwav_read_proc onRead; - drwav_write_proc onWrite; - drwav_seek_proc onSeek; + ma_dr_wav_read_proc onRead; + ma_dr_wav_write_proc onWrite; + ma_dr_wav_seek_proc onSeek; void* pUserData; - drwav_allocation_callbacks allocationCallbacks; - drwav_container container; - drwav_fmt fmt; - drwav_uint32 sampleRate; - drwav_uint16 channels; - drwav_uint16 bitsPerSample; - drwav_uint16 translatedFormatTag; - drwav_uint64 totalPCMFrameCount; - drwav_uint64 dataChunkDataSize; - drwav_uint64 dataChunkDataPos; - drwav_uint64 bytesRemaining; - drwav_uint64 readCursorInPCMFrames; - drwav_uint64 dataChunkDataSizeTargetWrite; - drwav_bool32 isSequentialWrite; - drwav_metadata_type allowedMetadataTypes; - drwav_metadata* pMetadata; - drwav_uint32 metadataCount; - drwav__memory_stream memoryStream; - drwav__memory_stream_write memoryStreamWrite; + ma_allocation_callbacks allocationCallbacks; + ma_dr_wav_container container; + ma_dr_wav_fmt fmt; + ma_uint32 sampleRate; + ma_uint16 channels; + ma_uint16 bitsPerSample; + ma_uint16 translatedFormatTag; + ma_uint64 totalPCMFrameCount; + ma_uint64 dataChunkDataSize; + ma_uint64 dataChunkDataPos; + ma_uint64 bytesRemaining; + ma_uint64 readCursorInPCMFrames; + ma_uint64 dataChunkDataSizeTargetWrite; + ma_bool32 isSequentialWrite; + ma_dr_wav_metadata* pMetadata; + ma_uint32 metadataCount; + ma_dr_wav__memory_stream memoryStream; + ma_dr_wav__memory_stream_write memoryStreamWrite; struct { - drwav_uint32 bytesRemainingInBlock; - drwav_uint16 predictor[2]; - drwav_int32 delta[2]; - drwav_int32 cachedFrames[4]; - drwav_uint32 cachedFrameCount; - drwav_int32 prevFrames[2][2]; + ma_uint32 bytesRemainingInBlock; + ma_uint16 predictor[2]; + ma_int32 delta[2]; + ma_int32 cachedFrames[4]; + ma_uint32 cachedFrameCount; + ma_int32 prevFrames[2][2]; } msadpcm; struct { - drwav_uint32 bytesRemainingInBlock; - drwav_int32 predictor[2]; - drwav_int32 stepIndex[2]; - drwav_int32 cachedFrames[16]; - drwav_uint32 cachedFrameCount; + ma_uint32 bytesRemainingInBlock; + ma_int32 predictor[2]; + ma_int32 stepIndex[2]; + ma_int32 cachedFrames[16]; + ma_uint32 cachedFrameCount; } ima; -} drwav; -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_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount); -DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount); -DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav); -DRWAV_API drwav_result drwav_uninit(drwav* pWav); -DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); -DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor); -DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength); -DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -#ifndef DR_WAV_NO_CONVERSION_API -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); -DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + struct + { + ma_bool8 isLE; + ma_bool8 isUnsigned; + } aiff; +} ma_dr_wav; +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); +MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); +MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); +MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); +MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); +MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); +MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); +MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +#ifndef MA_DR_WAV_NO_CONVERSION_API +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); #endif -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_WAV_NO_CONVERSION_API -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); -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_CONVERSION_API +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data); -DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data); -DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data); -DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data); -DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data); -DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data); -DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data); -DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]); -DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); +MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); +MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); +MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); +MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); +MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); +MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); +MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); +MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); +MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); #ifdef __cplusplus } #endif @@ -60195,354 +60112,284 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) /* dr_flac_h begin */ -#ifndef dr_flac_h -#define dr_flac_h +#ifndef ma_dr_flac_h +#define ma_dr_flac_h #ifdef __cplusplus extern "C" { #endif -#define DRFLAC_STRINGIFY(x) #x -#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) -#define DRFLAC_VERSION_MAJOR 0 -#define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 39 -#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) +#define MA_DR_FLAC_STRINGIFY(x) #x +#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) +#define MA_DR_FLAC_VERSION_MAJOR 0 +#define MA_DR_FLAC_VERSION_MINOR 12 +#define MA_DR_FLAC_VERSION_REVISION 41 +#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) #include -typedef signed char drflac_int8; -typedef unsigned char drflac_uint8; -typedef signed short drflac_int16; -typedef unsigned short drflac_uint16; -typedef signed int drflac_int32; -typedef unsigned int drflac_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drflac_int64; - typedef unsigned __int64 drflac_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drflac_int64; - typedef unsigned long long drflac_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #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__) - typedef drflac_uint64 drflac_uintptr; -#else - typedef drflac_uint32 drflac_uintptr; -#endif -typedef drflac_uint8 drflac_bool8; -typedef drflac_uint32 drflac_bool32; -#define DRFLAC_TRUE 1 -#define DRFLAC_FALSE 0 -#if !defined(DRFLAC_API) - #if defined(DRFLAC_DLL) - #if defined(_WIN32) - #define DRFLAC_DLL_IMPORT __declspec(dllimport) - #define DRFLAC_DLL_EXPORT __declspec(dllexport) - #define DRFLAC_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) - #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) - #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRFLAC_DLL_IMPORT - #define DRFLAC_DLL_EXPORT - #define DRFLAC_DLL_PRIVATE static - #endif - #endif - #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) - #define DRFLAC_API DRFLAC_DLL_EXPORT - #else - #define DRFLAC_API DRFLAC_DLL_IMPORT - #endif - #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE - #else - #define DRFLAC_API extern - #define DRFLAC_PRIVATE static - #endif -#endif #if defined(_MSC_VER) && _MSC_VER >= 1700 - #define DRFLAC_DEPRECATED __declspec(deprecated) + #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) #elif (defined(__GNUC__) && __GNUC__ >= 4) - #define DRFLAC_DEPRECATED __attribute__((deprecated)) + #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) #elif defined(__has_feature) #if __has_feature(attribute_deprecated) - #define DRFLAC_DEPRECATED __attribute__((deprecated)) + #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) #else - #define DRFLAC_DEPRECATED + #define MA_DR_FLAC_DEPRECATED #endif #else - #define DRFLAC_DEPRECATED + #define MA_DR_FLAC_DEPRECATED #endif -DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); -DRFLAC_API const char* drflac_version_string(void); -#ifndef DR_FLAC_BUFFER_SIZE -#define DR_FLAC_BUFFER_SIZE 4096 +MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_flac_version_string(void); +#ifndef MA_DR_FLAC_BUFFER_SIZE +#define MA_DR_FLAC_BUFFER_SIZE 4096 #endif -#if defined(_WIN64) || defined(_LP64) || defined(__LP64__) -#define DRFLAC_64BIT -#endif -#ifdef DRFLAC_64BIT -typedef drflac_uint64 drflac_cache_t; +#ifdef MA_64BIT +typedef ma_uint64 ma_dr_flac_cache_t; #else -typedef drflac_uint32 drflac_cache_t; +typedef ma_uint32 ma_dr_flac_cache_t; #endif -#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 -#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 -#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 -#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 -#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 -#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 -#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 -#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 -#define DRFLAC_PICTURE_TYPE_OTHER 0 -#define DRFLAC_PICTURE_TYPE_FILE_ICON 1 -#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 -#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 -#define DRFLAC_PICTURE_TYPE_COVER_BACK 4 -#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 -#define DRFLAC_PICTURE_TYPE_MEDIA 6 -#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 -#define DRFLAC_PICTURE_TYPE_ARTIST 8 -#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 -#define DRFLAC_PICTURE_TYPE_BAND 10 -#define DRFLAC_PICTURE_TYPE_COMPOSER 11 -#define DRFLAC_PICTURE_TYPE_LYRICIST 12 -#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 -#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 -#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 -#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 -#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 -#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 -#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 -#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 +#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 +#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 +#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 +#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 +#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 +#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 +#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 +#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 +#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 +#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 +#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 typedef enum { - drflac_container_native, - drflac_container_ogg, - drflac_container_unknown -} drflac_container; + ma_dr_flac_container_native, + ma_dr_flac_container_ogg, + ma_dr_flac_container_unknown +} ma_dr_flac_container; typedef enum { - drflac_seek_origin_start, - drflac_seek_origin_current -} drflac_seek_origin; + ma_dr_flac_seek_origin_start, + ma_dr_flac_seek_origin_current +} ma_dr_flac_seek_origin; typedef struct { - drflac_uint64 firstPCMFrame; - drflac_uint64 flacFrameOffset; - drflac_uint16 pcmFrameCount; -} drflac_seekpoint; + ma_uint64 firstPCMFrame; + ma_uint64 flacFrameOffset; + ma_uint16 pcmFrameCount; +} ma_dr_flac_seekpoint; typedef struct { - drflac_uint16 minBlockSizeInPCMFrames; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint32 minFrameSizeInPCMFrames; - drflac_uint32 maxFrameSizeInPCMFrames; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint64 totalPCMFrameCount; - drflac_uint8 md5[16]; -} drflac_streaminfo; + ma_uint16 minBlockSizeInPCMFrames; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint32 minFrameSizeInPCMFrames; + ma_uint32 maxFrameSizeInPCMFrames; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint64 totalPCMFrameCount; + ma_uint8 md5[16]; +} ma_dr_flac_streaminfo; typedef struct { - drflac_uint32 type; + ma_uint32 type; const void* pRawData; - drflac_uint32 rawDataSize; + ma_uint32 rawDataSize; union { - drflac_streaminfo streaminfo; + ma_dr_flac_streaminfo streaminfo; struct { int unused; } padding; struct { - drflac_uint32 id; + ma_uint32 id; const void* pData; - drflac_uint32 dataSize; + ma_uint32 dataSize; } application; struct { - drflac_uint32 seekpointCount; - const drflac_seekpoint* pSeekpoints; + ma_uint32 seekpointCount; + const ma_dr_flac_seekpoint* pSeekpoints; } seektable; struct { - drflac_uint32 vendorLength; + ma_uint32 vendorLength; const char* vendor; - drflac_uint32 commentCount; + ma_uint32 commentCount; const void* pComments; } vorbis_comment; struct { char catalog[128]; - drflac_uint64 leadInSampleCount; - drflac_bool32 isCD; - drflac_uint8 trackCount; + ma_uint64 leadInSampleCount; + ma_bool32 isCD; + ma_uint8 trackCount; const void* pTrackData; } cuesheet; struct { - drflac_uint32 type; - drflac_uint32 mimeLength; + ma_uint32 type; + ma_uint32 mimeLength; const char* mime; - drflac_uint32 descriptionLength; + ma_uint32 descriptionLength; const char* description; - drflac_uint32 width; - drflac_uint32 height; - drflac_uint32 colorDepth; - drflac_uint32 indexColorCount; - drflac_uint32 pictureDataSize; - const drflac_uint8* pPictureData; + ma_uint32 width; + ma_uint32 height; + ma_uint32 colorDepth; + ma_uint32 indexColorCount; + ma_uint32 pictureDataSize; + const ma_uint8* pPictureData; } picture; } data; -} drflac_metadata; -typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); -typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); +} ma_dr_flac_metadata; +typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); +typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); typedef struct { - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drflac_allocation_callbacks; -typedef struct -{ - const drflac_uint8* data; + const ma_uint8* data; size_t dataSize; size_t currentReadPos; -} drflac__memory_stream; +} ma_dr_flac__memory_stream; typedef struct { - drflac_read_proc onRead; - drflac_seek_proc onSeek; + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; void* pUserData; size_t unalignedByteCount; - drflac_cache_t unalignedCache; - drflac_uint32 nextL2Line; - drflac_uint32 consumedBits; - drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; - drflac_cache_t cache; - drflac_uint16 crc16; - drflac_cache_t crc16Cache; - drflac_uint32 crc16CacheIgnoredBytes; -} drflac_bs; + ma_dr_flac_cache_t unalignedCache; + ma_uint32 nextL2Line; + ma_uint32 consumedBits; + ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; + ma_dr_flac_cache_t cache; + ma_uint16 crc16; + ma_dr_flac_cache_t crc16Cache; + ma_uint32 crc16CacheIgnoredBytes; +} ma_dr_flac_bs; typedef struct { - drflac_uint8 subframeType; - drflac_uint8 wastedBitsPerSample; - drflac_uint8 lpcOrder; - drflac_int32* pSamplesS32; -} drflac_subframe; + ma_uint8 subframeType; + ma_uint8 wastedBitsPerSample; + ma_uint8 lpcOrder; + ma_int32* pSamplesS32; +} ma_dr_flac_subframe; typedef struct { - drflac_uint64 pcmFrameNumber; - drflac_uint32 flacFrameNumber; - drflac_uint32 sampleRate; - drflac_uint16 blockSizeInPCMFrames; - drflac_uint8 channelAssignment; - drflac_uint8 bitsPerSample; - drflac_uint8 crc8; -} drflac_frame_header; + ma_uint64 pcmFrameNumber; + ma_uint32 flacFrameNumber; + ma_uint32 sampleRate; + ma_uint16 blockSizeInPCMFrames; + ma_uint8 channelAssignment; + ma_uint8 bitsPerSample; + ma_uint8 crc8; +} ma_dr_flac_frame_header; typedef struct { - drflac_frame_header header; - drflac_uint32 pcmFramesRemaining; - drflac_subframe subframes[8]; -} drflac_frame; + ma_dr_flac_frame_header header; + ma_uint32 pcmFramesRemaining; + ma_dr_flac_subframe subframes[8]; +} ma_dr_flac_frame; typedef struct { - drflac_meta_proc onMeta; + ma_dr_flac_meta_proc onMeta; void* pUserDataMD; - drflac_allocation_callbacks allocationCallbacks; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint64 totalPCMFrameCount; - drflac_container container; - drflac_uint32 seekpointCount; - drflac_frame currentFLACFrame; - drflac_uint64 currentPCMFrame; - drflac_uint64 firstFLACFramePosInBytes; - drflac__memory_stream memoryStream; - drflac_int32* pDecodedSamples; - drflac_seekpoint* pSeekpoints; + ma_allocation_callbacks allocationCallbacks; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint64 totalPCMFrameCount; + ma_dr_flac_container container; + ma_uint32 seekpointCount; + ma_dr_flac_frame currentFLACFrame; + ma_uint64 currentPCMFrame; + ma_uint64 firstFLACFramePosInBytes; + ma_dr_flac__memory_stream memoryStream; + ma_int32* pDecodedSamples; + ma_dr_flac_seekpoint* pSeekpoints; void* _oggbs; - drflac_bool32 _noSeekTableSeek : 1; - drflac_bool32 _noBinarySearchSeek : 1; - drflac_bool32 _noBruteForceSeek : 1; - drflac_bs bs; - drflac_uint8 pExtraData[1]; -} drflac; -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_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* 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_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 void drflac_close(drflac* pFlac); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); -DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + ma_bool32 _noSeekTableSeek : 1; + ma_bool32 _noBinarySearchSeek : 1; + ma_bool32 _noBruteForceSeek : 1; + ma_dr_flac_bs bs; + ma_uint8 pExtraData[1]; +} ma_dr_flac; +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -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_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 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); -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); typedef struct { - drflac_uint32 countRemaining; + ma_uint32 countRemaining; const char* pRunningData; -} drflac_vorbis_comment_iterator; -DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); -DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); +} ma_dr_flac_vorbis_comment_iterator; +MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); +MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); typedef struct { - drflac_uint32 countRemaining; + ma_uint32 countRemaining; const char* pRunningData; -} drflac_cuesheet_track_iterator; +} ma_dr_flac_cuesheet_track_iterator; typedef struct { - drflac_uint64 offset; - drflac_uint8 index; - drflac_uint8 reserved[3]; -} drflac_cuesheet_track_index; + ma_uint64 offset; + ma_uint8 index; + ma_uint8 reserved[3]; +} ma_dr_flac_cuesheet_track_index; typedef struct { - drflac_uint64 offset; - drflac_uint8 trackNumber; + ma_uint64 offset; + ma_uint8 trackNumber; char ISRC[12]; - drflac_bool8 isAudio; - drflac_bool8 preEmphasis; - drflac_uint8 indexCount; - const drflac_cuesheet_track_index* pIndexPoints; -} drflac_cuesheet_track; -DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); -DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); + ma_bool8 isAudio; + ma_bool8 preEmphasis; + ma_uint8 indexCount; + const ma_dr_flac_cuesheet_track_index* pIndexPoints; +} ma_dr_flac_cuesheet_track; +MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); +MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); #ifdef __cplusplus } #endif @@ -60552,249 +60399,109 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) /* dr_mp3_h begin */ -#ifndef dr_mp3_h -#define dr_mp3_h +#ifndef ma_dr_mp3_h +#define ma_dr_mp3_h #ifdef __cplusplus extern "C" { #endif -#define DRMP3_STRINGIFY(x) #x -#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) -#define DRMP3_VERSION_MAJOR 0 -#define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 34 -#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) +#define MA_DR_MP3_STRINGIFY(x) #x +#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) +#define MA_DR_MP3_VERSION_MAJOR 0 +#define MA_DR_MP3_VERSION_MINOR 6 +#define MA_DR_MP3_VERSION_REVISION 37 +#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) #include -typedef signed char drmp3_int8; -typedef unsigned char drmp3_uint8; -typedef signed short drmp3_int16; -typedef unsigned short drmp3_uint16; -typedef signed int drmp3_int32; -typedef unsigned int drmp3_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drmp3_int64; - typedef unsigned __int64 drmp3_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drmp3_int64; - typedef unsigned long long drmp3_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #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__) - typedef drmp3_uint64 drmp3_uintptr; -#else - typedef drmp3_uint32 drmp3_uintptr; -#endif -typedef drmp3_uint8 drmp3_bool8; -typedef drmp3_uint32 drmp3_bool32; -#define DRMP3_TRUE 1 -#define DRMP3_FALSE 0 -#if !defined(DRMP3_API) - #if defined(DRMP3_DLL) - #if defined(_WIN32) - #define DRMP3_DLL_IMPORT __declspec(dllimport) - #define DRMP3_DLL_EXPORT __declspec(dllexport) - #define DRMP3_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) - #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) - #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRMP3_DLL_IMPORT - #define DRMP3_DLL_EXPORT - #define DRMP3_DLL_PRIVATE static - #endif - #endif - #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) - #define DRMP3_API DRMP3_DLL_EXPORT - #else - #define DRMP3_API DRMP3_DLL_IMPORT - #endif - #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE - #else - #define DRMP3_API extern - #define DRMP3_PRIVATE static - #endif -#endif -typedef drmp3_int32 drmp3_result; -#define DRMP3_SUCCESS 0 -#define DRMP3_ERROR -1 -#define DRMP3_INVALID_ARGS -2 -#define DRMP3_INVALID_OPERATION -3 -#define DRMP3_OUT_OF_MEMORY -4 -#define DRMP3_OUT_OF_RANGE -5 -#define DRMP3_ACCESS_DENIED -6 -#define DRMP3_DOES_NOT_EXIST -7 -#define DRMP3_ALREADY_EXISTS -8 -#define DRMP3_TOO_MANY_OPEN_FILES -9 -#define DRMP3_INVALID_FILE -10 -#define DRMP3_TOO_BIG -11 -#define DRMP3_PATH_TOO_LONG -12 -#define DRMP3_NAME_TOO_LONG -13 -#define DRMP3_NOT_DIRECTORY -14 -#define DRMP3_IS_DIRECTORY -15 -#define DRMP3_DIRECTORY_NOT_EMPTY -16 -#define DRMP3_END_OF_FILE -17 -#define DRMP3_NO_SPACE -18 -#define DRMP3_BUSY -19 -#define DRMP3_IO_ERROR -20 -#define DRMP3_INTERRUPT -21 -#define DRMP3_UNAVAILABLE -22 -#define DRMP3_ALREADY_IN_USE -23 -#define DRMP3_BAD_ADDRESS -24 -#define DRMP3_BAD_SEEK -25 -#define DRMP3_BAD_PIPE -26 -#define DRMP3_DEADLOCK -27 -#define DRMP3_TOO_MANY_LINKS -28 -#define DRMP3_NOT_IMPLEMENTED -29 -#define DRMP3_NO_MESSAGE -30 -#define DRMP3_BAD_MESSAGE -31 -#define DRMP3_NO_DATA_AVAILABLE -32 -#define DRMP3_INVALID_DATA -33 -#define DRMP3_TIMEOUT -34 -#define DRMP3_NO_NETWORK -35 -#define DRMP3_NOT_UNIQUE -36 -#define DRMP3_NOT_SOCKET -37 -#define DRMP3_NO_ADDRESS -38 -#define DRMP3_BAD_PROTOCOL -39 -#define DRMP3_PROTOCOL_UNAVAILABLE -40 -#define DRMP3_PROTOCOL_NOT_SUPPORTED -41 -#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRMP3_SOCKET_NOT_SUPPORTED -44 -#define DRMP3_CONNECTION_RESET -45 -#define DRMP3_ALREADY_CONNECTED -46 -#define DRMP3_NOT_CONNECTED -47 -#define DRMP3_CONNECTION_REFUSED -48 -#define DRMP3_NO_HOST -49 -#define DRMP3_IN_PROGRESS -50 -#define DRMP3_CANCELLED -51 -#define DRMP3_MEMORY_ALREADY_MAPPED -52 -#define DRMP3_AT_END -53 -#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 -#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) -#ifdef _MSC_VER - #define DRMP3_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRMP3_GNUC_INLINE_HINT __inline__ - #else - #define DRMP3_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRMP3_INLINE __inline -#else - #define DRMP3_INLINE -#endif -DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); -DRMP3_API const char* drmp3_version_string(void); +#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 +#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) +MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_mp3_version_string(void); typedef struct { int frame_bytes, channels, hz, layer, bitrate_kbps; -} drmp3dec_frame_info; +} ma_dr_mp3dec_frame_info; typedef struct { float mdct_overlap[2][9*32], qmf_state[15*2*32]; int reserv, free_format_bytes; - drmp3_uint8 header[4], reserv_buf[511]; -} drmp3dec; -DRMP3_API void drmp3dec_init(drmp3dec *dec); -DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); -DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); + ma_uint8 header[4], reserv_buf[511]; +} ma_dr_mp3dec; +MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); +MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); +MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); typedef enum { - drmp3_seek_origin_start, - drmp3_seek_origin_current -} drmp3_seek_origin; + ma_dr_mp3_seek_origin_start, + ma_dr_mp3_seek_origin_current +} ma_dr_mp3_seek_origin; typedef struct { - drmp3_uint64 seekPosInBytes; - drmp3_uint64 pcmFrameIndex; - drmp3_uint16 mp3FramesToDiscard; - drmp3_uint16 pcmFramesToDiscard; -} drmp3_seek_point; -typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); + ma_uint64 seekPosInBytes; + ma_uint64 pcmFrameIndex; + ma_uint16 mp3FramesToDiscard; + ma_uint16 pcmFramesToDiscard; +} ma_dr_mp3_seek_point; +typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); typedef struct { + ma_uint32 channels; + ma_uint32 sampleRate; +} ma_dr_mp3_config; +typedef struct +{ + ma_dr_mp3dec decoder; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_dr_mp3_read_proc onRead; + ma_dr_mp3_seek_proc onSeek; void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drmp3_allocation_callbacks; -typedef struct -{ - drmp3_uint32 channels; - drmp3_uint32 sampleRate; -} drmp3_config; -typedef struct -{ - drmp3dec decoder; - drmp3_uint32 channels; - drmp3_uint32 sampleRate; - drmp3_read_proc onRead; - drmp3_seek_proc onSeek; - void* pUserData; - drmp3_allocation_callbacks allocationCallbacks; - drmp3_uint32 mp3FrameChannels; - drmp3_uint32 mp3FrameSampleRate; - drmp3_uint32 pcmFramesConsumedInMP3Frame; - drmp3_uint32 pcmFramesRemainingInMP3Frame; - drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; - drmp3_uint64 currentPCMFrame; - drmp3_uint64 streamCursor; - drmp3_seek_point* pSeekPoints; - drmp3_uint32 seekPointCount; + ma_allocation_callbacks allocationCallbacks; + ma_uint32 mp3FrameChannels; + ma_uint32 mp3FrameSampleRate; + ma_uint32 pcmFramesConsumedInMP3Frame; + ma_uint32 pcmFramesRemainingInMP3Frame; + ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; + ma_uint64 currentPCMFrame; + ma_uint64 streamCursor; + ma_dr_mp3_seek_point* pSeekPoints; + ma_uint32 seekPointCount; size_t dataSize; size_t dataCapacity; size_t dataConsumed; - drmp3_uint8* pData; - drmp3_bool32 atEnd : 1; + ma_uint8* pData; + ma_bool32 atEnd : 1; struct { - const drmp3_uint8* pData; + const ma_uint8* pData; size_t dataSize; size_t currentReadPos; } memory; -} drmp3; -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_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_MP3_NO_STDIO -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); +} ma_dr_mp3; +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_MP3_NO_STDIO +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRMP3_API void drmp3_uninit(drmp3* pMP3); -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); -DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); -DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); -DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); -DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); -DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); -DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); -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_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); -#ifndef DR_MP3_NO_STDIO -DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); +MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); +MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); +MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); +MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); +MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_MP3_NO_STDIO +MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); +MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); #ifdef __cplusplus } #endif @@ -61008,7 +60715,7 @@ static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* } -static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_decoding_backend_config backendConfig; @@ -61037,6 +60744,93 @@ static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* p return MA_SUCCESS; } +static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitFile == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitFileW == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitMemory == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) @@ -61054,8 +60848,8 @@ static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConf /* The order each backend is listed is what defines the priority. */ for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL && pVTable->onInit != NULL) { - result = ma_decoder_init_from_vtable(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); + if (pVTable != NULL) { + result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); if (result == MA_SUCCESS) { return MA_SUCCESS; } else { @@ -61074,9 +60868,96 @@ static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConf return MA_NO_BACKEND; } +static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + /* WAV */ -#ifdef dr_wav_h +#ifdef ma_dr_wav_h #define MA_HAS_WAV typedef struct @@ -61088,7 +60969,7 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32, s16 or s32. */ #if !defined(MA_NO_WAV) - drwav dr; + ma_dr_wav dr; #endif } ma_wav; @@ -61142,25 +61023,6 @@ static ma_data_source_vtable g_ma_wav_ds_vtable = #if !defined(MA_NO_WAV) -static drwav_allocation_callbacks drwav_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drwav_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_wav* pWav = (ma_wav*)pUserData; @@ -61175,7 +61037,7 @@ static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t return bytesRead; } -static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_seek_origin origin) +static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { ma_wav* pWav = (ma_wav*)pUserData; ma_result result; @@ -61184,7 +61046,7 @@ static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_ MA_ASSERT(pWav != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == drwav_seek_origin_current) { + if (origin == ma_dr_wav_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -61226,6 +61088,47 @@ static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, return MA_SUCCESS; } +static ma_result ma_wav_post_init(ma_wav* pWav) +{ + /* + If an explicit format was not specified, try picking the closest match based on the internal + format. The format needs to be supported by miniaudio. + */ + if (pWav->format == ma_format_unknown) { + switch (pWav->dr.translatedFormatTag) + { + case MA_DR_WAVE_FORMAT_PCM: + { + if (pWav->dr.bitsPerSample == 8) { + pWav->format = ma_format_u8; + } else if (pWav->dr.bitsPerSample == 16) { + pWav->format = ma_format_s16; + } else if (pWav->dr.bitsPerSample == 24) { + pWav->format = ma_format_s24; + } else if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_s32; + } + } break; + + case MA_DR_WAVE_FORMAT_IEEE_FLOAT: + { + if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_f32; + } + } break; + + default: break; + } + + /* Fall back to f32 if we couldn't find anything. */ + if (pWav->format == ma_format_unknown) { + pWav->format = ma_format_f32; + } + } + + return MA_SUCCESS; +} + MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) { ma_result result; @@ -61246,49 +61149,14 @@ MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p #if !defined(MA_NO_WAV) { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, &wavAllocationCallbacks); + wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } - /* - If an explicit format was not specified, try picking the closest match based on the internal - format. The format needs to be supported by miniaudio. - */ - if (pWav->format == ma_format_unknown) { - switch (pWav->dr.translatedFormatTag) - { - case DR_WAVE_FORMAT_PCM: - { - if (pWav->dr.bitsPerSample == 8) { - pWav->format = ma_format_u8; - } else if (pWav->dr.bitsPerSample == 16) { - pWav->format = ma_format_s16; - } else if (pWav->dr.bitsPerSample == 24) { - pWav->format = ma_format_s24; - } else if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_s32; - } - } break; - - case DR_WAVE_FORMAT_IEEE_FLOAT: - { - if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_f32; - } - } break; - - default: break; - } - - /* Fall back to f32 if we couldn't find anything. */ - if (pWav->format == ma_format_unknown) { - pWav->format = ma_format_f32; - } - } + ma_wav_post_init(pWav); return MA_SUCCESS; } @@ -61312,14 +61180,15 @@ MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backe #if !defined(MA_NO_WAV) { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_init_file(&pWav->dr, pFilePath, &wavAllocationCallbacks); + wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } + ma_wav_post_init(pWav); + return MA_SUCCESS; } #else @@ -61343,14 +61212,15 @@ MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_ #if !defined(MA_NO_WAV) { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_init_file_w(&pWav->dr, pFilePath, &wavAllocationCallbacks); + wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } + ma_wav_post_init(pWav); + return MA_SUCCESS; } #else @@ -61374,14 +61244,15 @@ MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma #if !defined(MA_NO_WAV) { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_init_memory(&pWav->dr, pData, dataSize, &wavAllocationCallbacks); + wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } + ma_wav_post_init(pWav); + return MA_SUCCESS; } #else @@ -61405,7 +61276,7 @@ MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocati #if !defined(MA_NO_WAV) { - drwav_uninit(&pWav->dr); + ma_dr_wav_uninit(&pWav->dr); } #else { @@ -61444,28 +61315,28 @@ MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint6 { case ma_format_f32: { - totalFramesRead = drwav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); + totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = drwav_read_pcm_frames_s16(&pWav->dr, frameCount, (drwav_int16*)pFramesOut); + totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); } break; case ma_format_s32: { - totalFramesRead = drwav_read_pcm_frames_s32(&pWav->dr, frameCount, (drwav_int32*)pFramesOut); + totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); } break; /* Fallback to a raw read. */ case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ default: { - totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); + totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); } break; } - /* In the future we'll update dr_wav to return MA_AT_END for us. */ + /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -61502,10 +61373,10 @@ MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) #if !defined(MA_NO_WAV) { - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex); - if (wavResult != DRWAV_TRUE) { + wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); + if (wavResult != MA_TRUE) { return MA_ERROR; } @@ -61586,9 +61457,9 @@ MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCurso #if !defined(MA_NO_WAV) { - drwav_result wavResult = drwav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); - if (wavResult != DRWAV_SUCCESS) { - return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ + ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); + if (wavResult != MA_SUCCESS) { + return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ } return MA_SUCCESS; @@ -61616,9 +61487,9 @@ MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLengt #if !defined(MA_NO_WAV) { - drwav_result wavResult = drwav_get_length_in_pcm_frames(&pWav->dr, pLength); - if (wavResult != DRWAV_SUCCESS) { - return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ + ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); + if (wavResult != MA_SUCCESS) { + return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ } return MA_SUCCESS; @@ -61750,12 +61621,27 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); } -#endif /* dr_wav_h */ + +static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_wav_h */ /* FLAC */ -#ifdef dr_flac_h +#ifdef ma_dr_flac_h #define MA_HAS_FLAC typedef struct @@ -61767,7 +61653,7 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32, s16 or s32. */ #if !defined(MA_NO_FLAC) - drflac* dr; + ma_dr_flac* dr; #endif } ma_flac; @@ -61821,25 +61707,6 @@ static ma_data_source_vtable g_ma_flac_ds_vtable = #if !defined(MA_NO_FLAC) -static drflac_allocation_callbacks drflac_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drflac_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_flac* pFlac = (ma_flac*)pUserData; @@ -61854,7 +61721,7 @@ static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_ return bytesRead; } -static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drflac_seek_origin origin) +static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { ma_flac* pFlac = (ma_flac*)pUserData; ma_result result; @@ -61863,7 +61730,7 @@ static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drfl MA_ASSERT(pFlac != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == drflac_seek_origin_current) { + if (origin == ma_dr_flac_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -61925,9 +61792,7 @@ MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_ #if !defined(MA_NO_FLAC) { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, &flacAllocationCallbacks); + pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -61954,9 +61819,7 @@ MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_back #if !defined(MA_NO_FLAC) { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_file(pFilePath, &flacAllocationCallbacks); + pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -61984,9 +61847,7 @@ MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding #if !defined(MA_NO_FLAC) { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_file_w(pFilePath, &flacAllocationCallbacks); + pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62014,9 +61875,7 @@ MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const m #if !defined(MA_NO_FLAC) { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_memory(pData, dataSize, &flacAllocationCallbacks); + pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62044,7 +61903,7 @@ MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAlloc #if !defined(MA_NO_FLAC) { - drflac_close(pFlac->dr); + ma_dr_flac_close(pFlac->dr); } #else { @@ -62083,17 +61942,17 @@ MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_ui { case ma_format_f32: { - totalFramesRead = drflac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); + totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = drflac_read_pcm_frames_s16(pFlac->dr, frameCount, (drflac_int16*)pFramesOut); + totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); } break; case ma_format_s32: { - totalFramesRead = drflac_read_pcm_frames_s32(pFlac->dr, frameCount, (drflac_int32*)pFramesOut); + totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); } break; case ma_format_u8: @@ -62105,7 +61964,7 @@ MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_ui }; } - /* In the future we'll update dr_flac to return MA_AT_END for us. */ + /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -62142,10 +62001,10 @@ MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) #if !defined(MA_NO_FLAC) { - drflac_bool32 flacResult; + ma_bool32 flacResult; - flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex); - if (flacResult != DRFLAC_TRUE) { + flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); + if (flacResult != MA_TRUE) { return MA_ERROR; } @@ -62384,12 +62243,27 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); } -#endif /* dr_flac_h */ + +static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_flac_h */ /* MP3 */ -#ifdef dr_mp3_h +#ifdef ma_dr_mp3_h #define MA_HAS_MP3 typedef struct @@ -62401,9 +62275,9 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32 or s16. */ #if !defined(MA_NO_MP3) - drmp3 dr; - drmp3_uint32 seekPointCount; - drmp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ + ma_dr_mp3 dr; + ma_uint32 seekPointCount; + ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ #endif } ma_mp3; @@ -62457,25 +62331,6 @@ static ma_data_source_vtable g_ma_mp3_ds_vtable = #if !defined(MA_NO_MP3) -static drmp3_allocation_callbacks drmp3_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drmp3_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_mp3* pMP3 = (ma_mp3*)pUserData; @@ -62490,7 +62345,7 @@ static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t return bytesRead; } -static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_seek_origin origin) +static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) { ma_mp3* pMP3 = (ma_mp3*)pUserData; ma_result result; @@ -62499,7 +62354,7 @@ static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_ MA_ASSERT(pMP3 != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == drmp3_seek_origin_current) { + if (origin == ma_dr_mp3_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -62543,28 +62398,28 @@ static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3_bool32 mp3Result; - drmp3_uint32 seekPointCount = 0; - drmp3_seek_point* pSeekPoints = NULL; + ma_bool32 mp3Result; + ma_uint32 seekPointCount = 0; + ma_dr_mp3_seek_point* pSeekPoints = NULL; MA_ASSERT(pMP3 != NULL); MA_ASSERT(pConfig != NULL); seekPointCount = pConfig->seekPointCount; if (seekPointCount > 0) { - pSeekPoints = (drmp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); + pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); if (pSeekPoints == NULL) { return MA_OUT_OF_MEMORY; } } - mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); + mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; } - mp3Result = drmp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); + mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; @@ -62576,6 +62431,18 @@ static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_back return MA_SUCCESS; } +static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + + result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) { ma_result result; @@ -62596,15 +62463,14 @@ MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p #if !defined(MA_NO_MP3) { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, &mp3AllocationCallbacks); + mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -62628,15 +62494,14 @@ MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backe #if !defined(MA_NO_MP3) { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_init_file(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); + mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -62661,15 +62526,14 @@ MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_ #if !defined(MA_NO_MP3) { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_init_file_w(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); + mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -62694,15 +62558,14 @@ MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma #if !defined(MA_NO_MP3) { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_init_memory(&pMP3->dr, pData, dataSize, &mp3AllocationCallbacks); + mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -62725,7 +62588,7 @@ MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocati #if !defined(MA_NO_MP3) { - drmp3_uninit(&pMP3->dr); + ma_dr_mp3_uninit(&pMP3->dr); } #else { @@ -62767,12 +62630,12 @@ MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint6 { case ma_format_f32: { - totalFramesRead = drmp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); + totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = drmp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (drmp3_int16*)pFramesOut); + totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); } break; case ma_format_u8: @@ -62785,7 +62648,7 @@ MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint6 }; } - /* In the future we'll update dr_mp3 to return MA_AT_END for us. */ + /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -62818,10 +62681,10 @@ MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) #if !defined(MA_NO_MP3) { - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); - if (mp3Result != DRMP3_TRUE) { + mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); + if (mp3Result != MA_TRUE) { return MA_ERROR; } @@ -62929,7 +62792,7 @@ MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLengt #if !defined(MA_NO_MP3) { - *pLength = drmp3_get_pcm_frame_count(&pMP3->dr); + *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); return MA_SUCCESS; } @@ -63060,9 +62923,24 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); } -#endif /* dr_mp3_h */ + +static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_mp3_h */ /* Vorbis */ #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H @@ -63642,8 +63520,6 @@ MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 } result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); - pVorbis->cursor += framesRead; - if (result != MA_SUCCESS) { return result; } @@ -63879,7 +63755,22 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); } #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ @@ -63946,10 +63837,6 @@ static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_see MA_ZERO_OBJECT(pDecoder); - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; - } - dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; @@ -64193,7 +64080,7 @@ static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCur return MA_SUCCESS; } -static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); if (result != MA_SUCCESS) { @@ -64214,17 +64101,121 @@ static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - ma_decoder_config config; ma_result result; + ma_decoder_config config; - config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ + config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder); + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); if (result != MA_SUCCESS) { return result; } - return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); + if (pData == NULL || dataSize == 0) { + return MA_INVALID_ARGS; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* Use trial and error for stock decoders. */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ + result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; } @@ -64691,14 +64682,305 @@ MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, c return MA_SUCCESS; } + +static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* First try loading based on the file extension so we don't waste time opening and closing files. */ + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + + /* + If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we + need only iterate over our stock decoders. + */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ + result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return MA_SUCCESS; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; } MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* First try loading based on the file extension so we don't waste time opening and closing files. */ + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + + /* + If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we + need only iterate over our stock decoders. + */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ + result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return MA_SUCCESS; + } + } + + return MA_SUCCESS; } MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) @@ -65192,42 +65474,42 @@ static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pDa return bytesWritten; } -static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin) +static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { ma_encoder* pEncoder = (ma_encoder*)pUserData; ma_result result; MA_ASSERT(pEncoder != NULL); - result = pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); + result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); if (result != MA_SUCCESS) { - return DRWAV_FALSE; + return MA_FALSE; } else { - return DRWAV_TRUE; + return MA_TRUE; } } static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) { - drwav_data_format wavFormat; - drwav_allocation_callbacks allocationCallbacks; - drwav* pWav; + ma_dr_wav_data_format wavFormat; + ma_allocation_callbacks allocationCallbacks; + ma_dr_wav* pWav; MA_ASSERT(pEncoder != NULL); - pWav = (drwav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); + pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } - wavFormat.container = drwav_container_riff; + wavFormat.container = ma_dr_wav_container_riff; wavFormat.channels = pEncoder->config.channels; wavFormat.sampleRate = pEncoder->config.sampleRate; wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; if (pEncoder->config.format == ma_format_f32) { - wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT; + wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; } else { - wavFormat.format = DR_WAVE_FORMAT_PCM; + wavFormat.format = MA_DR_WAVE_FORMAT_PCM; } allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; @@ -65235,7 +65517,7 @@ static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { + if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { return MA_ERROR; } @@ -65246,28 +65528,28 @@ static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) { - drwav* pWav; + ma_dr_wav* pWav; MA_ASSERT(pEncoder != NULL); - pWav = (drwav*)pEncoder->pInternalEncoder; + pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); ma_free(pWav, &pEncoder->config.allocationCallbacks); } static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) { - drwav* pWav; + ma_dr_wav* pWav; ma_uint64 framesWritten; MA_ASSERT(pEncoder != NULL); - pWav = (drwav*)pEncoder->pInternalEncoder; + pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); - framesWritten = drwav_write_pcm_frames(pWav, frameCount, pFramesIn); + framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); if (pFramesWritten != NULL) { *pFramesWritten = framesWritten; @@ -65645,12 +65927,12 @@ static ma_int16 ma_waveform_sine_s16(double time, double amplitude) return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); } -static float ma_waveform_square_f32(double time, double amplitude) +static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) { double f = time - (ma_int64)time; double r; - if (f < 0.5) { + if (f < dutyCycle) { r = amplitude; } else { r = -amplitude; @@ -65659,9 +65941,9 @@ static float ma_waveform_square_f32(double time, double amplitude) return (float)r; } -static ma_int16 ma_waveform_square_s16(double time, double amplitude) +static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) { - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude)); + return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); } static float ma_waveform_triangle_f32(double time, double amplitude) @@ -65736,7 +66018,7 @@ static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFra } } -static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint64 iChannel; @@ -65749,7 +66031,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); + float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -65759,7 +66041,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude); + ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -65768,7 +66050,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); + float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -65886,7 +66168,7 @@ MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFram case ma_waveform_type_square: { - ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount); + ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); } break; case ma_waveform_type_triangle: @@ -65923,6 +66205,142 @@ MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 return MA_SUCCESS; } +MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) +{ + ma_pulsewave_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.dutyCycle = dutyCycle; + config.amplitude = amplitude; + config.frequency = frequency; + + return config; +} + +MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) +{ + ma_result result; + ma_waveform_config config; + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWaveform); + + config = ma_waveform_config_init( + pConfig->format, + pConfig->channels, + pConfig->sampleRate, + ma_waveform_type_square, + pConfig->amplitude, + pConfig->frequency + ); + + result = ma_waveform_init(&config, &pWaveform->waveform); + ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); + + return result; +} + +MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) +{ + if (pWaveform == NULL) { + return; + } + + ma_waveform_uninit(&pWaveform->waveform); +} + +MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesOut != NULL) { + ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); + } else { + pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ + } + + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.amplitude = amplitude; + ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.frequency = frequency; + ma_waveform_set_frequency(&pWaveform->waveform, frequency); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.sampleRate = sampleRate; + ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.dutyCycle = dutyCycle; + + return MA_SUCCESS; +} + + MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) { @@ -66959,12 +67377,12 @@ static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_ static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) { - return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type); + return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); } static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) { - c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); + ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); } static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) @@ -66976,7 +67394,7 @@ static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_ (void)pResourceManager; - refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; + refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; if (pNewRefCount != NULL) { *pNewRefCount = refCount; @@ -66994,7 +67412,7 @@ static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_ (void)pResourceManager; - refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; + refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; if (pNewRefCount != NULL) { *pNewRefCount = refCount; @@ -67033,7 +67451,7 @@ static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_m { MA_ASSERT(pDataBufferNode != NULL); - return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ + return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ } @@ -67621,7 +68039,7 @@ static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_ma static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); - return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); + return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); } static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) @@ -68092,7 +68510,7 @@ static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manage } /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ - c89atomic_exchange_i32(&pDataBufferNode->result, result); + ma_atomic_exchange_i32(&pDataBufferNode->result, result); } else { /* Loading asynchronously. We may need to wait for initialization. */ if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { @@ -68197,7 +68615,7 @@ stage2: ma_job job; /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ - c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); + ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); @@ -68236,7 +68654,7 @@ stage2: static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) { MA_ASSERT(pDataBuffer != NULL); - return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); + return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); } static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) @@ -68269,7 +68687,7 @@ static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; MA_ASSERT(pDataBuffer != NULL); - c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping); + ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); @@ -68365,7 +68783,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); - c89atomic_exchange_i32(&pDataBuffer->result, result); + ma_atomic_exchange_i32(&pDataBuffer->result, result); ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); goto done; @@ -68383,7 +68801,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other than MA_BUSY, it'll assume an error and fall through to an early exit. */ - c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); + ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); /* Acquire fences a second time. These will be released by the async thread. */ ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); @@ -68411,7 +68829,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma if (result != MA_SUCCESS) { /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); - c89atomic_exchange_i32(&pDataBuffer->result, result); + ma_atomic_exchange_i32(&pDataBuffer->result, result); /* Release the fences after the result has been set on the data buffer. */ ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); @@ -68540,7 +68958,7 @@ MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data We need to mark the node as unavailable so we don't try reading from it anymore, but also to let the loading thread know that it needs to abort it's loading procedure. */ - c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); + ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); if (result != MA_SUCCESS) { @@ -68800,7 +69218,7 @@ MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manage return MA_INVALID_ARGS; } - return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ + return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ } MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) @@ -68953,19 +69371,19 @@ MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pRes static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1); + return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); } static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); + return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); } static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter); + return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); } @@ -68999,7 +69417,7 @@ static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; MA_ASSERT(pDataStream != NULL); - c89atomic_exchange_32(&pDataStream->isLooping, isLooping); + ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); return MA_SUCCESS; } @@ -69022,7 +69440,7 @@ static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_mana absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; } - c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); + ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); } MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) @@ -69185,7 +69603,7 @@ MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data } /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ - c89atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); + ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); /* We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need @@ -69252,11 +69670,11 @@ static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_s /* Just read straight from the decoder. It will deal with ranges and looping for us. */ result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { - c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); + ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); } - c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); - c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); + ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); + ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); } static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) @@ -69301,14 +69719,14 @@ static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_st } /* If the page we're on is invalid it means we've caught up to the job thread. */ - if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { + if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { framesAvailable = 0; } else { /* The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. */ - ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); + ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; @@ -69360,7 +69778,7 @@ static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_ pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount); + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; @@ -69376,7 +69794,7 @@ static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_ job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ - c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); /* Before posting the job we need to make sure we set some state. */ pDataStream->relativeCursor = newRelativeCursor; @@ -69479,15 +69897,15 @@ MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_m } /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ - if (c89atomic_load_32(&pDataStream->seekCounter) == 0) { - if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { + if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { + if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { return MA_SUCCESS; } } /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ - c89atomic_fetch_add_32(&pDataStream->seekCounter, 1); + ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); @@ -69499,11 +69917,11 @@ MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_m */ pDataStream->relativeCursor = 0; pDataStream->currentPageIndex = 0; - c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); - c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ - c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); /* The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages @@ -69579,7 +69997,7 @@ MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_res return MA_INVALID_OPERATION; } - *pCursor = c89atomic_load_64(&pDataStream->absoluteCursor); + *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); return MA_SUCCESS; } @@ -69625,7 +70043,7 @@ MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manage return MA_INVALID_ARGS; } - return (ma_result)c89atomic_load_i32(&pDataStream->result); + return (ma_result)ma_atomic_load_i32(&pDataStream->result); } MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) @@ -69639,7 +70057,7 @@ MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_ma return MA_FALSE; } - return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ + return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ } MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) @@ -69664,10 +70082,10 @@ MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resourc relativeCursor = pDataStream->relativeCursor; availableFrames = 0; - if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { - availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; - if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { - availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); + if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { + availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; + if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { + availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); } } @@ -69973,7 +70391,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ } @@ -70084,7 +70502,7 @@ done: immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any other error code would cause the buffer to look like it's in a state that it's not. */ - c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); /* At this point initialization is complete and we can signal the notification if any. */ if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { @@ -70105,7 +70523,7 @@ done: } /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); /* A busy result should be considered successful from the point of view of the job system. */ if (result == MA_BUSY) { @@ -70128,7 +70546,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70143,7 +70561,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); } - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return MA_SUCCESS; } @@ -70161,7 +70579,7 @@ static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70204,7 +70622,7 @@ done: } /* Make sure we set the result of node in case some error occurred. */ - c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ if (result != MA_BUSY) { @@ -70217,7 +70635,7 @@ done: } } - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return result; } @@ -70241,7 +70659,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob pResourceManager = pDataBuffer->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ } @@ -70299,7 +70717,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob done: /* Only move away from a busy code so that we don't trash any existing error codes. */ - c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); + ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); /* Only signal the other threads after the result has been set just for cleanliness sake. */ if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { @@ -70322,7 +70740,7 @@ done: } } - c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); return result; } @@ -70338,7 +70756,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob pResourceManager = pDataBuffer->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70353,7 +70771,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); } - c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); return MA_SUCCESS; } @@ -70372,7 +70790,7 @@ static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70433,7 +70851,7 @@ done: ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ - c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); + ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); /* Only signal the other threads after the result has been set just for cleanliness sake. */ if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { @@ -70443,7 +70861,7 @@ done: ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); } - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -70459,7 +70877,7 @@ static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70485,7 +70903,7 @@ static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); } - /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ + /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ return MA_SUCCESS; } @@ -70502,7 +70920,7 @@ static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70515,7 +70933,7 @@ static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); done: - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -70532,7 +70950,7 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70552,10 +70970,10 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob ma_resource_manager_data_stream_fill_pages(pDataStream); /* We need to let the public API know that we're done seeking. */ - c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1); + ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); done: - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -70654,14 +71072,14 @@ MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) { MA_ASSERT(pNodeGraph != NULL); - c89atomic_exchange_32(&pNodeGraph->isReading, isReading); + ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); } #if 0 static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) { MA_ASSERT(pNodeGraph != NULL); - return c89atomic_load_32(&pNodeGraph->isReading); + return ma_atomic_load_32(&pNodeGraph->isReading); } #endif @@ -70911,26 +71329,26 @@ static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutp static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) { if (hasRead) { - c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); } else { - c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); } } static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) { - return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; + return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; } static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) { - c89atomic_exchange_32(&pOutputBus->isAttached, isAttached); + ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); } static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) { - return c89atomic_load_32(&pOutputBus->isAttached); + return ma_atomic_load_32(&pOutputBus->isAttached); } @@ -70942,14 +71360,14 @@ static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, f volume = 0.0f; } - c89atomic_exchange_f32(&pOutputBus->volume, volume); + ma_atomic_exchange_f32(&pOutputBus->volume, volume); return MA_SUCCESS; } static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) { - return c89atomic_load_f32((float*)&pOutputBus->volume); + return ma_atomic_load_f32((float*)&pOutputBus->volume); } @@ -70986,17 +71404,17 @@ static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) { - c89atomic_fetch_add_32(&pInputBus->nextCounter, 1); + ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); } static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) { - c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1); + ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); } static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) { - return c89atomic_load_32(&pInputBus->nextCounter); + return ma_atomic_load_32(&pInputBus->nextCounter); } @@ -71031,21 +71449,21 @@ static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInp */ ma_node_input_bus_lock(pInputBus); { - ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev); - ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext); + ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); + ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); if (pOldPrev != NULL) { - c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ + ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ } if (pOldNext != NULL) { - c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ + ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ } } ma_node_input_bus_unlock(pInputBus); /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ - c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ - c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ + ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ + ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ pOutputBus->pInputNode = NULL; pOutputBus->inputNodeInputBusIndex = 0; @@ -71069,7 +71487,7 @@ static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInp } /* Part 2: Wait for any reads to complete. */ - while (c89atomic_load_32(&pOutputBus->refCount) > 0) { + while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { ma_yield(); } @@ -71100,7 +71518,7 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu ma_node_output_bus_lock(pOutputBus); { - ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode); + ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); /* Detach from any existing attachment first if necessary. */ if (pOldInputNode != NULL) { @@ -71130,18 +71548,18 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu ma_node_input_bus_lock(pInputBus); { ma_node_output_bus* pNewPrev = &pInputBus->head; - ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); + ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); /* Update the local output bus. */ - c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); - c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); + ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); + ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); /* Update the other output buses to point back to the local output bus. */ - c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ + ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ /* Do the previous pointer last. This is only used for detachment. */ if (pNewNext != NULL) { - c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); + ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); } } ma_node_input_bus_unlock(pInputBus); @@ -71169,7 +71587,7 @@ static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, { pNext = pOutputBus; for (;;) { - pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext); + pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); if (pNext == NULL) { break; /* Reached the end. */ } @@ -71184,11 +71602,11 @@ static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, /* We need to increment the reference count of the selected node. */ if (pNext != NULL) { - c89atomic_fetch_add_32(&pNext->refCount, 1); + ma_atomic_fetch_add_32(&pNext->refCount, 1); } /* The previous node is no longer being referenced. */ - c89atomic_fetch_sub_32(&pOutputBus->refCount, 1); + ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); } ma_node_input_bus_next_end(pInputBus); @@ -71800,7 +72218,7 @@ static ma_result ma_node_detach_full(ma_node* pNode) linked list logic. We don't need to worry about the audio thread referencing these because the step above severed the connection to the graph. */ - for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) { + for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext)) { ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ } } @@ -71916,7 +72334,7 @@ MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) return MA_INVALID_ARGS; } - c89atomic_exchange_i32(&pNodeBase->state, state); + ma_atomic_exchange_i32(&pNodeBase->state, state); return MA_SUCCESS; } @@ -71929,7 +72347,7 @@ MA_API ma_node_state ma_node_get_state(const ma_node* pNode) return ma_node_state_stopped; } - return (ma_node_state)c89atomic_load_i32(&pNodeBase->state); + return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); } MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) @@ -71943,7 +72361,7 @@ MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_ return MA_INVALID_ARGS; } - c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); + ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); return MA_SUCCESS; } @@ -71959,7 +72377,7 @@ MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state stat return 0; } - return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); + return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); } MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) @@ -72009,7 +72427,7 @@ MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) return 0; } - return c89atomic_load_64(&((ma_node_base*)pNode)->localTime); + return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); } MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) @@ -72018,7 +72436,7 @@ MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) return MA_INVALID_ARGS; } - c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); + ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); return MA_SUCCESS; } @@ -72393,7 +72811,7 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); /* Advance our local time forward. */ - c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); + ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ return result; @@ -73520,7 +73938,7 @@ Engine static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) { MA_ASSERT(pSound != NULL); - c89atomic_exchange_32(&pSound->atEnd, atEnd); + ma_atomic_exchange_32(&pSound->atEnd, atEnd); /* Fire any callbacks or events. */ if (atEnd) { @@ -73533,7 +73951,7 @@ static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) { MA_ASSERT(pSound != NULL); - return c89atomic_load_32(&pSound->atEnd); + return ma_atomic_load_32(&pSound->atEnd); } @@ -73559,7 +73977,7 @@ static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) MA_ASSERT(pEngineNode != NULL); - newPitch = c89atomic_load_explicit_f32(&pEngineNode->pitch, c89atomic_memory_order_acquire); + newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); if (pEngineNode->oldPitch != newPitch) { pEngineNode->oldPitch = newPitch; @@ -73582,14 +74000,14 @@ static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngin MA_ASSERT(pEngineNode != NULL); /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ - return !c89atomic_load_explicit_32(&pEngineNode->isPitchDisabled, c89atomic_memory_order_acquire); + return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); } static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) { MA_ASSERT(pEngineNode != NULL); - return !c89atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, c89atomic_memory_order_acquire); + return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); } static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) @@ -73669,6 +74087,26 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo totalFramesProcessedIn = 0; totalFramesProcessedOut = 0; + /* Update the fader if applicable. */ + { + ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); + if (fadeLengthInFrames != ~(ma_uint64)0) { + float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); + float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); + ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); + if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { + fadeStartOffsetInFrames = 0; + } else { + fadeStartOffsetInFrames -= ma_engine_get_time(pEngineNode->pEngine); + } + + ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); + + /* Reset the fade length so we don't erroneously apply it again. */ + ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); + } + } + isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); @@ -73687,10 +74125,10 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo the output buffer and then do all effects from that point directly in the output buffer in-place. - Note that we're always running the resampler. If we try to be clever and skip resampling - when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then - away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler - itself. + Note that we're always running the resampler if pitching is enabled, even when the pitch + is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch + when we move away from 1, back to 1, and then away from 1 again. We'll want to implement + any pitch=1 optimizations in the resampler itself. There's a small optimization here that we'll utilize since it might be a fairly common case. When the input and output channel counts are the same, we'll read straight into the @@ -73858,14 +74296,14 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float } /* If we're seeking, do so now before reading. */ - seekTarget = c89atomic_load_64(&pSound->seekTarget); + seekTarget = ma_atomic_load_64(&pSound->seekTarget); if (seekTarget != MA_SEEK_TARGET_NONE) { ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); /* Any time-dependant effects need to have their times updated. */ ma_node_set_time(pSound, seekTarget); - c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); + ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); } /* @@ -74188,6 +74626,10 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; + ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); + ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); + ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); + ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); @@ -74455,6 +74897,8 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng pEngine->monoExpansionMode = engineConfig.monoExpansionMode; pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; + pEngine->onProcess = engineConfig.onProcess; + pEngine->pProcessUserData = engineConfig.pProcessUserData; ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); #if !defined(MA_NO_RESOURCE_MANAGER) @@ -74481,7 +74925,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng deviceConfig.playback.format = ma_format_f32; deviceConfig.playback.channels = engineConfig.channels; deviceConfig.sampleRate = engineConfig.sampleRate; - deviceConfig.dataCallback = ma_engine_data_callback_internal; + deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; deviceConfig.pUserData = pEngine; deviceConfig.notificationCallback = engineConfig.notificationCallback; deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; @@ -74753,7 +75197,27 @@ MA_API void ma_engine_uninit(ma_engine* pEngine) MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { - return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead); + ma_result result; + ma_uint64 framesRead = 0; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + return result; + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (pEngine->onProcess) { + pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ + } + + return MA_SUCCESS; } MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) @@ -74939,13 +75403,23 @@ MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); } -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) +MA_API float ma_engine_get_volume(ma_engine* pEngine) { if (pEngine == NULL) { - return MA_INVALID_ARGS; + return 0; } - return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, ma_volume_db_to_linear(gainDB)); + return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); +} + +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) +{ + return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); +} + +MA_API float ma_engine_get_gain_db(ma_engine* pEngine) +{ + return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); } @@ -75140,7 +75614,7 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa is uninitialize it and reinitialize it. All we're doing is recycling memory. */ pSound = pNextSound; - c89atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); + ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); break; } } @@ -75211,11 +75685,11 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa result = ma_sound_start(&pSound->sound); if (result != MA_SUCCESS) { /* Failed to start the sound. We need to mark it for recycling and return an error. */ - c89atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); + ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); return result; } - c89atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); + ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); return result; } @@ -75601,7 +76075,7 @@ MA_API ma_result ma_sound_start(ma_sound* pSound) } /* Make sure we clear the end indicator. */ - c89atomic_exchange_32(&pSound->atEnd, MA_FALSE); + ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); } /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ @@ -75622,6 +76096,31 @@ MA_API ma_result ma_sound_stop(ma_sound* pSound) return MA_SUCCESS; } +MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) +{ + ma_uint64 sampleRate; + + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); +} + MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) { if (pSound == NULL) { @@ -75690,7 +76189,7 @@ MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) return; } - c89atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, c89atomic_memory_order_release); + ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); } MA_API float ma_sound_get_pitch(const ma_sound* pSound) @@ -75699,7 +76198,7 @@ MA_API float ma_sound_get_pitch(const ma_sound* pSound) return 0; } - return c89atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ + return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ } MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) @@ -75708,7 +76207,7 @@ MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enab return; } - c89atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, c89atomic_memory_order_release); + ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); } MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) @@ -75726,7 +76225,7 @@ MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 liste return; } - c89atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, c89atomic_memory_order_release); + ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); } MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) @@ -75735,7 +76234,7 @@ MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) return MA_LISTENER_INDEX_CLOSEST; } - return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire); + return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); } MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) @@ -76023,7 +76522,7 @@ MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, f return; } - ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames); + ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); } MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) @@ -76035,6 +76534,36 @@ MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); } +MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + /* + We don't want to update the fader at this point because we need to use the engine's current time + to derive the fader's start offset. The timer is being updated on the audio thread so in order to + do this as accurately as possible we'll need to defer this to the audio thread. + */ + ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); + ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); + ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); + ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_uint32 sampleRate; + + if (pSound == NULL) { + return; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); +} + MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) { if (pSound == NULL) { @@ -76068,7 +76597,7 @@ MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 abs return; } - ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames); + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); } MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) @@ -76080,6 +76609,36 @@ MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 a ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); } +MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return; + } + + if (fadeLengthInFrames > 0) { + if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { + fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; + } + + ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); + } + + ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) +{ + ma_uint32 sampleRate; + + if (pSound == NULL) { + return; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); +} + MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) { if (pSound == NULL) { @@ -76098,6 +76657,11 @@ MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) return ma_node_get_time(pSound); } +MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) +{ + return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); +} + MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) { if (pSound == NULL) { @@ -76153,7 +76717,7 @@ MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameInd } /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ - c89atomic_exchange_64(&pSound->seekTarget, frameIndex); + ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); return MA_SUCCESS; } @@ -76578,167 +77142,135 @@ MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGr Auto Generated ============== -All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as dr_wav, dr_flac, etc. If you find a bug in the +All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the code below please report the bug to the respective repository for the relevant project (probably dr_libs). *************************************************************************************************************************************************************** **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_wav_c begin */ -#ifndef dr_wav_c -#define dr_wav_c +#ifndef ma_dr_wav_c +#define ma_dr_wav_c #ifdef __MRC__ #pragma options opt off #endif #include #include #include -#ifndef DR_WAV_NO_STDIO +#ifndef MA_DR_WAV_NO_STDIO #include -#ifndef DR_WAV_NO_WCHAR +#ifndef MA_DR_WAV_NO_WCHAR #include #endif #endif -#ifndef DRWAV_ASSERT +#ifndef MA_DR_WAV_ASSERT #include -#define DRWAV_ASSERT(expression) assert(expression) +#define MA_DR_WAV_ASSERT(expression) assert(expression) #endif -#ifndef DRWAV_MALLOC -#define DRWAV_MALLOC(sz) malloc((sz)) +#ifndef MA_DR_WAV_MALLOC +#define MA_DR_WAV_MALLOC(sz) malloc((sz)) #endif -#ifndef DRWAV_REALLOC -#define DRWAV_REALLOC(p, sz) realloc((p), (sz)) +#ifndef MA_DR_WAV_REALLOC +#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) #endif -#ifndef DRWAV_FREE -#define DRWAV_FREE(p) free((p)) +#ifndef MA_DR_WAV_FREE +#define MA_DR_WAV_FREE(p) free((p)) #endif -#ifndef DRWAV_COPY_MEMORY -#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#ifndef MA_DR_WAV_COPY_MEMORY +#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#ifndef DRWAV_ZERO_MEMORY -#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#ifndef MA_DR_WAV_ZERO_MEMORY +#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#ifndef DRWAV_ZERO_OBJECT -#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p)) -#endif -#define drwav_countof(x) (sizeof(x) / sizeof(x[0])) -#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) -#define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset)) -#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 -#if defined(__x86_64__) || defined(_M_X64) - #define DRWAV_X64 -#elif defined(__i386) || defined(_M_IX86) - #define DRWAV_X86 -#elif defined(__arm__) || defined(_M_ARM) - #define DRWAV_ARM -#endif -#ifdef _MSC_VER - #define DRWAV_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRWAV_GNUC_INLINE_HINT __inline__ - #else - #define DRWAV_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRWAV_INLINE __inline -#else - #define DRWAV_INLINE -#endif -#if defined(SIZE_MAX) - #define DRWAV_SIZE_MAX SIZE_MAX -#else - #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) - #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRWAV_SIZE_MAX 0xFFFFFFFF - #endif +#ifndef MA_DR_WAV_ZERO_OBJECT +#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) #endif +#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) +#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) +#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) +#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) +#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) +#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 +#define MA_DR_WAV_INT64_MIN ((ma_int64)0x80000000 << 32) +#define MA_DR_WAV_INT64_MAX ((((ma_int64)0x7FFFFFFF) << 32) | 0xFFFFFFFF) #if defined(_MSC_VER) && _MSC_VER >= 1400 - #define DRWAV_HAS_BYTESWAP16_INTRINSIC - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) - #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) - #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) - #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #endif #endif -DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision) +MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) { if (pMajor) { - *pMajor = DRWAV_VERSION_MAJOR; + *pMajor = MA_DR_WAV_VERSION_MAJOR; } if (pMinor) { - *pMinor = DRWAV_VERSION_MINOR; + *pMinor = MA_DR_WAV_VERSION_MINOR; } if (pRevision) { - *pRevision = DRWAV_VERSION_REVISION; + *pRevision = MA_DR_WAV_VERSION_REVISION; } } -DRWAV_API const char* drwav_version_string(void) +MA_API const char* ma_dr_wav_version_string(void) { - return DRWAV_VERSION_STRING; + return MA_DR_WAV_VERSION_STRING; } -#ifndef DRWAV_MAX_SAMPLE_RATE -#define DRWAV_MAX_SAMPLE_RATE 384000 +#ifndef MA_DR_WAV_MAX_SAMPLE_RATE +#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 #endif -#ifndef DRWAV_MAX_CHANNELS -#define DRWAV_MAX_CHANNELS 256 +#ifndef MA_DR_WAV_MAX_CHANNELS +#define MA_DR_WAV_MAX_CHANNELS 256 #endif -#ifndef DRWAV_MAX_BITS_PER_SAMPLE -#define DRWAV_MAX_BITS_PER_SAMPLE 64 +#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE +#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 #endif -static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; -static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static DRWAV_INLINE int drwav__is_little_endian(void) +static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; +static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static MA_INLINE int ma_dr_wav__is_little_endian(void) { -#if defined(DRWAV_X86) || defined(DRWAV_X64) - return DRWAV_TRUE; +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return DRWAV_TRUE; + return MA_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } -static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid) +static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) { int i; for (i = 0; i < 16; ++i) { guid[i] = data[i]; } } -static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) +static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) { -#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC +#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) @@ -76751,16 +77283,16 @@ static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) ((n & 0x00FF) << 8); #endif } -static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) +static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) { -#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC +#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) - #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) - drwav_uint32 r; + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) + ma_uint32 r; __asm__ __volatile__ ( - #if defined(DRWAV_64BIT) + #if defined(MA_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) @@ -76780,9 +77312,9 @@ static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) ((n & 0x000000FF) << 24); #endif } -static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) +static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) { -#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC +#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) @@ -76791,88 +77323,82 @@ static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) #error "This compiler does not support the byte swap intrinsic." #endif #else - return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) | - ((n & ((drwav_uint64)0xFF000000 )) << 8) | - ((n & ((drwav_uint64)0x00FF0000 )) << 24) | - ((n & ((drwav_uint64)0x0000FF00 )) << 40) | - ((n & ((drwav_uint64)0x000000FF )) << 56); + return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | + ((n & ((ma_uint64)0xFF000000 )) << 8) | + ((n & ((ma_uint64)0x00FF0000 )) << 24) | + ((n & ((ma_uint64)0x0000FF00 )) << 40) | + ((n & ((ma_uint64)0x000000FF )) << 56); #endif } -static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) +static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) { - return (drwav_int16)drwav__bswap16((drwav_uint16)n); + return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); } -static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) +static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) { - drwav_uint64 iSample; + ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); + pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); } } -static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) +static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) { - drwav_uint8 t; + ma_uint8 t; t = p[0]; p[0] = p[2]; p[2] = t; } -static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) +static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) { - drwav_uint64 iSample; + ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - drwav_uint8* pSample = pSamples + (iSample*3); - drwav__bswap_s24(pSample); + ma_uint8* pSample = pSamples + (iSample*3); + ma_dr_wav__bswap_s24(pSample); } } -static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) +static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) { - return (drwav_int32)drwav__bswap32((drwav_uint32)n); + return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); } -static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) +static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) { - drwav_uint64 iSample; + ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); + pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); } } -static DRWAV_INLINE float drwav__bswap_f32(float n) +static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) +{ + return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); +} +static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); + } +} +static MA_INLINE float ma_dr_wav__bswap_f32(float n) { union { - drwav_uint32 i; + ma_uint32 i; float f; } x; x.f = n; - x.i = drwav__bswap32(x.i); + x.i = ma_dr_wav__bswap32(x.i); return x.f; } -static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) +static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) { - drwav_uint64 iSample; + ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); + pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); } } -static DRWAV_INLINE double drwav__bswap_f64(double n) -{ - union { - drwav_uint64 i; - double f; - } x; - x.f = n; - x.i = drwav__bswap64(x.i); - return x.f; -} -static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]); - } -} -static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) +static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) { switch (bytesPerSample) { @@ -76881,87 +77407,108 @@ static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 s } break; case 2: { - drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); + ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); } break; case 3: { - drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); + ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); } break; case 4: { - drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); - } break; - default: - { - DRWAV_ASSERT(DRWAV_FALSE); - } break; - } -} -static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - #if 0 - case 2: - { - drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount); - } break; - #endif - case 4: - { - drwav__bswap_samples_f32((float*)pSamples, sampleCount); + ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); } break; case 8: { - drwav__bswap_samples_f64((double*)pSamples, sampleCount); + ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); } break; default: { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); } break; } } -static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format) +MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) { - switch (format) - { - case DR_WAVE_FORMAT_PCM: - { - drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample); - } break; - case DR_WAVE_FORMAT_IEEE_FLOAT: - { - drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample); - } break; - case DR_WAVE_FORMAT_ALAW: - case DR_WAVE_FORMAT_MULAW: - { - drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); - } break; - case DR_WAVE_FORMAT_ADPCM: - case DR_WAVE_FORMAT_DVI_ADPCM: - default: - { - DRWAV_ASSERT(DRWAV_FALSE); - } break; + if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { + return MA_TRUE; + } else { + return MA_FALSE; } } -DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData) +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) +{ + return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) +{ + return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) +{ + if (ma_dr_wav_is_container_be(container)) { + return ma_dr_wav_bytes_to_u16_be(data); + } else { + return ma_dr_wav_bytes_to_u16_le(data); + } +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) +{ + return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) +{ + return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) +{ + if (ma_dr_wav_is_container_be(container)) { + return ma_dr_wav_bytes_to_u32_be(data); + } else { + return ma_dr_wav_bytes_to_u32_le(data); + } +} +MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) +{ + ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; + ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); + ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); + ma_uint64 significand = (hi << 32) | lo; + int sign = exponent >> 15; + exponent &= 0x7FFF; + if (exponent == 0 && significand == 0) { + return 0; + } else if (exponent == 0x7FFF) { + return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; + } + exponent -= 16383; + if (exponent > 63) { + return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; + } else if (exponent < 1) { + return 0; + } + significand >>= (63 - exponent); + if (sign) { + return -(ma_int64)significand; + } else { + return (ma_int64)significand; + } +} +MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return DRWAV_MALLOC(sz); + return MA_DR_WAV_MALLOC(sz); } -DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData) +MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return DRWAV_REALLOC(p, sz); + return MA_DR_WAV_REALLOC(p, sz); } -DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData) +MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) { (void)pUserData; - DRWAV_FREE(p); + MA_DR_WAV_FREE(p); } -DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -76974,7 +77521,7 @@ DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocati } return NULL; } -DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -76989,14 +77536,14 @@ DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t return NULL; } if (p != NULL) { - DRWAV_COPY_MEMORY(p2, p, szOld); + MA_DR_WAV_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -77005,369 +77552,288 @@ DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_ca pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return *pAllocationCallbacks; } else { - drwav_allocation_callbacks allocationCallbacks; + ma_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drwav__malloc_default; - allocationCallbacks.onRealloc = drwav__realloc_default; - allocationCallbacks.onFree = drwav__free_default; + allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; + allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; + allocationCallbacks.onFree = ma_dr_wav__free_default; return allocationCallbacks; } } -static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) +static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) { return - formatTag == DR_WAVE_FORMAT_ADPCM || - formatTag == DR_WAVE_FORMAT_DVI_ADPCM; + formatTag == MA_DR_WAVE_FORMAT_ADPCM || + formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; } -DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) +MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) { return (unsigned int)(chunkSize % 2); } -DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) +MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) { return (unsigned int)(chunkSize % 8); } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); -DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); -DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); +MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); +MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) { - if (container == drwav_container_riff || container == drwav_container_rf64) { - drwav_uint8 sizeInBytes[4]; + if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { + ma_uint8 sizeInBytes[4]; if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return DRWAV_AT_END; + return MA_AT_END; } if (onRead(pUserData, sizeInBytes, 4) != 4) { - return DRWAV_INVALID_FILE; + return MA_INVALID_FILE; } - pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes); - pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); + pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); + pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); *pRunningBytesReadOut += 8; - } else { - drwav_uint8 sizeInBytes[8]; + } else if (container == ma_dr_wav_container_w64) { + ma_uint8 sizeInBytes[8]; if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return DRWAV_AT_END; + return MA_AT_END; } if (onRead(pUserData, sizeInBytes, 8) != 8) { - return DRWAV_INVALID_FILE; + return MA_INVALID_FILE; } - pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; - pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); + pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; + pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); *pRunningBytesReadOut += 24; + } else { + return MA_INVALID_FILE; } - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) { - drwav_uint64 bytesRemainingToSeek = offset; + ma_uint64 bytesRemainingToSeek = offset; while (bytesRemainingToSeek > 0) { if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { - return DRWAV_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; } bytesRemainingToSeek -= 0x7FFFFFFF; } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { - return DRWAV_FALSE; + if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; } bytesRemainingToSeek = 0; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_start); + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); } - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { - return DRWAV_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { + return MA_FALSE; } offset -= 0x7FFFFFFF; for (;;) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_current); + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); } - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { - return DRWAV_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; } offset -= 0x7FFFFFFF; } } -DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) -{ - drwav_chunk_header header; - drwav_uint8 fmt[16]; - if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav_fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) { - if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; - if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - } - if (container == drwav_container_riff || container == drwav_container_rf64) { - if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) { - return DRWAV_FALSE; - } - } else { - if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) { - return DRWAV_FALSE; - } - } - if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += sizeof(fmt); - fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0); - fmtOut->channels = drwav_bytes_to_u16(fmt + 2); - fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4); - fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8); - fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12); - fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14); - fmtOut->extendedSize = 0; - fmtOut->validBitsPerSample = 0; - fmtOut->channelMask = 0; - DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat)); - if (header.sizeInBytes > 16) { - drwav_uint8 fmt_cbSize[2]; - int bytesReadSoFar = 0; - if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += sizeof(fmt_cbSize); - bytesReadSoFar = 18; - fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize); - if (fmtOut->extendedSize > 0) { - if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmtOut->extendedSize != 22) { - return DRWAV_FALSE; - } - } - if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - drwav_uint8 fmtext[22]; - if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { - return DRWAV_FALSE; - } - fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0); - fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2); - drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat); - } else { - if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - } - *pRunningBytesReadOut += fmtOut->extendedSize; - bytesReadSoFar += fmtOut->extendedSize; - } - if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); - } - if (header.paddingSize > 0) { - if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += header.paddingSize; - } - return DRWAV_TRUE; -} -DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) +MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) { size_t bytesRead; - DRWAV_ASSERT(onRead != NULL); - DRWAV_ASSERT(pCursor != NULL); + MA_DR_WAV_ASSERT(onRead != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); bytesRead = onRead(pUserData, pBufferOut, bytesToRead); *pCursor += bytesRead; return bytesRead; } #if 0 -DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) { - DRWAV_ASSERT(onSeek != NULL); - DRWAV_ASSERT(pCursor != NULL); + MA_DR_WAV_ASSERT(onSeek != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); if (!onSeek(pUserData, offset, origin)) { - return DRWAV_FALSE; + return MA_FALSE; } - if (origin == drwav_seek_origin_start) { + if (origin == ma_dr_wav_seek_origin_start) { *pCursor = offset; } else { *pCursor += offset; } - return DRWAV_TRUE; + return MA_TRUE; } #endif -#define DRWAV_SMPL_BYTES 36 -#define DRWAV_SMPL_LOOP_BYTES 24 -#define DRWAV_INST_BYTES 7 -#define DRWAV_ACID_BYTES 24 -#define DRWAV_CUE_BYTES 4 -#define DRWAV_BEXT_BYTES 602 -#define DRWAV_BEXT_DESCRIPTION_BYTES 256 -#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32 -#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32 -#define DRWAV_BEXT_RESERVED_BYTES 180 -#define DRWAV_BEXT_UMID_BYTES 64 -#define DRWAV_CUE_POINT_BYTES 24 -#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4 -#define DRWAV_LIST_LABELLED_TEXT_BYTES 20 -#define DRWAV_METADATA_ALIGNMENT 8 +#define MA_DR_WAV_SMPL_BYTES 36 +#define MA_DR_WAV_SMPL_LOOP_BYTES 24 +#define MA_DR_WAV_INST_BYTES 7 +#define MA_DR_WAV_ACID_BYTES 24 +#define MA_DR_WAV_CUE_BYTES 4 +#define MA_DR_WAV_BEXT_BYTES 602 +#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 +#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 +#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 +#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 +#define MA_DR_WAV_BEXT_UMID_BYTES 64 +#define MA_DR_WAV_CUE_POINT_BYTES 24 +#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 +#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 +#define MA_DR_WAV_METADATA_ALIGNMENT 8 typedef enum { - drwav__metadata_parser_stage_count, - drwav__metadata_parser_stage_read -} drwav__metadata_parser_stage; + ma_dr_wav__metadata_parser_stage_count, + ma_dr_wav__metadata_parser_stage_read +} ma_dr_wav__metadata_parser_stage; typedef struct { - drwav_read_proc onRead; - drwav_seek_proc onSeek; + ma_dr_wav_read_proc onRead; + ma_dr_wav_seek_proc onSeek; void *pReadSeekUserData; - drwav__metadata_parser_stage stage; - drwav_metadata *pMetadata; - drwav_uint32 metadataCount; - drwav_uint8 *pData; - drwav_uint8 *pDataCursor; - drwav_uint64 metadataCursor; - drwav_uint64 extraCapacity; -} drwav__metadata_parser; -DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser) + ma_dr_wav__metadata_parser_stage stage; + ma_dr_wav_metadata *pMetadata; + ma_uint32 metadataCount; + ma_uint8 *pData; + ma_uint8 *pDataCursor; + ma_uint64 metadataCursor; + ma_uint64 extraCapacity; +} ma_dr_wav__metadata_parser; +MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) { - drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity; - if (cap > DRWAV_SIZE_MAX) { + ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; + if (cap > MA_SIZE_MAX) { return 0; } return (size_t)cap; } -DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align) +MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) { - drwav_uint8* pResult; + ma_uint8* pResult; if (align) { - drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align; + ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; if (modulo != 0) { pParser->pDataCursor += align - modulo; } } pResult = pParser->pDataCursor; - DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser))); + MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); pParser->pDataCursor += size; return pResult; } -DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align) +MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) { size_t extra = bytes + (align ? (align - 1) : 0); pParser->extraCapacity += extra; } -DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) { if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); - pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); + pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); pParser->pDataCursor = pParser->pData; if (pParser->pData == NULL) { - return DRWAV_OUT_OF_MEMORY; + return MA_OUT_OF_MEMORY; } - pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1); + pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); pParser->metadataCursor = 0; } - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) +MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) { if (pCursor != NULL) { - return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); + return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); } else { return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); } } -DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) { - drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; - drwav_uint64 totalBytesRead = 0; + ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; + ma_uint64 totalBytesRead = 0; size_t bytesJustRead; if (pMetadata == NULL) { return 0; } - bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - DRWAV_ASSERT(pChunkHeader != NULL); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + MA_DR_WAV_ASSERT(pChunkHeader != NULL); if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { - drwav_uint32 iSampleLoop; - pMetadata->type = drwav_metadata_type_smpl; - pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); - pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4); - pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8); - pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12); - pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16); - pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20); - pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24); - pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28); - pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32); - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { - pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); + ma_uint32 iSampleLoop; + pMetadata->type = ma_dr_wav_metadata_type_smpl; + pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); + pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); + pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); + pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); + pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); + pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); + pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); + pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); + pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); + if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { + pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; - bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); + ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; + bytesJustRead = ma_dr_wav__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 = ma_dr_wav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); } else { break; } } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); + pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); + MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); } } } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) { - drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; - drwav_uint64 totalBytesRead = 0; + ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; + ma_uint64 totalBytesRead = 0; size_t bytesJustRead; if (pMetadata == NULL) { return 0; } - bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueHeaderSectionData)) { - pMetadata->type = drwav_metadata_type_cue; - pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData); - if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) { - pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); - DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); + pMetadata->type = ma_dr_wav_metadata_type_cue; + pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); + if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { + pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); + MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); if (pMetadata->data.cue.cuePointCount > 0) { - drwav_uint32 iCuePoint; + ma_uint32 iCuePoint; for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; - bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); + ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); + pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); + pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; 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].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); + pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); } else { break; } @@ -77377,50 +77843,50 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) { - drwav_uint8 instData[DRWAV_INST_BYTES]; - drwav_uint64 bytesRead; + ma_uint8 instData[MA_DR_WAV_INST_BYTES]; + ma_uint64 bytesRead; if (pMetadata == NULL) { return 0; } - bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesRead == sizeof(instData)) { - pMetadata->type = drwav_metadata_type_inst; - pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0]; - pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1]; - pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2]; - pMetadata->data.inst.lowNote = (drwav_int8)instData[3]; - pMetadata->data.inst.highNote = (drwav_int8)instData[4]; - pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5]; - pMetadata->data.inst.highVelocity = (drwav_int8)instData[6]; + pMetadata->type = ma_dr_wav_metadata_type_inst; + pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; + pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; + pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; + pMetadata->data.inst.lowNote = (ma_int8)instData[3]; + pMetadata->data.inst.highNote = (ma_int8)instData[4]; + pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; + pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; } return bytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) { - drwav_uint8 acidData[DRWAV_ACID_BYTES]; - drwav_uint64 bytesRead; + ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; + ma_uint64 bytesRead; if (pMetadata == NULL) { return 0; } - bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesRead == sizeof(acidData)) { - pMetadata->type = drwav_metadata_type_acid; - pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0); - pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4); - pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6); - pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8); - pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12); - pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16); - pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18); - pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20); + pMetadata->type = ma_dr_wav_metadata_type_acid; + pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); + pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); + pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); + pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); + pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); + pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); + pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); + pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); } return bytesRead; } -DRWAV_PRIVATE size_t drwav__strlen(const char* str) +MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) { size_t result = 0; while (*str++) { @@ -77428,7 +77894,7 @@ DRWAV_PRIVATE size_t drwav__strlen(const char* str) } return result; } -DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) +MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) { size_t result = 0; while (*str++ && result < maxToRead) { @@ -77436,13 +77902,13 @@ DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) } return result; } -DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead) +MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) { - size_t len = drwav__strlen_clamped(str, maxToRead); + size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); if (len) { - char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); - DRWAV_ASSERT(result != NULL); - DRWAV_COPY_MEMORY(result, str, len); + char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); + MA_DR_WAV_ASSERT(result != NULL); + MA_DR_WAV_COPY_MEMORY(result, str, len); result[len] = '\0'; return result; } else { @@ -77454,36 +77920,36 @@ typedef struct const void* pBuffer; size_t sizeInBytes; size_t cursor; -} drwav_buffer_reader; -DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader) +} ma_dr_wav_buffer_reader; +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) { - DRWAV_ASSERT(pBuffer != NULL); - DRWAV_ASSERT(pReader != NULL); - DRWAV_ZERO_OBJECT(pReader); + MA_DR_WAV_ASSERT(pBuffer != NULL); + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ZERO_OBJECT(pReader); pReader->pBuffer = pBuffer; pReader->sizeInBytes = sizeInBytes; pReader->cursor = 0; - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader) +MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) { - DRWAV_ASSERT(pReader != NULL); - return drwav_offset_ptr(pReader->pBuffer, pReader->cursor); + MA_DR_WAV_ASSERT(pReader != NULL); + return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); } -DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek) +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) { - DRWAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pReader != NULL); if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { - return DRWAV_BAD_SEEK; + return MA_BAD_SEEK; } pReader->cursor += bytesToSeek; - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) { - drwav_result result = DRWAV_SUCCESS; + ma_result result = MA_SUCCESS; size_t bytesRemaining; - DRWAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pReader != NULL); if (pBytesRead != NULL) { *pBytesRead = 0; } @@ -77492,87 +77958,87 @@ DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader bytesToRead = bytesRemaining; } if (pDst == NULL) { - result = drwav_buffer_reader_seek(pReader, bytesToRead); + result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); } else { - DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead); + MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); pReader->cursor += bytesToRead; } - DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); - if (result == DRWAV_SUCCESS) { + MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); + if (result == MA_SUCCESS) { if (pBytesRead != NULL) { *pBytesRead = bytesToRead; } } - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst) +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) { - drwav_result result; + ma_result result; size_t bytesRead; - drwav_uint8 data[2]; - DRWAV_ASSERT(pReader != NULL); - DRWAV_ASSERT(pDst != NULL); + ma_uint8 data[2]; + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pDst != NULL); *pDst = 0; - result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { + result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { return result; } - *pDst = drwav_bytes_to_u16(data); - return DRWAV_SUCCESS; + *pDst = ma_dr_wav_bytes_to_u16(data); + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst) +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) { - drwav_result result; + ma_result result; size_t bytesRead; - drwav_uint8 data[4]; - DRWAV_ASSERT(pReader != NULL); - DRWAV_ASSERT(pDst != NULL); + ma_uint8 data[4]; + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pDst != NULL); *pDst = 0; - result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { + result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { return result; } - *pDst = drwav_bytes_to_u32(data); - return DRWAV_SUCCESS; + *pDst = ma_dr_wav_bytes_to_u32(data); + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) { - drwav_uint8 bextData[DRWAV_BEXT_BYTES]; - size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; + size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesRead == sizeof(bextData)) { - drwav_buffer_reader reader; - drwav_uint32 timeReferenceLow; - drwav_uint32 timeReferenceHigh; + ma_dr_wav_buffer_reader reader; + ma_uint32 timeReferenceLow; + ma_uint32 timeReferenceHigh; size_t extraBytes; - pMetadata->type = drwav_metadata_type_bext; - if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) { - pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES); - pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); - drwav_buffer_reader_read_u32(&reader, &timeReferenceLow); - drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh); - pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); - pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); - DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); + pMetadata->type = ma_dr_wav_metadata_type_bext; + if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { + pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); + ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); + ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); + pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); + pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); + MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); + extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); - DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory); + pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); + MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); + bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); + pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); } else { pMetadata->data.bext.pCodingHistory = NULL; pMetadata->data.bext.codingHistorySize = 0; @@ -77581,22 +78047,22 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_pars } return bytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type) +MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) { - drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES]; - 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); + ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueIDBuffer)) { - drwav_uint32 sizeIncludingNullTerminator; + ma_uint32 sizeIncludingNullTerminator; pMetadata->type = type; - pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer); - sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; + pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); + sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; if (sizeIncludingNullTerminator > 0) { pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); + pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelOrNote.stringLength = 0; pMetadata->data.labelOrNote.pString = NULL; @@ -77604,31 +78070,31 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav_ } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) { - drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES]; - drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 sizeIncludingNullTerminator; - pMetadata->type = drwav_metadata_type_list_labelled_cue_region; - pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0); - pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4); + ma_uint32 sizeIncludingNullTerminator; + pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; + pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); + pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; - pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12); - pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14); - pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16); - pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18); - sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES; + pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); + pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); + pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); + pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); + sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; if (sizeIncludingNullTerminator > 0) { pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); + pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelledCueRegion.stringLength = 0; pMetadata->data.labelledCueRegion.pString = NULL; @@ -77636,21 +78102,21 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj( } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type) +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) { - drwav_uint64 bytesRead = 0; - drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize; - if (pParser->stage == drwav__metadata_parser_stage_count) { + ma_uint64 bytesRead = 0; + ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); } else { - drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; pMetadata->type = type; if (stringSizeWithNullTerminator > 0) { pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; - pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL); - bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); + pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); if (bytesRead == chunkSize) { pParser->metadataCursor += 1; } else { @@ -77663,30 +78129,30 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metada } return bytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location) +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) { - drwav_uint64 bytesRead = 0; - if (location == drwav_metadata_location_invalid) { + ma_uint64 bytesRead = 0; + if (location == ma_dr_wav_metadata_location_invalid) { return 0; } - if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) { + if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { return 0; } - if (pParser->stage == drwav__metadata_parser_stage_count) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); } else { - drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = drwav_metadata_type_unknown; + ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = ma_dr_wav_metadata_type_unknown; pMetadata->data.unknown.chunkLocation = location; pMetadata->data.unknown.id[0] = pChunkId[0]; pMetadata->data.unknown.id[1] = pChunkId[1]; pMetadata->data.unknown.id[2] = pChunkId[2]; pMetadata->data.unknown.id[3] = pChunkId[3]; - pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize; - pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1); - DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); + pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; + pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); + MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77694,41 +78160,41 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata } return bytesRead; } -DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) +MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) { - return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID); + return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); } -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes) +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) { - const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc; - drwav_uint64 bytesRead = 0; - if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) { - if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - drwav_uint8 buffer[4]; + const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; + ma_uint64 bytesRead = 0; + if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + ma_uint8 buffer[4]; size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { return bytesRead; } bytesRead += 28; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 loopCount = drwav_bytes_to_u32(buffer); - drwav_uint64 calculatedLoopCount; - calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES; + ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); + ma_uint64 calculatedLoopCount; + calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; if (calculatedLoopCount == loopCount) { - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); + ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); - drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); } } else { } } } else { - bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77736,12 +78202,12 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) { - if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { + if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; } else { - bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77749,12 +78215,12 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) { - if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { + if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; } else { - bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77762,15 +78228,15 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) { - if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { size_t cueCount; pParser->metadataCount += 1; - cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES; - drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT); + cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); } else { - bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77778,35 +78244,35 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) { - if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1]; - size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; + size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; size_t bytesJustRead; - buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { + buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { return bytesRead; } - allocSizeNeeded += drwav__strlen(buffer) + 1; - buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { return bytesRead; } - allocSizeNeeded += drwav__strlen(buffer) + 1; - buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { return bytesRead; } - allocSizeNeeded += drwav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; - drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); pParser->metadataCount += 1; } else { - bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); + bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77814,37 +78280,37 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) { - drwav_metadata_location listType = drwav_metadata_location_invalid; + } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { + ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; while (bytesRead < pChunkHeader->sizeInBytes) { - drwav_uint8 subchunkId[4]; - drwav_uint8 subchunkSizeBuffer[4]; - drwav_uint64 subchunkDataSize; - drwav_uint64 subchunkBytesRead = 0; - drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); + ma_uint8 subchunkId[4]; + ma_uint8 subchunkSizeBuffer[4]; + ma_uint64 subchunkDataSize; + ma_uint64 subchunkBytesRead = 0; + ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); if (bytesJustRead != sizeof(subchunkId)) { break; } - if (drwav_fourcc_equal(subchunkId, "adtl")) { - listType = drwav_metadata_location_inside_adtl_list; + if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { + listType = ma_dr_wav_metadata_location_inside_adtl_list; continue; - } else if (drwav_fourcc_equal(subchunkId, "INFO")) { - listType = drwav_metadata_location_inside_info_list; + } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { + listType = ma_dr_wav_metadata_location_inside_info_list; continue; } - bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); if (bytesJustRead != sizeof(subchunkSizeBuffer)) { break; } - subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer); - if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) { - if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) { - drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; - if (pParser->stage == drwav__metadata_parser_stage_count) { + subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); + if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { + if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { + ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); } else { - subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note); + subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); if (subchunkBytesRead == subchunkDataSize) { pParser->metadataCursor += 1; } else { @@ -77852,14 +78318,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) { - if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) { - drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES; - if (pParser->stage == drwav__metadata_parser_stage_count) { + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { + if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { + ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); } else { - subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); + subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); if (subchunkBytesRead == subchunkDataSize) { pParser->metadataCursor += 1; } else { @@ -77867,332 +78333,542 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) { - 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 ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { - subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); + } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { + subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); } bytesRead += subchunkBytesRead; - DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize); + MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); if (subchunkBytesRead < subchunkDataSize) { - drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) { + ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { break; } bytesRead += bytesToSeek; } if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { break; } bytesRead += 1; } } - } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { - bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level); + } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { + bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); } return bytesRead; } -DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) +MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) { - drwav_uint32 bytesPerFrame; + ma_uint32 bytesPerFrame; if ((pWav->bitsPerSample & 0x7) == 0) { bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; } else { bytesPerFrame = pWav->fmt.blockAlign; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { if (bytesPerFrame != pWav->fmt.channels) { return 0; } } return bytesPerFrame; } -DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) +MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) { if (pFMT == NULL) { return 0; } - if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) { + if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { return pFMT->formatTag; } else { - return drwav_bytes_to_u16(pFMT->subFormat); + return ma_dr_wav_bytes_to_u16(pFMT->subFormat); } } -DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onRead = onRead; pWav->onSeek = onSeek; pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return DRWAV_FALSE; + return MA_FALSE; } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) +MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) { - drwav_uint64 cursor; - drwav_bool32 sequential; - drwav_uint8 riff[4]; - drwav_fmt fmt; + ma_result result; + ma_uint64 cursor; + ma_bool32 sequential; + ma_uint8 riff[4]; + ma_dr_wav_fmt fmt; unsigned short translatedFormatTag; - drwav_bool32 foundDataChunk; - drwav_uint64 dataChunkSize = 0; - drwav_uint64 sampleCountFromFactChunk = 0; - drwav_uint64 chunkSize; - drwav__metadata_parser metadataParser; + ma_uint64 dataChunkSize = 0; + ma_uint64 sampleCountFromFactChunk = 0; + ma_uint64 metadataStartPos; + ma_dr_wav__metadata_parser metadataParser; + ma_bool8 isProcessingMetadata = MA_FALSE; + ma_bool8 foundChunk_fmt = MA_FALSE; + ma_bool8 foundChunk_data = MA_FALSE; + ma_bool8 isAIFCFormType = MA_FALSE; + ma_uint64 aiffFrameCount = 0; cursor = 0; - sequential = (flags & DRWAV_SEQUENTIAL) != 0; - if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return DRWAV_FALSE; + sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; + MA_DR_WAV_ZERO_OBJECT(&fmt); + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { + return MA_FALSE; } - if (drwav_fourcc_equal(riff, "RIFF")) { - pWav->container = drwav_container_riff; - } else if (drwav_fourcc_equal(riff, "riff")) { + if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { + pWav->container = ma_dr_wav_container_riff; + } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { + pWav->container = ma_dr_wav_container_rifx; + } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { int i; - drwav_uint8 riff2[12]; - pWav->container = drwav_container_w64; - if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return DRWAV_FALSE; + ma_uint8 riff2[12]; + pWav->container = ma_dr_wav_container_w64; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { + return MA_FALSE; } for (i = 0; i < 12; ++i) { - if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { - return DRWAV_FALSE; + if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { + return MA_FALSE; } } - } else if (drwav_fourcc_equal(riff, "RF64")) { - pWav->container = drwav_container_rf64; + } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { + pWav->container = ma_dr_wav_container_rf64; + } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { + pWav->container = ma_dr_wav_container_aiff; } else { - return DRWAV_FALSE; + return MA_FALSE; } - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - drwav_uint8 chunkSizeBytes[4]; - drwav_uint8 wave[4]; - if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return DRWAV_FALSE; + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { + ma_uint8 chunkSizeBytes[4]; + ma_uint8 wave[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; } - if (pWav->container == drwav_container_riff) { - if (drwav_bytes_to_u32(chunkSizeBytes) < 36) { - return DRWAV_FALSE; + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { + if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_rf64) { + if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { + return MA_FALSE; } } else { - if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) { - return DRWAV_FALSE; - } + return MA_FALSE; } - if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return DRWAV_FALSE; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return MA_FALSE; } - if (!drwav_fourcc_equal(wave, "WAVE")) { - return DRWAV_FALSE; + if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_w64) { + ma_uint8 chunkSizeBytes[8]; + ma_uint8 wave[16]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return MA_FALSE; + } + if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint8 chunkSizeBytes[4]; + ma_uint8 aiff[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { + return MA_FALSE; + } + if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { + isAIFCFormType = MA_FALSE; + } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { + isAIFCFormType = MA_TRUE; + } else { + return MA_FALSE; } } else { - drwav_uint8 chunkSizeBytes[8]; - drwav_uint8 wave[16]; - if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return DRWAV_FALSE; - } - if (drwav_bytes_to_u64(chunkSizeBytes) < 80) { - return DRWAV_FALSE; - } - if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return DRWAV_FALSE; - } - if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) { - return DRWAV_FALSE; - } + return MA_FALSE; } - if (pWav->container == drwav_container_rf64) { - drwav_uint8 sizeBytes[8]; - drwav_uint64 bytesRemainingInChunk; - drwav_chunk_header header; - drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (pWav->container == ma_dr_wav_container_rf64) { + ma_uint8 sizeBytes[8]; + ma_uint64 bytesRemainingInChunk; + ma_dr_wav_chunk_header header; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + return MA_FALSE; } - if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) { - return DRWAV_FALSE; + if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { + return MA_FALSE; } bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { - return DRWAV_FALSE; + if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { + return MA_FALSE; } bytesRemainingInChunk -= 8; cursor += 8; - if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return DRWAV_FALSE; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return MA_FALSE; } bytesRemainingInChunk -= 8; - dataChunkSize = drwav_bytes_to_u64(sizeBytes); - if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return DRWAV_FALSE; + dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return MA_FALSE; } bytesRemainingInChunk -= 8; - sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes); - if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { - return DRWAV_FALSE; + sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); + if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { + return MA_FALSE; } cursor += bytesRemainingInChunk; } - if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) { - return DRWAV_FALSE; + metadataStartPos = cursor; + isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); + if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { + isProcessingMetadata = MA_FALSE; } - if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) || - (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return DRWAV_FALSE; - } - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0); - } - DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); - if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { - drwav_uint64 cursorForMetadata = cursor; + MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); + if (isProcessingMetadata) { metadataParser.onRead = pWav->onRead; metadataParser.onSeek = pWav->onSeek; metadataParser.pReadSeekUserData = pWav->pUserData; - metadataParser.stage = drwav__metadata_parser_stage_count; - for (;;) { - drwav_result result; - drwav_uint64 bytesRead; - drwav_uint64 remainingBytes; - drwav_chunk_header header; - result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header); - if (result != DRWAV_SUCCESS) { - break; - } - bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); - DRWAV_ASSERT(bytesRead <= header.sizeInBytes); - remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) { - break; - } - cursorForMetadata += remainingBytes; - } - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); - metadataParser.stage = drwav__metadata_parser_stage_read; + metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; } - foundDataChunk = DRWAV_FALSE; for (;;) { - drwav_chunk_header header; - drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != DRWAV_SUCCESS) { - if (!foundDataChunk) { - return DRWAV_FALSE; - } else { - break; - } - } - if (!sequential && onChunk != NULL) { - drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); - if (callbackBytesRead > 0) { - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - } - } - if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { - drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); - if (bytesRead > 0) { - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - } - } - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - chunkSize = header.sizeInBytes; - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - if (drwav_fourcc_equal(header.id.fourcc, "data")) { - foundDataChunk = DRWAV_TRUE; - if (pWav->container != drwav_container_rf64) { - dataChunkSize = chunkSize; - } - } - } else { - if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) { - foundDataChunk = DRWAV_TRUE; - dataChunkSize = chunkSize; - } - } - if (foundDataChunk && sequential) { + ma_dr_wav_chunk_header header; + ma_uint64 chunkSize; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { break; } - if (pWav->container == drwav_container_riff) { - if (drwav_fourcc_equal(header.id.fourcc, "fact")) { - drwav_uint32 sampleCount; - if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return DRWAV_FALSE; + chunkSize = header.sizeInBytes; + if (!sequential && onChunk != NULL) { + ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); + if (callbackBytesRead > 0) { + if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + } + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { + ma_uint8 fmtData[16]; + foundChunk_fmt = MA_TRUE; + if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { + return MA_FALSE; + } + cursor += sizeof(fmtData); + fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); + fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); + fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); + fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); + fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); + fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); + fmt.extendedSize = 0; + fmt.validBitsPerSample = 0; + fmt.channelMask = 0; + MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); + if (header.sizeInBytes > 16) { + ma_uint8 fmt_cbSize[2]; + int bytesReadSoFar = 0; + if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { + return MA_FALSE; + } + cursor += sizeof(fmt_cbSize); + bytesReadSoFar = 18; + fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); + if (fmt.extendedSize > 0) { + if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + if (fmt.extendedSize != 22) { + return MA_FALSE; + } + } + if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + ma_uint8 fmtext[22]; + if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { + return MA_FALSE; + } + fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); + fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); + ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); + } else { + if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { + return MA_FALSE; + } + } + cursor += fmt.extendedSize; + bytesReadSoFar += fmt.extendedSize; + } + if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { + return MA_FALSE; + } + cursor += (header.sizeInBytes - bytesReadSoFar); + } + if (header.paddingSize > 0) { + if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += header.paddingSize; + } + continue; + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { + foundChunk_data = MA_TRUE; + pWav->dataChunkDataPos = cursor; + if (pWav->container != ma_dr_wav_container_rf64) { + dataChunkSize = chunkSize; + } + if (sequential || !isProcessingMetadata) { + break; + } else { + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { + ma_uint8 sampleCount[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { + return MA_FALSE; } chunkSize -= 4; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = sampleCount; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); } else { sampleCountFromFactChunk = 0; } - } - } else if (pWav->container == drwav_container_w64) { - if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) { - if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return DRWAV_FALSE; + } else if (pWav->container == ma_dr_wav_container_w64) { + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { + return MA_FALSE; } chunkSize -= 8; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; + } else if (pWav->container == ma_dr_wav_container_rf64) { + } + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { + ma_uint8 commData[24]; + ma_uint32 commDataBytesToRead; + ma_uint16 channels; + ma_uint32 frameCount; + ma_uint16 sampleSizeInBits; + ma_int64 sampleRate; + ma_uint16 compressionFormat; + foundChunk_fmt = MA_TRUE; + if (isAIFCFormType) { + commDataBytesToRead = 24; + if (header.sizeInBytes < commDataBytesToRead) { + return MA_FALSE; + } + } else { + commDataBytesToRead = 18; + if (header.sizeInBytes != commDataBytesToRead) { + return MA_FALSE; } } - } else if (pWav->container == drwav_container_rf64) { + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { + return MA_FALSE; + } + channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); + frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); + sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); + sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); + if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { + return MA_FALSE; + } + if (isAIFCFormType) { + const ma_uint8* type = commData + 18; + if (ma_dr_wav_fourcc_equal(type, "NONE")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + if (sampleSizeInBits == 8) { + pWav->aiff.isUnsigned = MA_TRUE; + } + } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + pWav->aiff.isLE = MA_TRUE; + } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { + compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; + } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { + compressionFormat = MA_DR_WAVE_FORMAT_ALAW; + } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { + compressionFormat = MA_DR_WAVE_FORMAT_MULAW; + } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { + compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; + sampleSizeInBits = 4; + return MA_FALSE; + } else { + return MA_FALSE; + } + } else { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + } + aiffFrameCount = frameCount; + fmt.formatTag = compressionFormat; + fmt.channels = channels; + fmt.sampleRate = (ma_uint32)sampleRate; + fmt.bitsPerSample = sampleSizeInBits; + fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); + fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; + if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + fmt.blockAlign = 34 * fmt.channels; + } + if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { + if (fmt.bitsPerSample > 8) { + fmt.bitsPerSample = 8; + fmt.blockAlign = fmt.channels; + } + } + fmt.bitsPerSample += (fmt.bitsPerSample & 7); + if (isAIFCFormType) { + if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += (chunkSize - commDataBytesToRead); + } + continue; + } + if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { + ma_uint8 offsetAndBlockSizeData[8]; + ma_uint32 offset; + foundChunk_data = MA_TRUE; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { + return MA_FALSE; + } + offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); + if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += offset; + pWav->dataChunkDataPos = cursor; + dataChunkSize = chunkSize; + if (sequential || !isProcessingMetadata) { + break; + } else { + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + } + if (isProcessingMetadata) { + ma_uint64 metadataBytesRead; + metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); + MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes); + if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { + break; + } } chunkSize += header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) { + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { break; } cursor += chunkSize; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } } - pWav->pMetadata = metadataParser.pMetadata; - pWav->metadataCount = metadataParser.metadataCount; - if (!foundDataChunk) { - return DRWAV_FALSE; + if (!foundChunk_fmt || !foundChunk_data) { + return MA_FALSE; + } + if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || + (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || + (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || + fmt.blockAlign == 0) { + return MA_FALSE; + } + translatedFormatTag = fmt.formatTag; + if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); } if (!sequential) { - if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return DRWAV_FALSE; + if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { + return MA_FALSE; } cursor = pWav->dataChunkDataPos; } + if (isProcessingMetadata && metadataParser.metadataCount > 0) { + if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; + for (;;) { + ma_dr_wav_chunk_header header; + ma_uint64 metadataBytesRead; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + break; + } + metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); + if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { + ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + } + pWav->pMetadata = metadataParser.pMetadata; + pWav->metadataCount = metadataParser.metadataCount; + } + if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { + dataChunkSize = 0; + for (;;) { + ma_uint8 temp[4096]; + size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); + dataChunkSize += bytesRead; + if (bytesRead < sizeof(temp)) { + break; + } + } + } + if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } pWav->fmt = fmt; pWav->sampleRate = fmt.sampleRate; pWav->channels = fmt.channels; @@ -78202,24 +78878,27 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on pWav->dataChunkDataSize = dataChunkSize; if (sampleCountFromFactChunk != 0) { pWav->totalPCMFrameCount = sampleCountFromFactChunk; + } else if (aiffFrameCount != 0) { + pWav->totalPCMFrameCount = aiffFrameCount; } else { - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { - return DRWAV_FALSE; + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; } pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - drwav_uint64 totalBlockHeaderSizeInBytes; - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + ma_uint64 totalBlockHeaderSizeInBytes; + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; if ((blockCount * fmt.blockAlign) < dataChunkSize) { blockCount += 1; } totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - drwav_uint64 totalBlockHeaderSizeInBytes; - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + ma_uint64 totalBlockHeaderSizeInBytes; + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; if ((blockCount * fmt.blockAlign) < dataChunkSize) { blockCount += 1; } @@ -78228,307 +78907,308 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on pWav->totalPCMFrameCount += blockCount; } } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { if (pWav->channels > 2) { - return DRWAV_FALSE; + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; } } - if (drwav_get_bytes_per_pcm_frame(pWav) == 0) { - return DRWAV_FALSE; + if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; } -#ifdef DR_WAV_LIBSNDFILE_COMPAT - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; +#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; } #endif - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_ex(pWav, onRead, onSeek, 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) +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + return MA_FALSE; } - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); + return ma_dr_wav_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) +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; } - pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; - return drwav_init__internal(pWav, NULL, NULL, flags); + return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); } -DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav) +MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) { - drwav_metadata *result = pWav->pMetadata; + ma_dr_wav_metadata *result = pWav->pMetadata; pWav->pMetadata = NULL; pWav->metadataCount = 0; return result; } -DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) +MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); return pWav->onWrite(pWav->pUserData, pData, dataSize); } -DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte) +MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); return pWav->onWrite(pWav->pUserData, &byte, 1); } -DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) +MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap16(value); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap16(value); } - return drwav__write(pWav, &value, 2); + return ma_dr_wav__write(pWav, &value, 2); } -DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) +MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap32(value); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap32(value); } - return drwav__write(pWav, &value, 4); + return ma_dr_wav__write(pWav, &value, 4); } -DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) +MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap64(value); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap64(value); } - return drwav__write(pWav, &value, 8); + return ma_dr_wav__write(pWav, &value, 8); } -DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value) +MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) { union { - drwav_uint32 u32; + ma_uint32 u32; float f32; } u; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); u.f32 = value; - if (!drwav__is_little_endian()) { - u.u32 = drwav__bswap32(u.u32); + if (!ma_dr_wav__is_little_endian()) { + u.u32 = ma_dr_wav__bswap32(u.u32); } - return drwav__write(pWav, &u.u32, 4); + return ma_dr_wav__write(pWav, &u.u32, 4); } -DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize) +MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) { if (pWav == NULL) { return dataSize; } - return drwav__write(pWav, pData, dataSize); + return ma_dr_wav__write(pWav, pData, dataSize); } -DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte) +MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) { if (pWav == NULL) { return 1; } - return drwav__write_byte(pWav, byte); + return ma_dr_wav__write_byte(pWav, byte); } -DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value) +MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) { if (pWav == NULL) { return 2; } - return drwav__write_u16ne_to_le(pWav, value); + return ma_dr_wav__write_u16ne_to_le(pWav, value); } -DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value) +MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) { if (pWav == NULL) { return 4; } - return drwav__write_u32ne_to_le(pWav, value); + return ma_dr_wav__write_u32ne_to_le(pWav, value); } #if 0 -DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value) +MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) { if (pWav == NULL) { return 8; } - return drwav__write_u64ne_to_le(pWav, value); + return ma_dr_wav__write_u64ne_to_le(pWav, value); } #endif -DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value) +MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) { if (pWav == NULL) { return 4; } - return drwav__write_f32ne_to_le(pWav, value); + return ma_dr_wav__write_f32ne_to_le(pWav, value); } -DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize) +MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) { size_t len; if (pWav == NULL) { return bufFixedSize; } - len = drwav__strlen_clamped(str, bufFixedSize); - drwav__write_or_count(pWav, str, len); + len = ma_dr_wav__strlen_clamped(str, bufFixedSize); + ma_dr_wav__write_or_count(pWav, str, len); if (len < bufFixedSize) { size_t i; for (i = 0; i < bufFixedSize - len; ++i) { - drwav__write_byte(pWav, 0); + ma_dr_wav__write_byte(pWav, 0); } } return bufFixedSize; } -DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount) +MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) { size_t bytesWritten = 0; - drwav_bool32 hasListAdtl = DRWAV_FALSE; - drwav_bool32 hasListInfo = DRWAV_FALSE; - drwav_uint32 iMetadata; + ma_bool32 hasListAdtl = MA_FALSE; + ma_bool32 hasListInfo = MA_FALSE; + ma_uint32 iMetadata; if (pMetadatas == NULL || metadataCount == 0) { return 0; } for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 chunkSize = 0; - if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) { - hasListInfo = DRWAV_TRUE; + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 chunkSize = 0; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { + hasListInfo = MA_TRUE; } - if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) { - hasListAdtl = DRWAV_TRUE; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { + hasListAdtl = MA_TRUE; } switch (pMetadata->type) { - case drwav_metadata_type_smpl: + case ma_dr_wav_metadata_type_smpl: { - drwav_uint32 iLoop; - chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, "smpl", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + ma_uint32 iLoop; + chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; + bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); 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].sampleFraction); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); } } break; - case drwav_metadata_type_inst: + case ma_dr_wav_metadata_type_inst: { - chunkSize = DRWAV_INST_BYTES; - bytesWritten += drwav__write_or_count(pWav, "inst", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); + chunkSize = MA_DR_WAV_INST_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); } break; - case drwav_metadata_type_cue: + case ma_dr_wav_metadata_type_cue: { - drwav_uint32 iCuePoint; - chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; - bytesWritten += drwav__write_or_count(pWav, "cue ", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); + ma_uint32 iCuePoint; + chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; + bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); - 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 += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); } } break; - case drwav_metadata_type_acid: + case ma_dr_wav_metadata_type_acid: { - chunkSize = DRWAV_ACID_BYTES; - bytesWritten += drwav__write_or_count(pWav, "acid", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); - bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); - bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); + chunkSize = MA_DR_WAV_ACID_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); + bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); + bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); } break; - case drwav_metadata_type_bext: + case ma_dr_wav_metadata_type_bext: { - char reservedBuf[DRWAV_BEXT_RESERVED_BYTES]; - drwav_uint32 timeReferenceLow; - drwav_uint32 timeReferenceHigh; - chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; - bytesWritten += drwav__write_or_count(pWav, "bext", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); - timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); - bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); + char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; + ma_uint32 timeReferenceLow; + ma_uint32 timeReferenceHigh; + chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; + bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); + timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); + timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); + MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); + bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); if (pMetadata->data.bext.codingHistorySize > 0) { - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); } } break; - case drwav_metadata_type_unknown: + case ma_dr_wav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { chunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); } } break; default: break; } if ((chunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); } } if (hasListInfo) { - drwav_uint32 chunkSize = 4; + ma_uint32 chunkSize = 4; for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { chunkSize += 8; chunkSize += pMetadata->data.infoText.stringLength + 1; - } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { + } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { chunkSize += 8; chunkSize += pMetadata->data.unknown.dataSizeInBytes; } @@ -78536,73 +79216,73 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* chunkSize += 1; } } - bytesWritten += drwav__write_or_count(pWav, "LIST", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, "INFO", 4); + bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 subchunkSize = 0; - if (pMetadata->type & drwav_metadata_type_list_all_info_strings) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 subchunkSize = 0; + if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { 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 ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; + case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; + case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; + case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; + case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; + case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; + case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; + case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; default: break; } - DRWAV_ASSERT(pID != NULL); + MA_DR_WAV_ASSERT(pID != NULL); if (pMetadata->data.infoText.stringLength) { subchunkSize = pMetadata->data.infoText.stringLength + 1; - bytesWritten += drwav__write_or_count(pWav, pID, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); } - } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { + } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { if (pMetadata->data.unknown.dataSizeInBytes) { subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); } } if ((subchunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); } } } if (hasListAdtl) { - drwav_uint32 chunkSize = 4; + ma_uint32 chunkSize = 4; for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; switch (pMetadata->type) { - case drwav_metadata_type_list_label: - case drwav_metadata_type_list_note: + case ma_dr_wav_metadata_type_list_label: + case ma_dr_wav_metadata_type_list_note: { chunkSize += 8; - chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES; + chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; if (pMetadata->data.labelOrNote.stringLength > 0) { chunkSize += pMetadata->data.labelOrNote.stringLength + 1; } } break; - case drwav_metadata_type_list_labelled_cue_region: + case ma_dr_wav_metadata_type_list_labelled_cue_region: { chunkSize += 8; - chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES; + chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; if (pMetadata->data.labelledCueRegion.stringLength > 0) { chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; } } break; - case drwav_metadata_type_unknown: + case ma_dr_wav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { chunkSize += 8; chunkSize += pMetadata->data.unknown.dataSizeInBytes; } @@ -78613,968 +79293,457 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* chunkSize += 1; } } - bytesWritten += drwav__write_or_count(pWav, "LIST", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, "adtl", 4); + bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 subchunkSize = 0; + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 subchunkSize = 0; switch (pMetadata->type) { - case drwav_metadata_type_list_label: - case drwav_metadata_type_list_note: + case ma_dr_wav_metadata_type_list_label: + case ma_dr_wav_metadata_type_list_note: { if (pMetadata->data.labelOrNote.stringLength > 0) { const char *pID = NULL; - if (pMetadata->type == drwav_metadata_type_list_label) { + if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { pID = "labl"; } - else if (pMetadata->type == drwav_metadata_type_list_note) { + else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { pID = "note"; } - DRWAV_ASSERT(pID != NULL); - DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES; - bytesWritten += drwav__write_or_count(pWav, pID, 4); + MA_DR_WAV_ASSERT(pID != NULL); + MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); } } break; - case drwav_metadata_type_list_labelled_cue_region: + case ma_dr_wav_metadata_type_list_labelled_cue_region: { - subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES; - bytesWritten += drwav__write_or_count(pWav, "ltxt", 4); + subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); if (pMetadata->data.labelledCueRegion.stringLength > 0) { subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; } - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); if (pMetadata->data.labelledCueRegion.stringLength > 0) { - DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); } } break; - case drwav_metadata_type_unknown: + case ma_dr_wav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); } } break; default: break; } if ((subchunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); } } } - DRWAV_ASSERT((bytesWritten % 2) == 0); + MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); return bytesWritten; } -DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) { - drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); + ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); if (chunkSize > 0xFFFFFFFFUL) { chunkSize = 0xFFFFFFFFUL; } - return (drwav_uint32)chunkSize; + return (ma_uint32)chunkSize; } -DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) +MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) { if (dataChunkSize <= 0xFFFFFFFFUL) { - return (drwav_uint32)dataChunkSize; + return (ma_uint32)dataChunkSize; } else { return 0xFFFFFFFFUL; } } -DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) { - drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); + ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; } -DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) { return 24 + dataChunkSize; } -DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) { - drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); + ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); if (chunkSize > 0xFFFFFFFFUL) { chunkSize = 0xFFFFFFFFUL; } return chunkSize; } -DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) { return dataChunkSize; } -DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onWrite == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } if (!isSequential && onSeek == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { - return DRWAV_FALSE; + if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + return MA_FALSE; } - if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { - return DRWAV_FALSE; + if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return MA_FALSE; } - DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onWrite = onWrite; pWav->onSeek = onSeek; pWav->pUserData = pUserData; - pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return DRWAV_FALSE; + return MA_FALSE; } - pWav->fmt.formatTag = (drwav_uint16)pFormat->format; - pWav->fmt.channels = (drwav_uint16)pFormat->channels; + pWav->fmt.formatTag = (ma_uint16)pFormat->format; + pWav->fmt.channels = (ma_uint16)pFormat->channels; pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); + pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); + pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; pWav->fmt.extendedSize = 0; pWav->isSequentialWrite = isSequential; - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) { size_t runningPos = 0; - drwav_uint64 initialDataChunkSize = 0; - drwav_uint64 chunkSizeFMT; + ma_uint64 initialDataChunkSize = 0; + ma_uint64 chunkSizeFMT; if (pWav->isSequentialWrite) { initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - if (pFormat->container == drwav_container_riff) { + if (pFormat->container == ma_dr_wav_container_riff) { if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return DRWAV_FALSE; + return MA_FALSE; } } } pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; - runningPos += drwav__write(pWav, "RIFF", 4); - runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); - runningPos += drwav__write(pWav, "WAVE", 4); - } else if (pFormat->container == drwav_container_w64) { - drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); - runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); - } else if (pFormat->container == drwav_container_rf64) { - runningPos += drwav__write(pWav, "RF64", 4); - runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - runningPos += drwav__write(pWav, "WAVE", 4); + if (pFormat->container == ma_dr_wav_container_riff) { + ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "RIFF", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); + runningPos += ma_dr_wav__write(pWav, "WAVE", 4); + } else if (pFormat->container == ma_dr_wav_container_w64) { + ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + runningPos += ma_dr_wav__write(pWav, "RF64", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + runningPos += ma_dr_wav__write(pWav, "WAVE", 4); + } else { + return MA_FALSE; } - if (pFormat->container == drwav_container_rf64) { - drwav_uint32 initialds64ChunkSize = 28; - drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; - runningPos += drwav__write(pWav, "ds64", 4); - runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); - runningPos += drwav__write_u32ne_to_le(pWav, 0); + if (pFormat->container == ma_dr_wav_container_rf64) { + ma_uint32 initialds64ChunkSize = 28; + ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "ds64", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); } - if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) { + if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { chunkSizeFMT = 16; - runningPos += drwav__write(pWav, "fmt ", 4); - runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); - } else if (pFormat->container == drwav_container_w64) { + runningPos += ma_dr_wav__write(pWav, "fmt ", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); + } else if (pFormat->container == ma_dr_wav_container_w64) { chunkSizeFMT = 40; - runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); } - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); - runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); - runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) { - runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); + if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { + runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); } pWav->dataChunkDataPos = runningPos; - if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; - runningPos += drwav__write(pWav, "data", 4); - runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == drwav_container_w64) { - drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == drwav_container_rf64) { - runningPos += drwav__write(pWav, "data", 4); - runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + if (pFormat->container == ma_dr_wav_container_riff) { + ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "data", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == ma_dr_wav_container_w64) { + ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + runningPos += ma_dr_wav__write(pWav, "data", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); } pWav->container = pFormat->container; - pWav->channels = (drwav_uint16)pFormat->channels; + pWav->channels = (ma_uint16)pFormat->channels; pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (drwav_uint16)pFormat->format; + pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; + pWav->translatedFormatTag = (ma_uint16)pFormat->format; pWav->dataChunkDataPos = runningPos; - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; } - return drwav_init_write__internal(pWav, pFormat, 0); + return ma_dr_wav_init_write__internal(pWav, pFormat, 0); } -DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { + return MA_FALSE; } - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); + return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); } -DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); + return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) { - if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; } pWav->pMetadata = pMetadata; pWav->metadataCount = metadataCount; - return drwav_init_write__internal(pWav, pFormat, 0); + return ma_dr_wav_init_write__internal(pWav, pFormat, 0); } -DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) { - drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); - drwav_uint64 riffChunkSizeBytes; - drwav_uint64 fileSizeBytes = 0; - if (pFormat->container == drwav_container_riff) { - riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); + ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); + ma_uint64 riffChunkSizeBytes; + ma_uint64 fileSizeBytes = 0; + if (pFormat->container == ma_dr_wav_container_riff) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); fileSizeBytes = (8 + riffChunkSizeBytes); - } else if (pFormat->container == drwav_container_w64) { - riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); + } else if (pFormat->container == ma_dr_wav_container_w64) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); fileSizeBytes = riffChunkSizeBytes; - } else if (pFormat->container == drwav_container_rf64) { - riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); fileSizeBytes = (8 + riffChunkSizeBytes); } return fileSizeBytes; } -#ifndef DR_WAV_NO_STDIO -#include -DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRWAV_SUCCESS; - #ifdef EPERM - case EPERM: return DRWAV_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRWAV_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRWAV_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRWAV_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRWAV_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRWAV_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRWAV_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRWAV_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRWAV_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRWAV_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRWAV_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRWAV_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRWAV_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRWAV_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRWAV_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRWAV_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRWAV_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRWAV_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRWAV_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRWAV_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRWAV_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRWAV_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRWAV_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRWAV_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRWAV_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRWAV_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRWAV_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRWAV_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRWAV_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRWAV_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRWAV_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRWAV_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRWAV_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRWAV_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRWAV_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRWAV_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRWAV_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRWAV_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRWAV_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRWAV_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRWAV_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRWAV_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRWAV_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRWAV_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRWAV_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRWAV_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRWAV_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRWAV_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRWAV_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRWAV_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRWAV_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRWAV_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRWAV_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRWAV_ERROR; - #endif - #ifdef EADV - case EADV: return DRWAV_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRWAV_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRWAV_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRWAV_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRWAV_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRWAV_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRWAV_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRWAV_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRWAV_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRWAV_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRWAV_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRWAV_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRWAV_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRWAV_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRWAV_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRWAV_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRWAV_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRWAV_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRWAV_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRWAV_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRWAV_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRWAV_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRWAV_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRWAV_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRWAV_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRWAV_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRWAV_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRWAV_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRWAV_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRWAV_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRWAV_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRWAV_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRWAV_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRWAV_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRWAV_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRWAV_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRWAV_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRWAV_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRWAV_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRWAV_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRWAV_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRWAV_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRWAV_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRWAV_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRWAV_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRWAV_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRWAV_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRWAV_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRWAV_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRWAV_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRWAV_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRWAV_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRWAV_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRWAV_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRWAV_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRWAV_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRWAV_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRWAV_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRWAV_ERROR; - #endif - default: return DRWAV_ERROR; - } -} -DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRWAV_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drwav_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drwav_result result = drwav_result_from_errno(errno); - if (result == DRWAV_SUCCESS) { - result = DRWAV_ERROR; - } - return result; - } -#endif - return DRWAV_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRWAV_HAS_WFOPEN - #endif -#endif -#ifndef DR_WAV_NO_WCHAR -DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRWAV_INVALID_ARGS; - } -#if defined(DRWAV_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drwav_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drwav_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - #if defined(__DJGPP__) - { - } - #else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRWAV_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drwav_result_from_errno(errno); - } - pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRWAV_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRWAV_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - #endif - if (*ppFile == NULL) { - return DRWAV_ERROR; - } -#endif - return DRWAV_SUCCESS; -} -#endif -DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +#ifndef MA_DR_WAV_NO_STDIO +MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); } -DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) +MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) { return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); } -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); } -DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, drwav_metadata_type allowedMetadataTypes, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav_bool32 result; - result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRWAV_TRUE) { + ma_bool32 result; + result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { fclose(pFile); return result; } - pWav->allowedMetadataTypes = allowedMetadataTypes; - result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); - if (result != DRWAV_TRUE) { + result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); + if (result != MA_TRUE) { fclose(pFile); return result; } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); } #endif -DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); } #endif -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav_bool32 result; - result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRWAV_TRUE) { + ma_bool32 result; + result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { fclose(pFile); return result; } - result = drwav_init_write__internal(pWav, pFormat, totalSampleCount); - if (result != DRWAV_TRUE) { + result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); + if (result != MA_TRUE) { fclose(pFile); return result; } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); + return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); + return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } #endif -DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); + return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); + return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); + return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); + return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } #endif #endif -DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { - drwav* pWav = (drwav*)pUserData; + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; size_t bytesRemaining; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); + MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); pWav->memoryStream.currentReadPos += bytesToRead; } return bytesToRead; } -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { - drwav* pWav = (drwav*)pUserData; - DRWAV_ASSERT(pWav != NULL); - if (origin == drwav_seek_origin_current) { + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + if (origin == ma_dr_wav_seek_origin_current) { if (offset > 0) { if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return DRWAV_FALSE; + return MA_FALSE; } } else { if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return DRWAV_FALSE; + return MA_FALSE; } } pWav->memoryStream.currentReadPos += offset; } else { - if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { + if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { pWav->memoryStream.currentReadPos = offset; } else { - return DRWAV_FALSE; + return MA_FALSE; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) +MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) { - drwav* pWav = (drwav*)pUserData; + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; size_t bytesRemaining; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; if (bytesRemaining < bytesToWrite) { void* pNewData; @@ -79582,14 +79751,14 @@ DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; } - pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); + pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); if (pNewData == NULL) { return 0; } *pWav->memoryStreamWrite.ppData = pNewData; pWav->memoryStreamWrite.dataCapacity = newDataCapacity; } - DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); + MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); pWav->memoryStreamWrite.currentWritePos += bytesToWrite; if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; @@ -79597,11 +79766,11 @@ DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; return bytesToWrite; } -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { - drwav* pWav = (drwav*)pUserData; - DRWAV_ASSERT(pWav != NULL); - if (origin == drwav_seek_origin_current) { + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + if (origin == ma_dr_wav_seek_origin_current) { if (offset > 0) { if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); @@ -79613,146 +79782,143 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs } pWav->memoryStreamWrite.currentWritePos += offset; } else { - if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { + if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { pWav->memoryStreamWrite.currentWritePos = offset; } else { pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { if (data == NULL || dataSize == 0) { - return DRWAV_FALSE; + return MA_FALSE; } - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + return MA_FALSE; } - pWav->memoryStream.data = (const drwav_uint8*)data; + pWav->memoryStream.data = (const ma_uint8*)data; pWav->memoryStream.dataSize = dataSize; pWav->memoryStream.currentReadPos = 0; - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); + return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); } -DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { if (data == NULL || dataSize == 0) { - return DRWAV_FALSE; + return MA_FALSE; } - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + return MA_FALSE; } - pWav->memoryStream.data = (const drwav_uint8*)data; + pWav->memoryStream.data = (const ma_uint8*)data; pWav->memoryStream.dataSize = dataSize; pWav->memoryStream.currentReadPos = 0; - pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; - return drwav_init__internal(pWav, NULL, NULL, flags); + return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); } -DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) { if (ppData == NULL || pDataSize == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } *ppData = NULL; *pDataSize = 0; - if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { + return MA_FALSE; } pWav->memoryStreamWrite.ppData = ppData; pWav->memoryStreamWrite.pDataSize = pDataSize; pWav->memoryStreamWrite.dataSize = 0; pWav->memoryStreamWrite.dataCapacity = 0; pWav->memoryStreamWrite.currentWritePos = 0; - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); + return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); } -DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); + return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); + return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } -DRWAV_API drwav_result drwav_uninit(drwav* pWav) +MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) { - drwav_result result = DRWAV_SUCCESS; + ma_result result = MA_SUCCESS; if (pWav == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } if (pWav->onWrite != NULL) { - drwav_uint32 paddingSize = 0; - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); + ma_uint32 paddingSize = 0; + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { + paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); } else { - paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); + paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); } if (paddingSize > 0) { - drwav_uint64 paddingData = 0; - drwav__write(pWav, &paddingData, paddingSize); + ma_uint64 paddingData = 0; + ma_dr_wav__write(pWav, &paddingData, paddingSize); } if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == drwav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { - drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - drwav__write_u32ne_to_le(pWav, riffChunkSize); + if (pWav->container == ma_dr_wav_container_riff) { + if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { + ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { - drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); - drwav__write_u32ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { + ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); + ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); } - } else if (pWav->container == drwav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { - drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, riffChunkSize); + } else if (pWav->container == ma_dr_wav_container_w64) { + if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { + ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { - drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { + ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); } - } else if (pWav->container == drwav_container_rf64) { + } else if (pWav->container == ma_dr_wav_container_rf64) { int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { - drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - drwav__write_u64ne_to_le(pWav, riffChunkSize); + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { + ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { - drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { + ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); } } } if (pWav->isSequentialWrite) { if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = DRWAV_INVALID_FILE; + result = MA_INVALID_FILE; } } } else { - if (pWav->pMetadata != NULL) { - pWav->allocationCallbacks.onFree(pWav->pMetadata, pWav->allocationCallbacks.pUserData); - } + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); } -#ifndef DR_WAV_NO_STDIO - if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { +#ifndef MA_DR_WAV_NO_STDIO + if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { fclose((FILE*)pWav->pUserData); } #endif return result; } -DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) +MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) { size_t bytesRead; - drwav_uint32 bytesPerFrame; + ma_uint32 bytesPerFrame; if (pWav == NULL || bytesToRead == 0) { return 0; } @@ -79762,7 +79928,7 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu if (bytesToRead == 0) { return 0; } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -79775,13 +79941,13 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu if (bytesToSeek > 0x7FFFFFFF) { bytesToSeek = 0x7FFFFFFF; } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { break; } bytesRead += bytesToSeek; } while (bytesRead < bytesToRead) { - drwav_uint8 buffer[4096]; + ma_uint8 buffer[4096]; size_t bytesSeeked; size_t bytesToSeek = (bytesToRead - bytesRead); if (bytesToSeek > sizeof(buffer)) { @@ -79798,171 +79964,198 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu pWav->bytesRemaining -= bytesRead; return bytesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) { - drwav_uint32 bytesPerFrame; - drwav_uint64 bytesToRead; + ma_uint32 bytesPerFrame; + ma_uint64 bytesToRead; + ma_uint64 framesRemainingInFile; if (pWav == NULL || framesToRead == 0) { return 0; } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { return 0; } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; + if (framesToRead > framesRemainingInFile) { + framesToRead = framesRemainingInFile; + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesToRead = framesToRead * bytesPerFrame; - if (bytesToRead > DRWAV_SIZE_MAX) { - bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; + if (bytesToRead > MA_SIZE_MAX) { + bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; } if (bytesToRead == 0) { return 0; } - return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; + return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL) { - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } - drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels, pWav->translatedFormatTag); + ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); } return framesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) { - if (drwav__is_little_endian()) { - return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } else { - return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + ma_uint64 framesRead = 0; + if (ma_dr_wav_is_container_be(pWav->container)) { + if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { + if (ma_dr_wav__is_little_endian()) { + framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } else { + framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } + goto post_process; + } } + if (ma_dr_wav__is_little_endian()) { + framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } else { + framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } + post_process: + { + if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { + if (pBufferOut != NULL) { + ma_uint64 iSample; + for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { + ((ma_uint8*)pBufferOut)[iSample] += 128; + } + } + } + } + return framesRead; } -DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) +MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) { if (pWav->onWrite != NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { - return DRWAV_FALSE; + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { + return MA_FALSE; } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - DRWAV_ZERO_OBJECT(&pWav->msadpcm); - } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - DRWAV_ZERO_OBJECT(&pWav->ima); + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); + } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + MA_DR_WAV_ZERO_OBJECT(&pWav->ima); } else { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); } } pWav->readCursorInPCMFrames = 0; pWav->bytesRemaining = pWav->dataChunkDataSize; - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) +MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) { if (pWav == NULL || pWav->onSeek == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } if (pWav->onWrite != NULL) { - return DRWAV_FALSE; + return MA_FALSE; } if (pWav->totalPCMFrameCount == 0) { - return DRWAV_TRUE; + return MA_TRUE; } if (targetFrameIndex > pWav->totalPCMFrameCount) { targetFrameIndex = pWav->totalPCMFrameCount; } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { if (targetFrameIndex < pWav->readCursorInPCMFrames) { - if (!drwav_seek_to_first_pcm_frame(pWav)) { - return DRWAV_FALSE; + if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { + return MA_FALSE; } } if (targetFrameIndex > pWav->readCursorInPCMFrames) { - drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; - drwav_int16 devnull[2048]; + ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; + ma_int16 devnull[2048]; while (offsetInFrames > 0) { - drwav_uint64 framesRead = 0; - drwav_uint64 framesToRead = offsetInFrames; - if (framesToRead > drwav_countof(devnull)/pWav->channels) { - framesToRead = drwav_countof(devnull)/pWav->channels; + ma_uint64 framesRead = 0; + ma_uint64 framesToRead = offsetInFrames; + if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { + framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); + } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); } else { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); } if (framesRead != framesToRead) { - return DRWAV_FALSE; + return MA_FALSE; } offsetInFrames -= framesRead; } } } else { - drwav_uint64 totalSizeInBytes; - drwav_uint64 currentBytePos; - drwav_uint64 targetBytePos; - drwav_uint64 offset; - drwav_uint32 bytesPerFrame; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalSizeInBytes; + ma_uint64 currentBytePos; + ma_uint64 targetBytePos; + ma_uint64 offset; + ma_uint32 bytesPerFrame; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { - return DRWAV_FALSE; + return MA_FALSE; } totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; - DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); currentBytePos = totalSizeInBytes - pWav->bytesRemaining; targetBytePos = targetFrameIndex * bytesPerFrame; if (currentBytePos < targetBytePos) { offset = (targetBytePos - currentBytePos); } else { - if (!drwav_seek_to_first_pcm_frame(pWav)) { - return DRWAV_FALSE; + if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { + return MA_FALSE; } offset = targetBytePos; } while (offset > 0) { int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { - return DRWAV_FALSE; + if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; } pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; pWav->bytesRemaining -= offset32; offset -= offset32; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor) +MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) { if (pCursor == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } *pCursor = 0; if (pWav == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } *pCursor = pWav->readCursorInPCMFrames; - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength) +MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) { if (pLength == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } *pLength = 0; if (pWav == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } *pLength = pWav->totalPCMFrameCount; - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) +MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) { size_t bytesWritten; if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { @@ -79972,26 +80165,26 @@ DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* p pWav->dataChunkDataSize += bytesWritten; return bytesWritten; } -DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) { - drwav_uint64 bytesToWrite; - drwav_uint64 bytesWritten; - const drwav_uint8* pRunningData; + ma_uint64 bytesToWrite; + ma_uint64 bytesWritten; + const ma_uint8* pRunningData; if (pWav == NULL || framesToWrite == 0 || pData == NULL) { return 0; } bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > DRWAV_SIZE_MAX) { + if (bytesToWrite > MA_SIZE_MAX) { return 0; } bytesWritten = 0; - pRunningData = (const drwav_uint8*)pData; + pRunningData = (const ma_uint8*)pData; while (bytesToWrite > 0) { size_t bytesJustWritten; - drwav_uint64 bytesToWriteThisIteration; + ma_uint64 bytesToWriteThisIteration; bytesToWriteThisIteration = bytesToWrite; - DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); - bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); + MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); + bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); if (bytesJustWritten == 0) { break; } @@ -80001,39 +80194,39 @@ DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 frame } return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; } -DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) { - drwav_uint64 bytesToWrite; - drwav_uint64 bytesWritten; - drwav_uint32 bytesPerSample; - const drwav_uint8* pRunningData; + ma_uint64 bytesToWrite; + ma_uint64 bytesWritten; + ma_uint32 bytesPerSample; + const ma_uint8* pRunningData; if (pWav == NULL || framesToWrite == 0 || pData == NULL) { return 0; } bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > DRWAV_SIZE_MAX) { + if (bytesToWrite > MA_SIZE_MAX) { return 0; } bytesWritten = 0; - pRunningData = (const drwav_uint8*)pData; - bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; + pRunningData = (const ma_uint8*)pData; + bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; if (bytesPerSample == 0) { return 0; } while (bytesToWrite > 0) { - drwav_uint8 temp[4096]; - drwav_uint32 sampleCount; + ma_uint8 temp[4096]; + ma_uint32 sampleCount; size_t bytesJustWritten; - drwav_uint64 bytesToWriteThisIteration; + ma_uint64 bytesToWriteThisIteration; bytesToWriteThisIteration = bytesToWrite; - DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); + MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); sampleCount = sizeof(temp)/bytesPerSample; - if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; + if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { + bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; } - DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag); - bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); + MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); + ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); + bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); if (bytesJustWritten == 0) { break; } @@ -80043,49 +80236,49 @@ DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 frame } return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; } -DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) { - if (drwav__is_little_endian()) { - return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); + if (ma_dr_wav__is_little_endian()) { + return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); } else { - return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); + return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); } } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead = 0; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(framesToRead > 0); + ma_uint64 totalFramesRead = 0; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - DRWAV_ASSERT(framesToRead > 0); + MA_DR_WAV_ASSERT(framesToRead > 0); if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { if (pWav->channels == 1) { - drwav_uint8 header[7]; + ma_uint8 header[7]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5); + pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); + pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); + pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrameCount = 2; } else { - drwav_uint8 header[14]; + ma_uint8 header[14]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); pWav->msadpcm.predictor[0] = header[0]; pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12); + pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); + pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); + pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); + pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); + pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); + pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; @@ -80095,9 +80288,9 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav } while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { if (pBufferOut != NULL) { - drwav_uint32 iSample = 0; + ma_uint32 iSample = 0; for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; + pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; } pBufferOut += pWav->channels; } @@ -80113,15 +80306,15 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav if (pWav->msadpcm.bytesRemainingInBlock == 0) { continue; } else { - static drwav_int32 adaptationTable[] = { + static ma_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; + static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + ma_uint8 nibbles; + ma_int32 nibble0; + ma_int32 nibble1; if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { return totalFramesRead; } @@ -80129,11 +80322,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } if (pWav->channels == 1) { - drwav_int32 newSample0; - drwav_int32 newSample1; + ma_int32 newSample0; + ma_int32 newSample1; newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = drwav_clamp(newSample0, -32768, 32767); + newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -80142,7 +80335,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav pWav->msadpcm.prevFrames[0][1] = newSample0; newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = drwav_clamp(newSample1, -32768, 32767); + newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -80153,11 +80346,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav pWav->msadpcm.cachedFrames[3] = newSample1; pWav->msadpcm.cachedFrameCount = 2; } else { - drwav_int32 newSample0; - drwav_int32 newSample1; + ma_int32 newSample0; + ma_int32 newSample1; newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = drwav_clamp(newSample0, -32768, 32767); + newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -80166,7 +80359,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav pWav->msadpcm.prevFrames[0][1] = newSample0; newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = drwav_clamp(newSample1, -32768, 32767); + newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; if (pWav->msadpcm.delta[1] < 16) { pWav->msadpcm.delta[1] = 16; @@ -80182,15 +80375,15 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead = 0; - drwav_uint32 iChannel; - static drwav_int32 indexTable[16] = { + ma_uint64 totalFramesRead = 0; + ma_uint32 iChannel; + static ma_int32 indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; - static drwav_int32 stepTable[89] = { + static ma_int32 stepTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, @@ -80201,51 +80394,51 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(framesToRead > 0); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - DRWAV_ASSERT(framesToRead > 0); + MA_DR_WAV_ASSERT(framesToRead > 0); if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { if (pWav->channels == 1) { - drwav_uint8 header[4]; + ma_uint8 header[4]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } 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); + if (header[2] >= ma_dr_wav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } - pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; + pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); + pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; pWav->ima.cachedFrameCount = 1; } else { - drwav_uint8 header[8]; + ma_uint8 header[8]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } 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); + if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } - pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; + pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); + pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; pWav->ima.cachedFrameCount = 1; } } while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { if (pBufferOut != NULL) { - drwav_uint32 iSample; + ma_uint32 iSample; for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; + pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; } pBufferOut += pWav->channels; } @@ -80263,27 +80456,27 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin } else { pWav->ima.cachedFrameCount = 8; for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - drwav_uint32 iByte; - drwav_uint8 nibbles[4]; + ma_uint32 iByte; + ma_uint8 nibbles[4]; if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { pWav->ima.cachedFrameCount = 0; return totalFramesRead; } pWav->ima.bytesRemainingInBlock -= 4; for (iByte = 0; iByte < 4; ++iByte) { - drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - drwav_int32 predictor = pWav->ima.predictor[iChannel]; - drwav_int32 diff = step >> 3; + ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); + ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); + ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; + ma_int32 predictor = pWav->ima.predictor[iChannel]; + ma_int32 diff = step >> 3; if (nibble0 & 1) diff += step >> 2; if (nibble0 & 2) diff += step >> 1; if (nibble0 & 4) diff += step; if (nibble0 & 8) diff = -diff; - predictor = drwav_clamp(predictor + diff, -32768, 32767); + predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; step = stepTable[pWav->ima.stepIndex[iChannel]]; predictor = pWav->ima.predictor[iChannel]; diff = step >> 3; @@ -80291,10 +80484,10 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin if (nibble1 & 2) diff += step >> 1; if (nibble1 & 4) diff += step; if (nibble1 & 8) diff = -diff; - predictor = drwav_clamp(predictor + diff, -32768, 32767); + predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; } } } @@ -80302,8 +80495,8 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin } return totalFramesRead; } -#ifndef DR_WAV_NO_CONVERSION_API -static unsigned short g_drwavAlawTable[256] = { +#ifndef MA_DR_WAV_NO_CONVERSION_API +static unsigned short g_ma_dr_wavAlawTable[256] = { 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, @@ -80321,7 +80514,7 @@ static unsigned short g_drwavAlawTable[256] = { 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 }; -static unsigned short g_drwavMulawTable[256] = { +static unsigned short g_ma_dr_wavMulawTable[256] = { 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, @@ -80339,76 +80532,76 @@ static unsigned short g_drwavMulawTable[256] = { 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 }; -static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) +static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) { - return (short)g_drwavAlawTable[sampleIn]; + return (short)g_ma_dr_wavAlawTable[sampleIn]; } -static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) +static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) { - return (short)g_drwavMulawTable[sampleIn]; + return (short)g_ma_dr_wavMulawTable[sampleIn]; } -DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { size_t i; if (bytesPerSample == 1) { - drwav_u8_to_s16(pOut, pIn, totalSampleCount); + ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 2) { for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const drwav_int16*)pIn)[i]; + *pOut++ = ((const ma_int16*)pIn)[i]; } return; } if (bytesPerSample == 3) { - drwav_s24_to_s16(pOut, pIn, totalSampleCount); + ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 4) { - drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); + ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); return; } if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } for (i = 0; i < totalSampleCount; ++i) { - drwav_uint64 sample = 0; + ma_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; - *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); + *pOut++ = (ma_int16)((ma_int64)sample >> 48); } } -DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { - drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); + ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); return; } else if (bytesPerSample == 8) { - drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); + ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); return; } else { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80418,35 +80611,35 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uin } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80456,35 +80649,35 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80494,35 +80687,45 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80532,72 +80735,82 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_u } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; + if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); } return 0; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) { int r; size_t i; @@ -80608,17 +80821,17 @@ DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t pOut[i] = (short)r; } } -DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) { int r; size_t i; for (i = 0; i < sampleCount; ++i) { - int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8; + int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; r = x >> 8; pOut[i] = (short)r; } } -DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) { int r; size_t i; @@ -80628,7 +80841,7 @@ DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_ pOut[i] = (short)r; } } -DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) { int r; size_t i; @@ -80642,7 +80855,7 @@ DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t samp pOut[i] = (short)r; } } -DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) { int r; size_t i; @@ -80656,57 +80869,57 @@ DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sam pOut[i] = (short)r; } } -DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; for (i = 0; i < sampleCount; ++i) { - pOut[i] = drwav__alaw_to_s16(pIn[i]); + pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); } } -DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; for (i = 0; i < sampleCount; ++i) { - pOut[i] = drwav__mulaw_to_s16(pIn[i]); + pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); } } -DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) { unsigned int i; if (bytesPerSample == 1) { - drwav_u8_to_f32(pOut, pIn, sampleCount); + ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); return; } if (bytesPerSample == 2) { - drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); + ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); return; } if (bytesPerSample == 3) { - drwav_s24_to_f32(pOut, pIn, sampleCount); + ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); return; } if (bytesPerSample == 4) { - drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); + ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); return; } if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); return; } for (i = 0; i < sampleCount; ++i) { - drwav_uint64 sample = 0; + ma_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; - *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); + *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); } } -DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { unsigned int i; @@ -80715,21 +80928,21 @@ DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_ } return; } else if (bytesPerSample == 8) { - drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount); + ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); return; } else { - DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); return; } } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80739,54 +80952,54 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uin } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_int16 samples16[2048]; + ma_uint64 totalFramesRead; + ma_int16 samples16[2048]; totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80796,32 +81009,32 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80831,32 +81044,42 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80866,75 +81089,85 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_u } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels; + if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); } return 0; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } -#ifdef DR_WAV_LIBSNDFILE_COMPAT +#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT for (i = 0; i < sampleCount; ++i) { *pOut++ = (pIn[i] / 256.0f) * 2 - 1; } @@ -80947,7 +81180,7 @@ DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampl } #endif } -DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -80957,7 +81190,7 @@ DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t samp *pOut++ = pIn[i] * 0.000030517578125f; } } -DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -80965,14 +81198,14 @@ DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t samp } for (i = 0; i < sampleCount; ++i) { double x; - drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); - drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); - drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); - x = (double)((drwav_int32)(a | b | c) >> 8); + ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); + ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); + ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); + x = (double)((ma_int32)(a | b | c) >> 8); *pOut++ = (float)(x * 0.00000011920928955078125); } } -DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -80982,7 +81215,7 @@ DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t samp *pOut++ = (float)(pIn[i] / 2147483648.0); } } -DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -80992,88 +81225,88 @@ DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCou *pOut++ = (float)pIn[i]; } } -DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; + *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; } } -DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; + *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; } } -DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { unsigned int i; if (bytesPerSample == 1) { - drwav_u8_to_s32(pOut, pIn, totalSampleCount); + ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 2) { - drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); + ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); return; } if (bytesPerSample == 3) { - drwav_s24_to_s32(pOut, pIn, totalSampleCount); + ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 4) { for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const drwav_int32*)pIn)[i]; + *pOut++ = ((const ma_int32*)pIn)[i]; } return; } if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } for (i = 0; i < totalSampleCount; ++i) { - drwav_uint64 sample = 0; + ma_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; - *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); + *pOut++ = (ma_int32)((ma_int64)sample >> 32); } } -DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { - drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); + ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); return; } else if (bytesPerSample == 8) { - drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); + ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); return; } else { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81083,50 +81316,50 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uin } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead = 0; - drwav_int16 samples16[2048]; + ma_uint64 totalFramesRead = 0; + ma_int16 samples16[2048]; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81136,32 +81369,32 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81171,32 +81404,42 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81206,69 +81449,79 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_u } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels; + if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); } return 0; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -81278,7 +81531,7 @@ DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t *pOut++ = ((int)pIn[i] - 128) << 24; } } -DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -81288,7 +81541,7 @@ DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_ *pOut++ = pIn[i] << 16; } } -DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -81298,73 +81551,73 @@ DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_ unsigned int s0 = pIn[i*3 + 0]; unsigned int s1 = pIn[i*3 + 1]; unsigned int s2 = pIn[i*3 + 2]; - drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); + ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); *pOut++ = sample32; } } -DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); } } -DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); } } -DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; + *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; } } -DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i= 0; i < sampleCount; ++i) { - *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; + *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; } } -DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) { - drwav_uint64 sampleDataSize; - drwav_int16* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); + ma_uint64 sampleDataSize; + ma_int16* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); return NULL; } - pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); return NULL; } - framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); return NULL; } - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -81376,29 +81629,29 @@ DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, uns } return pSampleData; } -DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) { - drwav_uint64 sampleDataSize; + ma_uint64 sampleDataSize; float* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); return NULL; } - pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); return NULL; } - framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); return NULL; } - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -81410,29 +81663,29 @@ DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned } return pSampleData; } -DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) { - drwav_uint64 sampleDataSize; - drwav_int32* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); + ma_uint64 sampleDataSize; + ma_int32* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); return NULL; } - pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); return NULL; } - framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); return NULL; } - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -81444,9 +81697,9 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns } return pSampleData; } -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) +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81456,14 +81709,14 @@ DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__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) +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81473,14 +81726,14 @@ DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwa if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__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) +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81490,15 +81743,15 @@ DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81508,14 +81761,14 @@ DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filen if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81525,14 +81778,14 @@ DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, u if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81542,15 +81795,15 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filen if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -81560,14 +81813,14 @@ DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -81577,14 +81830,14 @@ DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filena if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -81594,16 +81847,16 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif #endif -DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81613,14 +81866,14 @@ DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* dat if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81630,14 +81883,14 @@ DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, siz if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81647,66 +81900,66 @@ DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* dat if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif -DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - drwav__free_from_callbacks(p, pAllocationCallbacks); + ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); } else { - drwav__free_default(p, NULL); + ma_dr_wav__free_default(p, NULL); } } -DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data) +MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) { - return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8); + return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); } -DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data) +MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) { - return (drwav_int16)drwav_bytes_to_u16(data); + return (ma_int16)ma_dr_wav_bytes_to_u16(data); } -DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data) +MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) { - return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24); + return ma_dr_wav_bytes_to_u32_le(data); } -DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data) +MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) { union { - drwav_uint32 u32; + ma_uint32 u32; float f32; } value; - value.u32 = drwav_bytes_to_u32(data); + value.u32 = ma_dr_wav_bytes_to_u32(data); return value.f32; } -DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data) +MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) { - return (drwav_int32)drwav_bytes_to_u32(data); + return (ma_int32)ma_dr_wav_bytes_to_u32(data); } -DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data) +MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) { return - ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | - ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); + ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | + ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); } -DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data) +MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) { - return (drwav_int64)drwav_bytes_to_u64(data); + return (ma_int64)ma_dr_wav_bytes_to_u64(data); } -DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) +MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) { int i; for (i = 0; i < 16; i += 1) { if (a[i] != b[i]) { - return DRWAV_FALSE; + return MA_FALSE; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) +MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) { return a[0] == b[0] && @@ -81719,14 +81972,14 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #endif #endif /* dr_wav_c end */ -#endif /* DRWAV_IMPLEMENTATION */ +#endif /* MA_DR_WAV_IMPLEMENTATION */ #endif /* MA_NO_WAV */ #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_flac_c begin */ -#ifndef dr_flac_c -#define dr_flac_c +#ifndef ma_dr_flac_c +#define ma_dr_flac_c #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #if __GNUC__ >= 7 @@ -81747,85 +82000,60 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #endif #include #include -#ifdef _MSC_VER - #define DRFLAC_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRFLAC_GNUC_INLINE_HINT __inline__ - #else - #define DRFLAC_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRFLAC_INLINE __inline -#else - #define DRFLAC_INLINE -#endif -#if defined(__x86_64__) || defined(_M_X64) - #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) - #define DRFLAC_ARM -#endif -#if !defined(DR_FLAC_NO_SIMD) - #if defined(DRFLAC_X64) || defined(DRFLAC_X86) +#if !defined(MA_DR_FLAC_NO_SIMD) + #if defined(MA_X64) || defined(MA_X86) #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) - #define DRFLAC_SUPPORT_SSE2 + #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) + #define MA_DR_FLAC_SUPPORT_SSE2 #endif - #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) - #define DRFLAC_SUPPORT_SSE41 + #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) + #define MA_DR_FLAC_SUPPORT_SSE41 #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) - #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) - #define DRFLAC_SUPPORT_SSE2 + #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) + #define MA_DR_FLAC_SUPPORT_SSE2 #endif - #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) - #define DRFLAC_SUPPORT_SSE41 + #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) + #define MA_DR_FLAC_SUPPORT_SSE41 #endif #endif #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include() - #define DRFLAC_SUPPORT_SSE2 + #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() + #define MA_DR_FLAC_SUPPORT_SSE2 #endif - #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include() - #define DRFLAC_SUPPORT_SSE41 + #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() + #define MA_DR_FLAC_SUPPORT_SSE41 #endif #endif - #if defined(DRFLAC_SUPPORT_SSE41) + #if defined(MA_DR_FLAC_SUPPORT_SSE41) #include - #elif defined(DRFLAC_SUPPORT_SSE2) + #elif defined(MA_DR_FLAC_SUPPORT_SSE2) #include #endif #endif - #if defined(DRFLAC_ARM) - #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define DRFLAC_SUPPORT_NEON + #if defined(MA_ARM) + #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define MA_DR_FLAC_SUPPORT_NEON #include #endif #endif #endif -#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) +#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 #include - static void drflac__cpuid(int info[4], int fid) + static void ma_dr_flac__cpuid(int info[4], int fid) { __cpuid(info, fid); } #else - #define DRFLAC_NO_CPUID + #define MA_DR_FLAC_NO_CPUID #endif #else #if defined(__GNUC__) || defined(__clang__) - static void drflac__cpuid(int info[4], int fid) + static void ma_dr_flac__cpuid(int info[4], int fid) { - #if defined(DRFLAC_X86) && defined(__PIC__) + #if defined(MA_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" @@ -81839,100 +82067,100 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #endif } #else - #define DRFLAC_NO_CPUID + #define MA_DR_FLAC_NO_CPUID #endif #endif #else - #define DRFLAC_NO_CPUID + #define MA_DR_FLAC_NO_CPUID #endif -static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) +static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) { -#if defined(DRFLAC_SUPPORT_SSE2) - #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) - #if defined(DRFLAC_X64) - return DRFLAC_TRUE; +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) + #if defined(MA_X64) + return MA_TRUE; #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return DRFLAC_TRUE; + return MA_TRUE; #else - #if defined(DRFLAC_NO_CPUID) - return DRFLAC_FALSE; + #if defined(MA_DR_FLAC_NO_CPUID) + return MA_FALSE; #else int info[4]; - drflac__cpuid(info, 1); + ma_dr_flac__cpuid(info, 1); return (info[3] & (1 << 26)) != 0; #endif #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif } -static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) +static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) { -#if defined(DRFLAC_SUPPORT_SSE41) - #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) +#if defined(MA_DR_FLAC_SUPPORT_SSE41) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) #if defined(__SSE4_1__) || defined(__AVX__) - return DRFLAC_TRUE; + return MA_TRUE; #else - #if defined(DRFLAC_NO_CPUID) - return DRFLAC_FALSE; + #if defined(MA_DR_FLAC_NO_CPUID) + return MA_FALSE; #else int info[4]; - drflac__cpuid(info, 1); + ma_dr_flac__cpuid(info, 1); return (info[2] & (1 << 19)) != 0; #endif #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif } -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) - #define DRFLAC_HAS_LZCNT_INTRINSIC +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define DRFLAC_HAS_LZCNT_INTRINSIC + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) - #define DRFLAC_HAS_LZCNT_INTRINSIC + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC #endif #endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC #endif #elif defined(__WATCOMC__) && defined(__386__) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC - extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); - extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); - extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + extern __inline ma_uint16 _watcom_bswap16(ma_uint16); + extern __inline ma_uint32 _watcom_bswap32(ma_uint32); + extern __inline ma_uint64 _watcom_bswap64(ma_uint64); #pragma aux _watcom_bswap16 = \ "xchg al, ah" \ parm [ax] \ @@ -81951,185 +82179,129 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) value [eax edx] \ modify nomemory; #endif -#ifndef DRFLAC_ASSERT +#ifndef MA_DR_FLAC_ASSERT #include -#define DRFLAC_ASSERT(expression) assert(expression) +#define MA_DR_FLAC_ASSERT(expression) assert(expression) #endif -#ifndef DRFLAC_MALLOC -#define DRFLAC_MALLOC(sz) malloc((sz)) +#ifndef MA_DR_FLAC_MALLOC +#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) #endif -#ifndef DRFLAC_REALLOC -#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) +#ifndef MA_DR_FLAC_REALLOC +#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) #endif -#ifndef DRFLAC_FREE -#define DRFLAC_FREE(p) free((p)) +#ifndef MA_DR_FLAC_FREE +#define MA_DR_FLAC_FREE(p) free((p)) #endif -#ifndef DRFLAC_COPY_MEMORY -#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#ifndef MA_DR_FLAC_COPY_MEMORY +#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#ifndef DRFLAC_ZERO_MEMORY -#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#ifndef MA_DR_FLAC_ZERO_MEMORY +#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#ifndef DRFLAC_ZERO_OBJECT -#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef MA_DR_FLAC_ZERO_OBJECT +#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) #endif -#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 -typedef drflac_int32 drflac_result; -#define DRFLAC_SUCCESS 0 -#define DRFLAC_ERROR -1 -#define DRFLAC_INVALID_ARGS -2 -#define DRFLAC_INVALID_OPERATION -3 -#define DRFLAC_OUT_OF_MEMORY -4 -#define DRFLAC_OUT_OF_RANGE -5 -#define DRFLAC_ACCESS_DENIED -6 -#define DRFLAC_DOES_NOT_EXIST -7 -#define DRFLAC_ALREADY_EXISTS -8 -#define DRFLAC_TOO_MANY_OPEN_FILES -9 -#define DRFLAC_INVALID_FILE -10 -#define DRFLAC_TOO_BIG -11 -#define DRFLAC_PATH_TOO_LONG -12 -#define DRFLAC_NAME_TOO_LONG -13 -#define DRFLAC_NOT_DIRECTORY -14 -#define DRFLAC_IS_DIRECTORY -15 -#define DRFLAC_DIRECTORY_NOT_EMPTY -16 -#define DRFLAC_END_OF_FILE -17 -#define DRFLAC_NO_SPACE -18 -#define DRFLAC_BUSY -19 -#define DRFLAC_IO_ERROR -20 -#define DRFLAC_INTERRUPT -21 -#define DRFLAC_UNAVAILABLE -22 -#define DRFLAC_ALREADY_IN_USE -23 -#define DRFLAC_BAD_ADDRESS -24 -#define DRFLAC_BAD_SEEK -25 -#define DRFLAC_BAD_PIPE -26 -#define DRFLAC_DEADLOCK -27 -#define DRFLAC_TOO_MANY_LINKS -28 -#define DRFLAC_NOT_IMPLEMENTED -29 -#define DRFLAC_NO_MESSAGE -30 -#define DRFLAC_BAD_MESSAGE -31 -#define DRFLAC_NO_DATA_AVAILABLE -32 -#define DRFLAC_INVALID_DATA -33 -#define DRFLAC_TIMEOUT -34 -#define DRFLAC_NO_NETWORK -35 -#define DRFLAC_NOT_UNIQUE -36 -#define DRFLAC_NOT_SOCKET -37 -#define DRFLAC_NO_ADDRESS -38 -#define DRFLAC_BAD_PROTOCOL -39 -#define DRFLAC_PROTOCOL_UNAVAILABLE -40 -#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 -#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRFLAC_SOCKET_NOT_SUPPORTED -44 -#define DRFLAC_CONNECTION_RESET -45 -#define DRFLAC_ALREADY_CONNECTED -46 -#define DRFLAC_NOT_CONNECTED -47 -#define DRFLAC_CONNECTION_REFUSED -48 -#define DRFLAC_NO_HOST -49 -#define DRFLAC_IN_PROGRESS -50 -#define DRFLAC_CANCELLED -51 -#define DRFLAC_MEMORY_ALREADY_MAPPED -52 -#define DRFLAC_AT_END -53 -#define DRFLAC_CRC_MISMATCH -128 -#define DRFLAC_SUBFRAME_CONSTANT 0 -#define DRFLAC_SUBFRAME_VERBATIM 1 -#define DRFLAC_SUBFRAME_FIXED 8 -#define DRFLAC_SUBFRAME_LPC 32 -#define DRFLAC_SUBFRAME_RESERVED 255 -#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 -#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 -#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 -#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 -#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 -#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -#define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 -#define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 -#define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 -#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) +#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 +#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 +#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 +#define MA_DR_FLAC_SUBFRAME_FIXED 8 +#define MA_DR_FLAC_SUBFRAME_LPC 32 +#define MA_DR_FLAC_SUBFRAME_RESERVED 255 +#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 +#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 +#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) { if (pMajor) { - *pMajor = DRFLAC_VERSION_MAJOR; + *pMajor = MA_DR_FLAC_VERSION_MAJOR; } if (pMinor) { - *pMinor = DRFLAC_VERSION_MINOR; + *pMinor = MA_DR_FLAC_VERSION_MINOR; } if (pRevision) { - *pRevision = DRFLAC_VERSION_REVISION; + *pRevision = MA_DR_FLAC_VERSION_REVISION; } } -DRFLAC_API const char* drflac_version_string(void) +MA_API const char* ma_dr_flac_version_string(void) { - return DRFLAC_VERSION_STRING; + return MA_DR_FLAC_VERSION_STRING; } #if defined(__has_feature) #if __has_feature(thread_sanitizer) - #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) + #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) #else - #define DRFLAC_NO_THREAD_SANITIZE + #define MA_DR_FLAC_NO_THREAD_SANITIZE #endif #else - #define DRFLAC_NO_THREAD_SANITIZE + #define MA_DR_FLAC_NO_THREAD_SANITIZE #endif -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) -static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; #endif -#ifndef DRFLAC_NO_CPUID -static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; -static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; -DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) +#ifndef MA_DR_FLAC_NO_CPUID +static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; +static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; +MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) { - static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; + static ma_bool32 isCPUCapsInitialized = MA_FALSE; if (!isCPUCapsInitialized) { -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) int info[4] = {0}; - drflac__cpuid(info, 0x80000001); - drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; + ma_dr_flac__cpuid(info, 0x80000001); + ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; #endif - drflac__gIsSSE2Supported = drflac_has_sse2(); - drflac__gIsSSE41Supported = drflac_has_sse41(); - isCPUCapsInitialized = DRFLAC_TRUE; + ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); + ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); + isCPUCapsInitialized = MA_TRUE; } } #else -static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; -static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) +static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; +static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) { -#if defined(DRFLAC_SUPPORT_NEON) - #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) +#if defined(MA_DR_FLAC_SUPPORT_NEON) + #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return DRFLAC_TRUE; + return MA_TRUE; #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif } -DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) +MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) { - drflac__gIsNEONSupported = drflac__has_neon(); -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - drflac__gIsLZCNTSupported = DRFLAC_TRUE; + ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + ma_dr_flac__gIsLZCNTSupported = MA_TRUE; #endif } #endif -static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) +static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) { -#if defined(DRFLAC_X86) || defined(DRFLAC_X64) - return DRFLAC_TRUE; +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return DRFLAC_TRUE; + return MA_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } -static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) +static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) { -#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC +#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) @@ -82144,16 +82316,16 @@ static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) ((n & 0x00FF) << 8); #endif } -static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) +static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) { -#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC +#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) - #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT) - drflac_uint32 r; + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) + ma_uint32 r; __asm__ __volatile__ ( - #if defined(DRFLAC_64BIT) + #if defined(MA_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) @@ -82175,9 +82347,9 @@ static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) ((n & 0x000000FF) << 24); #endif } -static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) +static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) { -#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC +#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) @@ -82188,64 +82360,64 @@ static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) #error "This compiler does not support the byte swap intrinsic." #endif #else - return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | - ((n & ((drflac_uint64)0xFF000000 )) << 8) | - ((n & ((drflac_uint64)0x00FF0000 )) << 24) | - ((n & ((drflac_uint64)0x0000FF00 )) << 40) | - ((n & ((drflac_uint64)0x000000FF )) << 56); + return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | + ((n & ((ma_uint64)0xFF000000 )) << 8) | + ((n & ((ma_uint64)0x00FF0000 )) << 24) | + ((n & ((ma_uint64)0x0000FF00 )) << 40) | + ((n & ((ma_uint64)0x000000FF )) << 56); #endif } -static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) +static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) { - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint16(n); + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint16(n); } return n; } -static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) +static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) { - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint32(n); + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint32(n); } return n; } -static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) +static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) { - const drflac_uint8* pNum = (drflac_uint8*)pData; + const ma_uint8* pNum = (ma_uint8*)pData; return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); } -static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) +static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) { - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint64(n); + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint64(n); } return n; } -static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) +static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) { - if (!drflac__is_little_endian()) { - return drflac__swap_endian_uint32(n); + if (!ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint32(n); } return n; } -static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) +static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) { - const drflac_uint8* pNum = (drflac_uint8*)pData; + const ma_uint8* pNum = (ma_uint8*)pData; return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; } -static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) +static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) { - drflac_uint32 result = 0; + ma_uint32 result = 0; result |= (n & 0x7F000000) >> 3; result |= (n & 0x007F0000) >> 2; result |= (n & 0x00007F00) >> 1; result |= (n & 0x0000007F) >> 0; return result; } -static drflac_uint8 drflac__crc8_table[] = { +static ma_uint8 ma_dr_flac__crc8_table[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, @@ -82263,7 +82435,7 @@ static drflac_uint8 drflac__crc8_table[] = { 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 }; -static drflac_uint16 drflac__crc16_table[] = { +static ma_uint16 ma_dr_flac__crc16_table[] = { 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, @@ -82297,22 +82469,22 @@ static drflac_uint16 drflac__crc16_table[] = { 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 }; -static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) +static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) { - return drflac__crc8_table[crc ^ data]; + return ma_dr_flac__crc8_table[crc ^ data]; } -static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) +static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) { -#ifdef DR_FLAC_NO_CRC +#ifdef MA_DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 - drflac_uint8 p = 0x07; + ma_uint8 p = 0x07; for (int i = count-1; i >= 0; --i) { - drflac_uint8 bit = (data & (1 << i)) >> i; + ma_uint8 bit = (data & (1 << i)) >> i; if (crc & 0x80) { crc = ((crc << 1) | bit) ^ p; } else { @@ -82321,75 +82493,75 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 da } return crc; #else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - DRFLAC_ASSERT(count <= 32); + MA_DR_FLAC_ASSERT(count <= 32); wholeBytes = count >> 3; leftoverBits = count - (wholeBytes*8); leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { - case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); + case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); } return crc; #endif #endif } -static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) +static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) { - return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; + return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; } -static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) +static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) { -#ifdef DRFLAC_64BIT - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#ifdef MA_64BIT + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); #endif - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); return crc; } -static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) +static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) { switch (byteCount) { -#ifdef DRFLAC_64BIT - case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); - case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); - case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); - case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#ifdef MA_64BIT + case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); + case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); + case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); + case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); #endif - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); } return crc; } #if 0 -static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) +static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) { -#ifdef DR_FLAC_NO_CRC +#ifdef MA_DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 - drflac_uint16 p = 0x8005; + ma_uint16 p = 0x8005; for (int i = count-1; i >= 0; --i) { - drflac_uint16 bit = (data & (1ULL << i)) >> i; + ma_uint16 bit = (data & (1ULL << i)) >> i; if (r & 0x8000) { r = ((r << 1) | bit) ^ p; } else { @@ -82398,433 +82570,433 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac } return crc; #else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - DRFLAC_ASSERT(count <= 64); + MA_DR_FLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif #endif } -static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) +static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) { -#ifdef DR_FLAC_NO_CRC +#ifdef MA_DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - DRFLAC_ASSERT(count <= 64); + MA_DR_FLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: - case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); - case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); - case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); - case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); + case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif } -static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) +static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) { -#ifdef DRFLAC_64BIT - return drflac_crc16__64bit(crc, data, count); +#ifdef MA_64BIT + return ma_dr_flac_crc16__64bit(crc, data, count); #else - return drflac_crc16__32bit(crc, data, count); + return ma_dr_flac_crc16__32bit(crc, data, count); #endif } #endif -#ifdef DRFLAC_64BIT -#define drflac__be2host__cache_line drflac__be2host_64 +#ifdef MA_64BIT +#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 #else -#define drflac__be2host__cache_line drflac__be2host_32 +#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 #endif -#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) -#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) -#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) -#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) -#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) -#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) -#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) -#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) -#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) -#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) -#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -#ifndef DR_FLAC_NO_CRC -static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) +#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) +#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) +#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) +#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) +#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) +#ifndef MA_DR_FLAC_NO_CRC +static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) { bs->crc16 = 0; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } -static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) +static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) { if (bs->crc16CacheIgnoredBytes == 0) { - bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); + bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); } else { - bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = 0; } } -static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) +static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) { - DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { - drflac__update_crc16(bs); + MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + ma_dr_flac__update_crc16(bs); } else { - bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } return bs->crc16; } #endif -static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) +static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) { size_t bytesRead; size_t alignedL1LineCount; - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; + return MA_TRUE; } if (bs->unalignedByteCount > 0) { - return DRFLAC_FALSE; + return MA_FALSE; } - bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); + bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); bs->nextL2Line = 0; - if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { + if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; + return MA_TRUE; } - alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); if (bs->unalignedByteCount > 0) { bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; } if (alignedL1LineCount > 0) { - size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; size_t i; for (i = alignedL1LineCount; i > 0; --i) { bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; } - bs->nextL2Line = (drflac_uint32)offset; + bs->nextL2Line = (ma_uint32)offset; bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; + return MA_TRUE; } else { - bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); - return DRFLAC_FALSE; + bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); + return MA_FALSE; } } -static drflac_bool32 drflac__reload_cache(drflac_bs* bs) +static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) { size_t bytesRead; -#ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - if (drflac__reload_l1_cache_from_l2(bs)) { - bs->cache = drflac__be2host__cache_line(bs->cache); + if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { + bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); bs->consumedBits = 0; -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif - return DRFLAC_TRUE; + return MA_TRUE; } bytesRead = bs->unalignedByteCount; if (bytesRead == 0) { - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); - return DRFLAC_FALSE; + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + return MA_FALSE; } - DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; - bs->cache = drflac__be2host__cache_line(bs->unalignedCache); - bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); + MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); + bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); bs->unalignedByteCount = 0; -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs->cache >> bs->consumedBits; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; #endif - return DRFLAC_TRUE; + return MA_TRUE; } -static void drflac__reset_cache(drflac_bs* bs) +static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) { - bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; bs->unalignedByteCount = 0; bs->unalignedCache = 0; -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = 0; bs->crc16CacheIgnoredBytes = 0; #endif } -static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) +static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) { - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResultOut != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 32); - if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResultOut != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 32); + if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } } - if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { -#ifdef DRFLAC_64BIT - *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { +#ifdef MA_64BIT + *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; #else - if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; } else { - *pResultOut = (drflac_uint32)bs->cache; - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + *pResultOut = (ma_uint32)bs->cache; + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; } #endif - return DRFLAC_TRUE; + return MA_TRUE; } else { - drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); - drflac_uint32 bitCountLo = bitCount - bitCountHi; - drflac_uint32 resultHi; - DRFLAC_ASSERT(bitCountHi > 0); - DRFLAC_ASSERT(bitCountHi < 32); - resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + ma_uint32 bitCountLo = bitCount - bitCountHi; + ma_uint32 resultHi; + MA_DR_FLAC_ASSERT(bitCountHi > 0); + MA_DR_FLAC_ASSERT(bitCountHi < 32); + resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } - *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; - return DRFLAC_TRUE; + return MA_TRUE; } } -static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) +static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) { - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 32); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 32); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; } if (bitCount < 32) { - drflac_uint32 signbit; + ma_uint32 signbit; signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; } - *pResult = (drflac_int32)result; - return DRFLAC_TRUE; + *pResult = (ma_int32)result; + return MA_TRUE; } -#ifdef DRFLAC_64BIT -static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) +#ifdef MA_64BIT +static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) { - drflac_uint32 resultHi; - drflac_uint32 resultLo; - DRFLAC_ASSERT(bitCount <= 64); - DRFLAC_ASSERT(bitCount > 32); - if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { - return DRFLAC_FALSE; + ma_uint32 resultHi; + ma_uint32 resultLo; + MA_DR_FLAC_ASSERT(bitCount <= 64); + MA_DR_FLAC_ASSERT(bitCount > 32); + if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { + return MA_FALSE; } - if (!drflac__read_uint32(bs, 32, &resultLo)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { + return MA_FALSE; } - *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); - return DRFLAC_TRUE; + *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); + return MA_TRUE; } #endif #if 0 -static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) +static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) { - drflac_uint64 result; - drflac_uint64 signbit; - DRFLAC_ASSERT(bitCount <= 64); - if (!drflac__read_uint64(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_uint64 result; + ma_uint64 signbit; + MA_DR_FLAC_ASSERT(bitCount <= 64); + if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { + return MA_FALSE; } signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; - *pResultOut = (drflac_int64)result; - return DRFLAC_TRUE; + *pResultOut = (ma_int64)result; + return MA_TRUE; } #endif -static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) +static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) { - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 16); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 16); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; } - *pResult = (drflac_uint16)result; - return DRFLAC_TRUE; + *pResult = (ma_uint16)result; + return MA_TRUE; } #if 0 -static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) +static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) { - drflac_int32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 16); - if (!drflac__read_int32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_int32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 16); + if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { + return MA_FALSE; } - *pResult = (drflac_int16)result; - return DRFLAC_TRUE; + *pResult = (ma_int16)result; + return MA_TRUE; } #endif -static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) +static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) { - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 8); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 8); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; } - *pResult = (drflac_uint8)result; - return DRFLAC_TRUE; + *pResult = (ma_uint8)result; + return MA_TRUE; } -static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) +static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) { - drflac_int32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 8); - if (!drflac__read_int32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_int32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 8); + if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { + return MA_FALSE; } - *pResult = (drflac_int8)result; - return DRFLAC_TRUE; + *pResult = (ma_int8)result; + return MA_TRUE; } -static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) +static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) { - if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (drflac_uint32)bitsToSeek; + if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (ma_uint32)bitsToSeek; bs->cache <<= bitsToSeek; - return DRFLAC_TRUE; + return MA_TRUE; } else { - bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); bs->cache = 0; -#ifdef DRFLAC_64BIT - while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - drflac_uint64 bin; - if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return DRFLAC_FALSE; +#ifdef MA_64BIT + while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + ma_uint64 bin; + if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return MA_FALSE; } - bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); } #else - while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - drflac_uint32 bin; - if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return DRFLAC_FALSE; + while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + ma_uint32 bin; + if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return MA_FALSE; } - bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); } #endif while (bitsToSeek >= 8) { - drflac_uint8 bin; - if (!drflac__read_uint8(bs, 8, &bin)) { - return DRFLAC_FALSE; + ma_uint8 bin; + if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { + return MA_FALSE; } bitsToSeek -= 8; } if (bitsToSeek > 0) { - drflac_uint8 bin; - if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { - return DRFLAC_FALSE; + ma_uint8 bin; + if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { + return MA_FALSE; } bitsToSeek = 0; } - DRFLAC_ASSERT(bitsToSeek == 0); - return DRFLAC_TRUE; + MA_DR_FLAC_ASSERT(bitsToSeek == 0); + return MA_TRUE; } } -static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) +static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) { - DRFLAC_ASSERT(bs != NULL); - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return DRFLAC_FALSE; + MA_DR_FLAC_ASSERT(bs != NULL); + if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return MA_FALSE; } for (;;) { - drflac_uint8 hi; -#ifndef DR_FLAC_NO_CRC - drflac__reset_crc16(bs); + ma_uint8 hi; +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__reset_crc16(bs); #endif - if (!drflac__read_uint8(bs, 8, &hi)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { + return MA_FALSE; } if (hi == 0xFF) { - drflac_uint8 lo; - if (!drflac__read_uint8(bs, 6, &lo)) { - return DRFLAC_FALSE; + ma_uint8 lo; + if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { + return MA_FALSE; } if (lo == 0x3E) { - return DRFLAC_TRUE; + return MA_TRUE; } else { - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return MA_FALSE; } } } } } -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) -#define DRFLAC_IMPLEMENT_CLZ_LZCNT +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) -#define DRFLAC_IMPLEMENT_CLZ_MSVC +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) +#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC #endif #if defined(__WATCOMC__) && defined(__386__) -#define DRFLAC_IMPLEMENT_CLZ_WATCOM +#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM #endif #ifdef __MRC__ #include -#define DRFLAC_IMPLEMENT_CLZ_MRC +#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC #endif -static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) +static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) { - drflac_uint32 n; - static drflac_uint32 clz_table_4[] = { + ma_uint32 n; + static ma_uint32 clz_table_4[] = { 0, 4, 3, 3, @@ -82836,11 +83008,11 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) } n = clz_table_4[x >> (sizeof(x)*8 - 4)]; if (n == 0) { -#ifdef DRFLAC_64BIT - if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } - if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } - if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } - if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } +#ifdef MA_64BIT + if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } + if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } + if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } + if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } #else if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } @@ -82850,52 +83022,52 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) } return n - 1; } -#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT -static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT +static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) { -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - return DRFLAC_TRUE; +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + return MA_TRUE; #elif defined(__MRC__) - return DRFLAC_TRUE; + return MA_TRUE; #else - #ifdef DRFLAC_HAS_LZCNT_INTRINSIC - return drflac__gIsLZCNTSupported; + #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC + return ma_dr_flac__gIsLZCNTSupported; #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #endif } -static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) +static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) { #if defined(_MSC_VER) - #ifdef DRFLAC_64BIT - return (drflac_uint32)__lzcnt64(x); + #ifdef MA_64BIT + return (ma_uint32)__lzcnt64(x); #else - return (drflac_uint32)__lzcnt(x); + return (ma_uint32)__lzcnt(x); #endif #else #if defined(__GNUC__) || defined(__clang__) - #if defined(DRFLAC_X64) + #if defined(MA_X64) { - drflac_uint64 r; + ma_uint64 r; __asm__ __volatile__ ( "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); - return (drflac_uint32)r; + return (ma_uint32)r; } - #elif defined(DRFLAC_X86) + #elif defined(MA_X86) { - drflac_uint32 r; + ma_uint32 r; __asm__ __volatile__ ( "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return r; } - #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT) + #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(MA_64BIT) { unsigned int r; __asm__ __volatile__ ( - #if defined(DRFLAC_64BIT) + #if defined(MA_64BIT) "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) #else "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) @@ -82907,10 +83079,10 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) if (x == 0) { return sizeof(x)*8; } - #ifdef DRFLAC_64BIT - return (drflac_uint32)__builtin_clzll((drflac_uint64)x); + #ifdef MA_64BIT + return (ma_uint32)__builtin_clzll((ma_uint64)x); #else - return (drflac_uint32)__builtin_clzl((drflac_uint32)x); + return (ma_uint32)__builtin_clzl((ma_uint32)x); #endif #endif #else @@ -82919,15 +83091,15 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) #endif } #endif -#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC #include -static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) +static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) { - drflac_uint32 n; + ma_uint32 n; if (x == 0) { return sizeof(x)*8; } -#ifdef DRFLAC_64BIT +#ifdef MA_64BIT _BitScanReverse64((unsigned long*)&n, x); #else _BitScanReverse((unsigned long*)&n, x); @@ -82935,16 +83107,16 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) return sizeof(x)*8 - n - 1; } #endif -#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM -static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); -#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT -#pragma aux drflac__clz_watcom_lzcnt = \ +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM +static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ "db 0F3h, 0Fh, 0BDh, 0C0h" \ parm [eax] \ value [eax] \ modify nomemory; #else -#pragma aux drflac__clz_watcom = \ +#pragma aux ma_dr_flac__clz_watcom = \ "bsr eax, eax" \ "xor eax, 31" \ parm [eax] nomemory \ @@ -82952,103 +83124,103 @@ static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); modify exact [eax] nomemory; #endif #endif -static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) +static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) { -#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT - if (drflac__is_lzcnt_supported()) { - return drflac__clz_lzcnt(x); +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT + if (ma_dr_flac__is_lzcnt_supported()) { + return ma_dr_flac__clz_lzcnt(x); } else #endif { -#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC - return drflac__clz_msvc(x); -#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) - return drflac__clz_watcom_lzcnt(x); -#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) - return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC + return ma_dr_flac__clz_msvc(x); +#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return ma_dr_flac__clz_watcom_lzcnt(x); +#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) + return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); #elif defined(__MRC__) return __cntlzw(x); #else - return drflac__clz_software(x); + return ma_dr_flac__clz_software(x); #endif } } -static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) { - drflac_uint32 zeroCounter = 0; - drflac_uint32 setBitOffsetPlus1; + ma_uint32 zeroCounter = 0; + ma_uint32 setBitOffsetPlus1; while (bs->cache == 0) { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } } if (bs->cache == 1) { - *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } - setBitOffsetPlus1 = drflac__clz(bs->cache); + setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); setBitOffsetPlus1 += 1; - if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } bs->consumedBits += setBitOffsetPlus1; bs->cache <<= setBitOffsetPlus1; *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) +static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) { - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(offsetFromStart > 0); + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(offsetFromStart > 0); if (offsetFromStart > 0x7FFFFFFF) { - drflac_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + ma_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } bytesRemaining -= 0x7FFFFFFF; while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } bytesRemaining -= 0x7FFFFFFF; } if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } } } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } } - drflac__reset_cache(bs); - return DRFLAC_TRUE; + ma_dr_flac__reset_cache(bs); + return MA_TRUE; } -static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) +static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) { - drflac_uint8 crc; - drflac_uint64 result; - drflac_uint8 utf8[7] = {0}; + ma_uint8 crc; + ma_uint64 result; + ma_uint8 utf8[7] = {0}; int byteCount; int i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pNumberOut != NULL); - DRFLAC_ASSERT(pCRCOut != NULL); + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pNumberOut != NULL); + MA_DR_FLAC_ASSERT(pCRCOut != NULL); crc = *pCRCOut; - if (!drflac__read_uint8(bs, 8, utf8)) { + if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { *pNumberOut = 0; - return DRFLAC_AT_END; + return MA_AT_END; } - crc = drflac_crc8(crc, utf8[0], 8); + crc = ma_dr_flac_crc8(crc, utf8[0], 8); if ((utf8[0] & 0x80) == 0) { *pNumberOut = utf8[0]; *pCRCOut = crc; - return DRFLAC_SUCCESS; + return MA_SUCCESS; } if ((utf8[0] & 0xE0) == 0xC0) { byteCount = 2; @@ -83064,26 +83236,26 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 byteCount = 7; } else { *pNumberOut = 0; - return DRFLAC_CRC_MISMATCH; + return MA_CRC_MISMATCH; } - DRFLAC_ASSERT(byteCount > 1); - result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + MA_DR_FLAC_ASSERT(byteCount > 1); + result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); for (i = 1; i < byteCount; ++i) { - if (!drflac__read_uint8(bs, 8, utf8 + i)) { + if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { *pNumberOut = 0; - return DRFLAC_AT_END; + return MA_AT_END; } - crc = drflac_crc8(crc, utf8[i], 8); + crc = ma_dr_flac_crc8(crc, utf8[i], 8); result = (result << 6) | (utf8[i] & 0x3F); } *pNumberOut = result; *pCRCOut = crc; - return DRFLAC_SUCCESS; + return MA_SUCCESS; } -static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) +static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) { #if 1 - drflac_uint32 result = 0; + ma_uint32 result = 0; while (x > 0) { result += 1; x >>= 1; @@ -83091,17 +83263,17 @@ static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) return result; #endif } -static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) +static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) { - return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; + return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif -static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) { - drflac_int32 prediction = 0; - DRFLAC_ASSERT(order <= 32); + ma_int32 prediction = 0; + MA_DR_FLAC_ASSERT(order <= 32); switch (order) { case 32: prediction += coefficients[31] * pDecodedSamples[-32]; @@ -83137,188 +83309,188 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; } - return (drflac_int32)(prediction >> shift); + return (ma_int32)(prediction >> shift); } -static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) { - drflac_int64 prediction; - DRFLAC_ASSERT(order <= 32); -#ifndef DRFLAC_64BIT + ma_int64 prediction; + MA_DR_FLAC_ASSERT(order <= 32); +#ifndef MA_64BIT if (order == 8) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; } else if (order == 7) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; } else if (order == 3) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; } else if (order == 6) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; } else if (order == 5) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; } else if (order == 4) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; } else if (order == 12) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; - prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; } else if (order == 2) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; } else if (order == 1) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; } else if (order == 10) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; } else if (order == 9) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; } else if (order == 11) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; } else { int j; prediction = 0; for (j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; + prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; } } #endif -#ifdef DRFLAC_64BIT +#ifdef MA_64BIT prediction = 0; switch (order) { - case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; + case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; } #endif - return (drflac_int32)(prediction >> shift); + return (ma_int32)(prediction >> shift); } #if 0 -static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { - drflac_uint32 zeroCounter = 0; + ma_uint32 zeroCounter = 0; for (;;) { - drflac_uint8 bit; - if (!drflac__read_uint8(bs, 1, &bit)) { - return DRFLAC_FALSE; + ma_uint8 bit; + if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { + return MA_FALSE; } if (bit == 0) { zeroCounter += 1; @@ -83326,10 +83498,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drfla break; } } - drflac_uint32 decodedRice; + ma_uint32 decodedRice; if (riceParam > 0) { - if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { + return MA_FALSE; } } else { decodedRice = 0; @@ -83340,24 +83512,24 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drfla } else { decodedRice = (decodedRice >> 1); } - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } - return DRFLAC_TRUE; + return MA_TRUE; } #endif #if 0 -static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) { - drflac_uint32 zeroCounter = 0; - drflac_uint32 decodedRice; + ma_uint32 zeroCounter = 0; + ma_uint32 decodedRice; for (;;) { - drflac_uint8 bit; - if (!drflac__read_uint8(bs, 1, &bit)) { - return DRFLAC_FALSE; + ma_uint8 bit; + if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { + return MA_FALSE; } if (bit == 0) { zeroCounter += 1; @@ -83366,142 +83538,142 @@ static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_ui } } if (riceParam > 0) { - if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { + return MA_FALSE; } } else { decodedRice = 0; } *pZeroCounterOut = zeroCounter; *pRiceParamPartOut = decodedRice; - return DRFLAC_TRUE; + return MA_TRUE; } #endif #if 0 -static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) { - drflac_cache_t riceParamMask; - drflac_uint32 zeroCounter; - drflac_uint32 setBitOffsetPlus1; - drflac_uint32 riceParamPart; - drflac_uint32 riceLength; - DRFLAC_ASSERT(riceParam > 0); - riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); + ma_dr_flac_cache_t riceParamMask; + ma_uint32 zeroCounter; + ma_uint32 setBitOffsetPlus1; + ma_uint32 riceParamPart; + ma_uint32 riceLength; + MA_DR_FLAC_ASSERT(riceParam > 0); + riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); zeroCounter = 0; while (bs->cache == 0) { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } } - setBitOffsetPlus1 = drflac__clz(bs->cache); + setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); zeroCounter += setBitOffsetPlus1; setBitOffsetPlus1 += 1; riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); + if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); bs->consumedBits += riceLength; bs->cache <<= riceLength; } else { - drflac_uint32 bitCountLo; - drflac_cache_t resultHi; + ma_uint32 bitCountLo; + ma_dr_flac_cache_t resultHi; bs->consumedBits += riceLength; - bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); - bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); - resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { -#ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); + bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs->consumedBits = 0; -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } } - riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); + riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; } pZeroCounterOut[0] = zeroCounter; pRiceParamPartOut[0] = riceParamPart; - return DRFLAC_TRUE; + return MA_TRUE; } #endif -static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) { - drflac_uint32 riceParamPlus1 = riceParam + 1; - drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); - drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - drflac_cache_t bs_cache = bs->cache; - drflac_uint32 bs_consumedBits = bs->consumedBits; - drflac_uint32 lzcount = drflac__clz(bs_cache); + ma_uint32 riceParamPlus1 = riceParam + 1; + ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); + ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + ma_dr_flac_cache_t bs_cache = bs->cache; + ma_uint32 bs_consumedBits = bs->consumedBits; + ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { pZeroCounterOut[0] = lzcount; extract_rice_param_part: bs_cache <<= lzcount; bs_consumedBits += lzcount; if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); + pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { - drflac_uint32 riceParamPartHi; - drflac_uint32 riceParamPartLo; - drflac_uint32 riceParamPartLoBitCount; - riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); + ma_uint32 riceParamPartHi; + ma_uint32 riceParamPartLo; + ma_uint32 riceParamPartLoBitCount; + riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; - #ifndef DR_FLAC_NO_CRC + #ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } - riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); + riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; bs_cache <<= riceParamPartLoBitCount; } } else { - drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); + ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); for (;;) { - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; - #ifndef DR_FLAC_NO_CRC + #ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } - lzcount = drflac__clz(bs_cache); + lzcount = ma_dr_flac__clz(bs_cache); zeroCounter += lzcount; if (lzcount < sizeof(bs_cache)*8) { break; @@ -83512,15 +83684,15 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drf } bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; - return DRFLAC_TRUE; + return MA_TRUE; } -static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) +static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) { - drflac_uint32 riceParamPlus1 = riceParam + 1; - drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - drflac_cache_t bs_cache = bs->cache; - drflac_uint32 bs_consumedBits = bs->consumedBits; - drflac_uint32 lzcount = drflac__clz(bs_cache); + ma_uint32 riceParamPlus1 = riceParam + 1; + ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + ma_dr_flac_cache_t bs_cache = bs->cache; + ma_uint32 bs_consumedBits = bs->consumedBits; + ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { extract_rice_param_part: bs_cache <<= lzcount; @@ -83529,23 +83701,23 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { - drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; - #ifndef DR_FLAC_NO_CRC + #ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; @@ -83554,23 +83726,23 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac } } else { for (;;) { - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; - #ifndef DR_FLAC_NO_CRC + #ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } - lzcount = drflac__clz(bs_cache); + lzcount = ma_dr_flac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { break; } @@ -83579,26 +83751,26 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac } bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { - drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - drflac_uint32 zeroCountPart0; - drflac_uint32 riceParamPart0; - drflac_uint32 riceParamMask; - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + ma_uint32 zeroCountPart0; + ma_uint32 riceParamPart0; + ma_uint32 riceParamMask; + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); (void)bitsPerSample; (void)order; (void)shift; (void)coefficients; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask = (ma_uint32)~((~0UL) << riceParam); i = 0; while (i < count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return MA_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); @@ -83606,36 +83778,36 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorde pSamplesOut[i] = riceParamPart0; i += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - drflac_uint32 zeroCountPart0 = 0; - drflac_uint32 zeroCountPart1 = 0; - drflac_uint32 zeroCountPart2 = 0; - drflac_uint32 zeroCountPart3 = 0; - drflac_uint32 riceParamPart0 = 0; - drflac_uint32 riceParamPart1 = 0; - drflac_uint32 riceParamPart2 = 0; - drflac_uint32 riceParamPart3 = 0; - drflac_uint32 riceParamMask; - const drflac_int32* pSamplesOutEnd; - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + ma_uint32 zeroCountPart0 = 0; + ma_uint32 zeroCountPart1 = 0; + ma_uint32 zeroCountPart2 = 0; + ma_uint32 zeroCountPart3 = 0; + ma_uint32 riceParamPart0 = 0; + ma_uint32 riceParamPart1 = 0; + ma_uint32 riceParamPart2 = 0; + ma_uint32 riceParamPart3 = 0; + ma_uint32 riceParamMask; + const ma_int32* pSamplesOutEnd; + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder == 0) { - return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask = (ma_uint32)~((~0UL) << riceParam); pSamplesOutEnd = pSamplesOut + (count & ~3); - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { while (pSamplesOut < pSamplesOutEnd) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return MA_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; @@ -83649,19 +83821,19 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } else { while (pSamplesOut < pSamplesOutEnd) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return MA_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; @@ -83675,33 +83847,33 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } i = (count & ~3); while (i < count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return MA_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } else { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } i += 1; pSamplesOut += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) { __m128i r; r = _mm_packs_epi32(a, b); @@ -83711,42 +83883,42 @@ static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m12 return r; } #endif -#if defined(DRFLAC_SUPPORT_SSE41) -static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) +#if defined(MA_DR_FLAC_SUPPORT_SSE41) +static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) { return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); } -static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) +static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) { __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); return _mm_add_epi32(x64, x32); } -static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) +static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) { return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); } -static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) +static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) { __m128i lo = _mm_srli_epi64(x, count); __m128i hi = _mm_srai_epi32(x, count); hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); return _mm_or_si128(lo, hi); } -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts0 = 0; - drflac_uint32 zeroCountParts1 = 0; - drflac_uint32 zeroCountParts2 = 0; - drflac_uint32 zeroCountParts3 = 0; - drflac_uint32 riceParamParts0 = 0; - drflac_uint32 riceParamParts1 = 0; - drflac_uint32 riceParamParts2 = 0; - drflac_uint32 riceParamParts3 = 0; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts0 = 0; + ma_uint32 zeroCountParts1 = 0; + ma_uint32 zeroCountParts2 = 0; + ma_uint32 zeroCountParts3 = 0; + ma_uint32 riceParamParts0 = 0; + ma_uint32 riceParamParts1 = 0; + ma_uint32 riceParamParts2 = 0; + ma_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; @@ -83754,8 +83926,8 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac __m128i samples128_4; __m128i samples128_8; __m128i riceParamMask128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); coefficients128_0 = _mm_setzero_si128(); coefficients128_4 = _mm_setzero_si128(); @@ -83809,39 +83981,39 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac #else switch (order) { - case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif while (pDecodedSamples < pDecodedSamplesEnd) { __m128i prediction128; __m128i zeroCountPart128; __m128i riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return MA_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); - prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); @@ -83851,7 +84023,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); @@ -83863,7 +84035,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); @@ -83877,32 +84049,32 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac } i = (count & ~3); while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return MA_FALSE; } riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts0 = 0; - drflac_uint32 zeroCountParts1 = 0; - drflac_uint32 zeroCountParts2 = 0; - drflac_uint32 zeroCountParts3 = 0; - drflac_uint32 riceParamParts0 = 0; - drflac_uint32 riceParamParts1 = 0; - drflac_uint32 riceParamParts2 = 0; - drflac_uint32 riceParamParts3 = 0; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts0 = 0; + ma_uint32 zeroCountParts1 = 0; + ma_uint32 zeroCountParts2 = 0; + ma_uint32 zeroCountParts3 = 0; + ma_uint32 riceParamParts0 = 0; + ma_uint32 riceParamParts1 = 0; + ma_uint32 riceParamParts2 = 0; + ma_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; @@ -83911,9 +84083,9 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac __m128i samples128_8; __m128i prediction128; __m128i riceParamMask128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - DRFLAC_ASSERT(order <= 12); - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + MA_DR_FLAC_ASSERT(order <= 12); + riceParamMask = (ma_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); prediction128 = _mm_setzero_si128(); coefficients128_0 = _mm_setzero_si128(); @@ -83968,34 +84140,34 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac #else switch (order) { - case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif while (pDecodedSamples < pDecodedSamplesEnd) { __m128i zeroCountPart128; __m128i riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return MA_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); for (i = 0; i < 4; i += 1) { prediction128 = _mm_xor_si128(prediction128, prediction128); switch (order) @@ -84013,8 +84185,8 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac case 2: case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); } - prediction128 = drflac__mm_hadd_epi64(prediction128); - prediction128 = drflac__mm_srai_epi64(prediction128, shift); + prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); + prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); @@ -84026,103 +84198,103 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac } i = (count & ~3); while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return MA_FALSE; } riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder > 0 && lpcOrder <= 12) { - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) { vst1q_s32(p+0, x.val[0]); vst1q_s32(p+4, x.val[1]); } -static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) +static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) { vst1q_u32(p+0, x.val[0]); vst1q_u32(p+4, x.val[1]); } -static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) +static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) { vst1q_f32(p+0, x.val[0]); vst1q_f32(p+4, x.val[1]); } -static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) +static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) { vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); } -static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) +static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) { vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); } -static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) +static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) { - drflac_int32 x[4]; + ma_int32 x[4]; x[3] = x3; x[2] = x2; x[1] = x1; x[0] = x0; return vld1q_s32(x); } -static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) +static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) { return vextq_s32(b, a, 1); } -static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) +static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) { return vextq_u32(b, a, 1); } -static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) +static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) { int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); return vpadd_s32(r, r); } -static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) +static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) { return vadd_s64(vget_high_s64(x), vget_low_s64(x)); } -static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) +static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) { return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); } -static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) +static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) { return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); } -static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) +static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) { return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); } -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts[4]; - drflac_uint32 riceParamParts[4]; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts[4]; + ma_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; @@ -84133,16 +84305,16 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ int32x4_t riceParam128; int32x2_t shift64; uint32x4_t one128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s32(-shift); one128 = vdupq_n_u32(1); { int runningOrder = order; - drflac_int32 tempC[4] = {0, 0, 0, 0}; - drflac_int32 tempS[4] = {0, 0, 0, 0}; + ma_int32 tempC[4] = {0, 0, 0, 0}; + ma_int32 tempS[4] = {0, 0, 0, 0}; if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); @@ -84185,58 +84357,58 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ samples128_8 = vld1q_s32(tempS); runningOrder = 0; } - coefficients128_0 = drflac__vrevq_s32(coefficients128_0); - coefficients128_4 = drflac__vrevq_s32(coefficients128_4); - coefficients128_8 = drflac__vrevq_s32(coefficients128_8); + coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); + coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); + coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { int32x4_t prediction128; int32x2_t prediction64; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return MA_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else if (order <= 8) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_8, samples128_8); prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } vst1q_s32(pDecodedSamples, samples128_0); @@ -84244,26 +84416,26 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ } i = (count & ~3); while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return MA_FALSE; } riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts[4]; - drflac_uint32 riceParamParts[4]; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts[4]; + ma_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; @@ -84277,16 +84449,16 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ int64x2_t prediction128 = { 0 }; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s64(-shift); one128 = vdupq_n_u32(1); { int runningOrder = order; - drflac_int32 tempC[4] = {0, 0, 0, 0}; - drflac_int32 tempS[4] = {0, 0, 0, 0}; + ma_int32 tempC[4] = {0, 0, 0, 0}; + ma_int32 tempS[4] = {0, 0, 0, 0}; if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); @@ -84329,22 +84501,22 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ samples128_8 = vld1q_s32(tempS); runningOrder = 0; } - coefficients128_0 = drflac__vrevq_s32(coefficients128_0); - coefficients128_4 = drflac__vrevq_s32(coefficients128_4); - coefficients128_8 = drflac__vrevq_s32(coefficients128_8); + coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); + coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); + coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return MA_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); for (i = 0; i < 4; i += 1) { int64x1_t prediction64; prediction128 = veorq_s64(prediction128, prediction128); @@ -84363,156 +84535,156 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ case 2: case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); } - prediction64 = drflac__vhaddq_s64(prediction128); + prediction64 = ma_dr_flac__vhaddq_s64(prediction128); prediction64 = vshl_s64(prediction64, shift64); prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); - samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } vst1q_s32(pDecodedSamples, samples128_0); pDecodedSamples += 4; } i = (count & ~3); while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return MA_FALSE; } riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder > 0 && lpcOrder <= 12) { - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif -static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { -#if defined(DRFLAC_SUPPORT_SSE41) - if (drflac__gIsSSE41Supported) { - return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); +#if defined(MA_DR_FLAC_SUPPORT_SSE41) + if (ma_dr_flac__gIsSSE41Supported) { + return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported) { - return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported) { + return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #endif { #if 0 - return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #else - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #endif } } -static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) +static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) { - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); for (i = 0; i < count; ++i) { - if (!drflac__seek_rice_parts(bs, riceParam)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { + return MA_FALSE; } } - return DRFLAC_TRUE; + return MA_TRUE; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif -static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(unencodedBitsPerSample <= 31); - DRFLAC_ASSERT(pSamplesOut != NULL); + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { if (unencodedBitsPerSample > 0) { - if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return MA_FALSE; } } else { pSamplesOut[i] = 0; } - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) { - drflac_uint8 residualMethod; - drflac_uint8 partitionOrder; - drflac_uint32 samplesInPartition; - drflac_uint32 partitionsRemaining; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(blockSize != 0); - DRFLAC_ASSERT(pDecodedSamples != NULL); - if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DRFLAC_FALSE; + ma_uint8 residualMethod; + ma_uint8 partitionOrder; + ma_uint32 samplesInPartition; + ma_uint32 partitionsRemaining; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(blockSize != 0); + MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); + if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { + return MA_FALSE; } - if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DRFLAC_FALSE; + if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return MA_FALSE; } pDecodedSamples += lpcOrder; - if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { + return MA_FALSE; } if (partitionOrder > 8) { - return DRFLAC_FALSE; + return MA_FALSE; } if ((blockSize / (1 << partitionOrder)) < lpcOrder) { - return DRFLAC_FALSE; + return MA_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; partitionsRemaining = (1 << partitionOrder); for (;;) { - drflac_uint8 riceParam = 0; - if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DRFLAC_FALSE; + ma_uint8 riceParam = 0; + if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { + return MA_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } - } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DRFLAC_FALSE; + } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { + return MA_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { - if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; } } else { - drflac_uint8 unencodedBitsPerSample = 0; - if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DRFLAC_FALSE; + ma_uint8 unencodedBitsPerSample = 0; + if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return MA_FALSE; } - if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; } } pDecodedSamples += samplesInPartition; @@ -84524,62 +84696,62 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ samplesInPartition = blockSize / (1 << partitionOrder); } } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) +static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) { - drflac_uint8 residualMethod; - drflac_uint8 partitionOrder; - drflac_uint32 samplesInPartition; - drflac_uint32 partitionsRemaining; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(blockSize != 0); - if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DRFLAC_FALSE; + ma_uint8 residualMethod; + ma_uint8 partitionOrder; + ma_uint32 samplesInPartition; + ma_uint32 partitionsRemaining; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(blockSize != 0); + if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { + return MA_FALSE; } - if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DRFLAC_FALSE; + if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return MA_FALSE; } - if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { + return MA_FALSE; } if (partitionOrder > 8) { - return DRFLAC_FALSE; + return MA_FALSE; } if ((blockSize / (1 << partitionOrder)) <= order) { - return DRFLAC_FALSE; + return MA_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - order; partitionsRemaining = (1 << partitionOrder); for (;;) { - drflac_uint8 riceParam = 0; - if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DRFLAC_FALSE; + ma_uint8 riceParam = 0; + if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { + return MA_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } - } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DRFLAC_FALSE; + } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { + return MA_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { - if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return MA_FALSE; } } else { - drflac_uint8 unencodedBitsPerSample = 0; - if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DRFLAC_FALSE; + ma_uint8 unencodedBitsPerSample = 0; + if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return MA_FALSE; } - if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return MA_FALSE; } } if (partitionsRemaining == 1) { @@ -84588,36 +84760,36 @@ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 partitionsRemaining -= 1; samplesInPartition = blockSize / (1 << partitionOrder); } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) { - drflac_uint32 i; - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; + ma_uint32 i; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; } for (i = 0; i < blockSize; ++i) { pDecodedSamples[i] = sample; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) { - drflac_uint32 i; + ma_uint32 i; for (i = 0; i < blockSize; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; } pDecodedSamples[i] = sample; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) { - drflac_uint32 i; - static drflac_int32 lpcCoefficientsTable[5][4] = { + ma_uint32 i; + static ma_int32 lpcCoefficientsTable[5][4] = { {0, 0, 0, 0}, {1, 0, 0, 0}, {2, -1, 0, 0}, @@ -84625,122 +84797,122 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 {4, -6, 4, -1} }; for (i = 0; i < lpcOrder; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; } pDecodedSamples[i] = sample; } - if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) { - drflac_uint8 i; - drflac_uint8 lpcPrecision; - drflac_int8 lpcShift; - drflac_int32 coefficients[32]; + ma_uint8 i; + ma_uint8 lpcPrecision; + ma_int8 lpcShift; + ma_int32 coefficients[32]; for (i = 0; i < lpcOrder; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DRFLAC_FALSE; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { + return MA_FALSE; } pDecodedSamples[i] = sample; } - if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { + return MA_FALSE; } if (lpcPrecision == 15) { - return DRFLAC_FALSE; + return MA_FALSE; } lpcPrecision += 1; - if (!drflac__read_int8(bs, 5, &lpcShift)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { + return MA_FALSE; } if (lpcShift < 0) { - return DRFLAC_FALSE; + return MA_FALSE; } - DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); + MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); for (i = 0; i < lpcOrder; ++i) { - if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { + return MA_FALSE; } } - if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) +static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) { - const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(header != NULL); + const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(header != NULL); for (;;) { - drflac_uint8 crc8 = 0xCE; - drflac_uint8 reserved = 0; - drflac_uint8 blockingStrategy = 0; - drflac_uint8 blockSize = 0; - drflac_uint8 sampleRate = 0; - drflac_uint8 channelAssignment = 0; - drflac_uint8 bitsPerSample = 0; - drflac_bool32 isVariableBlockSize; - if (!drflac__find_and_seek_to_next_sync_code(bs)) { - return DRFLAC_FALSE; + ma_uint8 crc8 = 0xCE; + ma_uint8 reserved = 0; + ma_uint8 blockingStrategy = 0; + ma_uint8 blockSize = 0; + ma_uint8 sampleRate = 0; + ma_uint8 channelAssignment = 0; + ma_uint8 bitsPerSample = 0; + ma_bool32 isVariableBlockSize; + if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { + return MA_FALSE; } - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { + return MA_FALSE; } if (reserved == 1) { continue; } - crc8 = drflac_crc8(crc8, reserved, 1); - if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, reserved, 1); + if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, blockingStrategy, 1); - if (!drflac__read_uint8(bs, 4, &blockSize)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); + if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { + return MA_FALSE; } if (blockSize == 0) { continue; } - crc8 = drflac_crc8(crc8, blockSize, 4); - if (!drflac__read_uint8(bs, 4, &sampleRate)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); + if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, sampleRate, 4); - if (!drflac__read_uint8(bs, 4, &channelAssignment)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); + if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { + return MA_FALSE; } if (channelAssignment > 10) { continue; } - crc8 = drflac_crc8(crc8, channelAssignment, 4); - if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); + if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { + return MA_FALSE; } if (bitsPerSample == 3 || bitsPerSample == 7) { continue; } - crc8 = drflac_crc8(crc8, bitsPerSample, 3); - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); + if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { + return MA_FALSE; } if (reserved == 1) { continue; } - crc8 = drflac_crc8(crc8, reserved, 1); + crc8 = ma_dr_flac_crc8(crc8, reserved, 1); isVariableBlockSize = blockingStrategy == 1; if (isVariableBlockSize) { - drflac_uint64 pcmFrameNumber; - drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_AT_END) { - return DRFLAC_FALSE; + ma_uint64 pcmFrameNumber; + ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); + if (result != MA_SUCCESS) { + if (result == MA_AT_END) { + return MA_FALSE; } else { continue; } @@ -84748,61 +84920,61 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u header->flacFrameNumber = 0; header->pcmFrameNumber = pcmFrameNumber; } else { - drflac_uint64 flacFrameNumber = 0; - drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_AT_END) { - return DRFLAC_FALSE; + ma_uint64 flacFrameNumber = 0; + ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); + if (result != MA_SUCCESS) { + if (result == MA_AT_END) { + return MA_FALSE; } else { continue; } } - header->flacFrameNumber = (drflac_uint32)flacFrameNumber; + header->flacFrameNumber = (ma_uint32)flacFrameNumber; header->pcmFrameNumber = 0; } - DRFLAC_ASSERT(blockSize > 0); + MA_DR_FLAC_ASSERT(blockSize > 0); if (blockSize == 1) { header->blockSizeInPCMFrames = 192; } else if (blockSize <= 5) { - DRFLAC_ASSERT(blockSize >= 2); + MA_DR_FLAC_ASSERT(blockSize >= 2); header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); } else if (blockSize == 6) { - if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); + crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); header->blockSizeInPCMFrames += 1; } else if (blockSize == 7) { - if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); + crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); if (header->blockSizeInPCMFrames == 0xFFFF) { - return DRFLAC_FALSE; + return MA_FALSE; } header->blockSizeInPCMFrames += 1; } else { - DRFLAC_ASSERT(blockSize >= 8); + MA_DR_FLAC_ASSERT(blockSize >= 8); header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); } if (sampleRate <= 11) { header->sampleRate = sampleRateTable[sampleRate]; } else if (sampleRate == 12) { - if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->sampleRate, 8); + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); header->sampleRate *= 1000; } else if (sampleRate == 13) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->sampleRate, 16); + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); } else if (sampleRate == 14) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->sampleRate, 16); + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); header->sampleRate *= 10; } else { continue; @@ -84813,286 +84985,286 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u header->bitsPerSample = streaminfoBitsPerSample; } if (header->bitsPerSample != streaminfoBitsPerSample) { - return DRFLAC_FALSE; + return MA_FALSE; } - if (!drflac__read_uint8(bs, 8, &header->crc8)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { + return MA_FALSE; } -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC if (header->crc8 != crc8) { continue; } #endif - return DRFLAC_TRUE; + return MA_TRUE; } } -static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) { - drflac_uint8 header; + ma_uint8 header; int type; - if (!drflac__read_uint8(bs, 8, &header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 8, &header)) { + return MA_FALSE; } if ((header & 0x80) != 0) { - return DRFLAC_FALSE; + return MA_FALSE; } type = (header & 0x7E) >> 1; if (type == 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; } else if (type == 1) { - pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; } else { if ((type & 0x20) != 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; } else if ((type & 0x08) != 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (ma_uint8)(type & 0x07); if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; pSubframe->lpcOrder = 0; } } else { - pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; } } - if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { - return DRFLAC_FALSE; + if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { + return MA_FALSE; } pSubframe->wastedBitsPerSample = 0; if ((header & 0x01) == 1) { unsigned int wastedBitsPerSample; - if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return MA_FALSE; } - pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; + pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) +static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) { - drflac_subframe* pSubframe; - drflac_uint32 subframeBitsPerSample; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(frame != NULL); + ma_dr_flac_subframe* pSubframe; + ma_uint32 subframeBitsPerSample; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { + return MA_FALSE; } subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (subframeBitsPerSample > 32) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return DRFLAC_FALSE; + return MA_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = pDecodedSamplesOut; switch (pSubframe->subframeType) { - case DRFLAC_SUBFRAME_CONSTANT: + case MA_DR_FLAC_SUBFRAME_CONSTANT: { - drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; - case DRFLAC_SUBFRAME_VERBATIM: + case MA_DR_FLAC_SUBFRAME_VERBATIM: { - drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; - case DRFLAC_SUBFRAME_FIXED: + case MA_DR_FLAC_SUBFRAME_FIXED: { - drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; - case DRFLAC_SUBFRAME_LPC: + case MA_DR_FLAC_SUBFRAME_LPC: { - drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; - default: return DRFLAC_FALSE; + default: return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) { - drflac_subframe* pSubframe; - drflac_uint32 subframeBitsPerSample; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(frame != NULL); + ma_dr_flac_subframe* pSubframe; + ma_uint32 subframeBitsPerSample; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { + return MA_FALSE; } subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return DRFLAC_FALSE; + return MA_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = NULL; switch (pSubframe->subframeType) { - case DRFLAC_SUBFRAME_CONSTANT: + case MA_DR_FLAC_SUBFRAME_CONSTANT: { - if (!drflac__seek_bits(bs, subframeBitsPerSample)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { + return MA_FALSE; } } break; - case DRFLAC_SUBFRAME_VERBATIM: + case MA_DR_FLAC_SUBFRAME_VERBATIM: { unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; } } break; - case DRFLAC_SUBFRAME_FIXED: + case MA_DR_FLAC_SUBFRAME_FIXED: { unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; } - if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return MA_FALSE; } } break; - case DRFLAC_SUBFRAME_LPC: + case MA_DR_FLAC_SUBFRAME_LPC: { - drflac_uint8 lpcPrecision; + ma_uint8 lpcPrecision; unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; } - if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { + return MA_FALSE; } if (lpcPrecision == 15) { - return DRFLAC_FALSE; + return MA_FALSE; } lpcPrecision += 1; bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; } - if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return MA_FALSE; } } break; - default: return DRFLAC_FALSE; + default: return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } -static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) +static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) { - drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; - DRFLAC_ASSERT(channelAssignment <= 10); + ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + MA_DR_FLAC_ASSERT(channelAssignment <= 10); return lookup[channelAssignment]; } -static drflac_result drflac__decode_flac_frame(drflac* pFlac) +static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) { int channelCount; int i; - drflac_uint8 paddingSizeInBits; - drflac_uint16 desiredCRC16; -#ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16; + ma_uint8 paddingSizeInBits; + ma_uint16 desiredCRC16; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint16 actualCRC16; #endif - DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); + MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { - return DRFLAC_ERROR; + return MA_ERROR; } - channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); if (channelCount != (int)pFlac->channels) { - return DRFLAC_ERROR; + return MA_ERROR; } for (i = 0; i < channelCount; ++i) { - if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { - return DRFLAC_ERROR; + if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { + return MA_ERROR; } } - paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); + paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); if (paddingSizeInBits > 0) { - drflac_uint8 padding = 0; - if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return DRFLAC_AT_END; + ma_uint8 padding = 0; + if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return MA_AT_END; } } -#ifndef DR_FLAC_NO_CRC - actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#ifndef MA_DR_FLAC_NO_CRC + actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); #endif - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_AT_END; + if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return MA_AT_END; } -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { - return DRFLAC_CRC_MISMATCH; + return MA_CRC_MISMATCH; } #endif pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - return DRFLAC_SUCCESS; + return MA_SUCCESS; } -static drflac_result drflac__seek_flac_frame(drflac* pFlac) +static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) { int channelCount; int i; - drflac_uint16 desiredCRC16; -#ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16; + ma_uint16 desiredCRC16; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint16 actualCRC16; #endif - channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); for (i = 0; i < channelCount; ++i) { - if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { - return DRFLAC_ERROR; + if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { + return MA_ERROR; } } - if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return DRFLAC_ERROR; + if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return MA_ERROR; } -#ifndef DR_FLAC_NO_CRC - actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#ifndef MA_DR_FLAC_NO_CRC + actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); #endif - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_AT_END; + if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return MA_AT_END; } -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { - return DRFLAC_CRC_MISMATCH; + return MA_CRC_MISMATCH; } #endif - return DRFLAC_SUCCESS; + return MA_SUCCESS; } -static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) +static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) { - DRFLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pFlac != NULL); for (;;) { - drflac_result result; - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + ma_result result; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } - result = drflac__decode_flac_frame(pFlac); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_CRC_MISMATCH) { + result = ma_dr_flac__decode_flac_frame(pFlac); + if (result != MA_SUCCESS) { + if (result == MA_CRC_MISMATCH) { continue; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } - return DRFLAC_TRUE; + return MA_TRUE; } } -static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) +static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) { - drflac_uint64 firstPCMFrame; - drflac_uint64 lastPCMFrame; - DRFLAC_ASSERT(pFlac != NULL); + ma_uint64 firstPCMFrame; + ma_uint64 lastPCMFrame; + MA_DR_FLAC_ASSERT(pFlac != NULL); firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; if (firstPCMFrame == 0) { - firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; + firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; } lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; if (lastPCMFrame > 0) { @@ -85105,32 +85277,32 @@ static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drf *pLastPCMFrame = lastPCMFrame; } } -static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) +static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) { - drflac_bool32 result; - DRFLAC_ASSERT(pFlac != NULL); - result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); - DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + ma_bool32 result; + MA_DR_FLAC_ASSERT(pFlac != NULL); + result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); + MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); pFlac->currentPCMFrame = 0; return result; } -static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) +static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) { - DRFLAC_ASSERT(pFlac != NULL); - return drflac__seek_flac_frame(pFlac); + MA_DR_FLAC_ASSERT(pFlac != NULL); + return ma_dr_flac__seek_flac_frame(pFlac); } -static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) +static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) { - drflac_uint64 pcmFramesRead = 0; + ma_uint64 pcmFramesRead = 0; while (pcmFramesToSeek > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { pcmFramesRead += pcmFramesToSeek; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; pcmFramesToSeek = 0; } else { pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; @@ -85142,107 +85314,107 @@ static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_ui pFlac->currentPCMFrame += pcmFramesRead; return pcmFramesRead; } -static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { - drflac_bool32 isMidFrame = DRFLAC_FALSE; - drflac_uint64 runningPCMFrameCount; - DRFLAC_ASSERT(pFlac != NULL); + ma_bool32 isMidFrame = MA_FALSE; + ma_uint64 runningPCMFrameCount; + MA_DR_FLAC_ASSERT(pFlac != NULL); if (pcmFrameIndex >= pFlac->currentPCMFrame) { runningPCMFrameCount = pFlac->currentPCMFrame; if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } else { - isMidFrame = DRFLAC_TRUE; + isMidFrame = MA_TRUE; } } else { runningPCMFrameCount = 0; - if (!drflac__seek_to_first_frame(pFlac)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_to_first_frame(pFlac)) { + return MA_FALSE; } - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } for (;;) { - drflac_uint64 pcmFrameCountInThisFLACFrame; - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + ma_uint64 pcmFrameCountInThisFLACFrame; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { goto next_iteration; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { if (!isMidFrame) { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { goto next_iteration; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = DRFLAC_FALSE; + isMidFrame = MA_FALSE; } if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return DRFLAC_TRUE; + return MA_TRUE; } } next_iteration: - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } } -#if !defined(DR_FLAC_NO_CRC) -#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f -static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) +#if !defined(MA_DR_FLAC_NO_CRC) +#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f +static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) { - DRFLAC_ASSERT(pFlac != NULL); - DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); - DRFLAC_ASSERT(targetByte >= rangeLo); - DRFLAC_ASSERT(targetByte <= rangeHi); + MA_DR_FLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); + MA_DR_FLAC_ASSERT(targetByte >= rangeLo); + MA_DR_FLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; for (;;) { - drflac_uint64 lastTargetByte = targetByte; - if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { + ma_uint64 lastTargetByte = targetByte; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { if (targetByte == 0) { - drflac__seek_to_first_frame(pFlac); - return DRFLAC_FALSE; + ma_dr_flac__seek_to_first_frame(pFlac); + return MA_FALSE; } targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { - DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); #if 1 - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { break; } #else - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { @@ -85251,48 +85423,48 @@ static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFla #endif } if(targetByte == lastTargetByte) { - return DRFLAC_FALSE; + return MA_FALSE; } } - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - DRFLAC_ASSERT(targetByte <= rangeHi); + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + MA_DR_FLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = targetByte; - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) +static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) { #if 0 - if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { - if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { - return DRFLAC_FALSE; + if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { + if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { + return MA_FALSE; } } #endif - return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; } -static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) { - drflac_uint64 targetByte; - drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; - drflac_uint64 pcmRangeHi = 0; - drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; - drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; - drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); + ma_uint64 targetByte; + ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; + ma_uint64 pcmRangeHi = 0; + ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; + ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; + ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } for (;;) { - if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { - drflac_uint64 newPCMRangeLo; - drflac_uint64 newPCMRangeHi; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); + if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { + ma_uint64 newPCMRangeLo; + ma_uint64 newPCMRangeHi; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); if (pcmRangeLo == newPCMRangeLo) { - if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { + if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { break; } - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return DRFLAC_TRUE; + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return MA_TRUE; } else { break; } @@ -85300,13 +85472,13 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p pcmRangeLo = newPCMRangeLo; pcmRangeHi = newPCMRangeHi; if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { - return DRFLAC_TRUE; + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { + return MA_TRUE; } else { break; } } else { - const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); + const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); if (pcmRangeLo > pcmFrameIndex) { byteRangeHi = lastSuccessfulSeekOffset; if (byteRangeLo > byteRangeHi) { @@ -85318,8 +85490,8 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p } } else { if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return DRFLAC_TRUE; + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return MA_TRUE; } else { break; } @@ -85328,7 +85500,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p if (byteRangeHi < byteRangeLo) { byteRangeHi = byteRangeLo; } - targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); + targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } @@ -85342,37 +85514,37 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p break; } } - drflac__seek_to_first_frame(pFlac); - return DRFLAC_FALSE; + ma_dr_flac__seek_to_first_frame(pFlac); + return MA_FALSE; } -static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { - drflac_uint64 byteRangeLo; - drflac_uint64 byteRangeHi; - drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { - return DRFLAC_FALSE; + ma_uint64 byteRangeLo; + ma_uint64 byteRangeHi; + ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { + return MA_FALSE; } if (pcmFrameIndex < seekForwardThreshold) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; } byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); + byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); } #endif -static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { - drflac_uint32 iClosestSeekpoint = 0; - drflac_bool32 isMidFrame = DRFLAC_FALSE; - drflac_uint64 runningPCMFrameCount; - drflac_uint32 iSeekpoint; - DRFLAC_ASSERT(pFlac != NULL); + ma_uint32 iClosestSeekpoint = 0; + ma_bool32 isMidFrame = MA_FALSE; + ma_uint64 runningPCMFrameCount; + ma_uint32 iSeekpoint; + MA_DR_FLAC_ASSERT(pFlac != NULL); if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { - return DRFLAC_FALSE; + return MA_FALSE; } for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { @@ -85381,31 +85553,31 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac iClosestSeekpoint = iSeekpoint; } if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { - return DRFLAC_FALSE; + return MA_FALSE; } -#if !defined(DR_FLAC_NO_CRC) +#if !defined(MA_DR_FLAC_NO_CRC) if (pFlac->totalPCMFrameCount > 0) { - drflac_uint64 byteRangeLo; - drflac_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + ma_uint64 byteRangeLo; + ma_uint64 byteRangeHi; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; if (iClosestSeekpoint < pFlac->seekpointCount-1) { - drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; + ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } - if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { + if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; } } - if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { - return DRFLAC_TRUE; + if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { + return MA_TRUE; } } } @@ -85414,173 +85586,173 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { runningPCMFrameCount = pFlac->currentPCMFrame; if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } else { - isMidFrame = DRFLAC_TRUE; + isMidFrame = MA_TRUE; } } else { runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + return MA_FALSE; } - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } for (;;) { - drflac_uint64 pcmFrameCountInThisFLACFrame; - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + ma_uint64 pcmFrameCountInThisFLACFrame; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { goto next_iteration; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { if (!isMidFrame) { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { goto next_iteration; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = DRFLAC_FALSE; + isMidFrame = MA_FALSE; } if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return DRFLAC_TRUE; + return MA_TRUE; } } next_iteration: - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } } -#ifndef DR_FLAC_NO_OGG +#ifndef MA_DR_FLAC_NO_OGG typedef struct { - drflac_uint8 capturePattern[4]; - drflac_uint8 structureVersion; - drflac_uint8 headerType; - drflac_uint64 granulePosition; - drflac_uint32 serialNumber; - drflac_uint32 sequenceNumber; - drflac_uint32 checksum; - drflac_uint8 segmentCount; - drflac_uint8 segmentTable[255]; -} drflac_ogg_page_header; + ma_uint8 capturePattern[4]; + ma_uint8 structureVersion; + ma_uint8 headerType; + ma_uint64 granulePosition; + ma_uint32 serialNumber; + ma_uint32 sequenceNumber; + ma_uint32 checksum; + ma_uint8 segmentCount; + ma_uint8 segmentTable[255]; +} ma_dr_flac_ogg_page_header; #endif typedef struct { - drflac_read_proc onRead; - drflac_seek_proc onSeek; - drflac_meta_proc onMeta; - drflac_container container; + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; + ma_dr_flac_meta_proc onMeta; + ma_dr_flac_container container; void* pUserData; void* pUserDataMD; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint64 totalPCMFrameCount; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint64 runningFilePos; - drflac_bool32 hasStreamInfoBlock; - drflac_bool32 hasMetadataBlocks; - drflac_bs bs; - drflac_frame_header firstFrameHeader; -#ifndef DR_FLAC_NO_OGG - drflac_uint32 oggSerial; - drflac_uint64 oggFirstBytePos; - drflac_ogg_page_header oggBosHeader; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint64 totalPCMFrameCount; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint64 runningFilePos; + ma_bool32 hasStreamInfoBlock; + ma_bool32 hasMetadataBlocks; + ma_dr_flac_bs bs; + ma_dr_flac_frame_header firstFrameHeader; +#ifndef MA_DR_FLAC_NO_OGG + ma_uint32 oggSerial; + ma_uint64 oggFirstBytePos; + ma_dr_flac_ogg_page_header oggBosHeader; #endif -} drflac_init_info; -static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +} ma_dr_flac_init_info; +static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) { - blockHeader = drflac__be2host_32(blockHeader); - *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); - *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); + blockHeader = ma_dr_flac__be2host_32(blockHeader); + *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); + *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); *blockSize = (blockHeader & 0x00FFFFFFUL); } -static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) { - drflac_uint32 blockHeader; + ma_uint32 blockHeader; *blockSize = 0; if (onRead(pUserData, &blockHeader, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } - drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return DRFLAC_TRUE; + ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return MA_TRUE; } -static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) { - drflac_uint32 blockSizes; - drflac_uint64 frameSizes = 0; - drflac_uint64 importantProps; - drflac_uint8 md5[16]; + ma_uint32 blockSizes; + ma_uint64 frameSizes = 0; + ma_uint64 importantProps; + ma_uint8 md5[16]; if (onRead(pUserData, &blockSizes, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, &frameSizes, 6) != 6) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, &importantProps, 8) != 8) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return DRFLAC_FALSE; + return MA_FALSE; } - blockSizes = drflac__be2host_32(blockSizes); - frameSizes = drflac__be2host_64(frameSizes); - importantProps = drflac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); - pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); - pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); - pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); - pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); - pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; - pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; - pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); - DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); - return DRFLAC_TRUE; + blockSizes = ma_dr_flac__be2host_32(blockSizes); + frameSizes = ma_dr_flac__be2host_64(frameSizes); + importantProps = ma_dr_flac__be2host_64(importantProps); + pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); + pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); + pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); + pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); + pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); + pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; + pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; + pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); + MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); + return MA_TRUE; } -static void* drflac__malloc_default(size_t sz, void* pUserData) +static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return DRFLAC_MALLOC(sz); + return MA_DR_FLAC_MALLOC(sz); } -static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) +static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return DRFLAC_REALLOC(p, sz); + return MA_DR_FLAC_REALLOC(p, sz); } -static void drflac__free_default(void* p, void* pUserData) +static void ma_dr_flac__free_default(void* p, void* pUserData) { (void)pUserData; - DRFLAC_FREE(p); + MA_DR_FLAC_FREE(p); } -static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) +static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -85593,7 +85765,7 @@ static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_ca } return NULL; } -static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) +static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -85608,14 +85780,14 @@ static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, return NULL; } if (p != NULL) { - DRFLAC_COPY_MEMORY(p2, p, szOld); + MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -85624,18 +85796,18 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -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 ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) { - drflac_uint64 runningFilePos = 42; - drflac_uint64 seektablePos = 0; - drflac_uint32 seektableSize = 0; + ma_uint64 runningFilePos = 42; + ma_uint64 seektablePos = 0; + ma_uint32 seektableSize = 0; for (;;) { - drflac_metadata metadata; - drflac_uint8 isLastBlock = 0; - drflac_uint8 blockType; - drflac_uint32 blockSize; - if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { - return DRFLAC_FALSE; + ma_dr_flac_metadata metadata; + ma_uint8 isLastBlock = 0; + ma_uint8 blockType; + ma_uint32 blockSize; + if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { + return MA_FALSE; } runningFilePos += 4; metadata.type = blockType; @@ -85643,159 +85815,159 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d metadata.rawDataSize = 0; switch (blockType) { - case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: { if (blockSize < 4) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onMeta) { - void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); - metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); + metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); + metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: { seektablePos = runningFilePos; seektableSize = blockSize; if (onMeta) { - drflac_uint32 seekpointCount; - drflac_uint32 iSeekpoint; + ma_uint32 seekpointCount; + ma_uint32 iSeekpoint; void* pRawData; - seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; - pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); + seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; + pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { - drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; - if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; + if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } - pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); - pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); - pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); + pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); + pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); + pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; metadata.data.seektable.seekpointCount = seekpointCount; - metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; + metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: { if (blockSize < 8) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; - drflac_uint32 i; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + ma_uint32 i; + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.data.vorbis_comment.pComments = pRunningData; for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { - drflac_uint32 commentLength; + ma_uint32 commentLength; if (pRunningDataEnd - pRunningData < 4) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } - commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } pRunningData += commentLength; } onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: { if (blockSize < 396) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; size_t bufferSize; - drflac_uint8 iTrack; - drflac_uint8 iIndex; + ma_uint8 iTrack; + ma_uint8 iIndex; void* pTrackData; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; + MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; metadata.data.cuesheet.pTrackData = NULL; { const char* pRunningDataSaved = pRunningData; - bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - drflac_uint8 indexCount; - drflac_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_uint8 indexCount; + ma_uint32 indexPointSize; + if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } pRunningData += 35; indexCount = pRunningData[0]; pRunningData += 1; - bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); - indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); + indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } pRunningData += indexPointSize; } @@ -85803,125 +85975,125 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d } { char* pRunningTrackData; - pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); if (pTrackData == NULL) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } pRunningTrackData = (char*)pTrackData; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - drflac_uint8 indexCount; - DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); - pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + ma_uint8 indexCount; + MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; indexCount = pRunningData[0]; pRunningData += 1; pRunningTrackData += 1; for (iIndex = 0; iIndex < indexCount; ++iIndex) { - drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; - DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); - pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - pRunningTrackData += sizeof(drflac_cuesheet_track_index); - pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); + ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; + MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); + pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); } } metadata.data.cuesheet.pTrackData = pTrackData; } - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); pRawData = NULL; onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); pTrackData = NULL; } } break; - case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: { if (blockSize < 32) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; - if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; + if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case DRFLAC_METADATA_BLOCK_TYPE_PADDING: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: { if (onMeta) { metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; } else { onMeta(pUserDataMD, &metadata); } } } break; - case DRFLAC_METADATA_BLOCK_TYPE_INVALID: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: { if (onMeta) { - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; } } } break; default: { if (onMeta) { - void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; } if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; } } runningFilePos += blockSize; @@ -85930,44 +86102,44 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d } } *pSeektablePos = seektablePos; - *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; *pFirstFramePos = runningFilePos; - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) { - drflac_uint8 isLastBlock; - drflac_uint8 blockType; - drflac_uint32 blockSize; + ma_uint8 isLastBlock; + ma_uint8 blockType; + ma_uint32 blockSize; (void)onSeek; - pInit->container = drflac_container_native; - if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DRFLAC_FALSE; + pInit->container = ma_dr_flac_container_native; + if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return MA_FALSE; } - if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { if (!relaxed) { - return DRFLAC_FALSE; + return MA_FALSE; } else { - pInit->hasStreamInfoBlock = DRFLAC_FALSE; - pInit->hasMetadataBlocks = DRFLAC_FALSE; - if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return DRFLAC_FALSE; + pInit->hasStreamInfoBlock = MA_FALSE; + pInit->hasMetadataBlocks = MA_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return MA_FALSE; } if (pInit->firstFrameHeader.bitsPerSample == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; pInit->maxBlockSizeInPCMFrames = 65535; - return DRFLAC_TRUE; + return MA_TRUE; } } else { - drflac_streaminfo streaminfo; - if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return DRFLAC_FALSE; + ma_dr_flac_streaminfo streaminfo; + if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return MA_FALSE; } - pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->hasStreamInfoBlock = MA_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; @@ -85975,26 +86147,26 @@ static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drfla pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { - drflac_metadata metadata; - metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + ma_dr_flac_metadata metadata; + metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; onMeta(pUserDataMD, &metadata); } - return DRFLAC_TRUE; + return MA_TRUE; } } -#ifndef DR_FLAC_NO_OGG -#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 -#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 +#ifndef MA_DR_FLAC_NO_OGG +#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 +#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 typedef enum { - drflac_ogg_recover_on_crc_mismatch, - drflac_ogg_fail_on_crc_mismatch -} drflac_ogg_crc_mismatch_recovery; -#ifndef DR_FLAC_NO_CRC -static drflac_uint32 drflac__crc32_table[] = { + ma_dr_flac_ogg_recover_on_crc_mismatch, + ma_dr_flac_ogg_fail_on_crc_mismatch +} ma_dr_flac_ogg_crc_mismatch_recovery; +#ifndef MA_DR_FLAC_NO_CRC +static ma_uint32 ma_dr_flac__crc32_table[] = { 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, @@ -86061,63 +86233,63 @@ static drflac_uint32 drflac__crc32_table[] = { 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L }; #endif -static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) +static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) { -#ifndef DR_FLAC_NO_CRC - return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#ifndef MA_DR_FLAC_NO_CRC + return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; #else (void)data; return crc32; #endif } #if 0 -static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) +static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) { - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); return crc32; } -static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) +static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) { - crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); - crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); + crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); return crc32; } #endif -static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) +static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) { - drflac_uint32 i; + ma_uint32 i; for (i = 0; i < dataSize; ++i) { - crc32 = drflac_crc32_byte(crc32, pData[i]); + crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); } return crc32; } -static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) +static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) { return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; } -static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) +static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) { return 27 + pHeader->segmentCount; } -static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) +static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) { - drflac_uint32 pageBodySize = 0; + ma_uint32 pageBodySize = 0; int i; for (i = 0; i < pHeader->segmentCount; ++i) { pageBodySize += pHeader->segmentTable[i]; } return pageBodySize; } -static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) { - drflac_uint8 data[23]; - drflac_uint32 i; - DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); + ma_uint8 data[23]; + ma_uint32 i; + MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); if (onRead(pUserData, data, 23) != 23) { - return DRFLAC_AT_END; + return MA_AT_END; } *pBytesRead += 23; pHeader->capturePattern[0] = 'O'; @@ -86126,44 +86298,44 @@ static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_r pHeader->capturePattern[3] = 'S'; pHeader->structureVersion = data[0]; pHeader->headerType = data[1]; - DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); - DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); - DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); - DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); + MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); pHeader->segmentCount = data[22]; data[18] = 0; data[19] = 0; data[20] = 0; data[21] = 0; for (i = 0; i < 23; ++i) { - *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); + *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); } if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return DRFLAC_AT_END; + return MA_AT_END; } *pBytesRead += pHeader->segmentCount; for (i = 0; i < pHeader->segmentCount; ++i) { - *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); } - return DRFLAC_SUCCESS; + return MA_SUCCESS; } -static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) { - drflac_uint8 id[4]; + ma_uint8 id[4]; *pBytesRead = 0; if (onRead(pUserData, id, 4) != 4) { - return DRFLAC_AT_END; + return MA_AT_END; } *pBytesRead += 4; for (;;) { - if (drflac_ogg__is_capture_pattern(id)) { - drflac_result result; - *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == DRFLAC_SUCCESS) { - return DRFLAC_SUCCESS; + if (ma_dr_flac_ogg__is_capture_pattern(id)) { + ma_result result; + *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; + result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == MA_SUCCESS) { + return MA_SUCCESS; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { continue; } else { return result; @@ -86174,7 +86346,7 @@ static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* id[1] = id[2]; id[2] = id[3]; if (onRead(pUserData, &id[3], 1) != 1) { - return DRFLAC_AT_END; + return MA_AT_END; } *pBytesRead += 1; } @@ -86182,91 +86354,91 @@ static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* } typedef struct { - drflac_read_proc onRead; - drflac_seek_proc onSeek; + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; void* pUserData; - drflac_uint64 currentBytePos; - drflac_uint64 firstBytePos; - drflac_uint32 serialNumber; - drflac_ogg_page_header bosPageHeader; - drflac_ogg_page_header currentPageHeader; - drflac_uint32 bytesRemainingInPage; - drflac_uint32 pageDataSize; - drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; -} drflac_oggbs; -static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) + ma_uint64 currentBytePos; + ma_uint64 firstBytePos; + ma_uint32 serialNumber; + ma_dr_flac_ogg_page_header bosPageHeader; + ma_dr_flac_ogg_page_header currentPageHeader; + ma_uint32 bytesRemainingInPage; + ma_uint32 pageDataSize; + ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; +} ma_dr_flac_oggbs; +static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) { size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); oggbs->currentBytePos += bytesActuallyRead; return bytesActuallyRead; } -static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) +static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) { - if (origin == drflac_seek_origin_start) { + if (origin == ma_dr_flac_seek_origin_start) { if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } oggbs->currentBytePos = offset; - return DRFLAC_TRUE; + return MA_TRUE; } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } oggbs->currentBytePos = offset; - return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); + return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); } } else { while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } oggbs->currentBytePos += offset; - return DRFLAC_TRUE; + return MA_TRUE; } } -static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) +static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) { - drflac_ogg_page_header header; + ma_dr_flac_ogg_page_header header; for (;;) { - drflac_uint32 crc32 = 0; - drflac_uint32 bytesRead; - drflac_uint32 pageBodySize; -#ifndef DR_FLAC_NO_CRC - drflac_uint32 actualCRC32; + ma_uint32 crc32 = 0; + ma_uint32 bytesRead; + ma_uint32 pageBodySize; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint32 actualCRC32; #endif - if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; + if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; } oggbs->currentBytePos += bytesRead; - pageBodySize = drflac_ogg__get_page_body_size(&header); - if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { + pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); + if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { continue; } if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } continue; } - if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { - return DRFLAC_FALSE; + if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return MA_FALSE; } oggbs->pageDataSize = pageBodySize; -#ifndef DR_FLAC_NO_CRC - actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); +#ifndef MA_DR_FLAC_NO_CRC + actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); if (actualCRC32 != header.checksum) { - if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { + if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { continue; } else { - drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); - return DRFLAC_FALSE; + ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); + return MA_FALSE; } } #else @@ -86274,17 +86446,17 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og #endif oggbs->currentPageHeader = header; oggbs->bytesRemainingInPage = pageBodySize; - return DRFLAC_TRUE; + return MA_TRUE; } } #if 0 -static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) +static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) { - drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - drflac_uint8 iSeg = 0; - drflac_uint32 iByte = 0; + ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + ma_uint8 iSeg = 0; + ma_uint32 iByte = 0; while (iByte < bytesConsumedInPage) { - drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (iByte + segmentSize > bytesConsumedInPage) { break; } else { @@ -86292,92 +86464,92 @@ static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, iByte += segmentSize; } } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); return iSeg; } -static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) +static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) { for (;;) { - drflac_bool32 atEndOfPage = DRFLAC_FALSE; - drflac_uint8 bytesRemainingInSeg; - drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + ma_bool32 atEndOfPage = MA_FALSE; + ma_uint8 bytesRemainingInSeg; + ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (segmentSize < 255) { if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = DRFLAC_TRUE; + atEndOfPage = MA_TRUE; } break; } bytesToEndOfPacketOrPage += segmentSize; } - drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); + ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; if (atEndOfPage) { - if (!drflac_oggbs__goto_next_page(oggbs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { + return MA_FALSE; } if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return DRFLAC_TRUE; + return MA_TRUE; } } else { - return DRFLAC_TRUE; + return MA_TRUE; } } } -static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) +static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) { - return drflac_oggbs__seek_to_next_packet(oggbs); + return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); } #endif -static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) { - drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; - drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; + ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; size_t bytesRead = 0; - DRFLAC_ASSERT(oggbs != NULL); - DRFLAC_ASSERT(pRunningBufferOut != NULL); + MA_DR_FLAC_ASSERT(oggbs != NULL); + MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); while (bytesRead < bytesToRead) { size_t bytesRemainingToRead = bytesToRead - bytesRead; if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; break; } if (oggbs->bytesRemainingInPage > 0) { - DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); bytesRead += oggbs->bytesRemainingInPage; pRunningBufferOut += oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } - DRFLAC_ASSERT(bytesRemainingToRead > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { break; } } return bytesRead; } -static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { - drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; int bytesSeeked = 0; - DRFLAC_ASSERT(oggbs != NULL); - DRFLAC_ASSERT(offset >= 0); - if (origin == drflac_seek_origin_start) { - if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + MA_DR_FLAC_ASSERT(oggbs != NULL); + MA_DR_FLAC_ASSERT(offset >= 0); + if (origin == ma_dr_flac_seek_origin_start) { + if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; } - return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); + return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); } - DRFLAC_ASSERT(origin == drflac_seek_origin_current); + MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); while (bytesSeeked < offset) { int bytesRemainingToSeek = offset - bytesSeeked; - DRFLAC_ASSERT(bytesRemainingToSeek >= 0); + MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { bytesSeeked += bytesRemainingToSeek; (void)bytesSeeked; @@ -86388,39 +86560,39 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see bytesSeeked += (int)oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } - DRFLAC_ASSERT(bytesRemainingToSeek > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - return DRFLAC_FALSE; + MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; } } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - drflac_uint64 originalBytePos; - drflac_uint64 runningGranulePosition; - drflac_uint64 runningFrameBytePos; - drflac_uint64 runningPCMFrameCount; - DRFLAC_ASSERT(oggbs != NULL); + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + ma_uint64 originalBytePos; + ma_uint64 runningGranulePosition; + ma_uint64 runningFrameBytePos; + ma_uint64 runningPCMFrameCount; + MA_DR_FLAC_ASSERT(oggbs != NULL); originalBytePos = oggbs->currentBytePos; - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { + return MA_FALSE; } oggbs->bytesRemainingInPage = 0; 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); - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); + return MA_FALSE; } - runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { break; } if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - drflac_uint8 firstBytesInPage[2]; + ma_uint8 firstBytesInPage[2]; firstBytesInPage[0] = oggbs->pageData[0]; firstBytesInPage[1] = oggbs->pageData[1]; if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { @@ -86430,120 +86602,120 @@ static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 } } } - if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + return MA_FALSE; } runningPCMFrameCount = runningGranulePosition; for (;;) { - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac_uint64 pcmFrameCountInThisFrame; - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_uint64 pcmFrameCountInThisFrame; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { pFlac->currentPCMFrame = pcmFrameIndex; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - return DRFLAC_TRUE; + return MA_TRUE; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); if (pcmFramesToDecode == 0) { - return DRFLAC_TRUE; + return MA_TRUE; } pFlac->currentPCMFrame = runningPCMFrameCount; - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { continue; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFrame; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { continue; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } } } -static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) { - drflac_ogg_page_header header; - drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - drflac_uint32 bytesRead = 0; + ma_dr_flac_ogg_page_header header; + ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; + ma_uint32 bytesRead = 0; (void)relaxed; - pInit->container = drflac_container_ogg; + pInit->container = ma_dr_flac_container_ogg; pInit->oggFirstBytePos = 0; - if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; + if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; } pInit->runningFilePos += bytesRead; for (;;) { int pageBodySize; if ((header.headerType & 0x02) == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } - pageBodySize = drflac_ogg__get_page_body_size(&header); + pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); if (pageBodySize == 51) { - drflac_uint32 bytesRemainingInPage = pageBodySize; - drflac_uint8 packetType; + ma_uint32 bytesRemainingInPage = pageBodySize; + ma_uint8 packetType; if (onRead(pUserData, &packetType, 1) != 1) { - return DRFLAC_FALSE; + return MA_FALSE; } bytesRemainingInPage -= 1; if (packetType == 0x7F) { - drflac_uint8 sig[4]; + ma_uint8 sig[4]; if (onRead(pUserData, sig, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } bytesRemainingInPage -= 4; if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - drflac_uint8 mappingVersion[2]; + ma_uint8 mappingVersion[2]; if (onRead(pUserData, mappingVersion, 2) != 2) { - return DRFLAC_FALSE; + return MA_FALSE; } if (mappingVersion[0] != 1) { - return DRFLAC_FALSE; + return MA_FALSE; } - if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } if (onRead(pUserData, sig, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - drflac_streaminfo streaminfo; - drflac_uint8 isLastBlock; - drflac_uint8 blockType; - drflac_uint32 blockSize; - if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DRFLAC_FALSE; + ma_dr_flac_streaminfo streaminfo; + ma_uint8 isLastBlock; + ma_uint8 blockType; + ma_uint32 blockSize; + if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return MA_FALSE; } - if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return DRFLAC_FALSE; + if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return MA_FALSE; } - if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { - pInit->hasStreamInfoBlock = DRFLAC_TRUE; + if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { + pInit->hasStreamInfoBlock = MA_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; @@ -86551,8 +86723,8 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { - drflac_metadata metadata; - metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + ma_dr_flac_metadata metadata; + metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; @@ -86564,44 +86736,44 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r pInit->oggBosHeader = header; break; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } else { - return DRFLAC_FALSE; + return MA_FALSE; } } else { - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } } } else { - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } } } else { - if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } } pInit->runningFilePos += pageBodySize; - if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; + if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; } pInit->runningFilePos += bytesRead; } - pInit->hasMetadataBlocks = DRFLAC_TRUE; - return DRFLAC_TRUE; + pInit->hasMetadataBlocks = MA_TRUE; + return MA_TRUE; } #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 ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) { - drflac_bool32 relaxed; - drflac_uint8 id[4]; + ma_bool32 relaxed; + ma_uint8 id[4]; if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } - DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); + MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); pInit->onRead = onRead; pInit->onSeek = onSeek; pInit->onMeta = onMeta; @@ -86611,29 +86783,29 @@ static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_p pInit->bs.onRead = onRead; pInit->bs.onSeek = onSeek; pInit->bs.pUserData = pUserData; - drflac__reset_cache(&pInit->bs); - relaxed = container != drflac_container_unknown; + ma_dr_flac__reset_cache(&pInit->bs); + relaxed = container != ma_dr_flac_container_unknown; for (;;) { if (onRead(pUserData, id, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } pInit->runningFilePos += 4; if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - drflac_uint8 header[6]; - drflac_uint8 flags; - drflac_uint32 headerSize; + ma_uint8 header[6]; + ma_uint8 flags; + ma_uint32 headerSize; if (onRead(pUserData, header, 6) != 6) { - return DRFLAC_FALSE; + return MA_FALSE; } pInit->runningFilePos += 6; flags = header[1]; - DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); - headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); + MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); + headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); if (flags & 0x10) { headerSize += 10; } - if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } pInit->runningFilePos += headerSize; } else { @@ -86641,56 +86813,56 @@ static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_p } } if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } -#ifndef DR_FLAC_NO_OGG +#ifndef MA_DR_FLAC_NO_OGG if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif if (relaxed) { - if (container == drflac_container_native) { - return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + if (container == ma_dr_flac_container_native) { + return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } -#ifndef DR_FLAC_NO_OGG - if (container == drflac_container_ogg) { - return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); +#ifndef MA_DR_FLAC_NO_OGG + if (container == ma_dr_flac_container_ogg) { + return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif } - return DRFLAC_FALSE; + return MA_FALSE; } -static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) +static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) { - DRFLAC_ASSERT(pFlac != NULL); - DRFLAC_ASSERT(pInit != NULL); - DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); + MA_DR_FLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pInit != NULL); + MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); pFlac->bs = pInit->bs; pFlac->onMeta = pInit->onMeta; pFlac->pUserDataMD = pInit->pUserDataMD; pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (drflac_uint8)pInit->channels; - pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; + pFlac->channels = (ma_uint8)pInit->channels; + pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; pFlac->container = pInit->container; } -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 ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac_init_info init; - drflac_uint32 allocationSize; - drflac_uint32 wholeSIMDVectorCountPerChannel; - drflac_uint32 decodedSamplesAllocationSize; -#ifndef DR_FLAC_NO_OGG - drflac_oggbs* pOggbs = NULL; + ma_dr_flac_init_info init; + ma_uint32 allocationSize; + ma_uint32 wholeSIMDVectorCountPerChannel; + ma_uint32 decodedSamplesAllocationSize; +#ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac_oggbs* pOggbs = NULL; #endif - drflac_uint64 firstFramePos; - drflac_uint64 seektablePos; - drflac_uint32 seekpointCount; - drflac_allocation_callbacks allocationCallbacks; - drflac* pFlac; - drflac__init_cpu_caps(); - if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + ma_uint64 firstFramePos; + ma_uint64 seektablePos; + ma_uint32 seekpointCount; + ma_allocation_callbacks allocationCallbacks; + ma_dr_flac* pFlac; + ma_dr_flac__init_cpu_caps(); + if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { return NULL; } if (pAllocationCallbacks != NULL) { @@ -86700,27 +86872,27 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac } } else { allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drflac__malloc_default; - allocationCallbacks.onRealloc = drflac__realloc_default; - allocationCallbacks.onFree = drflac__free_default; + allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; + allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; + allocationCallbacks.onFree = ma_dr_flac__free_default; } - allocationSize = sizeof(drflac); - if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); + allocationSize = sizeof(ma_dr_flac); + if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); } else { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; } - decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; allocationSize += decodedSamplesAllocationSize; - allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - allocationSize += sizeof(drflac_oggbs); - pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + allocationSize += sizeof(ma_dr_flac_oggbs); + pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); if (pOggbs == NULL) { return NULL; } - DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); pOggbs->onRead = onRead; pOggbs->onSeek = onSeek; pOggbs->pUserData = pUserData; @@ -86735,49 +86907,49 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac seektablePos = 0; seekpointCount = 0; if (init.hasMetadataBlocks) { - drflac_read_proc onReadOverride = onRead; - drflac_seek_proc onSeekOverride = onSeek; + ma_dr_flac_read_proc onReadOverride = onRead; + ma_dr_flac_seek_proc onSeekOverride = onSeek; void* pUserDataOverride = pUserData; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - onReadOverride = drflac__on_read_ogg; - onSeekOverride = drflac__on_seek_ogg; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + onReadOverride = ma_dr_flac__on_read_ogg; + onSeekOverride = ma_dr_flac__on_seek_ogg; pUserDataOverride = (void*)pOggbs; } #endif - if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { - #ifndef DR_FLAC_NO_OGG - drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } - allocationSize += seekpointCount * sizeof(drflac_seekpoint); + allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); } - pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); + pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); if (pFlac == NULL) { - #ifndef DR_FLAC_NO_OGG - drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } - drflac__init_from_info(pFlac, &init); + ma_dr_flac__init_from_info(pFlac, &init); pFlac->allocationCallbacks = allocationCallbacks; - pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); - DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); - drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); + MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); pOggbs = NULL; - pFlac->bs.onRead = drflac__on_read_ogg; - pFlac->bs.onSeek = drflac__on_seek_ogg; + pFlac->bs.onRead = ma_dr_flac__on_read_ogg; + pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; pFlac->_oggbs = (void*)pInternalOggbs; } #endif pFlac->firstFLACFramePosInBytes = firstFramePos; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; @@ -86787,24 +86959,24 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac { if (seektablePos != 0) { pFlac->seekpointCount = seekpointCount; - pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); - DRFLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { - drflac_uint32 iSeekpoint; + pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); + MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { + ma_uint32 iSeekpoint; for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { - pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); - pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); - pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { + pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); + pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); + pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); } else { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; break; } } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } else { @@ -86816,18 +86988,18 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac if (!init.hasStreamInfoBlock) { pFlac->currentFLACFrame.header = init.firstFrameHeader; for (;;) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { break; } else { - if (result == DRFLAC_CRC_MISMATCH) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); + if (result == MA_CRC_MISMATCH) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } continue; } else { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } @@ -86835,555 +87007,43 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac } return pFlac; } -#ifndef DR_FLAC_NO_STDIO +#ifndef MA_DR_FLAC_NO_STDIO #include -#ifndef DR_FLAC_NO_WCHAR +#ifndef MA_DR_FLAC_NO_WCHAR #include #endif -#include -static drflac_result drflac_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRFLAC_SUCCESS; - #ifdef EPERM - case EPERM: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRFLAC_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRFLAC_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRFLAC_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRFLAC_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRFLAC_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRFLAC_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRFLAC_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRFLAC_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRFLAC_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRFLAC_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRFLAC_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRFLAC_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRFLAC_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRFLAC_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRFLAC_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRFLAC_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRFLAC_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRFLAC_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRFLAC_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRFLAC_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRFLAC_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRFLAC_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRFLAC_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRFLAC_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRFLAC_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRFLAC_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRFLAC_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRFLAC_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRFLAC_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRFLAC_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRFLAC_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRFLAC_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRFLAC_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRFLAC_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRFLAC_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRFLAC_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRFLAC_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRFLAC_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRFLAC_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRFLAC_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRFLAC_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRFLAC_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRFLAC_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRFLAC_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRFLAC_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRFLAC_ERROR; - #endif - #ifdef EADV - case EADV: return DRFLAC_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRFLAC_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRFLAC_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRFLAC_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRFLAC_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRFLAC_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRFLAC_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRFLAC_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRFLAC_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRFLAC_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRFLAC_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRFLAC_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRFLAC_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRFLAC_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRFLAC_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRFLAC_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRFLAC_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRFLAC_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRFLAC_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRFLAC_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRFLAC_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRFLAC_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRFLAC_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRFLAC_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRFLAC_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRFLAC_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRFLAC_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRFLAC_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRFLAC_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRFLAC_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRFLAC_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRFLAC_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRFLAC_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRFLAC_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRFLAC_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRFLAC_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRFLAC_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRFLAC_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRFLAC_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRFLAC_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRFLAC_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRFLAC_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRFLAC_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRFLAC_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRFLAC_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRFLAC_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRFLAC_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRFLAC_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRFLAC_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRFLAC_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRFLAC_ERROR; - #endif - default: return DRFLAC_ERROR; - } -} -static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRFLAC_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drflac_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drflac_result result = drflac_result_from_errno(errno); - if (result == DRFLAC_SUCCESS) { - result = DRFLAC_ERROR; - } - return result; - } -#endif - return DRFLAC_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRFLAC_HAS_WFOPEN - #endif -#endif -#ifndef DR_FLAC_NO_WCHAR -static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRFLAC_INVALID_ARGS; - } -#if defined(DRFLAC_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drflac_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drflac_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - #if defined(__DJGPP__) - { - } - #else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRFLAC_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drflac_result_from_errno(errno); - } - pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRFLAC_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRFLAC_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - #endif - if (*ppFile == NULL) { - return DRFLAC_ERROR; - } -#endif - return DRFLAC_SUCCESS; -} -#endif -static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); } -static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { - DRFLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + MA_DR_FLAC_ASSERT(offset >= 0); + return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; FILE* pFile; - if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { return NULL; } - pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; } return pFlac; } -#ifndef DR_FLAC_NO_WCHAR -DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_FLAC_NO_WCHAR +MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; FILE* pFile; - if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { + if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { return NULL; } - pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -87391,29 +87051,29 @@ DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_all return pFlac; } #endif -DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; FILE* pFile; - if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { 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 = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; } return pFlac; } -#ifndef DR_FLAC_NO_WCHAR -DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_FLAC_NO_WCHAR +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; FILE* pFile; - if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { + if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { 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 = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -87422,61 +87082,61 @@ DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, dr } #endif #endif -static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { - drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; size_t bytesRemaining; - DRFLAC_ASSERT(memoryStream != NULL); - DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); memoryStream->currentReadPos += bytesToRead; } return bytesToRead; } -static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { - drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; - DRFLAC_ASSERT(memoryStream != NULL); - DRFLAC_ASSERT(offset >= 0); - if (offset > (drflac_int64)memoryStream->dataSize) { - return DRFLAC_FALSE; + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(offset >= 0); + if (offset > (ma_int64)memoryStream->dataSize) { + return MA_FALSE; } - if (origin == drflac_seek_origin_current) { + if (origin == ma_dr_flac_seek_origin_current) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { memoryStream->currentReadPos += offset; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } else { - if ((drflac_uint32)offset <= memoryStream->dataSize) { + if ((ma_uint32)offset <= memoryStream->dataSize) { memoryStream->currentReadPos = offset; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } - return DRFLAC_TRUE; + return MA_TRUE; } -DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac__memory_stream memoryStream; - drflac* pFlac; - memoryStream.data = (const drflac_uint8*)pData; + ma_dr_flac__memory_stream memoryStream; + ma_dr_flac* pFlac; + memoryStream.data = (const ma_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else @@ -87486,22 +87146,22 @@ DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const } return pFlac; } -DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac__memory_stream memoryStream; - drflac* pFlac; - memoryStream.data = (const drflac_uint8*)pData; + ma_dr_flac__memory_stream memoryStream; + ma_dr_flac* pFlac; + memoryStream.data = (const ma_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 = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else @@ -87511,104 +87171,104 @@ DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t da } return pFlac; } -DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_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) +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, 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) +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_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) +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } -DRFLAC_API void drflac_close(drflac* pFlac) +MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) { if (pFlac == NULL) { return; } -#ifndef DR_FLAC_NO_STDIO - if (pFlac->bs.onRead == drflac__on_read_stdio) { +#ifndef MA_DR_FLAC_NO_STDIO + if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { fclose((FILE*)pFlac->bs.pUserData); } -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); - if (oggbs->onRead == drflac__on_read_stdio) { +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); + if (oggbs->onRead == ma_dr_flac__on_read_stdio) { fclose((FILE*)oggbs->pUserData); } } #endif #endif - drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); + ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (drflac_int32)left0; - pOutputSamples[i*8+1] = (drflac_int32)right0; - pOutputSamples[i*8+2] = (drflac_int32)left1; - pOutputSamples[i*8+3] = (drflac_int32)right1; - pOutputSamples[i*8+4] = (drflac_int32)left2; - pOutputSamples[i*8+5] = (drflac_int32)right2; - pOutputSamples[i*8+6] = (drflac_int32)left3; - pOutputSamples[i*8+7] = (drflac_int32)right3; + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (ma_int32)left0; + pOutputSamples[i*8+1] = (ma_int32)right0; + pOutputSamples[i*8+2] = (ma_int32)left1; + pOutputSamples[i*8+3] = (ma_int32)right1; + pOutputSamples[i*8+4] = (ma_int32)left2; + pOutputSamples[i*8+5] = (ma_int32)right2; + pOutputSamples[i*8+6] = (ma_int32)left3; + pOutputSamples[i*8+7] = (ma_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -87617,26 +87277,26 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drf _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -87646,97 +87306,97 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drf left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); - drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (drflac_int32)left0; - pOutputSamples[i*8+1] = (drflac_int32)right0; - pOutputSamples[i*8+2] = (drflac_int32)left1; - pOutputSamples[i*8+3] = (drflac_int32)right1; - pOutputSamples[i*8+4] = (drflac_int32)left2; - pOutputSamples[i*8+5] = (drflac_int32)right2; - pOutputSamples[i*8+6] = (drflac_int32)left3; - pOutputSamples[i*8+7] = (drflac_int32)right3; + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (ma_int32)left0; + pOutputSamples[i*8+1] = (ma_int32)right0; + pOutputSamples[i*8+2] = (ma_int32)left1; + pOutputSamples[i*8+3] = (ma_int32)right1; + pOutputSamples[i*8+4] = (ma_int32)left2; + pOutputSamples[i*8+5] = (ma_int32)right2; + pOutputSamples[i*8+6] = (ma_int32)left3; + pOutputSamples[i*8+7] = (ma_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -87745,26 +87405,26 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(dr _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -87774,74 +87434,74 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(dr side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); - drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -87854,72 +87514,72 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(dr temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (drflac_int32)temp0L; - pOutputSamples[i*8+1] = (drflac_int32)temp0R; - pOutputSamples[i*8+2] = (drflac_int32)temp1L; - pOutputSamples[i*8+3] = (drflac_int32)temp1R; - pOutputSamples[i*8+4] = (drflac_int32)temp2L; - pOutputSamples[i*8+5] = (drflac_int32)temp2R; - pOutputSamples[i*8+6] = (drflac_int32)temp3L; - pOutputSamples[i*8+7] = (drflac_int32)temp3R; + pOutputSamples[i*8+0] = (ma_int32)temp0L; + pOutputSamples[i*8+1] = (ma_int32)temp0R; + pOutputSamples[i*8+2] = (ma_int32)temp1L; + pOutputSamples[i*8+3] = (ma_int32)temp1R; + pOutputSamples[i*8+4] = (ma_int32)temp2L; + pOutputSamples[i*8+5] = (ma_int32)temp2R; + pOutputSamples[i*8+6] = (ma_int32)temp3L; + pOutputSamples[i*8+7] = (ma_int32)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); - temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); - temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); - temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); - temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); - temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); - temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); - temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (drflac_int32)temp0L; - pOutputSamples[i*8+1] = (drflac_int32)temp0R; - pOutputSamples[i*8+2] = (drflac_int32)temp1L; - pOutputSamples[i*8+3] = (drflac_int32)temp1R; - pOutputSamples[i*8+4] = (drflac_int32)temp2L; - pOutputSamples[i*8+5] = (drflac_int32)temp2R; - pOutputSamples[i*8+6] = (drflac_int32)temp3L; - pOutputSamples[i*8+7] = (drflac_int32)temp3R; + temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); + temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); + temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); + temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); + temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); + temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); + temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); + temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (ma_int32)temp0L; + pOutputSamples[i*8+1] = (ma_int32)temp0R; + pOutputSamples[i*8+2] = (ma_int32)temp1L; + pOutputSamples[i*8+3] = (ma_int32)temp1R; + pOutputSamples[i*8+4] = (ma_int32)temp2L; + pOutputSamples[i*8+5] = (ma_int32)temp2R; + pOutputSamples[i*8+6] = (ma_int32)temp3L; + pOutputSamples[i*8+7] = (ma_int32)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -87935,11 +87595,11 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drfl _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; + pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; } } else { shift -= 1; @@ -87957,27 +87617,27 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drfl _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); } } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; int32x4_t wbpsShift1_4; uint32x4_t one4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); one4 = vdupq_n_u32(1); @@ -87992,14 +87652,14 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drfl mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; + pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; } } else { int32x4_t shift4; @@ -88015,86 +87675,86 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drfl mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); } } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (drflac_int32)tempL0; - pOutputSamples[i*8+1] = (drflac_int32)tempR0; - pOutputSamples[i*8+2] = (drflac_int32)tempL1; - pOutputSamples[i*8+3] = (drflac_int32)tempR1; - pOutputSamples[i*8+4] = (drflac_int32)tempL2; - pOutputSamples[i*8+5] = (drflac_int32)tempR2; - pOutputSamples[i*8+6] = (drflac_int32)tempL3; - pOutputSamples[i*8+7] = (drflac_int32)tempR3; + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (ma_int32)tempL0; + pOutputSamples[i*8+1] = (ma_int32)tempR0; + pOutputSamples[i*8+2] = (ma_int32)tempL1; + pOutputSamples[i*8+3] = (ma_int32)tempR1; + pOutputSamples[i*8+4] = (ma_int32)tempL2; + pOutputSamples[i*8+5] = (ma_int32)tempR2; + pOutputSamples[i*8+6] = (ma_int32)tempL3; + pOutputSamples[i*8+7] = (ma_int32)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -88102,20 +87762,20 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo_ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift4_0 = vdupq_n_s32(shift0); int32x4_t shift4_1 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88123,87 +87783,87 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo_ int32x4_t right; left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); } } } @@ -88211,47 +87871,47 @@ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; } } return framesRead; } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; @@ -88260,66 +87920,66 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(d right1 >>= 16; right2 >>= 16; right3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)left0; - pOutputSamples[i*8+1] = (drflac_int16)right0; - pOutputSamples[i*8+2] = (drflac_int16)left1; - pOutputSamples[i*8+3] = (drflac_int16)right1; - pOutputSamples[i*8+4] = (drflac_int16)left2; - pOutputSamples[i*8+5] = (drflac_int16)right2; - pOutputSamples[i*8+6] = (drflac_int16)left3; - pOutputSamples[i*8+7] = (drflac_int16)right3; + pOutputSamples[i*8+0] = (ma_int16)left0; + pOutputSamples[i*8+1] = (ma_int16)right0; + pOutputSamples[i*8+2] = (ma_int16)left1; + pOutputSamples[i*8+3] = (ma_int16)right1; + pOutputSamples[i*8+4] = (ma_int16)left2; + pOutputSamples[i*8+5] = (ma_int16)right2; + pOutputSamples[i*8+6] = (ma_int16)left3; + pOutputSamples[i*8+7] = (ma_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88331,74 +87991,74 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drf right = vsubq_u32(left, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); - drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; @@ -88407,66 +88067,66 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar( right1 >>= 16; right2 >>= 16; right3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)left0; - pOutputSamples[i*8+1] = (drflac_int16)right0; - pOutputSamples[i*8+2] = (drflac_int16)left1; - pOutputSamples[i*8+3] = (drflac_int16)right1; - pOutputSamples[i*8+4] = (drflac_int16)left2; - pOutputSamples[i*8+5] = (drflac_int16)right2; - pOutputSamples[i*8+6] = (drflac_int16)left3; - pOutputSamples[i*8+7] = (drflac_int16)right3; + pOutputSamples[i*8+0] = (ma_int16)left0; + pOutputSamples[i*8+1] = (ma_int16)right0; + pOutputSamples[i*8+2] = (ma_int16)left1; + pOutputSamples[i*8+3] = (ma_int16)right1; + pOutputSamples[i*8+4] = (ma_int16)left2; + pOutputSamples[i*8+5] = (ma_int16)right2; + pOutputSamples[i*8+6] = (ma_int16)left3; + pOutputSamples[i*8+7] = (ma_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88478,76 +88138,76 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(dr left = vaddq_u32(right, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); - drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -88568,45 +88228,45 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(dr temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)temp0L; - pOutputSamples[i*8+1] = (drflac_int16)temp0R; - pOutputSamples[i*8+2] = (drflac_int16)temp1L; - pOutputSamples[i*8+3] = (drflac_int16)temp1R; - pOutputSamples[i*8+4] = (drflac_int16)temp2L; - pOutputSamples[i*8+5] = (drflac_int16)temp2R; - pOutputSamples[i*8+6] = (drflac_int16)temp3L; - pOutputSamples[i*8+7] = (drflac_int16)temp3R; + pOutputSamples[i*8+0] = (ma_int16)temp0L; + pOutputSamples[i*8+1] = (ma_int16)temp0R; + pOutputSamples[i*8+2] = (ma_int16)temp1L; + pOutputSamples[i*8+3] = (ma_int16)temp1R; + pOutputSamples[i*8+4] = (ma_int16)temp2L; + pOutputSamples[i*8+5] = (ma_int16)temp2R; + pOutputSamples[i*8+6] = (ma_int16)temp3L; + pOutputSamples[i*8+7] = (ma_int16)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = ((drflac_int32)(mid0 + side0) >> 1); - temp1L = ((drflac_int32)(mid1 + side1) >> 1); - temp2L = ((drflac_int32)(mid2 + side2) >> 1); - temp3L = ((drflac_int32)(mid3 + side3) >> 1); - temp0R = ((drflac_int32)(mid0 - side0) >> 1); - temp1R = ((drflac_int32)(mid1 - side1) >> 1); - temp2R = ((drflac_int32)(mid2 - side2) >> 1); - temp3R = ((drflac_int32)(mid3 - side3) >> 1); + temp0L = ((ma_int32)(mid0 + side0) >> 1); + temp1L = ((ma_int32)(mid1 + side1) >> 1); + temp2L = ((ma_int32)(mid2 + side2) >> 1); + temp3L = ((ma_int32)(mid3 + side3) >> 1); + temp0R = ((ma_int32)(mid0 - side0) >> 1); + temp1R = ((ma_int32)(mid1 - side1) >> 1); + temp2R = ((ma_int32)(mid2 - side2) >> 1); + temp3R = ((ma_int32)(mid3 - side3) >> 1); temp0L >>= 16; temp1L >>= 16; temp2L >>= 16; @@ -88615,33 +88275,33 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(dr temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)temp0L; - pOutputSamples[i*8+1] = (drflac_int16)temp0R; - pOutputSamples[i*8+2] = (drflac_int16)temp1L; - pOutputSamples[i*8+3] = (drflac_int16)temp1R; - pOutputSamples[i*8+4] = (drflac_int16)temp2L; - pOutputSamples[i*8+5] = (drflac_int16)temp2R; - pOutputSamples[i*8+6] = (drflac_int16)temp3L; - pOutputSamples[i*8+7] = (drflac_int16)temp3R; + pOutputSamples[i*8+0] = (ma_int16)temp0L; + pOutputSamples[i*8+1] = (ma_int16)temp0R; + pOutputSamples[i*8+2] = (ma_int16)temp1L; + pOutputSamples[i*8+3] = (ma_int16)temp1R; + pOutputSamples[i*8+4] = (ma_int16)temp2L; + pOutputSamples[i*8+5] = (ma_int16)temp2R; + pOutputSamples[i*8+6] = (ma_int16)temp3L; + pOutputSamples[i*8+7] = (ma_int16)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -88655,14 +88315,14 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drfl right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); } } else { shift -= 1; @@ -88678,29 +88338,29 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drfl right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); } } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; int32x4_t wbpsShift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); if (shift == 0) { @@ -88716,14 +88376,14 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drfl right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); } } else { int32x4_t shift4; @@ -88741,63 +88401,63 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drfl right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); } } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; tempL0 >>= 16; tempL1 >>= 16; tempL2 >>= 16; @@ -88806,51 +88466,51 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo_ tempR1 >>= 16; tempR2 >>= 16; tempR3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)tempL0; - pOutputSamples[i*8+1] = (drflac_int16)tempR0; - pOutputSamples[i*8+2] = (drflac_int16)tempL1; - pOutputSamples[i*8+3] = (drflac_int16)tempR1; - pOutputSamples[i*8+4] = (drflac_int16)tempL2; - pOutputSamples[i*8+5] = (drflac_int16)tempR2; - pOutputSamples[i*8+6] = (drflac_int16)tempL3; - pOutputSamples[i*8+7] = (drflac_int16)tempR3; + pOutputSamples[i*8+0] = (ma_int16)tempL0; + pOutputSamples[i*8+1] = (ma_int16)tempR0; + pOutputSamples[i*8+2] = (ma_int16)tempL1; + pOutputSamples[i*8+3] = (ma_int16)tempR1; + pOutputSamples[i*8+4] = (ma_int16)tempL2; + pOutputSamples[i*8+5] = (ma_int16)tempR2; + pOutputSamples[i*8+6] = (ma_int16)tempL3; + pOutputSamples[i*8+7] = (ma_int16)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4 = vdupq_n_s32(shift0); int32x4_t shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88860,88 +88520,88 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo_ right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); + ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); } } } @@ -88949,74 +88609,74 @@ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; } } return framesRead; } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (ma_int32)left0 * factor; + pOutputSamples[i*8+1] = (ma_int32)right0 * factor; + pOutputSamples[i*8+2] = (ma_int32)left1 * factor; + pOutputSamples[i*8+3] = (ma_int32)right1 * factor; + pOutputSamples[i*8+4] = (ma_int32)left2 * factor; + pOutputSamples[i*8+5] = (ma_int32)right2 * factor; + pOutputSamples[i*8+6] = (ma_int32)left3 * factor; + pOutputSamples[i*8+7] = (ma_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left * factor; - pOutputSamples[i*2+1] = (drflac_int32)right * factor; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left * factor; + pOutputSamples[i*2+1] = (ma_int32)right * factor; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -89028,27 +88688,27 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drf _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); @@ -89063,99 +88723,99 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drf right = vsubq_u32(left, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (ma_int32)left0 * factor; + pOutputSamples[i*8+1] = (ma_int32)right0 * factor; + pOutputSamples[i*8+2] = (ma_int32)left1 * factor; + pOutputSamples[i*8+3] = (ma_int32)right1 * factor; + pOutputSamples[i*8+4] = (ma_int32)left2 * factor; + pOutputSamples[i*8+5] = (ma_int32)right2 * factor; + pOutputSamples[i*8+6] = (ma_int32)left3 * factor; + pOutputSamples[i*8+7] = (ma_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left * factor; - pOutputSamples[i*2+1] = (drflac_int32)right * factor; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left * factor; + pOutputSamples[i*2+1] = (ma_int32)right * factor; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -89167,27 +88827,27 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(dr _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); @@ -89202,75 +88862,75 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(dr left = vaddq_u32(right, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; float factor = 1 / 2147483648.0; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -89283,74 +88943,74 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(dr temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; - pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; - pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; - pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; - pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; - pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; - pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; - pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; + pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; + pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; + pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; + pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; + pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; + pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; + pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; + pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; } } else { for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); - temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); - temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); - temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); - temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); - temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); - temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); - temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; - pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; - pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; - pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; - pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; - pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; - pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; - pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; + temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); + temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); + temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); + temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); + temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); + temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); + temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); + temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; + pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; + pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; + pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; + pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; + pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; + pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; + pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample - 8; float factor; __m128 factor128; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor128 = _mm_set1_ps(factor); if (shift == 0) { @@ -89372,11 +89032,11 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drfl _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; + pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; @@ -89398,29 +89058,29 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drfl _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; } } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample - 8; float factor; float32x4_t factor4; int32x4_t shift4; int32x4_t wbps0_4; int32x4_t wbps1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor4 = vdupq_n_f32(factor); wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); @@ -89438,14 +89098,14 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drfl righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; + pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; @@ -89464,87 +89124,87 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drfl righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; } } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; + pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; + pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; + pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; + pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; + pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; + pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; + pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; __m128 factor128 = _mm_set1_ps(factor); for (i = 0; i < frameCount4; ++i) { @@ -89560,20 +89220,20 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo_ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; float32x4_t factor4 = vdupq_n_f32(factor); int32x4_t shift0_4 = vdupq_n_s32(shift0); @@ -89587,87 +89247,87 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo_ righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) { - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); } } @@ -89681,111 +89341,102 @@ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 } return framesRead; } -DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { if (pFlac == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pFlac->currentPCMFrame == pcmFrameIndex) { - return DRFLAC_TRUE; + return MA_TRUE; } if (pFlac->firstFLACFramePosInBytes == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pcmFrameIndex == 0) { pFlac->currentPCMFrame = 0; - return drflac__seek_to_first_frame(pFlac); + return ma_dr_flac__seek_to_first_frame(pFlac); } else { - drflac_bool32 wasSuccessful = DRFLAC_FALSE; - drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; + ma_bool32 wasSuccessful = MA_FALSE; + ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; if (pcmFrameIndex > pFlac->totalPCMFrameCount) { pcmFrameIndex = pFlac->totalPCMFrameCount; } if (pcmFrameIndex > pFlac->currentPCMFrame) { - drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); + ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { pFlac->currentFLACFrame.pcmFramesRemaining -= offset; pFlac->currentPCMFrame = pcmFrameIndex; - return DRFLAC_TRUE; + return MA_TRUE; } } else { - drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); - drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); + ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; if (currentFLACFramePCMFramesConsumed > offsetAbs) { pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; pFlac->currentPCMFrame = pcmFrameIndex; - return DRFLAC_TRUE; + return MA_TRUE; } } -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { - wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); + wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); } else #endif { if (!pFlac->_noSeekTableSeek) { - wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); } -#if !defined(DR_FLAC_NO_CRC) +#if !defined(MA_DR_FLAC_NO_CRC) if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { - wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); } #endif if (!wasSuccessful && !pFlac->_noBruteForceSeek) { - wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); } } if (wasSuccessful) { pFlac->currentPCMFrame = pcmFrameIndex; } else { - if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { - drflac_seek_to_pcm_frame(pFlac, 0); + if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { + ma_dr_flac_seek_to_pcm_frame(pFlac, 0); } } return wasSuccessful; } } -#if defined(SIZE_MAX) - #define DRFLAC_SIZE_MAX SIZE_MAX -#else - #if defined(DRFLAC_64BIT) - #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRFLAC_SIZE_MAX 0xFFFFFFFF - #endif -#endif -#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ -static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ +#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ +static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ { \ type* pSampleData = NULL; \ - drflac_uint64 totalPCMFrameCount; \ + ma_uint64 totalPCMFrameCount; \ \ - DRFLAC_ASSERT(pFlac != NULL); \ + MA_DR_FLAC_ASSERT(pFlac != NULL); \ \ totalPCMFrameCount = pFlac->totalPCMFrameCount; \ \ if (totalPCMFrameCount == 0) { \ type buffer[4096]; \ - drflac_uint64 pcmFramesRead; \ + ma_uint64 pcmFramesRead; \ size_t sampleDataBufferSize = sizeof(buffer); \ \ - pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ + pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ - while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ + while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ type* pNewSampleData; \ size_t newSampleDataBufferSize; \ \ newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ + pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ if (pNewSampleData == NULL) { \ - drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ + ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ goto on_error; \ } \ \ @@ -89793,43 +89444,43 @@ static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned pSampleData = pNewSampleData; \ } \ \ - DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ + MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ totalPCMFrameCount += pcmFramesRead; \ } \ \ \ - DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ + MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ } else { \ - drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ + ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ + if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ goto on_error; \ } \ \ - pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ + pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ - totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ + totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ } \ \ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ if (channelsOut) *channelsOut = pFlac->channels; \ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ \ - drflac_close(pFlac); \ + ma_dr_flac_close(pFlac); \ return pSampleData; \ \ on_error: \ - drflac_close(pFlac); \ + ma_dr_flac_close(pFlac); \ return NULL; \ } -DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) -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) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -89839,15 +89490,15 @@ DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return ma_dr_flac__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) +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -89857,15 +89508,15 @@ DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return ma_dr_flac__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) +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -89875,16 +89526,16 @@ DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, d if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89894,15 +89545,15 @@ DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* fi if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_file(filename, pAllocationCallbacks); + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89912,15 +89563,15 @@ DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* fi if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_file(filename, pAllocationCallbacks); + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89930,16 +89581,16 @@ DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_file(filename, pAllocationCallbacks); + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } #endif -DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89949,15 +89600,15 @@ DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89967,15 +89618,15 @@ DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89985,21 +89636,21 @@ DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, s if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - drflac__free_from_callbacks(p, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); } else { - drflac__free_default(p, NULL); + ma_dr_flac__free_default(p, NULL); } } -DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) +MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) { if (pIter == NULL) { return; @@ -90007,9 +89658,9 @@ DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterat pIter->countRemaining = commentCount; pIter->pRunningData = (const char*)pComments; } -DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) +MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) { - drflac_int32 length; + ma_int32 length; const char* pComment; if (pCommentLengthOut) { *pCommentLengthOut = 0; @@ -90017,7 +89668,7 @@ DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { return NULL; } - length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); + length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); pIter->pRunningData += 4; pComment = pIter->pRunningData; pIter->pRunningData += length; @@ -90027,7 +89678,7 @@ DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator } return pComment; } -DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) +MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) { if (pIter == NULL) { return; @@ -90035,127 +89686,127 @@ DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterat pIter->countRemaining = trackCount; pIter->pRunningData = (const char*)pTrackData; } -DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) +MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) { - drflac_cuesheet_track cuesheetTrack; + ma_dr_flac_cuesheet_track cuesheetTrack; const char* pRunningData; - drflac_uint64 offsetHi; - drflac_uint64 offsetLo; + ma_uint64 offsetHi; + ma_uint64 offsetLo; if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } pRunningData = pIter->pRunningData; - offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; + offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; cuesheetTrack.offset = offsetLo | (offsetHi << 32); cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; - DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; + MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; - cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); + cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); pIter->pRunningData = pRunningData; pIter->countRemaining -= 1; if (pCuesheetTrack) { *pCuesheetTrack = cuesheetTrack; } - return DRFLAC_TRUE; + return MA_TRUE; } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif /* dr_flac_c end */ -#endif /* DRFLAC_IMPLEMENTATION */ +#endif /* MA_DR_FLAC_IMPLEMENTATION */ #endif /* MA_NO_FLAC */ #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_mp3_c begin */ -#ifndef dr_mp3_c -#define dr_mp3_c +#ifndef ma_dr_mp3_c +#define ma_dr_mp3_c #include #include #include -DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) +MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) { if (pMajor) { - *pMajor = DRMP3_VERSION_MAJOR; + *pMajor = MA_DR_MP3_VERSION_MAJOR; } if (pMinor) { - *pMinor = DRMP3_VERSION_MINOR; + *pMinor = MA_DR_MP3_VERSION_MINOR; } if (pRevision) { - *pRevision = DRMP3_VERSION_REVISION; + *pRevision = MA_DR_MP3_VERSION_REVISION; } } -DRMP3_API const char* drmp3_version_string(void) +MA_API const char* ma_dr_mp3_version_string(void) { - return DRMP3_VERSION_STRING; + return MA_DR_MP3_VERSION_STRING; } #if defined(__TINYC__) -#define DR_MP3_NO_SIMD +#define MA_DR_MP3_NO_SIMD #endif -#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) -#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 -#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES -#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 +#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) +#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 +#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES +#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 #endif -#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE -#define DRMP3_MAX_BITRESERVOIR_BYTES 511 -#define DRMP3_SHORT_BLOCK_TYPE 2 -#define DRMP3_STOP_BLOCK_TYPE 3 -#define DRMP3_MODE_MONO 3 -#define DRMP3_MODE_JOINT_STEREO 1 -#define DRMP3_HDR_SIZE 4 -#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) -#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) -#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) -#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) -#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) -#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) -#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) -#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) -#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) -#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) -#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) -#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) -#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) -#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) -#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) -#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) -#define DRMP3_BITS_DEQUANTIZER_OUT -1 -#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) -#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) -#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) -#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) -#if !defined(DR_MP3_NO_SIMD) -#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) -#define DR_MP3_ONLY_SIMD +#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE +#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 +#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 +#define MA_DR_MP3_STOP_BLOCK_TYPE 3 +#define MA_DR_MP3_MODE_MONO 3 +#define MA_DR_MP3_MODE_JOINT_STEREO 1 +#define MA_DR_MP3_HDR_SIZE 4 +#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) +#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 +#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) +#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) +#if !defined(MA_DR_MP3_NO_SIMD) +#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +#define MA_DR_MP3_ONLY_SIMD #endif #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) #if defined(_MSC_VER) #include #endif #include -#define DRMP3_HAVE_SSE 1 -#define DRMP3_HAVE_SIMD 1 -#define DRMP3_VSTORE _mm_storeu_ps -#define DRMP3_VLD _mm_loadu_ps -#define DRMP3_VSET _mm_set1_ps -#define DRMP3_VADD _mm_add_ps -#define DRMP3_VSUB _mm_sub_ps -#define DRMP3_VMUL _mm_mul_ps -#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) -#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) -#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) -#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) -typedef __m128 drmp3_f4; -#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) -#define drmp3_cpuid __cpuid +#define MA_DR_MP3_HAVE_SSE 1 +#define MA_DR_MP3_HAVE_SIMD 1 +#define MA_DR_MP3_VSTORE _mm_storeu_ps +#define MA_DR_MP3_VLD _mm_loadu_ps +#define MA_DR_MP3_VSET _mm_set1_ps +#define MA_DR_MP3_VADD _mm_add_ps +#define MA_DR_MP3_VSUB _mm_sub_ps +#define MA_DR_MP3_VMUL _mm_mul_ps +#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 ma_dr_mp3_f4; +#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) +#define ma_dr_mp3_cpuid __cpuid #else -static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) +static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) { #if defined(__PIC__) __asm__ __volatile__( @@ -90179,9 +89830,9 @@ static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], #endif } #endif -static int drmp3_have_simd(void) +static int ma_dr_mp3_have_simd(void) { -#ifdef DR_MP3_ONLY_SIMD +#ifdef MA_DR_MP3_ONLY_SIMD return 1; #else static int g_have_simd; @@ -90193,10 +89844,10 @@ static int drmp3_have_simd(void) #endif if (g_have_simd) goto end; - drmp3_cpuid(CPUInfo, 0); + ma_dr_mp3_cpuid(CPUInfo, 0); if (CPUInfo[0] > 0) { - drmp3_cpuid(CPUInfo, 1); + ma_dr_mp3_cpuid(CPUInfo, 1); g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; return g_have_simd - 1; } @@ -90206,108 +89857,108 @@ end: } #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) #include -#define DRMP3_HAVE_SSE 0 -#define DRMP3_HAVE_SIMD 1 -#define DRMP3_VSTORE vst1q_f32 -#define DRMP3_VLD vld1q_f32 -#define DRMP3_VSET vmovq_n_f32 -#define DRMP3_VADD vaddq_f32 -#define DRMP3_VSUB vsubq_f32 -#define DRMP3_VMUL vmulq_f32 -#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) -#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) -#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) -#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) -typedef float32x4_t drmp3_f4; -static int drmp3_have_simd(void) +#define MA_DR_MP3_HAVE_SSE 0 +#define MA_DR_MP3_HAVE_SIMD 1 +#define MA_DR_MP3_VSTORE vst1q_f32 +#define MA_DR_MP3_VLD vld1q_f32 +#define MA_DR_MP3_VSET vmovq_n_f32 +#define MA_DR_MP3_VADD vaddq_f32 +#define MA_DR_MP3_VSUB vsubq_f32 +#define MA_DR_MP3_VMUL vmulq_f32 +#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t ma_dr_mp3_f4; +static int ma_dr_mp3_have_simd(void) { return 1; } #else -#define DRMP3_HAVE_SSE 0 -#define DRMP3_HAVE_SIMD 0 -#ifdef DR_MP3_ONLY_SIMD -#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#define MA_DR_MP3_HAVE_SSE 0 +#define MA_DR_MP3_HAVE_SIMD 0 +#ifdef MA_DR_MP3_ONLY_SIMD +#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled #endif #endif #else -#define DRMP3_HAVE_SIMD 0 +#define MA_DR_MP3_HAVE_SIMD 0 #endif #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) -#define DRMP3_HAVE_ARMV6 1 -static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) +#define MA_DR_MP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) { - drmp3_int32 x = 0; + ma_int32 x = 0; __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); return x; } #else -#define DRMP3_HAVE_ARMV6 0 +#define MA_DR_MP3_HAVE_ARMV6 0 #endif -#ifndef DRMP3_ASSERT +#ifndef MA_DR_MP3_ASSERT #include -#define DRMP3_ASSERT(expression) assert(expression) +#define MA_DR_MP3_ASSERT(expression) assert(expression) #endif -#ifndef DRMP3_COPY_MEMORY -#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#ifndef MA_DR_MP3_COPY_MEMORY +#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#ifndef DRMP3_MOVE_MEMORY -#define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#ifndef MA_DR_MP3_MOVE_MEMORY +#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) #endif -#ifndef DRMP3_ZERO_MEMORY -#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#ifndef MA_DR_MP3_ZERO_MEMORY +#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) -#ifndef DRMP3_MALLOC -#define DRMP3_MALLOC(sz) malloc((sz)) +#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef MA_DR_MP3_MALLOC +#define MA_DR_MP3_MALLOC(sz) malloc((sz)) #endif -#ifndef DRMP3_REALLOC -#define DRMP3_REALLOC(p, sz) realloc((p), (sz)) +#ifndef MA_DR_MP3_REALLOC +#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) #endif -#ifndef DRMP3_FREE -#define DRMP3_FREE(p) free((p)) +#ifndef MA_DR_MP3_FREE +#define MA_DR_MP3_FREE(p) free((p)) #endif typedef struct { - const drmp3_uint8 *buf; + const ma_uint8 *buf; int pos, limit; -} drmp3_bs; +} ma_dr_mp3_bs; typedef struct { float scf[3*64]; - drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; -} drmp3_L12_scale_info; + ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} ma_dr_mp3_L12_scale_info; typedef struct { - drmp3_uint8 tab_offset, code_tab_width, band_count; -} drmp3_L12_subband_alloc; + ma_uint8 tab_offset, code_tab_width, band_count; +} ma_dr_mp3_L12_subband_alloc; typedef struct { - const drmp3_uint8 *sfbtab; - drmp3_uint16 part_23_length, big_values, scalefac_compress; - drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; - drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; -} drmp3_L3_gr_info; + const ma_uint8 *sfbtab; + ma_uint16 part_23_length, big_values, scalefac_compress; + ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + ma_uint8 table_select[3], region_count[3], subblock_gain[3]; + ma_uint8 preflag, scalefac_scale, count1_table, scfsi; +} ma_dr_mp3_L3_gr_info; typedef struct { - drmp3_bs bs; - drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - drmp3_L3_gr_info gr_info[4]; + ma_dr_mp3_bs bs; + ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + ma_dr_mp3_L3_gr_info gr_info[4]; float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - drmp3_uint8 ist_pos[2][39]; -} drmp3dec_scratch; -static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) + ma_uint8 ist_pos[2][39]; +} ma_dr_mp3dec_scratch; +static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) { bs->buf = data; bs->pos = 0; bs->limit = bytes*8; } -static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) +static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) { - drmp3_uint32 next, cache = 0, s = bs->pos & 7; + ma_uint32 next, cache = 0, s = bs->pos & 7; int shl = n + s; - const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); + const ma_uint8 *p = bs->buf + (bs->pos >> 3); if ((bs->pos += n) > bs->limit) return 0; next = *p++ & (255 >> s); @@ -90318,72 +89969,72 @@ static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) } return cache | (next >> -shl); } -static int drmp3_hdr_valid(const drmp3_uint8 *h) +static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) { return h[0] == 0xff && ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && - (DRMP3_HDR_GET_LAYER(h) != 0) && - (DRMP3_HDR_GET_BITRATE(h) != 15) && - (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); + (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && + (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && + (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); } -static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) +static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) { - return drmp3_hdr_valid(h2) && + return ma_dr_mp3_hdr_valid(h2) && ((h1[1] ^ h2[1]) & 0xFE) == 0 && ((h1[2] ^ h2[2]) & 0x0C) == 0 && - !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); + !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); } -static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) +static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) { - static const drmp3_uint8 halfrate[2][3][15] = { + static const ma_uint8 halfrate[2][3][15] = { { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, }; - return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; + return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; } -static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) +static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) { static const unsigned g_hz[3] = { 44100, 48000, 32000 }; - return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); + return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); } -static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) +static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) { - return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); + return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); } -static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) +static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) { - int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); - if (DRMP3_HDR_IS_LAYER_1(h)) + int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); + if (MA_DR_MP3_HDR_IS_LAYER_1(h)) { frame_bytes &= ~3; } return frame_bytes ? frame_bytes : free_format_size; } -static int drmp3_hdr_padding(const drmp3_uint8 *h) +static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) { - return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; + return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; } -#ifndef DR_MP3_ONLY_MP3 -static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) +#ifndef MA_DR_MP3_ONLY_MP3 +static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) { - const drmp3_L12_subband_alloc *alloc; - int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); - int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; - if (DRMP3_HDR_IS_LAYER_1(hdr)) + const ma_dr_mp3_L12_subband_alloc *alloc; + int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) { - static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; alloc = g_alloc_L1; nbands = 32; - } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) + } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) { - static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; alloc = g_alloc_L2M2; nbands = 30; } else { - static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; - int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); - unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); if (!kbps) { kbps = 192; @@ -90392,7 +90043,7 @@ static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_ nbands = 27; if (kbps < 56) { - static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; alloc = g_alloc_L2M1_lowrate; nbands = sample_rate_idx == 2 ? 12 : 8; } else if (kbps >= 96 && sample_rate_idx != 1) @@ -90400,15 +90051,15 @@ static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_ nbands = 30; } } - sci->total_bands = (drmp3_uint8)nbands; - sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); + sci->total_bands = (ma_uint8)nbands; + sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); return alloc; } -static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) +static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) { static const float g_deq_L12[18*3] = { -#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x - DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) +#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) }; int i, m; for (i = 0; i < bands; i++) @@ -90420,16 +90071,16 @@ static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_ui { if (mask & m) { - int b = drmp3_bs_get_bits(bs, 6); + int b = ma_dr_mp3_bs_get_bits(bs, 6); s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); } *scf++ = s; } } } -static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) +static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) { - static const drmp3_uint8 g_bitalloc_code_tab[] = { + static const ma_uint8 g_bitalloc_code_tab[] = { 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, 0,17,18, 3,19,4,5,16, @@ -90438,12 +90089,12 @@ static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 }; - const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); + const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); int i, k = 0, ba_bits = 0; - const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; + const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; for (i = 0; i < sci->total_bands; i++) { - drmp3_uint8 ba; + ma_uint8 ba; if (i == k) { k += subband_alloc->band_count; @@ -90451,25 +90102,25 @@ static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; subband_alloc++; } - ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; sci->bitalloc[2*i] = ba; if (i < sci->stereo_bands) { - ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; } sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; } for (i = 0; i < 2*sci->total_bands; i++) { - sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); + sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); } - drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); for (i = sci->stereo_bands; i < sci->total_bands; i++) { sci->bitalloc[2*i + 1] = 0; } } -static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) +static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) { int i, j, k, choff = 576; for (j = 0; j < 4; j++) @@ -90485,12 +90136,12 @@ static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_sc int half = (1 << (ba - 1)) - 1; for (k = 0; k < group_size; k++) { - dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); + dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); } } else { unsigned mod = (2 << (ba - 17)) + 1; - unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); + unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); for (k = 0; k < group_size; k++, code /= mod) { dst[k] = (float)((int)(code % mod - mod/2)); @@ -90503,10 +90154,10 @@ static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_sc } return group_size*4; } -static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) +static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) { int i, k; - DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) { for (k = 0; k < 12; k++) @@ -90517,9 +90168,9 @@ static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, } } #endif -static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) { - static const drmp3_uint8 g_scf_long[8][23] = { + static const ma_uint8 g_scf_long[8][23] = { { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, @@ -90529,7 +90180,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } }; - static const drmp3_uint8 g_scf_short[8][40] = { + static const ma_uint8 g_scf_short[8][40] = { { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, @@ -90539,7 +90190,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } }; - static const drmp3_uint8 g_scf_mixed[8][40] = { + static const ma_uint8 g_scf_mixed[8][40] = { { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, @@ -90551,46 +90202,46 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm }; unsigned tables, scfsi = 0; int main_data_begin, part_23_sum = 0; - int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; - int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); - if (DRMP3_HDR_TEST_MPEG1(hdr)) + int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; + int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) { gr_count *= 2; - main_data_begin = drmp3_bs_get_bits(bs, 9); - scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); + main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); + scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); } else { - main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; } do { - if (DRMP3_HDR_IS_MONO(hdr)) + if (MA_DR_MP3_HDR_IS_MONO(hdr)) { scfsi <<= 4; } - gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); + gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); part_23_sum += gr->part_23_length; - gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); + gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); if (gr->big_values > 288) { return -1; } - gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); - gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); gr->sfbtab = g_scf_long[sr_idx]; gr->n_long_sfb = 22; gr->n_short_sfb = 0; - if (drmp3_bs_get_bits(bs, 1)) + if (ma_dr_mp3_bs_get_bits(bs, 1)) { - gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); + gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); if (!gr->block_type) { return -1; } - gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); gr->region_count[0] = 7; gr->region_count[1] = 255; - if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) + if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) { scfsi &= 0x0F0F; if (!gr->mixed_block_flag) @@ -90602,31 +90253,31 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm } else { gr->sfbtab = g_scf_mixed[sr_idx]; - gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; gr->n_short_sfb = 30; } } - tables = drmp3_bs_get_bits(bs, 10); + tables = ma_dr_mp3_bs_get_bits(bs, 10); tables <<= 5; - gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); - gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); - gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); } else { gr->block_type = 0; gr->mixed_block_flag = 0; - tables = drmp3_bs_get_bits(bs, 15); - gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); - gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + tables = ma_dr_mp3_bs_get_bits(bs, 15); + gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); + gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); gr->region_count[2] = 255; } - gr->table_select[0] = (drmp3_uint8)(tables >> 10); - gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); - gr->table_select[2] = (drmp3_uint8)((tables) & 31); - gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); - gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); - gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); - gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); + gr->table_select[0] = (ma_uint8)(tables >> 10); + gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); + gr->table_select[2] = (ma_uint8)((tables) & 31); + gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); scfsi <<= 4; gr++; } while(--gr_count); @@ -90636,7 +90287,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm } return main_data_begin; } -static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) +static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) { int i, k; for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) @@ -90644,22 +90295,22 @@ static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, c int cnt = scf_count[i]; if (scfsi & 8) { - DRMP3_COPY_MEMORY(scf, ist_pos, cnt); + MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); } else { int bits = scf_size[i]; if (!bits) { - DRMP3_ZERO_MEMORY(scf, cnt); - DRMP3_ZERO_MEMORY(ist_pos, cnt); + MA_DR_MP3_ZERO_MEMORY(scf, cnt); + MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); } else { int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; for (k = 0; k < cnt; k++) { - int s = drmp3_bs_get_bits(bitbuf, bits); - ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); - scf[k] = (drmp3_uint8)s; + int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); + scf[k] = (ma_uint8)s; } } } @@ -90668,86 +90319,86 @@ static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, c } scf[0] = scf[1] = scf[2] = 0; } -static float drmp3_L3_ldexp_q2(float y, int exp_q2) +static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) { static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; int e; do { - e = DRMP3_MIN(30*4, exp_q2); + e = MA_DR_MP3_MIN(30*4, exp_q2); y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); } while ((exp_q2 -= e) > 0); return y; } -static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) +static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) { - static const drmp3_uint8 g_scf_partitions[3][28] = { + static const ma_uint8 g_scf_partitions[3][28] = { { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } }; - const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; - drmp3_uint8 scf_size[4], iscf[40]; + const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + ma_uint8 scf_size[4], iscf[40]; int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; float gain; - if (DRMP3_HDR_TEST_MPEG1(hdr)) + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) { - static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; int part = g_scfc_decode[gr->scalefac_compress]; - scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); - scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); + scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); } else { - static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; - int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; + static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; sfc = gr->scalefac_compress >> ist; for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) { for (modprod = 1, i = 3; i >= 0; i--) { - scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); + scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); modprod *= g_mod[k + i]; } } scf_partition += k; scfsi = -16; } - drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); if (gr->n_short_sfb) { int sh = 3 - scf_shift; for (i = 0; i < gr->n_short_sfb; i += 3) { - iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); - iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); - iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); + iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); + iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); + iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); } } else if (gr->preflag) { - static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; for (i = 0; i < 10; i++) { - iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); + iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); } } - gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); - gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); + gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) { - scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); } } -static const float g_drmp3_pow43[129 + 16] = { +static const float g_ma_dr_mp3_pow43[129 + 16] = { 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f }; -static float drmp3_L3_pow_43(int x) +static float ma_dr_mp3_L3_pow_43(int x) { float frac; int sign, mult = 256; if (x < 129) { - return g_drmp3_pow43[16 + x]; + return g_ma_dr_mp3_pow43[16 + x]; } if (x < 1024) { @@ -90756,11 +90407,11 @@ static float drmp3_L3_pow_43(int x) } sign = 2*x & 64; frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; + return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; } -static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { - static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, @@ -90776,61 +90427,61 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; - static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; - static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; - static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) -#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } -#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } -#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; +#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) +#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) float one = 0.0f; int ireg = 0, big_val_cnt = gr_info->big_values; - const drmp3_uint8 *sfb = gr_info->sfbtab; - const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; - drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + const ma_uint8 *sfb = gr_info->sfbtab; + const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; bs_next_ptr += 4; while (big_val_cnt > 0) { int tab_num = gr_info->table_select[ireg]; int sfb_cnt = gr_info->region_count[ireg++]; - const drmp3_int16 *codebook = tabs + tabindex[tab_num]; + const ma_int16 *codebook = tabs + tabindex[tab_num]; int linbits = g_linbits[tab_num]; if (linbits) { do { np = *sfb++ / 2; - pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); one = *scf++; do { int j, w = 5; - int leaf = codebook[DRMP3_PEEK_BITS(w)]; + int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; while (leaf < 0) { - DRMP3_FLUSH_BITS(w); + MA_DR_MP3_FLUSH_BITS(w); w = leaf & 7; - leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; } - DRMP3_FLUSH_BITS(leaf >> 8); + MA_DR_MP3_FLUSH_BITS(leaf >> 8); for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; if (lsb == 15) { - lsb += DRMP3_PEEK_BITS(linbits); - DRMP3_FLUSH_BITS(linbits); - DRMP3_CHECK_BITS; - *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); + lsb += MA_DR_MP3_PEEK_BITS(linbits); + MA_DR_MP3_FLUSH_BITS(linbits); + MA_DR_MP3_CHECK_BITS; + *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); } else { - *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; } - DRMP3_FLUSH_BITS(lsb ? 1 : 0); + MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); } - DRMP3_CHECK_BITS; + MA_DR_MP3_CHECK_BITS; } while (--pairs_to_decode); } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); } else @@ -90838,68 +90489,68 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g do { np = *sfb++ / 2; - pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); one = *scf++; do { int j, w = 5; - int leaf = codebook[DRMP3_PEEK_BITS(w)]; + int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; while (leaf < 0) { - DRMP3_FLUSH_BITS(w); + MA_DR_MP3_FLUSH_BITS(w); w = leaf & 7; - leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; } - DRMP3_FLUSH_BITS(leaf >> 8); + MA_DR_MP3_FLUSH_BITS(leaf >> 8); for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; - *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - DRMP3_FLUSH_BITS(lsb ? 1 : 0); + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); } - DRMP3_CHECK_BITS; + MA_DR_MP3_CHECK_BITS; } while (--pairs_to_decode); } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); } } for (np = 1 - big_val_cnt;; dst += 4) { - const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; - int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; + const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; if (!(leaf & 8)) { leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; } - DRMP3_FLUSH_BITS(leaf & 7); - if (DRMP3_BSPOS > layer3gr_limit) + MA_DR_MP3_FLUSH_BITS(leaf & 7); + if (MA_DR_MP3_BSPOS > layer3gr_limit) { break; } -#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } -#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } - DRMP3_RELOAD_SCALEFACTOR; - DRMP3_DEQ_COUNT1(0); - DRMP3_DEQ_COUNT1(1); - DRMP3_RELOAD_SCALEFACTOR; - DRMP3_DEQ_COUNT1(2); - DRMP3_DEQ_COUNT1(3); - DRMP3_CHECK_BITS; +#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } + MA_DR_MP3_RELOAD_SCALEFACTOR; + MA_DR_MP3_DEQ_COUNT1(0); + MA_DR_MP3_DEQ_COUNT1(1); + MA_DR_MP3_RELOAD_SCALEFACTOR; + MA_DR_MP3_DEQ_COUNT1(2); + MA_DR_MP3_DEQ_COUNT1(3); + MA_DR_MP3_CHECK_BITS; } bs->pos = layer3gr_limit; } -static void drmp3_L3_midside_stereo(float *left, int n) +static void ma_dr_mp3_L3_midside_stereo(float *left, int n) { int i = 0; float *right = left + 576; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) { for (; i < n - 3; i += 4) { - drmp3_f4 vl = DRMP3_VLD(left + i); - drmp3_f4 vr = DRMP3_VLD(right + i); - DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); - DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); + ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); + ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); + MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); + MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); } #ifdef __GNUC__ if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) @@ -90915,7 +90566,7 @@ static void drmp3_L3_midside_stereo(float *left, int n) right[i] = a - b; } } -static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) { int i; for (i = 0; i < n; i++) @@ -90924,7 +90575,7 @@ static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float k left[i] = left[i]*kl; } } -static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) +static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) { int i, k; max_band[0] = max_band[1] = max_band[2] = -1; @@ -90941,57 +90592,57 @@ static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, right += sfb[i]; } } -static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) +static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) { static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; - unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; for (i = 0; sfb[i]; i++) { unsigned ipos = ist_pos[i]; if ((int)i > max_band[i % 3] && ipos < max_pos) { - float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; - if (DRMP3_HDR_TEST_MPEG1(hdr)) + float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) { kl = g_pan[2*ipos]; kr = g_pan[2*ipos + 1]; } else { kl = 1; - kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); if (ipos & 1) { kl = kr; kr = 1; } } - drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); - } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) + ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) { - drmp3_L3_midside_stereo(left, sfb[i]); + ma_dr_mp3_L3_midside_stereo(left, sfb[i]); } left += sfb[i]; } } -static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) { int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; int i, max_blocks = gr->n_short_sfb ? 3 : 1; - drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); if (gr->n_long_sfb) { - max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); + max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); } for (i = 0; i < max_blocks; i++) { - int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; int itop = n_sfb - max_blocks + i; int prev = itop - max_blocks; - ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); } - drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); + ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); } -static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) +static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) { int i, len; float *src = grbuf, *dst = scratch; @@ -91004,9 +90655,9 @@ static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sf *dst++ = src[2*len]; } } - DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); + MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); } -static void drmp3_L3_antialias(float *grbuf, int nbands) +static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) { static const float g_aa[2][8] = { {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, @@ -91015,20 +90666,20 @@ static void drmp3_L3_antialias(float *grbuf, int nbands) for (; nbands > 0; nbands--, grbuf += 18) { int i = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; i < 8; i += 4) +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) { - drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); - drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); - drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); - drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); - vd = DRMP3_VREV(vd); - DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); - vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); - DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); + ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); + ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); + ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); + ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); + vd = MA_DR_MP3_VREV(vd); + MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); + vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); + MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); } #endif -#ifndef DR_MP3_ONLY_SIMD +#ifndef MA_DR_MP3_ONLY_SIMD for(; i < 8; i++) { float u = grbuf[18 + i]; @@ -91039,7 +90690,7 @@ static void drmp3_L3_antialias(float *grbuf, int nbands) #endif } } -static void drmp3_L3_dct3_9(float *y) +static void ma_dr_mp3_L3_dct3_9(float *y) { float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; @@ -91072,7 +90723,7 @@ static void drmp3_L3_dct3_9(float *y) y[7] = s2 - s1; y[8] = s4 + s7; } -static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) { int i, j; static const float g_twid9[18] = { @@ -91090,28 +90741,28 @@ static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); } - drmp3_L3_dct3_9(co); - drmp3_L3_dct3_9(si); + ma_dr_mp3_L3_dct3_9(co); + ma_dr_mp3_L3_dct3_9(si); si[1] = -si[1]; si[3] = -si[3]; si[5] = -si[5]; si[7] = -si[7]; i = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; i < 8; i += 4) +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) { - drmp3_f4 vovl = DRMP3_VLD(overlap + i); - drmp3_f4 vc = DRMP3_VLD(co + i); - drmp3_f4 vs = DRMP3_VLD(si + i); - drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); - drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); - drmp3_f4 vw0 = DRMP3_VLD(window + i); - drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); - drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); - DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); - DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); - vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); - DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); + ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); + ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); + ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); + ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); + ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); + ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); + ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); + ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); + MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); + MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); + vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); + MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); } #endif for (; i < 9; i++) @@ -91124,7 +90775,7 @@ static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, } } } -static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) +static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) { float m1 = x1*0.86602540f; float a1 = x0 - x2*0.5f; @@ -91132,13 +90783,13 @@ static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) dst[0] = a1 + m1; dst[2] = a1 - m1; } -static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) +static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) { static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; float co[3], si[3]; int i; - drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); - drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); si[1] = -si[1]; for (i = 0; i < 3; i++) { @@ -91149,26 +90800,26 @@ static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; } } -static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) { for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) { float tmp[18]; - DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); - DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); - drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); - drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); - drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); + MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); + ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); } } -static void drmp3_L3_change_sign(float *grbuf) +static void ma_dr_mp3_L3_change_sign(float *grbuf) { int b, i; for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) for (i = 1; i < 18; i += 2) grbuf[i] = -grbuf[i]; } -static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) { static const float g_mdct_window[2][18] = { { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, @@ -91176,159 +90827,159 @@ static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, }; if (n_long_bands) { - drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); grbuf += 18*n_long_bands; overlap += 9*n_long_bands; } - if (block_type == DRMP3_SHORT_BLOCK_TYPE) - drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) + ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); else - drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); + ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); } -static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) +static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) { int pos = (s->bs.pos + 7)/8u; int remains = s->bs.limit/8u - pos; - if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) + if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) { - pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; - remains = DRMP3_MAX_BITRESERVOIR_BYTES; + pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; + remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; } if (remains > 0) { - DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); + MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); } h->reserv = remains; } -static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) +static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) { int frame_bytes = (bs->limit - bs->pos)/8; - int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); - DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); - DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); - drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); + MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); + MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); return h->reserv >= main_data_begin; } -static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) +static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) { int ch; for (ch = 0; ch < nch; ch++) { int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; - drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); - drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); } - if (DRMP3_HDR_TEST_I_STEREO(h->header)) + if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) { - drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); - } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) + ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) { - drmp3_L3_midside_stereo(s->grbuf[0], 576); + ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); } for (ch = 0; ch < nch; ch++, gr_info++) { int aa_bands = 31; - int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); if (gr_info->n_short_sfb) { aa_bands = n_long_bands - 1; - drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); } - drmp3_L3_antialias(s->grbuf[ch], aa_bands); - drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); - drmp3_L3_change_sign(s->grbuf[ch]); + ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); + ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + ma_dr_mp3_L3_change_sign(s->grbuf[ch]); } } -static void drmp3d_DCT_II(float *grbuf, int n) +static void ma_dr_mp3d_DCT_II(float *grbuf, int n) { static const float g_sec[24] = { 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f }; int i, k = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; k < n; k += 4) +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) { - drmp3_f4 t[4][8], *x; + ma_dr_mp3_f4 t[4][8], *x; float *y = grbuf + k; for (x = t[0], i = 0; i < 8; i++, x++) { - drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); - drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); - drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); - drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); - drmp3_f4 t0 = DRMP3_VADD(x0, x3); - drmp3_f4 t1 = DRMP3_VADD(x1, x2); - drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); - drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); - x[0] = DRMP3_VADD(t0, t1); - x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); - x[16] = DRMP3_VADD(t3, t2); - x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); + ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); + ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); + ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); + ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); + ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); + ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); + ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); + ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = MA_DR_MP3_VADD(t0, t1); + x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = MA_DR_MP3_VADD(t3, t2); + x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); } for (x = t[0], i = 0; i < 4; i++, x += 8) { - drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); - x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); - x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); - x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); - x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); - x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); - x[0] = DRMP3_VADD(x0, x1); - x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); - x5 = DRMP3_VADD(x5, x6); - x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); - x7 = DRMP3_VADD(x7, xt); - x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); - x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); - x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); - x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); - x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); - x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); - x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); - x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); - x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); - x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); - x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); + ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); + x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); + x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); + x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); + x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); + x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); + x[0] = MA_DR_MP3_VADD(x0, x1); + x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); + x5 = MA_DR_MP3_VADD(x5, x6); + x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); + x7 = MA_DR_MP3_VADD(x7, xt); + x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); + x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); + x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); + x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); + x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); + x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); + x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); + x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); + x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); + x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); + x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); } if (k > n - 3) { -#if DRMP3_HAVE_SSE -#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#if MA_DR_MP3_HAVE_SSE +#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) #else -#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) +#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) #endif for (i = 0; i < 7; i++, y += 4*18) { - drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); - DRMP3_VSAVE2(0, t[0][i]); - DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); - DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); - DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); + ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); + MA_DR_MP3_VSAVE2(0, t[0][i]); + MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); + MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); + MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); } - DRMP3_VSAVE2(0, t[0][7]); - DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); - DRMP3_VSAVE2(2, t[1][7]); - DRMP3_VSAVE2(3, t[3][7]); + MA_DR_MP3_VSAVE2(0, t[0][7]); + MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); + MA_DR_MP3_VSAVE2(2, t[1][7]); + MA_DR_MP3_VSAVE2(3, t[3][7]); } else { -#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) +#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) for (i = 0; i < 7; i++, y += 4*18) { - drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); - DRMP3_VSAVE4(0, t[0][i]); - DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); - DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); - DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); + ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); + MA_DR_MP3_VSAVE4(0, t[0][i]); + MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); + MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); + MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); } - DRMP3_VSAVE4(0, t[0][7]); - DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); - DRMP3_VSAVE4(2, t[1][7]); - DRMP3_VSAVE4(3, t[3][7]); + MA_DR_MP3_VSAVE4(0, t[0][7]); + MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); + MA_DR_MP3_VSAVE4(2, t[1][7]); + MA_DR_MP3_VSAVE4(3, t[3][7]); } } else #endif -#ifdef DR_MP3_ONLY_SIMD +#ifdef MA_DR_MP3_ONLY_SIMD {} #else for (; k < n; k++) @@ -91389,31 +91040,31 @@ static void drmp3d_DCT_II(float *grbuf, int n) } #endif } -#ifndef DR_MP3_FLOAT_OUTPUT -typedef drmp3_int16 drmp3d_sample_t; -static drmp3_int16 drmp3d_scale_pcm(float sample) +#ifndef MA_DR_MP3_FLOAT_OUTPUT +typedef ma_int16 ma_dr_mp3d_sample_t; +static ma_int16 ma_dr_mp3d_scale_pcm(float sample) { - drmp3_int16 s; -#if DRMP3_HAVE_ARMV6 - drmp3_int32 s32 = (drmp3_int32)(sample + .5f); + ma_int16 s; +#if MA_DR_MP3_HAVE_ARMV6 + ma_int32 s32 = (ma_int32)(sample + .5f); s32 -= (s32 < 0); - s = (drmp3_int16)drmp3_clip_int16_arm(s32); + s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); #else - if (sample >= 32766.5) return (drmp3_int16) 32767; - if (sample <= -32767.5) return (drmp3_int16)-32768; - s = (drmp3_int16)(sample + .5f); + if (sample >= 32766.5) return (ma_int16) 32767; + if (sample <= -32767.5) return (ma_int16)-32768; + s = (ma_int16)(sample + .5f); s -= (s < 0); #endif return s; } #else -typedef float drmp3d_sample_t; -static float drmp3d_scale_pcm(float sample) +typedef float ma_dr_mp3d_sample_t; +static float ma_dr_mp3d_scale_pcm(float sample) { return sample*(1.f/32768.f); } #endif -static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) +static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) { float a; a = (z[14*64] - z[ 0]) * 29; @@ -91424,7 +91075,7 @@ static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) a += (z[ 5*64] + z[ 9*64]) * 6574; a += (z[ 8*64] - z[ 6*64]) * 37489; a += z[ 7*64] * 75038; - pcm[0] = drmp3d_scale_pcm(a); + pcm[0] = ma_dr_mp3d_scale_pcm(a); z += 2; a = z[14*64] * 104; a += z[12*64] * 1567; @@ -91434,13 +91085,13 @@ static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) a += z[ 4*64] * -45; a += z[ 2*64] * 146; a += z[ 0*64] * -5; - pcm[16*nch] = drmp3d_scale_pcm(a); + pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); } -static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) +static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) { int i; float *xr = xl + 576*(nch - 1); - drmp3d_sample_t *dstr = dstl + (nch - 1); + ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1); static const float g_win[] = { -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, @@ -91468,18 +91119,18 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) zlin[4*31 + 1] = xr[1 + 18*16]; zlin[4*31 + 2] = xl[1]; zlin[4*31 + 3] = xr[1]; - drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); - drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); - drmp3d_synth_pair(dstl, nch, lins + 4*15); - drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (i = 14; i >= 0; i--) + ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); + ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) { -#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); -#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } -#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } -#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } - drmp3_f4 a, b; +#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } +#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } +#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } + ma_dr_mp3_f4 a, b; zlin[4*i] = xl[18*(31 - i)]; zlin[4*i + 1] = xr[18*(31 - i)]; zlin[4*i + 2] = xl[1 + 18*(31 - i)]; @@ -91488,28 +91139,28 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; zlin[4*i - 64 + 2] = xl[18*(1 + i)]; zlin[4*i - 64 + 3] = xr[18*(1 + i)]; - DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) + MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) { -#ifndef DR_MP3_FLOAT_OUTPUT -#if DRMP3_HAVE_SSE - static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; +#ifndef MA_DR_MP3_FLOAT_OUTPUT +#if MA_DR_MP3_HAVE_SSE + static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); #else int16x4_t pcma, pcmb; - a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); - b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); + b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); @@ -91520,14 +91171,14 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); #endif #else - #if DRMP3_HAVE_SSE - static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #if MA_DR_MP3_HAVE_SSE + static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; #else - const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); #endif - a = DRMP3_VMUL(a, g_scale); - b = DRMP3_VMUL(b, g_scale); -#if DRMP3_HAVE_SSE + a = MA_DR_MP3_VMUL(a, g_scale); + b = MA_DR_MP3_VMUL(b, g_scale); +#if MA_DR_MP3_HAVE_SSE _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); @@ -91550,15 +91201,15 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) } } else #endif -#ifdef DR_MP3_ONLY_SIMD +#ifdef MA_DR_MP3_ONLY_SIMD {} #else for (i = 14; i >= 0; i--) { -#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; -#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } -#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } -#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } +#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } float a[4], b[4]; zlin[4*i] = xl[18*(31 - i)]; zlin[4*i + 1] = xr[18*(31 - i)]; @@ -91568,31 +91219,31 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; - DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) - dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); - dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); - dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); - dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); - dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); - dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); - dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); - dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); + MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) + dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); } #endif } -static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) +static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) { int i; for (i = 0; i < nch; i++) { - drmp3d_DCT_II(grbuf + 576*i, nbands); + ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); } - DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); + MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); for (i = 0; i < nbands; i += 2) { - drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); } -#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL +#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL if (nch == 1) { for (i = 0; i < 15*64; i += 2) @@ -91602,38 +91253,38 @@ static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int } else #endif { - DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); + MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); } } -static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) +static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) { int i, nmatch; - for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) { - i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); - if (i + DRMP3_HDR_SIZE > mp3_bytes) + i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); + if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) return nmatch > 0; - if (!drmp3_hdr_compare(hdr, hdr + i)) + if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) return 0; } return 1; } -static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) { int i, k; - for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) + for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) { - if (drmp3_hdr_valid(mp3)) + if (ma_dr_mp3_hdr_valid(mp3)) { - int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); - int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); - for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) + int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); + for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) { - if (drmp3_hdr_compare(mp3, mp3 + k)) + if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) { - int fb = k - drmp3_hdr_padding(mp3); - int nextfb = fb + drmp3_hdr_padding(mp3 + k); - if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) + int fb = k - ma_dr_mp3_hdr_padding(mp3); + int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); + if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) continue; frame_and_padding = k; frame_bytes = fb; @@ -91641,7 +91292,7 @@ static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_fo } } if ((frame_bytes && i + frame_and_padding <= mp3_bytes && - drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || (!i && frame_and_padding == mp3_bytes)) { *ptr_frame_bytes = frame_and_padding; @@ -91653,28 +91304,28 @@ static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_fo *ptr_frame_bytes = 0; return mp3_bytes; } -DRMP3_API void drmp3dec_init(drmp3dec *dec) +MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) { dec->header[0] = 0; } -DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) +MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) { int i = 0, igr, frame_size = 0, success = 1; - const drmp3_uint8 *hdr; - drmp3_bs bs_frame[1]; - drmp3dec_scratch scratch; - if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) + const ma_uint8 *hdr; + ma_dr_mp3_bs bs_frame[1]; + ma_dr_mp3dec_scratch scratch; + if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) { - frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); - if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) + frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) { frame_size = 0; } } if (!frame_size) { - DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); - i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); + i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); if (!frame_size || i + frame_size > mp3_bytes) { info->frame_bytes = i; @@ -91682,96 +91333,96 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m } } hdr = mp3 + i; - DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); + MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_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->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); - info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); - drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); - if (DRMP3_HDR_IS_CRC(hdr)) + info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); + ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); + if (MA_DR_MP3_HDR_IS_CRC(hdr)) { - drmp3_bs_get_bits(bs_frame, 16); + ma_dr_mp3_bs_get_bits(bs_frame, 16); } if (info->layer == 3) { - int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) { - drmp3dec_init(dec); + ma_dr_mp3dec_init(dec); return 0; } - success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); if (success && pcm != NULL) { - for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) + for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) { - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); } } - drmp3_L3_save_reservoir(dec, &scratch); + ma_dr_mp3_L3_save_reservoir(dec, &scratch); } else { -#ifdef DR_MP3_ONLY_MP3 +#ifdef MA_DR_MP3_ONLY_MP3 return 0; #else - drmp3_L12_scale_info sci[1]; + ma_dr_mp3_L12_scale_info sci[1]; if (pcm == NULL) { - return drmp3_hdr_frame_samples(hdr); + return ma_dr_mp3_hdr_frame_samples(hdr); } - drmp3_L12_read_scale_info(hdr, bs_frame, sci); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); for (i = 0, igr = 0; igr < 3; igr++) { - if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) { i = 0; - drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); + ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); } if (bs_frame->pos > bs_frame->limit) { - drmp3dec_init(dec); + ma_dr_mp3dec_init(dec); return 0; } } #endif } - return success*drmp3_hdr_frame_samples(dec->header); + return success*ma_dr_mp3_hdr_frame_samples(dec->header); } -DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) +MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) { size_t i = 0; -#if DRMP3_HAVE_SIMD +#if MA_DR_MP3_HAVE_SIMD size_t aligned_count = num_samples & ~7; for(; i < aligned_count; i+=8) { - drmp3_f4 scale = DRMP3_VSET(32768.0f); - drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); - drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale); -#if DRMP3_HAVE_SSE - drmp3_f4 s16max = DRMP3_VSET( 32767.0f); - drmp3_f4 s16min = DRMP3_VSET(-32768.0f); + ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); + ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); + ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); +#if MA_DR_MP3_HAVE_SSE + ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); + ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); - out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); - out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); - out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); - out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); - out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); - out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); - out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); - out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); #else int16x4_t pcma, pcmb; - a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); - b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); + b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); vst1_lane_s16(out+i , pcma, 0); vst1_lane_s16(out+i+1, pcma, 1); vst1_lane_s16(out+i+2, pcma, 2); @@ -91787,78 +91438,69 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num { float sample = in[i] * 32768.0f; if (sample >= 32766.5) - out[i] = (drmp3_int16) 32767; + out[i] = (ma_int16) 32767; else if (sample <= -32767.5) - out[i] = (drmp3_int16)-32768; + out[i] = (ma_int16)-32768; else { - short s = (drmp3_int16)(sample + .5f); + short s = (ma_int16)(sample + .5f); s -= (s < 0); out[i] = s; } } } -#if defined(SIZE_MAX) - #define DRMP3_SIZE_MAX SIZE_MAX -#else - #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) - #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRMP3_SIZE_MAX 0xFFFFFFFF - #endif +#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES +#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 #endif -#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES -#define DRMP3_SEEK_LEADING_MP3_FRAMES 2 +#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 +#ifndef MA_DR_MP3_DATA_CHUNK_SIZE +#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) #endif -#define DRMP3_MIN_DATA_CHUNK_SIZE 16384 -#ifndef DRMP3_DATA_CHUNK_SIZE -#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) +#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) +#ifndef MA_DR_MP3_PI_D +#define MA_DR_MP3_PI_D 3.14159265358979323846264 #endif -#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) -#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) -#ifndef DRMP3_PI_D -#define DRMP3_PI_D 3.14159265358979323846264 -#endif -#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 -static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) +#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 +static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) { return x*(1-a) + y*a; } -static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) +static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) { float r0 = (y - x); float r1 = r0*a; return x + r1; } -static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) +static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) { for (;;) { if (b == 0) { break; } else { - drmp3_uint32 t = a; + ma_uint32 t = a; a = b; b = t % a; } } return a; } -static void* drmp3__malloc_default(size_t sz, void* pUserData) +static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return DRMP3_MALLOC(sz); + return MA_DR_MP3_MALLOC(sz); } -static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) +static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return DRMP3_REALLOC(p, sz); + return MA_DR_MP3_REALLOC(p, sz); } -static void drmp3__free_default(void* p, void* pUserData) +static void ma_dr_mp3__free_default(void* p, void* pUserData) { (void)pUserData; - DRMP3_FREE(p); + MA_DR_MP3_FREE(p); } -static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -91871,7 +91513,7 @@ static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_call } return NULL; } -static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) +static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -91886,14 +91528,14 @@ static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, return NULL; } if (p != NULL) { - DRMP3_COPY_MEMORY(p2, p, szOld); + MA_DR_MP3_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -91902,111 +91544,114 @@ static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) +static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return *pAllocationCallbacks; } else { - drmp3_allocation_callbacks allocationCallbacks; + ma_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drmp3__malloc_default; - allocationCallbacks.onRealloc = drmp3__realloc_default; - allocationCallbacks.onFree = drmp3__free_default; + allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; + allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; + allocationCallbacks.onFree = ma_dr_mp3__free_default; return allocationCallbacks; } } -static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) +static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) { size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); pMP3->streamCursor += bytesRead; return bytesRead; } -static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) +static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) { - DRMP3_ASSERT(offset >= 0); + MA_DR_MP3_ASSERT(offset >= 0); if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { - return DRMP3_FALSE; + return MA_FALSE; } - if (origin == drmp3_seek_origin_start) { - pMP3->streamCursor = (drmp3_uint64)offset; + if (origin == ma_dr_mp3_seek_origin_start) { + pMP3->streamCursor = (ma_uint64)offset; } else { pMP3->streamCursor += offset; } - return DRMP3_TRUE; + return MA_TRUE; } -static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) +static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) { if (offset <= 0x7FFFFFFF) { - return drmp3__on_seek(pMP3, (int)offset, origin); + return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); } - if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) { - return DRMP3_FALSE; + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; } offset -= 0x7FFFFFFF; while (offset > 0) { if (offset <= 0x7FFFFFFF) { - if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) { - return DRMP3_FALSE; + if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { + return MA_FALSE; } offset = 0; } else { - if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) { - return DRMP3_FALSE; + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { + return MA_FALSE; } offset -= 0x7FFFFFFF; } } - return DRMP3_TRUE; + return MA_TRUE; } -static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { - drmp3_uint32 pcmFramesRead = 0; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onRead != NULL); + ma_uint32 pcmFramesRead = 0; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); if (pMP3->atEnd) { return 0; } for (;;) { - drmp3dec_frame_info info; - if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { + ma_dr_mp3dec_frame_info info; + if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { size_t bytesRead; if (pMP3->pData != NULL) { - DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); } pMP3->dataConsumed = 0; - if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { - drmp3_uint8* pNewData; + if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { + ma_uint8* pNewData; size_t newDataCap; - newDataCap = DRMP3_DATA_CHUNK_SIZE; - pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); if (pNewData == NULL) { return 0; } pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { if (pMP3->dataSize == 0) { - pMP3->atEnd = DRMP3_TRUE; + pMP3->atEnd = MA_TRUE; return 0; } } pMP3->dataSize += bytesRead; } if (pMP3->dataSize > INT_MAX) { - pMP3->atEnd = DRMP3_TRUE; + pMP3->atEnd = MA_TRUE; return 0; } - DRMP3_ASSERT(pMP3->pData != NULL); - DRMP3_ASSERT(pMP3->dataCapacity > 0); - pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); + MA_DR_MP3_ASSERT(pMP3->pData != NULL); + MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); + if (pMP3->pData == NULL) { + return 0; + } + pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); if (info.frame_bytes > 0) { pMP3->dataConsumed += (size_t)info.frame_bytes; pMP3->dataSize -= (size_t)info.frame_bytes; } if (pcmFramesRead > 0) { - pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; @@ -92014,22 +91659,22 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa break; } else if (info.frame_bytes == 0) { size_t bytesRead; - DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); pMP3->dataConsumed = 0; if (pMP3->dataCapacity == pMP3->dataSize) { - drmp3_uint8* pNewData; + ma_uint8* pNewData; size_t newDataCap; - newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; - pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); if (pNewData == NULL) { return 0; } pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { - pMP3->atEnd = DRMP3_TRUE; + pMP3->atEnd = MA_TRUE; return 0; } pMP3->dataSize += bytesRead; @@ -92037,19 +91682,19 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa }; return pcmFramesRead; } -static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { - drmp3_uint32 pcmFramesRead = 0; - drmp3dec_frame_info info; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->memory.pData != NULL); + ma_uint32 pcmFramesRead = 0; + ma_dr_mp3dec_frame_info info; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); if (pMP3->atEnd) { return 0; } for (;;) { - pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); if (pcmFramesRead > 0) { - pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; @@ -92064,25 +91709,25 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sampl pMP3->memory.currentReadPos += (size_t)info.frame_bytes; return pcmFramesRead; } -static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); } else { - return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); } } -static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) +static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) { - DRMP3_ASSERT(pMP3 != NULL); - return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); + MA_DR_MP3_ASSERT(pMP3 != NULL); + return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); } #if 0 -static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) +static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) { - drmp3_uint32 pcmFrameCount; - DRMP3_ASSERT(pMP3 != NULL); - pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); + ma_uint32 pcmFrameCount; + MA_DR_MP3_ASSERT(pMP3 != NULL); + pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFrameCount == 0) { return 0; } @@ -92092,55 +91737,55 @@ static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) return pcmFrameCount; } #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 ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(onRead != NULL); - drmp3dec_init(&pMP3->decoder); + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(onRead != NULL); + ma_dr_mp3dec_init(&pMP3->decoder); pMP3->onRead = onRead; pMP3->onSeek = onSeek; pMP3->pUserData = pUserData; - pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { - return DRMP3_FALSE; + return MA_FALSE; } - if (drmp3_decode_next_frame(pMP3) == 0) { - drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); - return DRMP3_FALSE; + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { + ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); + return MA_FALSE; } pMP3->channels = pMP3->mp3FrameChannels; pMP3->sampleRate = pMP3->mp3FrameSampleRate; - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL || onRead == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } - DRMP3_ZERO_OBJECT(pMP3); - return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); + MA_DR_MP3_ZERO_OBJECT(pMP3); + return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); } -static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { - drmp3* pMP3 = (drmp3*)pUserData; + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; size_t bytesRemaining; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); pMP3->memory.currentReadPos += bytesToRead; } return bytesToRead; } -static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) +static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) { - drmp3* pMP3 = (drmp3*)pUserData; - DRMP3_ASSERT(pMP3 != NULL); - if (origin == drmp3_seek_origin_current) { + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + MA_DR_MP3_ASSERT(pMP3 != NULL); + if (origin == ma_dr_mp3_seek_origin_current) { if (byteOffset > 0) { if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); @@ -92152,585 +91797,75 @@ static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3 } pMP3->memory.currentReadPos += byteOffset; } else { - if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { + if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { pMP3->memory.currentReadPos = byteOffset; } else { pMP3->memory.currentReadPos = pMP3->memory.dataSize; } } - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } - DRMP3_ZERO_OBJECT(pMP3); + MA_DR_MP3_ZERO_OBJECT(pMP3); if (pData == NULL || dataSize == 0) { - return DRMP3_FALSE; + return MA_FALSE; } - pMP3->memory.pData = (const drmp3_uint8*)pData; + pMP3->memory.pData = (const ma_uint8*)pData; pMP3->memory.dataSize = dataSize; pMP3->memory.currentReadPos = 0; - return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks); + return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); } -#ifndef DR_MP3_NO_STDIO +#ifndef MA_DR_MP3_NO_STDIO #include #include -#include -static drmp3_result drmp3_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRMP3_SUCCESS; - #ifdef EPERM - case EPERM: return DRMP3_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRMP3_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRMP3_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRMP3_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRMP3_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRMP3_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRMP3_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRMP3_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRMP3_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRMP3_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRMP3_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRMP3_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRMP3_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRMP3_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRMP3_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRMP3_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRMP3_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRMP3_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRMP3_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRMP3_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRMP3_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRMP3_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRMP3_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRMP3_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRMP3_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRMP3_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRMP3_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRMP3_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRMP3_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRMP3_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRMP3_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRMP3_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRMP3_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRMP3_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRMP3_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRMP3_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRMP3_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRMP3_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRMP3_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRMP3_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRMP3_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRMP3_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRMP3_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRMP3_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRMP3_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRMP3_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRMP3_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRMP3_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRMP3_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRMP3_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRMP3_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRMP3_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRMP3_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRMP3_ERROR; - #endif - #ifdef EADV - case EADV: return DRMP3_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRMP3_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRMP3_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRMP3_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRMP3_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRMP3_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRMP3_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRMP3_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRMP3_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRMP3_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRMP3_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRMP3_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRMP3_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRMP3_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRMP3_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRMP3_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRMP3_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRMP3_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRMP3_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRMP3_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRMP3_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRMP3_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRMP3_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRMP3_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRMP3_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRMP3_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRMP3_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRMP3_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRMP3_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRMP3_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRMP3_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRMP3_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRMP3_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRMP3_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRMP3_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRMP3_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRMP3_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRMP3_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRMP3_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRMP3_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRMP3_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRMP3_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRMP3_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRMP3_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRMP3_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRMP3_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRMP3_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRMP3_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRMP3_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRMP3_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRMP3_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRMP3_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRMP3_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRMP3_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRMP3_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRMP3_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRMP3_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRMP3_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRMP3_ERROR; - #endif - default: return DRMP3_ERROR; - } -} -static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRMP3_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drmp3_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drmp3_result result = drmp3_result_from_errno(errno); - if (result == DRMP3_SUCCESS) { - result = DRMP3_ERROR; - } - return result; - } -#endif - return DRMP3_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRMP3_HAS_WFOPEN - #endif -#endif -static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRMP3_INVALID_ARGS; - } -#if defined(DRMP3_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drmp3_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drmp3_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - #if defined(__DJGPP__) - { - } - #else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRMP3_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drmp3_result_from_errno(errno); - } - pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRMP3_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRMP3_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - #endif - if (*ppFile == NULL) { - return DRMP3_ERROR; - } -#endif - return DRMP3_SUCCESS; -} -static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); } -static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) +static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3_bool32 result; + ma_bool32 result; FILE* pFile; - if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { - return DRMP3_FALSE; + if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { + return MA_FALSE; } - result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRMP3_TRUE) { + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { fclose(pFile); return result; } - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3_bool32 result; + ma_bool32 result; FILE* pFile; - if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { - return DRMP3_FALSE; + if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; } - result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRMP3_TRUE) { + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { fclose(pFile); return result; } - return DRMP3_TRUE; + return MA_TRUE; } #endif -DRMP3_API void drmp3_uninit(drmp3* pMP3) +MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) { if (pMP3 == NULL) { return; } -#ifndef DR_MP3_NO_STDIO - if (pMP3->onRead == drmp3__on_read_stdio) { +#ifndef MA_DR_MP3_NO_STDIO + if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { FILE* pFile = (FILE*)pMP3->pUserData; if (pFile != NULL) { fclose(pFile); @@ -92738,14 +91873,14 @@ DRMP3_API void drmp3_uninit(drmp3* pMP3) } } #endif - drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); + ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); } -#if defined(DR_MP3_FLOAT_OUTPUT) -static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) +#if defined(MA_DR_MP3_FLOAT_OUTPUT) +static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) { - drmp3_uint64 i; - drmp3_uint64 i4; - drmp3_uint64 sampleCount4; + ma_uint64 i; + ma_uint64 i4; + ma_uint64 sampleCount4; i = 0; sampleCount4 = sampleCount >> 2; for (i4 = 0; i4 < sampleCount4; i4 += 1) { @@ -92761,24 +91896,24 @@ static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sa x1 = x1 * 32767.0f; x2 = x2 * 32767.0f; x3 = x3 * 32767.0f; - dst[i+0] = (drmp3_int16)x0; - dst[i+1] = (drmp3_int16)x1; - dst[i+2] = (drmp3_int16)x2; - dst[i+3] = (drmp3_int16)x3; + dst[i+0] = (ma_int16)x0; + dst[i+1] = (ma_int16)x1; + dst[i+2] = (ma_int16)x2; + dst[i+3] = (ma_int16)x3; i += 4; } for (; i < sampleCount; i += 1) { float x = src[i]; x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); x = x * 32767.0f; - dst[i] = (drmp3_int16)x; + dst[i] = (ma_int16)x; } } #endif -#if !defined(DR_MP3_FLOAT_OUTPUT) -static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) +#if !defined(MA_DR_MP3_FLOAT_OUTPUT) +static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) { - drmp3_uint64 i; + ma_uint64 i; for (i = 0; i < sampleCount; i += 1) { float x = (float)src[i]; x = x * 0.000030517578125f; @@ -92786,22 +91921,22 @@ static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sa } } #endif -static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut) +static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) { - drmp3_uint64 totalFramesRead = 0; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onRead != NULL); + ma_uint64 totalFramesRead = 0; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); while (framesToRead > 0) { - drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); if (pBufferOut != NULL) { - #if defined(DR_MP3_FLOAT_OUTPUT) - 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); + #if defined(MA_DR_MP3_FLOAT_OUTPUT) + float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); #else - 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); + ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); + ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); #endif } pMP3->currentPCMFrame += framesToConsume; @@ -92812,125 +91947,125 @@ static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesTo if (framesToRead == 0) { break; } - DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - if (drmp3_decode_next_frame(pMP3) == 0) { + MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { break; } } return totalFramesRead; } -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) { if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } -#if defined(DR_MP3_FLOAT_OUTPUT) - return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#if defined(MA_DR_MP3_FLOAT_OUTPUT) + return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); #else { - drmp3_int16 pTempS16[8192]; - drmp3_uint64 totalPCMFramesRead = 0; + ma_int16 pTempS16[8192]; + ma_uint64 totalPCMFramesRead = 0; while (totalPCMFramesRead < framesToRead) { - drmp3_uint64 framesJustRead; - drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; + ma_uint64 framesJustRead; + ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; if (framesToReadNow > framesRemaining) { framesToReadNow = framesRemaining; } - framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); + framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); if (framesJustRead == 0) { break; } - drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); + ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); totalPCMFramesRead += framesJustRead; } return totalPCMFramesRead; } #endif } -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) { if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } -#if !defined(DR_MP3_FLOAT_OUTPUT) - return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#if !defined(MA_DR_MP3_FLOAT_OUTPUT) + return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); #else { float pTempF32[4096]; - drmp3_uint64 totalPCMFramesRead = 0; + ma_uint64 totalPCMFramesRead = 0; while (totalPCMFramesRead < framesToRead) { - drmp3_uint64 framesJustRead; - drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; + ma_uint64 framesJustRead; + ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; if (framesToReadNow > framesRemaining) { framesToReadNow = framesRemaining; } - framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); if (framesJustRead == 0) { break; } - drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); + ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); totalPCMFramesRead += framesJustRead; } return totalPCMFramesRead; } #endif } -static void drmp3_reset(drmp3* pMP3) +static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) { - DRMP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3 != NULL); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = 0; pMP3->currentPCMFrame = 0; pMP3->dataSize = 0; - pMP3->atEnd = DRMP3_FALSE; - drmp3dec_init(&pMP3->decoder); + pMP3->atEnd = MA_FALSE; + ma_dr_mp3dec_init(&pMP3->decoder); } -static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) +static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) { - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onSeek != NULL); - if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) { - return DRMP3_FALSE; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); + if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; } - drmp3_reset(pMP3); - return DRMP3_TRUE; + ma_dr_mp3_reset(pMP3); + return MA_TRUE; } -static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) +static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) { - drmp3_uint64 framesRead; -#if defined(DR_MP3_FLOAT_OUTPUT) - framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); + ma_uint64 framesRead; +#if defined(MA_DR_MP3_FLOAT_OUTPUT) + framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); #else - framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); + framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); #endif if (framesRead != frameOffset) { - return DRMP3_FALSE; + return MA_FALSE; } - return DRMP3_TRUE; + return MA_TRUE; } -static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) +static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) { - DRMP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3 != NULL); if (frameIndex == pMP3->currentPCMFrame) { - return DRMP3_TRUE; + return MA_TRUE; } if (frameIndex < pMP3->currentPCMFrame) { - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } } - DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); - return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); + MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); + return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); } -static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) +static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) { - drmp3_uint32 iSeekPoint; - DRMP3_ASSERT(pSeekPointIndex != NULL); + ma_uint32 iSeekPoint; + MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); *pSeekPointIndex = 0; if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { - return DRMP3_FALSE; + return MA_FALSE; } for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { @@ -92938,18 +92073,18 @@ static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 fram } *pSeekPointIndex = iSeekPoint; } - return DRMP3_TRUE; + return MA_TRUE; } -static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) +static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) { - drmp3_seek_point seekPoint; - drmp3_uint32 priorSeekPointIndex; - drmp3_uint16 iMP3Frame; - drmp3_uint64 leftoverFrames; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->pSeekPoints != NULL); - DRMP3_ASSERT(pMP3->seekPointCount > 0); - if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { + ma_dr_mp3_seek_point seekPoint; + ma_uint32 priorSeekPointIndex; + ma_uint16 iMP3Frame; + ma_uint64 leftoverFrames; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); + MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); + if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; } else { seekPoint.seekPosInBytes = 0; @@ -92957,71 +92092,71 @@ static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint6 seekPoint.mp3FramesToDiscard = 0; seekPoint.pcmFramesToDiscard = 0; } - if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) { - return DRMP3_FALSE; + if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; } - drmp3_reset(pMP3); + ma_dr_mp3_reset(pMP3); for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - drmp3_uint32 pcmFramesRead; - drmp3d_sample_t* pPCMFrames; + ma_uint32 pcmFramesRead; + ma_dr_mp3d_sample_t* pPCMFrames; pPCMFrames = NULL; if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { - pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; + pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; } - pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); + pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); if (pcmFramesRead == 0) { - return DRMP3_FALSE; + return MA_FALSE; } } pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; leftoverFrames = frameIndex - pMP3->currentPCMFrame; - return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); + return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); } -DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) +MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) { if (pMP3 == NULL || pMP3->onSeek == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } if (frameIndex == 0) { - return drmp3_seek_to_start_of_stream(pMP3); + return ma_dr_mp3_seek_to_start_of_stream(pMP3); } if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { - return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); + return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); } else { - return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); + return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); } } -DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) +MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) { - drmp3_uint64 currentPCMFrame; - drmp3_uint64 totalPCMFrameCount; - drmp3_uint64 totalMP3FrameCount; + ma_uint64 currentPCMFrame; + ma_uint64 totalPCMFrameCount; + ma_uint64 totalMP3FrameCount; if (pMP3 == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } if (pMP3->onSeek == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } currentPCMFrame = pMP3->currentPCMFrame; - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } totalPCMFrameCount = 0; totalMP3FrameCount = 0; for (;;) { - drmp3_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); + ma_uint32 pcmFramesInCurrentMP3Frame; + pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3Frame == 0) { break; } totalPCMFrameCount += pcmFramesInCurrentMP3Frame; totalMP3FrameCount += 1; } - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } - if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return MA_FALSE; } if (pMP3FrameCount != NULL) { *pMP3FrameCount = totalMP3FrameCount; @@ -93029,89 +92164,89 @@ DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint if (pPCMFrameCount != NULL) { *pPCMFrameCount = totalPCMFrameCount; } - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) +MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) { - drmp3_uint64 totalPCMFrameCount; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + ma_uint64 totalPCMFrameCount; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { return 0; } return totalPCMFrameCount; } -DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) +MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) { - drmp3_uint64 totalMP3FrameCount; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { + ma_uint64 totalMP3FrameCount; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { return 0; } return totalMP3FrameCount; } -static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) +static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) { float srcRatio; float pcmFrameCountOutF; - drmp3_uint32 pcmFrameCountOut; + ma_uint32 pcmFrameCountOut; srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - DRMP3_ASSERT(srcRatio > 0); + MA_DR_MP3_ASSERT(srcRatio > 0); pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); - pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; + pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; *pRunningPCMFrameCount += pcmFrameCountOut; } typedef struct { - drmp3_uint64 bytePos; - drmp3_uint64 pcmFrameIndex; -} drmp3__seeking_mp3_frame_info; -DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) + ma_uint64 bytePos; + ma_uint64 pcmFrameIndex; +} ma_dr_mp3__seeking_mp3_frame_info; +MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) { - drmp3_uint32 seekPointCount; - drmp3_uint64 currentPCMFrame; - drmp3_uint64 totalMP3FrameCount; - drmp3_uint64 totalPCMFrameCount; + ma_uint32 seekPointCount; + ma_uint64 currentPCMFrame; + ma_uint64 totalMP3FrameCount; + ma_uint64 totalPCMFrameCount; if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } seekPointCount = *pSeekPointCount; if (seekPointCount == 0) { - return DRMP3_FALSE; + return MA_FALSE; } currentPCMFrame = pMP3->currentPCMFrame; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { + return MA_FALSE; } - if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) { + if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { seekPointCount = 1; pSeekPoints[0].seekPosInBytes = 0; pSeekPoints[0].pcmFrameIndex = 0; pSeekPoints[0].mp3FramesToDiscard = 0; pSeekPoints[0].pcmFramesToDiscard = 0; } else { - drmp3_uint64 pcmFramesBetweenSeekPoints; - drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1]; - drmp3_uint64 runningPCMFrameCount = 0; + ma_uint64 pcmFramesBetweenSeekPoints; + ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; + ma_uint64 runningPCMFrameCount = 0; float runningPCMFrameCountFractionalPart = 0; - drmp3_uint64 nextTargetPCMFrame; - drmp3_uint32 iMP3Frame; - drmp3_uint32 iSeekPoint; + ma_uint64 nextTargetPCMFrame; + ma_uint32 iMP3Frame; + ma_uint32 iSeekPoint; if (seekPointCount > totalMP3FrameCount-1) { - seekPointCount = (drmp3_uint32)totalMP3FrameCount-1; + seekPointCount = (ma_uint32)totalMP3FrameCount-1; } pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } - for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { - drmp3_uint32 pcmFramesInCurrentMP3FrameIn; - DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); + for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { + ma_uint32 pcmFramesInCurrentMP3FrameIn; + MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { - return DRMP3_FALSE; + return MA_FALSE; } - drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); } nextTargetPCMFrame = 0; for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { @@ -93120,43 +92255,43 @@ DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pS if (nextTargetPCMFrame < runningPCMFrameCount) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); break; } else { size_t i; - drmp3_uint32 pcmFramesInCurrentMP3FrameIn; - for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) { + ma_uint32 pcmFramesInCurrentMP3FrameIn; + for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { mp3FrameInfo[i] = mp3FrameInfo[i+1]; } - mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); break; } - drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); } } } - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } - if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return MA_FALSE; } } *pSeekPointCount = seekPointCount; - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) +MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) { if (pMP3 == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } if (seekPointCount == 0 || pSeekPoints == NULL) { pMP3->seekPointCount = 0; @@ -93165,25 +92300,25 @@ DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPoint pMP3->seekPointCount = seekPointCount; pMP3->pSeekPoints = pSeekPoints; } - return DRMP3_TRUE; + return MA_TRUE; } -static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) { - drmp3_uint64 totalFramesRead = 0; - drmp3_uint64 framesCapacity = 0; + ma_uint64 totalFramesRead = 0; + ma_uint64 framesCapacity = 0; float* pFrames = NULL; float temp[4096]; - DRMP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3 != NULL); for (;;) { - drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; - drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; + ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; } if (framesCapacity < totalFramesRead + framesJustRead) { - drmp3_uint64 oldFramesBufferSize; - drmp3_uint64 newFramesBufferSize; - drmp3_uint64 newFramesCap; + ma_uint64 oldFramesBufferSize; + ma_uint64 newFramesBufferSize; + ma_uint64 newFramesCap; float* pNewFrames; newFramesCap = framesCapacity * 2; if (newFramesCap < totalFramesRead + framesJustRead) { @@ -93191,18 +92326,18 @@ static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, } oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); - if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { + if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { break; } - pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { - drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); break; } pFrames = pNewFrames; framesCapacity = newFramesCap; } - DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); totalFramesRead += framesJustRead; if (framesJustRead != framesToReadRightNow) { break; @@ -93212,48 +92347,48 @@ static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, pConfig->channels = pMP3->channels; pConfig->sampleRate = pMP3->sampleRate; } - drmp3_uninit(pMP3); + ma_dr_mp3_uninit(pMP3); if (pTotalFrameCount) { *pTotalFrameCount = totalFramesRead; } return pFrames; } -static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) { - drmp3_uint64 totalFramesRead = 0; - drmp3_uint64 framesCapacity = 0; - drmp3_int16* pFrames = NULL; - drmp3_int16 temp[4096]; - DRMP3_ASSERT(pMP3 != NULL); + ma_uint64 totalFramesRead = 0; + ma_uint64 framesCapacity = 0; + ma_int16* pFrames = NULL; + ma_int16 temp[4096]; + MA_DR_MP3_ASSERT(pMP3 != NULL); for (;;) { - drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; - drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); + ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; + ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; } if (framesCapacity < totalFramesRead + framesJustRead) { - drmp3_uint64 newFramesBufferSize; - drmp3_uint64 oldFramesBufferSize; - drmp3_uint64 newFramesCap; - drmp3_int16* pNewFrames; + ma_uint64 newFramesBufferSize; + ma_uint64 oldFramesBufferSize; + ma_uint64 newFramesCap; + ma_int16* pNewFrames; newFramesCap = framesCapacity * 2; if (newFramesCap < totalFramesRead + framesJustRead) { newFramesCap = totalFramesRead + framesJustRead; } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); - if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); + if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { break; } - pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { - drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); break; } pFrames = pNewFrames; framesCapacity = newFramesCap; } - DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16))); + MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); totalFramesRead += framesJustRead; if (framesJustRead != framesToReadRightNow) { break; @@ -93263,81 +92398,81 @@ static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pC pConfig->channels = pMP3->channels; pConfig->sampleRate = pMP3->sampleRate; } - drmp3_uninit(pMP3); + ma_dr_mp3_uninit(pMP3); if (pTotalFrameCount) { *pTotalFrameCount = totalFramesRead; } return pFrames; } -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) +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__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) +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } -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) +MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -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) +MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } -#ifndef DR_MP3_NO_STDIO -DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_MP3_NO_STDIO +MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } #endif -DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); + return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); } else { - return drmp3__malloc_default(sz, NULL); + return ma_dr_mp3__malloc_default(sz, NULL); } } -DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - drmp3__free_from_callbacks(p, pAllocationCallbacks); + ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); } else { - drmp3__free_default(p, NULL); + ma_dr_mp3__free_default(p, NULL); } } #endif /* dr_mp3_c end */ -#endif /* DRMP3_IMPLEMENTATION */ +#endif /* MA_DR_MP3_IMPLEMENTATION */ #endif /* MA_NO_MP3 */ From 7eb49d1c7b92efdfd5ebe8680776967197888121 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:57:49 +0200 Subject: [PATCH 0653/1710] EXTERNAL: msf_gif.h, reviewed some warnings --- src/external/msf_gif.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/external/msf_gif.h b/src/external/msf_gif.h index 9374c8b84..bc2c6edef 100644 --- a/src/external/msf_gif.h +++ b/src/external/msf_gif.h @@ -256,16 +256,16 @@ 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 - const static int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 }; - const static int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 }; - const static int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 }; + 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; - const static int ditherKernel[16] = { + 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, @@ -404,7 +404,7 @@ static MsfGifBuffer * msf_compress_frame(void * allocContext, int width, int hei MsfGifBuffer * buffer = (MsfGifBuffer *) MSF_GIF_MALLOC(allocContext, maxBufSize); if (!buffer) { return NULL; } uint8_t * writeHead = buffer->data; - MsfStridedList lzw = { lzwMem }; + MsfStridedList lzw = { lzwMem, 0, 0 }; //allocate tlb int totalBits = frame.rbits + frame.gbits + frame.bbits; From f27ea1f0c874b5bbae5f83de423256cc141aebc6 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:58:14 +0200 Subject: [PATCH 0654/1710] REVIEWED: `IsGestureDetected()` parameter type --- src/raylib.h | 2 +- src/rgestures.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 98e5c48a0..635b3dfb7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1163,7 +1163,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected +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 Vector2 GetGestureDragVector(void); // Get gesture drag vector diff --git a/src/rgestures.h b/src/rgestures.h index 779dbed56..9161b7484 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -255,7 +255,7 @@ void SetGesturesEnabled(unsigned int flags) } // Check if a gesture have been detected -bool IsGestureDetected(int gesture) +bool IsGestureDetected(unsigned int gesture) { if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true; else return false; From 83d82b6697204efa4601455ebb4beaef609bfc86 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:58:24 +0200 Subject: [PATCH 0655/1710] Update rmodels.c --- src/rmodels.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 1f8568625..f9ceec137 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5246,7 +5246,7 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo float tend = 0.0f; int keyframe = 0; // Defaults to first pose - for (int i = 0; i < input->count - 1; i++) + 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; @@ -5722,7 +5722,7 @@ static Model LoadM3D(const char *fileName) 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) + if ((skinid != M3D_UNDEF) && (skinid < (int)m3d->numskin)) { for (j = 0; j < 4; j++) { From a3a5aa7c639410f4ff8ed776d590f952653f5ed1 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:58:53 +0200 Subject: [PATCH 0656/1710] REVIEWED: `LoadFileData()` potential issues with dataSize --- src/utils.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/utils.c b/src/utils.c index 27747b757..51d84cfe4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -200,7 +200,7 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize) // 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); + 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) @@ -210,11 +210,24 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize) if (data != NULL) { // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] - unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); - *dataSize = count; + 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 (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); - else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); + 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); } @@ -254,7 +267,9 @@ bool SaveFileData(const char *fileName, void *data, int dataSize) if (file != NULL) { - unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), dataSize, file); + // 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); From 557aeff253255a7ce2aac3b1872aeefba6ff2a51 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:59:52 +0200 Subject: [PATCH 0657/1710] REVIEWED: `glInternalFormat` is unsigned and actually, `rlGetGlTextureFormats()` returns 0 if fails --- src/rlgl.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 9ed484408..bbf52db25 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3012,7 +3012,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); - if (glInternalFormat != -1) + if (glInternalFormat != 0) { if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, dataPtr); #if !defined(GRAPHICS_API_OPENGL_11) @@ -3166,7 +3166,7 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format) unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - if (glInternalFormat != -1) + if (glInternalFormat != 0) { // Load cubemap faces for (unsigned int i = 0; i < 6; i++) @@ -3234,7 +3234,7 @@ void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int h unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) + if ((glInternalFormat != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) { glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, data); } @@ -3378,7 +3378,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); unsigned int size = rlGetPixelDataSize(width, height, format); - if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) + if ((glInternalFormat != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) { pixels = RL_MALLOC(size); glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); From c8a6093d52ff6bdd17f9df012f6666b74629f595 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 23 Sep 2023 11:13:11 +0200 Subject: [PATCH 0658/1710] Update examples creation requirements --- examples/examples_template.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/examples_template.c b/examples/examples_template.c index 2b962d319..5cf0b98d8 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -35,6 +35,23 @@ 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. + + - raylib/examples//_example_name.c + - raylib/examples//_example_name.png + - raylib/examples//resources/*.* + - raylib/examples/Makefile + - raylib/examples/Makefile.Web + - raylib/examples/README.md + - raylib/projects/VS2022/examples/_example_name.vcxproj + - raylib/projects/VS2022/raylib.sln + - raylib.com/common/examples.js + - raylib.com/examples//_example_name.html + - raylib.com/examples//_example_name.data + - raylib.com/examples//_example_name.wasm + - raylib.com/examples//_example_name.js */ /******************************************************************************************* From f7c3035b8c5f7b86165ba24dfa868a4527e48a2a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 24 Sep 2023 23:51:23 +0200 Subject: [PATCH 0659/1710] Update raygui.h --- examples/shapes/raygui.h | 1389 +++++++++++++++++++++++++++----------- 1 file changed, 985 insertions(+), 404 deletions(-) diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 63a27fa90..26d6bacb1 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raygui v4.0-dev - A simple and easy-to-use immediate-mode gui library +* raygui v4.0 - 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 @@ -10,20 +10,26 @@ * - Immediate-mode gui, minimal retained data * - +25 controls provided (basic and advanced) * - Styling system for colors, font and metrics -* - Icons supported, embeds a complete 1-bit icons pack -* - Standalone usage mode option (custom graphics backends) -* - Multiple tools provided for raygui development +* - 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: -* - Redesign functions that require a value as parameter to be returned, pass by reference * - Better standalone mode API for easy plug of custom backends -* - Externalize required inputs in some way, allow user customization +* - Externalize required inputs, allow user easier customization * * LIMITATIONS: -* - No multi-line word-wraped text box support -* - No auto-layout mechanism provided, up to the user to define controls position and size +* - 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 GuiLoadStyleDefaulf() 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 @@ -31,13 +37,15 @@ * - Line * - Panel --> StatusBar * - ScrollPanel --> StatusBar +* - TabBar --> Button * * # Basic Controls * - Label -* - Button * - LabelButton --> Label +* - Button * - Toggle * - ToggleGroup --> Toggle +* - ToggleSlider * - CheckBox * - ComboBox * - DropdownBox @@ -126,11 +134,24 @@ * 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: -* 4.0 (xx-Jun-2023) REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value +* 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 @@ -146,6 +167,10 @@ * 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) @@ -221,17 +246,43 @@ * 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria. * * DEPENDENCIES: -* raylib 4.5 Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing +* raylib 4.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) +* 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) @@ -265,7 +316,7 @@ #define RAYGUI_VERSION_MAJOR 4 #define RAYGUI_VERSION_MINOR 0 #define RAYGUI_VERSION_PATCH 0 -#define RAYGUI_VERSION "4.0-dev" +#define RAYGUI_VERSION "4.0" #if !defined(RAYGUI_STANDALONE) #include "raylib.h" @@ -390,37 +441,69 @@ } Font; #endif + // Style property +// NOTE: Used when exporting style as code for convenience typedef struct GuiStyleProp { - unsigned short controlId; - unsigned short propertyId; - unsigned int propertyValue; + 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, + STATE_DISABLED } GuiState; // Gui control text alignment typedef enum { TEXT_ALIGN_LEFT = 0, TEXT_ALIGN_CENTER, - TEXT_ALIGN_RIGHT, + 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 + SLIDER, // Used also for: SLIDERBAR, TOGGLESLIDER PROGRESSBAR, CHECKBOX, COMBOBOX, @@ -437,37 +520,55 @@ typedef enum { // Gui base properties for every control // NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties) typedef enum { - BORDER_COLOR_NORMAL = 0, - BASE_COLOR_NORMAL, - TEXT_COLOR_NORMAL, - BORDER_COLOR_FOCUSED, - BASE_COLOR_FOCUSED, - TEXT_COLOR_FOCUSED, - BORDER_COLOR_PRESSED, - BASE_COLOR_PRESSED, - TEXT_COLOR_PRESSED, - BORDER_COLOR_DISABLED, - BASE_COLOR_DISABLED, - TEXT_COLOR_DISABLED, - BORDER_WIDTH, - TEXT_PADDING, - TEXT_ALIGNMENT, - RESERVED + 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, // 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_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls } GuiControlProperty; -// Gui extended properties depend on control -// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default 8 properties) -//---------------------------------------------------------------------------------- +// 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 thikness } 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; @@ -492,12 +593,12 @@ typedef enum { // ScrollBar typedef enum { - ARROWS_SIZE = 16, - ARROWS_VISIBLE, - SCROLL_SLIDER_PADDING, // (SLIDERBAR, SLIDER_PADDING) - SCROLL_SLIDER_SIZE, - SCROLL_PADDING, - SCROLL_SPEED, + 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 @@ -519,11 +620,7 @@ typedef enum { // TextBox/TextBoxMulti/ValueBox/Spinner typedef enum { - TEXT_INNER_PADDING = 16, // TextBox/TextBoxMulti/ValueBox/Spinner inner text padding - TEXT_LINES_SPACING, // TextBoxMulti lines separation - TEXT_ALIGNMENT_VERTICAL, // TextBoxMulti vertical alignment: 0-CENTERED, 1-UP, 2-DOWN - TEXT_MULTILINE, // TextBox supports multiple lines - TEXT_WRAP_MODE // TextBox wrap mode for multiline: 0-NO_WRAP, 1-CHAR_WRAP, 2-WORD_WRAP + TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable } GuiTextBoxProperty; // Spinner @@ -537,7 +634,7 @@ 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-left, 1-right) + SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE) } GuiListViewProperty; // ColorPicker @@ -571,7 +668,7 @@ RAYGUIAPI void GuiDisable(void); // Disable gui c 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 GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f +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) @@ -618,6 +715,7 @@ RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control, returns true when active RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control, returns active toggle index +RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control, returns true when clicked 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, returns selected item index @@ -642,6 +740,8 @@ RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); 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 returns HSV color value, used by GuiColorPickerHSV() //---------------------------------------------------------------------------------------------------------- @@ -1134,11 +1234,11 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = 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, 0x47fe483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT + 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, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM + 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, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK + 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 @@ -1237,8 +1337,10 @@ static unsigned int *guiIconsPtr = guiIcons; #define RAYGUI_ICON_SIZE 0 #endif -#define RAYGUI_MAX_CONTROLS 16 // Maximum number of standard controls -#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of standard properties +// 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 //---------------------------------------------------------------------------------- @@ -1254,7 +1356,7 @@ static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_N 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 element transpacency on drawing +static float guiAlpha = 1.0f; // Gui controls transparency static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) @@ -1264,7 +1366,7 @@ static const char *guiTooltipPtr = NULL; // Tooltip string pointer (strin static bool guiSliderDragging = false; // Gui slider drag state (no inputs processed except dragged slider) static Rectangle guiSliderActive = { 0 }; // Gui slider active bounds rectangle, used as an unique identifier -static unsigned int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() +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 @@ -1317,25 +1419,33 @@ static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() // Drawing required functions //------------------------------------------------------------------------------- -static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle(), GuiDrawIcon() +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 LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // -- GuiLoadStyle() -static Font GetFontDefault(void); // -- GuiLoadStyleDefault() -static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle() -static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle() -static char *LoadFileText(const char *fileName); // -- GuiLoadStyle() -static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle() +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 Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f 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 @@ -1358,7 +1468,7 @@ static int GetTextWidth(const char *text); // Gui get text 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 bounds, int alignment, Color tint); // Gui draw text using default font +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 @@ -1368,6 +1478,7 @@ static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color 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 @@ -1390,7 +1501,7 @@ void GuiUnlock(void) { guiLocked = false; } bool GuiIsLocked(void) { return guiLocked; } // Set gui controls alpha global state -void GuiFade(float alpha) +void GuiSetAlpha(float alpha) { if (alpha < 0.0f) alpha = 0.0f; else if (alpha > 1.0f) alpha = 1.0f; @@ -1416,7 +1527,6 @@ void GuiSetFont(Font font) if (!guiStyleLoaded) GuiLoadStyleDefault(); guiFont = font; - GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize); } } @@ -1487,7 +1597,7 @@ int GuiWindowBox(Rectangle bounds, const char *title) GuiSetStyle(BUTTON, BORDER_WIDTH, 1); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); #if defined(RAYGUI_NO_ICONS) - clicked = GuiButton(closeButtonRec, "x"); + result = GuiButton(closeButtonRec, "x"); #else result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); #endif @@ -1510,9 +1620,9 @@ int GuiGroupBox(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + 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))); GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text); //-------------------------------------------------------------------- @@ -1533,7 +1643,7 @@ int GuiLine(Rectangle bounds, const char *text) int result = 0; GuiState state = guiState; - Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha); + Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)); // Draw control //-------------------------------------------------------------------- @@ -1574,15 +1684,15 @@ int GuiPanel(Rectangle bounds, const char *text) { // Move panel bounds after the header bar bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; - bounds.height -= (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, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha), - Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha)); + 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))); //-------------------------------------------------------------------- return result; @@ -1595,7 +1705,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) #define RAYGUI_TABBAR_ITEM_WIDTH 160 int result = -1; - GuiState state = guiState; + //GuiState state = guiState; Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height }; @@ -1638,7 +1748,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); // Draw tab close button - // NOTE: Only draw close button for curren tab: if (CheckCollisionPointRec(mousePoint, tabBounds)) + // 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); @@ -1663,8 +1773,12 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) // 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 + int result = 0; GuiState state = guiState; + float mouseWheelSpeed = 20.0f; // Default movement speed with mouse wheel Rectangle temp = { 0 }; if (view == NULL) view = &temp; @@ -1692,8 +1806,31 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector 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) }; + 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 + // NOTE: If content >>> bounds, size could be very small or even 0 + if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) + { + horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH; + mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.height vs bounds.height + } + if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) + { + verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT; + mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.width vs bounds.width + } // Calculate view area (area without the scrollbars) *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? @@ -1736,9 +1873,9 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector #endif float wheelMove = GetMouseWheelMove(); - // Horizontal scroll ((Left Control or Left Shift) + Mouse wheel) - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*20; - else scrollPos.y += wheelMove*20; // Vertical scroll + // Horizontal and vertical scrolling with mouse wheel + if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed; + else scrollPos.y += wheelMove*mouseWheelSpeed; // Vertical scroll } } @@ -1753,7 +1890,7 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector //-------------------------------------------------------------------- if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar - GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); // Draw background + 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); @@ -1780,11 +1917,11 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector 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, Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))), guiAlpha)); + GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3)))); } // Draw scrollbar lines depending on current state - GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, 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); @@ -1808,7 +1945,7 @@ int GuiLabel(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -1839,8 +1976,8 @@ int GuiButton(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); - GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); + 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); //------------------------------------------------------------------ @@ -1877,7 +2014,7 @@ int GuiLabelButton(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return pressed; @@ -1916,13 +2053,13 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active) //-------------------------------------------------------------------- if (state == STATE_NORMAL) { - GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); - GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha)); + 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), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state*3)), guiAlpha)); - GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)), guiAlpha)); + 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); @@ -1980,7 +2117,79 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active) return result; } -// Check Box control, returns true when active +// Toggle Slider control extended, returns true when clicked +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 = 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)GetTextWidth(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; @@ -2019,14 +2228,18 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) *checked = !(*checked); + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + *checked = !(*checked); + result = 1; + } } } //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK); if (*checked) { @@ -2034,10 +2247,10 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) 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, Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)), guiAlpha)); + 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, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -2088,8 +2301,8 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) // Draw control //-------------------------------------------------------------------- // Draw combo box main - GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))), guiAlpha)); - GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); + 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 @@ -2184,8 +2397,8 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod //-------------------------------------------------------------------- if (editMode) GuiPanel(boundsOpen, NULL); - GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)), guiAlpha)); - GuiDrawText(items[itemSelected], GetTextBounds(DEFAULT, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3)), guiAlpha)); + 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) { @@ -2197,25 +2410,25 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod if (i == itemSelected) { - GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)), guiAlpha)); - GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)), guiAlpha)); + 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), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)), guiAlpha)); - GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)), guiAlpha)); + 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(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)), guiAlpha)); + else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL))); } } // 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, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); #else GuiDrawText("#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); // ICON_ARROW_DOWN_FILL + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL #endif //-------------------------------------------------------------------- @@ -2239,12 +2452,12 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) 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 textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex); - int textIndexOffset = 0; // Text index offset to start drawing in the box - - int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); - int multiline = GuiGetStyle(TEXTBOX, TEXT_MULTILINE); + int textIndexOffset = 0; // Text index offset to start drawing in the box // Cursor rectangle // NOTE: Position X value should be updated @@ -2255,17 +2468,15 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2 }; - switch (alignmentVertical) - { - case 0: cursor.y = textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE); break; // CENTERED - case 1: cursor.y = textBounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; break; // UP - case 2: cursor.y = textBounds.y + textBounds.height; break; // DOWN - default: break; - } - 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; + // 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++; @@ -2281,9 +2492,14 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + // 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 + !guiSliderDragging && // No gui slider on dragging + (wrapMode == TEXT_WRAP_NONE)) // No wrap mode { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePosition = GetMousePosition(); if (editMode) { @@ -2301,7 +2517,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex); } - unsigned int textLength = (unsigned int)strlen(text); // Get current text length + int textLength = (int)strlen(text); // Get current text length int codepoint = GetCharPressed(); // Get Unicode codepoint if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; @@ -2329,16 +2545,10 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } // Move cursor to start - if ((textLength > 0) && IsKeyPressed(KEY_HOME)) - { - textBoxCursorIndex = 0; - } + if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0; // Move cursor to end - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) - { - textBoxCursorIndex = textLength; - } + 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)))) @@ -2359,7 +2569,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) text[textLength] = '\0'; } } - + // Delete codepoint from text, before current cursor position if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) { @@ -2386,7 +2596,6 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } // Move cursor position with keys - //if (IsKeyDown(KEY_LEFT) && autoCursorMode) if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))) { autoCursorDelayCounter++; @@ -2412,19 +2621,59 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } } - // TODO: Get cursor rectangle from mouse position - //cursor = GetCursorFromMousePosition(bounds, text, mouse); // Gui style considered internally, including wrapMode + // Move cursor position with mouse + if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text + { + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; + int codepoint = 0; + int codepointSize = 0; + int codepointIndex = 0; + float glyphWidth = 0.0f; + float widthToMouseX = 0; + int mouseCursorIndex = 0; - // TODO: Get cursor rectangle from buffer index - //cursor = GetCursorFromIndex(bounds, text, index); // Gui style considered internally, including wrapMode + for (int i = textIndexOffset; i < textLength; i++) + { + 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 = GetTextWidth(text + textIndexOffset); + if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) + { + mouseCursor.x = textBounds.x + textEndWidth; + mouseCursorIndex = strlen(text); + } + + // 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) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); //if (multiline) cursor.y = GetTextLines() - // Finish text editing on ENTER (if not multiline mode) or mouse click outside bounds + // Finish text editing on ENTER or mouse click outside bounds if ((!multiline && IsKeyPressed(KEY_ENTER)) || - (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) { textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index result = 1; @@ -2432,7 +2681,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } else { - if (CheckCollisionPointRec(mousePoint, bounds)) + if (CheckCollisionPointRec(mousePosition, bounds)) { state = STATE_FOCUSED; @@ -2450,23 +2699,26 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) //-------------------------------------------------------------------- if (state == STATE_PRESSED) { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); + 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), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + 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), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); + 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), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); // Draw cursor - if (editMode) + if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) { //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) - GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + 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); //-------------------------------------------------------------------- @@ -2475,22 +2727,23 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } /* -// Text Box control with multiple lines -// NOTE: It's a regular GuiTextBox() but enabling multiline support, -// unfortunately cursor placement is not working properly so the function is removed +// 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 bufferSize, bool editMode) { bool pressed = false; - GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1); - GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 1); + 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, bufferSize, editMode); - GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 0); - GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 0); - + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE); + GuiSetStyle(TEXTBOX, TEXT_READONLY, 0); + return pressed; } */ @@ -2562,7 +2815,7 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int 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, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- *value = tempValue; @@ -2661,20 +2914,19 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); - // WARNING: BLANK color does not work properly with Fade() - GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha), baseColor); - GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha)); + 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 + GetTextWidth(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, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + 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, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -2718,7 +2970,9 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { - // Get equivalent value and slider position from mousePoint.x + state = STATE_PRESSED; + + // Get equivalent value and slider position from mousePosition.x *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; } } @@ -2736,11 +2990,14 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, guiSliderDragging = true; guiSliderActive = bounds; // Store bounds as an identifier when dragging starts - // Get equivalent value and slider position from mousePoint.x - *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + if (!CheckCollisionPointRec(mousePoint, slider)) + { + // Get equivalent value and slider position from mousePosition.x + *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; - if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider - else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar + if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider + else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar + } } else state = STATE_FOCUSED; } @@ -2763,11 +3020,12 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + 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) || (state == STATE_PRESSED)) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); - else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); + 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))); // Draw left/right text if provided if (textLeft != NULL) @@ -2778,7 +3036,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, 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, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3)))); } if (textRight != NULL) @@ -2789,7 +3047,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, 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, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3)))); } //-------------------------------------------------------------------- @@ -2817,6 +3075,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight 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 - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) }; @@ -2825,16 +3084,39 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight //-------------------------------------------------------------------- if (*value > maxValue) *value = maxValue; - if (state != STATE_DISABLED) progress.width = ((float)(*value/(maxValue - minValue))*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); + // 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); //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), guiAlpha), BLANK); + 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 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); - // Draw slider internal progress bar (depends on state) - if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha)); - else if (state == STATE_FOCUSED) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha)); + 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))); + 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))); + } + + // 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) @@ -2845,7 +3127,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight 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, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3)))); } if (textRight != NULL) @@ -2856,7 +3138,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight 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, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3)))); } //-------------------------------------------------------------------- @@ -2871,9 +3153,8 @@ int GuiStatusBar(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha), - Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + 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; @@ -2902,8 +3183,8 @@ int GuiDummyRec(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + 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; @@ -2930,7 +3211,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd GuiState state = guiState; int itemFocused = (focus == NULL)? -1 : *focus; - int itemSelected = *active; + int itemSelected = (active == NULL)? -1 : *active; // Check if we need a scroll bar bool useScrollBar = false; @@ -3002,35 +3283,35 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, 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 (state == STATE_DISABLED) { - if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha)); + if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, 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), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); } else { - if ((startIndex + i) == itemSelected) + if (((startIndex + i) == itemSelected) && (active != NULL)) { // Draw item selected - GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha)); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, 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) + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned! { // Draw item focused - GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha)); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)), guiAlpha)); + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, 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 - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); } } @@ -3062,10 +3343,10 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd } //-------------------------------------------------------------------- + if (active != NULL) *active = itemSelected; if (focus != NULL) *focus = itemFocused; if (scrollIndex != NULL) *scrollIndex = startIndex; - *active = itemSelected; return result; } @@ -3137,14 +3418,14 @@ int GuiColorPanel(Rectangle bounds, const char *text, Color *color) // 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, Fade(colWhite, guiAlpha)); + 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), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); //-------------------------------------------------------------------- return result; @@ -3174,6 +3455,8 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { + state = STATE_PRESSED; + *alpha = (mousePoint.x - bounds.x)/bounds.width; if (*alpha <= 0.0f) *alpha = 0.0f; if (*alpha >= 1.0f) *alpha = 1.0f; @@ -3217,7 +3500,7 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) 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(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f), guiAlpha) : Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f), guiAlpha)); + 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)); } } @@ -3225,10 +3508,10 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) } 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), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); // Draw alpha bar: selector - GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); //-------------------------------------------------------------------- return result; @@ -3258,6 +3541,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { + 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; @@ -3303,7 +3588,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) if (state != STATE_DISABLED) { // Draw hue bar:color bars - 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)); + // 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)); @@ -3312,10 +3598,10 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } 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), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); // Draw hue bar: selector - GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); //-------------------------------------------------------------------- return result; @@ -3340,9 +3626,9 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color) //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) }; 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); @@ -3351,6 +3637,105 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color) 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, returns HSV color value in *colorHsv. +// Used by GuiColorPickerHSV() +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 + + float hue = -1.0f; + Vector3 maxHue = { hue >= 0.0f ? hue : 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 && !guiSliderDragging) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + 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) { @@ -3371,7 +3756,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; - int textWidth = GetTextWidth(message); + int textWidth = GetTextWidth(message) + 2; Rectangle textBounds = { 0 }; textBounds.x = bounds.x + bounds.width/2 - textWidth/2; @@ -3511,8 +3896,9 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect Vector2 mousePoint = GetMousePosition(); Vector2 currentMouseCell = { 0 }; - int linesV = ((int)(bounds.width/spacing))*subdivs + 1; - int linesH = ((int)(bounds.height/spacing))*subdivs + 1; + float spaceWidth = spacing/(float)subdivs; + int linesV = (int)(bounds.width/spaceWidth) + 1; + int linesH = (int)(bounds.height/spaceWidth) + 1; // Update control //-------------------------------------------------------------------- @@ -3539,14 +3925,14 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect for (int i = 0; i < linesV; i++) { Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height }; - GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); + GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_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 }; - GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); + GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); } } } break; @@ -3620,34 +4006,37 @@ void GuiLoadStyle(const char *fileName) 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 characters from charmap file, - // expected '\n' separated list of integer values - char *charValues = LoadFileText(charmapFileName); - if (charValues != NULL) - { - int glyphCount = 0; - const char **chars = TextSplit(charValues, '\n', &glyphCount); - - int *values = (int *)RAYGUI_MALLOC(glyphCount*sizeof(int)); - for (int i = 0; i < glyphCount; i++) values[i] = TextToInteger(chars[i]); - - if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); - font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, values, glyphCount); - if (font.texture.id == 0) font = GetFontDefault(); - - RAYGUI_FREE(values); - } + // 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); } - else + + 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); - font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); - if (font.texture.id == 0) font = GetFontDefault(); + + 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; @@ -3674,12 +4063,12 @@ void GuiLoadStyle(const char *fileName) if (fileDataSize > 0) { - unsigned char *fileData = (unsigned char *)RL_MALLOC(fileDataSize*sizeof(unsigned char)); + unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char)); fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); GuiLoadStyleFromMemory(fileData, fileDataSize); - RL_FREE(fileData); + RAYGUI_FREE(fileData); } fclose(rgsFile); @@ -3695,6 +4084,8 @@ void GuiLoadStyleDefault(void) 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); @@ -3707,17 +4098,29 @@ void GuiLoadStyleDefault(void) GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff); GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff); GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff); - GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); // WARNING: Some controls use other values - GuiSetStyle(DEFAULT, TEXT_PADDING, 0); // WARNING: Some controls use other values - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); // WARNING: Some controls use other values + 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); @@ -3729,10 +4132,6 @@ void GuiLoadStyleDefault(void) // Initialize 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(TOGGLE, GROUP_PADDING, 2); GuiSetStyle(SLIDER, SLIDER_WIDTH, 16); GuiSetStyle(SLIDER, SLIDER_PADDING, 1); @@ -3742,8 +4141,6 @@ void GuiLoadStyleDefault(void) GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); - GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, (int)((float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f)); - GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24); GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2); GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); @@ -3767,12 +4164,17 @@ void GuiLoadStyleDefault(void) { // Unload previous font texture UnloadTexture(guiFont.texture); + RL_FREE(guiFont.recs); + RL_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 }); } @@ -3901,7 +4303,7 @@ void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) { #if !defined(RAYGUI_STANDALONE) - DrawRectangle(posX + (k%RAYGUI_ICON_SIZE)*pixelSize, posY + y*pixelSize, pixelSize, pixelSize, color); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color); #endif } @@ -3922,7 +4324,8 @@ void GuiSetIconScale(int scale) // Module specific Functions Definition //---------------------------------------------------------------------------------- -// Load style from memory (binary only) +// Load style from memory +// WARNING: Binary files only static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) { unsigned char *fileDataPtr = (unsigned char *)fileData; @@ -3977,7 +4380,6 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) { Font font = { 0 }; int fontType = 0; // 0-Normal, 1-SDF - Rectangle whiteRec = { 0 }; memcpy(&font.baseSize, fileDataPtr, sizeof(int)); memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int)); @@ -3985,7 +4387,8 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) fileDataPtr += 12; // Load font white rectangle - memcpy(&whiteRec, fileDataPtr, sizeof(Rectangle)); + Rectangle fontWhiteRec = { 0 }; + memcpy(&fontWhiteRec, fileDataPtr, sizeof(Rectangle)); fileDataPtr += 16; // Load font image parameters @@ -4002,7 +4405,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int)); fileDataPtr += 12; - if (fontImageCompSize < fontImageUncompSize) + if ((fontImageCompSize > 0) && (fontImageCompSize != fontImageUncompSize)) { // Compressed font atlas image data (DEFLATE), it requires DecompressData() int dataUncompSize = 0; @@ -4027,34 +4430,117 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); font.texture = LoadTextureFromImage(imFont); - if (font.texture.id == 0) font = GetFontDefault(); RAYGUI_FREE(imFont.data); - // Load font recs data - font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); - for (int i = 0; i < font.glyphCount; i++) + // Validate font atlas texture was loaded correctly + if (font.texture.id != 0) { - memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle)); - fileDataPtr += sizeof(Rectangle); - } + // Load font recs data + int recsDataSize = font.glyphCount*sizeof(Rectangle); + int recsDataCompressedSize = 0; - // Load font chars info data - font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); - 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; + // 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_MALLOC(recsDataCompressedSize); + + 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_MALLOC(glyphsDataCompressedSize); + + 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: This way, all gui can be draw using a single draw call - if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec); + // 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 } @@ -4109,10 +4595,10 @@ static int GetTextWidth(const char *text) int codepoint = GetCodepointNext(&text[i], &codepointSize); int codepointIndex = GetGlyphIndex(guiFont, codepoint); - if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING)); + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); - textSize.x += glyphWidth; + textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); } } @@ -4130,15 +4616,23 @@ static Rectangle GetTextBounds(int control, Rectangle 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); + textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); // NOTE: Text is processed line per line! - // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT - // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) - // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER + // 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 SPINNER: + // 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); } @@ -4192,13 +4686,13 @@ const char **GetTextLines(const char *text, int *count) lines[0] = text; int len = 0; *count = 1; - int lineSize = 0; // Stores current line size, not returned + //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; + //lineSize = len; k++; lines[k] = &text[i + 1]; // WARNING: next value is valid? len = 0; @@ -4212,8 +4706,37 @@ const char **GetTextLines(const char *text, int *count) 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 bounds, int alignment, Color tint) +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 @@ -4221,119 +4744,172 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color #define ICON_TEXT_PADDING 4 #endif - int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); + if ((text == NULL) || (text[0] == '\0')) return; // Security check - // We process the text lines one by one - if ((text != NULL) && (text[0] != '\0')) + // 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++) { - // Get text lines ('\n' delimiter) to process lines individually - // NOTE: We can't use GuiTextSplit() because it can be already used before calling - // GuiDrawText() and static buffer would be overriden :( - int lineCount = 0; - const char **lines = GetTextLines(text, &lineCount); + int iconId = 0; + lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor - //Rectangle textBounds = GetTextBounds(LABEL, bounds); - float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); - float posOffsetY = 0; + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; - for (int i = 0; i < lineCount; i++) + // 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]); + + // If text requires an icon, add size to measure + if (iconId >= 0) { - int iconId = 0; - lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor + textSizeX += RAYGUI_ICON_SIZE*guiIconScale; - // Get text position depending on alignment and iconId - //--------------------------------------------------------------------------------- - Vector2 position = { bounds.x, bounds.y }; - - // 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]); - - // 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 ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; - } - - // Check guiTextAlign global variables - switch (alignment) - { - case TEXT_ALIGN_LEFT: position.x = bounds.x; break; - case TEXT_ALIGN_CENTER: position.x = bounds.x + bounds.width/2 - textSizeX/2; break; - case TEXT_ALIGN_RIGHT: position.x = bounds.x + bounds.width - textSizeX; break; - default: break; - } - - switch (alignmentVertical) - { - case 0: position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // CENTERED - case 1: position.y = bounds.y + posOffsetY; break; // UP - case 2: position.y = bounds.y + posOffsetY + bounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // DOWN - default: break; - } - - // NOTE: Make sure we get pixel-perfect coordinates, - // In case of decimals we got weird text positioning - position.x = (float)((int)position.x); - position.y = (float)((int)position.y); - //--------------------------------------------------------------------------------- - - // Draw text (with icon if available) - //--------------------------------------------------------------------------------- + // WARNING: If only icon provided, text could be pointing to EOF character: '\0' #if !defined(RAYGUI_NO_ICONS) - if (iconId >= 0) - { - // NOTE: We consider icon height, probably different than text size - GuiDrawIcon(iconId, (int)position.x, (int)(bounds.y + bounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)), guiIconScale, tint); - position.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); - } + if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; #endif - //DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); + } - // Get size in bytes of text, - // considering end of line and line break - int size = 0; - for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n'); c++, size++){ } - float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + // 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; + } - int textOffsetY = 0; - float textOffsetX = 0.0f; - for (int c = 0, codepointSize = 0; c < size; c += codepointSize) + 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 += (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 textOffsetY = 0; + float textOffsetX = 0.0f; + float glyphWidth = 0; + 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 + + // Wrap mode text measuring to space to validate if it can be drawn or + // a new line is required + if (wrapMode == TEXT_WRAP_CHAR) { - int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); - int index = GetGlyphIndex(guiFont, codepoint); + // 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; - // 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; - - if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint - else + // Jump to next line if current character reach end of the box limits + if ((textOffsetX + glyphWidth) > textBounds.width) { - if ((codepoint != ' ') && (codepoint != '\t')) - { - // Draw only required text glyphs fitting the bounds.width - if (textOffsetX < (bounds.width - guiFont.recs[index].width)) - { - DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ position.x + textOffsetX, position.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), tint); - } - } - - 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)); + textOffsetX = 0.0f; + textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); } } + else if (wrapMode == TEXT_WRAP_WORD) + { + // Get width to next space in line + int nextSpaceIndex = 0; + float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex); - // TODO: Allow users to set line spacing for text: GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, x) - posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f; - //--------------------------------------------------------------------------------- + if ((textOffsetX + nextSpaceWidth) > textBounds.width) + { + textOffsetX = 0.0f; + textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + } + + // TODO: Consider case: (nextSpaceWidth >= textBounds.width) + } + + 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 (textOffsetX <= (textBounds.width - glyphWidth)) + { + 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(bounds, 0, WHITE, Fade(RED, 0.4f)); + GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f)); #endif } @@ -4343,17 +4919,21 @@ static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, if (color.a > 0) { // Draw rectangle filled with color - DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, 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, borderColor); - DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor); - DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor); - DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, borderColor); + 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 @@ -4629,12 +5209,16 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) if (guiSliderDragging) // Keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + !CheckCollisionPointRec(mousePoint, arrowUpLeft) && + !CheckCollisionPointRec(mousePoint, arrowDownRight)) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { - if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); - else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); + 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 @@ -4646,8 +5230,6 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) else if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - guiSliderDragging = true; - guiSliderActive = bounds; // Store bounds as an identifier when dragging starts // Handle mouse wheel int wheel = (int)GetMouseWheelMove(); @@ -4656,6 +5238,9 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) // Handle mouse button down if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + guiSliderDragging = true; + guiSliderActive = 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); @@ -4668,11 +5253,6 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) state = STATE_PRESSED; } - else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) - { - if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); - else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); - } // Keyboard control on mouse hover scrollbar /* @@ -4697,10 +5277,10 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background + 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, Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), guiAlpha)); // Draw the scrollbar active area background - GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state*3)), guiAlpha)); // Draw the slider bar + 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)) @@ -4708,17 +5288,17 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) #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, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + 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, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + 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, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL + 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, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL #endif } //-------------------------------------------------------------------- @@ -4726,6 +5306,18 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) 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) @@ -4757,17 +5349,6 @@ static bool CheckCollisionPointRec(Vector2 point, Rectangle rec) return collision; } -// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f -static Color Fade(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)(255.0f*alpha) }; - - return result; -} - // Formatting of text with variables to 'embed' static const char *TextFormat(const char *text, ...) { From f2389a1e5504feea58f2ac8b482bea1db99684df Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 26 Sep 2023 12:54:24 +0200 Subject: [PATCH 0660/1710] Remove trail spaces --- src/rlgl.h | 8 ++++---- src/rtextures.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index bbf52db25..7a0d7f862 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2998,7 +2998,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipWidth = width; int mipHeight = height; int mipOffset = 0; // Mipmap data offset, only used for tracelog - + // NOTE: Added pointer math separately from function to avoid UBSAN complaining unsigned char *dataPtr = (unsigned char *)data; @@ -3403,7 +3403,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) // Attach our texture to FBO 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)); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); @@ -3697,7 +3697,7 @@ void rlDrawVertexArrayElements(int offset, int count, const void *buffer) // NOTE: Added pointer math separately from function to avoid UBSAN complaining unsigned short *bufferPtr = (unsigned short *)buffer; if (offset > 0) bufferPtr += offset; - + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr); } @@ -3716,7 +3716,7 @@ void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffe // NOTE: Added pointer math separately from function to avoid UBSAN complaining unsigned short *bufferPtr = (unsigned short *)buffer; if (offset > 0) bufferPtr += offset; - + glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr, instances); #endif } diff --git a/src/rtextures.c b/src/rtextures.c index 8dc49fac5..fa8d91ea4 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -325,7 +325,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) #if defined(SUPPORT_FILEFORMAT_SVG) bool isSvgStringValid = false; - + // Validate fileName or string if (fileNameOrString != NULL) { @@ -355,7 +355,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) if (isSvgStringValid) { struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); - + unsigned char *img = RL_MALLOC(width*height*4); // Calculate scales for both the width and the height From 115ba2b079188d79bee86335972a890e79f1e517 Mon Sep 17 00:00:00 2001 From: WraithGlade <8360085+WraithGlade@users.noreply.github.com> Date: Tue, 26 Sep 2023 12:59:40 -0400 Subject: [PATCH 0661/1710] Factor's Raylib bindings mention being at v4.5 (#3350) I looked at the linked Factor binding page for Raylib and found that the file [summary.txt](https://github.com/factor/factor/blob/master/extra/raylib/summary.txt) has the following text in it: ``` Bindings for Raylib 4.5 ``` The bindings have also been updated within the last week. This implies that Factor's Raylib bindings are either already up to date with v4.5 or will be soon. Thus, I have submitted this likely correction to Raylib's bindings table. (PS: There are other Factor Raylib bindings on the internet that are less up-to-date, so don't confuse those with this.) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 9fe86919d..e3de68251 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -23,7 +23,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-d | **4.5** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | 4.0 | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | -| raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | +| raylib-factor | **4.5** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | fortran-raylib | **4.5** | [Fortran](https://fortran-lang.org/) | ISC | https://github.com/interkosmos/fortran-raylib | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | From 9ff47fc807d268d184504e5d0672bac8320367a5 Mon Sep 17 00:00:00 2001 From: XXIV <13811862+thechampagne@users.noreply.github.com> Date: Wed, 27 Sep 2023 21:57:47 +0300 Subject: [PATCH 0662/1710] fix BINDINGS.md (#3358) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index e3de68251..da585f41c 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -44,7 +44,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | -| raylib-bindings | **4.5** | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/vaiorabbit/raylib-bindings | +| raylib-bindings | **4.5** | [Ruby](https://www.ruby-lang.org/en/) | Zlib | https://github.com/vaiorabbit/raylib-bindings | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | From 8d5a90ea3c41119df3ee0b52630463ee55463876 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 27 Sep 2023 14:47:18 -0700 Subject: [PATCH 0663/1710] Expose rcamera functions to the dll so they can be picked up by dll users and bindings that need the dll (#3355) --- src/rcamera.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/rcamera.h b/src/rcamera.h index de169fc39..c999370f5 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -46,6 +46,20 @@ // Defines and Macros //---------------------------------------------------------------------------------- // Function specifiers definition + +// 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 +#endif + #ifndef RLAPI #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) #endif From 411d0ee4371831a90082907f881eb34c87fb4c51 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Sep 2023 00:28:03 +0200 Subject: [PATCH 0664/1710] Update raylib_parser.c --- parser/raylib_parser.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 94ff95626..e1ff37918 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -169,10 +169,12 @@ 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 char inFileName[512] = { 0 }; // Input file name (required in case of provided through CLI) -static char outFileName[512] = { 0 }; // Output file name (required for file save/export) 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 //---------------------------------------------------------------------------------- @@ -499,7 +501,7 @@ int main(int argc, char* argv[]) (ch == '/') || (ch == ' ') || (ch == '\t')) continue; - + // Read number operand else if (isdigit(ch)) { @@ -534,7 +536,7 @@ int main(int argc, char* argv[]) 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 @@ -654,7 +656,7 @@ int main(int argc, char* argv[]) { if (structs[i].fieldName[originalIndex][c] == ',') additionalFields++; } - + if (additionalFields > 0) { int originalLength = -1; @@ -702,7 +704,7 @@ int main(int argc, char* argv[]) { if (structs[i].fieldType[originalIndex][c] == ',') additionalFields++; } - + if (additionalFields > 0) { // Copy original name to last additional field From 73b54c862e9034b7158980c0e6eb3b264fac4729 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Sep 2023 00:28:16 +0200 Subject: [PATCH 0665/1710] Update raylib.h --- src/raylib.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 635b3dfb7..f52f3460d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1331,7 +1331,7 @@ RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) RLAPI bool IsTextureReady(Texture2D texture); // Check if a texture is ready 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 IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready 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 @@ -1379,7 +1379,7 @@ RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileDat RLAPI bool IsFontReady(Font font); // Check if a font is ready 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) +RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success @@ -1541,7 +1541,7 @@ RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileDat RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready 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 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 void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data RLAPI void UnloadWave(Wave wave); // Unload wave data @@ -1598,7 +1598,7 @@ RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set vol RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered) 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 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 s RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream From c3a1c8c8eb8c9b7fac20fa6993e4663a84be17f1 Mon Sep 17 00:00:00 2001 From: BLUELOVETH Date: Mon, 2 Oct 2023 04:56:43 +0800 Subject: [PATCH 0666/1710] Update BINDINGS.md with raylib-pkpy-bindings (#3361) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index da585f41c..47a1c0a27 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -59,6 +59,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylibpyctbg | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | | raylib-py | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | | raylib-python-ctypes | 4.6-dev | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | +| raylib-pkpy-bindings | 4.6-dev | [pocketpy](https://pocketpy.dev/) | MIT | https://github.com/blueloveTH/pkpy-bindings | | raylib-php | **4.5** | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | From 7351240218849b1579db962f30e42fb982400c7e Mon Sep 17 00:00:00 2001 From: Brian E <72316548+Brian-ED@users.noreply.github.com> Date: Sun, 1 Oct 2023 22:00:25 +0100 Subject: [PATCH 0667/1710] [rcore] reveiwed GetWorldToScreenEx (#3351) * reveiwed GetWorldToScreenEx Used the inputted "width" instead of global CORE.window. Used Vector3Transform instead of quaternion. * reverted accidental unrelated change * reverted Vector3Transform back * fixed mistyped result --------- Co-authored-by: Brian-E --- src/rcore.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 26ff79d9a..3ba0404d7 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2952,7 +2952,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + double aspect = ((double)width/(double)height); double top = camera.fovy/2.0; double right = top*aspect; @@ -2963,8 +2963,6 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh // 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 }; From bc15c19518968878b68bbfe8eac3fe4297f11770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Almeida?= <60551627+luis605@users.noreply.github.com> Date: Sun, 1 Oct 2023 22:01:59 +0100 Subject: [PATCH 0668/1710] Texture Tiling Example - luis605 (#3353) * Texture Tiling Example - luis605 * Removed SetTraceLogLevel(LOG_WARNING); --- .../resources/shaders/glsl330/tiling.fs | 14 +++ examples/shaders/shader_texture_tiling.c | 94 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl330/tiling.fs create mode 100644 examples/shaders/shader_texture_tiling.c diff --git a/examples/shaders/resources/shaders/glsl330/tiling.fs b/examples/shaders/resources/shaders/glsl330/tiling.fs new file mode 100644 index 000000000..6e7f52434 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/tiling.fs @@ -0,0 +1,14 @@ +#version 330 core + +uniform sampler2D diffuseMap; +uniform vec2 tiling; + +in vec2 fragTexCoord; + +out vec4 fragColor; + +void main() +{ + vec2 texCoord = fragTexCoord * tiling; + fragColor = texture(diffuseMap, texCoord); +} diff --git a/examples/shaders/shader_texture_tiling.c b/examples/shaders/shader_texture_tiling.c new file mode 100644 index 000000000..868d6b8f1 --- /dev/null +++ b/examples/shaders/shader_texture_tiling.c @@ -0,0 +1,94 @@ +/******************************************************************************************* +* +* raylib [textures] example - Texture Tiling +* +* Example demonstrates how to tile a texture on a 3D model using raylib. +* +* Example contributed by Luís Almeida (https://github.com/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) 2023 Luís Almeida (https://github.com/luis605) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ + +int main(void) +{ + const int screenWidth = 800; + const int screenHeight = 600; + + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "Raylib Texture Tiling"); + + SetTargetFPS(60); + + // Load a texture + Texture2D texture = LoadTexture("resources/raylib_logo.png"); + + // Create a cube mesh + Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); + + // Load the texture onto the GPU + Model model = LoadModelFromMesh(cube); + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; + + // Set the tiling of the texture + float tiling[2] = {3.0f, 3.0f}; + Shader shader = LoadShader(0, "resources/shaders/glsl330/tiling.fs"); // Create a custom shader in a .glsl file + SetShaderValue(shader, GetShaderLocation(shader, "tiling"), tiling, SHADER_UNIFORM_VEC2); + model.materials[0].shader = shader; + + // Camera setup + Camera camera = { 0 }; + camera.position = (Vector3){ 3.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; + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + + BeginDrawing(); + ClearBackground(RAYWHITE); + UpdateCamera(&camera, CAMERA_FREE); + + // Draw the model + { + BeginMode3D(camera); + BeginShaderMode(shader); + + DrawModel(model, (Vector3){ 0.0f, 0.0f, 0.0f }, 5.0f, WHITE); + + EndShaderMode(); + EndMode3D(); + } + + DrawText("Use mouse to rotate the camera", 10, 10, 20, DARKGRAY); + + EndDrawing(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + UnloadShader(shader); // Unload shader + + + CloseWindow(); // Close window and OpenGL context + + return 0; +} From da5407b77651dc3ffb6ff9df466ac0ec57d82e4f Mon Sep 17 00:00:00 2001 From: DaveH355 <101005658+DaveH355@users.noreply.github.com> Date: Sun, 8 Oct 2023 03:02:05 +0800 Subject: [PATCH 0669/1710] Optimize m3d mesh creation (#3363) * Optimize m3d mesh creation * Avoid qsort() in rmodels.c * Revert "Avoid qsort() in rmodels.c" This reverts commit dc1bd559fdda8d338d480dd7d9c3d77bb1ec5ac2. * Add comment --- src/rmodels.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/rmodels.c b/src/rmodels.c index f9ceec137..c25fe3342 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5567,6 +5567,15 @@ static Model LoadVOX(const char *fileName) 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); } +// Comparison function for qsort +static int m3d_compare_faces(const void *a, const void *b) +{ + m3df_t *fa = (m3df_t *)a; + m3df_t *fb = (m3df_t *)b; + + return (fa->materialid - fb->materialid); +} + // Load M3D mesh data static Model LoadM3D(const char *fileName) { @@ -5614,6 +5623,9 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; + // failsafe, model should already have faces grouped by material + qsort(m3d->face, m3d->numface, sizeof(m3df_t), m3d_compare_faces); + 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)); From 52ba44c47407fe14ba57c312045b6200099b9117 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 7 Oct 2023 21:07:50 +0200 Subject: [PATCH 0670/1710] REVIEWED: #3363 --- src/rmodels.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index c25fe3342..27fe4d85f 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5567,15 +5567,6 @@ static Model LoadVOX(const char *fileName) 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); } -// Comparison function for qsort -static int m3d_compare_faces(const void *a, const void *b) -{ - m3df_t *fa = (m3df_t *)a; - m3df_t *fb = (m3df_t *)b; - - return (fa->materialid - fb->materialid); -} - // Load M3D mesh data static Model LoadM3D(const char *fileName) { @@ -5623,8 +5614,19 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; - // failsafe, model should already have faces grouped by material + // WARNING: Sorting is not needed, valid M3D model files should already be sorted + // faces should already by grouped by material + // Just keeping the sorting function for reference (Check PR #3363) + /* + static int m3d_compare_faces(const void *a, const void *b) + { + m3df_t *fa = (m3df_t *)a; + m3df_t *fb = (m3df_t *)b; + return (fa->materialid - fb->materialid); + } + qsort(m3d->face, m3d->numface, sizeof(m3df_t), m3d_compare_faces); + */ model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); @@ -5692,7 +5694,7 @@ static Model LoadM3D(const char *fileName) 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 + // 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) From 97c2744a16ad7e6cf6b546a7a6416ec6b21e3a6c Mon Sep 17 00:00:00 2001 From: BLUELOVETH Date: Sun, 8 Oct 2023 03:10:27 +0800 Subject: [PATCH 0671/1710] Update `raylib_api.*` (#3379) --- parser/output/raylib_api.json | 281 ++++++-- parser/output/raylib_api.lua | 209 ++++-- parser/output/raylib_api.txt | 1158 +++++++++++++++++---------------- parser/output/raylib_api.xml | 126 ++-- 4 files changed, 1088 insertions(+), 686 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index b42f03a83..ef94c3d86 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -1428,6 +1428,11 @@ "value": 16384, "description": "Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED" }, + { + "name": "FLAG_BORDERLESS_WINDOWED_MODE", + "value": 32768, + "description": "Set to run program in borderless windowed mode" + }, { "name": "FLAG_MSAA_4X_HINT", "value": 32, @@ -2605,58 +2610,73 @@ "description": "32*4 bpp (4 channels - float)" }, { - "name": "PIXELFORMAT_COMPRESSED_DXT1_RGB", + "name": "PIXELFORMAT_UNCOMPRESSED_R16", "value": 11, + "description": "16 bpp (1 channel - half float)" + }, + { + "name": "PIXELFORMAT_UNCOMPRESSED_R16G16B16", + "value": 12, + "description": "16*3 bpp (3 channels - half float)" + }, + { + "name": "PIXELFORMAT_UNCOMPRESSED_R16G16B16A16", + "value": 13, + "description": "16*4 bpp (4 channels - half float)" + }, + { + "name": "PIXELFORMAT_COMPRESSED_DXT1_RGB", + "value": 14, "description": "4 bpp (no alpha)" }, { "name": "PIXELFORMAT_COMPRESSED_DXT1_RGBA", - "value": 12, + "value": 15, "description": "4 bpp (1 bit alpha)" }, { "name": "PIXELFORMAT_COMPRESSED_DXT3_RGBA", - "value": 13, + "value": 16, "description": "8 bpp" }, { "name": "PIXELFORMAT_COMPRESSED_DXT5_RGBA", - "value": 14, - "description": "8 bpp" - }, - { - "name": "PIXELFORMAT_COMPRESSED_ETC1_RGB", - "value": 15, - "description": "4 bpp" - }, - { - "name": "PIXELFORMAT_COMPRESSED_ETC2_RGB", - "value": 16, - "description": "4 bpp" - }, - { - "name": "PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA", "value": 17, "description": "8 bpp" }, { - "name": "PIXELFORMAT_COMPRESSED_PVRT_RGB", + "name": "PIXELFORMAT_COMPRESSED_ETC1_RGB", "value": 18, "description": "4 bpp" }, { - "name": "PIXELFORMAT_COMPRESSED_PVRT_RGBA", + "name": "PIXELFORMAT_COMPRESSED_ETC2_RGB", "value": 19, "description": "4 bpp" }, { - "name": "PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA", + "name": "PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA", "value": 20, "description": "8 bpp" }, { - "name": "PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA", + "name": "PIXELFORMAT_COMPRESSED_PVRT_RGB", "value": 21, + "description": "4 bpp" + }, + { + "name": "PIXELFORMAT_COMPRESSED_PVRT_RGBA", + "value": 22, + "description": "4 bpp" + }, + { + "name": "PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA", + "value": 23, + "description": "8 bpp" + }, + { + "name": "PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA", + "value": 24, "description": "2 bpp" } ] @@ -2986,8 +3006,8 @@ "name": "fileName" }, { - "type": "unsigned int *", - "name": "bytesRead" + "type": "int *", + "name": "dataSize" } ] }, @@ -3005,8 +3025,8 @@ "name": "data" }, { - "type": "unsigned int", - "name": "bytesToWrite" + "type": "int", + "name": "dataSize" } ] }, @@ -3155,6 +3175,11 @@ "description": "Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP)", "returnType": "void" }, + { + "name": "ToggleBorderlessWindowed", + "description": "Toggle window state: borderless windowed (only PLATFORM_DESKTOP)", + "returnType": "void" + }, { "name": "MaximizeWindow", "description": "Set window state: maximized, if resizable (only PLATFORM_DESKTOP)", @@ -3198,7 +3223,7 @@ }, { "name": "SetWindowTitle", - "description": "Set title for window (only PLATFORM_DESKTOP)", + "description": "Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)", "returnType": "void", "params": [ { @@ -3224,7 +3249,7 @@ }, { "name": "SetWindowMonitor", - "description": "Set monitor for the current window (fullscreen mode)", + "description": "Set monitor for the current window", "returnType": "void", "params": [ { @@ -3248,6 +3273,21 @@ } ] }, + { + "name": "SetWindowMaxSize", + "description": "Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)", + "returnType": "void", + "params": [ + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + } + ] + }, { "name": "SetWindowSize", "description": "Set window dimensions", @@ -3274,6 +3314,11 @@ } ] }, + { + "name": "SetWindowFocused", + "description": "Set window focused (only PLATFORM_DESKTOP)", + "returnType": "void" + }, { "name": "GetWindowHandle", "description": "Get native window handle", @@ -3387,7 +3432,7 @@ }, { "name": "GetMonitorName", - "description": "Get the human-readable, UTF-8 encoded name of the primary monitor", + "description": "Get the human-readable, UTF-8 encoded name of the specified monitor", "returnType": "const char *", "params": [ { @@ -4132,8 +4177,8 @@ "name": "fileName" }, { - "type": "unsigned int *", - "name": "bytesRead" + "type": "int *", + "name": "dataSize" } ] }, @@ -4162,8 +4207,8 @@ "name": "data" }, { - "type": "unsigned int", - "name": "bytesToWrite" + "type": "int", + "name": "dataSize" } ] }, @@ -4177,8 +4222,8 @@ "name": "data" }, { - "type": "unsigned int", - "name": "size" + "type": "int", + "name": "dataSize" }, { "type": "const char *", @@ -4333,7 +4378,7 @@ }, { "name": "GetApplicationDirectory", - "description": "Get the directory if the running application (uses static string)", + "description": "Get the directory of the running application (uses static string)", "returnType": "const char *" }, { @@ -4514,6 +4559,17 @@ } ] }, + { + "name": "IsKeyPressedRepeat", + "description": "Check if a key has been pressed again (Only PLATFORM_DESKTOP)", + "returnType": "bool", + "params": [ + { + "type": "int", + "name": "key" + } + ] + }, { "name": "IsKeyDown", "description": "Check if a key is being pressed", @@ -4876,7 +4932,7 @@ "returnType": "bool", "params": [ { - "type": "int", + "type": "unsigned int", "name": "gesture" } ] @@ -5148,6 +5204,52 @@ } ] }, + { + "name": "DrawLineBSpline", + "description": "Draw a B-Spline line, minimum 4 points", + "returnType": "void", + "params": [ + { + "type": "Vector2 *", + "name": "points" + }, + { + "type": "int", + "name": "pointCount" + }, + { + "type": "float", + "name": "thick" + }, + { + "type": "Color", + "name": "color" + } + ] + }, + { + "name": "DrawLineCatmullRom", + "description": "Draw a Catmull Rom spline line, minimum 4 points", + "returnType": "void", + "params": [ + { + "type": "Vector2 *", + "name": "points" + }, + { + "type": "int", + "name": "pointCount" + }, + { + "type": "float", + "name": "thick" + }, + { + "type": "Color", + "name": "color" + } + ] + }, { "name": "DrawLineStrip", "description": "Draw lines sequence", @@ -6119,6 +6221,25 @@ } ] }, + { + "name": "LoadImageSvg", + "description": "Load image from SVG file data or string with specified size", + "returnType": "Image", + "params": [ + { + "type": "const char *", + "name": "fileNameOrString" + }, + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + } + ] + }, { "name": "LoadImageAnim", "description": "Load image sequence from file (frames appended to image.data)", @@ -6206,6 +6327,25 @@ } ] }, + { + "name": "ExportImageToMemory", + "description": "Export image to memory buffer", + "returnType": "unsigned char *", + "params": [ + { + "type": "Image", + "name": "image" + }, + { + "type": "const char *", + "name": "fileType" + }, + { + "type": "int *", + "name": "fileSize" + } + ] + }, { "name": "ExportImageAsCode", "description": "Export image as code file defining an array of bytes, returns true on success", @@ -6759,7 +6899,7 @@ }, { "name": "ImageRotate", - "description": "Rotate image by input angle in degrees (-359 to 359) ", + "description": "Rotate image by input angle in degrees (-359 to 359)", "returnType": "void", "params": [ { @@ -7930,7 +8070,7 @@ }, { "name": "LoadFontEx", - "description": "Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set", + "description": "Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character setFont", "returnType": "Font", "params": [ { @@ -7943,11 +8083,11 @@ }, { "type": "int *", - "name": "fontChars" + "name": "codepoints" }, { "type": "int", - "name": "glyphCount" + "name": "codepointCount" } ] }, @@ -7993,11 +8133,11 @@ }, { "type": "int *", - "name": "fontChars" + "name": "codepoints" }, { "type": "int", - "name": "glyphCount" + "name": "codepointCount" } ] }, @@ -8031,11 +8171,11 @@ }, { "type": "int *", - "name": "fontChars" + "name": "codepoints" }, { "type": "int", - "name": "glyphCount" + "name": "codepointCount" }, { "type": "int", @@ -8050,11 +8190,11 @@ "params": [ { "type": "const GlyphInfo *", - "name": "chars" + "name": "glyphs" }, { "type": "Rectangle **", - "name": "recs" + "name": "glyphRecs" }, { "type": "int", @@ -8081,7 +8221,7 @@ "params": [ { "type": "GlyphInfo *", - "name": "chars" + "name": "glyphs" }, { "type": "int", @@ -8269,7 +8409,7 @@ }, { "type": "int", - "name": "count" + "name": "codepointCount" }, { "type": "Vector2", @@ -8289,6 +8429,17 @@ } ] }, + { + "name": "SetTextLineSpacing", + "description": "Set vertical line spacing when drawing with line-breaks", + "returnType": "void", + "params": [ + { + "type": "int", + "name": "spacing" + } + ] + }, { "name": "MeasureText", "description": "Measure string width for default font", @@ -9937,7 +10088,7 @@ "name": "fileName" }, { - "type": "unsigned int *", + "type": "int *", "name": "animCount" } ] @@ -9982,8 +10133,8 @@ "name": "animations" }, { - "type": "unsigned int", - "name": "count" + "type": "int", + "name": "animCount" } ] }, @@ -10251,6 +10402,17 @@ } ] }, + { + "name": "LoadSoundAlias", + "description": "Create a new sound that shares the same sample data as the source sound, does not own the sound data", + "returnType": "Sound", + "params": [ + { + "type": "Sound", + "name": "source" + } + ] + }, { "name": "IsSoundReady", "description": "Checks if a sound is ready", @@ -10303,6 +10465,17 @@ } ] }, + { + "name": "UnloadSoundAlias", + "description": "Unload a sound alias (does not deallocate sample data)", + "returnType": "void", + "params": [ + { + "type": "Sound", + "name": "alias" + } + ] + }, { "name": "ExportWave", "description": "Export wave data to file, returns true on success", @@ -10907,7 +11080,7 @@ }, { "name": "AttachAudioStreamProcessor", - "description": "Attach audio stream processor to stream", + "description": "Attach audio stream processor to stream, receives the samples as s", "returnType": "void", "params": [ { @@ -10937,7 +11110,7 @@ }, { "name": "AttachAudioMixedProcessor", - "description": "Attach audio stream processor to the entire audio pipeline", + "description": "Attach audio stream processor to the entire audio pipeline, receives the samples as s", "returnType": "void", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index ed11ac08f..26b2564a5 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -1428,6 +1428,11 @@ return { value = 16384, description = "Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED" }, + { + name = "FLAG_BORDERLESS_WINDOWED_MODE", + value = 32768, + description = "Set to run program in borderless windowed mode" + }, { name = "FLAG_MSAA_4X_HINT", value = 32, @@ -2605,58 +2610,73 @@ return { description = "32*4 bpp (4 channels - float)" }, { - name = "PIXELFORMAT_COMPRESSED_DXT1_RGB", + name = "PIXELFORMAT_UNCOMPRESSED_R16", value = 11, + description = "16 bpp (1 channel - half float)" + }, + { + name = "PIXELFORMAT_UNCOMPRESSED_R16G16B16", + value = 12, + description = "16*3 bpp (3 channels - half float)" + }, + { + name = "PIXELFORMAT_UNCOMPRESSED_R16G16B16A16", + value = 13, + description = "16*4 bpp (4 channels - half float)" + }, + { + name = "PIXELFORMAT_COMPRESSED_DXT1_RGB", + value = 14, description = "4 bpp (no alpha)" }, { name = "PIXELFORMAT_COMPRESSED_DXT1_RGBA", - value = 12, + value = 15, description = "4 bpp (1 bit alpha)" }, { name = "PIXELFORMAT_COMPRESSED_DXT3_RGBA", - value = 13, + value = 16, description = "8 bpp" }, { name = "PIXELFORMAT_COMPRESSED_DXT5_RGBA", - value = 14, - description = "8 bpp" - }, - { - name = "PIXELFORMAT_COMPRESSED_ETC1_RGB", - value = 15, - description = "4 bpp" - }, - { - name = "PIXELFORMAT_COMPRESSED_ETC2_RGB", - value = 16, - description = "4 bpp" - }, - { - name = "PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA", value = 17, description = "8 bpp" }, { - name = "PIXELFORMAT_COMPRESSED_PVRT_RGB", + name = "PIXELFORMAT_COMPRESSED_ETC1_RGB", value = 18, description = "4 bpp" }, { - name = "PIXELFORMAT_COMPRESSED_PVRT_RGBA", + name = "PIXELFORMAT_COMPRESSED_ETC2_RGB", value = 19, description = "4 bpp" }, { - name = "PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA", + name = "PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA", value = 20, description = "8 bpp" }, { - name = "PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA", + name = "PIXELFORMAT_COMPRESSED_PVRT_RGB", value = 21, + description = "4 bpp" + }, + { + name = "PIXELFORMAT_COMPRESSED_PVRT_RGBA", + value = 22, + description = "4 bpp" + }, + { + name = "PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA", + value = 23, + description = "8 bpp" + }, + { + name = "PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA", + value = 24, description = "2 bpp" } } @@ -2973,7 +2993,7 @@ return { returnType = "unsigned char *", params = { {type = "const char *", name = "fileName"}, - {type = "unsigned int *", name = "bytesRead"} + {type = "int *", name = "dataSize"} } }, { @@ -2983,7 +3003,7 @@ return { params = { {type = "const char *", name = "fileName"}, {type = "void *", name = "data"}, - {type = "unsigned int", name = "bytesToWrite"} + {type = "int", name = "dataSize"} } }, { @@ -3098,6 +3118,11 @@ return { description = "Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP)", returnType = "void" }, + { + name = "ToggleBorderlessWindowed", + description = "Toggle window state: borderless windowed (only PLATFORM_DESKTOP)", + returnType = "void" + }, { name = "MaximizeWindow", description = "Set window state: maximized, if resizable (only PLATFORM_DESKTOP)", @@ -3132,7 +3157,7 @@ return { }, { name = "SetWindowTitle", - description = "Set title for window (only PLATFORM_DESKTOP)", + description = "Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)", returnType = "void", params = { {type = "const char *", name = "title"} @@ -3149,7 +3174,7 @@ return { }, { name = "SetWindowMonitor", - description = "Set monitor for the current window (fullscreen mode)", + description = "Set monitor for the current window", returnType = "void", params = { {type = "int", name = "monitor"} @@ -3164,6 +3189,15 @@ return { {type = "int", name = "height"} } }, + { + name = "SetWindowMaxSize", + description = "Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)", + returnType = "void", + params = { + {type = "int", name = "width"}, + {type = "int", name = "height"} + } + }, { name = "SetWindowSize", description = "Set window dimensions", @@ -3181,6 +3215,11 @@ return { {type = "float", name = "opacity"} } }, + { + name = "SetWindowFocused", + description = "Set window focused (only PLATFORM_DESKTOP)", + returnType = "void" + }, { name = "GetWindowHandle", description = "Get native window handle", @@ -3276,7 +3315,7 @@ return { }, { name = "GetMonitorName", - description = "Get the human-readable, UTF-8 encoded name of the primary monitor", + description = "Get the human-readable, UTF-8 encoded name of the specified monitor", returnType = "const char *", params = { {type = "int", name = "monitor"} @@ -3792,7 +3831,7 @@ return { returnType = "unsigned char *", params = { {type = "const char *", name = "fileName"}, - {type = "unsigned int *", name = "bytesRead"} + {type = "int *", name = "dataSize"} } }, { @@ -3810,7 +3849,7 @@ return { params = { {type = "const char *", name = "fileName"}, {type = "void *", name = "data"}, - {type = "unsigned int", name = "bytesToWrite"} + {type = "int", name = "dataSize"} } }, { @@ -3819,7 +3858,7 @@ return { returnType = "bool", params = { {type = "const unsigned char *", name = "data"}, - {type = "unsigned int", name = "size"}, + {type = "int", name = "dataSize"}, {type = "const char *", name = "fileName"} } }, @@ -3928,7 +3967,7 @@ return { }, { name = "GetApplicationDirectory", - description = "Get the directory if the running application (uses static string)", + description = "Get the directory of the running application (uses static string)", returnType = "const char *" }, { @@ -4046,6 +4085,14 @@ return { {type = "int", name = "key"} } }, + { + name = "IsKeyPressedRepeat", + description = "Check if a key has been pressed again (Only PLATFORM_DESKTOP)", + returnType = "bool", + params = { + {type = "int", name = "key"} + } + }, { name = "IsKeyDown", description = "Check if a key is being pressed", @@ -4311,7 +4358,7 @@ return { description = "Check if a gesture have been detected", returnType = "bool", params = { - {type = "int", name = "gesture"} + {type = "unsigned int", name = "gesture"} } }, { @@ -4461,6 +4508,28 @@ return { {type = "Color", name = "color"} } }, + { + name = "DrawLineBSpline", + description = "Draw a B-Spline line, minimum 4 points", + returnType = "void", + params = { + {type = "Vector2 *", name = "points"}, + {type = "int", name = "pointCount"}, + {type = "float", name = "thick"}, + {type = "Color", name = "color"} + } + }, + { + name = "DrawLineCatmullRom", + description = "Draw a Catmull Rom spline line, minimum 4 points", + returnType = "void", + params = { + {type = "Vector2 *", name = "points"}, + {type = "int", name = "pointCount"}, + {type = "float", name = "thick"}, + {type = "Color", name = "color"} + } + }, { name = "DrawLineStrip", description = "Draw lines sequence", @@ -4919,6 +4988,16 @@ return { {type = "int", name = "headerSize"} } }, + { + name = "LoadImageSvg", + description = "Load image from SVG file data or string with specified size", + returnType = "Image", + params = { + {type = "const char *", name = "fileNameOrString"}, + {type = "int", name = "width"}, + {type = "int", name = "height"} + } + }, { name = "LoadImageAnim", description = "Load image sequence from file (frames appended to image.data)", @@ -4976,6 +5055,16 @@ return { {type = "const char *", name = "fileName"} } }, + { + name = "ExportImageToMemory", + description = "Export image to memory buffer", + returnType = "unsigned char *", + params = { + {type = "Image", name = "image"}, + {type = "const char *", name = "fileType"}, + {type = "int *", name = "fileSize"} + } + }, { name = "ExportImageAsCode", description = "Export image as code file defining an array of bytes, returns true on success", @@ -5268,7 +5357,7 @@ return { }, { name = "ImageRotate", - description = "Rotate image by input angle in degrees (-359 to 359) ", + description = "Rotate image by input angle in degrees (-359 to 359)", returnType = "void", params = { {type = "Image *", name = "image"}, @@ -5911,13 +6000,13 @@ return { }, { name = "LoadFontEx", - description = "Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set", + description = "Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character setFont", returnType = "Font", params = { {type = "const char *", name = "fileName"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "fontChars"}, - {type = "int", name = "glyphCount"} + {type = "int *", name = "codepoints"}, + {type = "int", name = "codepointCount"} } }, { @@ -5939,8 +6028,8 @@ return { {type = "const unsigned char *", name = "fileData"}, {type = "int", name = "dataSize"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "fontChars"}, - {type = "int", name = "glyphCount"} + {type = "int *", name = "codepoints"}, + {type = "int", name = "codepointCount"} } }, { @@ -5959,8 +6048,8 @@ return { {type = "const unsigned char *", name = "fileData"}, {type = "int", name = "dataSize"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "fontChars"}, - {type = "int", name = "glyphCount"}, + {type = "int *", name = "codepoints"}, + {type = "int", name = "codepointCount"}, {type = "int", name = "type"} } }, @@ -5969,8 +6058,8 @@ return { description = "Generate image font atlas using chars info", returnType = "Image", params = { - {type = "const GlyphInfo *", name = "chars"}, - {type = "Rectangle **", name = "recs"}, + {type = "const GlyphInfo *", name = "glyphs"}, + {type = "Rectangle **", name = "glyphRecs"}, {type = "int", name = "glyphCount"}, {type = "int", name = "fontSize"}, {type = "int", name = "padding"}, @@ -5982,7 +6071,7 @@ return { description = "Unload font chars info data (RAM)", returnType = "void", params = { - {type = "GlyphInfo *", name = "chars"}, + {type = "GlyphInfo *", name = "glyphs"}, {type = "int", name = "glyphCount"} } }, @@ -6071,13 +6160,21 @@ return { params = { {type = "Font", name = "font"}, {type = "const int *", name = "codepoints"}, - {type = "int", name = "count"}, + {type = "int", name = "codepointCount"}, {type = "Vector2", name = "position"}, {type = "float", name = "fontSize"}, {type = "float", name = "spacing"}, {type = "Color", name = "tint"} } }, + { + name = "SetTextLineSpacing", + description = "Set vertical line spacing when drawing with line-breaks", + returnType = "void", + params = { + {type = "int", name = "spacing"} + } + }, { name = "MeasureText", description = "Measure string width for default font", @@ -6954,7 +7051,7 @@ return { returnType = "ModelAnimation *", params = { {type = "const char *", name = "fileName"}, - {type = "unsigned int *", name = "animCount"} + {type = "int *", name = "animCount"} } }, { @@ -6981,7 +7078,7 @@ return { returnType = "void", params = { {type = "ModelAnimation *", name = "animations"}, - {type = "unsigned int", name = "count"} + {type = "int", name = "animCount"} } }, { @@ -7140,6 +7237,14 @@ return { {type = "Wave", name = "wave"} } }, + { + name = "LoadSoundAlias", + description = "Create a new sound that shares the same sample data as the source sound, does not own the sound data", + returnType = "Sound", + params = { + {type = "Sound", name = "source"} + } + }, { name = "IsSoundReady", description = "Checks if a sound is ready", @@ -7174,6 +7279,14 @@ return { {type = "Sound", name = "sound"} } }, + { + name = "UnloadSoundAlias", + description = "Unload a sound alias (does not deallocate sample data)", + returnType = "void", + params = { + {type = "Sound", name = "alias"} + } + }, { name = "ExportWave", description = "Export wave data to file, returns true on success", @@ -7568,7 +7681,7 @@ return { }, { name = "AttachAudioStreamProcessor", - description = "Attach audio stream processor to stream", + description = "Attach audio stream processor to stream, receives the samples as s", returnType = "void", params = { {type = "AudioStream", name = "stream"}, @@ -7586,7 +7699,7 @@ return { }, { name = "AttachAudioMixedProcessor", - description = "Attach audio stream processor to the entire audio pipeline", + description = "Attach audio stream processor to the entire audio pipeline, receives the samples as s", returnType = "void", params = { {type = "AudioCallback", name = "processor"} diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 0d0ccccf1..725d6ae91 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -566,7 +566,7 @@ Alias 005: Camera Enums found: 21 -Enum 01: ConfigFlags (15 values) +Enum 01: ConfigFlags (16 values) Name: ConfigFlags Description: System/Window config flags Value[FLAG_VSYNC_HINT]: 64 @@ -582,6 +582,7 @@ Enum 01: ConfigFlags (15 values) Value[FLAG_WINDOW_TRANSPARENT]: 16 Value[FLAG_WINDOW_HIGHDPI]: 8192 Value[FLAG_WINDOW_MOUSE_PASSTHROUGH]: 16384 + Value[FLAG_BORDERLESS_WINDOWED_MODE]: 32768 Value[FLAG_MSAA_4X_HINT]: 32 Value[FLAG_INTERLACED_HINT]: 65536 Enum 02: TraceLogLevel (8 values) @@ -824,7 +825,7 @@ Enum 11: ShaderAttributeDataType (4 values) Value[SHADER_ATTRIB_VEC2]: 1 Value[SHADER_ATTRIB_VEC3]: 2 Value[SHADER_ATTRIB_VEC4]: 3 -Enum 12: PixelFormat (21 values) +Enum 12: PixelFormat (24 values) Name: PixelFormat Description: Pixel formats Value[PIXELFORMAT_UNCOMPRESSED_GRAYSCALE]: 1 @@ -837,17 +838,20 @@ Enum 12: PixelFormat (21 values) Value[PIXELFORMAT_UNCOMPRESSED_R32]: 8 Value[PIXELFORMAT_UNCOMPRESSED_R32G32B32]: 9 Value[PIXELFORMAT_UNCOMPRESSED_R32G32B32A32]: 10 - Value[PIXELFORMAT_COMPRESSED_DXT1_RGB]: 11 - Value[PIXELFORMAT_COMPRESSED_DXT1_RGBA]: 12 - Value[PIXELFORMAT_COMPRESSED_DXT3_RGBA]: 13 - Value[PIXELFORMAT_COMPRESSED_DXT5_RGBA]: 14 - Value[PIXELFORMAT_COMPRESSED_ETC1_RGB]: 15 - Value[PIXELFORMAT_COMPRESSED_ETC2_RGB]: 16 - Value[PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA]: 17 - Value[PIXELFORMAT_COMPRESSED_PVRT_RGB]: 18 - Value[PIXELFORMAT_COMPRESSED_PVRT_RGBA]: 19 - Value[PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA]: 20 - Value[PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA]: 21 + Value[PIXELFORMAT_UNCOMPRESSED_R16]: 11 + Value[PIXELFORMAT_UNCOMPRESSED_R16G16B16]: 12 + Value[PIXELFORMAT_UNCOMPRESSED_R16G16B16A16]: 13 + Value[PIXELFORMAT_COMPRESSED_DXT1_RGB]: 14 + Value[PIXELFORMAT_COMPRESSED_DXT1_RGBA]: 15 + Value[PIXELFORMAT_COMPRESSED_DXT3_RGBA]: 16 + Value[PIXELFORMAT_COMPRESSED_DXT5_RGBA]: 17 + Value[PIXELFORMAT_COMPRESSED_ETC1_RGB]: 18 + Value[PIXELFORMAT_COMPRESSED_ETC2_RGB]: 19 + Value[PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA]: 20 + Value[PIXELFORMAT_COMPRESSED_PVRT_RGB]: 21 + Value[PIXELFORMAT_COMPRESSED_PVRT_RGBA]: 22 + Value[PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA]: 23 + Value[PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA]: 24 Enum 13: TextureFilter (6 values) Name: TextureFilter Description: Texture parameters: filter mode @@ -938,14 +942,14 @@ Callback 002: LoadFileDataCallback() (2 input parameters) Return type: unsigned char * Description: FileIO: Load binary data Param[1]: fileName (type: const char *) - Param[2]: bytesRead (type: unsigned int *) + Param[2]: dataSize (type: int *) Callback 003: SaveFileDataCallback() (3 input parameters) Name: SaveFileDataCallback Return type: bool Description: FileIO: Save binary data Param[1]: fileName (type: const char *) Param[2]: data (type: void *) - Param[3]: bytesToWrite (type: unsigned int) + Param[3]: dataSize (type: int) Callback 004: LoadFileTextCallback() (1 input parameters) Name: LoadFileTextCallback Return type: char * @@ -964,7 +968,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 518 +Functions found: 529 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -1038,276 +1042,292 @@ Function 014: ToggleFullscreen() (0 input parameters) Return type: void Description: Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) No input parameters -Function 015: MaximizeWindow() (0 input parameters) +Function 015: ToggleBorderlessWindowed() (0 input parameters) + Name: ToggleBorderlessWindowed + Return type: void + Description: Toggle window state: borderless windowed (only PLATFORM_DESKTOP) + No input parameters +Function 016: MaximizeWindow() (0 input parameters) Name: MaximizeWindow Return type: void Description: Set window state: maximized, if resizable (only PLATFORM_DESKTOP) No input parameters -Function 016: MinimizeWindow() (0 input parameters) +Function 017: MinimizeWindow() (0 input parameters) Name: MinimizeWindow Return type: void Description: Set window state: minimized, if resizable (only PLATFORM_DESKTOP) No input parameters -Function 017: RestoreWindow() (0 input parameters) +Function 018: RestoreWindow() (0 input parameters) Name: RestoreWindow Return type: void Description: Set window state: not minimized/maximized (only PLATFORM_DESKTOP) No input parameters -Function 018: SetWindowIcon() (1 input parameters) +Function 019: SetWindowIcon() (1 input parameters) Name: SetWindowIcon Return type: void Description: Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) Param[1]: image (type: Image) -Function 019: SetWindowIcons() (2 input parameters) +Function 020: SetWindowIcons() (2 input parameters) Name: SetWindowIcons Return type: void Description: Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) Param[1]: images (type: Image *) Param[2]: count (type: int) -Function 020: SetWindowTitle() (1 input parameters) +Function 021: SetWindowTitle() (1 input parameters) Name: SetWindowTitle Return type: void - Description: Set title for window (only PLATFORM_DESKTOP) + Description: Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) Param[1]: title (type: const char *) -Function 021: SetWindowPosition() (2 input parameters) +Function 022: SetWindowPosition() (2 input parameters) Name: SetWindowPosition Return type: void Description: Set window position on screen (only PLATFORM_DESKTOP) Param[1]: x (type: int) Param[2]: y (type: int) -Function 022: SetWindowMonitor() (1 input parameters) +Function 023: SetWindowMonitor() (1 input parameters) Name: SetWindowMonitor Return type: void - Description: Set monitor for the current window (fullscreen mode) + Description: Set monitor for the current window Param[1]: monitor (type: int) -Function 023: SetWindowMinSize() (2 input parameters) +Function 024: SetWindowMinSize() (2 input parameters) Name: SetWindowMinSize Return type: void Description: Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) Param[1]: width (type: int) Param[2]: height (type: int) -Function 024: SetWindowSize() (2 input parameters) +Function 025: SetWindowMaxSize() (2 input parameters) + Name: SetWindowMaxSize + Return type: void + Description: Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE) + Param[1]: width (type: int) + Param[2]: height (type: int) +Function 026: SetWindowSize() (2 input parameters) Name: SetWindowSize Return type: void Description: Set window dimensions Param[1]: width (type: int) Param[2]: height (type: int) -Function 025: SetWindowOpacity() (1 input parameters) +Function 027: SetWindowOpacity() (1 input parameters) Name: SetWindowOpacity Return type: void Description: Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) Param[1]: opacity (type: float) -Function 026: GetWindowHandle() (0 input parameters) +Function 028: SetWindowFocused() (0 input parameters) + Name: SetWindowFocused + Return type: void + Description: Set window focused (only PLATFORM_DESKTOP) + No input parameters +Function 029: GetWindowHandle() (0 input parameters) Name: GetWindowHandle Return type: void * Description: Get native window handle No input parameters -Function 027: GetScreenWidth() (0 input parameters) +Function 030: GetScreenWidth() (0 input parameters) Name: GetScreenWidth Return type: int Description: Get current screen width No input parameters -Function 028: GetScreenHeight() (0 input parameters) +Function 031: GetScreenHeight() (0 input parameters) Name: GetScreenHeight Return type: int Description: Get current screen height No input parameters -Function 029: GetRenderWidth() (0 input parameters) +Function 032: GetRenderWidth() (0 input parameters) Name: GetRenderWidth Return type: int Description: Get current render width (it considers HiDPI) No input parameters -Function 030: GetRenderHeight() (0 input parameters) +Function 033: GetRenderHeight() (0 input parameters) Name: GetRenderHeight Return type: int Description: Get current render height (it considers HiDPI) No input parameters -Function 031: GetMonitorCount() (0 input parameters) +Function 034: GetMonitorCount() (0 input parameters) Name: GetMonitorCount Return type: int Description: Get number of connected monitors No input parameters -Function 032: GetCurrentMonitor() (0 input parameters) +Function 035: GetCurrentMonitor() (0 input parameters) Name: GetCurrentMonitor Return type: int Description: Get current connected monitor No input parameters -Function 033: GetMonitorPosition() (1 input parameters) +Function 036: GetMonitorPosition() (1 input parameters) Name: GetMonitorPosition Return type: Vector2 Description: Get specified monitor position Param[1]: monitor (type: int) -Function 034: GetMonitorWidth() (1 input parameters) +Function 037: GetMonitorWidth() (1 input parameters) Name: GetMonitorWidth Return type: int Description: Get specified monitor width (current video mode used by monitor) Param[1]: monitor (type: int) -Function 035: GetMonitorHeight() (1 input parameters) +Function 038: GetMonitorHeight() (1 input parameters) Name: GetMonitorHeight Return type: int Description: Get specified monitor height (current video mode used by monitor) Param[1]: monitor (type: int) -Function 036: GetMonitorPhysicalWidth() (1 input parameters) +Function 039: GetMonitorPhysicalWidth() (1 input parameters) Name: GetMonitorPhysicalWidth Return type: int Description: Get specified monitor physical width in millimetres Param[1]: monitor (type: int) -Function 037: GetMonitorPhysicalHeight() (1 input parameters) +Function 040: GetMonitorPhysicalHeight() (1 input parameters) Name: GetMonitorPhysicalHeight Return type: int Description: Get specified monitor physical height in millimetres Param[1]: monitor (type: int) -Function 038: GetMonitorRefreshRate() (1 input parameters) +Function 041: GetMonitorRefreshRate() (1 input parameters) Name: GetMonitorRefreshRate Return type: int Description: Get specified monitor refresh rate Param[1]: monitor (type: int) -Function 039: GetWindowPosition() (0 input parameters) +Function 042: GetWindowPosition() (0 input parameters) Name: GetWindowPosition Return type: Vector2 Description: Get window position XY on monitor No input parameters -Function 040: GetWindowScaleDPI() (0 input parameters) +Function 043: GetWindowScaleDPI() (0 input parameters) Name: GetWindowScaleDPI Return type: Vector2 Description: Get window scale DPI factor No input parameters -Function 041: GetMonitorName() (1 input parameters) +Function 044: GetMonitorName() (1 input parameters) Name: GetMonitorName Return type: const char * - Description: Get the human-readable, UTF-8 encoded name of the primary monitor + Description: Get the human-readable, UTF-8 encoded name of the specified monitor Param[1]: monitor (type: int) -Function 042: SetClipboardText() (1 input parameters) +Function 045: SetClipboardText() (1 input parameters) Name: SetClipboardText Return type: void Description: Set clipboard text content Param[1]: text (type: const char *) -Function 043: GetClipboardText() (0 input parameters) +Function 046: GetClipboardText() (0 input parameters) Name: GetClipboardText Return type: const char * Description: Get clipboard text content No input parameters -Function 044: EnableEventWaiting() (0 input parameters) +Function 047: EnableEventWaiting() (0 input parameters) Name: EnableEventWaiting Return type: void Description: Enable waiting for events on EndDrawing(), no automatic event polling No input parameters -Function 045: DisableEventWaiting() (0 input parameters) +Function 048: DisableEventWaiting() (0 input parameters) Name: DisableEventWaiting Return type: void Description: Disable waiting for events on EndDrawing(), automatic events polling No input parameters -Function 046: SwapScreenBuffer() (0 input parameters) +Function 049: SwapScreenBuffer() (0 input parameters) Name: SwapScreenBuffer Return type: void Description: Swap back buffer with front buffer (screen drawing) No input parameters -Function 047: PollInputEvents() (0 input parameters) +Function 050: PollInputEvents() (0 input parameters) Name: PollInputEvents Return type: void Description: Register all input events No input parameters -Function 048: WaitTime() (1 input parameters) +Function 051: WaitTime() (1 input parameters) Name: WaitTime Return type: void Description: Wait for some time (halt program execution) Param[1]: seconds (type: double) -Function 049: ShowCursor() (0 input parameters) +Function 052: ShowCursor() (0 input parameters) Name: ShowCursor Return type: void Description: Shows cursor No input parameters -Function 050: HideCursor() (0 input parameters) +Function 053: HideCursor() (0 input parameters) Name: HideCursor Return type: void Description: Hides cursor No input parameters -Function 051: IsCursorHidden() (0 input parameters) +Function 054: IsCursorHidden() (0 input parameters) Name: IsCursorHidden Return type: bool Description: Check if cursor is not visible No input parameters -Function 052: EnableCursor() (0 input parameters) +Function 055: EnableCursor() (0 input parameters) Name: EnableCursor Return type: void Description: Enables cursor (unlock cursor) No input parameters -Function 053: DisableCursor() (0 input parameters) +Function 056: DisableCursor() (0 input parameters) Name: DisableCursor Return type: void Description: Disables cursor (lock cursor) No input parameters -Function 054: IsCursorOnScreen() (0 input parameters) +Function 057: IsCursorOnScreen() (0 input parameters) Name: IsCursorOnScreen Return type: bool Description: Check if cursor is on the screen No input parameters -Function 055: ClearBackground() (1 input parameters) +Function 058: ClearBackground() (1 input parameters) Name: ClearBackground Return type: void Description: Set background color (framebuffer clear color) Param[1]: color (type: Color) -Function 056: BeginDrawing() (0 input parameters) +Function 059: BeginDrawing() (0 input parameters) Name: BeginDrawing Return type: void Description: Setup canvas (framebuffer) to start drawing No input parameters -Function 057: EndDrawing() (0 input parameters) +Function 060: EndDrawing() (0 input parameters) Name: EndDrawing Return type: void Description: End canvas drawing and swap buffers (double buffering) No input parameters -Function 058: BeginMode2D() (1 input parameters) +Function 061: BeginMode2D() (1 input parameters) Name: BeginMode2D Return type: void Description: Begin 2D mode with custom camera (2D) Param[1]: camera (type: Camera2D) -Function 059: EndMode2D() (0 input parameters) +Function 062: EndMode2D() (0 input parameters) Name: EndMode2D Return type: void Description: Ends 2D mode with custom camera No input parameters -Function 060: BeginMode3D() (1 input parameters) +Function 063: BeginMode3D() (1 input parameters) Name: BeginMode3D Return type: void Description: Begin 3D mode with custom camera (3D) Param[1]: camera (type: Camera3D) -Function 061: EndMode3D() (0 input parameters) +Function 064: EndMode3D() (0 input parameters) Name: EndMode3D Return type: void Description: Ends 3D mode and returns to default 2D orthographic mode No input parameters -Function 062: BeginTextureMode() (1 input parameters) +Function 065: BeginTextureMode() (1 input parameters) Name: BeginTextureMode Return type: void Description: Begin drawing to render texture Param[1]: target (type: RenderTexture2D) -Function 063: EndTextureMode() (0 input parameters) +Function 066: EndTextureMode() (0 input parameters) Name: EndTextureMode Return type: void Description: Ends drawing to render texture No input parameters -Function 064: BeginShaderMode() (1 input parameters) +Function 067: BeginShaderMode() (1 input parameters) Name: BeginShaderMode Return type: void Description: Begin custom shader drawing Param[1]: shader (type: Shader) -Function 065: EndShaderMode() (0 input parameters) +Function 068: EndShaderMode() (0 input parameters) Name: EndShaderMode Return type: void Description: End custom shader drawing (use default shader) No input parameters -Function 066: BeginBlendMode() (1 input parameters) +Function 069: BeginBlendMode() (1 input parameters) Name: BeginBlendMode Return type: void Description: Begin blending mode (alpha, additive, multiplied, subtract, custom) Param[1]: mode (type: int) -Function 067: EndBlendMode() (0 input parameters) +Function 070: EndBlendMode() (0 input parameters) Name: EndBlendMode Return type: void Description: End blending mode (reset to default: alpha blending) No input parameters -Function 068: BeginScissorMode() (4 input parameters) +Function 071: BeginScissorMode() (4 input parameters) Name: BeginScissorMode Return type: void Description: Begin scissor mode (define screen area for following drawing) @@ -1315,61 +1335,61 @@ Function 068: BeginScissorMode() (4 input parameters) Param[2]: y (type: int) Param[3]: width (type: int) Param[4]: height (type: int) -Function 069: EndScissorMode() (0 input parameters) +Function 072: EndScissorMode() (0 input parameters) Name: EndScissorMode Return type: void Description: End scissor mode No input parameters -Function 070: BeginVrStereoMode() (1 input parameters) +Function 073: BeginVrStereoMode() (1 input parameters) Name: BeginVrStereoMode Return type: void Description: Begin stereo rendering (requires VR simulator) Param[1]: config (type: VrStereoConfig) -Function 071: EndVrStereoMode() (0 input parameters) +Function 074: EndVrStereoMode() (0 input parameters) Name: EndVrStereoMode Return type: void Description: End stereo rendering (requires VR simulator) No input parameters -Function 072: LoadVrStereoConfig() (1 input parameters) +Function 075: LoadVrStereoConfig() (1 input parameters) Name: LoadVrStereoConfig Return type: VrStereoConfig Description: Load VR stereo config for VR simulator device parameters Param[1]: device (type: VrDeviceInfo) -Function 073: UnloadVrStereoConfig() (1 input parameters) +Function 076: UnloadVrStereoConfig() (1 input parameters) Name: UnloadVrStereoConfig Return type: void Description: Unload VR stereo config Param[1]: config (type: VrStereoConfig) -Function 074: LoadShader() (2 input parameters) +Function 077: LoadShader() (2 input parameters) Name: LoadShader Return type: Shader Description: Load shader from files and bind default locations Param[1]: vsFileName (type: const char *) Param[2]: fsFileName (type: const char *) -Function 075: LoadShaderFromMemory() (2 input parameters) +Function 078: LoadShaderFromMemory() (2 input parameters) Name: LoadShaderFromMemory Return type: Shader Description: Load shader from code strings and bind default locations Param[1]: vsCode (type: const char *) Param[2]: fsCode (type: const char *) -Function 076: IsShaderReady() (1 input parameters) +Function 079: IsShaderReady() (1 input parameters) Name: IsShaderReady Return type: bool Description: Check if a shader is ready Param[1]: shader (type: Shader) -Function 077: GetShaderLocation() (2 input parameters) +Function 080: GetShaderLocation() (2 input parameters) Name: GetShaderLocation Return type: int Description: Get shader uniform location Param[1]: shader (type: Shader) Param[2]: uniformName (type: const char *) -Function 078: GetShaderLocationAttrib() (2 input parameters) +Function 081: GetShaderLocationAttrib() (2 input parameters) Name: GetShaderLocationAttrib Return type: int Description: Get shader attribute location Param[1]: shader (type: Shader) Param[2]: attribName (type: const char *) -Function 079: SetShaderValue() (4 input parameters) +Function 082: SetShaderValue() (4 input parameters) Name: SetShaderValue Return type: void Description: Set shader uniform value @@ -1377,7 +1397,7 @@ Function 079: SetShaderValue() (4 input parameters) Param[2]: locIndex (type: int) Param[3]: value (type: const void *) Param[4]: uniformType (type: int) -Function 080: SetShaderValueV() (5 input parameters) +Function 083: SetShaderValueV() (5 input parameters) Name: SetShaderValueV Return type: void Description: Set shader uniform value vector @@ -1386,54 +1406,54 @@ Function 080: SetShaderValueV() (5 input parameters) Param[3]: value (type: const void *) Param[4]: uniformType (type: int) Param[5]: count (type: int) -Function 081: SetShaderValueMatrix() (3 input parameters) +Function 084: SetShaderValueMatrix() (3 input parameters) Name: SetShaderValueMatrix Return type: void Description: Set shader uniform value (matrix 4x4) Param[1]: shader (type: Shader) Param[2]: locIndex (type: int) Param[3]: mat (type: Matrix) -Function 082: SetShaderValueTexture() (3 input parameters) +Function 085: SetShaderValueTexture() (3 input parameters) Name: SetShaderValueTexture Return type: void Description: Set shader uniform value for texture (sampler2d) Param[1]: shader (type: Shader) Param[2]: locIndex (type: int) Param[3]: texture (type: Texture2D) -Function 083: UnloadShader() (1 input parameters) +Function 086: UnloadShader() (1 input parameters) Name: UnloadShader Return type: void Description: Unload shader from GPU memory (VRAM) Param[1]: shader (type: Shader) -Function 084: GetMouseRay() (2 input parameters) +Function 087: GetMouseRay() (2 input parameters) Name: GetMouseRay Return type: Ray Description: Get a ray trace from mouse position Param[1]: mousePosition (type: Vector2) Param[2]: camera (type: Camera) -Function 085: GetCameraMatrix() (1 input parameters) +Function 088: GetCameraMatrix() (1 input parameters) Name: GetCameraMatrix Return type: Matrix Description: Get camera transform matrix (view matrix) Param[1]: camera (type: Camera) -Function 086: GetCameraMatrix2D() (1 input parameters) +Function 089: GetCameraMatrix2D() (1 input parameters) Name: GetCameraMatrix2D Return type: Matrix Description: Get camera 2d transform matrix Param[1]: camera (type: Camera2D) -Function 087: GetWorldToScreen() (2 input parameters) +Function 090: GetWorldToScreen() (2 input parameters) Name: GetWorldToScreen Return type: Vector2 Description: Get the screen space position for a 3d world space position Param[1]: position (type: Vector3) Param[2]: camera (type: Camera) -Function 088: GetScreenToWorld2D() (2 input parameters) +Function 091: GetScreenToWorld2D() (2 input parameters) Name: GetScreenToWorld2D Return type: Vector2 Description: Get the world space position for a 2d camera screen space position Param[1]: position (type: Vector2) Param[2]: camera (type: Camera2D) -Function 089: GetWorldToScreenEx() (4 input parameters) +Function 092: GetWorldToScreenEx() (4 input parameters) Name: GetWorldToScreenEx Return type: Vector2 Description: Get size position for a 3d world space position @@ -1441,517 +1461,522 @@ Function 089: GetWorldToScreenEx() (4 input parameters) Param[2]: camera (type: Camera) Param[3]: width (type: int) Param[4]: height (type: int) -Function 090: GetWorldToScreen2D() (2 input parameters) +Function 093: GetWorldToScreen2D() (2 input parameters) Name: GetWorldToScreen2D Return type: Vector2 Description: Get the screen space position for a 2d camera world space position Param[1]: position (type: Vector2) Param[2]: camera (type: Camera2D) -Function 091: SetTargetFPS() (1 input parameters) +Function 094: SetTargetFPS() (1 input parameters) Name: SetTargetFPS Return type: void Description: Set target FPS (maximum) Param[1]: fps (type: int) -Function 092: GetFPS() (0 input parameters) +Function 095: GetFPS() (0 input parameters) Name: GetFPS Return type: int Description: Get current FPS No input parameters -Function 093: GetFrameTime() (0 input parameters) +Function 096: GetFrameTime() (0 input parameters) Name: GetFrameTime Return type: float Description: Get time in seconds for last frame drawn (delta time) No input parameters -Function 094: GetTime() (0 input parameters) +Function 097: GetTime() (0 input parameters) Name: GetTime Return type: double Description: Get elapsed time in seconds since InitWindow() No input parameters -Function 095: GetRandomValue() (2 input parameters) +Function 098: GetRandomValue() (2 input parameters) Name: GetRandomValue Return type: int Description: Get a random value between min and max (both included) Param[1]: min (type: int) Param[2]: max (type: int) -Function 096: SetRandomSeed() (1 input parameters) +Function 099: SetRandomSeed() (1 input parameters) Name: SetRandomSeed Return type: void Description: Set the seed for the random number generator Param[1]: seed (type: unsigned int) -Function 097: TakeScreenshot() (1 input parameters) +Function 100: TakeScreenshot() (1 input parameters) Name: TakeScreenshot Return type: void Description: Takes a screenshot of current screen (filename extension defines format) Param[1]: fileName (type: const char *) -Function 098: SetConfigFlags() (1 input parameters) +Function 101: SetConfigFlags() (1 input parameters) Name: SetConfigFlags Return type: void Description: Setup init configuration flags (view FLAGS) Param[1]: flags (type: unsigned int) -Function 099: TraceLog() (3 input parameters) +Function 102: TraceLog() (3 input parameters) Name: TraceLog Return type: void Description: Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) Param[1]: logLevel (type: int) Param[2]: text (type: const char *) Param[3]: args (type: ...) -Function 100: SetTraceLogLevel() (1 input parameters) +Function 103: SetTraceLogLevel() (1 input parameters) Name: SetTraceLogLevel Return type: void Description: Set the current threshold (minimum) log level Param[1]: logLevel (type: int) -Function 101: MemAlloc() (1 input parameters) +Function 104: MemAlloc() (1 input parameters) Name: MemAlloc Return type: void * Description: Internal memory allocator Param[1]: size (type: unsigned int) -Function 102: MemRealloc() (2 input parameters) +Function 105: MemRealloc() (2 input parameters) Name: MemRealloc Return type: void * Description: Internal memory reallocator Param[1]: ptr (type: void *) Param[2]: size (type: unsigned int) -Function 103: MemFree() (1 input parameters) +Function 106: MemFree() (1 input parameters) Name: MemFree Return type: void Description: Internal memory free Param[1]: ptr (type: void *) -Function 104: OpenURL() (1 input parameters) +Function 107: OpenURL() (1 input parameters) Name: OpenURL Return type: void Description: Open URL with default system browser (if available) Param[1]: url (type: const char *) -Function 105: SetTraceLogCallback() (1 input parameters) +Function 108: SetTraceLogCallback() (1 input parameters) Name: SetTraceLogCallback Return type: void Description: Set custom trace log Param[1]: callback (type: TraceLogCallback) -Function 106: SetLoadFileDataCallback() (1 input parameters) +Function 109: SetLoadFileDataCallback() (1 input parameters) Name: SetLoadFileDataCallback Return type: void Description: Set custom file binary data loader Param[1]: callback (type: LoadFileDataCallback) -Function 107: SetSaveFileDataCallback() (1 input parameters) +Function 110: SetSaveFileDataCallback() (1 input parameters) Name: SetSaveFileDataCallback Return type: void Description: Set custom file binary data saver Param[1]: callback (type: SaveFileDataCallback) -Function 108: SetLoadFileTextCallback() (1 input parameters) +Function 111: SetLoadFileTextCallback() (1 input parameters) Name: SetLoadFileTextCallback Return type: void Description: Set custom file text data loader Param[1]: callback (type: LoadFileTextCallback) -Function 109: SetSaveFileTextCallback() (1 input parameters) +Function 112: SetSaveFileTextCallback() (1 input parameters) Name: SetSaveFileTextCallback Return type: void Description: Set custom file text data saver Param[1]: callback (type: SaveFileTextCallback) -Function 110: LoadFileData() (2 input parameters) +Function 113: LoadFileData() (2 input parameters) Name: LoadFileData Return type: unsigned char * Description: Load file data as byte array (read) Param[1]: fileName (type: const char *) - Param[2]: bytesRead (type: unsigned int *) -Function 111: UnloadFileData() (1 input parameters) + Param[2]: dataSize (type: int *) +Function 114: UnloadFileData() (1 input parameters) Name: UnloadFileData Return type: void Description: Unload file data allocated by LoadFileData() Param[1]: data (type: unsigned char *) -Function 112: SaveFileData() (3 input parameters) +Function 115: SaveFileData() (3 input parameters) Name: SaveFileData Return type: bool Description: Save data to file from byte array (write), returns true on success Param[1]: fileName (type: const char *) Param[2]: data (type: void *) - Param[3]: bytesToWrite (type: unsigned int) -Function 113: ExportDataAsCode() (3 input parameters) + Param[3]: dataSize (type: int) +Function 116: ExportDataAsCode() (3 input parameters) Name: ExportDataAsCode Return type: bool Description: Export data to code (.h), returns true on success Param[1]: data (type: const unsigned char *) - Param[2]: size (type: unsigned int) + Param[2]: dataSize (type: int) Param[3]: fileName (type: const char *) -Function 114: LoadFileText() (1 input parameters) +Function 117: LoadFileText() (1 input parameters) Name: LoadFileText Return type: char * Description: Load text data from file (read), returns a '\0' terminated string Param[1]: fileName (type: const char *) -Function 115: UnloadFileText() (1 input parameters) +Function 118: UnloadFileText() (1 input parameters) Name: UnloadFileText Return type: void Description: Unload file text data allocated by LoadFileText() Param[1]: text (type: char *) -Function 116: SaveFileText() (2 input parameters) +Function 119: SaveFileText() (2 input parameters) Name: SaveFileText Return type: bool Description: Save text data to file (write), string must be '\0' terminated, returns true on success Param[1]: fileName (type: const char *) Param[2]: text (type: char *) -Function 117: FileExists() (1 input parameters) +Function 120: FileExists() (1 input parameters) Name: FileExists Return type: bool Description: Check if file exists Param[1]: fileName (type: const char *) -Function 118: DirectoryExists() (1 input parameters) +Function 121: DirectoryExists() (1 input parameters) Name: DirectoryExists Return type: bool Description: Check if a directory path exists Param[1]: dirPath (type: const char *) -Function 119: IsFileExtension() (2 input parameters) +Function 122: IsFileExtension() (2 input parameters) Name: IsFileExtension Return type: bool Description: Check file extension (including point: .png, .wav) Param[1]: fileName (type: const char *) Param[2]: ext (type: const char *) -Function 120: GetFileLength() (1 input parameters) +Function 123: GetFileLength() (1 input parameters) Name: GetFileLength Return type: int Description: Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) Param[1]: fileName (type: const char *) -Function 121: GetFileExtension() (1 input parameters) +Function 124: GetFileExtension() (1 input parameters) Name: GetFileExtension Return type: const char * Description: Get pointer to extension for a filename string (includes dot: '.png') Param[1]: fileName (type: const char *) -Function 122: GetFileName() (1 input parameters) +Function 125: GetFileName() (1 input parameters) Name: GetFileName Return type: const char * Description: Get pointer to filename for a path string Param[1]: filePath (type: const char *) -Function 123: GetFileNameWithoutExt() (1 input parameters) +Function 126: GetFileNameWithoutExt() (1 input parameters) Name: GetFileNameWithoutExt Return type: const char * Description: Get filename string without extension (uses static string) Param[1]: filePath (type: const char *) -Function 124: GetDirectoryPath() (1 input parameters) +Function 127: GetDirectoryPath() (1 input parameters) Name: GetDirectoryPath Return type: const char * Description: Get full path for a given fileName with path (uses static string) Param[1]: filePath (type: const char *) -Function 125: GetPrevDirectoryPath() (1 input parameters) +Function 128: GetPrevDirectoryPath() (1 input parameters) Name: GetPrevDirectoryPath Return type: const char * Description: Get previous directory path for a given path (uses static string) Param[1]: dirPath (type: const char *) -Function 126: GetWorkingDirectory() (0 input parameters) +Function 129: GetWorkingDirectory() (0 input parameters) Name: GetWorkingDirectory Return type: const char * Description: Get current working directory (uses static string) No input parameters -Function 127: GetApplicationDirectory() (0 input parameters) +Function 130: GetApplicationDirectory() (0 input parameters) Name: GetApplicationDirectory Return type: const char * - Description: Get the directory if the running application (uses static string) + Description: Get the directory of the running application (uses static string) No input parameters -Function 128: ChangeDirectory() (1 input parameters) +Function 131: ChangeDirectory() (1 input parameters) Name: ChangeDirectory Return type: bool Description: Change working directory, return true on success Param[1]: dir (type: const char *) -Function 129: IsPathFile() (1 input parameters) +Function 132: IsPathFile() (1 input parameters) Name: IsPathFile Return type: bool Description: Check if a given path is a file or a directory Param[1]: path (type: const char *) -Function 130: LoadDirectoryFiles() (1 input parameters) +Function 133: LoadDirectoryFiles() (1 input parameters) Name: LoadDirectoryFiles Return type: FilePathList Description: Load directory filepaths Param[1]: dirPath (type: const char *) -Function 131: LoadDirectoryFilesEx() (3 input parameters) +Function 134: LoadDirectoryFilesEx() (3 input parameters) Name: LoadDirectoryFilesEx Return type: FilePathList Description: Load directory filepaths with extension filtering and recursive directory scan Param[1]: basePath (type: const char *) Param[2]: filter (type: const char *) Param[3]: scanSubdirs (type: bool) -Function 132: UnloadDirectoryFiles() (1 input parameters) +Function 135: UnloadDirectoryFiles() (1 input parameters) Name: UnloadDirectoryFiles Return type: void Description: Unload filepaths Param[1]: files (type: FilePathList) -Function 133: IsFileDropped() (0 input parameters) +Function 136: IsFileDropped() (0 input parameters) Name: IsFileDropped Return type: bool Description: Check if a file has been dropped into window No input parameters -Function 134: LoadDroppedFiles() (0 input parameters) +Function 137: LoadDroppedFiles() (0 input parameters) Name: LoadDroppedFiles Return type: FilePathList Description: Load dropped filepaths No input parameters -Function 135: UnloadDroppedFiles() (1 input parameters) +Function 138: UnloadDroppedFiles() (1 input parameters) Name: UnloadDroppedFiles Return type: void Description: Unload dropped filepaths Param[1]: files (type: FilePathList) -Function 136: GetFileModTime() (1 input parameters) +Function 139: GetFileModTime() (1 input parameters) Name: GetFileModTime Return type: long Description: Get file modification time (last write time) Param[1]: fileName (type: const char *) -Function 137: CompressData() (3 input parameters) +Function 140: CompressData() (3 input parameters) Name: CompressData Return type: unsigned char * Description: Compress data (DEFLATE algorithm), memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: compDataSize (type: int *) -Function 138: DecompressData() (3 input parameters) +Function 141: DecompressData() (3 input parameters) Name: DecompressData Return type: unsigned char * Description: Decompress data (DEFLATE algorithm), memory must be MemFree() Param[1]: compData (type: const unsigned char *) Param[2]: compDataSize (type: int) Param[3]: dataSize (type: int *) -Function 139: EncodeDataBase64() (3 input parameters) +Function 142: EncodeDataBase64() (3 input parameters) Name: EncodeDataBase64 Return type: char * Description: Encode data to Base64 string, memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: outputSize (type: int *) -Function 140: DecodeDataBase64() (2 input parameters) +Function 143: DecodeDataBase64() (2 input parameters) Name: DecodeDataBase64 Return type: unsigned char * Description: Decode Base64 string data, memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: outputSize (type: int *) -Function 141: IsKeyPressed() (1 input parameters) +Function 144: IsKeyPressed() (1 input parameters) Name: IsKeyPressed Return type: bool Description: Check if a key has been pressed once Param[1]: key (type: int) -Function 142: IsKeyDown() (1 input parameters) +Function 145: IsKeyPressedRepeat() (1 input parameters) + Name: IsKeyPressedRepeat + Return type: bool + Description: Check if a key has been pressed again (Only PLATFORM_DESKTOP) + Param[1]: key (type: int) +Function 146: IsKeyDown() (1 input parameters) Name: IsKeyDown Return type: bool Description: Check if a key is being pressed Param[1]: key (type: int) -Function 143: IsKeyReleased() (1 input parameters) +Function 147: IsKeyReleased() (1 input parameters) Name: IsKeyReleased Return type: bool Description: Check if a key has been released once Param[1]: key (type: int) -Function 144: IsKeyUp() (1 input parameters) +Function 148: IsKeyUp() (1 input parameters) Name: IsKeyUp Return type: bool Description: Check if a key is NOT being pressed Param[1]: key (type: int) -Function 145: SetExitKey() (1 input parameters) +Function 149: SetExitKey() (1 input parameters) Name: SetExitKey Return type: void Description: Set a custom key to exit program (default is ESC) Param[1]: key (type: int) -Function 146: GetKeyPressed() (0 input parameters) +Function 150: GetKeyPressed() (0 input parameters) Name: GetKeyPressed Return type: int Description: Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty No input parameters -Function 147: GetCharPressed() (0 input parameters) +Function 151: GetCharPressed() (0 input parameters) Name: GetCharPressed Return type: int Description: Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty No input parameters -Function 148: IsGamepadAvailable() (1 input parameters) +Function 152: IsGamepadAvailable() (1 input parameters) Name: IsGamepadAvailable Return type: bool Description: Check if a gamepad is available Param[1]: gamepad (type: int) -Function 149: GetGamepadName() (1 input parameters) +Function 153: GetGamepadName() (1 input parameters) Name: GetGamepadName Return type: const char * Description: Get gamepad internal name id Param[1]: gamepad (type: int) -Function 150: IsGamepadButtonPressed() (2 input parameters) +Function 154: IsGamepadButtonPressed() (2 input parameters) Name: IsGamepadButtonPressed Return type: bool Description: Check if a gamepad button has been pressed once Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 151: IsGamepadButtonDown() (2 input parameters) +Function 155: IsGamepadButtonDown() (2 input parameters) Name: IsGamepadButtonDown Return type: bool Description: Check if a gamepad button is being pressed Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 152: IsGamepadButtonReleased() (2 input parameters) +Function 156: IsGamepadButtonReleased() (2 input parameters) Name: IsGamepadButtonReleased Return type: bool Description: Check if a gamepad button has been released once Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 153: IsGamepadButtonUp() (2 input parameters) +Function 157: IsGamepadButtonUp() (2 input parameters) Name: IsGamepadButtonUp Return type: bool Description: Check if a gamepad button is NOT being pressed Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 154: GetGamepadButtonPressed() (0 input parameters) +Function 158: GetGamepadButtonPressed() (0 input parameters) Name: GetGamepadButtonPressed Return type: int Description: Get the last gamepad button pressed No input parameters -Function 155: GetGamepadAxisCount() (1 input parameters) +Function 159: GetGamepadAxisCount() (1 input parameters) Name: GetGamepadAxisCount Return type: int Description: Get gamepad axis count for a gamepad Param[1]: gamepad (type: int) -Function 156: GetGamepadAxisMovement() (2 input parameters) +Function 160: GetGamepadAxisMovement() (2 input parameters) Name: GetGamepadAxisMovement Return type: float Description: Get axis movement value for a gamepad axis Param[1]: gamepad (type: int) Param[2]: axis (type: int) -Function 157: SetGamepadMappings() (1 input parameters) +Function 161: SetGamepadMappings() (1 input parameters) Name: SetGamepadMappings Return type: int Description: Set internal gamepad mappings (SDL_GameControllerDB) Param[1]: mappings (type: const char *) -Function 158: IsMouseButtonPressed() (1 input parameters) +Function 162: IsMouseButtonPressed() (1 input parameters) Name: IsMouseButtonPressed Return type: bool Description: Check if a mouse button has been pressed once Param[1]: button (type: int) -Function 159: IsMouseButtonDown() (1 input parameters) +Function 163: IsMouseButtonDown() (1 input parameters) Name: IsMouseButtonDown Return type: bool Description: Check if a mouse button is being pressed Param[1]: button (type: int) -Function 160: IsMouseButtonReleased() (1 input parameters) +Function 164: IsMouseButtonReleased() (1 input parameters) Name: IsMouseButtonReleased Return type: bool Description: Check if a mouse button has been released once Param[1]: button (type: int) -Function 161: IsMouseButtonUp() (1 input parameters) +Function 165: IsMouseButtonUp() (1 input parameters) Name: IsMouseButtonUp Return type: bool Description: Check if a mouse button is NOT being pressed Param[1]: button (type: int) -Function 162: GetMouseX() (0 input parameters) +Function 166: GetMouseX() (0 input parameters) Name: GetMouseX Return type: int Description: Get mouse position X No input parameters -Function 163: GetMouseY() (0 input parameters) +Function 167: GetMouseY() (0 input parameters) Name: GetMouseY Return type: int Description: Get mouse position Y No input parameters -Function 164: GetMousePosition() (0 input parameters) +Function 168: GetMousePosition() (0 input parameters) Name: GetMousePosition Return type: Vector2 Description: Get mouse position XY No input parameters -Function 165: GetMouseDelta() (0 input parameters) +Function 169: GetMouseDelta() (0 input parameters) Name: GetMouseDelta Return type: Vector2 Description: Get mouse delta between frames No input parameters -Function 166: SetMousePosition() (2 input parameters) +Function 170: SetMousePosition() (2 input parameters) Name: SetMousePosition Return type: void Description: Set mouse position XY Param[1]: x (type: int) Param[2]: y (type: int) -Function 167: SetMouseOffset() (2 input parameters) +Function 171: SetMouseOffset() (2 input parameters) Name: SetMouseOffset Return type: void Description: Set mouse offset Param[1]: offsetX (type: int) Param[2]: offsetY (type: int) -Function 168: SetMouseScale() (2 input parameters) +Function 172: SetMouseScale() (2 input parameters) Name: SetMouseScale Return type: void Description: Set mouse scaling Param[1]: scaleX (type: float) Param[2]: scaleY (type: float) -Function 169: GetMouseWheelMove() (0 input parameters) +Function 173: GetMouseWheelMove() (0 input parameters) Name: GetMouseWheelMove Return type: float Description: Get mouse wheel movement for X or Y, whichever is larger No input parameters -Function 170: GetMouseWheelMoveV() (0 input parameters) +Function 174: GetMouseWheelMoveV() (0 input parameters) Name: GetMouseWheelMoveV Return type: Vector2 Description: Get mouse wheel movement for both X and Y No input parameters -Function 171: SetMouseCursor() (1 input parameters) +Function 175: SetMouseCursor() (1 input parameters) Name: SetMouseCursor Return type: void Description: Set mouse cursor Param[1]: cursor (type: int) -Function 172: GetTouchX() (0 input parameters) +Function 176: GetTouchX() (0 input parameters) Name: GetTouchX Return type: int Description: Get touch position X for touch point 0 (relative to screen size) No input parameters -Function 173: GetTouchY() (0 input parameters) +Function 177: GetTouchY() (0 input parameters) Name: GetTouchY Return type: int Description: Get touch position Y for touch point 0 (relative to screen size) No input parameters -Function 174: GetTouchPosition() (1 input parameters) +Function 178: GetTouchPosition() (1 input parameters) Name: GetTouchPosition Return type: Vector2 Description: Get touch position XY for a touch point index (relative to screen size) Param[1]: index (type: int) -Function 175: GetTouchPointId() (1 input parameters) +Function 179: GetTouchPointId() (1 input parameters) Name: GetTouchPointId Return type: int Description: Get touch point identifier for given index Param[1]: index (type: int) -Function 176: GetTouchPointCount() (0 input parameters) +Function 180: GetTouchPointCount() (0 input parameters) Name: GetTouchPointCount Return type: int Description: Get number of touch points No input parameters -Function 177: SetGesturesEnabled() (1 input parameters) +Function 181: SetGesturesEnabled() (1 input parameters) Name: SetGesturesEnabled Return type: void Description: Enable a set of gestures using flags Param[1]: flags (type: unsigned int) -Function 178: IsGestureDetected() (1 input parameters) +Function 182: IsGestureDetected() (1 input parameters) Name: IsGestureDetected Return type: bool Description: Check if a gesture have been detected - Param[1]: gesture (type: int) -Function 179: GetGestureDetected() (0 input parameters) + Param[1]: gesture (type: unsigned int) +Function 183: GetGestureDetected() (0 input parameters) Name: GetGestureDetected Return type: int Description: Get latest detected gesture No input parameters -Function 180: GetGestureHoldDuration() (0 input parameters) +Function 184: GetGestureHoldDuration() (0 input parameters) Name: GetGestureHoldDuration Return type: float Description: Get gesture hold time in milliseconds No input parameters -Function 181: GetGestureDragVector() (0 input parameters) +Function 185: GetGestureDragVector() (0 input parameters) Name: GetGestureDragVector Return type: Vector2 Description: Get gesture drag vector No input parameters -Function 182: GetGestureDragAngle() (0 input parameters) +Function 186: GetGestureDragAngle() (0 input parameters) Name: GetGestureDragAngle Return type: float Description: Get gesture drag angle No input parameters -Function 183: GetGesturePinchVector() (0 input parameters) +Function 187: GetGesturePinchVector() (0 input parameters) Name: GetGesturePinchVector Return type: Vector2 Description: Get gesture pinch delta No input parameters -Function 184: GetGesturePinchAngle() (0 input parameters) +Function 188: GetGesturePinchAngle() (0 input parameters) Name: GetGesturePinchAngle Return type: float Description: Get gesture pinch angle No input parameters -Function 185: UpdateCamera() (2 input parameters) +Function 189: UpdateCamera() (2 input parameters) Name: UpdateCamera Return type: void Description: Update camera position for selected mode Param[1]: camera (type: Camera *) Param[2]: mode (type: int) -Function 186: UpdateCameraPro() (4 input parameters) +Function 190: UpdateCameraPro() (4 input parameters) Name: UpdateCameraPro Return type: void Description: Update camera movement/rotation @@ -1959,26 +1984,26 @@ Function 186: UpdateCameraPro() (4 input parameters) Param[2]: movement (type: Vector3) Param[3]: rotation (type: Vector3) Param[4]: zoom (type: float) -Function 187: SetShapesTexture() (2 input parameters) +Function 191: SetShapesTexture() (2 input parameters) Name: SetShapesTexture Return type: void Description: Set texture and rectangle to be used on shapes drawing Param[1]: texture (type: Texture2D) Param[2]: source (type: Rectangle) -Function 188: DrawPixel() (3 input parameters) +Function 192: DrawPixel() (3 input parameters) Name: DrawPixel Return type: void Description: Draw a pixel Param[1]: posX (type: int) Param[2]: posY (type: int) Param[3]: color (type: Color) -Function 189: DrawPixelV() (2 input parameters) +Function 193: DrawPixelV() (2 input parameters) Name: DrawPixelV Return type: void Description: Draw a pixel (Vector version) Param[1]: position (type: Vector2) Param[2]: color (type: Color) -Function 190: DrawLine() (5 input parameters) +Function 194: DrawLine() (5 input parameters) Name: DrawLine Return type: void Description: Draw a line @@ -1987,14 +2012,14 @@ Function 190: DrawLine() (5 input parameters) Param[3]: endPosX (type: int) Param[4]: endPosY (type: int) Param[5]: color (type: Color) -Function 191: DrawLineV() (3 input parameters) +Function 195: DrawLineV() (3 input parameters) Name: DrawLineV Return type: void Description: Draw a line (Vector version) Param[1]: startPos (type: Vector2) Param[2]: endPos (type: Vector2) Param[3]: color (type: Color) -Function 192: DrawLineEx() (4 input parameters) +Function 196: DrawLineEx() (4 input parameters) Name: DrawLineEx Return type: void Description: Draw a line defining thickness @@ -2002,7 +2027,7 @@ Function 192: DrawLineEx() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 193: DrawLineBezier() (4 input parameters) +Function 197: DrawLineBezier() (4 input parameters) Name: DrawLineBezier Return type: void Description: Draw a line using cubic-bezier curves in-out @@ -2010,7 +2035,7 @@ Function 193: DrawLineBezier() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 194: DrawLineBezierQuad() (5 input parameters) +Function 198: DrawLineBezierQuad() (5 input parameters) Name: DrawLineBezierQuad Return type: void Description: Draw line using quadratic bezier curves with a control point @@ -2019,7 +2044,7 @@ Function 194: DrawLineBezierQuad() (5 input parameters) Param[3]: controlPos (type: Vector2) Param[4]: thick (type: float) Param[5]: color (type: Color) -Function 195: DrawLineBezierCubic() (6 input parameters) +Function 199: DrawLineBezierCubic() (6 input parameters) Name: DrawLineBezierCubic Return type: void Description: Draw line using cubic bezier curves with 2 control points @@ -2029,14 +2054,30 @@ Function 195: DrawLineBezierCubic() (6 input parameters) Param[4]: endControlPos (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 196: DrawLineStrip() (3 input parameters) +Function 200: DrawLineBSpline() (4 input parameters) + Name: DrawLineBSpline + Return type: void + Description: Draw a B-Spline line, minimum 4 points + Param[1]: points (type: Vector2 *) + Param[2]: pointCount (type: int) + Param[3]: thick (type: float) + Param[4]: color (type: Color) +Function 201: DrawLineCatmullRom() (4 input parameters) + Name: DrawLineCatmullRom + Return type: void + Description: Draw a Catmull Rom spline line, minimum 4 points + Param[1]: points (type: Vector2 *) + Param[2]: pointCount (type: int) + Param[3]: thick (type: float) + Param[4]: color (type: Color) +Function 202: DrawLineStrip() (3 input parameters) Name: DrawLineStrip Return type: void Description: Draw lines sequence Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 197: DrawCircle() (4 input parameters) +Function 203: DrawCircle() (4 input parameters) Name: DrawCircle Return type: void Description: Draw a color-filled circle @@ -2044,7 +2085,7 @@ Function 197: DrawCircle() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 198: DrawCircleSector() (6 input parameters) +Function 204: DrawCircleSector() (6 input parameters) Name: DrawCircleSector Return type: void Description: Draw a piece of a circle @@ -2054,7 +2095,7 @@ Function 198: DrawCircleSector() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 199: DrawCircleSectorLines() (6 input parameters) +Function 205: DrawCircleSectorLines() (6 input parameters) Name: DrawCircleSectorLines Return type: void Description: Draw circle sector outline @@ -2064,7 +2105,7 @@ Function 199: DrawCircleSectorLines() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 200: DrawCircleGradient() (5 input parameters) +Function 206: DrawCircleGradient() (5 input parameters) Name: DrawCircleGradient Return type: void Description: Draw a gradient-filled circle @@ -2073,14 +2114,14 @@ Function 200: DrawCircleGradient() (5 input parameters) Param[3]: radius (type: float) Param[4]: color1 (type: Color) Param[5]: color2 (type: Color) -Function 201: DrawCircleV() (3 input parameters) +Function 207: DrawCircleV() (3 input parameters) Name: DrawCircleV Return type: void Description: Draw a color-filled circle (Vector version) Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 202: DrawCircleLines() (4 input parameters) +Function 208: DrawCircleLines() (4 input parameters) Name: DrawCircleLines Return type: void Description: Draw circle outline @@ -2088,7 +2129,7 @@ Function 202: DrawCircleLines() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 203: DrawEllipse() (5 input parameters) +Function 209: DrawEllipse() (5 input parameters) Name: DrawEllipse Return type: void Description: Draw ellipse @@ -2097,7 +2138,7 @@ Function 203: DrawEllipse() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 204: DrawEllipseLines() (5 input parameters) +Function 210: DrawEllipseLines() (5 input parameters) Name: DrawEllipseLines Return type: void Description: Draw ellipse outline @@ -2106,7 +2147,7 @@ Function 204: DrawEllipseLines() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 205: DrawRing() (7 input parameters) +Function 211: DrawRing() (7 input parameters) Name: DrawRing Return type: void Description: Draw ring @@ -2117,7 +2158,7 @@ Function 205: DrawRing() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 206: DrawRingLines() (7 input parameters) +Function 212: DrawRingLines() (7 input parameters) Name: DrawRingLines Return type: void Description: Draw ring outline @@ -2128,7 +2169,7 @@ Function 206: DrawRingLines() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 207: DrawRectangle() (5 input parameters) +Function 213: DrawRectangle() (5 input parameters) Name: DrawRectangle Return type: void Description: Draw a color-filled rectangle @@ -2137,20 +2178,20 @@ Function 207: DrawRectangle() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 208: DrawRectangleV() (3 input parameters) +Function 214: DrawRectangleV() (3 input parameters) Name: DrawRectangleV Return type: void Description: Draw a color-filled rectangle (Vector version) Param[1]: position (type: Vector2) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 209: DrawRectangleRec() (2 input parameters) +Function 215: DrawRectangleRec() (2 input parameters) Name: DrawRectangleRec Return type: void Description: Draw a color-filled rectangle Param[1]: rec (type: Rectangle) Param[2]: color (type: Color) -Function 210: DrawRectanglePro() (4 input parameters) +Function 216: DrawRectanglePro() (4 input parameters) Name: DrawRectanglePro Return type: void Description: Draw a color-filled rectangle with pro parameters @@ -2158,7 +2199,7 @@ Function 210: DrawRectanglePro() (4 input parameters) Param[2]: origin (type: Vector2) Param[3]: rotation (type: float) Param[4]: color (type: Color) -Function 211: DrawRectangleGradientV() (6 input parameters) +Function 217: DrawRectangleGradientV() (6 input parameters) Name: DrawRectangleGradientV Return type: void Description: Draw a vertical-gradient-filled rectangle @@ -2168,7 +2209,7 @@ Function 211: DrawRectangleGradientV() (6 input parameters) Param[4]: height (type: int) Param[5]: color1 (type: Color) Param[6]: color2 (type: Color) -Function 212: DrawRectangleGradientH() (6 input parameters) +Function 218: DrawRectangleGradientH() (6 input parameters) Name: DrawRectangleGradientH Return type: void Description: Draw a horizontal-gradient-filled rectangle @@ -2178,7 +2219,7 @@ Function 212: DrawRectangleGradientH() (6 input parameters) Param[4]: height (type: int) Param[5]: color1 (type: Color) Param[6]: color2 (type: Color) -Function 213: DrawRectangleGradientEx() (5 input parameters) +Function 219: DrawRectangleGradientEx() (5 input parameters) Name: DrawRectangleGradientEx Return type: void Description: Draw a gradient-filled rectangle with custom vertex colors @@ -2187,7 +2228,7 @@ Function 213: DrawRectangleGradientEx() (5 input parameters) Param[3]: col2 (type: Color) Param[4]: col3 (type: Color) Param[5]: col4 (type: Color) -Function 214: DrawRectangleLines() (5 input parameters) +Function 220: DrawRectangleLines() (5 input parameters) Name: DrawRectangleLines Return type: void Description: Draw rectangle outline @@ -2196,14 +2237,14 @@ Function 214: DrawRectangleLines() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 215: DrawRectangleLinesEx() (3 input parameters) +Function 221: DrawRectangleLinesEx() (3 input parameters) Name: DrawRectangleLinesEx Return type: void Description: Draw rectangle outline with extended parameters Param[1]: rec (type: Rectangle) Param[2]: lineThick (type: float) Param[3]: color (type: Color) -Function 216: DrawRectangleRounded() (4 input parameters) +Function 222: DrawRectangleRounded() (4 input parameters) Name: DrawRectangleRounded Return type: void Description: Draw rectangle with rounded edges @@ -2211,7 +2252,7 @@ Function 216: DrawRectangleRounded() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 217: DrawRectangleRoundedLines() (5 input parameters) +Function 223: DrawRectangleRoundedLines() (5 input parameters) Name: DrawRectangleRoundedLines Return type: void Description: Draw rectangle with rounded edges outline @@ -2220,7 +2261,7 @@ Function 217: DrawRectangleRoundedLines() (5 input parameters) Param[3]: segments (type: int) Param[4]: lineThick (type: float) Param[5]: color (type: Color) -Function 218: DrawTriangle() (4 input parameters) +Function 224: DrawTriangle() (4 input parameters) Name: DrawTriangle Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -2228,7 +2269,7 @@ Function 218: DrawTriangle() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 219: DrawTriangleLines() (4 input parameters) +Function 225: DrawTriangleLines() (4 input parameters) Name: DrawTriangleLines Return type: void Description: Draw triangle outline (vertex in counter-clockwise order!) @@ -2236,21 +2277,21 @@ Function 219: DrawTriangleLines() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 220: DrawTriangleFan() (3 input parameters) +Function 226: DrawTriangleFan() (3 input parameters) Name: DrawTriangleFan Return type: void Description: Draw a triangle fan defined by points (first vertex is the center) Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 221: DrawTriangleStrip() (3 input parameters) +Function 227: DrawTriangleStrip() (3 input parameters) Name: DrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 222: DrawPoly() (5 input parameters) +Function 228: DrawPoly() (5 input parameters) Name: DrawPoly Return type: void Description: Draw a regular polygon (Vector version) @@ -2259,7 +2300,7 @@ Function 222: DrawPoly() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 223: DrawPolyLines() (5 input parameters) +Function 229: DrawPolyLines() (5 input parameters) Name: DrawPolyLines Return type: void Description: Draw a polygon outline of n sides @@ -2268,7 +2309,7 @@ Function 223: DrawPolyLines() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 224: DrawPolyLinesEx() (6 input parameters) +Function 230: DrawPolyLinesEx() (6 input parameters) Name: DrawPolyLinesEx Return type: void Description: Draw a polygon outline of n sides with extended parameters @@ -2278,13 +2319,13 @@ Function 224: DrawPolyLinesEx() (6 input parameters) Param[4]: rotation (type: float) Param[5]: lineThick (type: float) Param[6]: color (type: Color) -Function 225: CheckCollisionRecs() (2 input parameters) +Function 231: CheckCollisionRecs() (2 input parameters) Name: CheckCollisionRecs Return type: bool Description: Check collision between two rectangles Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 226: CheckCollisionCircles() (4 input parameters) +Function 232: CheckCollisionCircles() (4 input parameters) Name: CheckCollisionCircles Return type: bool Description: Check collision between two circles @@ -2292,27 +2333,27 @@ Function 226: CheckCollisionCircles() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector2) Param[4]: radius2 (type: float) -Function 227: CheckCollisionCircleRec() (3 input parameters) +Function 233: CheckCollisionCircleRec() (3 input parameters) Name: CheckCollisionCircleRec Return type: bool Description: Check collision between circle and rectangle Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: rec (type: Rectangle) -Function 228: CheckCollisionPointRec() (2 input parameters) +Function 234: CheckCollisionPointRec() (2 input parameters) Name: CheckCollisionPointRec Return type: bool Description: Check if point is inside rectangle Param[1]: point (type: Vector2) Param[2]: rec (type: Rectangle) -Function 229: CheckCollisionPointCircle() (3 input parameters) +Function 235: CheckCollisionPointCircle() (3 input parameters) Name: CheckCollisionPointCircle Return type: bool Description: Check if point is inside circle Param[1]: point (type: Vector2) Param[2]: center (type: Vector2) Param[3]: radius (type: float) -Function 230: CheckCollisionPointTriangle() (4 input parameters) +Function 236: CheckCollisionPointTriangle() (4 input parameters) Name: CheckCollisionPointTriangle Return type: bool Description: Check if point is inside a triangle @@ -2320,14 +2361,14 @@ Function 230: CheckCollisionPointTriangle() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: p3 (type: Vector2) -Function 231: CheckCollisionPointPoly() (3 input parameters) +Function 237: CheckCollisionPointPoly() (3 input parameters) Name: CheckCollisionPointPoly Return type: bool Description: Check if point is within a polygon described by array of vertices Param[1]: point (type: Vector2) Param[2]: points (type: Vector2 *) Param[3]: pointCount (type: int) -Function 232: CheckCollisionLines() (5 input parameters) +Function 238: CheckCollisionLines() (5 input parameters) Name: CheckCollisionLines Return type: bool Description: Check the collision between two lines defined by two points each, returns collision point by reference @@ -2336,7 +2377,7 @@ Function 232: CheckCollisionLines() (5 input parameters) Param[3]: startPos2 (type: Vector2) Param[4]: endPos2 (type: Vector2) Param[5]: collisionPoint (type: Vector2 *) -Function 233: CheckCollisionPointLine() (4 input parameters) +Function 239: CheckCollisionPointLine() (4 input parameters) Name: CheckCollisionPointLine Return type: bool Description: Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] @@ -2344,18 +2385,18 @@ Function 233: CheckCollisionPointLine() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: threshold (type: int) -Function 234: GetCollisionRec() (2 input parameters) +Function 240: GetCollisionRec() (2 input parameters) Name: GetCollisionRec Return type: Rectangle Description: Get collision rectangle for two rectangles collision Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 235: LoadImage() (1 input parameters) +Function 241: LoadImage() (1 input parameters) Name: LoadImage Return type: Image Description: Load image from file into CPU memory (RAM) Param[1]: fileName (type: const char *) -Function 236: LoadImageRaw() (5 input parameters) +Function 242: LoadImageRaw() (5 input parameters) Name: LoadImageRaw Return type: Image Description: Load image from RAW file data @@ -2364,59 +2405,73 @@ Function 236: LoadImageRaw() (5 input parameters) Param[3]: height (type: int) Param[4]: format (type: int) Param[5]: headerSize (type: int) -Function 237: LoadImageAnim() (2 input parameters) +Function 243: LoadImageSvg() (3 input parameters) + Name: LoadImageSvg + Return type: Image + Description: Load image from SVG file data or string with specified size + Param[1]: fileNameOrString (type: const char *) + Param[2]: width (type: int) + Param[3]: height (type: int) +Function 244: LoadImageAnim() (2 input parameters) Name: LoadImageAnim Return type: Image Description: Load image sequence from file (frames appended to image.data) Param[1]: fileName (type: const char *) Param[2]: frames (type: int *) -Function 238: LoadImageFromMemory() (3 input parameters) +Function 245: LoadImageFromMemory() (3 input parameters) Name: LoadImageFromMemory Return type: Image Description: Load image from memory buffer, fileType refers to extension: i.e. '.png' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 239: LoadImageFromTexture() (1 input parameters) +Function 246: LoadImageFromTexture() (1 input parameters) Name: LoadImageFromTexture Return type: Image Description: Load image from GPU texture data Param[1]: texture (type: Texture2D) -Function 240: LoadImageFromScreen() (0 input parameters) +Function 247: LoadImageFromScreen() (0 input parameters) Name: LoadImageFromScreen Return type: Image Description: Load image from screen buffer and (screenshot) No input parameters -Function 241: IsImageReady() (1 input parameters) +Function 248: IsImageReady() (1 input parameters) Name: IsImageReady Return type: bool Description: Check if an image is ready Param[1]: image (type: Image) -Function 242: UnloadImage() (1 input parameters) +Function 249: UnloadImage() (1 input parameters) Name: UnloadImage Return type: void Description: Unload image from CPU memory (RAM) Param[1]: image (type: Image) -Function 243: ExportImage() (2 input parameters) +Function 250: ExportImage() (2 input parameters) Name: ExportImage Return type: bool Description: Export image data to file, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 244: ExportImageAsCode() (2 input parameters) +Function 251: ExportImageToMemory() (3 input parameters) + Name: ExportImageToMemory + Return type: unsigned char * + Description: Export image to memory buffer + Param[1]: image (type: Image) + Param[2]: fileType (type: const char *) + Param[3]: fileSize (type: int *) +Function 252: ExportImageAsCode() (2 input parameters) Name: ExportImageAsCode Return type: bool Description: Export image as code file defining an array of bytes, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 245: GenImageColor() (3 input parameters) +Function 253: GenImageColor() (3 input parameters) Name: GenImageColor Return type: Image Description: Generate image: plain color Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 246: GenImageGradientLinear() (5 input parameters) +Function 254: GenImageGradientLinear() (5 input parameters) Name: GenImageGradientLinear Return type: Image Description: Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient @@ -2425,7 +2480,7 @@ Function 246: GenImageGradientLinear() (5 input parameters) Param[3]: direction (type: int) Param[4]: start (type: Color) Param[5]: end (type: Color) -Function 247: GenImageGradientRadial() (5 input parameters) +Function 255: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2434,7 +2489,7 @@ Function 247: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 248: GenImageGradientSquare() (5 input parameters) +Function 256: GenImageGradientSquare() (5 input parameters) Name: GenImageGradientSquare Return type: Image Description: Generate image: square gradient @@ -2443,7 +2498,7 @@ Function 248: GenImageGradientSquare() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 249: GenImageChecked() (6 input parameters) +Function 257: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2453,14 +2508,14 @@ Function 249: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 250: GenImageWhiteNoise() (3 input parameters) +Function 258: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 251: GenImagePerlinNoise() (5 input parameters) +Function 259: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2469,39 +2524,39 @@ Function 251: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 252: GenImageCellular() (3 input parameters) +Function 260: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 253: GenImageText() (3 input parameters) +Function 261: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 254: ImageCopy() (1 input parameters) +Function 262: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 255: ImageFromImage() (2 input parameters) +Function 263: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 256: ImageText() (3 input parameters) +Function 264: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 257: ImageTextEx() (5 input parameters) +Function 265: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2510,69 +2565,69 @@ Function 257: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 258: ImageFormat() (2 input parameters) +Function 266: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 259: ImageToPOT() (2 input parameters) +Function 267: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 260: ImageCrop() (2 input parameters) +Function 268: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 261: ImageAlphaCrop() (2 input parameters) +Function 269: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 262: ImageAlphaClear() (3 input parameters) +Function 270: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 263: ImageAlphaMask() (2 input parameters) +Function 271: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 264: ImageAlphaPremultiply() (1 input parameters) +Function 272: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 265: ImageBlurGaussian() (2 input parameters) +Function 273: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 266: ImageResize() (3 input parameters) +Function 274: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 267: ImageResizeNN() (3 input parameters) +Function 275: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 268: ImageResizeCanvas() (6 input parameters) +Function 276: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2582,12 +2637,12 @@ Function 268: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 269: ImageMipmaps() (1 input parameters) +Function 277: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 270: ImageDither() (5 input parameters) +Function 278: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2596,109 +2651,109 @@ Function 270: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 271: ImageFlipVertical() (1 input parameters) +Function 279: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 272: ImageFlipHorizontal() (1 input parameters) +Function 280: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 273: ImageRotate() (2 input parameters) +Function 281: ImageRotate() (2 input parameters) Name: ImageRotate Return type: void - Description: Rotate image by input angle in degrees (-359 to 359) + Description: Rotate image by input angle in degrees (-359 to 359) Param[1]: image (type: Image *) Param[2]: degrees (type: int) -Function 274: ImageRotateCW() (1 input parameters) +Function 282: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 275: ImageRotateCCW() (1 input parameters) +Function 283: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 276: ImageColorTint() (2 input parameters) +Function 284: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 277: ImageColorInvert() (1 input parameters) +Function 285: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 278: ImageColorGrayscale() (1 input parameters) +Function 286: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 279: ImageColorContrast() (2 input parameters) +Function 287: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 280: ImageColorBrightness() (2 input parameters) +Function 288: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 281: ImageColorReplace() (3 input parameters) +Function 289: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 282: LoadImageColors() (1 input parameters) +Function 290: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 283: LoadImagePalette() (3 input parameters) +Function 291: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 284: UnloadImageColors() (1 input parameters) +Function 292: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 285: UnloadImagePalette() (1 input parameters) +Function 293: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 286: GetImageAlphaBorder() (2 input parameters) +Function 294: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 287: GetImageColor() (3 input parameters) +Function 295: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 288: ImageClearBackground() (2 input parameters) +Function 296: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 289: ImageDrawPixel() (4 input parameters) +Function 297: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2706,14 +2761,14 @@ Function 289: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 290: ImageDrawPixelV() (3 input parameters) +Function 298: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 291: ImageDrawLine() (6 input parameters) +Function 299: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2723,7 +2778,7 @@ Function 291: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 292: ImageDrawLineV() (4 input parameters) +Function 300: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2731,7 +2786,7 @@ Function 292: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 293: ImageDrawCircle() (5 input parameters) +Function 301: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2740,7 +2795,7 @@ Function 293: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 294: ImageDrawCircleV() (4 input parameters) +Function 302: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2748,7 +2803,7 @@ Function 294: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 295: ImageDrawCircleLines() (5 input parameters) +Function 303: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2757,7 +2812,7 @@ Function 295: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 296: ImageDrawCircleLinesV() (4 input parameters) +Function 304: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2765,7 +2820,7 @@ Function 296: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 297: ImageDrawRectangle() (6 input parameters) +Function 305: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2775,7 +2830,7 @@ Function 297: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 298: ImageDrawRectangleV() (4 input parameters) +Function 306: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2783,14 +2838,14 @@ Function 298: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 299: ImageDrawRectangleRec() (3 input parameters) +Function 307: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 300: ImageDrawRectangleLines() (4 input parameters) +Function 308: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2798,7 +2853,7 @@ Function 300: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 301: ImageDraw() (5 input parameters) +Function 309: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2807,7 +2862,7 @@ Function 301: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 302: ImageDrawText() (6 input parameters) +Function 310: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2817,7 +2872,7 @@ Function 302: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 303: ImageDrawTextEx() (7 input parameters) +Function 311: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2828,79 +2883,79 @@ Function 303: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 304: LoadTexture() (1 input parameters) +Function 312: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 305: LoadTextureFromImage() (1 input parameters) +Function 313: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 306: LoadTextureCubemap() (2 input parameters) +Function 314: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 307: LoadRenderTexture() (2 input parameters) +Function 315: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 308: IsTextureReady() (1 input parameters) +Function 316: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 309: UnloadTexture() (1 input parameters) +Function 317: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 310: IsRenderTextureReady() (1 input parameters) +Function 318: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 311: UnloadRenderTexture() (1 input parameters) +Function 319: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 312: UpdateTexture() (2 input parameters) +Function 320: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 313: UpdateTextureRec() (3 input parameters) +Function 321: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 314: GenTextureMipmaps() (1 input parameters) +Function 322: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 315: SetTextureFilter() (2 input parameters) +Function 323: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 316: SetTextureWrap() (2 input parameters) +Function 324: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 317: DrawTexture() (4 input parameters) +Function 325: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2908,14 +2963,14 @@ Function 317: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 318: DrawTextureV() (3 input parameters) +Function 326: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 319: DrawTextureEx() (5 input parameters) +Function 327: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2924,7 +2979,7 @@ Function 319: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 320: DrawTextureRec() (4 input parameters) +Function 328: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2932,7 +2987,7 @@ Function 320: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 321: DrawTexturePro() (6 input parameters) +Function 329: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2942,7 +2997,7 @@ Function 321: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 322: DrawTextureNPatch() (6 input parameters) +Function 330: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2952,121 +3007,121 @@ Function 322: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 323: Fade() (2 input parameters) +Function 331: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 324: ColorToInt() (1 input parameters) +Function 332: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 325: ColorNormalize() (1 input parameters) +Function 333: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 326: ColorFromNormalized() (1 input parameters) +Function 334: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 327: ColorToHSV() (1 input parameters) +Function 335: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 328: ColorFromHSV() (3 input parameters) +Function 336: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 329: ColorTint() (2 input parameters) +Function 337: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 330: ColorBrightness() (2 input parameters) +Function 338: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 331: ColorContrast() (2 input parameters) +Function 339: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 332: ColorAlpha() (2 input parameters) +Function 340: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 333: ColorAlphaBlend() (3 input parameters) +Function 341: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 334: GetColor() (1 input parameters) +Function 342: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 335: GetPixelColor() (2 input parameters) +Function 343: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 336: SetPixelColor() (3 input parameters) +Function 344: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 337: GetPixelDataSize() (3 input parameters) +Function 345: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 338: GetFontDefault() (0 input parameters) +Function 346: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 339: LoadFont() (1 input parameters) +Function 347: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 340: LoadFontEx() (4 input parameters) +Function 348: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font - Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set + Description: Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character setFont Param[1]: fileName (type: const char *) Param[2]: fontSize (type: int) - Param[3]: fontChars (type: int *) - Param[4]: glyphCount (type: int) -Function 341: LoadFontFromImage() (3 input parameters) + Param[3]: codepoints (type: int *) + Param[4]: codepointCount (type: int) +Function 349: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 342: LoadFontFromMemory() (6 input parameters) +Function 350: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3074,57 +3129,57 @@ Function 342: LoadFontFromMemory() (6 input parameters) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) Param[4]: fontSize (type: int) - Param[5]: fontChars (type: int *) - Param[6]: glyphCount (type: int) -Function 343: IsFontReady() (1 input parameters) + Param[5]: codepoints (type: int *) + Param[6]: codepointCount (type: int) +Function 351: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 344: LoadFontData() (6 input parameters) +Function 352: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use Param[1]: fileData (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: fontSize (type: int) - Param[4]: fontChars (type: int *) - Param[5]: glyphCount (type: int) + Param[4]: codepoints (type: int *) + Param[5]: codepointCount (type: int) Param[6]: type (type: int) -Function 345: GenImageFontAtlas() (6 input parameters) +Function 353: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info - Param[1]: chars (type: const GlyphInfo *) - Param[2]: recs (type: Rectangle **) + Param[1]: glyphs (type: const GlyphInfo *) + Param[2]: glyphRecs (type: Rectangle **) Param[3]: glyphCount (type: int) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 346: UnloadFontData() (2 input parameters) +Function 354: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) - Param[1]: chars (type: GlyphInfo *) + Param[1]: glyphs (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 347: UnloadFont() (1 input parameters) +Function 355: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 348: ExportFontAsCode() (2 input parameters) +Function 356: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 349: DrawFPS() (2 input parameters) +Function 357: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 350: DrawText() (5 input parameters) +Function 358: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3133,7 +3188,7 @@ Function 350: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 351: DrawTextEx() (6 input parameters) +Function 359: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3143,7 +3198,7 @@ Function 351: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 352: DrawTextPro() (8 input parameters) +Function 360: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3155,7 +3210,7 @@ Function 352: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 353: DrawTextCodepoint() (5 input parameters) +Function 361: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3164,24 +3219,29 @@ Function 353: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 354: DrawTextCodepoints() (7 input parameters) +Function 362: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) Param[1]: font (type: Font) Param[2]: codepoints (type: const int *) - Param[3]: count (type: int) + Param[3]: codepointCount (type: int) Param[4]: position (type: Vector2) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 355: MeasureText() (2 input parameters) +Function 363: SetTextLineSpacing() (1 input parameters) + Name: SetTextLineSpacing + Return type: void + Description: Set vertical line spacing when drawing with line-breaks + Param[1]: spacing (type: int) +Function 364: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 356: MeasureTextEx() (4 input parameters) +Function 365: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3189,180 +3249,180 @@ Function 356: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 357: GetGlyphIndex() (2 input parameters) +Function 366: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 358: GetGlyphInfo() (2 input parameters) +Function 367: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 359: GetGlyphAtlasRec() (2 input parameters) +Function 368: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 360: LoadUTF8() (2 input parameters) +Function 369: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 361: UnloadUTF8() (1 input parameters) +Function 370: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 362: LoadCodepoints() (2 input parameters) +Function 371: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 363: UnloadCodepoints() (1 input parameters) +Function 372: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 364: GetCodepointCount() (1 input parameters) +Function 373: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 365: GetCodepoint() (2 input parameters) +Function 374: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 366: GetCodepointNext() (2 input parameters) +Function 375: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 367: GetCodepointPrevious() (2 input parameters) +Function 376: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 368: CodepointToUTF8() (2 input parameters) +Function 377: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 369: TextCopy() (2 input parameters) +Function 378: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 370: TextIsEqual() (2 input parameters) +Function 379: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 371: TextLength() (1 input parameters) +Function 380: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 372: TextFormat() (2 input parameters) +Function 381: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 373: TextSubtext() (3 input parameters) +Function 382: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 374: TextReplace() (3 input parameters) +Function 383: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 375: TextInsert() (3 input parameters) +Function 384: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 376: TextJoin() (3 input parameters) +Function 385: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 377: TextSplit() (3 input parameters) +Function 386: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 378: TextAppend() (3 input parameters) +Function 387: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 379: TextFindIndex() (2 input parameters) +Function 388: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 380: TextToUpper() (1 input parameters) +Function 389: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 381: TextToLower() (1 input parameters) +Function 390: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 382: TextToPascal() (1 input parameters) +Function 391: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 383: TextToInteger() (1 input parameters) +Function 392: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 384: DrawLine3D() (3 input parameters) +Function 393: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 385: DrawPoint3D() (2 input parameters) +Function 394: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 386: DrawCircle3D() (5 input parameters) +Function 395: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3371,7 +3431,7 @@ Function 386: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 387: DrawTriangle3D() (4 input parameters) +Function 396: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3379,14 +3439,14 @@ Function 387: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 388: DrawTriangleStrip3D() (3 input parameters) +Function 397: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 389: DrawCube() (5 input parameters) +Function 398: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3395,14 +3455,14 @@ Function 389: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 390: DrawCubeV() (3 input parameters) +Function 399: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 391: DrawCubeWires() (5 input parameters) +Function 400: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3411,21 +3471,21 @@ Function 391: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 392: DrawCubeWiresV() (3 input parameters) +Function 401: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 393: DrawSphere() (3 input parameters) +Function 402: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 394: DrawSphereEx() (5 input parameters) +Function 403: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3434,7 +3494,7 @@ Function 394: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 395: DrawSphereWires() (5 input parameters) +Function 404: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3443,7 +3503,7 @@ Function 395: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 396: DrawCylinder() (6 input parameters) +Function 405: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3453,7 +3513,7 @@ Function 396: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 397: DrawCylinderEx() (6 input parameters) +Function 406: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3463,7 +3523,7 @@ Function 397: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 398: DrawCylinderWires() (6 input parameters) +Function 407: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3473,7 +3533,7 @@ Function 398: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 399: DrawCylinderWiresEx() (6 input parameters) +Function 408: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3483,7 +3543,7 @@ Function 399: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 400: DrawCapsule() (6 input parameters) +Function 409: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3493,7 +3553,7 @@ Function 400: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 401: DrawCapsuleWires() (6 input parameters) +Function 410: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3503,51 +3563,51 @@ Function 401: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 402: DrawPlane() (3 input parameters) +Function 411: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 403: DrawRay() (2 input parameters) +Function 412: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 404: DrawGrid() (2 input parameters) +Function 413: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 405: LoadModel() (1 input parameters) +Function 414: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 406: LoadModelFromMesh() (1 input parameters) +Function 415: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 407: IsModelReady() (1 input parameters) +Function 416: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 408: UnloadModel() (1 input parameters) +Function 417: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 409: GetModelBoundingBox() (1 input parameters) +Function 418: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 410: DrawModel() (4 input parameters) +Function 419: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3555,7 +3615,7 @@ Function 410: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 411: DrawModelEx() (6 input parameters) +Function 420: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3565,7 +3625,7 @@ Function 411: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 412: DrawModelWires() (4 input parameters) +Function 421: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3573,7 +3633,7 @@ Function 412: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 413: DrawModelWiresEx() (6 input parameters) +Function 422: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3583,13 +3643,13 @@ Function 413: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 414: DrawBoundingBox() (2 input parameters) +Function 423: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 415: DrawBillboard() (5 input parameters) +Function 424: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3598,7 +3658,7 @@ Function 415: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 416: DrawBillboardRec() (6 input parameters) +Function 425: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3608,7 +3668,7 @@ Function 416: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 417: DrawBillboardPro() (9 input parameters) +Function 426: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3621,13 +3681,13 @@ Function 417: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 418: UploadMesh() (2 input parameters) +Function 427: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 419: UpdateMeshBuffer() (5 input parameters) +Function 428: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3636,19 +3696,19 @@ Function 419: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 420: UnloadMesh() (1 input parameters) +Function 429: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 421: DrawMesh() (3 input parameters) +Function 430: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 422: DrawMeshInstanced() (4 input parameters) +Function 431: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3656,29 +3716,29 @@ Function 422: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 423: ExportMesh() (2 input parameters) +Function 432: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 424: GetMeshBoundingBox() (1 input parameters) +Function 433: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 425: GenMeshTangents() (1 input parameters) +Function 434: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 426: GenMeshPoly() (2 input parameters) +Function 435: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 427: GenMeshPlane() (4 input parameters) +Function 436: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3686,42 +3746,42 @@ Function 427: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 428: GenMeshCube() (3 input parameters) +Function 437: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 429: GenMeshSphere() (3 input parameters) +Function 438: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 430: GenMeshHemiSphere() (3 input parameters) +Function 439: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 431: GenMeshCylinder() (3 input parameters) +Function 440: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 432: GenMeshCone() (3 input parameters) +Function 441: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 433: GenMeshTorus() (4 input parameters) +Function 442: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3729,7 +3789,7 @@ Function 433: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 434: GenMeshKnot() (4 input parameters) +Function 443: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3737,84 +3797,84 @@ Function 434: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 435: GenMeshHeightmap() (2 input parameters) +Function 444: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 436: GenMeshCubicmap() (2 input parameters) +Function 445: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 437: LoadMaterials() (2 input parameters) +Function 446: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 438: LoadMaterialDefault() (0 input parameters) +Function 447: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 439: IsMaterialReady() (1 input parameters) +Function 448: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 440: UnloadMaterial() (1 input parameters) +Function 449: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 441: SetMaterialTexture() (3 input parameters) +Function 450: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 442: SetModelMeshMaterial() (3 input parameters) +Function 451: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 443: LoadModelAnimations() (2 input parameters) +Function 452: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) - Param[2]: animCount (type: unsigned int *) -Function 444: UpdateModelAnimation() (3 input parameters) + Param[2]: animCount (type: int *) +Function 453: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 445: UnloadModelAnimation() (1 input parameters) +Function 454: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 446: UnloadModelAnimations() (2 input parameters) +Function 455: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) - Param[2]: count (type: unsigned int) -Function 447: IsModelAnimationValid() (2 input parameters) + Param[2]: animCount (type: int) +Function 456: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 448: CheckCollisionSpheres() (4 input parameters) +Function 457: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3822,40 +3882,40 @@ Function 448: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 449: CheckCollisionBoxes() (2 input parameters) +Function 458: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 450: CheckCollisionBoxSphere() (3 input parameters) +Function 459: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 451: GetRayCollisionSphere() (3 input parameters) +Function 460: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 452: GetRayCollisionBox() (2 input parameters) +Function 461: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 453: GetRayCollisionMesh() (3 input parameters) +Function 462: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 454: GetRayCollisionTriangle() (4 input parameters) +Function 463: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3863,7 +3923,7 @@ Function 454: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 455: GetRayCollisionQuad() (5 input parameters) +Function 464: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3872,143 +3932,153 @@ Function 455: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 456: InitAudioDevice() (0 input parameters) +Function 465: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 457: CloseAudioDevice() (0 input parameters) +Function 466: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 458: IsAudioDeviceReady() (0 input parameters) +Function 467: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 459: SetMasterVolume() (1 input parameters) +Function 468: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 460: LoadWave() (1 input parameters) +Function 469: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 461: LoadWaveFromMemory() (3 input parameters) +Function 470: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 462: IsWaveReady() (1 input parameters) +Function 471: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 463: LoadSound() (1 input parameters) +Function 472: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 464: LoadSoundFromWave() (1 input parameters) +Function 473: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 465: IsSoundReady() (1 input parameters) +Function 474: LoadSoundAlias() (1 input parameters) + Name: LoadSoundAlias + Return type: Sound + Description: Create a new sound that shares the same sample data as the source sound, does not own the sound data + Param[1]: source (type: Sound) +Function 475: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 466: UpdateSound() (3 input parameters) +Function 476: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 467: UnloadWave() (1 input parameters) +Function 477: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 468: UnloadSound() (1 input parameters) +Function 478: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 469: ExportWave() (2 input parameters) +Function 479: UnloadSoundAlias() (1 input parameters) + Name: UnloadSoundAlias + Return type: void + Description: Unload a sound alias (does not deallocate sample data) + Param[1]: alias (type: Sound) +Function 480: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 470: ExportWaveAsCode() (2 input parameters) +Function 481: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 471: PlaySound() (1 input parameters) +Function 482: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 472: StopSound() (1 input parameters) +Function 483: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 473: PauseSound() (1 input parameters) +Function 484: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 474: ResumeSound() (1 input parameters) +Function 485: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 475: IsSoundPlaying() (1 input parameters) +Function 486: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 476: SetSoundVolume() (2 input parameters) +Function 487: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 477: SetSoundPitch() (2 input parameters) +Function 488: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 478: SetSoundPan() (2 input parameters) +Function 489: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 479: WaveCopy() (1 input parameters) +Function 490: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 480: WaveCrop() (3 input parameters) +Function 491: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 481: WaveFormat() (4 input parameters) +Function 492: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4016,203 +4086,203 @@ Function 481: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 482: LoadWaveSamples() (1 input parameters) +Function 493: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 483: UnloadWaveSamples() (1 input parameters) +Function 494: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 484: LoadMusicStream() (1 input parameters) +Function 495: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 485: LoadMusicStreamFromMemory() (3 input parameters) +Function 496: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 486: IsMusicReady() (1 input parameters) +Function 497: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 487: UnloadMusicStream() (1 input parameters) +Function 498: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 488: PlayMusicStream() (1 input parameters) +Function 499: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 489: IsMusicStreamPlaying() (1 input parameters) +Function 500: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 490: UpdateMusicStream() (1 input parameters) +Function 501: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 491: StopMusicStream() (1 input parameters) +Function 502: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 492: PauseMusicStream() (1 input parameters) +Function 503: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 493: ResumeMusicStream() (1 input parameters) +Function 504: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 494: SeekMusicStream() (2 input parameters) +Function 505: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 495: SetMusicVolume() (2 input parameters) +Function 506: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 496: SetMusicPitch() (2 input parameters) +Function 507: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 497: SetMusicPan() (2 input parameters) +Function 508: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 498: GetMusicTimeLength() (1 input parameters) +Function 509: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 499: GetMusicTimePlayed() (1 input parameters) +Function 510: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 500: LoadAudioStream() (3 input parameters) +Function 511: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 501: IsAudioStreamReady() (1 input parameters) +Function 512: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 502: UnloadAudioStream() (1 input parameters) +Function 513: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 503: UpdateAudioStream() (3 input parameters) +Function 514: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 504: IsAudioStreamProcessed() (1 input parameters) +Function 515: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 505: PlayAudioStream() (1 input parameters) +Function 516: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 506: PauseAudioStream() (1 input parameters) +Function 517: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 507: ResumeAudioStream() (1 input parameters) +Function 518: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 508: IsAudioStreamPlaying() (1 input parameters) +Function 519: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 509: StopAudioStream() (1 input parameters) +Function 520: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 510: SetAudioStreamVolume() (2 input parameters) +Function 521: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 511: SetAudioStreamPitch() (2 input parameters) +Function 522: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 512: SetAudioStreamPan() (2 input parameters) +Function 523: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 513: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 524: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 514: SetAudioStreamCallback() (2 input parameters) +Function 525: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 515: AttachAudioStreamProcessor() (2 input parameters) +Function 526: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void - Description: Attach audio stream processor to stream + Description: Attach audio stream processor to stream, receives the samples as s Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 516: DetachAudioStreamProcessor() (2 input parameters) +Function 527: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 517: AttachAudioMixedProcessor() (1 input parameters) +Function 528: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void - Description: Attach audio stream processor to the entire audio pipeline + Description: Attach audio stream processor to the entire audio pipeline, receives the samples as s Param[1]: processor (type: AudioCallback) -Function 518: DetachAudioMixedProcessor() (1 input parameters) +Function 529: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index f450069cc..aa7605b8b 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -292,7 +292,7 @@ - + @@ -306,6 +306,7 @@ + @@ -539,7 +540,7 @@ - + @@ -550,17 +551,20 @@ - - - - - - - - - - - + + + + + + + + + + + + + + @@ -637,12 +641,12 @@ - + - + @@ -656,7 +660,7 @@ - + @@ -691,6 +695,8 @@ + + @@ -704,20 +710,24 @@ - + - + + + + + @@ -725,6 +735,8 @@ + + @@ -761,7 +773,7 @@ - + @@ -976,7 +988,7 @@ - + @@ -984,11 +996,11 @@ - + - + @@ -1031,7 +1043,7 @@ - + @@ -1082,6 +1094,9 @@ + + + @@ -1187,7 +1202,7 @@ - + @@ -1263,6 +1278,18 @@ + + + + + + + + + + + + @@ -1516,6 +1543,11 @@ + + + + + @@ -1540,6 +1572,11 @@ + + + + + @@ -1685,7 +1722,7 @@ - + @@ -1995,11 +2032,11 @@ - + - - + + @@ -2011,8 +2048,8 @@ - - + + @@ -2021,20 +2058,20 @@ - - + + - - + + - + @@ -2083,12 +2120,15 @@ - + + + + @@ -2523,7 +2563,7 @@ - + @@ -2535,7 +2575,7 @@ - + @@ -2609,6 +2649,9 @@ + + + @@ -2623,6 +2666,9 @@ + + + @@ -2785,7 +2831,7 @@ - + @@ -2793,7 +2839,7 @@ - + From be8eea9edadea82395b0ebec1c8afddbed129641 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 18:10:05 +0200 Subject: [PATCH 0672/1710] Format tweaks --- parser/raylib_parser.c | 4 ++-- src/config.h | 4 +--- src/raylib.h | 2 +- src/rlgl.h | 2 +- src/rmodels.c | 8 ++++---- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index e1ff37918..678b64d65 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -447,11 +447,11 @@ int main(int argc, char* argv[]) { if (isFloat) { - defines[defineIndex].type = linePtr[j-1] == 'f' ? FLOAT : DOUBLE; + defines[defineIndex].type = (linePtr[j-1] == 'f')? FLOAT : DOUBLE; } else { - defines[defineIndex].type = linePtr[j-1] == 'L' ? LONG : INT; + defines[defineIndex].type = (linePtr[j-1] == 'L')? LONG : INT; defines[defineIndex].isHex = isHex; } } diff --git a/src/config.h b/src/config.h index 370837610..7886aeeba 100644 --- a/src/config.h +++ b/src/config.h @@ -55,9 +55,7 @@ // 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 -// Wait for events passively (sleeping while no events) instead of polling them actively every frame -//#define SUPPORT_EVENTS_WAITING 1 +#define SUPPORT_PARTIALBUSY_WAIT_LOOP 1 // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() #define SUPPORT_SCREEN_CAPTURE 1 // Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() diff --git a/src/raylib.h b/src/raylib.h index f52f3460d..5fa66bd03 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -938,8 +938,8 @@ extern "C" { // Prevents name mangling of functions // Window-related functions RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context -RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed RLAPI void CloseWindow(void); // Close window and unload OpenGL context +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) diff --git a/src/rlgl.h b/src/rlgl.h index 7a0d7f862..40e7b4785 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4248,7 +4248,7 @@ void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool re unsigned int glInternalFormat = 0, glFormat = 0, glType = 0; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - glBindImageTexture(index, id, 0, 0, 0, readonly ? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); + glBindImageTexture(index, id, 0, 0, 0, readonly? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); #endif } diff --git a/src/rmodels.c b/src/rmodels.c index 27fe4d85f..490eac555 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1202,7 +1202,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) // 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; + void *vertices = (mesh->animVertices != NULL)? mesh->animVertices : mesh->vertices; mesh->vboId[0] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic); rlSetVertexAttribute(0, 3, RL_FLOAT, 0, 0, 0); rlEnableVertexAttribute(0); @@ -1218,7 +1218,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) if (mesh->normals != NULL) { // Enable vertex attributes: normals (shader-location = 2) - void *normals = mesh->animNormals != NULL ? mesh->animNormals : mesh->normals; + void *normals = (mesh->animNormals != NULL)? mesh->animNormals : mesh->normals; mesh->vboId[2] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic); rlSetVertexAttribute(2, 3, RL_FLOAT, 0, 0, 0); rlEnableVertexAttribute(2); @@ -5585,7 +5585,7 @@ static Model LoadM3D(const char *fileName) 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); + 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; @@ -5910,7 +5910,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou 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); + TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2); UnloadFileData(fileData); return NULL; } From 1327b570e3aac6111ebdb3b233b9c60cdc12ee9f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 18:11:55 +0200 Subject: [PATCH 0673/1710] Update raylib_parser.c --- parser/raylib_parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 678b64d65..bdec4915a 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -534,8 +534,8 @@ int main(int argc, char* argv[]) { // 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 (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; } From fecf56e15aaba160aefe14b241de19262a20ab3e Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 18:36:07 +0200 Subject: [PATCH 0674/1710] WARNING: `rcore` module split per-platform **BIG CHANGE** (#3388) * Submodules (#3311) * Check in current state * Add submodules to Makefile and clean up some imports * Start moving InitGraphicsDeivce * Move android_main and CloseWindow() out of rcore * Move WindowShouldClose out of rcore * Move IsWindowHidden out of rcore * Move IsWindowMinimized out of rcore * Move IsWindowMaximized, IsWindowFocused and IsWindowResized out of rcore * Move ToggleFullscreen out of rcore * Move MaximizeWindow, MinimizeWindow and RestoreWindow out of rcore * Move 13 functions out of rcore: ToggleBorderlessWindowed SetWindowState ClearWindowState SetWindowIcon SetWindowIcons SetWindowTitle SetWindowPosition SetWindowMonitor SetWindowMinSize SetWindowMaxSize SetWindowSize SetWindowOpacity SetWindowFocused * Minor clean up, revert makefile change, include submodules directly in rcore * Fix makefile comment * Remove rcore.h from Makefile * Remove debug include * Move 18 functions from rcore to submodules GetWindowHandle GetMonitorCount GetCurrentMonitor GetMonitorPosition GetMonitorWidth GetMonitorHeight GetMonitorPhysicalHeight GetMonitorRefreshRate GetWindowPosition GetWindowScaleDPI GetMonitorName SetClipboardText GetClipboardText ShowCursor HideCursor EnableCursor DisableCursor GetTime * Move TakeScreenshot, OpenURL, GetGamepadName out of rcore into submodules * remove debugging #defines * Move GetMonitorPhysicalWidth from rcore to submodule * Move GetGamepadAxisCount from rcore * Move SetGamepadMappings out of rcore * Move GetMouseX, GetMouseY, GetMousePosition out of rcore * Move SetMousePosition out of rcore * Move GetMouseWheelMove out of rcore * Move the last functions out of rcore * Move shared function defs and some global var to rcore.h * Clean up rcore.c and rcore.h a little more * Remove unnecessary #define --------- Co-authored-by: MichaelFiber * REVIEWED: `PLATFORM_DESKTOP` Windows building * Revert "REVIEWED: `PLATFORM_DESKTOP` Windows building" This reverts commit 71a12171f768eb25053ef908732b4ce8fdf802f7. * Reviewed Windows building * [split] Fix compilation for web (and desktop) (#3329) * Fix compilation for web * Remove EM_ASM_INT from core_input_gestures_web example * Fix raymath undefined symbols for desktop and web * Remove raylib_opengl_interop from examples Makefile * Revert previous commit (8651c78) * Fix TraceLog for web and desktop * [split] `rcore`, `rcore_web` and `rcore_desktop` changes (batch 2) (#3334) * Fix formatting * Reapply commit 9d230d7 (#3305) that was missing * Reapplies commits 719365f (#3309) and 8a1779b (#3312) that were missing * Reapply commit 5c9cc3f (#3323) that was missing * Reapply commit a2b3b1e that was missing * Revert commit cef25c6 to fix macro redefined warning * Move rcore.h #include to after config.h to fix macro redefinitions warnings * [split] `rcore`, `web`, `desktop`, `android` changes (batch 3) (#3338) * First pass to remove unneeded platform macros for web * Second pass to remove unneeded platform macros for web * Move GetTouchX, GetTouchY, GetTouchPosition from rcore to web, desktop, android * Move SetMouseCursor from rcore to android, desktop, web * [split] `rcore`, `web`, `desktop`, `android` changes (batch 4) (#3343) * Fix ToggleBorderlessWindowed duplicated glfwSetWindowSize calls * First pass to remove unneeded platform macros for android * Second pass to remove unneeded platform macros for android * Remove unneeded platform macros for desktop * Relocate GetGamepadName and update SetGamepadMappings on android, desktop, web * Add missing comment to web * [split] `rcore`, `web`, `desktop`, `android` changes (batch 5) (#3345) * Move SetExitKey from core to android, desktop, web * Move some callbacks from core to desktop and web * Relocate emscripten callbacks on web * Relocate android callbacks on android * Revert "Relocate android callbacks on android" This reverts commit bbdbecc01ea7f871dae56019724386e73611c69c. * Updates UnloadVrStereoConfig on rcore * Update SetClipboardText on android * Fix screenMin/Max default values for android * [split] `rcore`, `drm` changes (#3347) * Tweak makefiles for PLATFORM_DRM and move rcore_drm's dependencies to rcore.h * Move drm functions to rcore_drm.c * Fix a typo in rcore.c * Add SetExitKey to rcore_drm.c --------- Co-authored-by: MichaelFiber * Fix compilation for android (#3360) * Fix android include (#3364) * Reviewed platform split #3313 - Added file headers info - Added TRACELOG message for unimplemented functions - Reviewed code formatting and organization - Several code tweaks * REVIEWED: `GetDirectoryPath()` --------- Co-authored-by: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Co-authored-by: MichaelFiber Co-authored-by: ubkp <118854183+ubkp@users.noreply.github.com> --- .gitignore | 3 + examples/Makefile | 1 + examples/core/core_input_gestures_web.c | 26 +- src/Makefile | 1 + src/rcore.c | 5041 +---------------------- src/rcore.h | 318 ++ src/rcore_android.c | 1267 ++++++ src/rcore_desktop.c | 2074 ++++++++++ src/rcore_drm.c | 2059 +++++++++ src/rcore_web.c | 1604 ++++++++ 10 files changed, 7485 insertions(+), 4909 deletions(-) create mode 100644 src/rcore.h create mode 100644 src/rcore_android.c create mode 100644 src/rcore_desktop.c create mode 100644 src/rcore_drm.c create mode 100644 src/rcore_web.c diff --git a/.gitignore b/.gitignore index ec9325081..fb4865ba3 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,9 @@ xcschememanagement.plist xcuserdata/ DerivedData/ +# VSCode project +.vscode + # Jetbrains project .idea/ cmake-build-*/ diff --git a/examples/Makefile b/examples/Makefile index cd079c312..9404d39cc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -225,6 +225,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif ifeq ($(PLATFORM),PLATFORM_DRM) + INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) INCLUDE_PATHS += -I/usr/include/libdrm endif diff --git a/examples/core/core_input_gestures_web.c b/examples/core/core_input_gestures_web.c index 0129a983b..e1492244c 100644 --- a/examples/core/core_input_gestures_web.c +++ b/examples/core/core_input_gestures_web.c @@ -135,7 +135,7 @@ void Update(void) } } } - + int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled if (currentGesture !=0) { @@ -156,16 +156,16 @@ void Update(void) 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)); + TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture)); } // Handle protractor @@ -182,14 +182,14 @@ void Update(void) { 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) @@ -204,7 +204,7 @@ void Update(void) // Draw //-------------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(RAYWHITE); // Draw common @@ -235,7 +235,7 @@ void Update(void) // 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; @@ -286,7 +286,7 @@ void Update(void) 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 @@ -308,14 +308,6 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - #if defined( PLATFORM_WEB ) - // Using Emscripten EM_ASM_INT macro, get the page canvas width - const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); - - if (canvasWidth > 400) screenWidth = canvasWidth; - else screenWidth = 400; // Set a minimum width for the screen - #endif - InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures web"); //-------------------------------------------------------------------------------------- diff --git a/src/Makefile b/src/Makefile index 63cbe2a41..e75ae5368 100644 --- a/src/Makefile +++ b/src/Makefile @@ -384,6 +384,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) # without EGL_NO_X11 eglplatform.h tears Xlib.h in which tears X.h in # which contains a conflicting type Font CFLAGS += -DEGL_NO_X11 + CFLAGS += -Werror=implicit-function-declaration endif # Use Wayland display on Linux desktop ifeq ($(PLATFORM),PLATFORM_DESKTOP) diff --git a/src/rcore.c b/src/rcore.c index 3ba0404d7..44b7ee787 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -53,9 +53,6 @@ * #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_EVENTS_WAITING -* Wait for events passively (sleeping while no events) instead of polling them actively every frame -* * #define SUPPORT_SCREEN_CAPTURE * Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() * @@ -105,12 +102,11 @@ #include "config.h" // Defines module configuration flags #endif -#include "utils.h" // Required for: TRACELOG() macros +#include "rcore.h" #define RLGL_IMPLEMENTATION #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 -#define RAYMATH_IMPLEMENTATION // Define external out-of-line implementation #include "raymath.h" // Vector3, Quaternion and Matrix functionality #if defined(SUPPORT_GESTURES_SYSTEM) @@ -141,10 +137,6 @@ #include "external/sdefl.h" // Deflate (RFC 1951) compressor #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 #if defined(__linux__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -166,12 +158,6 @@ #endif // OSs #endif // PLATFORM_DESKTOP -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - #define _CRT_INTERNAL_NONSTDC_NAMES 1 #include // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()] @@ -199,318 +185,21 @@ #define CHDIR chdir #endif -#if defined(PLATFORM_DESKTOP) - #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 - // NOTE: Already provided by rlgl implementation (on glad.h) - #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management - // NOTE: GLFW3 already includes gl.h (OpenGL) headers - - // Support retrieving native window handlers - #if defined(_WIN32) - typedef void *PVOID; - typedef PVOID HANDLE; - typedef HANDLE HWND; - #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); - #endif - #endif - #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) - #include // 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 - //#define GLFW_EXPOSE_NATIVE_MIR - #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window() - #endif - #if defined(__APPLE__) - #include // Required for: usleep() - - //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition - void *glfwGetCocoaWindow(GLFWwindow* handle); - #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() - #endif - - // TODO: HACK: Added flag if not provided by GLFW when using external library - // Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev - #if !defined(GLFW_MOUSE_PASSTHROUGH) - #define GLFW_MOUSE_PASSTHROUGH 0x0002000D - #endif -#endif - -#if defined(PLATFORM_ANDROID) - //#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) - #include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others - #include // Required for: android_app struct and activity management - #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] - - #include // Native platform windowing system interface - //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_DRM) - #include // POSIX file control definitions - open(), creat(), fcntl() - #include // POSIX standard function definitions - read(), close(), STDIN_FILENO - #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() - #include // POSIX threads management (inputs reading) - #include // POSIX directory browsing - - #include // Required for: ioctl() - UNIX System call for device-specific input/output operations - #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition - #include // Linux: Keycodes constants definition (KEY_A, ...) - #include // Linux: Joystick support library - - #include // Generic Buffer Management (native platform for EGL on DRM) - #include // Direct Rendering Manager user-level library interface - #include // Direct Rendering Manager mode setting (KMS) interface - - #include "EGL/egl.h" // Native platform windowing system interface - #include "EGL/eglext.h" // EGL extensions - //#include "GLES2/gl2.h" // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_WEB) - #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) - //#define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) - #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management - #include // Required for: timespec, nanosleep(), select() - POSIX - - #include // Emscripten functionality for C - #include // Emscripten HTML5 library -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) - #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number - - #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) - #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events -#endif - -#ifndef MAX_FILEPATH_CAPACITY - #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath -#endif -#ifndef MAX_FILEPATH_LENGTH - #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) -#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_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 - -// 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 -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) -typedef struct { - pthread_t threadId; // Event reading thread id - int fd; // File descriptor to the device it is assigned to - int eventNum; // Number of 'event' device - 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 isMouse; // True if device supports relative X Y movements - bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH - bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH - bool isKeyboard; // True if device has letter keycodes - bool isGamepad; // True if device has gamepad buttons -} InputEventWorker; -#endif - -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 { -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - GLFWwindow *handle; // GLFW window handle (graphic device) -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) -#if defined(PLATFORM_DRM) - 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) -#endif // PLATFORM_DRM - 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 -#endif - 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 - - 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; -#if defined(PLATFORM_ANDROID) - struct { - bool appEnabled; // Flag to detect if app is active ** = true - struct android_app *app; // Android activity - struct android_poll_source *source; // Android events polling source - bool contextRebindRequired; // Used to know context rebind required - } Android; -#endif - struct { - const char *basePath; // Base path for data storage - } Storage; - struct { -#if defined(PLATFORM_DRM) - InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" -#endif - 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 - -#if defined(PLATFORM_DRM) - int defaultMode; // Default keyboard mode -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - bool evtMode; // Keyboard in event mode -#endif - int defaultFileFlags; // Default IO file flags - struct termios defaultSettings; // Default keyboard settings - int fd; // File descriptor for the evdev keyboard -#endif - } 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 -#if defined(PLATFORM_DRM) - 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 -#endif - } 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; // 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 -#if defined(PLATFORM_DRM) - pthread_t threadId; // Gamepad reading thread id - int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor -#endif - } 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 -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - unsigned long long int base; // Base time measure for hi-res timer -#endif - 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 -static CoreData CORE = { 0 }; // Global CORE state context +CoreData CORE = { 0 }; // Global CORE state context #if defined(SUPPORT_SCREEN_CAPTURE) -static int screenshotCounter = 0; // Screenshots counter +static int screenshotCounter = 0; // Screenshots counter #endif #if defined(SUPPORT_GIF_RECORDING) -static int gifFrameCounter = 0; // GIF frames counter -static bool gifRecording = false; // GIF recording state -static MsfGifState gifState = { 0 }; // MSGIF context state +int gifFrameCounter = 0; // GIF frames counter +bool gifRecording = false; // GIF recording state +MsfGifState gifState = { 0 }; // MSGIF context state #endif #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -602,79 +291,22 @@ static bool eventsRecording = false; // Record events //----------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- -// Other Modules Functions Declaration (required by core) +// 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 +extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow() +extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -static void InitTimer(void); // Initialize timer (hi-resolution if available) -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device -static void SetupFramebuffer(int width, int height); // Setup main framebuffer -static void SetupViewport(int width, int height); // Set viewport for a provided width and height +static void InitTimer(void); // Initialize timer (hi-resolution if available) +static void SetupFramebuffer(int width, int height); // Setup main framebuffer +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(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) -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 -#if !defined(PLATFORM_WEB) -static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized -#endif -static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored -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 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 -#endif - -#if defined(PLATFORM_ANDROID) -static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands -static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs -#endif - -#if defined(PLATFORM_WEB) -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 EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); - -static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); -static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); -static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); -#endif - -#if defined(PLATFORM_DRM) -static void InitKeyboard(void); // Initialize raw keyboard system -static void RestoreKeyboard(void); // Restore keyboard system -#if defined(SUPPORT_SSH_KEYBOARD_RPI) -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 *EventThread(void *arg); // Input device events reading thread - -static void InitGamepad(void); // Initialize raw gamepad input -static void *GamepadThread(void *arg); // Mouse reading thread - -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 - -#endif // PLATFORM_DRM - #if defined(SUPPORT_EVENTS_AUTOMATION) static void LoadAutomationEvents(const char *fileName); // Load automation events from file static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file @@ -691,443 +323,73 @@ void __stdcall Sleep(unsigned long msTimeout); // Required for: Wai const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' #endif // !SUPPORT_MODULE_RTEXT +// Include platform-specific submodules +#if defined(PLATFORM_DESKTOP) + #include "rcore_desktop.c" +#elif defined(PLATFORM_WEB) + #include "rcore_web.c" +#elif defined(PLATFORM_DRM) + #include "rcore_drm.c" +#elif defined(PLATFORM_ANDROID) + #include "rcore_android.c" +#else + // Software rendering backend, user needs to provide buffer ;) +#endif + //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) -// To allow easier porting to android, we allow the user to define a -// main function which we call from android_main, defined by ourselves -extern int main(int argc, char *argv[]); -void android_main(struct android_app *app) -{ - char arg0[] = "raylib"; // NOTE: argv[] are mutable - CORE.Android.app = app; +// NOTE: Multiple window/display/monitor management functions have been moved to platform-specific modules + +// Platform-specific functions: +//void InitWindow(int width, int height, const char *title); +//void CloseWindow(void); +//bool WindowShouldClose(void) +//bool IsWindowHidden(void) +//bool IsWindowMinimized(void) +//bool IsWindowMaximized(void) +//bool IsWindowFocused(void) +//bool IsWindowResized(void) +//void ToggleFullscreen(void) +//void MaximizeWindow(void) +//void MinimizeWindow(void) +//void RestoreWindow(void) +//void ToggleBorderlessWindowed(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) +//int GetMonitorCount(void) +//int GetCurrentMonitor(void) +//Vector2 GetMonitorPosition(int monitor) +//int GetMonitorWidth(int monitor) +//int GetMonitorHeight(int monitor) +//int GetMonitorPhysicalWidth(int monitor) +//int GetMonitorPhysicalHeight(int monitor) +//int GetMonitorRefreshRate(int monitor) +//const char *GetMonitorName(int monitor) +//Vector2 GetWindowPosition(void) +//Vector2 GetWindowScaleDPI(void) +//void SetClipboardText(const char *text) +//const char *GetClipboardText(void) +//void ShowCursor(void) +//void HideCursor(void) +//void EnableCursor(void) +//void DisableCursor(void) +//double GetTime(void) +//void TakeScreenshot(const char *fileName) +//void OpenURL(const char *url) - // NOTE: Return from main is ignored - (void)main(1, (char *[]) { arg0, NULL }); - - // Request to end the native activity - ANativeActivity_finish(app->activity); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Waiting for application events before complete finishing - while (!app->destroyRequested) - { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) - { - if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); - } - } -} - -// NOTE: Add this to header (if apps really need it) -struct android_app *GetAndroidApp(void) -{ - return CORE.Android.app; -} -#endif - -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - 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 - - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); - 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 = 0; // GAMEPAD_BUTTON_UNKNOWN -#if defined(SUPPORT_EVENTS_WAITING) - CORE.Window.eventWaiting = true; -#endif - -#if defined(PLATFORM_ANDROID) - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - - int orientation = AConfiguration_getOrientation(CORE.Android.app->config); - - if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); - else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); - - // TODO: Automatic orientation doesn't seem to work - if (width <= height) - { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); - } - else - { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); - } - - //AConfiguration_getDensity(CORE.Android.app->config); - //AConfiguration_getKeyboard(CORE.Android.app->config); - //AConfiguration_getScreenSize(CORE.Android.app->config); - //AConfiguration_getScreenLong(CORE.Android.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - CORE.Android.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - CORE.Android.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; - - TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Wait for window to be initialized (display and context) - while (!CORE.Window.ready) - { - // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) - { - // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); - - // NOTE: Never close window, native activity is controlled by the system! - //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; - } - } -#endif -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); - - // Initialize random seed - srand((unsigned int)time(NULL)); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - -#if defined(SUPPORT_MODULE_RTEXT) && 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 -#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 -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(PLATFORM_DRM) - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) -#endif - -#if defined(PLATFORM_WEB) - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // 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) - //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) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Trigger this once to get initial window sizing - 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); - - // 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_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - -#endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_DRM -} - -// 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 - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwDestroyWindow(CORE.Window.handle); - glfwTerminate(); -#endif - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - -#if defined(PLATFORM_ANDROID) - // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - if (CORE.Window.context != EGL_NO_CONTEXT) - { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; - } - - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; - } -#endif - -#if defined(PLATFORM_DRM) - if (CORE.Window.prevFB) - { - drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); - CORE.Window.prevFB = 0; - } - - if (CORE.Window.prevBO) - { - gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); - CORE.Window.prevBO = NULL; - } - - if (CORE.Window.gbmSurface) - { - gbm_surface_destroy(CORE.Window.gbmSurface); - CORE.Window.gbmSurface = NULL; - } - - if (CORE.Window.gbmDevice) - { - gbm_device_destroy(CORE.Window.gbmDevice); - CORE.Window.gbmDevice = NULL; - } - - if (CORE.Window.crtc) - { - if (CORE.Window.connector) - { - drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id, - CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode); - drmModeFreeConnector(CORE.Window.connector); - CORE.Window.connector = NULL; - } - - drmModeFreeCrtc(CORE.Window.crtc); - CORE.Window.crtc = NULL; - } - - if (CORE.Window.fd != -1) - { - close(CORE.Window.fd); - CORE.Window.fd = -1; - } - - // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) - { - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - if (CORE.Window.context != EGL_NO_CONTEXT) - { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; - } - - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; - } -#endif - -#if defined(PLATFORM_DRM) - // Wait for mouse and gamepad threads to finish before closing - // NOTE: Those threads should already have finished at this point - // because they are controlled by CORE.Window.shouldClose variable - - CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called - - // Close the evdev keyboard - if (CORE.Input.Keyboard.fd != -1) - { - close(CORE.Input.Keyboard.fd); - CORE.Input.Keyboard.fd = -1; - } - - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].threadId) - { - pthread_join(CORE.Input.eventWorker[i].threadId, NULL); - } - } - - if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - -// Check if KEY_ESCAPE pressed or Close icon pressed -bool WindowShouldClose(void) -{ -#if defined(PLATFORM_WEB) - // 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); - return false; -#endif - -#if defined(PLATFORM_DESKTOP) - if (CORE.Window.ready) - { - // While window minimized, stop loop execution - while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - - CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); - - // Reset close status for next frame - glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); - - return CORE.Window.shouldClose; - } - else return true; -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - if (CORE.Window.ready) return CORE.Window.shouldClose; - else return true; -#endif -} // Check if window has been initialized successfully bool IsWindowReady(void) @@ -1141,713 +403,12 @@ bool IsWindowFullscreen(void) return CORE.Window.fullscreen; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ -#if defined(PLATFORM_DESKTOP) - return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); -#endif - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); -#endif - return false; -} - -// Check if window has been maximized (only PLATFORM_DESKTOP) -bool IsWindowMaximized(void) -{ -#if defined(PLATFORM_DESKTOP) - return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); -#endif - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); -#endif -#if defined(PLATFORM_ANDROID) - return CORE.Android.appEnabled; -#endif - return true; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return CORE.Window.resizedLastFrame; -#else - return false; -#endif -} - // Check if one specific window flag is enabled bool IsWindowState(unsigned int flag) { return ((CORE.Window.flags & flag) > 0); } -// Toggle fullscreen mode (only PLATFORM_DESKTOP) -void ToggleFullscreen(void) -{ -#if defined(PLATFORM_DESKTOP) - if (!CORE.Window.fullscreen) - { - // Store previous window position (in case we exit fullscreen) - glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); - - 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) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); - - CORE.Window.fullscreen = false; - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - - glfwSetWindowMonitor(CORE.Window.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; - - glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - } - else - { - CORE.Window.fullscreen = false; - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - - glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - - // 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); -#endif -#if defined(PLATFORM_WEB) -/* - 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 (!CORE.Window.fullscreen) - { - // 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, height; - emscripten_get_canvas_element_size("#canvas", &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; - } - 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); - - CORE.Window.fullscreen = false; // Toggle fullscreen flag - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - } -*/ - - CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode"); -#endif -} - -// Toggle borderless windowed mode (only PLATFORM_DESKTOP) -void ToggleBorderlessWindowed(void) -{ -#if defined(PLATFORM_DESKTOP) - // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it - bool wasOnFullscreen = false; - if (CORE.Window.fullscreen) - { - CORE.Window.previousPosition = CORE.Window.position; - ToggleFullscreen(); - wasOnFullscreen = true; - } - - const int monitor = GetCurrentMonitor(); - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - if (mode) - { - if (!IsWindowState(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) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); - CORE.Window.previousScreen = CORE.Window.screen; - - // Set undecorated and topmost modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; - - // 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; - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); - - // Set screen position and size - glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); - - // Refocus window - glfwFocusWindow(CORE.Window.handle); - - CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; - } - else - { - // Remove topmost and undecorated modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); - 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(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); - - // Refocus window - glfwFocusWindow(CORE.Window.handle); - - CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -} - -// Set window state: maximized, if resizable (only PLATFORM_DESKTOP) -void MaximizeWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - glfwMaximizeWindow(CORE.Window.handle); - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; - } -#endif -} - -// Set window state: minimized (only PLATFORM_DESKTOP) -void MinimizeWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(CORE.Window.handle); -#endif -} - -// Set window state: not minimized/maximized (only PLATFORM_DESKTOP) -void RestoreWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - // Restores the specified window if it was previously iconified (minimized) or maximized - glfwRestoreWindow(CORE.Window.handle); - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; - CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; - } -#endif -} - -// Set window configuration state using flags -void SetWindowState(unsigned int flags) -{ -#if defined(PLATFORM_DESKTOP) - // 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)) - { - glfwSwapInterval(1); - 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)) - { - ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_FULLSCREEN_MODE - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (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)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); - 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)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); - 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)) - { - glfwHideWindow(CORE.Window.handle); - 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)) - { - //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)) - { - //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)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); - 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)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); - 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)) - { - 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)) - { - 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)) - { - 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)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); - 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)) - { - 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)) - { - TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); - } -#endif -} - -// Clear window configuration state flags -void ClearWindowState(unsigned int flags) -{ -#if defined(PLATFORM_DESKTOP) - // 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) > 0) && ((flags & FLAG_VSYNC_HINT) > 0)) - { - glfwSwapInterval(0); - 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)) - { - 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)) - { - 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)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; - } - - // State change: FLAG_WINDOW_HIDDEN - if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) - { - glfwShowWindow(CORE.Window.handle); - CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; - } - - // State change: FLAG_WINDOW_MINIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) - { - 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)) - { - 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)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - } - - // State change: FLAG_WINDOW_UNFOCUSED - if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; - } - - // State change: FLAG_WINDOW_TOPMOST - if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); - 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)) - { - 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)) - { - 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)) - { - 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)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); - 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)) - { - 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)) - { - TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); - } -#endif -} - -// Set icon for window (only PLATFORM_DESKTOP) -// NOTE 1: Image must be in RGBA format, 8bit per channel -// NOTE 2: Image is scaled by the OS for all required sizes -void SetWindowIcon(Image image) -{ -#if defined(PLATFORM_DESKTOP) - if (image.data == NULL) - { - // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); - } - else - { - if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - GLFWimage icon[1] = { 0 }; - - icon[0].width = image.width; - icon[0].height = image.height; - icon[0].pixels = (unsigned char *)image.data; - - // NOTE 1: We only support one image icon - // NOTE 2: The specified image data is copied before this function returns - glfwSetWindowIcon(CORE.Window.handle, 1, icon); - } - else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); - } -#endif -} - -// Set icon for window (multiple images, only PLATFORM_DESKTOP) -// NOTE 1: Images must be in RGBA format, 8bit per channel -// NOTE 2: The multiple images are used depending on provided sizes -// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 -void SetWindowIcons(Image *images, int count) -{ -#if defined(PLATFORM_DESKTOP) - if ((images == NULL) || (count <= 0)) - { - // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); - } - else - { - int valid = 0; - GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); - - for (int i = 0; i < count; i++) - { - if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - icons[valid].width = images[i].width; - icons[valid].height = images[i].height; - icons[valid].pixels = (unsigned char *)images[i].data; - - valid++; - } - else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); - } - // NOTE: Images data is copied internally before this function returns - glfwSetWindowIcon(CORE.Window.handle, valid, icons); - - RL_FREE(icons); - } -#endif -} - -// Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) -void SetWindowTitle(const char *title) -{ - CORE.Window.title = title; -#if defined(PLATFORM_DESKTOP) - glfwSetWindowTitle(CORE.Window.handle, title); -#endif -#if defined(PLATFORM_WEB) - emscripten_set_window_title(title); -#endif -} - -// Set window position on screen (windowed mode) -void SetWindowPosition(int x, int y) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetWindowPos(CORE.Window.handle, x, y); -#endif -} - -// Set monitor for the current window -void SetWindowMonitor(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount = 0; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - if (CORE.Window.fullscreen) - { - TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); - - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); - } - else - { - TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); - - const int screenWidth = CORE.Window.screen.width; - const int screenHeight = CORE.Window.screen.height; - int monitorWorkareaX = 0; - int monitorWorkareaY = 0; - int monitorWorkareaWidth = 0; - int monitorWorkareaHeight = 0; - glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); - - // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it - if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); - else - { - const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); - const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); - glfwSetWindowPos(CORE.Window.handle, x, y); - } - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -} - -// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) -void SetWindowMinSize(int width, int height) -{ - CORE.Window.screenMin.width = width; - CORE.Window.screenMin.height = height; -#if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); -#endif -#if defined(PLATFORM_WEB) - // 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); -#endif -} - -// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) -void SetWindowMaxSize(int width, int height) -{ - CORE.Window.screenMax.width = width; - CORE.Window.screenMax.height = height; -#if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); -#endif -#if defined(PLATFORM_WEB) - // 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); -#endif -} - -// Set window dimensions -void SetWindowSize(int width, int height) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSetWindowSize(CORE.Window.handle, width, height); -#endif -} - -// Set window opacity, value opacity is between 0.0 and 1.0 -void SetWindowOpacity(float opacity) -{ -#if defined(PLATFORM_DESKTOP) - if (opacity >= 1.0f) opacity = 1.0f; - else if (opacity <= 0.0f) opacity = 0.0f; - glfwSetWindowOpacity(CORE.Window.handle, opacity); -#endif -} - -// Set window focused -void SetWindowFocused(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwFocusWindow(CORE.Window.handle); -#endif -} - // Get current screen width int GetScreenWidth(void) { @@ -1872,335 +433,6 @@ int GetRenderHeight(void) return CORE.Window.render.height; } -// Get native window handle -void *GetWindowHandle(void) -{ -#if defined(PLATFORM_DESKTOP) && defined(_WIN32) - // NOTE: Returned handle is: void *HWND (windows.h) - return glfwGetWin32Window(CORE.Window.handle); -#endif -#if defined(PLATFORM_DESKTOP) && defined(__linux__) - // NOTE: Returned handle is: unsigned long Window (X.h) - // typedef unsigned long XID; - // typedef XID Window; - //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle); - //return NULL; // TODO: Find a way to return value... cast to void *? - return (void *)CORE.Window.handle; -#endif -#if defined(__APPLE__) - // NOTE: Returned handle is: (objc_object *) - return (void *)glfwGetCocoaWindow(CORE.Window.handle); -#endif - - return NULL; -} - -// Get number of monitors -int GetMonitorCount(void) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - glfwGetMonitors(&monitorCount); - return monitorCount; -#else - return 1; -#endif -} - -// Get number of monitors -int GetCurrentMonitor(void) -{ - int index = 0; - -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - GLFWmonitor *monitor = NULL; - - if (monitorCount > 1) - { - if (IsWindowFullscreen()) - { - // Get the handle of the monitor that the specified window is in full screen on - monitor = glfwGetWindowMonitor(CORE.Window.handle); - - for (int i = 0; i < monitorCount; i++) - { - if (monitors[i] == monitor) - { - index = i; - break; - } - } - } - else - { - int x = 0; - int y = 0; - - glfwGetWindowPos(CORE.Window.handle, &x, &y); - - for (int i = 0; i < monitorCount; i++) - { - int mx = 0; - int my = 0; - - monitor = monitors[i]; - glfwGetMonitorPos(monitor, &mx, &my); - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - if (mode) - { - const int width = mode->width; - const int height = mode->height; - - if ((x >= mx) && - (x < (mx + width)) && - (y >= my) && - (y < (my + height))) - { - index = i; - break; - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - } - } -#endif - - return index; -} - -// Get selected monitor position -Vector2 GetMonitorPosition(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int x, y; - glfwGetMonitorPos(monitors[monitor], &x, &y); - - return (Vector2){ (float)x, (float)y }; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return (Vector2){ 0, 0 }; -} - -// Get selected monitor width (currently used by monitor) -int GetMonitorWidth(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - - if (mode) return mode->width; - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -#if defined(PLATFORM_ANDROID) - if (CORE.Android.app->window != NULL) - { - return ANativeWindow_getWidth(CORE.Android.app->window); - } -#endif - return 0; -} - -// Get selected monitor height (currently used by monitor) -int GetMonitorHeight(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - - if (mode) return mode->height; - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -#if defined(PLATFORM_ANDROID) - if (CORE.Android.app->window != NULL) - { - return ANativeWindow_getHeight(CORE.Android.app->window); - } -#endif - return 0; -} - -// Get selected monitor physical width in millimetres -int GetMonitorPhysicalWidth(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int physicalWidth; - glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL); - return physicalWidth; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return 0; -} - -// Get selected monitor physical height in millimetres -int GetMonitorPhysicalHeight(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int physicalHeight; - glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight); - return physicalHeight; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return 0; -} - -// Get selected monitor refresh rate -int GetMonitorRefreshRate(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]); - return vidmode->refreshRate; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -#if defined(PLATFORM_DRM) - if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0)) - { - return CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh; - } -#endif - return 0; -} - -// Get window position XY on monitor -Vector2 GetWindowPosition(void) -{ - int x = 0; - int y = 0; -#if defined(PLATFORM_DESKTOP) - glfwGetWindowPos(CORE.Window.handle, &x, &y); -#endif - 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(PLATFORM_DESKTOP) - float xdpi = 1.0; - float ydpi = 1.0; - Vector2 windowPos = GetWindowPosition(); - - int monitorCount = 0; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - // Check window monitor - for (int i = 0; i < monitorCount; i++) - { - glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi); - - int xpos, ypos, width, height; - glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height); - - if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) && - (windowPos.y >= ypos) && (windowPos.y < ypos + height)) - { - scale.x = xdpi; - scale.y = ydpi; - break; - } - } -#endif - - return scale; -} - -// Get the human-readable, UTF-8 encoded name of the selected monitor -const char *GetMonitorName(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - return glfwGetMonitorName(monitors[monitor]); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return ""; -} - -// Set clipboard text content -void SetClipboardText(const char *text) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetClipboardString(CORE.Window.handle, text); -#endif -#if defined(PLATFORM_WEB) - // 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); -#endif -} - -// Get clipboard text content -// NOTE: returned string is allocated and freed by GLFW -const char *GetClipboardText(void) -{ -#if defined(PLATFORM_DESKTOP) - return glfwGetClipboardString(CORE.Window.handle); -#endif -#if defined(PLATFORM_WEB) -/* - // 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; -#endif - return NULL; -} - // Enable waiting for events on EndDrawing(), no automatic event polling void EnableEventWaiting(void) { @@ -2213,62 +445,12 @@ void DisableEventWaiting(void) CORE.Window.eventWaiting = false; } -// Show mouse cursor -void ShowCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); -#endif - - CORE.Input.Mouse.cursorHidden = false; -} - -// Hides mouse cursor -void HideCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); -#endif - - CORE.Input.Mouse.cursorHidden = true; -} - // Check if cursor is not visible bool IsCursorHidden(void) { return CORE.Input.Mouse.cursorHidden; } -// Enables cursor (unlock cursor) -void EnableCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); -#endif -#if defined(PLATFORM_WEB) - emscripten_exit_pointerlock(); -#endif - // 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) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); -#endif -#if defined(PLATFORM_WEB) - emscripten_request_pointerlock("#canvas", 1); -#endif - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); - - CORE.Input.Mouse.cursorHidden = true; -} - // Check if cursor is on the current screen. bool IsCursorOnScreen(void) { @@ -2674,7 +856,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) // Unload VR stereo config properties void UnloadVrStereoConfig(VrStereoConfig config) { - //... + TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c"); } // Load shader from files and bind default locations @@ -2952,7 +1134,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - double aspect = ((double)width/(double)height); + float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; double top = camera.fovy/2.0; double right = top*aspect; @@ -2963,6 +1145,8 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh // 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 }; @@ -3047,26 +1231,6 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } -// Get elapsed time measure in seconds since InitTimer() -// NOTE: On PLATFORM_DESKTOP InitTimer() is called on InitWindow() -// NOTE: On PLATFORM_DESKTOP, timer is initialized on glfwInit() -double GetTime(void) -{ - double time = 0.0; -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - time = glfwGetTime(); // Elapsed time since glfwInit() -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - 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; -} - // 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. @@ -3078,63 +1242,9 @@ void SetConfigFlags(unsigned int flags) CORE.Window.flags |= flags; } -// NOTE TRACELOG() function is located in [utils.h] - -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - 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[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - -#if defined(PLATFORM_WEB) - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); -#endif - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - -// Get a random value between min and max (both included) -// 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 -int GetRandomValue(int min, int max) -{ - if (min > max) - { - int tmp = max; - max = min; - min = tmp; - } - - if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) - { - TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); - } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); -} +//---------------------------------------------------------------------------------- +// Module Functions Definition: FileSystem +//---------------------------------------------------------------------------------- // Check if the file exists bool FileExists(const char *fileName) @@ -3290,7 +1400,7 @@ const char *GetFileNameWithoutExt(const char *filePath) // 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) @@ -3298,7 +1408,7 @@ const char *GetDirectoryPath(const char *filePath) #else char separator = '/'; #endif -*/ + */ const char *lastSlash = NULL; static char dirPath[MAX_FILEPATH_LENGTH] = { 0 }; memset(dirPath, 0, MAX_FILEPATH_LENGTH); @@ -3325,8 +1435,10 @@ const char *GetDirectoryPath(const char *filePath) else { // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' - memcpy(dirPath + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0), filePath, strlen(filePath) - (strlen(lastSlash) - 1)); - dirPath[strlen(filePath) - strlen(lastSlash) + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0)] = '\0'; // Add '\0' manually + unsigned 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 } } @@ -3578,6 +1690,37 @@ long GetFileModTime(const char *fileName) return 0; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// Get a random value between min and max (both included) +// 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 +int GetRandomValue(int min, int max) +{ + if (min > max) + { + int tmp = max; + max = min; + min = tmp; + } + + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) + { + TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); + } + + return (rand()%(abs(max - min) + 1) + min); +} + +// Set the seed for the random number generator +void SetRandomSeed(unsigned int seed) +{ + srand(seed); +} + // Compress data (DEFLATE algorithm) unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize) { @@ -3588,12 +1731,12 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa #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 = dataSize*2;//sdefl_bound(dataSize); + 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 @@ -3719,67 +1862,31 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) return decodedData; } -// 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 PLATFORM_WEB - if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); - else - { -#if defined(PLATFORM_DESKTOP) - 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); -#endif -#if defined(PLATFORM_WEB) - emscripten_run_script(TextFormat("window.open('%s', '_blank')", url)); -#endif -#if defined(PLATFORM_ANDROID) - JNIEnv *env = NULL; - JavaVM *vm = CORE.Android.app->activity->vm; - (*vm)->AttachCurrentThread(vm, &env, NULL); - - jstring urlString = (*env)->NewStringUTF(env, url); - jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); - jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); - jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); - - jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); - jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); - jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); - jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); - jobject intent = (*env)->AllocObject(env, intentClass); - - (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); - jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); - jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); - - (*vm)->DetachCurrentThread(vm); -#endif - } -} - //---------------------------------------------------------------------------------- -// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions +// Module Functions Definition: Inputs //---------------------------------------------------------------------------------- + +// Platform-specific functions +//void SetExitKey(int key) +//const char *GetGamepadName(int gamepad) +//int GetGamepadAxisCount(int gamepad) +//int SetGamepadMappings(const char *mappings) +//int GetMouseX(void) +//int GetMouseY(void) +//Vector2 GetMousePosition(void) +//void SetMousePosition(int x, int y) +//float GetMouseWheelMove(void) +//void SetMouseCursor(int cursor) +//int GetTouchX(void) +//int GetTouchY(void) +//Vector2 GetTouchPosition(int index) +//void SwapScreenBuffer(void) +//void PollInputEvents(void) + // Check if a key has been pressed once bool IsKeyPressed(int key) { + bool pressed = false; if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) @@ -3886,17 +1993,6 @@ int GetCharPressed(void) return value; } -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ -#if !defined(PLATFORM_ANDROID) - CORE.Input.Keyboard.exitKey = key; -#endif -} - -// NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB) - // Check if a gamepad is available bool IsGamepadAvailable(int gamepad) { @@ -3907,40 +2003,6 @@ bool IsGamepadAvailable(int gamepad) return result; } -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - const char *name = NULL; - -#if defined(PLATFORM_DESKTOP) - if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); -#endif -#if defined(PLATFORM_DRM) - if (CORE.Input.Gamepad.ready[gamepad]) - { - ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); - name = CORE.Input.Gamepad.name[gamepad]; - } -#endif -#if defined(PLATFORM_WEB) - name = CORE.Input.Gamepad.name[gamepad]; -#endif - - return name; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ -#if defined(PLATFORM_DRM) - int axisCount = 0; - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); - CORE.Input.Gamepad.axisCount = axisCount; -#endif - - return CORE.Input.Gamepad.axisCount; -} - // Get axis movement vector for a gamepad float GetGamepadAxisMovement(int gamepad, int axis) { @@ -4002,18 +2064,6 @@ int GetGamepadButtonPressed(void) return CORE.Input.Gamepad.lastButtonPressed; } -// Set internal gamepad mappings -int SetGamepadMappings(const char *mappings) -{ - int result = 0; - -#if defined(PLATFORM_DESKTOP) - result = glfwUpdateGamepadMappings(mappings); -#endif - - return result; -} - // Check if a mouse button has been pressed once bool IsMouseButtonPressed(int button) { @@ -4066,44 +2116,6 @@ bool IsMouseButtonUp(int button) return up; } -// Get mouse position X -int GetMouseX(void) -{ -#if defined(PLATFORM_ANDROID) - return (int)CORE.Input.Touch.position[0].x; -#else - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -#endif -} - -// Get mouse position Y -int GetMouseY(void) -{ -#if defined(PLATFORM_ANDROID) - return (int)CORE.Input.Touch.position[0].y; -#else - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -#endif -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // TODO: Review touch position on PLATFORM_WEB - -#if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) - position = GetTouchPosition(0); -#else - // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned - 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; -#endif - - return position; -} - // Get mouse delta between frames Vector2 GetMouseDelta(void) { @@ -4115,18 +2127,6 @@ Vector2 GetMouseDelta(void) return delta; } -// 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; - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); -#endif -} - // Set mouse offset // NOTE: Useful when rendering to different size targets void SetMouseOffset(int offsetX, int offsetY) @@ -4141,19 +2141,6 @@ 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 !defined(PLATFORM_ANDROID) - 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; -#endif - - return result; -} - // Get mouse wheel movement X/Y as a vector Vector2 GetMouseWheelMoveV(void) { @@ -4164,61 +2151,6 @@ Vector2 GetMouseWheelMoveV(void) return result; } -// Set mouse cursor -// NOTE: This is a no-op on platforms other than PLATFORM_DESKTOP -void SetMouseCursor(int cursor) -{ -#if defined(PLATFORM_DESKTOP) - CORE.Input.Mouse.cursor = cursor; - if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); - else - { - // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values - glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); - } -#endif -} - -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) - return (int)CORE.Input.Touch.position[0].x; -#else // PLATFORM_DESKTOP, PLATFORM_DRM - return GetMouseX(); -#endif -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) - return (int)CORE.Input.Touch.position[0].y; -#else // PLATFORM_DESKTOP, PLATFORM_DRM - return GetMouseY(); -#endif -} - -// 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 defined(PLATFORM_DESKTOP) - // 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 - if (index == 0) position = GetMousePosition(); -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) - 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); -#endif - - return position; -} - // Get touch point identifier for given index int GetTouchPointId(int index) { @@ -4236,740 +2168,14 @@ int GetTouchPointCount(void) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) -{ - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ -#if defined(__APPLE__) - glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); -#endif - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } - - glfwDefaultWindowHints(); // Set default windows hints - //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits - //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits - //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits - //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits - //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits - //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window - //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API - //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 - 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 - else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window - - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) 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; - - // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization - if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; - - if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); - else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); - - if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); - else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); - - // NOTE: Some GLFW flags are not supported on HTML5 -#if defined(PLATFORM_DESKTOP) - if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) 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) - { - // 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); - #endif - } - else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); - - // Mouse passthrough - if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); - else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); -#endif - - if (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"); - glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 - } - - // 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. - - // Check selection OpenGL version - if (rlGetVersion() == RL_OPENGL_21) - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) - } - 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 -#if defined(__APPLE__) - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility -#else - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! -#endif - //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context - } - else if (rlGetVersion() == RL_OPENGL_43) - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // 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); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context -#endif - } - else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); -#if defined(PLATFORM_DESKTOP) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); -#else - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); -#endif - } - else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); -#if defined(PLATFORM_DESKTOP) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); -#else - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); -#endif - } - -#if defined(PLATFORM_DESKTOP) - // 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 - if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); -#endif - -#if defined(PLATFORM_DESKTOP) - // Find monitor resolution - GLFWmonitor *monitor = glfwGetPrimaryMonitor(); - if (!monitor) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); - return false; - } - - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - - 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; -#endif // PLATFORM_DESKTOP - -#if defined(PLATFORM_WEB) - // 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; -#endif // PLATFORM_WEB - - if (CORE.Window.fullscreen) - { - // 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. - CORE.Window.position.x = CORE.Window.display.width/4; - CORE.Window.position.y = CORE.Window.display.height/4; - } - 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; - } - - 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(glfwGetPrimaryMonitor(), &count); - - // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height - for (int i = 0; i < count; i++) - { - 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; - } - } - } - 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); - - CORE.Window.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... - //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - else - { -#if defined(PLATFORM_DESKTOP) - // If we are windowed fullscreen, ensures that window does not minimize when focus is lost - if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) - { - glfwWindowHint(GLFW_AUTO_ICONIFY, 0); - } -#endif - // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); - - if (CORE.Window.handle) - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - } - } - - if (!CORE.Window.handle) - { - glfwTerminate(); - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; - } - -// glfwCreateWindow title doesn't work with emscripten. -#if defined(PLATFORM_WEB) - emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); -#endif - - // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! -#if !defined(PLATFORM_WEB) - glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); -#endif - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); - - // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); - - glfwMakeContextCurrent(CORE.Window.handle); - -#if !defined(PLATFORM_WEB) - glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) - - glfwSwapInterval(0); // No V-Sync by default -#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, it doesn't need - // to be activated on web platforms since VSync is enforced there. -#if !defined(PLATFORM_WEB) - if (CORE.Window.flags & FLAG_VSYNC_HINT) - { - // WARNING: It seems to hit a critical render path in Intel HD Graphics - glfwSwapInterval(1); - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); - } -#endif - - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - -#if defined(PLATFORM_DESKTOP) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // 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__) - glfwGetFramebufferSize(CORE.Window.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); - - // 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; - 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); - -#endif // PLATFORM_DESKTOP || PLATFORM_WEB - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - CORE.Window.fullscreen = true; - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - -#if defined(PLATFORM_DRM) - CORE.Window.fd = -1; - CORE.Window.connector = NULL; - CORE.Window.modeIndex = -1; - CORE.Window.crtc = NULL; - CORE.Window.gbmDevice = NULL; - CORE.Window.gbmSurface = NULL; - CORE.Window.prevBO = NULL; - CORE.Window.prevFB = 0; - -#if defined(DEFAULT_GRAPHIC_DEVICE_DRM) - CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); -#else - TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card"); - CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) - - if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL)) - { - TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); - CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded - } - - if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL)) - { - TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0"); - CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) - } -#endif - if (-1 == CORE.Window.fd) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); - return false; - } - - drmModeRes *res = drmModeGetResources(CORE.Window.fd); - if (!res) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); - return false; - } - - 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(CORE.Window.fd, res->connectors[i]); - TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); - if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); - CORE.Window.connector = con; - break; - } - else - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); - drmModeFreeConnector(con); - } - } - - if (!CORE.Window.connector) - { - TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); - drmModeFreeResources(res); - return false; - } - - drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id); - if (!enc) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); - drmModeFreeResources(res); - return false; - } - - CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id); - if (!CORE.Window.crtc) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); - drmModeFreeEncoder(enc); - drmModeFreeResources(res); - return false; - } - - // 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..."); - - CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode); - - if (CORE.Window.modeIndex < 0) - { - TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); - drmModeFreeEncoder(enc); - drmModeFreeResources(res); - return false; - } - - 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 - CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); - - // If nothing found, try to find a nearly matching mode - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); - - // If nothing found, try to find an exactly matching mode including interlaced - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); - - // If nothing found, try to find a nearly matching mode including interlaced - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); - - // If nothing found, there is no suitable mode - if (CORE.Window.modeIndex < 0) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); - drmModeFreeEncoder(enc); - drmModeFreeResources(res); - return false; - } - - CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay; - CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay; - - TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name, - CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, - (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p', - CORE.Window.connector->modes[CORE.Window.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; - - CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd); - if (!CORE.Window.gbmDevice) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); - return false; - } - - CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, - CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!CORE.Window.gbmSurface) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); - return false; - } -#endif - - 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 -#if defined(PLATFORM_DRM) - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android! -#endif - 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) -#if defined(PLATFORM_DRM) - EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer) -#endif - //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 - }; - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - EGLint numConfigs = 0; - - // Get an EGL device connection -#if defined(PLATFORM_DRM) - CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice); -#else - CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); -#endif - if (CORE.Window.device == EGL_NO_DISPLAY) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; - } - - // Initialize the EGL device connection - if (eglInitialize(CORE.Window.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; - } - -#if defined(PLATFORM_DRM) - if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); - return false; - } - - 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 false; - } - - EGLint matchingNumConfigs = 0; - if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); - free(configs); - return false; - } - - 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(CORE.Window.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); - CORE.Window.config = configs[i]; - found = 1; - break; - } - } - - RL_FREE(configs); - - if (!found) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config"); - return false; - } -#else - // Get an appropriate EGL framebuffer configuration - eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs); -#endif - - // Set rendering API - eglBindAPI(EGL_OPENGL_ES_API); - - // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_CONTEXT) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; - } -#endif - - // Create an EGL window surface - //--------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) - 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(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - - // 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); - - ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size - - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); -#endif // PLATFORM_ANDROID - -#if defined(PLATFORM_DRM) - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); - if (EGL_NO_SURFACE == CORE.Window.surface) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); - return false; - } - - // 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); -#endif // PLATFORM_DRM - - // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(CORE.Window.device, 1); - - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; - } - else - { - 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); - } -#endif // PLATFORM_ANDROID || PLATFORM_DRM - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - rlLoadExtensions(glfwGetProcAddress); -#else - rlLoadExtensions(eglGetProcAddress); -#endif - - // 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); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(PLATFORM_ANDROID) - CORE.Window.ready = true; -#endif - - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - - return true; -} +// Platform-specific functions +//static bool InitGraphicsDevice(int width, int height) // Set viewport for a provided width and height -static void SetupViewport(int width, int height) +void SetupViewport(int width, int height) { CORE.Window.render.width = width; CORE.Window.render.height = height; @@ -4998,7 +2204,7 @@ static void SetupViewport(int width, int height) // 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) +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)) @@ -5075,7 +2281,7 @@ static void SetupFramebuffer(int width, int height) } // Initialize hi-resolution timer -static void InitTimer(void) +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. @@ -5142,323 +2348,6 @@ void WaitTime(double seconds) #endif } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSwapBuffers(CORE.Window.handle); -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); - -#if defined(PLATFORM_DRM) - - if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); - - struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface); - if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); - - uint32_t fb = 0; - int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.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(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); - - if (CORE.Window.prevFB) - { - result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); - } - - CORE.Window.prevFB = fb; - - if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); - - CORE.Window.prevBO = bo; - -#endif // PLATFORM_DRM -#endif // PLATFORM_ANDROID || PLATFORM_DRM -} - -// 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; - -#if defined(PLATFORM_DRM) - // 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(); - - // Register previous mouse states - CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; - CORE.Input.Mouse.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] = CORE.Input.Mouse.currentButtonStateEvdev[i]; - } - - // Register gamepads buttons events - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (CORE.Input.Gamepad.ready[i]) - { - // 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]; - } - } -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - // 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; -#endif - - // 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 PLATFORM_WEB 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 }; - -#if defined(PLATFORM_DESKTOP) - // Check if gamepads are ready - // NOTE: We do it here in case of disconnection - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; - else CORE.Input.Gamepad.ready[i] = false; - } - - // Register gamepads buttons events - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available - { - // 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]; - - // 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 - - const unsigned char *buttons = state.buttons; - - for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) - { - int button = -1; // GamepadButton enum values assigned - - switch (k) - { - case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - - case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - - case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; - case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - - case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - - case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break; - case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; - default: break; - } - - if (button != -1) // Check for valid button - { - if (buttons[k] == GLFW_PRESS) - { - CORE.Input.Gamepad.currentButtonState[i][button] = 1; - CORE.Input.Gamepad.lastButtonPressed = button; - } - else CORE.Input.Gamepad.currentButtonState[i][button] = 0; - } - } - - // Get current axis state - const float *axes = state.axes; - - for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) - { - 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); - - CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; - } - } - - CORE.Window.resizedLastFrame = false; - - 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) -#endif // PLATFORM_DESKTOP - -#if defined(PLATFORM_WEB) - CORE.Window.resizedLastFrame = false; -#endif // PLATFORM_WEB - -// Gamepad support using emscripten API -// NOTE: GLFW3 joystick functionality not available in web -#if defined(PLATFORM_WEB) - // 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; - - 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) // 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; - } - - //TRACELOGD("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++) - { - CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; - } - - CORE.Input.Gamepad.axisCount = gamepadState.numAxes; - } - } -#endif - -#if defined(PLATFORM_ANDROID) - // 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; - } - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Poll Events (registered events) - // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) - while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) - { - // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); - - // NOTE: Never close window, native activity is controlled by the system! - if (CORE.Android.app->destroyRequested != 0) - { - //CORE.Window.shouldClose = true; - //ANativeActivity_finish(CORE.Android.app->activity); - } - } -#endif - -#if defined(PLATFORM_DRM) && 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 (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); - - // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() - // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() -#endif -} - // 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 @@ -5549,1638 +2438,6 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath); } -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) -// 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 -static void WindowSizeCallback(GLFWwindow *window, int width, int height) -{ - // Reset viewport and projection matrix for new size - SetupViewport(width, height); - - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - CORE.Window.resizedLastFrame = true; - - if (IsWindowFullscreen()) return; - - // Set current screen size -#if defined(__APPLE__) - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; -#else - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - 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; - } -#endif - - // NOTE: Postprocessing texture is not scaled to new size -} - -// GLFW3 WindowIconify 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 !defined(PLATFORM_WEB) -// GLFW3 WindowMaximize 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 -} -#endif - -// GLFW3 WindowFocus 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 -} - -// 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 - - // 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; - -#if !defined(PLATFORM_WEB) - // 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; -#endif - - // Check if there is space available in the key queue - if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) - { - // Add character to the queue - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; - CORE.Input.Keyboard.keyPressedQueueCount++; - } - - // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_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); - - #if defined(PLATFORM_WEB) - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); - #endif - - 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 - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif -} - -// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) -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 - - // Check if there is space available in the queue - if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) - { - // Add character to the queue - CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; - CORE.Input.Keyboard.charPressedQueueCount++; - } -} - -// 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, - // but future releases may add more actions (i.e. GLFW_REPEAT) - CORE.Input.Mouse.currentButtonState[button] = action; - -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP - // Process mouse events as touches to be able to use mouse-gestures - GestureEvent gestureEvent = { 0 }; - - // Register touch actions - 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() - - // 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 -#if defined(PLATFORM_WEB) - // 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); -#else - ProcessGestureEvent(gestureEvent); -#endif - -#endif -} - -// GLFW3 Cursor Position Callback, runs on mouse move -static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) -{ - CORE.Input.Mouse.currentPosition.x = (float)x; - CORE.Input.Mouse.currentPosition.y = (float)y; - CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; - -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP - // 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 -} - -// GLFW3 Scrolling Callback, runs on mouse wheel -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) -{ - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; - else CORE.Input.Mouse.cursorOnScreen = false; -} - -// GLFW3 Window Drop Callback, runs when drop files into window -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)); - strcpy(CORE.Window.dropFilepaths[i], paths[i]); - } - } -} -#endif - -#if defined(PLATFORM_ANDROID) -// ANDROID: Process activity lifecycle commands -static void AndroidCommandCallback(struct android_app *app, int32_t cmd) -{ - switch (cmd) - { - case APP_CMD_START: - { - //rendering = true; - } break; - case APP_CMD_RESUME: break; - case APP_CMD_INIT_WINDOW: - { - if (app->window != NULL) - { - if (CORE.Android.contextRebindRequired) - { - // Reset screen scaling to full display size - EGLint displayFormat = 0; - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - - // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the - // context rebinding if the screen is scaled unless offsets are added. There's probably a more - // appropriate way to fix this - ANativeWindow_setBuffersGeometry(app->window, - CORE.Window.render.width + CORE.Window.renderOffset.x, - CORE.Window.render.height + CORE.Window.renderOffset.y, - displayFormat); - - // Recreate display surface and re-attach OpenGL context - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); - eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); - - CORE.Android.contextRebindRequired = false; - } - else - { - CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); - CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); - - // Initialize graphics device (display device and OpenGL context) - InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); - - // Initialize hi-res timer - InitTimer(); - - // Initialize random seed - srand((unsigned int)time(NULL)); - - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - Rectangle rec = GetFontDefault().recs[95]; - // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering - #if defined(SUPPORT_MODULE_RSHAPES) - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes - #endif - #endif - - // 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: - { - CORE.Android.appEnabled = true; - //ResumeMusicStream(); - } break; - case APP_CMD_PAUSE: break; - case APP_CMD_LOST_FOCUS: - { - CORE.Android.appEnabled = false; - //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 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 (CORE.Window.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - CORE.Android.contextRebindRequired = true; - } - // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' - // this means that the user has already called 'CloseWindow()' - - } break; - case APP_CMD_SAVE_STATE: break; - case APP_CMD_STOP: break; - case APP_CMD_DESTROY: break; - case APP_CMD_CONFIG_CHANGED: - { - //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); - //print_cur_config(CORE.Android.app); - - // Check screen orientation here! - } break; - default: break; - } -} - -static GamepadButton AndroidTranslateGamepadButton(int button) -{ - switch (button) - { - case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN; - case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; - case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT; - case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP; - case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1; - case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1; - case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2; - case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2; - case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB; - case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB; - case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT; - case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT; - case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE; - // On some (most?) gamepads dpad events are reported as axis motion instead - case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN; - case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT; - case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT; - case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP; - default: return GAMEPAD_BUTTON_UNKNOWN; - } -} - -// ANDROID: Get input events -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 - - 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)) - { - // For now we'll assume a single gamepad which we "detect" on its input event - CORE.Input.Gamepad.ready[0] = true; - - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_X, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_Y, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_Z, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_RZ, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; - - // dpad is reported as an axis on android - float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); - float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); - - if (dpadX == 1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; - } - else if (dpadX == -1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1; - } - else - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; - } - - if (dpadY == 1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; - } - else if (dpadY == -1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1; - } - else - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; - } - - return 1; // Handled gamepad axis motion - } - } - else if (type == AINPUT_EVENT_TYPE_KEY) - { - int32_t keycode = AKeyEvent_getKeyCode(event); - //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)) - { - // For now we'll assume a single gamepad which we "detect" on its input event - CORE.Input.Gamepad.ready[0] = true; - - GamepadButton button = AndroidTranslateGamepadButton(keycode); - - if (button == GAMEPAD_BUTTON_UNKNOWN) return 1; - - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) - { - CORE.Input.Gamepad.currentButtonState[0][button] = 1; - } - else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up - - return 1; // Handled gamepad button - } - - // Save current button and its state - // NOTE: Android key action is 0 for down and 1 for up - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) - { - CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; - CORE.Input.Keyboard.keyPressedQueueCount++; - } - else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1; - else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up - - if (keycode == AKEYCODE_POWER) - { - // 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. - // NOTE: AndroidManifest.xml must have - // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour - return 0; - } - else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU)) - { - // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS! - return 1; - } - else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN)) - { - // Set default OS behaviour - return 0; - } - - return 0; - } - - // Register touch points count - CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event); - - for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - // Register touch points id - CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i); - - // Register touch points position - CORE.Input.Touch.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; - } - - int32_t action = AMotionEvent_getAction(event); - unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; - -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID - GestureEvent gestureEvent = { 0 }; - - gestureEvent.pointCount = CORE.Input.Touch.pointCount; - - // Register touch actions - if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN; - else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP; - 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++) - { - gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; - gestureEvent.position[i] = CORE.Input.Touch.position[i]; - } - - // 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_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++) - { - CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; - CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; - } - - CORE.Input.Touch.pointCount--; - } - - // When all touchpoints are tapped and released really quickly, this event is generated - if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; - - if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; - else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; - - return 0; -} -#endif - -#if defined(PLATFORM_WEB) -// Register fullscreen change events -static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) -{ - // TODO: Implement EmscriptenFullscreenChangeCallback()? - - 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) -{ - // 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 < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; - else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; - - if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; - else if (height > 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 - - return 0; -} - -// Register mouse input events -static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) -{ - // This is only for registering mouse click events with emscripten and doesn't need to do anything - - return 1; // The event was consumed by the callback handler -} - -// Register connected/disconnected 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\"", - 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]); - */ - - if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) - { - CORE.Input.Gamepad.ready[gamepadEvent->index] = true; - sprintf(CORE.Input.Gamepad.name[gamepadEvent->index],"%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 -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("#canvas", &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; - } - -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB - 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); - - // 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 - - return 1; // The event was consumed by the callback handler -} -#endif - -#if defined(PLATFORM_DRM) -// 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, &CORE.Input.Keyboard.defaultSettings); - - // Reconfigure terminal with new settings - struct termios keyboardNewSettings = { 0 }; - keyboardNewSettings = CORE.Input.Keyboard.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 - CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.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, &CORE.Input.Keyboard.defaultMode); - - // In case of failure, it could mean a remote keyboard is used (SSH) - if (result < 0) TRACELOG(LOG_WARNING, "RPI: 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, &CORE.Input.Keyboard.defaultSettings); - - // Reconfigure keyboard to default mode - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); - ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); -} - -#if defined(SUPPORT_SSH_KEYBOARD_RPI) -// 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 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++; - } - } - - // Check exit key (same functionality as GLFW3 KeyCallback()) - if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; - -#if defined(SUPPORT_SCREEN_CAPTURE) - // Check screen capture key (raylib key: KEY_F12) - if (CORE.Input.Keyboard.currentKeyState[301] == 1) - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } -#endif -} -#endif // SUPPORT_SSH_KEYBOARD_RPI - -// Initialise user input from evdev(/dev/input/event) 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 - CORE.Input.Keyboard.fd = -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, "RPI: 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(bit)) & 1) - - struct input_absinfo absinfo = { 0 }; - 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 }; - bool hasAbs = false; - bool hasRel = false; - bool hasAbsMulti = false; - int freeWorkerId = -1; - int fd = -1; - - InputEventWorker *worker = NULL; - - // Open the device and allocate worker - //------------------------------------------------------------------------------------------------------- - // Find a free spot in the workers array - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].threadId == 0) - { - freeWorkerId = i; - break; - } - } - - // Select the free worker from array - if (freeWorkerId >= 0) - { - worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker - memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker - } - else - { - TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device); - return; - } - - // Open the device - fd = open(device, O_RDONLY | O_NONBLOCK); - if (fd < 0) - { - TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device); - return; - } - worker->fd = fd; - - // Grab number on the end of the devices name "event" - int devNum = 0; - char *ptrDevName = strrchr(device, 't'); - worker->eventNum = -1; - - if (ptrDevName != NULL) - { - if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum; - } - else worker->eventNum = 0; // TODO: HACK: Grab number for mouse0 device! - - // 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 - //------------------------------------------------------------------------------------------------------- - ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties - - // Check for absolute input devices - if (TEST_BIT(evBits, EV_ABS)) - { - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); - - // Check for absolute movement support (usually touchscreens, but also joysticks) - if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) - { - hasAbs = true; - - // Get the scaling values - ioctl(fd, EVIOCGABS(ABS_X), &absinfo); - worker->absRange.x = absinfo.minimum; - worker->absRange.width = absinfo.maximum - absinfo.minimum; - ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); - worker->absRange.y = absinfo.minimum; - worker->absRange.height = absinfo.maximum - absinfo.minimum; - } - - // Check for multiple absolute movement support (usually multitouch touchscreens) - if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) - { - hasAbsMulti = true; - - // Get the scaling values - ioctl(fd, EVIOCGABS(ABS_X), &absinfo); - worker->absRange.x = absinfo.minimum; - worker->absRange.width = absinfo.maximum - absinfo.minimum; - ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); - worker->absRange.y = absinfo.minimum; - worker->absRange.height = absinfo.maximum - absinfo.minimum; - } - } - - // Check for relative movement support (usually mouse) - if (TEST_BIT(evBits, EV_REL)) - { - ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); - - if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; - } - - // Check for button support to determine the device type(usually on all input devices) - if (TEST_BIT(evBits, EV_KEY)) - { - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); - - if (hasAbs || hasAbsMulti) - { - if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen - if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet - if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet - if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet - if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device - } - - if (hasRel) - { - if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse - if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse - } - - if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad - - if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard - } - //------------------------------------------------------------------------------------------------------- - - // Decide what to do with the device - //------------------------------------------------------------------------------------------------------- - if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) - { - // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a - // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate - // threads so that they don't drop events when the frame rate is slow. - TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); - CORE.Input.Keyboard.fd = worker->fd; - } - else if (worker->isTouch || worker->isMouse) - { - // Looks like an interesting device - TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device, - worker->isMouse? "mouse " : "", - worker->isMultitouch? "multitouch " : "", - worker->isTouch? "touchscreen " : "", - worker->isGamepad? "gamepad " : ""); - - // Create a thread for this device - int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); - if (error != 0) - { - TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error); - worker->threadId = 0; - close(fd); - } - -#if defined(USE_LAST_TOUCH_DEVICE) - // Find touchscreen with the highest index - int maxTouchNumber = -1; - - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; - } - - // Find touchscreens with lower indexes - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) - { - if (CORE.Input.eventWorker[i].threadId != 0) - { - TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); - pthread_cancel(CORE.Input.eventWorker[i].threadId); - close(CORE.Input.eventWorker[i].fd); - } - } - } -#endif - } - else close(fd); // We are not interested in this device - //------------------------------------------------------------------------------------------------------- -} - -static void PollKeyboardEvents(void) -{ - // Scancode to keycode mapping for US keyboards - // 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 - static const int keymapUS[] = { - 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 - }; - - int fd = CORE.Input.Keyboard.fd; - 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)) - { - // Button parsing - if (event.type == EV_KEY) - { -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - // Change keyboard mode to events - CORE.Input.Keyboard.evtMode = true; -#endif - // Keyboard button parsing - if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 - { - keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the appropriate keycode - - // Make sure we got a valid keycode - if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) - { - // 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)? 1 : 0; - if (event.value >= 1) - { - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed - CORE.Input.Keyboard.keyPressedQueueCount++; - } - - #if defined(SUPPORT_SCREEN_CAPTURE) - // Check screen capture key (raylib key: KEY_F12) - if (CORE.Input.Keyboard.currentKeyState[301] == 1) - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - #endif - - if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; - - TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", event.value == 0 ? "UP":"DOWN", event.code, keycode); - } - } - } - } -} - -// Input device events reading thread -static void *EventThread(void *arg) -{ - struct input_event event = { 0 }; - InputEventWorker *worker = (InputEventWorker *)arg; - - int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE - bool gestureUpdate = false; // Flag to note gestures require to update - - while (!CORE.Window.shouldClose) - { - // Try to read data from the device and only continue if successful - while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) - { - // Relative movement parsing - if (event.type == EV_REL) - { - if (event.code == REL_X) - { - CORE.Input.Mouse.currentPosition.x += event.value; - CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == REL_Y) - { - CORE.Input.Mouse.currentPosition.y += event.value; - CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == REL_WHEEL) CORE.Input.Mouse.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 - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == ABS_Y) - { - CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - // Multitouch movement - if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events - - if (event.code == ABS_MT_POSITION_X) - { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - } - - if (event.code == ABS_MT_POSITION_Y) - { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - } - - if (event.code == ABS_MT_TRACKING_ID) - { - if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS)) - { - // Touch has ended for this point - CORE.Input.Touch.position[worker->touchSlot].x = -1; - CORE.Input.Touch.position[worker->touchSlot].y = -1; - } - } - - // Touchscreen tap - if (event.code == ABS_PRESSURE) - { - int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; - - if (!event.value && previousMouseLeftButtonState) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; - - touchAction = 0; // TOUCH_ACTION_UP - gestureUpdate = true; - } - - if (event.value && !previousMouseLeftButtonState) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; - - touchAction = 1; // TOUCH_ACTION_DOWN - gestureUpdate = true; - } - } - - } - - // Button parsing - if (event.type == EV_KEY) - { - // Mouse button parsing - if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; - - if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN - else touchAction = 0; // TOUCH_ACTION_UP - gestureUpdate = true; - } - - if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; - if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; - if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; - if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; - if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; - if (event.code == BTN_BACK) CORE.Input.Mouse.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) // PLATFORM_DRM - if (gestureUpdate) - { - 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); - } -#endif - } - - WaitTime(0.005); // Sleep for 5ms to avoid hogging CPU time - } - - close(worker->fd); - - return NULL; -} - -// Initialize gamepad system -static void InitGamepad(void) -{ - char gamepadDev[128] = { 0 }; - - for (int i = 0; i < MAX_GAMEPADS; i++) - { - sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); - - if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) - { - // NOTE: Only show message for first gamepad - if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); - } - else - { - CORE.Input.Gamepad.ready[i] = true; - - // NOTE: Only create one thread - if (i == 0) - { - int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); - - if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); - else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); - } - } - } -} - -// Process Gamepad (/dev/input/js0) -static void *GamepadThread(void *arg) -{ - #define JS_EVENT_BUTTON 0x01 // Button pressed/released - #define JS_EVENT_AXIS 0x02 // Joystick axis moved - #define JS_EVENT_INIT 0x80 // Initial state of device - - struct js_event { - unsigned int time; // event timestamp in milliseconds - short value; // event value - unsigned char type; // event type - unsigned char number; // event axis/button number - }; - - // Read gamepad event - struct js_event gamepadEvent = { 0 }; - - while (!CORE.Window.shouldClose) - { - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) - { - gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events - - // Process gamepad events by type - if (gamepadEvent.type == JS_EVENT_BUTTON) - { - //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value); - - if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS) - { - // 1 - button pressed, 0 - button released - CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value; - - if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number; - else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - } - } - else if (gamepadEvent.type == JS_EVENT_AXIS) - { - //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value); - - if (gamepadEvent.number < MAX_GAMEPAD_AXIS) - { - // NOTE: Scaling of gamepadEvent.value to get values between -1..1 - CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768; - } - } - } - else WaitTime(0.001); // Sleep for 1 ms to avoid hogging CPU time - } - } - - return NULL; -} -#endif // PLATFORM_DRM - -#if defined(PLATFORM_DRM) -// 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(&CORE.Window.crtc->mode, &CORE.Window.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 < CORE.Window.connector->count_modes; i++) - { - const drmModeModeInfo *const mode = &CORE.Window.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 < CORE.Window.connector->count_modes; i++) - { - const drmModeModeInfo *const mode = &CORE.Window.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(CORE.Window.connector->modes[nearestIndex].hdisplay - width); - const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); - const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps); - - if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { - nearestIndex = i; - } - } - - return nearestIndex; -} -#endif - #if defined(SUPPORT_EVENTS_AUTOMATION) // NOTE: Loading happens over AutomationEvent *events // TODO: This system should probably be redesigned diff --git a/src/rcore.h b/src/rcore.h new file mode 100644 index 000000000..5d9e3bd74 --- /dev/null +++ b/src/rcore.h @@ -0,0 +1,318 @@ +/********************************************************************************************** +* +* rcore - Common types and globals (all platforms) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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. +* +**********************************************************************************************/ + +#ifndef RCORE_H +#define RCORE_H + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] + +#include "utils.h" // Required for: TRACELOG() macros + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) + #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management + // NOTE: GLFW3 already includes gl.h (OpenGL) headers +#endif + +#if defined(PLATFORM_ANDROID) + #include // Native platform windowing system interface + //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) +#endif + +#if defined(PLATFORM_DRM) + + #include // POSIX file control definitions - open(), creat(), fcntl() + #include // POSIX standard function definitions - read(), close(), STDIN_FILENO + #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() + #include // POSIX threads management (inputs reading) + #include // POSIX directory browsing + + #include // Required for: ioctl() - UNIX System call for device-specific input/output operations + #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition + #include // Linux: Keycodes constants definition (KEY_A, ...) + #include // Linux: Joystick support library + + #include // Generic Buffer Management (native platform for EGL on DRM) + #include // Direct Rendering Manager user-level library interface + #include // Direct Rendering Manager mode setting (KMS) interface + + #include "EGL/egl.h" // Native platform windowing system interface + #include "EGL/eglext.h" // EGL extensions + + typedef struct + { + pthread_t threadId; // Event reading thread id + int fd; // File descriptor to the device it is assigned to + int eventNum; // Number of 'event' device + 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 isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons + } InputEventWorker; + +#endif + +// TODO: PROVIDE A HEADER TO BE USED BY ALL THE rcore_* IMPLEMENTATIONS + +#include "raylib.h" + +#include "rlgl.h" + +#define RAYMATH_IMPLEMENTATION +#include "raymath.h" + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#if defined(PLATFORM_DRM) + #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number + + #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) + #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events +#endif + +#ifndef MAX_FILEPATH_CAPACITY + #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath +#endif +#ifndef MAX_FILEPATH_LENGTH + #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) +#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_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 + +// 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)) + +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#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 + +//---------------------------------------------------------------------------------- +// 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 { +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + GLFWwindow *handle; // GLFW window handle (graphic device) +#endif +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) + 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) +#endif // PLATFORM_DRM + 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 +#endif + 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 + + 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) + Size windowMin; // Window minimum width and height + Size windowMax; // Window maximum width and height + 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; +#if defined(PLATFORM_ANDROID) + struct { + bool appEnabled; // Flag to detect if app is active ** = true + struct android_app *app; // Android activity + struct android_poll_source *source; // Android events polling source + bool contextRebindRequired; // Used to know context rebind required + } Android; +#endif + struct { + const char *basePath; // Base path for data storage + } Storage; + struct { +#if defined(PLATFORM_DRM) + InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" +#endif + 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 + +#if defined(PLATFORM_DRM) + int defaultMode; // Default keyboard mode +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + bool evtMode; // Keyboard in event mode +#endif + int defaultFileFlags; // Default IO file flags + struct termios defaultSettings; // Default keyboard settings + int fd; // File descriptor for the evdev keyboard +#endif + } 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 +#if defined(PLATFORM_DRM) + 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 +#endif + } 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; // 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 +#if defined(PLATFORM_DRM) + pthread_t threadId; // Gamepad reading thread id + int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor +#endif + } 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 +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) + unsigned long long int base; // Base time measure for hi-res timer +#endif + unsigned int frameCounter; // Frame counter + } Time; +} CoreData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; + +#endif diff --git a/src/rcore_android.c b/src/rcore_android.c new file mode 100644 index 000000000..61d729a1c --- /dev/null +++ b/src/rcore_android.c @@ -0,0 +1,1267 @@ +/********************************************************************************************** +* +* rcore_android - Functions to manage window, graphics device and inputs +* +* PLATFORM: ANDROID +* - Android (ARM, ARM64) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_ANDROID_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_ANDROID -not used- +* +* DEPENDENCIES: +* Android NDK - Provides C API to access Android functionality +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) +#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others +#include // Required for: android_app struct and activity management +#include // Required for: JNIEnv and JavaVM [Used in OpenURL()] + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands +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 + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// To allow easier porting to android, we allow the user to define a +// main function which we call from android_main, defined by ourselves +extern int main(int argc, char *argv[]); + +// Android main function +void android_main(struct android_app *app) +{ + char arg0[] = "raylib"; // NOTE: argv[] are mutable + CORE.Android.app = app; + + // NOTE: Return from main is ignored + (void)main(1, (char *[]) { arg0, NULL }); + + // Request to end the native activity + ANativeActivity_finish(app->activity); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Waiting for application events before complete finishing + while (!app->destroyRequested) + { + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) + { + if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); + } + } +} + +// NOTE: Add this to header (if apps really need it) +struct android_app *GetAndroidApp(void) +{ + return CORE.Android.app; +} + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + 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 + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + 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 = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + + // Set desired windows flags before initializing anything + ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + + int orientation = AConfiguration_getOrientation(CORE.Android.app->config); + + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); + + // TODO: Automatic orientation doesn't seem to work + if (width <= height) + { + AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); + } + else + { + AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); + } + + //AConfiguration_getDensity(CORE.Android.app->config); + //AConfiguration_getKeyboard(CORE.Android.app->config); + //AConfiguration_getScreenSize(CORE.Android.app->config); + //AConfiguration_getScreenLong(CORE.Android.app->config); + + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + CORE.Android.app->onAppCmd = AndroidCommandCallback; + + // Initialize input events system + CORE.Android.app->onInputEvent = AndroidInputCallback; + + // Initialize assets manager + InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); + + // Initialize base path for storage + CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; + + TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Wait for window to be initialized (display and context) + while (!CORE.Window.ready) + { + // Process events loop + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + { + // Process this event + if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + + // NOTE: Never close window, native activity is controlled by the system! + //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; + } + } +} + +// 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 + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + // Close surface, context and display + if (CORE.Window.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + if (CORE.Window.context != EGL_NO_CONTEXT) + { + eglDestroyContext(CORE.Window.device, CORE.Window.context); + CORE.Window.context = EGL_NO_CONTEXT; + } + + eglTerminate(CORE.Window.device); + CORE.Window.device = EGL_NO_DISPLAY; + } + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return CORE.Android.appEnabled; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_ANDROID"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_ANDROID"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_ANDROID"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_ANDROID"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_ANDROID"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_ANDROID"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_ANDROID"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_ANDROID"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_ANDROID"); +} + +// 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 PLATFORM_ANDROID"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_ANDROID"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_ANDROID"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_ANDROID"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_ANDROID"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_ANDROID"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_ANDROID"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_ANDROID"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_ANDROID"); + 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); + + 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; +} + +// 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; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_ANDROID + 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[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// 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 PLATFORM_ANDROID + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + JNIEnv *env = NULL; + JavaVM *vm = CORE.Android.app->activity->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + + jstring urlString = (*env)->NewStringUTF(env, url); + jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); + jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); + jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); + + jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); + jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); + jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); + jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); + jobject intent = (*env)->AllocObject(env, intentClass); + + (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); + jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); + jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); + (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); + + (*vm)->DetachCurrentThread(vm); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +void SetExitKey(int key) +{ + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_ANDROID"); +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_ANDROID"); + return NULL; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + return GetTouchPosition(0); +} + +// 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; +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_ANDROID"); + return 0.0f; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_ANDROID"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// 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; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(CORE.Window.device, CORE.Window.surface); +} + +// 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 PLATFORM_ANDROID 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; + } + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Poll Events (registered events) + // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) + while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + { + // Process this event + if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + + // NOTE: Never close window, native activity is controlled by the system! + if (CORE.Android.app->destroyRequested != 0) + { + //CORE.Window.shouldClose = true; + //ANativeActivity_finish(CORE.Android.app->activity); + } + } +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + 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_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 + CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (CORE.Window.device == EGL_NO_DISPLAY) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Initialize the EGL device connection + if (eglInitialize(CORE.Window.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(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs); + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); + if (CORE.Window.context == EGL_NO_CONTEXT) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); + return false; + } + + // 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(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // 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); + + ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size + + CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); + + // There must be at least one frame displayed before the buffers are swapped + //eglSwapInterval(CORE.Window.device, 1); + + if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + 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); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // 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); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + CORE.Window.ready = true; + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// ANDROID: Process activity lifecycle commands +static void AndroidCommandCallback(struct android_app *app, int32_t cmd) +{ + switch (cmd) + { + case APP_CMD_START: + { + //rendering = true; + } break; + case APP_CMD_RESUME: break; + case APP_CMD_INIT_WINDOW: + { + if (app->window != NULL) + { + if (CORE.Android.contextRebindRequired) + { + // Reset screen scaling to full display size + EGLint displayFormat = 0; + eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the + // context rebinding if the screen is scaled unless offsets are added. There's probably a more + // appropriate way to fix this + ANativeWindow_setBuffersGeometry(app->window, + CORE.Window.render.width + CORE.Window.renderOffset.x, + CORE.Window.render.height + CORE.Window.renderOffset.y, + displayFormat); + + // Recreate display surface and re-attach OpenGL context + CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); + eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); + + CORE.Android.contextRebindRequired = false; + } + else + { + CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); + CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); + + // Initialize graphics device (display device and OpenGL context) + InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + Rectangle rec = GetFontDefault().recs[95]; + // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + #if defined(SUPPORT_MODULE_RSHAPES) + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes + #endif + #endif + + // 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: + { + CORE.Android.appEnabled = true; + //ResumeMusicStream(); + } break; + case APP_CMD_PAUSE: break; + case APP_CMD_LOST_FOCUS: + { + CORE.Android.appEnabled = false; + //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 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 (CORE.Window.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + CORE.Android.contextRebindRequired = true; + } + // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' + // this means that the user has already called 'CloseWindow()' + + } break; + case APP_CMD_SAVE_STATE: break; + case APP_CMD_STOP: break; + case APP_CMD_DESTROY: break; + case APP_CMD_CONFIG_CHANGED: + { + //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); + //print_cur_config(CORE.Android.app); + + // Check screen orientation here! + } break; + default: break; + } +} + +// ANDROID: Map Android gamepad button to raylib gamepad button +static GamepadButton AndroidTranslateGamepadButton(int button) +{ + switch (button) + { + case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN; + case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; + case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT; + case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP; + case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1; + case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1; + case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2; + case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB; + case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB; + case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT; + case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT; + case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE; + // On some (most?) gamepads dpad events are reported as axis motion instead + case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN; + case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT; + case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT; + case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP; + default: return GAMEPAD_BUTTON_UNKNOWN; + } +} + +// ANDROID: Get input events +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 + + 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)) + { + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; + + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_X, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Y, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Z, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_RZ, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; + + // dpad is reported as an axis on android + float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); + float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); + + if (dpadX == 1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; + } + else if (dpadX == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; + } + + if (dpadY == 1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + else if (dpadY == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + + return 1; // Handled gamepad axis motion + } + } + else if (type == AINPUT_EVENT_TYPE_KEY) + { + int32_t keycode = AKeyEvent_getKeyCode(event); + //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)) + { + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; + + GamepadButton button = AndroidTranslateGamepadButton(keycode); + + if (button == GAMEPAD_BUTTON_UNKNOWN) return 1; + + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Gamepad.currentButtonState[0][button] = 1; + } + else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up + + return 1; // Handled gamepad button + } + + // Save current button and its state + // NOTE: Android key action is 0 for down and 1 for up + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1; + else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up + + if (keycode == AKEYCODE_POWER) + { + // 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. + // NOTE: AndroidManifest.xml must have + // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour + return 0; + } + else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU)) + { + // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS! + return 1; + } + else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN)) + { + // Set default OS behaviour + return 0; + } + + return 0; + } + + // Register touch points count + CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event); + + for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // Register touch points id + CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i); + + // Register touch points position + CORE.Input.Touch.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; + } + + int32_t action = AMotionEvent_getAction(event); + unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; + +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID + GestureEvent gestureEvent = { 0 }; + + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + + // Register touch actions + if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP; + 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++) + { + gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + } + + // 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_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++) + { + CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; + CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; + } + + CORE.Input.Touch.pointCount--; + } + + // When all touchpoints are tapped and released really quickly, this event is generated + if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; + + if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; + else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + + return 0; +} + +// EOF diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c new file mode 100644 index 000000000..f8c6b0e2c --- /dev/null +++ b/src/rcore_desktop.c @@ -0,0 +1,2074 @@ +/********************************************************************************************** +* +* rcore_desktop - Functions to manage window, graphics device and inputs +* +* PLATFORM: DESKTOP +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - OSX/macOS +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_DESKTOP_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_DESKTOP -not used- +* +* DEPENDENCIES: +* rglfw - Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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. +* +**********************************************************************************************/ + +#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) +#include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management + // NOTE: GLFW3 already includes gl.h (OpenGL) headers + +// Support retrieving native window handlers +#if defined(_WIN32) + typedef void *PVOID; + typedef PVOID HANDLE; + typedef HANDLE HWND; + #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); + #endif +#endif +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) + #include // 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 + //#define GLFW_EXPOSE_NATIVE_MIR + #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window() +#endif +#if defined(__APPLE__) + #include // Required for: usleep() + + //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition + void *glfwGetCocoaWindow(GLFWwindow* handle); + #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +// Error callback event +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 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 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 + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + 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 + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE 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 = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && 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 +#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 +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// 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 + + glfwDestroyWindow(CORE.Window.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +// NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) + { + // While window minimized, stop loop execution + while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); + + CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); + + // Reset close status for next frame + glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); + + return CORE.Window.shouldClose; + } + else return true; +} + +// 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; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + if (!CORE.Window.fullscreen) + { + // Store previous window position (in case we exit fullscreen) + glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); + + 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) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); + + CORE.Window.fullscreen = false; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + + glfwSetWindowMonitor(CORE.Window.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; + + glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + + } + else + { + CORE.Window.fullscreen = false; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + + glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + + // 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); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + glfwMaximizeWindow(CORE.Window.handle); + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; + } +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + // NOTE: Following function launches callback that sets appropriate flag! + glfwIconifyWindow(CORE.Window.handle); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + // Restores the specified window if it was previously iconified (minimized) or maximized + glfwRestoreWindow(CORE.Window.handle); + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + } +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it + bool wasOnFullscreen = false; + if (CORE.Window.fullscreen) + { + CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + wasOnFullscreen = true; + } + + const int monitor = GetCurrentMonitor(); + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) + { + if (!IsWindowState(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) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + CORE.Window.previousScreen = CORE.Window.screen; + + // Set undecorated and topmost modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + + // 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; + + // Set screen position and size + glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); + glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + // Remove topmost and undecorated modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + 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(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +} + +// Set window configuration state using flags +void SetWindowState(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 (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0)) + { + glfwSwapInterval(1); + 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)) + { + ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_FULLSCREEN_MODE + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); + 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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + 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)) + { + glfwHideWindow(CORE.Window.handle); + 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)) + { + //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)) + { + //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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + 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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + 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)) + { + 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)) + { + 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)) + { + 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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + 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)) + { + 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)) + { + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); + } +} + +// 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 (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0)) + { + glfwSwapInterval(0); + 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)) + { + 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)) + { + 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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; + } + + // State change: FLAG_WINDOW_HIDDEN + if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) + { + glfwShowWindow(CORE.Window.handle); + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; + } + + // State change: FLAG_WINDOW_MINIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) + { + 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)) + { + 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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + } + + // State change: FLAG_WINDOW_UNFOCUSED + if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; + } + + // State change: FLAG_WINDOW_TOPMOST + if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + 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)) + { + 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)) + { + 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)) + { + 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)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + 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)) + { + 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)) + { + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); + } +} + +// Set icon for window +// NOTE 1: Image must be in RGBA format, 8bit per channel +// NOTE 2: Image is scaled by the OS for all required sizes +void SetWindowIcon(Image image) +{ + if (image.data == NULL) + { + // Revert to the default window icon, pass in an empty image array + glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + } + else + { + if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + GLFWimage icon[1] = { 0 }; + + icon[0].width = image.width; + icon[0].height = image.height; + icon[0].pixels = (unsigned char *)image.data; + + // NOTE 1: We only support one image icon + // NOTE 2: The specified image data is copied before this function returns + glfwSetWindowIcon(CORE.Window.handle, 1, icon); + } + else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); + } +} + +// Set icon for window, multiple images +// NOTE 1: Images must be in RGBA format, 8bit per channel +// NOTE 2: The multiple images are used depending on provided sizes +// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 +void SetWindowIcons(Image *images, int count) +{ + if ((images == NULL) || (count <= 0)) + { + // Revert to the default window icon, pass in an empty image array + glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + } + else + { + int valid = 0; + GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); + + for (int i = 0; i < count; i++) + { + if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + icons[valid].width = images[i].width; + icons[valid].height = images[i].height; + icons[valid].pixels = (unsigned char *)images[i].data; + + valid++; + } + else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); + } + // NOTE: Images data is copied internally before this function returns + glfwSetWindowIcon(CORE.Window.handle, valid, icons); + + RL_FREE(icons); + } +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; + glfwSetWindowTitle(CORE.Window.handle, title); +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + glfwSetWindowPos(CORE.Window.handle, x, y); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + if (CORE.Window.fullscreen) + { + TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + } + else + { + TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + + const int screenWidth = CORE.Window.screen.width; + const int screenHeight = CORE.Window.screen.height; + int monitorWorkareaX = 0; + int monitorWorkareaY = 0; + int monitorWorkareaWidth = 0; + int monitorWorkareaHeight = 0; + glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); + + // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it + if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); + else + { + const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); + const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); + glfwSetWindowPos(CORE.Window.handle, x, y); + } + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + glfwSetWindowSize(CORE.Window.handle, width, 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; + glfwSetWindowOpacity(CORE.Window.handle, opacity); +} + +// Set window focused +void SetWindowFocused(void) +{ + glfwFocusWindow(CORE.Window.handle); +} + +// Get native window handle +void *GetWindowHandle(void) +{ +#if defined(_WIN32) + // NOTE: Returned handle is: void *HWND (windows.h) + return glfwGetWin32Window(CORE.Window.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(CORE.Window.handle); + //return NULL; // TODO: Find a way to return value... cast to void *? + return (void *)CORE.Window.handle; +#endif +#if defined(__APPLE__) + // NOTE: Returned handle is: (objc_object *) + return (void *)glfwGetCocoaWindow(CORE.Window.handle); +#endif + + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + int monitorCount = 0; + + glfwGetMonitors(&monitorCount); + + return monitorCount; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + int index = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor *monitor = NULL; + + if (monitorCount >= 1) + { + if (IsWindowFullscreen()) + { + // Get the handle of the monitor that the specified window is in full screen on + monitor = glfwGetWindowMonitor(CORE.Window.handle); + + for (int i = 0; i < monitorCount; i++) + { + if (monitors[i] == monitor) + { + index = i; + break; + } + } + } + else + { + int x = 0; + int y = 0; + + glfwGetWindowPos(CORE.Window.handle, &x, &y); + + for (int i = 0; i < monitorCount; i++) + { + int mx = 0; + int my = 0; + + monitor = monitors[i]; + glfwGetMonitorPos(monitor, &mx, &my); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + + if (mode) + { + const int width = mode->width; + const int height = mode->height; + + if ((x >= mx) && + (x < (mx + width)) && + (y >= my) && + (y < (my + height))) + { + index = i; + break; + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + } + } + + return index; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int x, y; + glfwGetMonitorPos(monitors[monitor], &x, &y); + + return (Vector2){ (float)x, (float)y }; + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + int width = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) width = mode->width; + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return width; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + int height = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) height = mode->height; + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return height; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + int width = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL); + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return width; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + int height = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height); + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return height; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + int refresh = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]); + refresh = vidmode->refreshRate; + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return refresh; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + return glfwGetMonitorName(monitors[monitor]); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + int x = 0; + int y = 0; + + glfwGetWindowPos(CORE.Window.handle, &x, &y); + + return (Vector2){ (float)x, (float)y }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + float xdpi = 1.0; + float ydpi = 1.0; + Vector2 scale = { 1.0f, 1.0f }; + Vector2 windowPos = GetWindowPosition(); + + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + // Check window monitor + for (int i = 0; i < monitorCount; i++) + { + glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi); + + int xpos, ypos, width, height; + glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height); + + if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) && + (windowPos.y >= ypos) && (windowPos.y < ypos + height)) + { + scale.x = xdpi; + scale.y = ydpi; + break; + } + } + + return scale; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + glfwSetClipboardString(CORE.Window.handle, text); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + return glfwGetClipboardString(CORE.Window.handle); +} + +// Show mouse cursor +void ShowCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + // 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) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = glfwGetTime(); // Elapsed time since glfwInit() + return time; +} + +// Takes a screenshot of current screen (saved a .png) +// WARNING: This function requires [rtextures] module functionality +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[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// 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 + { + 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); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + return glfwUpdateGamepadMappings(mappings); +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// 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; +} + +// 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; + + // NOTE: emscripten not implemented + glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); +} + +// 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; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + CORE.Input.Mouse.cursor = cursor; + if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); + else + { + // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values + glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); + } +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return GetMouseX(); +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return GetMouseY(); +} + +// 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 }; + + // 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 + if (index == 0) position = GetMousePosition(); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(CORE.Window.handle); +} + +// 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; + // 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 PLATFORM_WEB 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 }; + + // Check if gamepads are ready + // NOTE: We do it here in case of disconnection + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; + else CORE.Input.Gamepad.ready[i] = false; + } + + // Register gamepads buttons events + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available + { + // 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]; + + // 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 + + const unsigned char *buttons = state.buttons; + + for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) + { + int button = -1; // GamepadButton enum values assigned + + switch (k) + { + case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + + case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; + case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + + case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; + default: break; + } + + if (button != -1) // Check for valid button + { + if (buttons[k] == GLFW_PRESS) + { + CORE.Input.Gamepad.currentButtonState[i][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + } + else CORE.Input.Gamepad.currentButtonState[i][button] = 0; + } + } + + // Get current axis state + const float *axes = state.axes; + + for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) + { + 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); + + CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; + } + } + + CORE.Window.resizedLastFrame = false; + + 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) +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ +#if defined(__APPLE__) + glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); +#endif + + if (!glfwInit()) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); + return false; + } + + glfwDefaultWindowHints(); // Set default windows hints + //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits + //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits + //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits + //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits + //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits + //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window + //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API + //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 + 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 + else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window + + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) 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; + + // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); + else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); + + if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) 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 + else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // 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); +#endif + } + else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); + + // Mouse passthrough + if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + + if (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"); + glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 + } + + // 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. + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) + } + 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 +#if defined(__APPLE__) + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility +#else + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! +#endif + //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context + } + else if (rlGetVersion() == RL_OPENGL_43) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // 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); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + 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. + // REF: https://github.com/raysan5/raylib/issues/1554 + if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); + + // Find monitor resolution + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + if (!monitor) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); + return false; + } + + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + + 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; + + if (CORE.Window.fullscreen) + { + // 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. + CORE.Window.position.x = CORE.Window.display.width/4; + CORE.Window.position.y = CORE.Window.display.height/4; + } + 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; + } + + 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(glfwGetPrimaryMonitor(), &count); + + // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height + for (int i = 0; i < count; i++) + { + 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; + } + } + } + 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); + + CORE.Window.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... + //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + else + { + // If we are windowed fullscreen, ensures that window does not minimize when focus is lost + if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + { + glfwWindowHint(GLFW_AUTO_ICONIFY, 0); + } + + // No-fullscreen window creation + CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + + if (CORE.Window.handle) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + } + } + + if (!CORE.Window.handle) + { + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); + return false; + } + + // Set window callback events + glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); + glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(CORE.Window.handle, KeyCallback); + glfwSetCharCallback(CORE.Window.handle, CharCallback); + glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); + glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + + glfwMakeContextCurrent(CORE.Window.handle); + + glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + + 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) + { + // WARNING: It seems to hit a critical render path in Intel HD Graphics + glfwSwapInterval(1); + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + } + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // 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__) + glfwGetFramebufferSize(CORE.Window.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); + + // Mouse input scaling for the new screen size + SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); +#endif + } + + 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); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + + rlLoadExtensions(glfwGetProcAddress); + + // 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); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// 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 +static void WindowSizeCallback(GLFWwindow *window, int width, int height) +{ + // Reset viewport and projection matrix for new size + SetupViewport(width, height); + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return; + + // Set current screen size +#if defined(__APPLE__) + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; +#else + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + 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; + } +#endif + + // NOTE: Postprocessing texture is not scaled to new size +} + +// GLFW3 WindowIconify 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 +} + +// GLFW3 WindowMaximize 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 +} + +// GLFW3 WindowFocus 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 +} + +// GLFW3 Window Drop Callback, runs when drop files into window +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)); + strcpy(CORE.Window.dropFilepaths[i], paths[i]); + } + } +} + +// 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 + + // 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; + + // 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; + + // Check if there is space available in the key queue + if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) + { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + // Check the exit key to set close window + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + +#if defined(SUPPORT_SCREEN_CAPTURE) + if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) + { +#if defined(SUPPORT_GIF_RECORDING) + if (mods & GLFW_MOD_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 + +#if defined(SUPPORT_EVENTS_AUTOMATION) + if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) + { + eventsRecording = !eventsRecording; + + // On finish recording, we export events into a file + if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); + } + else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) + { + LoadAutomationEvents("eventsrec.rep"); + eventsPlaying = true; + + TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); + } +#endif +} + +// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) +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 + + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; + CORE.Input.Keyboard.charPressedQueueCount++; + } +} + +// 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, + // but future releases may add more actions (i.e. GLFW_REPEAT) + CORE.Input.Mouse.currentButtonState[button] = action; + +#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[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() + + // 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 + ProcessGestureEvent(gestureEvent); + +#endif +} + +// GLFW3 Cursor Position Callback, runs on mouse move +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) +{ + CORE.Input.Mouse.currentPosition.x = (float)x; + CORE.Input.Mouse.currentPosition.y = (float)y; + 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 +} + +// GLFW3 Scrolling Callback, runs on mouse wheel +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) +{ + if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + else CORE.Input.Mouse.cursorOnScreen = false; +} + +// EOF + diff --git a/src/rcore_drm.c b/src/rcore_drm.c new file mode 100644 index 000000000..2c05ab46e --- /dev/null +++ b/src/rcore_drm.c @@ -0,0 +1,2059 @@ +/********************************************************************************************** +* +* rcore_drm - Functions to manage window, graphics device and inputs +* +* PLATFORM: DRM +* - Raspberry Pi 0-5 +* - Linux native mode (KMS driver) +* +* LIMITATIONS: +* - Most of the window/monitor functions are not implemented (not required) +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_DRM_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_DRM -not used- +* +* DEPENDENCIES: +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +static void InitKeyboard(void); // Initialize raw keyboard system +static void RestoreKeyboard(void); // Restore keyboard system +#if defined(SUPPORT_SSH_KEYBOARD_RPI) +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 *EventThread(void *arg); // Input device events reading thread + +static void InitGamepad(void); // Initialize raw gamepad input +static void *GamepadThread(void *arg); // Mouse reading thread + +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 +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + 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 + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + 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 = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else + SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && 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 +#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 +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// 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 + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + if (CORE.Window.prevFB) + { + drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); + CORE.Window.prevFB = 0; + } + + if (CORE.Window.prevBO) + { + gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); + CORE.Window.prevBO = NULL; + } + + if (CORE.Window.gbmSurface) + { + gbm_surface_destroy(CORE.Window.gbmSurface); + CORE.Window.gbmSurface = NULL; + } + + if (CORE.Window.gbmDevice) + { + gbm_device_destroy(CORE.Window.gbmDevice); + CORE.Window.gbmDevice = NULL; + } + + if (CORE.Window.crtc) + { + if (CORE.Window.connector) + { + drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id, + CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode); + drmModeFreeConnector(CORE.Window.connector); + CORE.Window.connector = NULL; + } + + drmModeFreeCrtc(CORE.Window.crtc); + CORE.Window.crtc = NULL; + } + + if (CORE.Window.fd != -1) + { + close(CORE.Window.fd); + CORE.Window.fd = -1; + } + + // Close surface, context and display + if (CORE.Window.device != EGL_NO_DISPLAY) + { + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + if (CORE.Window.context != EGL_NO_CONTEXT) + { + eglDestroyContext(CORE.Window.device, CORE.Window.context); + CORE.Window.context = EGL_NO_CONTEXT; + } + + eglTerminate(CORE.Window.device); + CORE.Window.device = EGL_NO_DISPLAY; + } + + // Wait for mouse and gamepad threads to finish before closing + // NOTE: Those threads should already have finished at this point + // because they are controlled by CORE.Window.shouldClose variable + + CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called + + // Close the evdev keyboard + if (CORE.Input.Keyboard.fd != -1) + { + close(CORE.Input.Keyboard.fd); + CORE.Input.Keyboard.fd = -1; + } + + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].threadId) + { + pthread_join(CORE.Input.eventWorker[i].threadId, NULL); + } + } + + if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// 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; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return true; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_DRM"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_DRM"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_DRM"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_DRM"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_DRM"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_DRM"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_DRM"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_DRM"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_DRM"); +} + +// 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 PLATFORM_DRM"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_DRM"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_DRM"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_DRM"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_DRM"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_DRM"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_DRM"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_DRM"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + int refresh = 0; + + if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0)) + { + refresh = CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh; + } + + return refresh; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_DRM"); + return ""; +} + +// 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 PLATFORM_DRM"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_DRM"); + 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); + + 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; +} + +// 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; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_WEB + 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[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// 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 PLATFORM_DRM"); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + if (CORE.Input.Gamepad.ready[gamepad]) + { + ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); + name = CORE.Input.Gamepad.name[gamepad]; + } + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + int axisCount = 0; + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); + CORE.Input.Gamepad.axisCount = axisCount; + + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// 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; +} + +// 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; +} + +// 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; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_DRM"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return GetMouseX(); +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return GetMouseY(); +} + +// 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; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + + if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + + struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface); + if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); + + uint32_t fb = 0; + int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.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(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); + + if (CORE.Window.prevFB) + { + result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); + } + + CORE.Window.prevFB = fb; + + if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); + + CORE.Window.prevBO = bo; +} + +// 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 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(); + + // Register previous mouse states + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; + CORE.Input.Mouse.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] = CORE.Input.Mouse.currentButtonStateEvdev[i]; + } + + // Register gamepads buttons events + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (CORE.Input.Gamepad.ready[i]) + { + // 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]; + } + } + + // 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 PLATFORM_WEB 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 }; + +#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 (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); + + // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() + // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() +#endif +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the window minimum and maximum default values to 0 + CORE.Window.windowMin.width = 0; + CORE.Window.windowMin.height = 0; + CORE.Window.windowMax.width = 0; + CORE.Window.windowMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + CORE.Window.fd = -1; + CORE.Window.connector = NULL; + CORE.Window.modeIndex = -1; + CORE.Window.crtc = NULL; + CORE.Window.gbmDevice = NULL; + CORE.Window.gbmSurface = NULL; + CORE.Window.prevBO = NULL; + CORE.Window.prevFB = 0; + +#if defined(DEFAULT_GRAPHIC_DEVICE_DRM) + CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); +#else + TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card"); + CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + + if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + { + TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); + CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded + } + + if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + { + TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0"); + CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) + } +#endif + + if (CORE.Window.fd == -1) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); + return false; + } + + drmModeRes *res = drmModeGetResources(CORE.Window.fd); + if (!res) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); + return false; + } + + 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(CORE.Window.fd, res->connectors[i]); + TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); + + if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); + CORE.Window.connector = con; + break; + } + else + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); + drmModeFreeConnector(con); + } + } + + if (!CORE.Window.connector) + { + TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); + drmModeFreeResources(res); + return false; + } + + drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id); + if (!enc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); + drmModeFreeResources(res); + return false; + } + + CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id); + if (!CORE.Window.crtc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); + drmModeFreeEncoder(enc); + drmModeFreeResources(res); + return false; + } + + // 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..."); + + CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode); + + if (CORE.Window.modeIndex < 0) + { + TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); + drmModeFreeEncoder(enc); + drmModeFreeResources(res); + return false; + } + + 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 + CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + + // If nothing found, try to find a nearly matching mode + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + + // If nothing found, try to find an exactly matching mode including interlaced + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + + // If nothing found, try to find a nearly matching mode including interlaced + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + + // If nothing found, there is no suitable mode + if (CORE.Window.modeIndex < 0) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); + drmModeFreeEncoder(enc); + drmModeFreeResources(res); + return false; + } + + CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay; + CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay; + + TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name, + CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, + (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p', + CORE.Window.connector->modes[CORE.Window.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; + + CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd); + if (!CORE.Window.gbmDevice) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); + return false; + } + + CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, + CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (!CORE.Window.gbmSurface) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); + return false; + } + + 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 + CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice); + if (CORE.Window.device == EGL_NO_DISPLAY) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Initialize the EGL device connection + if (eglInitialize(CORE.Window.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; + } + + if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); + return false; + } + + 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 false; + } + + EGLint matchingNumConfigs = 0; + if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); + free(configs); + return false; + } + + 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(CORE.Window.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); + CORE.Window.config = configs[i]; + found = 1; + break; + } + } + + RL_FREE(configs); + + if (!found) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config"); + return false; + } + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); + if (CORE.Window.context == EGL_NO_CONTEXT) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); + return false; + } + + // Create an EGL window surface + //--------------------------------------------------------------------------------- + CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); + if (EGL_NO_SURFACE == CORE.Window.surface) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); + return false; + } + + // 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(CORE.Window.device, 1); + + if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + 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); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // 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); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// 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, &CORE.Input.Keyboard.defaultSettings); + + // Reconfigure terminal with new settings + struct termios keyboardNewSettings = { 0 }; + keyboardNewSettings = CORE.Input.Keyboard.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 + CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags + fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.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, &CORE.Input.Keyboard.defaultMode); + + // In case of failure, it could mean a remote keyboard is used (SSH) + if (result < 0) TRACELOG(LOG_WARNING, "RPI: 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, &CORE.Input.Keyboard.defaultSettings); + + // Reconfigure keyboard to default mode + fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); + ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); +} + +#if defined(SUPPORT_SSH_KEYBOARD_RPI) +// 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 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++; + } + } + + // Check exit key (same functionality as GLFW3 KeyCallback()) + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; + +#if defined(SUPPORT_SCREEN_CAPTURE) + // Check screen capture key (raylib key: KEY_F12) + if (CORE.Input.Keyboard.currentKeyState[301] == 1) + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } +#endif +} +#endif // SUPPORT_SSH_KEYBOARD_RPI + +// Initialise user input from evdev(/dev/input/event) +// 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 + CORE.Input.Keyboard.fd = -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, "RPI: 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(bit)) & 1) + + struct input_absinfo absinfo = { 0 }; + 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 }; + bool hasAbs = false; + bool hasRel = false; + bool hasAbsMulti = false; + int freeWorkerId = -1; + int fd = -1; + + InputEventWorker *worker = NULL; + + // Open the device and allocate worker + //------------------------------------------------------------------------------------------------------- + // Find a free spot in the workers array + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].threadId == 0) + { + freeWorkerId = i; + break; + } + } + + // Select the free worker from array + if (freeWorkerId >= 0) + { + worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker + memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker + } + else + { + TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device); + return; + } + + // Open the device + fd = open(device, O_RDONLY | O_NONBLOCK); + if (fd < 0) + { + TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device); + return; + } + worker->fd = fd; + + // Grab number on the end of the devices name "event" + int devNum = 0; + char *ptrDevName = strrchr(device, 't'); + worker->eventNum = -1; + + if (ptrDevName != NULL) + { + if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum; + } + else worker->eventNum = 0; // TODO: HACK: Grab number for mouse0 device! + + // 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 + //------------------------------------------------------------------------------------------------------- + ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties + + // Check for absolute input devices + if (TEST_BIT(evBits, EV_ABS)) + { + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); + + // Check for absolute movement support (usually touchscreens, but also joysticks) + if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) + { + hasAbs = true; + + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + worker->absRange.x = absinfo.minimum; + worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + worker->absRange.y = absinfo.minimum; + worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + + // Check for multiple absolute movement support (usually multitouch touchscreens) + if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) + { + hasAbsMulti = true; + + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + worker->absRange.x = absinfo.minimum; + worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + worker->absRange.y = absinfo.minimum; + worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + } + + // Check for relative movement support (usually mouse) + if (TEST_BIT(evBits, EV_REL)) + { + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); + + if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; + } + + // Check for button support to determine the device type(usually on all input devices) + if (TEST_BIT(evBits, EV_KEY)) + { + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); + + if (hasAbs || hasAbsMulti) + { + if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen + if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet + if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet + if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet + if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device + } + + if (hasRel) + { + if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse + if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse + } + + if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad + + if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard + } + //------------------------------------------------------------------------------------------------------- + + // Decide what to do with the device + //------------------------------------------------------------------------------------------------------- + if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) + { + // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a + // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate + // threads so that they don't drop events when the frame rate is slow. + TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); + CORE.Input.Keyboard.fd = worker->fd; + } + else if (worker->isTouch || worker->isMouse) + { + // Looks like an interesting device + TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device, + worker->isMouse? "mouse " : "", + worker->isMultitouch? "multitouch " : "", + worker->isTouch? "touchscreen " : "", + worker->isGamepad? "gamepad " : ""); + + // Create a thread for this device + int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); + if (error != 0) + { + TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error); + worker->threadId = 0; + close(fd); + } + +#if defined(USE_LAST_TOUCH_DEVICE) + // Find touchscreen with the highest index + int maxTouchNumber = -1; + + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; + } + + // Find touchscreens with lower indexes + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) + { + if (CORE.Input.eventWorker[i].threadId != 0) + { + TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); + pthread_cancel(CORE.Input.eventWorker[i].threadId); + close(CORE.Input.eventWorker[i].fd); + } + } + } +#endif + } + else close(fd); // We are not interested in this device + //------------------------------------------------------------------------------------------------------- +} + +// Poll and process evdev keyboard events +static void PollKeyboardEvents(void) +{ + // Scancode to keycode mapping for US keyboards + // 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 + static const int keymapUS[] = { + 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 + }; + + int fd = CORE.Input.Keyboard.fd; + 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)) + { + // Button parsing + if (event.type == EV_KEY) + { +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + // Change keyboard mode to events + CORE.Input.Keyboard.evtMode = true; +#endif + // Keyboard button parsing + if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 + { + keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the appropriate keycode + + // Make sure we got a valid keycode + if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) + { + // 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)? 1 : 0; + if (event.value >= 1) + { + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + #if defined(SUPPORT_SCREEN_CAPTURE) + // Check screen capture key (raylib key: KEY_F12) + if (CORE.Input.Keyboard.currentKeyState[301] == 1) + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + #endif + + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; + + TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", (event.value == 0)? "UP" : "DOWN", event.code, keycode); + } + } + } + } +} + +// Input device events reading thread +static void *EventThread(void *arg) +{ + struct input_event event = { 0 }; + InputEventWorker *worker = (InputEventWorker *)arg; + + int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE + bool gestureUpdate = false; // Flag to note gestures require to update + + while (!CORE.Window.shouldClose) + { + // Try to read data from the device and only continue if successful + while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) + { + // Relative movement parsing + if (event.type == EV_REL) + { + if (event.code == REL_X) + { + CORE.Input.Mouse.currentPosition.x += event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == REL_Y) + { + CORE.Input.Mouse.currentPosition.y += event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == REL_WHEEL) CORE.Input.Mouse.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 - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == ABS_Y) + { + CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + // Multitouch movement + if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events + + if (event.code == ABS_MT_POSITION_X) + { + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + } + + if (event.code == ABS_MT_POSITION_Y) + { + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + } + + if (event.code == ABS_MT_TRACKING_ID) + { + if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS)) + { + // Touch has ended for this point + CORE.Input.Touch.position[worker->touchSlot].x = -1; + CORE.Input.Touch.position[worker->touchSlot].y = -1; + } + } + + // Touchscreen tap + if (event.code == ABS_PRESSURE) + { + int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; + + if (!event.value && previousMouseLeftButtonState) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; + + touchAction = 0; // TOUCH_ACTION_UP + gestureUpdate = true; + } + + if (event.value && !previousMouseLeftButtonState) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; + + touchAction = 1; // TOUCH_ACTION_DOWN + gestureUpdate = true; + } + } + + } + + // Button parsing + if (event.type == EV_KEY) + { + // Mouse button parsing + if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; + + if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN + else touchAction = 0; // TOUCH_ACTION_UP + gestureUpdate = true; + } + + if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; + if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; + if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; + if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; + if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; + if (event.code == BTN_BACK) CORE.Input.Mouse.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) // PLATFORM_DRM + if (gestureUpdate) + { + 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); + } +#endif + } + + WaitTime(0.005); // Sleep for 5ms to avoid hogging CPU time + } + + close(worker->fd); + + return NULL; +} + +// Initialize gamepad system +static void InitGamepad(void) +{ + char gamepadDev[128] = { 0 }; + + for (int i = 0; i < MAX_GAMEPADS; i++) + { + sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); + + if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) + { + // NOTE: Only show message for first gamepad + if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); + } + else + { + CORE.Input.Gamepad.ready[i] = true; + + // NOTE: Only create one thread + if (i == 0) + { + int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); + + if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); + else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); + } + } + } +} + +// Process Gamepad (/dev/input/js0) +static void *GamepadThread(void *arg) +{ + #define JS_EVENT_BUTTON 0x01 // Button pressed/released + #define JS_EVENT_AXIS 0x02 // Joystick axis moved + #define JS_EVENT_INIT 0x80 // Initial state of device + + struct js_event { + unsigned int time; // event timestamp in milliseconds + short value; // event value + unsigned char type; // event type + unsigned char number; // event axis/button number + }; + + // Read gamepad event + struct js_event gamepadEvent = { 0 }; + + while (!CORE.Window.shouldClose) + { + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) + { + gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events + + // Process gamepad events by type + if (gamepadEvent.type == JS_EVENT_BUTTON) + { + //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value); + + if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS) + { + // 1 - button pressed, 0 - button released + CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value; + + if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number; + else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + } + } + else if (gamepadEvent.type == JS_EVENT_AXIS) + { + //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value); + + if (gamepadEvent.number < MAX_GAMEPAD_AXIS) + { + // NOTE: Scaling of gamepadEvent.value to get values between -1..1 + CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768; + } + } + } + else WaitTime(0.001); // Sleep for 1 ms to avoid hogging CPU time + } + } + + return NULL; +} + +// 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(&CORE.Window.crtc->mode, &CORE.Window.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 < CORE.Window.connector->count_modes; i++) + { + const drmModeModeInfo *const mode = &CORE.Window.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 < CORE.Window.connector->count_modes; i++) + { + const drmModeModeInfo *const mode = &CORE.Window.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(CORE.Window.connector->modes[nearestIndex].hdisplay - width); + const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); + const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps); + + if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { + nearestIndex = i; + } + } + + return nearestIndex; +} + +// EOF diff --git a/src/rcore_web.c b/src/rcore_web.c new file mode 100644 index 000000000..baafd60d6 --- /dev/null +++ b/src/rcore_web.c @@ -0,0 +1,1604 @@ +/********************************************************************************************** +* +* rcore_web - Functions to manage window, graphics device and inputs +* +* PLATFORM: WEB +* - HTML5 (WebAssembly) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* 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_WEB_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_WEB -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) 2013-2023 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 "rcore.h" + +#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#include // Required for: timespec, nanosleep(), select() - POSIX + +#include // Emscripten functionality for C +#include // Emscripten HTML5 library + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +// Error callback event +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 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 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 + +// Emscripten 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 EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); + +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); +static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + 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 + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + 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 = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && 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 +#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 +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); + + // 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) + // 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) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + + // Trigger this once to get initial window sizing + 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); + + // 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_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// 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 + + glfwDestroyWindow(CORE.Window.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +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); + return false; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// 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; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + /* + 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 (!CORE.Window.fullscreen) + { + // 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, height; + emscripten_get_canvas_element_size("#canvas", &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; + } + 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); + + CORE.Window.fullscreen = false; // Toggle fullscreen flag + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + } + */ + + CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_WEB"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_WEB"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_WEB"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_WEB"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_WEB"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_WEB"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_WEB"); +} + +// Set icon for window, multiple images +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_WEB"); +} + +// 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 PLATFORM_WEB"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_WEB"); +} + +// 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 ((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 ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + glfwSetWindowSize(CORE.Window.handle, 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 PLATFORM_WEB"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_WEB"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_WEB"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_WEB"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_WEB"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_WEB"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_WEB"); + return (Vector2){ 1.0f, 1.0f }; +} + +// 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; +} + +// 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) +{ + emscripten_exit_pointerlock(); + + // 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) +{ + // TODO: figure out how not to hard code the canvas ID here. + emscripten_request_pointerlock("#canvas", 1); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = glfwGetTime(); // Elapsed time since glfwInit() + return time; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_WEB + 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[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + // Download file from MEMFS (emscripten memory filesystem) + // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html + emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// 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 PLATFORM_WEB + 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 a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + name = CORE.Input.Gamepad.name[gamepad]; + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_INFO, "SetGamepadMappings not implemented in rcore_web.c"); + + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + // TODO: Review touch position + + // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned + 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; +} + +// 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; + + // NOTE: emscripten not implemented + glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); +} + +// 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; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// 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; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(CORE.Window.handle); +} + +// 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; + // 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 PLATFORM_WEB 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 }; + + CORE.Window.resizedLastFrame = false; + + // Gamepad support using emscripten API + // NOTE: GLFW3 joystick functionality not available in web + + // 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; + + 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) // 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; + } + + //TRACELOGD("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++) + { + CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; + } + + CORE.Input.Gamepad.axisCount = gamepadState.numAxes; + } + } +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ + + if (!glfwInit()) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); + return false; + } + + glfwDefaultWindowHints(); // Set default windows hints + // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits + // glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits + // glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits + // glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits + // glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits + // glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window + // glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API + // 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 + 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 + else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window + + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) 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; + + // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); + else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); + + if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); + else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); + + // NOTE: Some GLFW flags are not supported on HTML5 + // e.g.: GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_SCALE_TO_MONITOR, GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_MOUSE_PASSTHROUGH + + if (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"); + glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 + } + + // 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. + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) + } + 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_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! + // glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context + } + else if (rlGetVersion() == RL_OPENGL_43) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // 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); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + } + + // 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; + + if (CORE.Window.fullscreen) + { + // 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. + CORE.Window.position.x = CORE.Window.display.width/4; + CORE.Window.position.y = CORE.Window.display.height/4; + } + 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; + } + + 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(glfwGetPrimaryMonitor(), &count); + + // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height + for (int i = 0; i < count; i++) + { + 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; + } + } + } + + 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); + + CORE.Window.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... + // glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + else + { + // No-fullscreen window creation + CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + + if (CORE.Window.handle) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + } + } + + if (!CORE.Window.handle) + { + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); + return false; + } + + // WARNING: glfwCreateWindow() title doesn't work with emscripten + emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); + + // Set window callback events + glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); + glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(CORE.Window.handle, KeyCallback); + glfwSetCharCallback(CORE.Window.handle, CharCallback); + glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); + glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + + glfwMakeContextCurrent(CORE.Window.handle); + + // 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. + + 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); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + + // 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); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// 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 +static void WindowSizeCallback(GLFWwindow *window, int width, int height) +{ + // Reset viewport and projection matrix for new size + SetupViewport(width, height); + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return; + + // Set current screen size + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + 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; + } + + // NOTE: Postprocessing texture is not scaled to new size +} + +// GLFW3 WindowIconify 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 +} + +// GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowMaximizeCallback(GLFWwindow *window, int maximized) +{ + +} + +// GLFW3 WindowFocus 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 +} + +// GLFW3 Window Drop Callback, runs when drop files into window +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)); + strcpy(CORE.Window.dropFilepaths[i], paths[i]); + } + } +} + + +// 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 + + // 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; + + // Check if there is space available in the key queue + if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) + { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + // Check the exit key to set close window + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + +#if defined(SUPPORT_SCREEN_CAPTURE) + if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) + { +#if defined(SUPPORT_GIF_RECORDING) + if (mods & GLFW_MOD_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); + + // Download file from MEMFS (emscripten memory filesystem) + // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html + emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); + + 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 + +#if defined(SUPPORT_EVENTS_AUTOMATION) + if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) + { + eventsRecording = !eventsRecording; + + // On finish recording, we export events into a file + if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); + } + else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) + { + LoadAutomationEvents("eventsrec.rep"); + eventsPlaying = true; + + TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); + } +#endif +} + +// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) +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 + + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; + CORE.Input.Keyboard.charPressedQueueCount++; + } +} + +// 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, + // but future releases may add more actions (i.e. GLFW_REPEAT) + CORE.Input.Mouse.currentButtonState[button] = action; + +#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[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() + + // 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 +} + +// GLFW3 Cursor Position Callback, runs on mouse move +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) +{ + CORE.Input.Mouse.currentPosition.x = (float)x; + CORE.Input.Mouse.currentPosition.y = (float)y; + 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 +} + +// GLFW3 Scrolling Callback, runs on mouse wheel +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) +{ + if (enter == true) 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) +{ + // TODO: Implement EmscriptenFullscreenChangeCallback()? + + 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) +{ + // 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 < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; + else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; + + if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; + else if (height > 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 + + return 0; +} + +// Register mouse input events +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) +{ + // This is only for registering mouse click events with emscripten and doesn't need to do anything + + return 1; // The event was consumed by the callback handler +} + +// Register connected/disconnected 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\"", + 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]); + */ + + if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) + { + CORE.Input.Gamepad.ready[gamepadEvent->index] = true; + sprintf(CORE.Input.Gamepad.name[gamepadEvent->index], "%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 +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("#canvas", &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; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB + 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); + + // 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 + + return 1; // The event was consumed by the callback handler +} + +// EOF From bbbaae55621a854a6e69bcb9cfe1c6a5a8697e2c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 23:38:52 +0200 Subject: [PATCH 0675/1710] Reviewed #3313 --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 44b7ee787..ff7558dc3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1050,7 +1050,7 @@ Ray GetMouseRay(Vector2 mouse, Camera camera) } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + double aspect = (double)CORE.Window.screen.width/(double)CORE.Window.screen.height; double top = camera.fovy/2.0; double right = top*aspect; @@ -1134,7 +1134,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + double aspect = (double)width/(double)height; double top = camera.fovy/2.0; double right = top*aspect; From d445fdaa199eae6540621b5a35f2d498f5ffab5c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 00:41:06 +0200 Subject: [PATCH 0676/1710] WARNING: REDESIGN: Move platform specific data to platform submodules #3313 REVIEWED: Defines, macros, types and tweaks --- src/rcore.c | 70 +++----- src/rcore.h | 149 +++-------------- src/rcore_android.c | 156 +++++++++-------- src/rcore_desktop.c | 189 +++++++++++---------- src/rcore_drm.c | 398 ++++++++++++++++++++++++++------------------ src/rcore_web.c | 75 +++++---- src/utils.h | 2 +- 7 files changed, 519 insertions(+), 520 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index ff7558dc3..f28aff74f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1,33 +1,18 @@ /********************************************************************************************** * -* rcore - Basic functions to manage windows, OpenGL context and input on multiple platforms +* rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: * - PLATFORM_DESKTOP: Windows (Win32, Win64) * - PLATFORM_DESKTOP: Linux (X11 desktop mode) * - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) * - PLATFORM_DESKTOP: OSX/macOS +* - PLATFORM_WEB: HTML5 (WebAssembly) +* - PLATFORM_DRM: Raspberry Pi 0-5 +* - PLATFORM_DRM: Linux native mode (KMS driver) * - PLATFORM_ANDROID: Android (ARM, ARM64) -* - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver -* - PLATFORM_WEB: HTML5 with WebAssembly * * CONFIGURATION: -* #define PLATFORM_DESKTOP -* Windowing and input system configured for desktop platforms: -* Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly -* -* #define PLATFORM_ANDROID -* Windowing and input system configured for Android device, app activity managed internally in this module. -* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL -* -* #define PLATFORM_DRM -* Windowing and input system configured for DRM native mode (RPI4 and other devices) -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ -* -* #define PLATFORM_WEB -* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js -* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. -* * #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) @@ -42,11 +27,6 @@ * #define SUPPORT_MOUSE_GESTURES * Mouse gestures are directly mapped like touches and processed by gestures system. * -* #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. -* * #define SUPPORT_BUSY_WAIT_LOOP * Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used * @@ -68,7 +48,6 @@ * Support automatic generated events, loading and recording of those events when required * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX, FreeBSD...) * 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) @@ -76,7 +55,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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. @@ -102,7 +81,7 @@ #include "config.h" // Defines module configuration flags #endif -#include "rcore.h" +#include "rcore.h" // Defines types and globals #define RLGL_IMPLEMENTATION #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 @@ -142,21 +121,19 @@ #endif // Platform specific defines to handle GetApplicationDirectory() -#if defined (PLATFORM_DESKTOP) - #if defined(_WIN32) - #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); - #elif defined(__linux__) - #include - #elif defined(__APPLE__) - #include - #include - #endif // OSs -#endif // PLATFORM_DESKTOP +#if defined(_WIN32) + #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); +#elif defined(__linux__) + #include +#elif defined(__APPLE__) + #include + #include +#endif // OSs #define _CRT_INTERNAL_NONSTDC_NAMES 1 #include // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()] @@ -165,7 +142,7 @@ #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif -#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__)) +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__)) #define DIRENT_MALLOC RL_MALLOC #define DIRENT_FREE RL_FREE @@ -333,7 +310,8 @@ const char *TextFormat(const char *text, ...); // Formatting of text with #elif defined(PLATFORM_ANDROID) #include "rcore_android.c" #else - // Software rendering backend, user needs to provide buffer ;) + // TODO: Include your custom platform backend! + // i.e software rendering backend or console backend! #endif //---------------------------------------------------------------------------------- @@ -1897,7 +1875,7 @@ bool IsKeyPressed(int key) return pressed; } -// Check if a key has been pressed again (only PLATFORM_DESKTOP) +// Check if a key has been pressed again bool IsKeyPressedRepeat(int key) { bool repeat = false; @@ -2291,7 +2269,7 @@ void InitTimer(void) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) struct timespec now = { 0 }; if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success diff --git a/src/rcore.h b/src/rcore.h index 5d9e3bd74..132c8e007 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -35,64 +35,6 @@ #ifndef RCORE_H #define RCORE_H -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - -#include "utils.h" // Required for: TRACELOG() macros - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 - // NOTE: Already provided by rlgl implementation (on glad.h) - #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management - // NOTE: GLFW3 already includes gl.h (OpenGL) headers -#endif - -#if defined(PLATFORM_ANDROID) - #include // Native platform windowing system interface - //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_DRM) - - #include // POSIX file control definitions - open(), creat(), fcntl() - #include // POSIX standard function definitions - read(), close(), STDIN_FILENO - #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() - #include // POSIX threads management (inputs reading) - #include // POSIX directory browsing - - #include // Required for: ioctl() - UNIX System call for device-specific input/output operations - #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition - #include // Linux: Keycodes constants definition (KEY_A, ...) - #include // Linux: Joystick support library - - #include // Generic Buffer Management (native platform for EGL on DRM) - #include // Direct Rendering Manager user-level library interface - #include // Direct Rendering Manager mode setting (KMS) interface - - #include "EGL/egl.h" // Native platform windowing system interface - #include "EGL/eglext.h" // EGL extensions - - typedef struct - { - pthread_t threadId; // Event reading thread id - int fd; // File descriptor to the device it is assigned to - int eventNum; // Number of 'event' device - 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 isMouse; // True if device supports relative X Y movements - bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH - bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH - bool isKeyboard; // True if device has letter keycodes - bool isGamepad; // True if device has gamepad buttons - } InputEventWorker; - -#endif - -// TODO: PROVIDE A HEADER TO BE USED BY ALL THE rcore_* IMPLEMENTATIONS - #include "raylib.h" #include "rlgl.h" @@ -100,16 +42,17 @@ #define RAYMATH_IMPLEMENTATION #include "raymath.h" +#include "utils.h" // Required for: TRACELOG() macros + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) - #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number - - #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) - #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events -#endif - #ifndef MAX_FILEPATH_CAPACITY #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath #endif @@ -152,12 +95,6 @@ #define FLAG_TOGGLE(n, f) ((n) ^= (f)) #define FLAG_CHECK(n, f) ((n) & (f)) -// TODO: HACK: Added flag if not provided by GLFW when using external library -// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev -#if !defined(GLFW_MOUSE_PASSTHROUGH) - #define GLFW_MOUSE_PASSTHROUGH 0x0002000D -#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. @@ -172,25 +109,6 @@ typedef struct { unsigned int width; unsigned int height; } Size; // Core global state context data typedef struct CoreData { struct { -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - GLFWwindow *handle; // GLFW window handle (graphic device) -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) -#if defined(PLATFORM_DRM) - 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) -#endif // PLATFORM_DRM - 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 -#endif 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 @@ -215,45 +133,27 @@ typedef struct CoreData { char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings - + } Window; -#if defined(PLATFORM_ANDROID) - struct { - bool appEnabled; // Flag to detect if app is active ** = true - struct android_app *app; // Android activity - struct android_poll_source *source; // Android events polling source - bool contextRebindRequired; // Used to know context rebind required - } Android; -#endif struct { const char *basePath; // Base path for data storage + } Storage; struct { -#if defined(PLATFORM_DRM) - InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" -#endif 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 + 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. + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. - int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue + 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 charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) int charPressedQueueCount; // Input characters queue count -#if defined(PLATFORM_DRM) - int defaultMode; // Default keyboard mode -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - bool evtMode; // Keyboard in event mode -#endif - int defaultFileFlags; // Default IO file flags - struct termios defaultSettings; // Default keyboard settings - int fd; // File descriptor for the evdev keyboard -#endif } Keyboard; struct { Vector2 offset; // Mouse offset @@ -269,11 +169,7 @@ typedef struct CoreData { char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state Vector2 currentWheelMove; // Registers current mouse wheel variation Vector2 previousWheelMove; // Registers previous mouse wheel variation -#if defined(PLATFORM_DRM) - 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 -#endif + } Mouse; struct { int pointCount; // Number of touch points active @@ -281,6 +177,7 @@ typedef struct CoreData { 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 @@ -290,10 +187,7 @@ typedef struct CoreData { 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 -#if defined(PLATFORM_DRM) - pthread_t threadId; // Gamepad reading thread id - int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor -#endif + } Gamepad; } Input; struct { @@ -303,10 +197,9 @@ typedef struct CoreData { 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 -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - unsigned long long int base; // Base time measure for hi-res timer -#endif + unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) unsigned int frameCounter; // Frame counter + } Time; } CoreData; diff --git a/src/rcore_android.c b/src/rcore_android.c index 61d729a1c..ad7a905c1 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -53,15 +53,31 @@ #include // Required for: android_app struct and activity management #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] +#include // Native platform windowing system interface + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... +typedef struct { + // Application data + struct android_app *app; // Android activity + struct android_poll_source *source; // Android events polling source + bool appEnabled; // Flag to detect if app is active ** = true + bool contextRebindRequired; // Used to know context rebind 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; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -89,7 +105,7 @@ extern int main(int argc, char *argv[]); void android_main(struct android_app *app) { char arg0[] = "raylib"; // NOTE: argv[] are mutable - CORE.Android.app = app; + platform.app = app; // NOTE: Return from main is ignored (void)main(1, (char *[]) { arg0, NULL }); @@ -104,9 +120,9 @@ void android_main(struct android_app *app) // Waiting for application events before complete finishing while (!app->destroyRequested) { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&platform.source)) >= 0) { - if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(app, platform.source); } } } @@ -114,7 +130,7 @@ void android_main(struct android_app *app) // NOTE: Add this to header (if apps really need it) struct android_app *GetAndroidApp(void) { - return CORE.Android.app; + return platform.app; } // Initialize window and OpenGL context @@ -169,9 +185,9 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.height = height; // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - int orientation = AConfiguration_getOrientation(CORE.Android.app->config); + int orientation = AConfiguration_getOrientation(platform.app->config); if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); @@ -179,32 +195,32 @@ void InitWindow(int width, int height, const char *title) // TODO: Automatic orientation doesn't seem to work if (width <= height) { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); } else { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); } - //AConfiguration_getDensity(CORE.Android.app->config); - //AConfiguration_getKeyboard(CORE.Android.app->config); - //AConfiguration_getScreenSize(CORE.Android.app->config); - //AConfiguration_getScreenLong(CORE.Android.app->config); + //AConfiguration_getDensity(platform.app->config); + //AConfiguration_getKeyboard(platform.app->config); + //AConfiguration_getScreenSize(platform.app->config); + //AConfiguration_getScreenLong(platform.app->config); // Initialize App command system // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - CORE.Android.app->onAppCmd = AndroidCommandCallback; + platform.app->onAppCmd = AndroidCommandCallback; // Initialize input events system - CORE.Android.app->onInputEvent = AndroidInputCallback; + platform.app->onInputEvent = AndroidInputCallback; // Initialize assets manager - InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); + InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize base path for storage - CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; + CORE.Storage.basePath = platform.app->activity->internalDataPath; TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); @@ -216,13 +232,13 @@ void InitWindow(int width, int height, const char *title) while (!CORE.Window.ready) { // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) { // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(platform.app, platform.source); // NOTE: Never close window, native activity is controlled by the system! - //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; + //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } } @@ -250,24 +266,24 @@ void CloseWindow(void) #endif // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - if (CORE.Window.context != EGL_NO_CONTEXT) + if (platform.context != EGL_NO_CONTEXT) { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; } - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; } #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -306,7 +322,7 @@ bool IsWindowMaximized(void) // Check if window has the focus bool IsWindowFocused(void) { - return CORE.Android.appEnabled; + return platform.appEnabled; } // Check if window has been resizedLastFrame @@ -595,7 +611,7 @@ void OpenURL(const char *url) else { JNIEnv *env = NULL; - JavaVM *vm = CORE.Android.app->activity->vm; + JavaVM *vm = platform.app->activity->vm; (*vm)->AttachCurrentThread(vm, &env, NULL); jstring urlString = (*env)->NewStringUTF(env, url); @@ -612,7 +628,7 @@ void OpenURL(const char *url) (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); + (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); (*vm)->DetachCurrentThread(vm); } @@ -713,7 +729,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + eglSwapBuffers(platform.device, platform.surface); } // Register all input events @@ -756,17 +772,17 @@ void PollInputEvents(void) int pollEvents = 0; // Poll Events (registered events) - // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) - while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + // NOTE: Activity is paused if not enabled (platform.appEnabled) + while ((pollResult = ALooper_pollAll(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) >= 0) { // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(platform.app, platform.source); // NOTE: Never close window, native activity is controlled by the system! - if (CORE.Android.app->destroyRequested != 0) + if (platform.app->destroyRequested != 0) { //CORE.Window.shouldClose = true; - //ANativeActivity_finish(CORE.Android.app->activity); + //ANativeActivity_finish(platform.app->activity); } } } @@ -829,15 +845,15 @@ static bool InitGraphicsDevice(int width, int height) EGLint numConfigs = 0; // Get an EGL device connection - CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (CORE.Window.device == EGL_NO_DISPLAY) + 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(CORE.Window.device, NULL, NULL) == EGL_FALSE) + 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"); @@ -845,14 +861,14 @@ static bool InitGraphicsDevice(int width, int height) } // Get an appropriate EGL framebuffer configuration - eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs); + eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs); // Set rendering API eglBindAPI(EGL_OPENGL_ES_API); // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_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 false; @@ -864,7 +880,7 @@ static bool InitGraphicsDevice(int width, int height) // 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(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: @@ -873,15 +889,15 @@ static bool InitGraphicsDevice(int width, int height) // -> CORE.Window.screenScale SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size + ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); + 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(CORE.Window.device, 1); + //eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return false; @@ -933,11 +949,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) { if (app->window != NULL) { - if (CORE.Android.contextRebindRequired) + if (platform.contextRebindRequired) { // Reset screen scaling to full display size EGLint displayFormat = 0; - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the // context rebinding if the screen is scaled unless offsets are added. There's probably a more @@ -948,15 +964,15 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) displayFormat); // Recreate display surface and re-attach OpenGL context - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); - eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); + platform.surface = eglCreateWindowSurface(platform.device, platform.config, app->window, NULL); + eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); - CORE.Android.contextRebindRequired = false; + platform.contextRebindRequired = false; } else { - CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); - CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); + CORE.Window.display.width = ANativeWindow_getWidth(platform.app->window); + CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window); // Initialize graphics device (display device and OpenGL context) InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); @@ -997,13 +1013,13 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } break; case APP_CMD_GAINED_FOCUS: { - CORE.Android.appEnabled = true; + platform.appEnabled = true; //ResumeMusicStream(); } break; case APP_CMD_PAUSE: break; case APP_CMD_LOST_FOCUS: { - CORE.Android.appEnabled = false; + platform.appEnabled = false; //PauseMusicStream(); } break; case APP_CMD_TERM_WINDOW: @@ -1012,19 +1028,19 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // 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 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 (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - CORE.Android.contextRebindRequired = true; + platform.contextRebindRequired = true; } - // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' + // If 'platform.device' is already set to 'EGL_NO_DISPLAY' // this means that the user has already called 'CloseWindow()' } break; @@ -1033,8 +1049,8 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_DESTROY: break; case APP_CMD_CONFIG_CHANGED: { - //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); - //print_cur_config(CORE.Android.app); + //AConfiguration_fromAssetManager(platform.app->config, platform.app->activity->assetManager); + //print_cur_config(platform.app); // Check screen orientation here! } break; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index f8c6b0e2c..33a4aab0e 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -85,15 +85,28 @@ #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() #endif +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#endif + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... +typedef struct { + GLFWwindow *handle; // GLFW window handle (graphic device) +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -255,7 +268,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - glfwDestroyWindow(CORE.Window.handle); + glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) @@ -279,10 +292,10 @@ bool WindowShouldClose(void) // While window minimized, stop loop execution while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); + CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); // Reset close status for next frame - glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); + glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); return CORE.Window.shouldClose; } @@ -325,7 +338,7 @@ void ToggleFullscreen(void) if (!CORE.Window.fullscreen) { // Store previous window position (in case we exit fullscreen) - glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); + glfwGetWindowPos(platform.handle, &CORE.Window.position.x, &CORE.Window.position.y); int monitorCount = 0; int monitorIndex = GetCurrentMonitor(); @@ -341,14 +354,14 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = false; CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + 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; - glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } } @@ -357,7 +370,7 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = false; CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) @@ -368,9 +381,9 @@ void ToggleFullscreen(void) // Set window state: maximized, if resizable void MaximizeWindow(void) { - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) { - glfwMaximizeWindow(CORE.Window.handle); + glfwMaximizeWindow(platform.handle); CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; } } @@ -379,16 +392,16 @@ void MaximizeWindow(void) void MinimizeWindow(void) { // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(CORE.Window.handle); + glfwIconifyWindow(platform.handle); } // Set window state: not minimized/maximized void RestoreWindow(void) { - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) { // Restores the specified window if it was previously iconified (minimized) or maximized - glfwRestoreWindow(CORE.Window.handle); + glfwRestoreWindow(platform.handle); CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; } @@ -420,13 +433,13 @@ void ToggleBorderlessWindowed(void) { // Store screen position and size // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here - if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + if (!wasOnFullscreen) glfwGetWindowPos(platform.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); CORE.Window.previousScreen = CORE.Window.screen; // Set undecorated and topmost modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_TOPMOST; // Get monitor position and size @@ -437,29 +450,29 @@ void ToggleBorderlessWindowed(void) const int monitorHeight = mode->height; // Set screen position and size - glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY); + glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight); // Refocus window - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; } else { // Remove topmost and undecorated modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); 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(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + glfwSetWindowSize(platform.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); // Refocus window - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; } @@ -498,21 +511,21 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_RESIZABLE if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE); 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)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); 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)) { - glfwHideWindow(CORE.Window.handle); + glfwHideWindow(platform.handle); CORE.Window.flags |= FLAG_WINDOW_HIDDEN; } @@ -533,14 +546,14 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_UNFOCUSED if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); 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)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_TOPMOST; } @@ -567,7 +580,7 @@ void SetWindowState(unsigned int flags) // 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)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH; } @@ -613,14 +626,14 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_RESIZABLE if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; } // State change: FLAG_WINDOW_HIDDEN if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) { - glfwShowWindow(CORE.Window.handle); + glfwShowWindow(platform.handle); CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; } @@ -639,21 +652,21 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_UNDECORATED if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; } // State change: FLAG_WINDOW_UNFOCUSED if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; } // State change: FLAG_WINDOW_TOPMOST if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; } @@ -680,7 +693,7 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH; } @@ -705,7 +718,7 @@ void SetWindowIcon(Image image) if (image.data == NULL) { // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + glfwSetWindowIcon(platform.handle, 0, NULL); } else { @@ -719,7 +732,7 @@ void SetWindowIcon(Image image) // NOTE 1: We only support one image icon // NOTE 2: The specified image data is copied before this function returns - glfwSetWindowIcon(CORE.Window.handle, 1, icon); + glfwSetWindowIcon(platform.handle, 1, icon); } else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); } @@ -734,7 +747,7 @@ void SetWindowIcons(Image *images, int count) if ((images == NULL) || (count <= 0)) { // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + glfwSetWindowIcon(platform.handle, 0, NULL); } else { @@ -754,7 +767,7 @@ void SetWindowIcons(Image *images, int count) else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); } // NOTE: Images data is copied internally before this function returns - glfwSetWindowIcon(CORE.Window.handle, valid, icons); + glfwSetWindowIcon(platform.handle, valid, icons); RL_FREE(icons); } @@ -764,13 +777,13 @@ void SetWindowIcons(Image *images, int count) void SetWindowTitle(const char *title) { CORE.Window.title = title; - glfwSetWindowTitle(CORE.Window.handle, title); + glfwSetWindowTitle(platform.handle, title); } // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - glfwSetWindowPos(CORE.Window.handle, x, y); + glfwSetWindowPos(platform.handle, x, y); } // Set monitor for the current window @@ -786,7 +799,7 @@ void SetWindowMonitor(int monitor) TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + glfwSetWindowMonitor(platform.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); } else { @@ -801,12 +814,12 @@ void SetWindowMonitor(int monitor) glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it - if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); + if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(platform.handle, monitorWorkareaX, monitorWorkareaY); else { const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); - glfwSetWindowPos(CORE.Window.handle, x, y); + glfwSetWindowPos(platform.handle, x, y); } } } @@ -822,7 +835,7 @@ void SetWindowMinSize(int width, int height) int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) @@ -834,13 +847,13 @@ void SetWindowMaxSize(int width, int height) int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } // Set window dimensions void SetWindowSize(int width, int height) { - glfwSetWindowSize(CORE.Window.handle, width, height); + glfwSetWindowSize(platform.handle, width, height); } // Set window opacity, value opacity is between 0.0 and 1.0 @@ -848,13 +861,13 @@ void SetWindowOpacity(float opacity) { if (opacity >= 1.0f) opacity = 1.0f; else if (opacity <= 0.0f) opacity = 0.0f; - glfwSetWindowOpacity(CORE.Window.handle, opacity); + glfwSetWindowOpacity(platform.handle, opacity); } // Set window focused void SetWindowFocused(void) { - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); } // Get native window handle @@ -862,19 +875,19 @@ void *GetWindowHandle(void) { #if defined(_WIN32) // NOTE: Returned handle is: void *HWND (windows.h) - return glfwGetWin32Window(CORE.Window.handle); + 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(CORE.Window.handle); + //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle); //return NULL; // TODO: Find a way to return value... cast to void *? - return (void *)CORE.Window.handle; + return (void *)platform.handle; #endif #if defined(__APPLE__) // NOTE: Returned handle is: (objc_object *) - return (void *)glfwGetCocoaWindow(CORE.Window.handle); + return (void *)glfwGetCocoaWindow(platform.handle); #endif return NULL; @@ -903,7 +916,7 @@ int GetCurrentMonitor(void) if (IsWindowFullscreen()) { // Get the handle of the monitor that the specified window is in full screen on - monitor = glfwGetWindowMonitor(CORE.Window.handle); + monitor = glfwGetWindowMonitor(platform.handle); for (int i = 0; i < monitorCount; i++) { @@ -919,7 +932,7 @@ int GetCurrentMonitor(void) int x = 0; int y = 0; - glfwGetWindowPos(CORE.Window.handle, &x, &y); + glfwGetWindowPos(platform.handle, &x, &y); for (int i = 0; i < monitorCount; i++) { @@ -1070,7 +1083,7 @@ Vector2 GetWindowPosition(void) int x = 0; int y = 0; - glfwGetWindowPos(CORE.Window.handle, &x, &y); + glfwGetWindowPos(platform.handle, &x, &y); return (Vector2){ (float)x, (float)y }; } @@ -1109,34 +1122,34 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - glfwSetClipboardString(CORE.Window.handle, text); + glfwSetClipboardString(platform.handle, text); } // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { - return glfwGetClipboardString(CORE.Window.handle); + return glfwGetClipboardString(platform.handle); } // Show mouse cursor void ShowCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); CORE.Input.Mouse.cursorHidden = false; } // Hides mouse cursor void HideCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); CORE.Input.Mouse.cursorHidden = true; } // Enables cursor (unlock cursor) void EnableCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -1147,7 +1160,7 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -1276,7 +1289,7 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); + glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } // Get mouse wheel movement Y @@ -1294,11 +1307,11 @@ float GetMouseWheelMove(void) void SetMouseCursor(int cursor) { CORE.Input.Mouse.cursor = cursor; - if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); + if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(platform.handle, NULL); else { // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values - glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); + glfwSetCursor(platform.handle, glfwCreateStandardCursor(0x00036000 + cursor)); } } @@ -1331,7 +1344,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - glfwSwapBuffers(CORE.Window.handle); + glfwSwapBuffers(platform.handle); } // Register all input events @@ -1691,10 +1704,10 @@ static bool InitGraphicsDevice(int width, int height) // HighDPI monitors are properly considered in a following similar function: SetupViewport() SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + 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... - //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { @@ -1705,16 +1718,16 @@ static bool InitGraphicsDevice(int width, int height) } // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (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 (CORE.Window.handle) + if (platform.handle) { CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; } } - if (!CORE.Window.handle) + if (!platform.handle) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); @@ -1722,23 +1735,23 @@ static bool InitGraphicsDevice(int width, int height) } // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(platform.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwMakeContextCurrent(CORE.Window.handle); + glfwMakeContextCurrent(platform.handle); - glfwSetInputMode(CORE.Window.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) glfwSwapInterval(0); // No V-Sync by default @@ -1760,7 +1773,7 @@ static bool InitGraphicsDevice(int width, int height) // 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__) - glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight); + 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); @@ -1913,7 +1926,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i } // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); #if defined(SUPPORT_SCREEN_CAPTURE) if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2c05ab46e..8c63b63da 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -17,8 +17,10 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_DRM_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_DRM -not used- +* #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: * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) @@ -47,15 +49,93 @@ #include "rcore.h" +#include // POSIX file control definitions - open(), creat(), fcntl() +#include // POSIX standard function definitions - read(), close(), STDIN_FILENO +#include // POSIX terminal control definitions - tcgetattr(), tcsetattr() +#include // POSIX threads management (inputs reading) +#include // POSIX directory browsing + +#include // Required for: ioctl() - UNIX System call for device-specific input/output operations +#include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition +#include // Linux: Keycodes constants definition (KEY_A, ...) +#include // Linux: Joystick support library + +#include // Generic Buffer Management (native platform for EGL on DRM) +#include // Direct Rendering Manager user-level library interface +#include // Direct Rendering Manager mode setting (KMS) interface + +#include "EGL/egl.h" // Native platform windowing system interface +#include "EGL/eglext.h" // EGL extensions + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number + +#define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) +#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... + +typedef struct { + pthread_t threadId; // Event reading thread id + + int fd; // File descriptor to the device it is assigned to + int eventNum; // Number of 'event' device + 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 isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons +} InputEventWorker; + +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 + + // Input data + InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" + + // 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 + + // Gamepad data + pthread_t gamepadThreadId; // Gamepad reading thread id + int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor + +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -227,67 +307,67 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - if (CORE.Window.prevFB) + if (platform.prevFB) { - drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); - CORE.Window.prevFB = 0; + drmModeRmFB(platform.fd, platform.prevFB); + platform.prevFB = 0; } - if (CORE.Window.prevBO) + if (platform.prevBO) { - gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); - CORE.Window.prevBO = NULL; + gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + platform.prevBO = NULL; } - if (CORE.Window.gbmSurface) + if (platform.gbmSurface) { - gbm_surface_destroy(CORE.Window.gbmSurface); - CORE.Window.gbmSurface = NULL; + gbm_surface_destroy(platform.gbmSurface); + platform.gbmSurface = NULL; } - if (CORE.Window.gbmDevice) + if (platform.gbmDevice) { - gbm_device_destroy(CORE.Window.gbmDevice); - CORE.Window.gbmDevice = NULL; + gbm_device_destroy(platform.gbmDevice); + platform.gbmDevice = NULL; } - if (CORE.Window.crtc) + if (platform.crtc) { - if (CORE.Window.connector) + if (platform.connector) { - drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id, - CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode); - drmModeFreeConnector(CORE.Window.connector); - CORE.Window.connector = NULL; + 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(CORE.Window.crtc); - CORE.Window.crtc = NULL; + drmModeFreeCrtc(platform.crtc); + platform.crtc = NULL; } - if (CORE.Window.fd != -1) + if (platform.fd != -1) { - close(CORE.Window.fd); - CORE.Window.fd = -1; + close(platform.fd); + platform.fd = -1; } // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - if (CORE.Window.context != EGL_NO_CONTEXT) + if (platform.context != EGL_NO_CONTEXT) { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; } - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; } // Wait for mouse and gamepad threads to finish before closing @@ -297,21 +377,21 @@ void CloseWindow(void) CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called // Close the evdev keyboard - if (CORE.Input.Keyboard.fd != -1) + if (platform.keyboardFd != -1) { - close(CORE.Input.Keyboard.fd); - CORE.Input.Keyboard.fd = -1; + close(platform.keyboardFd); + platform.keyboardFd = -1; } - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].threadId) + if (platform.eventWorker[i].threadId) { - pthread_join(CORE.Input.eventWorker[i].threadId, NULL); + pthread_join(platform.eventWorker[i].threadId, NULL); } } - if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); + if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -524,9 +604,9 @@ int GetMonitorRefreshRate(int monitor) { int refresh = 0; - if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0)) + if ((platform.connector) && (platform.modeIndex >= 0)) { - refresh = CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh; + refresh = platform.connector->modes[platform.modeIndex].vrefresh; } return refresh; @@ -659,7 +739,7 @@ const char *GetGamepadName(int gamepad) if (CORE.Input.Gamepad.ready[gamepad]) { - ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); + ioctl(platform.gamepadStreamFd[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); name = CORE.Input.Gamepad.name[gamepad]; } @@ -670,7 +750,7 @@ const char *GetGamepadName(int gamepad) int GetGamepadAxisCount(int gamepad) { int axisCount = 0; - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; return CORE.Input.Gamepad.axisCount; @@ -756,31 +836,31 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + eglSwapBuffers(platform.device, platform.surface); - if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + 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(CORE.Window.gbmSurface); + 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(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); + 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(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]); + 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 (CORE.Window.prevFB) + if (platform.prevFB) { - result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); + result = drmModeRmFB(platform.fd, platform.prevFB); if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); } - CORE.Window.prevFB = fb; + platform.prevFB = fb; - if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); + if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); - CORE.Window.prevBO = bo; + platform.prevBO = bo; } // Register all input events @@ -814,12 +894,12 @@ void PollInputEvents(void) // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; - CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f }; + 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] = CORE.Input.Mouse.currentButtonStateEvdev[i]; + CORE.Input.Mouse.currentButtonState[i] = platform.currentButtonStateEvdev[i]; } // Register gamepads buttons events @@ -844,7 +924,7 @@ void PollInputEvents(void) // 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 (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); + if (!platform.eventKeyboardMode) ProcessKeyboard(); // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() @@ -877,41 +957,41 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - CORE.Window.fd = -1; - CORE.Window.connector = NULL; - CORE.Window.modeIndex = -1; - CORE.Window.crtc = NULL; - CORE.Window.gbmDevice = NULL; - CORE.Window.gbmSurface = NULL; - CORE.Window.prevBO = NULL; - CORE.Window.prevFB = 0; + platform.fd = -1; + platform.connector = NULL; + platform.modeIndex = -1; + platform.crtc = NULL; + platform.gbmDevice = NULL; + platform.gbmSurface = NULL; + platform.prevBO = NULL; + platform.prevFB = 0; #if defined(DEFAULT_GRAPHIC_DEVICE_DRM) - CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); + platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); #else TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card"); - CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) - if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); - CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded + platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded } - if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0"); - CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) + platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) } #endif - if (CORE.Window.fd == -1) + if (platform.fd == -1) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); return false; } - drmModeRes *res = drmModeGetResources(CORE.Window.fd); + drmModeRes *res = drmModeGetResources(platform.fd); if (!res) { TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); @@ -924,13 +1004,13 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); - drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]); + drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]); TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) { TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); - CORE.Window.connector = con; + platform.connector = con; break; } else @@ -940,14 +1020,14 @@ static bool InitGraphicsDevice(int width, int height) } } - if (!CORE.Window.connector) + if (!platform.connector) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); drmModeFreeResources(res); return false; } - drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id); + drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); if (!enc) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); @@ -955,8 +1035,8 @@ static bool InitGraphicsDevice(int width, int height) return false; } - CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id); - if (!CORE.Window.crtc) + platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); + if (!platform.crtc) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); drmModeFreeEncoder(enc); @@ -969,9 +1049,9 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode..."); - CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode); + platform.modeIndex = FindMatchingConnectorMode(platform.connector, &platform.crtc->mode); - if (CORE.Window.modeIndex < 0) + if (platform.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); drmModeFreeEncoder(enc); @@ -987,19 +1067,19 @@ static bool InitGraphicsDevice(int width, int height) const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60; // Try to find an exact matching mode - CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + 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 (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + 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 (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + 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 (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + 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 (CORE.Window.modeIndex < 0) + if (platform.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); drmModeFreeEncoder(enc); @@ -1007,13 +1087,13 @@ static bool InitGraphicsDevice(int width, int height) return false; } - CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay; - CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay; + 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)", CORE.Window.connector->modes[CORE.Window.modeIndex].name, - CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, - (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p', - CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh); + 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; @@ -1025,16 +1105,16 @@ static bool InitGraphicsDevice(int width, int height) drmModeFreeResources(res); res = NULL; - CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd); - if (!CORE.Window.gbmDevice) + platform.gbmDevice = gbm_create_device(platform.fd); + if (!platform.gbmDevice) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); return false; } - CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, - CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!CORE.Window.gbmSurface) + 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 false; @@ -1073,22 +1153,22 @@ static bool InitGraphicsDevice(int width, int height) EGLint numConfigs = 0; // Get an EGL device connection - CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice); - if (CORE.Window.device == EGL_NO_DISPLAY) + platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice); + if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); return false; } // Initialize the EGL device connection - if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + 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; } - if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) + if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); return false; @@ -1104,7 +1184,7 @@ static bool InitGraphicsDevice(int width, int height) } EGLint matchingNumConfigs = 0; - if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) + if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); free(configs); @@ -1118,7 +1198,7 @@ static bool InitGraphicsDevice(int width, int height) for (EGLint i = 0; i < matchingNumConfigs; ++i) { EGLint id = 0; - if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id)) + 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; @@ -1127,7 +1207,7 @@ static bool InitGraphicsDevice(int width, int height) if (GBM_FORMAT_ARGB8888 == id) { TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i); - CORE.Window.config = configs[i]; + platform.config = configs[i]; found = 1; break; } @@ -1145,8 +1225,8 @@ static bool InitGraphicsDevice(int width, int height) eglBindAPI(EGL_OPENGL_ES_API); // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_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 false; @@ -1154,8 +1234,8 @@ static bool InitGraphicsDevice(int width, int height) // Create an EGL window surface //--------------------------------------------------------------------------------- - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); - if (EGL_NO_SURFACE == CORE.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 false; @@ -1169,9 +1249,9 @@ static bool InitGraphicsDevice(int width, int height) SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(CORE.Window.device, 1); + //eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return false; @@ -1214,11 +1294,11 @@ static void InitKeyboard(void) // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE // Save terminal keyboard settings - tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings); + tcgetattr(STDIN_FILENO, &platform.defaultSettings); // Reconfigure terminal with new settings struct termios keyboardNewSettings = { 0 }; - keyboardNewSettings = CORE.Input.Keyboard.defaultSettings; + 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 @@ -1231,11 +1311,11 @@ static void InitKeyboard(void) tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings); // Save old keyboard mode to restore it at the end - CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified + 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, &CORE.Input.Keyboard.defaultMode); + 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, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used"); @@ -1257,11 +1337,11 @@ static void InitKeyboard(void) static void RestoreKeyboard(void) { // Reset to default keyboard settings - tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings); + tcsetattr(STDIN_FILENO, TCSANOW, &platform.defaultSettings); // Reconfigure keyboard to default mode - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); - ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); + fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags); + ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode); } #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -1389,7 +1469,7 @@ static void InitEvdevInput(void) struct dirent *entity = NULL; // Initialise keyboard file descriptor - CORE.Input.Keyboard.fd = -1; + platform.keyboardFd = -1; // Reset variables for (int i = 0; i < MAX_TOUCH_POINTS; ++i) @@ -1451,9 +1531,9 @@ static void ConfigureEvdevDevice(char *device) // Open the device and allocate worker //------------------------------------------------------------------------------------------------------- // Find a free spot in the workers array - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].threadId == 0) + if (platform.eventWorker[i].threadId == 0) { freeWorkerId = i; break; @@ -1463,7 +1543,7 @@ static void ConfigureEvdevDevice(char *device) // Select the free worker from array if (freeWorkerId >= 0) { - worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker + worker = &(platform.eventWorker[freeWorkerId]); // Grab a pointer to the worker memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker } else @@ -1574,13 +1654,13 @@ static void ConfigureEvdevDevice(char *device) // Decide what to do with the device //------------------------------------------------------------------------------------------------------- - if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) + if (worker->isKeyboard && (platform.keyboardFd == -1)) { // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate // threads so that they don't drop events when the frame rate is slow. TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); - CORE.Input.Keyboard.fd = worker->fd; + platform.keyboardFd = worker->fd; } else if (worker->isTouch || worker->isMouse) { @@ -1604,21 +1684,21 @@ static void ConfigureEvdevDevice(char *device) // Find touchscreen with the highest index int maxTouchNumber = -1; - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; + if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = platform.eventWorker[i].eventNum; } // Find touchscreens with lower indexes - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) + if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum < maxTouchNumber)) { - if (CORE.Input.eventWorker[i].threadId != 0) + if (platform.eventWorker[i].threadId != 0) { TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); - pthread_cancel(CORE.Input.eventWorker[i].threadId); - close(CORE.Input.eventWorker[i].fd); + pthread_cancel(platform.eventWorker[i].threadId); + close(platform.eventWorker[i].fd); } } } @@ -1652,7 +1732,7 @@ static void PollKeyboardEvents(void) 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 }; - int fd = CORE.Input.Keyboard.fd; + int fd = platform.keyboardFd; if (fd == -1) return; struct input_event event = { 0 }; @@ -1666,7 +1746,7 @@ static void PollKeyboardEvents(void) { #if defined(SUPPORT_SSH_KEYBOARD_RPI) // Change keyboard mode to events - CORE.Input.Keyboard.evtMode = true; + platform.eventKeyboardMode = true; #endif // Keyboard button parsing if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 @@ -1739,7 +1819,7 @@ static void *EventThread(void *arg) gestureUpdate = true; } - if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; + if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value; } // Absolute movement parsing @@ -1790,11 +1870,11 @@ static void *EventThread(void *arg) // Touchscreen tap if (event.code == ABS_PRESSURE) { - int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; + int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; if (!event.value && previousMouseLeftButtonState) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; touchAction = 0; // TOUCH_ACTION_UP gestureUpdate = true; @@ -1802,7 +1882,7 @@ static void *EventThread(void *arg) if (event.value && !previousMouseLeftButtonState) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; touchAction = 1; // TOUCH_ACTION_DOWN gestureUpdate = true; @@ -1817,19 +1897,19 @@ static void *EventThread(void *arg) // Mouse button parsing if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN else touchAction = 0; // TOUCH_ACTION_UP gestureUpdate = true; } - if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; - if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; - if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; - if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; - if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; - if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; + 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 @@ -1885,7 +1965,7 @@ static void InitGamepad(void) { sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); - if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) + if ((platform.gamepadStreamFd[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) { // NOTE: Only show message for first gamepad if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); @@ -1897,7 +1977,7 @@ static void InitGamepad(void) // NOTE: Only create one thread if (i == 0) { - int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); + int error = pthread_create(&platform.gamepadThreadId, NULL, &GamepadThread, NULL); if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); @@ -1927,7 +2007,7 @@ static void *GamepadThread(void *arg) { for (int i = 0; i < MAX_GAMEPADS; i++) { - if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) + if (read(platform.gamepadStreamFd[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) { gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events @@ -1977,7 +2057,7 @@ static int FindMatchingConnectorMode(const drmModeConnector *connector, const dr 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(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i])) return i; + if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i; } return -1; @@ -1992,9 +2072,9 @@ static int FindExactConnectorMode(const drmModeConnector *connector, uint width, if (NULL == connector) return -1; - for (int i = 0; i < CORE.Window.connector->count_modes; i++) + for (int i = 0; i < platform.connector->count_modes; i++) { - const drmModeModeInfo *const mode = &CORE.Window.connector->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"); @@ -2015,9 +2095,9 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt if (NULL == connector) return -1; int nearestIndex = -1; - for (int i = 0; i < CORE.Window.connector->count_modes; i++) + for (int i = 0; i < platform.connector->count_modes; i++) { - const drmModeModeInfo *const mode = &CORE.Window.connector->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"); @@ -2044,9 +2124,9 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt const int heightDiff = abs(mode->vdisplay - height); const int fpsDiff = abs(mode->vrefresh - fps); - const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width); - const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); - const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].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; diff --git a/src/rcore_web.c b/src/rcore_web.c index baafd60d6..fa296b90b 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -47,23 +47,42 @@ #include "rcore.h" -#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) -#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management + +#include // Emscripten functionality for C +#include // Emscripten HTML5 library + #include // Required for: timespec, nanosleep(), select() - POSIX -#include // Emscripten functionality for C -#include // Emscripten HTML5 library +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#endif + +#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 { + GLFWwindow *handle; // GLFW window handle (graphic device) +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -263,7 +282,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - glfwDestroyWindow(CORE.Window.handle); + glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) @@ -481,7 +500,7 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - glfwSetWindowSize(CORE.Window.handle, width, height); + glfwSetWindowSize(platform.handle, width, height); } // Set window opacity, value opacity is between 0.0 and 1.0 @@ -759,7 +778,7 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); + glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } // Get mouse wheel movement Y @@ -806,7 +825,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - glfwSwapBuffers(CORE.Window.handle); + glfwSwapBuffers(platform.handle); } // Register all input events @@ -1110,24 +1129,24 @@ static bool InitGraphicsDevice(int width, int height) // HighDPI monitors are properly considered in a following similar function: SetupViewport() SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + 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... - // glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + // glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (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 (CORE.Window.handle) + if (platform.handle) { CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; } } - if (!CORE.Window.handle) + if (!platform.handle) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); @@ -1138,20 +1157,20 @@ static bool InitGraphicsDevice(int width, int height) emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(platform.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwMakeContextCurrent(CORE.Window.handle); + glfwMakeContextCurrent(platform.handle); // 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 @@ -1293,7 +1312,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i } // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); #if defined(SUPPORT_SCREEN_CAPTURE) if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) diff --git a/src/utils.h b/src/utils.h index ff8246a7b..6ec0f7f1b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,7 +32,7 @@ #include // Required for: AAssetManager #endif -#if defined(SUPPORT_TRACELOG) +#if defined(SUPPORT_TRACELOG) && !defined(TRACELOG) #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #if defined(SUPPORT_TRACELOG_DEBUG) From df8d3a5afb6bec908ad0e720a1cbb2b444cb951d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 00:47:22 +0200 Subject: [PATCH 0677/1710] REVIEWED: Some warnings #3313 --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index f28aff74f..85a84f590 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1413,10 +1413,10 @@ const char *GetDirectoryPath(const char *filePath) else { // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' - unsigned char *dirPathPtr = dirPath; + 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 + dirPath[strlen(filePath) - strlen(lastSlash) + (((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0)] = '\0'; // Add '\0' manually } } From a0b30b0363cfbadb359db87ffd8fa80748485980 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 01:02:19 +0200 Subject: [PATCH 0678/1710] REVIEWED: `SetupViewport()` macOS #3313 --- src/rcore.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 85a84f590..3a8820e38 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2162,9 +2162,10 @@ void SetupViewport(int width, int 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__) - float xScale = 1.0f, yScale = 1.0f; - glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); - rlViewport(CORE.Window.renderOffset.x/2*xScale, CORE.Window.renderOffset.y/2*yScale, (CORE.Window.render.width)*xScale, (CORE.Window.render.height)*yScale); + //float xScale = 1.0f, yScale = 1.0f; + //glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); + 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 From 5ed7717f0d9e524b72f1bddc6fcd3e2e5f32e217 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 01:21:46 +0200 Subject: [PATCH 0679/1710] REVIEWED: `WaitTime()`, added validation #3377 --- src/rcore.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 3a8820e38..69b5e1a83 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2290,6 +2290,8 @@ void InitTimer(void) // Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! void WaitTime(double seconds) { + if (seconds < 0) return; + #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) double destinationTime = GetTime() + seconds; #endif From 682992e868ffd97fa25d432e15ec209a161c26d9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:45:57 +0200 Subject: [PATCH 0680/1710] REVIEWED: Reorganize functions `TakeScreenshot()` moved to `rcore.c` --- src/rcore.c | 23 ++++++++++++ src/rcore_android.c | 44 ++++++++-------------- src/rcore_desktop.c | 42 ++++++--------------- src/rcore_drm.c | 90 ++++++++++++++++++--------------------------- src/rcore_web.c | 46 ++++++----------------- 5 files changed, 97 insertions(+), 148 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 69b5e1a83..a3075aac5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2329,6 +2329,29 @@ void WaitTime(double seconds) #endif } +// Takes a screenshot of current screen (saved a .png) +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, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + // 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 diff --git a/src/rcore_android.c b/src/rcore_android.c index ad7a905c1..a7c8f3307 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -133,6 +133,10 @@ struct android_app *GetAndroidApp(void) return platform.app; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + // Initialize window and OpenGL context // NOTE: data parameter could be used to pass any kind of required data to the initialization void InitWindow(int width, int height, const char *title) @@ -563,6 +567,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(platform.device, platform.surface); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -576,29 +590,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_ANDROID - 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[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // 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. @@ -726,12 +717,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - eglSwapBuffers(platform.device, platform.surface); -} - // Register all input events void PollInputEvents(void) { @@ -787,6 +772,7 @@ void PollInputEvents(void) } } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 33a4aab0e..dcd71ef18 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -137,7 +137,7 @@ static void CursorEnterCallback(GLFWwindow *window, int enter); // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -1168,6 +1168,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(platform.handle); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -1175,30 +1185,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -// WARNING: This function requires [rtextures] module functionality -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[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // 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. @@ -1341,12 +1327,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - glfwSwapBuffers(platform.handle); -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 8c63b63da..aadf0f475 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -166,7 +166,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -675,6 +675,40 @@ void DisableCursor(void) 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) { @@ -688,29 +722,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - 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[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // 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. @@ -833,36 +844,6 @@ Vector2 GetTouchPosition(int index) return position; } -// 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; -} - // Register all input events void PollInputEvents(void) { @@ -931,6 +912,7 @@ void PollInputEvents(void) #endif } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/rcore_web.c b/src/rcore_web.c index fa296b90b..249f0db4c 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -122,7 +122,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -664,6 +664,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(platform.handle); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -671,33 +681,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - 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[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // 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. @@ -822,12 +805,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - glfwSwapBuffers(platform.handle); -} - // Register all input events void PollInputEvents(void) { @@ -944,6 +921,7 @@ void PollInputEvents(void) } } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- From ea9de852bdc6b6867fdf2805e6c5caf476b61bde Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:46:57 +0200 Subject: [PATCH 0681/1710] ADDED: Custom platform template! #3313 --- src/rcore_custom.c | 789 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 789 insertions(+) create mode 100644 src/rcore_custom.c diff --git a/src/rcore_custom.c b/src/rcore_custom.c new file mode 100644 index 000000000..f20996268 --- /dev/null +++ b/src/rcore_custom.c @@ -0,0 +1,789 @@ +/********************************************************************************************** +* +* rcore_ - Functions to manage window, graphics device and inputs +* +* PLATFORM: +* - TODO: Define the target platform for the core +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_ -not used- +* +* DEPENDENCIES: +* - Dependency 01 +* - Dependency 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +// TODO: Include the platform specific libraries + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +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; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + 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 + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + 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 = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + + // TODO: Initialize window/display system + + // TODO: Initialize input events system + + // TODO: Initialize assets manager + + // TODO: Initialize base path for storage + //CORE.Storage.basePath = platform.app->activity->internalDataPath; + + TRACELOG(LOG_INFO, "PLATFORM: Application initialized successfully"); +} + +// 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 + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + // TODO: Close surface, context and display + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return platform.appEnabled; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_CUSTOM"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_CUSTOM"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_CUSTOM"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_CUSTOM"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_CUSTOM"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_CUSTOM"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_CUSTOM"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_CUSTOM"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_CUSTOM"); +} + +// 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 PLATFORM_CUSTOM"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_CUSTOM"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_CUSTOM"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_CUSTOM"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_CUSTOM"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_CUSTOM"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_CUSTOM"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_CUSTOM"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_CUSTOM"); + 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); + + 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) +{ + eglSwapBuffers(platform.device, platform.surface); +} + +//---------------------------------------------------------------------------------- +// 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) +{ + // Security check to (partially) avoid malicious code on PLATFORM_CUSTOM + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + JNIEnv *env = NULL; + JavaVM *vm = platform.app->activity->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + + jstring urlString = (*env)->NewStringUTF(env, url); + jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); + jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); + jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); + + jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); + jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); + jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); + jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); + jobject intent = (*env)->AllocObject(env, intentClass); + + (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); + jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); + jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); + (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); + + (*vm)->DetachCurrentThread(vm); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +void SetExitKey(int key) +{ + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_CUSTOM"); +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_CUSTOM"); + return NULL; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + return GetTouchPosition(0); +} + +// 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; +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_CUSTOM"); + return 0.0f; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_CUSTOM"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// 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; +} + +// 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 PLATFORM_CUSTOM 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 plaform +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + 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_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 false; + } + + // 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); + + // 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); + + ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //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); + + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + 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); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // 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); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + CORE.Window.ready = true; + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// EOF From d309b1eaa7712dc0d15431bfb6efc0508e6ecbc8 Mon Sep 17 00:00:00 2001 From: Masoud Naservand Date: Mon, 9 Oct 2023 11:17:54 +0330 Subject: [PATCH 0682/1710] Call nsvgDeleteRasterizer() on created rasterizer (#3392) the `NSVGrasterizer *rast` needs to be passed to nsvgDeleteRasterizer() when we are done with it. --- src/rtextures.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index fa8d91ea4..dc7f4af05 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -384,6 +384,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) // Free used memory nsvgDelete(svgImage); + nsvgDeleteRasterizer(rast); } if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); @@ -555,6 +556,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; nsvgDelete(svgImage); + nsvgDeleteRasterizer(rast); } } #endif From 7ab911b9a4fb0ba3798bb8f117e69475a4fb89bc Mon Sep 17 00:00:00 2001 From: "Dennis E. Hamilton" Date: Mon, 9 Oct 2023 00:49:58 -0700 Subject: [PATCH 0683/1710] Ensure m3d faces in non-decreasing materialid sequence (#3385) This modification replaces the expensive qsort protection with an insertion sort that is near-instantaneous in the expected ordered case. --- src/rmodels.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 490eac555..77c89b6e9 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5614,19 +5614,26 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; - // WARNING: Sorting is not needed, valid M3D model files should already be sorted - // faces should already by grouped by material - // Just keeping the sorting function for reference (Check PR #3363) - /* - static int m3d_compare_faces(const void *a, const void *b) + // Faces must be in non-decreasing materialid order + // Verify that quickly, sorting them otherwise. + for (i = 1; i < m3d->numface; i++) { - m3df_t *fa = (m3df_t *)a; - m3df_t *fb = (m3df_t *)b; - return (fa->materialid - fb->materialid); + 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; } - - qsort(m3d->face, m3d->numface, sizeof(m3df_t), m3d_compare_faces); - */ model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); From 33c84b3c0092982aa3472e94a766f6bf711a31b9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:53:52 +0200 Subject: [PATCH 0684/1710] Update rmodels.c --- src/rmodels.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 77c89b6e9..0809fce80 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5614,12 +5614,13 @@ static Model LoadM3D(const char *fileName) // 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. + // 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; + 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]; @@ -5634,6 +5635,7 @@ static Model LoadM3D(const char *fileName) m3d->face[j+1] = slider; } + */ model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); From dfb0326d00c65e145472464b5ebe74802fe630cc Mon Sep 17 00:00:00 2001 From: SuperUserNameMan Date: Mon, 9 Oct 2023 09:54:43 +0200 Subject: [PATCH 0685/1710] Update rcore.c (#3326) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index a3075aac5..29881198a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -762,7 +762,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) { VrStereoConfig config = { 0 }; - if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() >= RL_OPENGL_ES_20)) + if (rlGetVersion() != RL_OPENGL_11) { // Compute aspect ratio float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution; From f86f4159e6eb4ed84cca6147c2f1e76ff2524c19 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 10:51:37 +0200 Subject: [PATCH 0686/1710] Avoid references to `PLATFORM_` flags #3313 --- src/rcore_android.c | 76 ++++++++++++++++++++++----------------------- src/rcore_custom.c | 74 +++++++++++++++++++++---------------------- src/rcore_desktop.c | 6 ++-- src/rcore_drm.c | 60 +++++++++++++++++------------------ src/rcore_web.c | 62 ++++++++++++++++++------------------ 5 files changed, 135 insertions(+), 143 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index a7c8f3307..bd0674b29 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -17,8 +17,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_ANDROID_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_ANDROID -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * Android NDK - Provides C API to access Android functionality @@ -338,55 +338,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -398,13 +398,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -424,116 +424,116 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -597,7 +597,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_ANDROID + // Security check to (partially) avoid malicious code if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { @@ -632,13 +632,13 @@ void OpenURL(const char *url) // Set a custom key to exit program void SetExitKey(int key) { - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); } // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); return NULL; } @@ -651,7 +651,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -683,14 +683,14 @@ void SetMousePosition(int x, int y) // Get mouse wheel movement Y float GetMouseWheelMove(void) { - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); return 0.0f; } // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -740,8 +740,6 @@ void PollInputEvents(void) 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 PLATFORM_ANDROID 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 @@ -1222,7 +1220,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = { 0 }; gestureEvent.pointCount = CORE.Input.Touch.pointCount; diff --git a/src/rcore_custom.c b/src/rcore_custom.c index f20996268..2d35a3b4f 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -17,8 +17,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_ -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * - Dependency 01 @@ -219,55 +219,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -279,13 +279,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -305,116 +305,116 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -478,7 +478,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_CUSTOM + // 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 { @@ -513,13 +513,13 @@ void OpenURL(const char *url) // Set a custom key to exit program void SetExitKey(int key) { - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); } // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); return NULL; } @@ -532,7 +532,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -564,14 +564,14 @@ void SetMousePosition(int x, int y) // Get mouse wheel movement Y float GetMouseWheelMove(void) { - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); return 0.0f; } // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -622,7 +622,7 @@ void PollInputEvents(void) 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 PLATFORM_CUSTOM the mouse position and not filled again until a move-event, + // 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 }; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index dcd71ef18..2c9872a34 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -20,8 +20,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_DESKTOP_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_DESKTOP -not used- +* #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...) @@ -1368,8 +1368,6 @@ void PollInputEvents(void) 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 PLATFORM_WEB 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 }; // Check if gamepads are ready diff --git a/src/rcore_drm.c b/src/rcore_drm.c index aadf0f475..50e45c4ae 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -442,55 +442,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -502,13 +502,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -528,74 +528,74 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); return 0; } @@ -615,7 +615,7 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); return ""; } @@ -634,14 +634,14 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -729,7 +729,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - TRACELOG(LOG_WARNING, "OpenURL() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform"); } //---------------------------------------------------------------------------------- @@ -770,7 +770,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -818,7 +818,7 @@ float GetMouseWheelMove(void) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -897,8 +897,6 @@ void PollInputEvents(void) 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 PLATFORM_WEB 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 }; #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -1911,7 +1909,7 @@ static void *EventThread(void *arg) if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; } -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM +#if defined(SUPPORT_GESTURES_SYSTEM) if (gestureUpdate) { GestureEvent gestureEvent = { 0 }; diff --git a/src/rcore_web.c b/src/rcore_web.c index 249f0db4c..111666045 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -16,8 +16,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_WEB_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_WEB -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * emscripten - Allow interaction between browser API and C @@ -413,49 +413,49 @@ void ToggleFullscreen(void) // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -468,13 +468,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -506,96 +506,96 @@ void SetWindowSize(int width, int height) // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); return (Vector2){ 1.0f, 1.0f }; } @@ -688,7 +688,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_WEB + // 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)); } @@ -745,9 +745,7 @@ Vector2 GetMousePosition(void) { Vector2 position = { 0 }; - // TODO: Review touch position - - // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned + // NOTE: On canvas scaling, mouse position is proportionally returned 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; @@ -846,7 +844,7 @@ void PollInputEvents(void) 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 PLATFORM_WEB the mouse position and not filled again until a move-event, + // 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 }; @@ -1567,7 +1565,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; } -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = {0}; gestureEvent.pointCount = CORE.Input.Touch.pointCount; From b55cf40b910a428cba7e69ea9133d555a63b818b Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:07:02 +0200 Subject: [PATCH 0687/1710] Format tweaks --- src/rcore.h | 10 +++++----- src/rcore_android.c | 19 ++++++++++++------- src/rcore_custom.c | 25 ++++++++++++------------- src/rcore_desktop.c | 21 ++++++++++++--------- src/rcore_drm.c | 31 +++++++++++++++++-------------- src/rcore_web.c | 17 ++++++++--------- src/rmodels.c | 8 ++++---- 7 files changed, 70 insertions(+), 61 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 132c8e007..7fb35915a 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -133,18 +133,18 @@ typedef struct CoreData { 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. @@ -177,7 +177,7 @@ typedef struct CoreData { 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 @@ -199,7 +199,7 @@ typedef struct CoreData { 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; diff --git a/src/rcore_android.c b/src/rcore_android.c index bd0674b29..a3db7e5fa 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_android - Functions to manage window, graphics device and inputs +* rcore_android - Functions to manage window, graphics device and inputs * * PLATFORM: ANDROID * - Android (ARM, ARM64) @@ -48,13 +48,13 @@ #include "rcore.h" -//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) -#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others #include // Required for: android_app struct and activity management +#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others +//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] #include // Native platform windowing system interface - + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -64,7 +64,7 @@ typedef struct { struct android_poll_source *source; // Android events polling source bool appEnabled; // Flag to detect if app is active ** = true bool contextRebindRequired; // Used to know context rebind required - + // Display data EGLDisplay device; // Native display device (physical screen connection) EGLSurface surface; // Surface to draw on, framebuffers (connected to context) @@ -94,7 +94,7 @@ static GamepadButton AndroidTranslateGamepadButton(int button); // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Application //---------------------------------------------------------------------------------- // To allow easier porting to android, we allow the user to define a @@ -188,6 +188,8 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; + // Platform specific init window + //-------------------------------------------------------------- // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -269,6 +271,8 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif + // Platform specific close window + //-------------------------------------------------------------- // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) { @@ -289,6 +293,7 @@ void CloseWindow(void) eglTerminate(platform.device); platform.device = EGL_NO_DISPLAY; } + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -586,7 +591,7 @@ double GetTime(void) 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; } diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 2d35a3b4f..799c6054b 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_ - Functions to manage window, graphics device and inputs +* rcore_ - Functions to manage window, graphics device and inputs * * PLATFORM: * - TODO: Define the target platform for the core @@ -48,14 +48,14 @@ #include "rcore.h" -// TODO: Include the platform specific libraries - +// TODO: Include the platform specific libraries + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- 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) @@ -128,15 +128,15 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Window.eventWaiting = false; CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; // TODO: Initialize window/display system - + // TODO: Initialize input events system // TODO: Initialize assets manager @@ -165,11 +165,10 @@ void CloseWindow(void) rlglClose(); // De-init rlgl -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // TODO: Close surface, context and display + // Platform specific close window + //-------------------------------------------------------------- + // TODO. + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -467,7 +466,7 @@ double GetTime(void) 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; } @@ -610,7 +609,7 @@ 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; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 2c9872a34..2cf509507 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_desktop - Functions to manage window, graphics device and inputs +* rcore_desktop - Functions to manage window, graphics device and inputs * * PLATFORM: DESKTOP * - Windows (Win32, Win64) @@ -268,12 +268,15 @@ void CloseWindow(void) rlglClose(); // De-init rlgl + // Platform specific close window + //-------------------------------------------------------------- glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) timeEndPeriod(1); // Restore time period #endif + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -422,11 +425,11 @@ void ToggleBorderlessWindowed(void) const int monitor = GetCurrentMonitor(); int monitorCount; GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - + if ((monitor >= 0) && (monitor < monitorCount)) { const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - + if (mode) { if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) @@ -1016,7 +1019,7 @@ int GetMonitorHeight(int monitor) else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return height; } @@ -1029,7 +1032,7 @@ int GetMonitorPhysicalWidth(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL); else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return width; } @@ -1042,7 +1045,7 @@ int GetMonitorPhysicalHeight(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height); else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return height; } @@ -1059,7 +1062,7 @@ int GetMonitorRefreshRate(int monitor) refresh = vidmode->refreshRate; } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return refresh; } @@ -1082,9 +1085,9 @@ Vector2 GetWindowPosition(void) { int x = 0; int y = 0; - + glfwGetWindowPos(platform.handle, &x, &y); - + return (Vector2){ (float)x, (float)y }; } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 50e45c4ae..4a0d805f8 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_drm - Functions to manage window, graphics device and inputs +* rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM * - Raspberry Pi 0-5 @@ -81,7 +81,7 @@ typedef struct { pthread_t threadId; // Event reading thread id - + int fd; // File descriptor to the device it is assigned to int eventNum; // Number of 'event' device Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) @@ -111,7 +111,7 @@ typedef struct { // Input data InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" - + // Keyboard data int defaultKeyboardMode; // Default keyboard mode bool eventKeyboardMode; // Keyboard in event mode @@ -307,6 +307,8 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif + // Platform specific close window + //-------------------------------------------------------------- if (platform.prevFB) { drmModeRmFB(platform.fd, platform.prevFB); @@ -392,6 +394,7 @@ void CloseWindow(void) } if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -603,12 +606,12 @@ int GetMonitorPhysicalHeight(int monitor) int GetMonitorRefreshRate(int monitor) { int refresh = 0; - + if ((platform.connector) && (platform.modeIndex >= 0)) { refresh = platform.connector->modes[platform.modeIndex].vrefresh; } - + return refresh; } @@ -718,7 +721,7 @@ double GetTime(void) 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; } @@ -763,7 +766,7 @@ int GetGamepadAxisCount(int gamepad) int axisCount = 0; if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; - + return CORE.Input.Gamepad.axisCount; } @@ -837,10 +840,10 @@ int GetTouchY(void) 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; } @@ -856,7 +859,7 @@ 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; @@ -979,14 +982,14 @@ static bool InitGraphicsDevice(int width, int height) } 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); - + if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) { TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); @@ -1440,7 +1443,7 @@ static void ProcessKeyboard(void) } #endif // SUPPORT_SSH_KEYBOARD_RPI -// Initialise user input from evdev(/dev/input/event) +// Initialise user input from evdev(/dev/input/event) // this means mouse, keyboard or gamepad devices static void InitEvdevInput(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index 111666045..f4f412f2a 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_web - Functions to manage window, graphics device and inputs +* rcore_web - Functions to manage window, graphics device and inputs * * PLATFORM: WEB * - HTML5 (WebAssembly) @@ -237,7 +237,7 @@ void InitWindow(int width, int height, const char *title) // 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) emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - + // Trigger this once to get initial window sizing EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); @@ -282,12 +282,11 @@ void CloseWindow(void) rlglClose(); // De-init rlgl + // Platform specific close window + //-------------------------------------------------------------- glfwDestroyWindow(platform.handle); glfwTerminate(); - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -482,7 +481,7 @@ 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 ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); } @@ -1090,7 +1089,7 @@ static bool InitGraphicsDevice(int width, int height) } } } - + 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, @@ -1229,7 +1228,7 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified) // GLFW3 Window Maximize Callback, runs when window is maximized static void WindowMaximizeCallback(GLFWwindow *window, int maximized) { - + } // GLFW3 WindowFocus Callback, runs when window get/lose focus diff --git a/src/rmodels.c b/src/rmodels.c index 0809fce80..a11ecf799 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5466,11 +5466,11 @@ static Model LoadVOX(const char *fileName) 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); @@ -5575,7 +5575,7 @@ static Model LoadM3D(const char *fileName) 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); @@ -5905,7 +5905,7 @@ static Model LoadM3D(const char *fileName) static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount) { ModelAnimation *animations = NULL; - + m3d_t *m3d = NULL; int i = 0, j = 0; *animCount = 0; From f93d0ff9bc72749e66b99e8813fb41fd7b8bb813 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:17:09 +0200 Subject: [PATCH 0688/1710] Update raudio.c --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 61e00a386..c2590e302 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1906,7 +1906,7 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRead = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); + int frameCountRead = (int)drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); frameCountReadTotal += frameCountRead; frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; From cfffa74f96c06231f6fa327e9cc52863b18d52b2 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:17:22 +0200 Subject: [PATCH 0689/1710] REVIEWED: Libs include order --- src/rcore.h | 8 ++++---- src/utils.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 7fb35915a..4d40c9567 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -37,12 +37,12 @@ #include "raylib.h" -#include "rlgl.h" +#include "utils.h" // Required for: TRACELOG() macros + +#include "rlgl.h" // Required for: graphics layer functionality #define RAYMATH_IMPLEMENTATION -#include "raymath.h" - -#include "utils.h" // Required for: TRACELOG() macros +#include "raymath.h" // Required for: Vector2/Vector3/Matrix functionality #include // Required for: srand(), rand(), atexit() #include // Required for: sprintf() [Used in OpenURL()] diff --git a/src/utils.h b/src/utils.h index 6ec0f7f1b..ff8246a7b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,7 +32,7 @@ #include // Required for: AAssetManager #endif -#if defined(SUPPORT_TRACELOG) && !defined(TRACELOG) +#if defined(SUPPORT_TRACELOG) #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #if defined(SUPPORT_TRACELOG_DEBUG) From 540ad9944205235cd9ccbd716c5a667daf929616 Mon Sep 17 00:00:00 2001 From: Purple4pur <49893724+purple4pur@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:05:19 +0800 Subject: [PATCH 0690/1710] Update zig build system to zig version 0.11.0 (#3393) * update build.zig for zig 0.11.0 * fix build.zig in examples to install executable correctly * discard build.zig, only use src/build.zig, to avoid annoying zig-out path problem * update zig version note --- build.zig | 7 ------- examples/build.zig | 36 ++++++++++++++++++++---------------- src/build.zig | 10 +++++----- 3 files changed, 25 insertions(+), 28 deletions(-) delete mode 100644 build.zig diff --git a/build.zig b/build.zig deleted file mode 100644 index 12c0513f6..000000000 --- a/build.zig +++ /dev/null @@ -1,7 +0,0 @@ -const std = @import("std"); -const raylib = @import("src/build.zig"); - -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 -pub fn build(b: *std.Build) void { - raylib.build(b); -} diff --git a/examples/build.zig b/examples/build.zig index 6e13ab3da..383d9f651 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .File) continue; + if (entry.kind != .file) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(path, &[_][]const u8{}); + exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => "../src/zig-out/lib/raylib.lib", - .linux => "../src/zig-out/lib/libraylib.a", - .macos => "../src/zig-out/lib/libraylib.a", - .emscripten => "../src/zig-out/lib/libraylib.a", + .windows => .{ .path = "../src/zig-out/lib/raylib.lib" }, + .linux => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .macos => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .emscripten => .{ .path = "../src/zig-out/lib/libraylib.a" }, else => @panic("Unsupported OS"), }); - exe.addIncludePath("../src"); - exe.addIncludePath("../src/external"); - exe.addIncludePath("../src/external/glfw/include"); + exe.addIncludePath(.{ .path = "../src" }); + exe.addIncludePath(.{ .path = "../src/external" }); + exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath("external/glfw/deps/mingw"); + exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,11 +71,15 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - b.installArtifact(exe); - var run = b.addRunArtifact(exe); - run.cwd = module; - b.step(name, name).dependOn(&run.step); - all.dependOn(&exe.step); + const install_cmd = b.addInstallArtifact(exe, .{}); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(&install_cmd.step); + + const run_step = b.step(name, name); + run_step.dependOn(&run_cmd.step); + + all.dependOn(&install_cmd.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 27250f5ff..3bb4f4213 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -192,12 +192,12 @@ pub fn build(b: *std.Build) void { const lib = addRaylib(b, target, optimize, options); - lib.installHeader("src/raylib.h", "raylib.h"); - lib.installHeader("src/raymath.h", "raymath.h"); - lib.installHeader("src/rlgl.h", "rlgl.h"); + lib.installHeader("raylib.h", "raylib.h"); + lib.installHeader("raymath.h", "raymath.h"); + lib.installHeader("rlgl.h", "rlgl.h"); if (options.raygui) { - lib.installHeader("../raygui/src/raygui.h", "raygui.h"); + lib.installHeader("../../raygui/src/raygui.h", "raygui.h"); } b.installArtifact(lib); From 0d8a6cfbfa474be123cedff6a4faddfe5443fcec Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 08:48:55 +0200 Subject: [PATCH 0691/1710] Revert "Update zig build system to zig version 0.11.0 (#3393)" This reverts commit 540ad9944205235cd9ccbd716c5a667daf929616. --- build.zig | 7 +++++++ examples/build.zig | 36 ++++++++++++++++-------------------- src/build.zig | 10 +++++----- 3 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 build.zig diff --git a/build.zig b/build.zig new file mode 100644 index 000000000..12c0513f6 --- /dev/null +++ b/build.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const raylib = @import("src/build.zig"); + +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +pub fn build(b: *std.Build) void { + raylib.build(b); +} diff --git a/examples/build.zig b/examples/build.zig index 383d9f651..6e13ab3da 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .file) continue; + if (entry.kind != .File) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); + exe.addCSourceFile(path, &[_][]const u8{}); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => .{ .path = "../src/zig-out/lib/raylib.lib" }, - .linux => .{ .path = "../src/zig-out/lib/libraylib.a" }, - .macos => .{ .path = "../src/zig-out/lib/libraylib.a" }, - .emscripten => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .windows => "../src/zig-out/lib/raylib.lib", + .linux => "../src/zig-out/lib/libraylib.a", + .macos => "../src/zig-out/lib/libraylib.a", + .emscripten => "../src/zig-out/lib/libraylib.a", else => @panic("Unsupported OS"), }); - exe.addIncludePath(.{ .path = "../src" }); - exe.addIncludePath(.{ .path = "../src/external" }); - exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); + exe.addIncludePath("../src"); + exe.addIncludePath("../src/external"); + exe.addIncludePath("../src/external/glfw/include"); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); + exe.addIncludePath("external/glfw/deps/mingw"); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,15 +71,11 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - const install_cmd = b.addInstallArtifact(exe, .{}); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.step.dependOn(&install_cmd.step); - - const run_step = b.step(name, name); - run_step.dependOn(&run_cmd.step); - - all.dependOn(&install_cmd.step); + b.installArtifact(exe); + var run = b.addRunArtifact(exe); + run.cwd = module; + b.step(name, name).dependOn(&run.step); + all.dependOn(&exe.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 3bb4f4213..27250f5ff 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -192,12 +192,12 @@ pub fn build(b: *std.Build) void { const lib = addRaylib(b, target, optimize, options); - lib.installHeader("raylib.h", "raylib.h"); - lib.installHeader("raymath.h", "raymath.h"); - lib.installHeader("rlgl.h", "rlgl.h"); + lib.installHeader("src/raylib.h", "raylib.h"); + lib.installHeader("src/raymath.h", "raymath.h"); + lib.installHeader("src/rlgl.h", "rlgl.h"); if (options.raygui) { - lib.installHeader("../../raygui/src/raygui.h", "raygui.h"); + lib.installHeader("../raygui/src/raygui.h", "raygui.h"); } b.installArtifact(lib); From f0d949f931e286773aa0d0b87af4f58214ef611b Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Tue, 10 Oct 2023 02:59:09 -0400 Subject: [PATCH 0692/1710] Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. (#3394) --- examples/others/raymath_vector_angle.c | 56 +++++++++++++++++--------- src/raymath.h | 5 ++- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index b193648fe..ab8ccf571 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -2,7 +2,7 @@ * * raylib [shapes] example - Vector Angle * -* Example originally created with raylib 1.0, last time updated with raylib 4.2 +* 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 @@ -10,7 +10,7 @@ * Copyright (c) 2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ - + #include "raylib.h" #include "raymath.h" @@ -28,7 +28,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [math] example - vector angle"); Vector2 v0 = { screenWidth/2, screenHeight/2 }; - Vector2 v1 = { 100.0f, 80.0f }; + Vector2 v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); Vector2 v2 = { 0 }; // Updated with mouse position float angle = 0.0f; // Angle in degrees @@ -42,21 +42,29 @@ int main(void) { // Update //---------------------------------------------------------------------------------- + float startangle; + + 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) - v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); - v2 = GetMousePosition(); - angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; + 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 - v1 = (Vector2){ screenWidth/2, screenHeight/2 }; - v2 = GetMousePosition(); - angle = Vector2LineAngle(v1, v2)*RAD2DEG; + angle = Vector2LineAngle(v0, v2)*RAD2DEG; } //---------------------------------------------------------------------------------- @@ -66,32 +74,40 @@ int main(void) ClearBackground(RAYWHITE); - if (angleMode == 0) DrawText("v0", v0.x, v0.y, 10, DARKGRAY); - DrawText("v1", v1.x, v1.y, 10, DARKGRAY); - DrawText("v2", v2.x, v2.y, 10, DARKGRAY); - if (angleMode == 0) { - DrawText("MODE: Angle between V1 and V2", 10, 10, 20, BLACK); + 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); - float startangle = 90 - Vector2LineAngle(v0, v1)*RAD2DEG; - DrawCircleSector(v0, 40.0f, startangle, startangle + angle - 360.0f*(angle > 180.0f), 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { - DrawText("MODE: Angle formed by line V1 to V2", 10, 10, 20, BLACK); + DrawText("MODE 1: Angle formed by line V1 to V2", 10, 10, 20, BLACK); DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); - DrawLineEx(v1, v2, 2.0f, RED); + DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v1, 40.0f, 90.0f, 180 - angle - 90, 32, Fade(GREEN, 0.6f)); + 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, 40, 20, LIME); + DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 70, 20, LIME); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/raymath.h b/src/raymath.h index db04c51ee..b01b0b22b 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -323,6 +323,8 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) float dot = v1.x*v2.x + v1.y*v2.y; float det = v1.x*v2.y - v1.y*v2.x; + + // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior result = -atan2f(det, dot); return result; @@ -335,7 +337,8 @@ RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - result = atan2f(end.y - start.y, end.x - start.x); + // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior + result = -atan2f(end.y - start.y, end.x - start.x); return result; } From 9702a17152d137766d85cd02060219787e3e228b Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Tue, 10 Oct 2023 04:42:11 -0400 Subject: [PATCH 0693/1710] [raymath] Hotfix for Vector2Angle() and Vector2LineAngle() (#3396) * Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. * Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. * [raymath] Hotfix for Vector2Angle and corresponding example * [raymath] Hotfix for Vector2Angle and corresponding example --------- Co-authored-by: Ray --- examples/others/raymath_vector_angle.c | 10 +++++----- src/raymath.h | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index ab8ccf571..dc6887a41 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -44,7 +44,7 @@ int main(void) //---------------------------------------------------------------------------------- float startangle; - if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; + if (angleMode == 0) startangle = Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; v2 = GetMousePosition(); @@ -81,8 +81,8 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - - DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); + + DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { @@ -90,8 +90,8 @@ int main(void) 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)); + + DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f)); } DrawText("v0", v0.x, v0.y, 10, DARKGRAY); diff --git a/src/raymath.h b/src/raymath.h index b01b0b22b..ff6017039 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -324,8 +324,7 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) float dot = v1.x*v2.x + v1.y*v2.y; float det = v1.x*v2.y - v1.y*v2.x; - // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior - result = -atan2f(det, dot); + result = atan2f(det, dot); return result; } From cb571659565f4555fafd18108e369b706a83775e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:48:30 +0200 Subject: [PATCH 0694/1710] REVIEWED: Fix #3387 --- examples/others/raymath_vector_angle.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index dc6887a41..ad573f54d 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -44,7 +44,7 @@ int main(void) //---------------------------------------------------------------------------------- float startangle; - if (angleMode == 0) startangle = Vector2LineAngle(v0, v1)*RAD2DEG; + if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; v2 = GetMousePosition(); @@ -91,7 +91,7 @@ int main(void) 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)); + DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } DrawText("v0", v0.x, v0.y, 10, DARKGRAY); From 67a1e1ffaeb6d12004028ce50d687ffb4be1cbf7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:48:46 +0200 Subject: [PATCH 0695/1710] Update rtextures.c --- src/rtextures.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index dc7f4af05..8624bbd48 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -664,9 +664,9 @@ void UnloadImage(Image image) // NOTE: File format depends on fileName extension bool ExportImage(Image image, const char *fileName) { - int success = 0; + int result = 0; - if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return success; + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result; #if defined(SUPPORT_IMAGE_EXPORT) int channels = 4; @@ -689,21 +689,21 @@ bool ExportImage(Image image, const char *fileName) { int dataSize = 0; unsigned char *fileData = stbi_write_png_to_mem((const unsigned char *)imgData, image.width*channels, image.width, image.height, channels, &dataSize); - success = SaveFileData(fileName, fileData, dataSize); + result = SaveFileData(fileName, fileData, dataSize); RL_FREE(fileData); } #else if (false) { } #endif #if defined(SUPPORT_FILEFORMAT_BMP) - else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, channels, imgData); + else if (IsFileExtension(fileName, ".bmp")) result = stbi_write_bmp(fileName, image.width, image.height, channels, imgData); #endif #if defined(SUPPORT_FILEFORMAT_TGA) - else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, channels, imgData); + else if (IsFileExtension(fileName, ".tga")) result = stbi_write_tga(fileName, image.width, image.height, channels, imgData); #endif #if defined(SUPPORT_FILEFORMAT_JPG) else if (IsFileExtension(fileName, ".jpg") || - IsFileExtension(fileName, ".jpeg")) success = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100 + IsFileExtension(fileName, ".jpeg")) result = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100 #endif #if defined(SUPPORT_FILEFORMAT_QOI) else if (IsFileExtension(fileName, ".qoi")) @@ -721,30 +721,30 @@ bool ExportImage(Image image, const char *fileName) desc.channels = channels; desc.colorspace = QOI_SRGB; - success = qoi_write(fileName, imgData, &desc); + result = qoi_write(fileName, imgData, &desc); } } #endif #if defined(SUPPORT_FILEFORMAT_KTX) else if (IsFileExtension(fileName, ".ktx")) { - success = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); + result = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); } #endif else if (IsFileExtension(fileName, ".raw")) { // Export raw pixel data (without header) // NOTE: It's up to the user to track image parameters - success = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format)); + result = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format)); } if (allocatedData) RL_FREE(imgData); #endif // SUPPORT_IMAGE_EXPORT - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); + if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName); - return success; + return result; } // Export image to memory buffer From b94e6290a4e5e96196e23d66a480cdf9d707c380 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:50:09 +0200 Subject: [PATCH 0696/1710] Added some comments and tweaks #3313 --- src/rcore_android.c | 3 ++- src/rcore_custom.c | 66 ++++++++++++++++++++++++++++++++++++++++----- src/rcore_desktop.c | 32 +++++++++++----------- src/rcore_drm.c | 26 +++++++++--------- src/rcore_web.c | 21 ++++++++------- 5 files changed, 103 insertions(+), 45 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index a3db7e5fa..6b43b78b4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -228,7 +228,7 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; - TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); // Android ALooper_pollAll() variables int pollResult = 0; @@ -247,6 +247,7 @@ void InitWindow(int width, int height, const char *title) //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } + //-------------------------------------------------------------- } // Close window and unload OpenGL context diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 799c6054b..933f222fb 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -135,16 +135,70 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - // TODO: Initialize window/display system + // Initialize graphics device + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); - // TODO: Initialize input events system + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } + + // Initialize hi-res timer + InitTimer(); - // TODO: Initialize assets manager + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); - // TODO: Initialize base path for storage - //CORE.Storage.basePath = platform.app->activity->internalDataPath; + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && 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 +#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 +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif - TRACELOG(LOG_INFO, "PLATFORM: Application initialized successfully"); +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // TODO: Platform specific init window + //-------------------------------------------------------------- + // ... + //-------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } // Close window and unload OpenGL context diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 2cf509507..4fd86d93e 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -186,23 +186,19 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; - // Initialize graphics device (display device and OpenGL context) + // Initialize graphics device // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -248,6 +244,8 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } // Close window and unload OpenGL context @@ -834,10 +832,12 @@ void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; - int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -846,10 +846,12 @@ void SetWindowMaxSize(int width, int height) { CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; - int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 4a0d805f8..c7823f820 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -220,19 +220,14 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } - else - SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -274,15 +269,20 @@ void InitWindow(int width, int height, const char *title) } #endif - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif + + // Platform specific init window + //-------------------------------------------------------------- + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //-------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } // Close window and unload OpenGL context diff --git a/src/rcore_web.c b/src/rcore_web.c index f4f412f2a..83acae02f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -176,18 +176,14 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -229,6 +225,13 @@ void InitWindow(int width, int height, const char *title) } #endif +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // Platform specific init window + //-------------------------------------------------------------- // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -257,11 +260,9 @@ void InitWindow(int width, int height, const char *title) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //-------------------------------------------------------------- -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif + TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } // Close window and unload OpenGL context From 101a9b04458a38413a29a11f22e6c8e0a472a75b Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 11:59:41 +0200 Subject: [PATCH 0697/1710] Added comments and review some functions #3313 --- src/raylib.h | 10 +- src/rcore.c | 244 ++++++++++++++++++++++++++++---------------- src/rcore_android.c | 9 +- src/rcore_custom.c | 21 ++-- src/rcore_desktop.c | 29 +++--- src/rcore_drm.c | 8 +- src/rcore_web.c | 13 +-- 7 files changed, 191 insertions(+), 143 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 5fa66bd03..5d63d4b62 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1058,15 +1058,16 @@ RLAPI int GetRandomValue(int min, int max); // Get a rando RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator 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 -RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) - // Set custom callbacks // WARNING: Callbacks setup is intended for advance users RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log @@ -1083,6 +1084,9 @@ RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char 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 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) @@ -1120,9 +1124,9 @@ RLAPI bool IsKeyPressedRepeat(int key); // Check if a key 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 void SetExitKey(int key); // Set a custom key to exit program (default is ESC) 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) // Input-related functions: gamepads RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available diff --git a/src/rcore.c b/src/rcore.c index 29881198a..fa8f50d86 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -315,14 +315,12 @@ const char *TextFormat(const char *text, ...); // Formatting of text with #endif //---------------------------------------------------------------------------------- -// Module Functions Definition - Window and OpenGL Context Functions +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// NOTE: Multiple window/display/monitor management functions have been moved to platform-specific modules - -// Platform-specific functions: -//void InitWindow(int width, int height, const char *title); -//void CloseWindow(void); +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void InitWindow(int width, int height, const char *title) +//void CloseWindow(void) //bool WindowShouldClose(void) //bool IsWindowHidden(void) //bool IsWindowMinimized(void) @@ -364,9 +362,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void HideCursor(void) //void EnableCursor(void) //void DisableCursor(void) -//double GetTime(void) -//void TakeScreenshot(const char *fileName) -//void OpenURL(const char *url) // Check if window has been initialized successfully @@ -435,6 +430,19 @@ bool IsCursorOnScreen(void) return CORE.Input.Mouse.cursorOnScreen; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Custom frame control +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void SwapScreenBuffer(void); +//void PollInputEvents(void); +//void WaitTime(double seconds); + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Screen Drawing +//---------------------------------------------------------------------------------- + // Set background color (framebuffer clear color) void ClearBackground(Color color) { @@ -741,6 +749,10 @@ void EndScissorMode(void) rlDisableScissorTest(); } +//---------------------------------------------------------------------------------- +// Module Functions Definition: VR Stereo Rendering +//---------------------------------------------------------------------------------- + // Begin VR drawing configuration void BeginVrStereoMode(VrStereoConfig config) { @@ -837,6 +849,10 @@ 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) @@ -1002,6 +1018,10 @@ void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture) } } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Screen-space Queries +//---------------------------------------------------------------------------------- + // Get a ray trace from mouse position Ray GetMouseRay(Vector2 mouse, Camera camera) { @@ -1161,6 +1181,13 @@ Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera) return (Vector2){ transform.x, transform.y }; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Timming +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//double GetTime(void) + // Set target FPS (maximum) void SetTargetFPS(int fps) { @@ -1209,6 +1236,63 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void OpenURL(const char *url) + +// Get a random value between min and max (both included) +// 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 +int GetRandomValue(int min, int max) +{ + if (min > max) + { + int tmp = max; + max = min; + min = tmp; + } + + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) + { + TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); + } + + return (rand()%(abs(max - min) + 1) + min); +} + +// Set the seed for the random number generator +void SetRandomSeed(unsigned int seed) +{ + srand(seed); +} + +// Takes a screenshot of current screen (saved a .png) +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, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", 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. @@ -1221,7 +1305,7 @@ void SetConfigFlags(unsigned int flags) } //---------------------------------------------------------------------------------- -// Module Functions Definition: FileSystem +// Module Functions Definition: File system //---------------------------------------------------------------------------------- // Check if the file exists @@ -1669,36 +1753,9 @@ long GetFileModTime(const char *fileName) } //---------------------------------------------------------------------------------- -// Module Functions Definition: Misc +// Module Functions Definition: Compression and Encoding //---------------------------------------------------------------------------------- -// Get a random value between min and max (both included) -// 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 -int GetRandomValue(int min, int max) -{ - if (min > max) - { - int tmp = max; - max = min; - min = tmp; - } - - if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) - { - TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); - } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); -} - // Compress data (DEFLATE algorithm) unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize) { @@ -1841,26 +1898,9 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) } //---------------------------------------------------------------------------------- -// Module Functions Definition: Inputs +// Module Functions Definition: Input Handling: Keyboard //---------------------------------------------------------------------------------- -// Platform-specific functions -//void SetExitKey(int key) -//const char *GetGamepadName(int gamepad) -//int GetGamepadAxisCount(int gamepad) -//int SetGamepadMappings(const char *mappings) -//int GetMouseX(void) -//int GetMouseY(void) -//Vector2 GetMousePosition(void) -//void SetMousePosition(int x, int y) -//float GetMouseWheelMove(void) -//void SetMouseCursor(int cursor) -//int GetTouchX(void) -//int GetTouchY(void) -//Vector2 GetTouchPosition(int index) -//void SwapScreenBuffer(void) -//void PollInputEvents(void) - // Check if a key has been pressed once bool IsKeyPressed(int key) { @@ -1971,6 +2011,22 @@ int GetCharPressed(void) 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_.c +//int GetGamepadAxisCount(int gamepad) ** +//const char *GetGamepadName(int gamepad) ** +//int SetGamepadMappings(const char *mappings) + // Check if a gamepad is available bool IsGamepadAvailable(int gamepad) { @@ -1981,16 +2037,11 @@ bool IsGamepadAvailable(int gamepad) return result; } -// Get axis movement vector for a gamepad -float GetGamepadAxisMovement(int gamepad, int axis) -{ - float value = 0; - - if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) && - (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA - - return value; -} +// Get gamepad internal name id +//const char *GetGamepadName(int gamepad) +//{ +// return CORE.Input.Gamepad.ready[gamepad]; +//} // Check if a gamepad button has been pressed once bool IsGamepadButtonPressed(int gamepad, int button) @@ -2042,6 +2093,35 @@ int GetGamepadButtonPressed(void) return CORE.Input.Gamepad.lastButtonPressed; } +// Get gamepad axis count +//int GetGamepadAxisCount(int gamepad) +//{ +// return CORE.Input.Gamepad.axisCount; +//} + +// Get axis movement vector for a gamepad +float GetGamepadAxisMovement(int gamepad, int axis) +{ + float value = 0; + + if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) && + (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA + + return value; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Input Handling: Mouse +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//int GetMouseX(void) ** +//int GetMouseY(void) ** +//Vector2 GetMousePosition(void) ** +//void SetMousePosition(int x, int y) +//float GetMouseWheelMove(void) ** +//void SetMouseCursor(int cursor) + // Check if a mouse button has been pressed once bool IsMouseButtonPressed(int button) { @@ -2129,6 +2209,15 @@ Vector2 GetMouseWheelMoveV(void) return result; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Input Handling: Touch +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//int GetTouchX(void) +//int GetTouchY(void) +//Vector2 GetTouchPosition(int index) + // Get touch point identifier for given index int GetTouchPointId(int index) { @@ -2329,29 +2418,6 @@ void WaitTime(double seconds) #endif } -// Takes a screenshot of current screen (saved a .png) -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, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // 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 diff --git a/src/rcore_android.c b/src/rcore_android.c index 6b43b78b4..a9fc5fb9b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -635,17 +635,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -void SetExitKey(int key) -{ - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); - return NULL; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 933f222fb..e17aa96b1 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -563,17 +563,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -void SetExitKey(int key) -{ - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); - return NULL; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count @@ -592,19 +585,25 @@ int SetGamepadMappings(const char *mappings) // Get mouse position X int GetMouseX(void) { - return (int)CORE.Input.Touch.position[0].x; + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); } // Get mouse position Y int GetMouseY(void) { - return (int)CORE.Input.Touch.position[0].y; + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); } // Get mouse position XY Vector2 GetMousePosition(void) { - return GetTouchPosition(0); + Vector2 position = { 0 }; + + // NOTE: On canvas scaling, mouse position is proportionally returned + 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; } // Set mouse position XY diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 4fd86d93e..c410b713b 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -130,6 +130,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int 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 JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -1221,21 +1222,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count @@ -1731,6 +1721,7 @@ static bool InitGraphicsDevice(int width, int height) glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes glfwSetScrollCallback(platform.handle, MouseScrollCallback); glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); + glfwSetJoystickCallback(JoystickCallback); glfwMakeContextCurrent(platform.handle); @@ -2066,5 +2057,17 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) else CORE.Input.Mouse.cursorOnScreen = false; } -// EOF +// GLFW3 Joystick Connected/Disconnected Callback +static void JoystickCallback(int jid, int event) +{ + if (event == GLFW_CONNECTED) + { + strcpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid)); + } + else if (event == GLFW_DISCONNECTED) + { + memset(CORE.Input.Gamepad.name[jid], 0, 64); + } +} +// EOF diff --git a/src/rcore_drm.c b/src/rcore_drm.c index c7823f820..ccf44feb3 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -739,13 +739,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { @@ -764,6 +757,7 @@ const char *GetGamepadName(int gamepad) int GetGamepadAxisCount(int gamepad) { int axisCount = 0; + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; diff --git a/src/rcore_web.c b/src/rcore_web.c index 83acae02f..ed7dc2657 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -697,21 +697,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - name = CORE.Input.Gamepad.name[gamepad]; - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count From daba1a27945d6be914e71f71fe8e7cfaef1eca5e Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Wed, 11 Oct 2023 04:30:51 -0400 Subject: [PATCH 0698/1710] Split drm update input (#3397) * Update `PLATFORM_DRM` implementation of `GetGamepadAxisCount` * Update * Update `PLATFORM_DRM` implementation of `GetGamepadName` * Add example to test gamepad info functions Fix typo * Update new gamepad info example * Move axis count update out of GamepadThread - race condition * Remove pointless if statement --- examples/Makefile | 1 + examples/core/core_input_gamepad_info.c | 60 +++++++++++++++++++++++++ src/rcore_drm.c | 18 ++------ 3 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 examples/core/core_input_gamepad_info.c diff --git a/examples/Makefile b/examples/Makefile index 9404d39cc..6031f05e9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -365,6 +365,7 @@ CORE = \ core/core_input_mouse \ core/core_input_mouse_wheel \ core/core_input_gamepad \ + core/core_input_gamepad_info \ core/core_input_multitouch \ core/core_input_gestures \ core/core_input_gestures_web \ diff --git a/examples/core/core_input_gamepad_info.c b/examples/core/core_input_gamepad_info.c new file mode 100644 index 000000000..84a687cd8 --- /dev/null +++ b/examples/core/core_input_gamepad_info.c @@ -0,0 +1,60 @@ +/******************************************************************************************* +* +* 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-2023 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 + { + int y = 10; + + BeginDrawing(); + + ClearBackground(RAYWHITE); + + for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. + { + if (IsGamepadAvailable(i)) + { + DrawText(TextFormat("Gamepad:\n\tName: %s\n\tAxes: %d", GetGamepadName(i), GetGamepadAxisCount(i)), 10, y, 20, BLACK); + y += 40; + } + } + + EndDrawing(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- +} \ No newline at end of file diff --git a/src/rcore_drm.c b/src/rcore_drm.c index ccf44feb3..3b688b8a1 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -742,25 +742,12 @@ void OpenURL(const char *url) // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - if (CORE.Input.Gamepad.ready[gamepad]) - { - ioctl(platform.gamepadStreamFd[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); - name = CORE.Input.Gamepad.name[gamepad]; - } - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count int GetGamepadAxisCount(int gamepad) { - int axisCount = 0; - - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); - CORE.Input.Gamepad.axisCount = axisCount; - return CORE.Input.Gamepad.axisCount; } @@ -1959,6 +1946,9 @@ static void InitGamepad(void) if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); } + + ioctl(platform.gamepadStreamFd[i], JSIOCGNAME(64), &CORE.Input.Gamepad.name[i]); + ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount); } } } From ddca5251321ba5542fe59369765b48c1d4c6e5c9 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:11:09 +0200 Subject: [PATCH 0699/1710] RENAMED: `rcore_custom` to `rcore_template` --- src/{rcore_custom.c => rcore_template.c} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{rcore_custom.c => rcore_template.c} (99%) diff --git a/src/rcore_custom.c b/src/rcore_template.c similarity index 99% rename from src/rcore_custom.c rename to src/rcore_template.c index e17aa96b1..3007e4f54 100644 --- a/src/rcore_custom.c +++ b/src/rcore_template.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_ - Functions to manage window, graphics device and inputs +* rcore_ template - Functions to manage window, graphics device and inputs * * PLATFORM: * - TODO: Define the target platform for the core From 6ebfec99c50ef0b00bb693ac25ddebc01457153a Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:14:03 +0200 Subject: [PATCH 0700/1710] Added gamepad functions as generic for all platforms --- src/rcore.c | 18 ++++++++---------- src/rcore_android.c | 12 ------------ src/rcore_desktop.c | 12 ------------ src/rcore_drm.c | 12 ------------ src/rcore_template.c | 12 ------------ src/rcore_web.c | 12 ------------ 6 files changed, 8 insertions(+), 70 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index fa8f50d86..2e6d32132 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2023,8 +2023,6 @@ void SetExitKey(int key) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetGamepadAxisCount(int gamepad) ** -//const char *GetGamepadName(int gamepad) ** //int SetGamepadMappings(const char *mappings) // Check if a gamepad is available @@ -2038,10 +2036,10 @@ bool IsGamepadAvailable(int gamepad) } // Get gamepad internal name id -//const char *GetGamepadName(int gamepad) -//{ -// return CORE.Input.Gamepad.ready[gamepad]; -//} +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) @@ -2094,10 +2092,10 @@ int GetGamepadButtonPressed(void) } // Get gamepad axis count -//int GetGamepadAxisCount(int gamepad) -//{ -// return CORE.Input.Gamepad.axisCount; -//} +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} // Get axis movement vector for a gamepad float GetGamepadAxisMovement(int gamepad, int axis) diff --git a/src/rcore_android.c b/src/rcore_android.c index a9fc5fb9b..d9f72894a 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -635,18 +635,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index c410b713b..eaef9517b 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1222,18 +1222,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 3b688b8a1..13d41bbbd 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -739,18 +739,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 3007e4f54..68a43b2cb 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -563,18 +563,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_web.c b/src/rcore_web.c index ed7dc2657..00691e430 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -697,18 +697,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { From 0d175a69ae1e7072e05587b5f7505bac0c07b4ce Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:36:44 +0200 Subject: [PATCH 0701/1710] REVIEWED: Mouse and Touch functions generic to all platforms #3313 --- src/rcore.c | 65 ++++++++++++++++++++++++++++++++++++------ src/rcore_android.c | 53 +++-------------------------------- src/rcore_desktop.c | 67 +++++--------------------------------------- src/rcore_drm.c | 57 ------------------------------------- src/rcore_template.c | 55 ------------------------------------ src/rcore_web.c | 59 -------------------------------------- 6 files changed, 68 insertions(+), 288 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 2e6d32132..595cbfe02 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2113,11 +2113,7 @@ float GetGamepadAxisMovement(int gamepad, int axis) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetMouseX(void) ** -//int GetMouseY(void) ** -//Vector2 GetMousePosition(void) ** //void SetMousePosition(int x, int y) -//float GetMouseWheelMove(void) ** //void SetMouseCursor(int cursor) // Check if a mouse button has been pressed once @@ -2172,6 +2168,29 @@ bool IsMouseButtonUp(int button) return up; } +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// 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) { @@ -2197,6 +2216,17 @@ 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) { @@ -2211,10 +2241,29 @@ Vector2 GetMouseWheelMoveV(void) // Module Functions Definition: Input Handling: Touch //---------------------------------------------------------------------------------- -// NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetTouchX(void) -//int GetTouchY(void) -//Vector2 GetTouchPosition(int index) +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// 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) diff --git a/src/rcore_android.c b/src/rcore_android.c index d9f72894a..1921ed79b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -642,24 +642,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - return GetTouchPosition(0); -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -667,43 +649,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); - return 0.0f; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// 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; -} - // Register all input events void PollInputEvents(void) { @@ -1247,6 +1198,10 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 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; } diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index eaef9517b..8c4b73c8c 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1228,29 +1228,6 @@ int SetGamepadMappings(const char *mappings) return glfwUpdateGamepadMappings(mappings); } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// 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; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -1261,17 +1238,6 @@ void SetMousePosition(int x, int y) glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } -// 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; -} - // Set mouse cursor void SetMouseCursor(int cursor) { @@ -1284,32 +1250,6 @@ void SetMouseCursor(int cursor) } } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return GetMouseX(); -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return GetMouseY(); -} - -// 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 }; - - // 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 - if (index == 0) position = GetMousePosition(); - - return position; -} - // Register all input events void PollInputEvents(void) { @@ -1352,6 +1292,13 @@ void PollInputEvents(void) // 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 + // WARNING: If the target desktop device supports touch screen, this behavious 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 + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; // Check if gamepads are ready // NOTE: We do it here in case of disconnection diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 13d41bbbd..839edeb47 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -746,29 +746,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// 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; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -776,46 +753,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// 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; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return GetMouseX(); -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return GetMouseY(); -} - -// 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; -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 68a43b2cb..b6f0ff97e 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -570,30 +570,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // NOTE: On canvas scaling, mouse position is proportionally returned - 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; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -601,43 +577,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); - return 0.0f; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// 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; -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index 00691e430..d6af5a6de 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -705,30 +705,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // NOTE: On canvas scaling, mouse position is proportionally returned - 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; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -739,47 +715,12 @@ void SetMousePosition(int x, int y) glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } -// 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; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// 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; -} - // Register all input events void PollInputEvents(void) { From a2c5f01059e9c8efd951c06a87a37e39120daf00 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:55:12 +0200 Subject: [PATCH 0702/1710] Reordered one function --- src/rcore.c | 2 +- src/rcore_android.c | 12 ++++----- src/rcore_desktop.c | 58 ++++++++++++++++++++++---------------------- src/rcore_drm.c | 12 ++++----- src/rcore_template.c | 12 ++++----- src/rcore_web.c | 12 ++++----- 6 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 595cbfe02..8b5b3bf49 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2285,7 +2285,7 @@ int GetTouchPointCount(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Platform-specific functions +// NOTE: Functions with a platform-specific implementation on rcore_.c //static bool InitGraphicsDevice(int width, int height) // Set viewport for a provided width and height diff --git a/src/rcore_android.c b/src/rcore_android.c index 1921ed79b..4413dfa87 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -347,6 +347,12 @@ 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) { @@ -365,12 +371,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 8c4b73c8c..54b684a0c 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -380,35 +380,6 @@ void ToggleFullscreen(void) if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); } -// Set window state: maximized, if resizable -void MaximizeWindow(void) -{ - if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - glfwMaximizeWindow(platform.handle); - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; - } -} - -// Set window state: minimized -void MinimizeWindow(void) -{ - // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(platform.handle); -} - -// Set window state: not 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; - } -} - // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { @@ -484,6 +455,35 @@ void ToggleBorderlessWindowed(void) else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); } +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + glfwMaximizeWindow(platform.handle); + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; + } +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + // NOTE: Following function launches callback that sets appropriate flag! + glfwIconifyWindow(platform.handle); +} + +// Set window state: not 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; + } +} + // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 839edeb47..459444ada 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -448,6 +448,12 @@ 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) { @@ -466,12 +472,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_template.c b/src/rcore_template.c index b6f0ff97e..5b6507eb8 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -275,6 +275,12 @@ 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) { @@ -293,12 +299,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_web.c b/src/rcore_web.c index d6af5a6de..d24ef1214 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -410,6 +410,12 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag } +// 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) { @@ -428,12 +434,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { From da9c2894feee6d64780d9cfe161255c4db91decc Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 12:10:38 +0200 Subject: [PATCH 0703/1710] Reorganized some functions, `WaitTime()` is common to all platforms --- src/raylib.h | 18 +++--- src/rcore.c | 157 +++++++++++++++++++++++++-------------------------- 2 files changed, 87 insertions(+), 88 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 5d63d4b62..c03e0a576 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -986,14 +986,6 @@ RLAPI const char *GetClipboardText(void); // Get clipboa 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 -// Custom frame control functions -// NOTE: Those functions are intended for advance 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) - // Cursor-related functions RLAPI void ShowCursor(void); // Shows cursor RLAPI void HideCursor(void); // Hides cursor @@ -1049,9 +1041,17 @@ RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the // Timing-related functions RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) -RLAPI int GetFPS(void); // Get current FPS 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 advance 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) // Misc. functions RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) diff --git a/src/rcore.c b/src/rcore.c index 8b5b3bf49..18d2e2929 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -430,15 +430,6 @@ bool IsCursorOnScreen(void) return CORE.Input.Mouse.cursorOnScreen; } -//---------------------------------------------------------------------------------- -// Module Functions Definition: Custom frame control -//---------------------------------------------------------------------------------- - -// NOTE: Functions with a platform-specific implementation on rcore_.c -//void SwapScreenBuffer(void); -//void PollInputEvents(void); -//void WaitTime(double seconds); - //---------------------------------------------------------------------------------- // Module Functions Definition: Screen Drawing //---------------------------------------------------------------------------------- @@ -1236,6 +1227,60 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Custom frame control +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.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; + +#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 //---------------------------------------------------------------------------------- @@ -2288,6 +2333,30 @@ int GetTouchPointCount(void) // NOTE: Functions with a platform-specific implementation on rcore_.c //static bool InitGraphicsDevice(int width, int height) +// 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) + 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) { @@ -2395,76 +2464,6 @@ void SetupFramebuffer(int width, int height) } } -// 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) - 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 -} - -// 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; - -#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 -} - // 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 From 28fb58f0ea11f0b30b8aaf79d7bb148491ed8775 Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Wed, 11 Oct 2023 06:15:40 -0400 Subject: [PATCH 0704/1710] [rtext] TextFormat() warn user if buffer overflow occured. (#3399) * [rtext] TextFormat now alerts user to truncation. * Update rtext.c * Update rcore.c * Update rtext.c --- src/rcore.c | 12 +++++++++++- src/rtext.c | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 18d2e2929..c1c9980b2 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2922,6 +2922,7 @@ static void PlayAutomationEvent(unsigned int frame) #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 @@ -2940,12 +2941,21 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); + // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + { + // We are going to insert [TRUN] at the end of the string so the user knows what happened + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + sprintf(truncBuffer, "[TRUN]"); + } + index += 1; // Move to next buffer for next function call if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; return currentBuffer; } + #endif // !SUPPORT_MODULE_RTEXT diff --git a/src/rtext.c b/src/rtext.c index fb8440131..16b65507b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1371,15 +1371,24 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); + // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + { + // We are going to insert [TRUN] at the end of the string so the user knows what happened + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + sprintf(truncBuffer, "[TRUN]"); + } + index += 1; // Move to next buffer for next function call if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; return currentBuffer; } + // Get integer value from text // NOTE: This function replaces atoi() [stdlib.h] int TextToInteger(const char *text) From 61af8e76310cfc23015e0eaee99c55fb72714a30 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 12:20:03 +0200 Subject: [PATCH 0705/1710] REVIEWED: #3399, Fix #3366 --- src/rcore.c | 9 ++++----- src/rtext.c | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index c1c9980b2..e7868ebb8 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2922,7 +2922,6 @@ static void PlayAutomationEvent(unsigned int frame) #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 @@ -2941,14 +2940,14 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); - // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured - if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six char + '\0' sprintf(truncBuffer, "[TRUN]"); } diff --git a/src/rtext.c b/src/rtext.c index 16b65507b..593fdb53f 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1371,14 +1371,14 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); - // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured - if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six chars + '\0' sprintf(truncBuffer, "[TRUN]"); } From 6ed8acde6730c34a7e127b16ab567a5f3438b29b Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:29:21 -0300 Subject: [PATCH 0706/1710] Fix windowMin/Max to screenMin/Max for android, drm, template (#3400) --- src/rcore.h | 2 -- src/rcore_android.c | 10 +++++----- src/rcore_drm.c | 18 +++++++++--------- src/rcore_template.c | 12 ++++++------ 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 4d40c9567..dbff6ab13 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -127,8 +127,6 @@ typedef struct CoreData { 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) - Size windowMin; // Window minimum width and height - Size windowMax; // Window maximum width and height Matrix screenScale; // Matrix to scale screen (framebuffer rendering) char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) diff --git a/src/rcore_android.c b/src/rcore_android.c index 4413dfa87..bfb57fedf 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -416,15 +416,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = 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.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions @@ -1198,7 +1198,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 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 }; diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 459444ada..2f0eab682 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -281,7 +281,7 @@ void InitWindow(int width, int height, const char *title) InitGamepad(); // Gamepad init InitKeyboard(); // Keyboard init (stdin) //-------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -517,15 +517,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = 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.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions @@ -841,10 +841,10 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default // Set the window minimum and maximum default values to 0 - CORE.Window.windowMin.width = 0; - CORE.Window.windowMin.height = 0; - CORE.Window.windowMax.width = 0; - CORE.Window.windowMax.height = 0; + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) diff --git a/src/rcore_template.c b/src/rcore_template.c index 5b6507eb8..ea5af40d9 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -141,7 +141,7 @@ void InitWindow(int width, int height, const char *title) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - + // Initialize hi-res timer InitTimer(); @@ -150,7 +150,7 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -344,15 +344,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = 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.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions From 876e6b3a0d4db8e6124bf8f95d048879f2f760b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 20:25:09 +0200 Subject: [PATCH 0707/1710] REVIEWED: `TextFormat()`, added "..." for truncation #3366 It seems more standard than [TRUN] --- src/rcore.c | 6 +++--- src/rtext.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index e7868ebb8..253436e51 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2946,9 +2946,9 @@ const char *TextFormat(const char *text, ...) // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { - // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six char + '\0' - sprintf(truncBuffer, "[TRUN]"); + // 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 diff --git a/src/rtext.c b/src/rtext.c index 593fdb53f..2d72bbe6b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1377,9 +1377,9 @@ const char *TextFormat(const char *text, ...) // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { - // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six chars + '\0' - sprintf(truncBuffer, "[TRUN]"); + // 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 From 2e65bc675ce2ef92ccc784e25739b27edd7be94b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 13 Oct 2023 14:14:16 +0200 Subject: [PATCH 0708/1710] Moved some platforms functions to generic `rcore` #3313 Reviewed `InitWindow()` to clearly note platform specific code --- src/rcore.c | 53 +++++++++++++++++----- src/rcore_android.c | 45 +++++-------------- src/rcore_desktop.c | 70 ++++++++++------------------- src/rcore_drm.c | 60 ++++++++----------------- src/rcore_template.c | 48 ++++---------------- src/rcore_web.c | 102 ++++++++++++++++--------------------------- 6 files changed, 142 insertions(+), 236 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 253436e51..3efa67b2c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -322,18 +322,15 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void InitWindow(int width, int height, const char *title) //void CloseWindow(void) //bool WindowShouldClose(void) -//bool IsWindowHidden(void) -//bool IsWindowMinimized(void) -//bool IsWindowMaximized(void) -//bool IsWindowFocused(void) -//bool IsWindowResized(void) //void ToggleFullscreen(void) +//void ToggleBorderlessWindowed(void) //void MaximizeWindow(void) //void MinimizeWindow(void) //void RestoreWindow(void) -//void ToggleBorderlessWindowed(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) @@ -345,25 +342,27 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void SetWindowOpacity(float opacity) //void SetWindowFocused(void) //void *GetWindowHandle(void) +//Vector2 GetWindowPosition(void) +//Vector2 GetWindowScaleDPI(void) + //int GetMonitorCount(void) //int GetCurrentMonitor(void) -//Vector2 GetMonitorPosition(int monitor) //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) -//Vector2 GetWindowPosition(void) -//Vector2 GetWindowScaleDPI(void) + //void SetClipboardText(const char *text) //const char *GetClipboardText(void) + //void ShowCursor(void) //void HideCursor(void) //void EnableCursor(void) //void DisableCursor(void) - // Check if window has been initialized successfully bool IsWindowReady(void) { @@ -376,6 +375,36 @@ 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) { @@ -394,13 +423,13 @@ int GetScreenHeight(void) return CORE.Window.screen.height; } -// Get current render width which is equal to screen width * dpi scale +// Get current render width which is equal to screen width*dpi scale int GetRenderWidth(void) { return CORE.Window.render.width; } -// Get current screen height which is equal to screen height * dpi scale +// Get current screen height which is equal to screen height*dpi scale int GetRenderHeight(void) { return CORE.Window.render.height; diff --git a/src/rcore_android.c b/src/rcore_android.c index bfb57fedf..7cc71bbca 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -183,13 +183,14 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - - // Platform specific init window - //-------------------------------------------------------------- + // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -227,6 +228,12 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; + + // 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 TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); @@ -311,36 +318,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return platform.appEnabled; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { @@ -936,12 +913,14 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_GAINED_FOCUS: { platform.appEnabled = true; + 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; //PauseMusicStream(); } break; case APP_CMD_TERM_WINDOW: diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 54b684a0c..c2e5b23f1 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -2,7 +2,7 @@ * * rcore_desktop - Functions to manage window, graphics device and inputs * -* PLATFORM: DESKTOP +* PLATFORM: DESKTOP: GLFW * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) @@ -187,8 +187,26 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ + // Initialize graphics device // NOTE: returns true if window and graphic device has been initialized successfully + // WARNING: Actually, all window initialization and input callbacks initialization is + // done inside InitGraphicsDevice(), this functionality should be changed! CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program @@ -197,13 +215,15 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -304,36 +324,6 @@ bool WindowShouldClose(void) else return true; } -// 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; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { @@ -1408,18 +1398,6 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ #if defined(__APPLE__) glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); #endif diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2f0eab682..c0e88c723 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -215,6 +215,9 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- // Initialize graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); @@ -223,15 +226,28 @@ void InitWindow(int width, int height, const char *title) if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } 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 + // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -274,14 +290,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // Platform specific init window - //-------------------------------------------------------------- - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -412,36 +420,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return true; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index ea5af40d9..88b3c4a7e 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -128,8 +128,11 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + + + // TODO: Platform specific init window + //-------------------------------------------------------------- CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; @@ -144,13 +147,15 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -193,11 +198,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // TODO: Platform specific init window - //-------------------------------------------------------------- - // ... - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } @@ -239,36 +239,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return platform.appEnabled; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index d24ef1214..261498a5b 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -171,6 +171,9 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- // Initialize graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); @@ -181,13 +184,44 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); + + // 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) + // 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) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + + // Trigger this once to get initial window sizing + 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); + + // 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_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -230,38 +264,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // Platform specific init window - //-------------------------------------------------------------- - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // 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) - // 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) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - - // Trigger this once to get initial window sizing - 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); - - // 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_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } @@ -309,36 +311,6 @@ bool WindowShouldClose(void) return false; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// 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; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { From 0daa5ce1e71963577a2bcf6140df2d71d8e6c730 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:36:42 +0200 Subject: [PATCH 0709/1710] Fix `GetMouseDelta()` issue for Android (#3404) --- src/rcore_android.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rcore_android.c b/src/rcore_android.c index 7cc71bbca..5f6f34bab 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1178,6 +1178,16 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) 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) + { + 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 }; From 0f4a8cf7cb18dbd3594c77c5b2558f6cc979f2ef Mon Sep 17 00:00:00 2001 From: Babak Date: Fri, 13 Oct 2023 16:37:35 +0200 Subject: [PATCH 0710/1710] Ported to stb_image_resize2.h (#3403) --- src/external/stb_image_resize.h | 2634 -------- src/external/stb_image_resize2.h | 10303 +++++++++++++++++++++++++++++ src/rtextures.c | 12 +- 3 files changed, 10309 insertions(+), 2640 deletions(-) delete mode 100644 src/external/stb_image_resize.h create mode 100644 src/external/stb_image_resize2.h diff --git a/src/external/stb_image_resize.h b/src/external/stb_image_resize.h deleted file mode 100644 index ef9e6fe87..000000000 --- a/src/external/stb_image_resize.h +++ /dev/null @@ -1,2634 +0,0 @@ -/* stb_image_resize - v0.97 - public domain image resizing - by Jorge L Rodriguez (@VinoBS) - 2014 - http://github.com/nothings/stb - - Written with emphasis on usability, portability, and efficiency. (No - SIMD or threads, so it be easily outperformed by libs that use those.) - Only scaling and translation is supported, no rotations or shears. - Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. - - COMPILING & LINKING - In one C/C++ file that #includes this file, do this: - #define STB_IMAGE_RESIZE_IMPLEMENTATION - before the #include. That will create the implementation in that file. - - QUICKSTART - stbir_resize_uint8( input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, num_channels) - stbir_resize_float(...) - stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0) - stbir_resize_uint8_srgb_edgemode( - input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) - // WRAP/REFLECT/ZERO - - FULL API - See the "header file" section of the source for API documentation. - - ADDITIONAL DOCUMENTATION - - SRGB & FLOATING POINT REPRESENTATION - The sRGB functions presume IEEE floating point. If you do not have - IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use - a slower implementation. - - MEMORY ALLOCATION - The resize functions here perform a single memory allocation using - malloc. To control the memory allocation, before the #include that - triggers the implementation, do: - - #define STBIR_MALLOC(size,context) ... - #define STBIR_FREE(ptr,context) ... - - Each resize function makes exactly one call to malloc/free, so to use - temp memory, store the temp memory in the context and return that. - - ASSERT - Define STBIR_ASSERT(boolval) to override assert() and not use assert.h - - OPTIMIZATION - Define STBIR_SATURATE_INT to compute clamp values in-range using - integer operations instead of float operations. This may be faster - on some platforms. - - DEFAULT FILTERS - For functions which don't provide explicit control over what filters - to use, you can change the compile-time defaults with - - #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something - #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something - - See stbir_filter in the header-file section for the list of filters. - - NEW FILTERS - A number of 1D filter kernels are used. For a list of - supported filters see the stbir_filter enum. To add a new filter, - write a filter function and add it to stbir__filter_info_table. - - PROGRESS - For interactive use with slow resize operations, you can install - a progress-report callback: - - #define STBIR_PROGRESS_REPORT(val) some_func(val) - - The parameter val is a float which goes from 0 to 1 as progress is made. - - For example: - - static void my_progress_report(float progress); - #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) - - #define STB_IMAGE_RESIZE_IMPLEMENTATION - #include "stb_image_resize.h" - - static void my_progress_report(float progress) - { - printf("Progress: %f%%\n", progress*100); - } - - MAX CHANNELS - If your image has more than 64 channels, define STBIR_MAX_CHANNELS - to the max you'll have. - - ALPHA CHANNEL - Most of the resizing functions provide the ability to control how - the alpha channel of an image is processed. The important things - to know about this: - - 1. The best mathematically-behaved version of alpha to use is - called "premultiplied alpha", in which the other color channels - have had the alpha value multiplied in. If you use premultiplied - alpha, linear filtering (such as image resampling done by this - library, or performed in texture units on GPUs) does the "right - thing". While premultiplied alpha is standard in the movie CGI - industry, it is still uncommon in the videogame/real-time world. - - If you linearly filter non-premultiplied alpha, strange effects - occur. (For example, the 50/50 average of 99% transparent bright green - and 1% transparent black produces 50% transparent dark green when - non-premultiplied, whereas premultiplied it produces 50% - transparent near-black. The former introduces green energy - that doesn't exist in the source image.) - - 2. Artists should not edit premultiplied-alpha images; artists - want non-premultiplied alpha images. Thus, art tools generally output - non-premultiplied alpha images. - - 3. You will get best results in most cases by converting images - to premultiplied alpha before processing them mathematically. - - 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the - resizer does not do anything special for the alpha channel; - it is resampled identically to other channels. This produces - the correct results for premultiplied-alpha images, but produces - less-than-ideal results for non-premultiplied-alpha images. - - 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, - then the resizer weights the contribution of input pixels - based on their alpha values, or, equivalently, it multiplies - the alpha value into the color channels, resamples, then divides - by the resultant alpha value. Input pixels which have alpha=0 do - not contribute at all to output pixels unless _all_ of the input - pixels affecting that output pixel have alpha=0, in which case - the result for that pixel is the same as it would be without - STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for - input images in integer formats. For input images in float format, - input pixels with alpha=0 have no effect, and output pixels - which have alpha=0 will be 0 in all channels. (For float images, - you can manually achieve the same result by adding a tiny epsilon - value to the alpha channel of every image, and then subtracting - or clamping it at the end.) - - 6. You can suppress the behavior described in #5 and make - all-0-alpha pixels have 0 in all channels by #defining - STBIR_NO_ALPHA_EPSILON. - - 7. You can separately control whether the alpha channel is - interpreted as linear or affected by the colorspace. By default - it is linear; you almost never want to apply the colorspace. - (For example, graphics hardware does not apply sRGB conversion - to the alpha channel.) - - CONTRIBUTORS - Jorge L Rodriguez: Implementation - Sean Barrett: API design, optimizations - Aras Pranckevicius: bugfix - Nathan Reed: warning fixes - - REVISIONS - 0.97 (2020-02-02) fixed warning - 0.96 (2019-03-04) fixed warnings - 0.95 (2017-07-23) fixed warnings - 0.94 (2017-03-18) fixed warnings - 0.93 (2017-03-03) fixed bug with certain combinations of heights - 0.92 (2017-01-02) fix integer overflow on large (>2GB) images - 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions - 0.90 (2014-09-17) first released version - - LICENSE - See end of file for license information. - - TODO - Don't decode all of the image data when only processing a partial tile - Don't use full-width decode buffers when only processing a partial tile - When processing wide images, break processing into tiles so data fits in L1 cache - Installable filters? - Resize that respects alpha test coverage - (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: - https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) -*/ - -#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H -#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H - -#ifdef _MSC_VER -typedef unsigned char stbir_uint8; -typedef unsigned short stbir_uint16; -typedef unsigned int stbir_uint32; -#else -#include -typedef uint8_t stbir_uint8; -typedef uint16_t stbir_uint16; -typedef uint32_t stbir_uint32; -#endif - -#ifndef STBIRDEF -#ifdef STB_IMAGE_RESIZE_STATIC -#define STBIRDEF static -#else -#ifdef __cplusplus -#define STBIRDEF extern "C" -#else -#define STBIRDEF extern -#endif -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Easy-to-use API: -// -// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) -// * input_w is input image width (x-axis), input_h is input image height (y-axis) -// * stride is the offset between successive rows of image data in memory, in bytes. you can -// specify 0 to mean packed continuously in memory -// * alpha channel is treated identically to other channels. -// * colorspace is linear or sRGB as specified by function name -// * returned result is 1 for success or 0 in case of an error. -// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. -// * Memory required grows approximately linearly with input and output size, but with -// discontinuities at input_w == output_w and input_h == output_h. -// * These functions use a "default" resampling filter defined at compile time. To change the filter, -// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE -// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. - -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - - -// The following functions interpret image data as gamma-corrected sRGB. -// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, -// or otherwise provide the index of the alpha channel. Flags value -// of 0 will probably do the right thing if you're not sure what -// the flags mean. - -#define STBIR_ALPHA_CHANNEL_NONE -1 - -// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will -// use alpha-weighted resampling (effectively premultiplying, resampling, -// then unpremultiplying). -#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) -// The specified alpha channel should be handled as gamma-corrected value even -// when doing sRGB operations. -#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) - -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags); - - -typedef enum -{ - STBIR_EDGE_CLAMP = 1, - STBIR_EDGE_REFLECT = 2, - STBIR_EDGE_WRAP = 3, - STBIR_EDGE_ZERO = 4, -} stbir_edge; - -// This function adds the ability to specify how requests to sample off the edge of the image are handled. -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode); - -////////////////////////////////////////////////////////////////////////////// -// -// Medium-complexity API -// -// This extends the easy-to-use API as follows: -// -// * Alpha-channel can be processed separately -// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE -// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) -// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) -// * Filter can be selected explicitly -// * uint16 image type -// * sRGB colorspace available for all types -// * context parameter for passing to STBIR_MALLOC - -typedef enum -{ - STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios - STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering - STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque - STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline - STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 -} stbir_filter; - -typedef enum -{ - STBIR_COLORSPACE_LINEAR, - STBIR_COLORSPACE_SRGB, - - STBIR_MAX_COLORSPACES, -} stbir_colorspace; - -// The following functions are all identical except for the type of the image data - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - - - -////////////////////////////////////////////////////////////////////////////// -// -// Full-complexity API -// -// This extends the medium API as follows: -// -// * uint32 image type -// * not typesafe -// * separate filter types for each axis -// * separate edge modes for each axis -// * can specify scale explicitly for subpixel correctness -// * can specify image source tile using texture coordinates - -typedef enum -{ - STBIR_TYPE_UINT8 , - STBIR_TYPE_UINT16, - STBIR_TYPE_UINT32, - STBIR_TYPE_FLOAT , - - STBIR_MAX_TYPES -} stbir_datatype; - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context); - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, - float x_offset, float y_offset); - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1); -// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H - - - - - -#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION - -#ifndef STBIR_ASSERT -#include -#define STBIR_ASSERT(x) assert(x) -#endif - -// For memset -#include - -#include - -#ifndef STBIR_MALLOC -#include -// use comma operator to evaluate c, to avoid "unused parameter" warnings -#define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) -#define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) -#endif - -#ifndef _MSC_VER -#ifdef __cplusplus -#define stbir__inline inline -#else -#define stbir__inline -#endif -#else -#define stbir__inline __forceinline -#endif - - -// should produce compiler error if size is wrong -typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBIR__NOTUSED(v) (void)(v) -#else -#define STBIR__NOTUSED(v) (void)sizeof(v) -#endif - -#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) - -#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE -#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM -#endif - -#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE -#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL -#endif - -#ifndef STBIR_PROGRESS_REPORT -#define STBIR_PROGRESS_REPORT(float_0_to_1) -#endif - -#ifndef STBIR_MAX_CHANNELS -#define STBIR_MAX_CHANNELS 64 -#endif - -#if STBIR_MAX_CHANNELS > 65536 -#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." -// because we store the indices in 16-bit variables -#endif - -// This value is added to alpha just before premultiplication to avoid -// zeroing out color values. It is equivalent to 2^-80. If you don't want -// that behavior (it may interfere if you have floating point images with -// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to -// disable it. -#ifndef STBIR_ALPHA_EPSILON -#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) -#endif - - - -#ifdef _MSC_VER -#define STBIR__UNUSED_PARAM(v) (void)(v) -#else -#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) -#endif - -// must match stbir_datatype -static unsigned char stbir__type_size[] = { - 1, // STBIR_TYPE_UINT8 - 2, // STBIR_TYPE_UINT16 - 4, // STBIR_TYPE_UINT32 - 4, // STBIR_TYPE_FLOAT -}; - -// Kernel function centered at 0 -typedef float (stbir__kernel_fn)(float x, float scale); -typedef float (stbir__support_fn)(float scale); - -typedef struct -{ - stbir__kernel_fn* kernel; - stbir__support_fn* support; -} stbir__filter_info; - -// When upsampling, the contributors are which source pixels contribute. -// When downsampling, the contributors are which destination pixels are contributed to. -typedef struct -{ - int n0; // First contributing pixel - int n1; // Last contributing pixel -} stbir__contributors; - -typedef struct -{ - const void* input_data; - int input_w; - int input_h; - int input_stride_bytes; - - void* output_data; - int output_w; - int output_h; - int output_stride_bytes; - - float s0, t0, s1, t1; - - float horizontal_shift; // Units: output pixels - float vertical_shift; // Units: output pixels - float horizontal_scale; - float vertical_scale; - - int channels; - int alpha_channel; - stbir_uint32 flags; - stbir_datatype type; - stbir_filter horizontal_filter; - stbir_filter vertical_filter; - stbir_edge edge_horizontal; - stbir_edge edge_vertical; - stbir_colorspace colorspace; - - stbir__contributors* horizontal_contributors; - float* horizontal_coefficients; - - stbir__contributors* vertical_contributors; - float* vertical_coefficients; - - int decode_buffer_pixels; - float* decode_buffer; - - float* horizontal_buffer; - - // cache these because ceil/floor are inexplicably showing up in profile - int horizontal_coefficient_width; - int vertical_coefficient_width; - int horizontal_filter_pixel_width; - int vertical_filter_pixel_width; - int horizontal_filter_pixel_margin; - int vertical_filter_pixel_margin; - int horizontal_num_contributors; - int vertical_num_contributors; - - int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) - int ring_buffer_num_entries; // Total number of entries in the ring buffer. - int ring_buffer_first_scanline; - int ring_buffer_last_scanline; - int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer - float* ring_buffer; - - float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. - - int horizontal_contributors_size; - int horizontal_coefficients_size; - int vertical_contributors_size; - int vertical_coefficients_size; - int decode_buffer_size; - int horizontal_buffer_size; - int ring_buffer_size; - int encode_buffer_size; -} stbir__info; - - -static const float stbir__max_uint8_as_float = 255.0f; -static const float stbir__max_uint16_as_float = 65535.0f; -static const double stbir__max_uint32_as_float = 4294967295.0; - - -static stbir__inline int stbir__min(int a, int b) -{ - return a < b ? a : b; -} - -static stbir__inline float stbir__saturate(float x) -{ - if (x < 0) - return 0; - - if (x > 1) - return 1; - - return x; -} - -#ifdef STBIR_SATURATE_INT -static stbir__inline stbir_uint8 stbir__saturate8(int x) -{ - if ((unsigned int) x <= 255) - return x; - - if (x < 0) - return 0; - - return 255; -} - -static stbir__inline stbir_uint16 stbir__saturate16(int x) -{ - if ((unsigned int) x <= 65535) - return x; - - if (x < 0) - return 0; - - return 65535; -} -#endif - -static float stbir__srgb_uchar_to_linear_float[256] = { - 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, - 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, - 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, - 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, - 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, - 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, - 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, - 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, - 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, - 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, - 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, - 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, - 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, - 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, - 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, - 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, - 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, - 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, - 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, - 0.982251f, 0.991102f, 1.0f -}; - -static float stbir__srgb_to_linear(float f) -{ - if (f <= 0.04045f) - return f / 12.92f; - else - return (float)pow((f + 0.055f) / 1.055f, 2.4f); -} - -static float stbir__linear_to_srgb(float f) -{ - if (f <= 0.0031308f) - return f * 12.92f; - else - return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; -} - -#ifndef STBIR_NON_IEEE_FLOAT -// From https://gist.github.com/rygorous/2203834 - -typedef union -{ - stbir_uint32 u; - float f; -} stbir__FP32; - -static const stbir_uint32 fp32_to_srgb8_tab4[104] = { - 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, - 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, - 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, - 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, - 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, - 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, - 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, - 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, - 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, - 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, - 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, - 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, - 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float in) -{ - static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps - static const stbir__FP32 minval = { (127-13) << 23 }; - stbir_uint32 tab,bias,scale,t; - stbir__FP32 f; - - // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. - // The tests are carefully written so that NaNs map to 0, same as in the reference - // implementation. - if (!(in > minval.f)) // written this way to catch NaNs - in = minval.f; - if (in > almostone.f) - in = almostone.f; - - // Do the table lookup and unpack bias, scale - f.f = in; - tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; - bias = (tab >> 16) << 9; - scale = tab & 0xffff; - - // Grab next-highest mantissa bits and perform linear interpolation - t = (f.u >> 12) & 0xff; - return (unsigned char) ((bias + scale*t) >> 16); -} - -#else -// sRGB transition values, scaled by 1<<28 -static int stbir__srgb_offset_to_linear_scaled[256] = -{ - 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, - 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, - 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, - 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, - 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, - 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, - 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, - 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, - 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, - 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, - 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, - 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, - 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, - 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, - 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, - 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, - 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, - 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, - 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, - 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, - 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, - 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, - 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, - 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, - 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, - 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, - 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, - 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, - 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, - 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, - 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, - 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float f) -{ - int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp - int v = 0; - int i; - - // Refine the guess with a short binary search. - i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - - return (stbir_uint8) v; -} -#endif - -static float stbir__filter_trapezoid(float x, float scale) -{ - float halfscale = scale / 2; - float t = 0.5f + halfscale; - STBIR_ASSERT(scale <= 1); - - x = (float)fabs(x); - - if (x >= t) - return 0; - else - { - float r = 0.5f - halfscale; - if (x <= r) - return 1; - else - return (t - x) / scale; - } -} - -static float stbir__support_trapezoid(float scale) -{ - STBIR_ASSERT(scale <= 1); - return 0.5f + scale / 2; -} - -static float stbir__filter_triangle(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x <= 1.0f) - return 1 - x; - else - return 0; -} - -static float stbir__filter_cubic(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return (4 + x*x*(3*x - 6))/6; - else if (x < 2.0f) - return (8 + x*(-12 + x*(6 - x)))/6; - - return (0.0f); -} - -static float stbir__filter_catmullrom(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return 1 - x*x*(2.5f - 1.5f*x); - else if (x < 2.0f) - return 2 - x*(4 + x*(0.5f*x - 2.5f)); - - return (0.0f); -} - -static float stbir__filter_mitchell(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return (16 + x*x*(21 * x - 36))/18; - else if (x < 2.0f) - return (32 + x*(-60 + x*(36 - 7*x)))/18; - - return (0.0f); -} - -static float stbir__support_zero(float s) -{ - STBIR__UNUSED_PARAM(s); - return 0; -} - -static float stbir__support_one(float s) -{ - STBIR__UNUSED_PARAM(s); - return 1; -} - -static float stbir__support_two(float s) -{ - STBIR__UNUSED_PARAM(s); - return 2; -} - -static stbir__filter_info stbir__filter_info_table[] = { - { NULL, stbir__support_zero }, - { stbir__filter_trapezoid, stbir__support_trapezoid }, - { stbir__filter_triangle, stbir__support_one }, - { stbir__filter_cubic, stbir__support_two }, - { stbir__filter_catmullrom, stbir__support_two }, - { stbir__filter_mitchell, stbir__support_two }, -}; - -stbir__inline static int stbir__use_upsampling(float ratio) -{ - return ratio > 1; -} - -stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) -{ - return stbir__use_upsampling(stbir_info->horizontal_scale); -} - -stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) -{ - return stbir__use_upsampling(stbir_info->vertical_scale); -} - -// This is the maximum number of input samples that can affect an output sample -// with the given filter -static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) -{ - STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); -} - -// This is how much to expand buffers to account for filters seeking outside -// the image boundaries. -static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) -{ - return stbir__get_filter_pixel_width(filter, scale) / 2; -} - -static int stbir__get_coefficient_width(stbir_filter filter, float scale) -{ - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); -} - -static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) -{ - if (stbir__use_upsampling(scale)) - return output_size; - else - return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); -} - -static int stbir__get_total_horizontal_coefficients(stbir__info* info) -{ - return info->horizontal_num_contributors - * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); -} - -static int stbir__get_total_vertical_coefficients(stbir__info* info) -{ - return info->vertical_num_contributors - * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); -} - -static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) -{ - return &contributors[n]; -} - -// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, -// if you change it here change it there too. -static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) -{ - int width = stbir__get_coefficient_width(filter, scale); - return &coefficients[width*n + c]; -} - -static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) -{ - switch (edge) - { - case STBIR_EDGE_ZERO: - return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later - - case STBIR_EDGE_CLAMP: - if (n < 0) - return 0; - - if (n >= max) - return max - 1; - - return n; // NOTREACHED - - case STBIR_EDGE_REFLECT: - { - if (n < 0) - { - if (n < max) - return -n; - else - return max - 1; - } - - if (n >= max) - { - int max2 = max * 2; - if (n >= max2) - return 0; - else - return max2 - n - 1; - } - - return n; // NOTREACHED - } - - case STBIR_EDGE_WRAP: - if (n >= 0) - return (n % max); - else - { - int m = (-n) % max; - - if (m != 0) - m = max - m; - - return (m); - } - // NOTREACHED - - default: - STBIR_ASSERT(!"Unimplemented edge type"); - return 0; - } -} - -stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) -{ - // avoid per-pixel switch - if (n >= 0 && n < max) - return n; - return stbir__edge_wrap_slow(edge, n, max); -} - -// What input pixels contribute to this output pixel? -static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) -{ - float out_pixel_center = (float)n + 0.5f; - float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; - float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; - - float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; - float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; - - *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; - *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); - *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); -} - -// What output pixels does this input pixel contribute to? -static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) -{ - float in_pixel_center = (float)n + 0.5f; - float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; - float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; - - float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; - float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; - - *out_center_of_in = in_pixel_center * scale_ratio - out_shift; - *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); - *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); -} - -static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) -{ - int i; - float total_filter = 0; - float filter_scale; - - STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - - contributor->n0 = in_first_pixel; - contributor->n1 = in_last_pixel; - - STBIR_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - { - float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); - - // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) - if (i == 0 && !coefficient_group[i]) - { - contributor->n0 = ++in_first_pixel; - i--; - continue; - } - - total_filter += coefficient_group[i]; - } - - // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. - // It would be true in exact math but is at best approximately true in floating-point math, - // and it would not make sense to try and put actual bounds on this here because it depends - // on the image aspect ratio which can get pretty extreme. - //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); - - STBIR_ASSERT(total_filter > 0.9); - STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. - - // Make sure the sum of all coefficients is 1. - filter_scale = 1 / total_filter; - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - coefficient_group[i] *= filter_scale; - - for (i = in_last_pixel - in_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; - - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } -} - -static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) -{ - int i; - - STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - - contributor->n0 = out_first_pixel; - contributor->n1 = out_last_pixel; - - STBIR_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= out_last_pixel - out_first_pixel; i++) - { - float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; - float x = out_pixel_center - out_center_of_in; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; - } - - // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. - // It would be true in exact math but is at best approximately true in floating-point math, - // and it would not make sense to try and put actual bounds on this here because it depends - // on the image aspect ratio which can get pretty extreme. - //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); - - for (i = out_last_pixel - out_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; - - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } -} - -static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size) -{ - int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); - int i, j; - int skip; - - for (i = 0; i < output_size; i++) - { - float scale; - float total = 0; - - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - { - float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); - total += coefficient; - } - else if (i < contributors[j].n0) - break; - } - - STBIR_ASSERT(total > 0.9f); - STBIR_ASSERT(total < 1.1f); - - scale = 1 / total; - - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; - else if (i < contributors[j].n0) - break; - } - } - - // Optimize: Skip zero coefficients and contributions outside of image bounds. - // Do this after normalizing because normalization depends on the n0/n1 values. - for (j = 0; j < num_contributors; j++) - { - int range, max, width; - - skip = 0; - while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) - skip++; - - contributors[j].n0 += skip; - - while (contributors[j].n0 < 0) - { - contributors[j].n0++; - skip++; - } - - range = contributors[j].n1 - contributors[j].n0 + 1; - max = stbir__min(num_coefficients, range); - - width = stbir__get_coefficient_width(filter, scale_ratio); - for (i = 0; i < max; i++) - { - if (i + skip >= width) - break; - - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); - } - - continue; - } - - // Using min to avoid writing into invalid pixels. - for (i = 0; i < num_contributors; i++) - contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); -} - -// Each scan line uses the same kernel values so we should calculate the kernel -// values once and then we can use them for every scan line. -static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) -{ - int n; - int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - - if (stbir__use_upsampling(scale_ratio)) - { - float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; - - // Looping through out pixels - for (n = 0; n < total_contributors; n++) - { - float in_center_of_out; // Center of the current out pixel in the in pixel space - int in_first_pixel, in_last_pixel; - - stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - - stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - } - else - { - float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; - - // Looping through in pixels - for (n = 0; n < total_contributors; n++) - { - float out_center_of_in; // Center of the current out pixel in the in pixel space - int out_first_pixel, out_last_pixel; - int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); - - stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - - stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - - stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size); - } -} - -static float* stbir__get_decode_buffer(stbir__info* stbir_info) -{ - // The 0 index of the decode buffer starts after the margin. This makes - // it okay to use negative indexes on the decode buffer. - return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; -} - -#define STBIR__DECODE(type, colorspace) ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace)) - -static void stbir__decode_scanline(stbir__info* stbir_info, int n) -{ - int c; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int input_w = stbir_info->input_w; - size_t input_stride_bytes = stbir_info->input_stride_bytes; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir_edge edge_horizontal = stbir_info->edge_horizontal; - stbir_edge edge_vertical = stbir_info->edge_vertical; - size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; - const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; - int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; - int decode = STBIR__DECODE(type, colorspace); - - int x = -stbir_info->horizontal_filter_pixel_margin; - - // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, - // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO - if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) - { - for (; x < max_x; x++) - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - return; - } - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float)); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; - } - - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) - { - int decode_pixel_index = x * channels; - - // If the alpha value is 0 it will clobber the color values. Make sure it's not. - float alpha = decode_buffer[decode_pixel_index + alpha_channel]; -#ifndef STBIR_NO_ALPHA_EPSILON - if (stbir_info->type != STBIR_TYPE_FLOAT) { - alpha += STBIR_ALPHA_EPSILON; - decode_buffer[decode_pixel_index + alpha_channel] = alpha; - } -#endif - for (c = 0; c < channels; c++) - { - if (c == alpha_channel) - continue; - - decode_buffer[decode_pixel_index + c] *= alpha; - } - } - } - - if (edge_horizontal == STBIR_EDGE_ZERO) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - for (x = input_w; x < max_x; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - } -} - -static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) -{ - return &ring_buffer[index * ring_buffer_length]; -} - -static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) -{ - int ring_buffer_index; - float* ring_buffer; - - stbir_info->ring_buffer_last_scanline = n; - - if (stbir_info->ring_buffer_begin_index < 0) - { - ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; - stbir_info->ring_buffer_first_scanline = n; - } - else - { - ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; - STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); - } - - ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); - memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); - - return ring_buffer; -} - - -static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer) -{ - int x, k; - int output_w = stbir_info->output_w; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - - for (x = 0; x < output_w; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int out_pixel_index = x * channels; - int coefficient_group = coefficient_width * x; - int coefficient_counter = 0; - - STBIR_ASSERT(n1 >= n0); - STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - int c; - STBIR_ASSERT(coefficient != 0); - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} - -static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer) -{ - int x, k; - int input_w = stbir_info->input_w; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; - int max_x = input_w + filter_pixel_margin * 2; - - STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); - - switch (channels) { - case 1: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 1; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - } - break; - - case 2: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 2; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - } - break; - - case 3: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 3; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - } - break; - - case 4: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 4; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - } - break; - - default: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * channels; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int c; - int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } - break; - } -} - -static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); - - // Now resample it into the ring buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - else - stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - - // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. -} - -static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); - - memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); - - // Now resample it into the horizontal buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); - else - stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); - - // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. -} - -// Get the specified scan line from the ring buffer. -static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length) -{ - int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; - return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); -} - - -static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) -{ - int x; - int n; - int num_nonalpha; - stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - float alpha = encode_buffer[pixel_index + alpha_channel]; - float reciprocal_alpha = alpha ? 1.0f / alpha : 0; - - // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb - for (n = 0; n < channels; n++) - if (n != alpha_channel) - encode_buffer[pixel_index + n] *= reciprocal_alpha; - - // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. - // Because we only add it for integer types, it will automatically be discarded on integer - // conversion, so we don't need to subtract it back out (which would be problematic for - // numeric precision reasons). - } - } - - // build a table of all channels that need colorspace correction, so - // we don't perform colorspace correction on channels that don't need it. - for (x = 0, num_nonalpha = 0; x < channels; ++x) - { - if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - { - nonalpha[num_nonalpha++] = (stbir_uint16)x; - } - } - - #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) - #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) - - #ifdef STBIR__SATURATE_INT - #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float )) - #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float)) - #else - #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float ) - #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) - #endif - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); - } - - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = encode_buffer[index]; - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; - } - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } -} - -static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int ring_buffer_entries = stbir_info->ring_buffer_num_entries; - void* output_data = stbir_info->output_data; - float* encode_buffer = stbir_info->encode_buffer; - int decode = STBIR__DECODE(type, colorspace); - int coefficient_width = stbir_info->vertical_coefficient_width; - int coefficient_counter; - int contributor = n; - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - - int n0,n1, output_row_start; - int coefficient_group = coefficient_width * contributor; - - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; - - output_row_start = n * stbir_info->output_stride_bytes; - - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - - memset(encode_buffer, 0, output_w * sizeof(float) * channels); - - // I tried reblocking this for better cache usage of encode_buffer - // (using x_outer, k, x_inner), but it lost speed. -- stb - - coefficient_counter = 0; - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 1; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - } - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 2; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - } - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 3; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - } - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 4; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; - } - } - break; - default: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * channels; - int c; - for (c = 0; c < channels; c++) - encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; - } - } - break; - } - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); -} - -static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int ring_buffer_entries = stbir_info->ring_buffer_num_entries; - float* horizontal_buffer = stbir_info->horizontal_buffer; - int coefficient_width = stbir_info->vertical_coefficient_width; - int contributor = n + stbir_info->vertical_filter_pixel_margin; - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1; - - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; - - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - - for (k = n0; k <= n1; k++) - { - int coefficient_index = k - n0; - int coefficient_group = coefficient_width * contributor; - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - - switch (channels) { - case 1: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 1; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 2; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 3; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 4; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - - int c; - for (c = 0; c < channels; c++) - ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} - -static void stbir__buffer_loop_upsample(stbir__info* stbir_info) -{ - int y; - float scale_ratio = stbir_info->vertical_scale; - float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; - - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - - for (y = 0; y < stbir_info->output_h; y++) - { - float in_center_of_out = 0; // Center of the current out scanline in the in scanline space - int in_first_scanline = 0, in_last_scanline = 0; - - stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); - - STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); - - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (in_first_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; - } - } - } - - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); - - while (in_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - - // Now all buffers should be ready to write a row of vertical sampling. - stbir__resample_vertical_upsample(stbir_info, y); - - STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); - } -} - -static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) -{ - int output_stride_bytes = stbir_info->output_stride_bytes; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int output_w = stbir_info->output_w; - void* output_data = stbir_info->output_data; - int decode = STBIR__DECODE(type, colorspace); - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) - { - int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; - float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); - STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); - } - - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; - } - } - } -} - -static void stbir__buffer_loop_downsample(stbir__info* stbir_info) -{ - int y; - float scale_ratio = stbir_info->vertical_scale; - int output_h = stbir_info->output_h; - float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; - int pixel_margin = stbir_info->vertical_filter_pixel_margin; - int max_y = stbir_info->input_h + pixel_margin; - - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - - for (y = -pixel_margin; y < max_y; y++) - { - float out_center_of_in; // Center of the current out scanline in the in scanline space - int out_first_scanline, out_last_scanline; - - stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - - STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); - - if (out_last_scanline < 0 || out_first_scanline >= output_h) - continue; - - stbir__empty_ring_buffer(stbir_info, out_first_scanline); - - stbir__decode_and_resample_downsample(stbir_info, y); - - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); - - while (out_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - - // Now the horizontal buffer is ready to write to all ring buffer rows. - stbir__resample_vertical_downsample(stbir_info, y); - } - - stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); -} - -static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) -{ - info->input_w = input_w; - info->input_h = input_h; - info->output_w = output_w; - info->output_h = output_h; - info->channels = channels; -} - -static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) -{ - info->s0 = s0; - info->t0 = t0; - info->s1 = s1; - info->t1 = t1; - - if (transform) - { - info->horizontal_scale = transform[0]; - info->vertical_scale = transform[1]; - info->horizontal_shift = transform[2]; - info->vertical_shift = transform[3]; - } - else - { - info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); - info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); - - info->horizontal_shift = s0 * info->output_w / (s1 - s0); - info->vertical_shift = t0 * info->output_h / (t1 - t0); - } -} - -static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) -{ - if (h_filter == 0) - h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - if (v_filter == 0) - v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - info->horizontal_filter = h_filter; - info->vertical_filter = v_filter; -} - -static stbir_uint32 stbir__calculate_memory(stbir__info *info) -{ - int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); - - info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); - info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); - - // One extra entry because floating point precision problems sometimes cause an extra to be necessary. - info->ring_buffer_num_entries = filter_height + 1; - - info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); - info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); - info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); - info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); - info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); - info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); - info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); - info->encode_buffer_size = info->output_w * info->channels * sizeof(float); - - STBIR_ASSERT(info->horizontal_filter != 0); - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - STBIR_ASSERT(info->vertical_filter != 0); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - - if (stbir__use_height_upsampling(info)) - // The horizontal buffer is for when we're downsampling the height and we - // can't output the result of sampling the decode buffer directly into the - // ring buffers. - info->horizontal_buffer_size = 0; - else - // The encode buffer is to retain precision in the height upsampling method - // and isn't used when height downsampling. - info->encode_buffer_size = 0; - - return info->horizontal_contributors_size + info->horizontal_coefficients_size - + info->vertical_contributors_size + info->vertical_coefficients_size - + info->decode_buffer_size + info->horizontal_buffer_size - + info->ring_buffer_size + info->encode_buffer_size; -} - -static int stbir__resize_allocated(stbir__info *info, - const void* input_data, int input_stride_in_bytes, - void* output_data, int output_stride_in_bytes, - int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, - void* tempmem, size_t tempmem_size_in_bytes) -{ - size_t memory_required = stbir__calculate_memory(info); - - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; - -#ifdef STBIR_DEBUG_OVERWRITE_TEST -#define OVERWRITE_ARRAY_SIZE 8 - unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - - size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; - memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); -#endif - - STBIR_ASSERT(info->channels >= 0); - STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); - - if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) - return 0; - - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - - if (alpha_channel < 0) - flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; - - if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { - STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); - } - - if (alpha_channel >= info->channels) - return 0; - - STBIR_ASSERT(tempmem); - - if (!tempmem) - return 0; - - STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); - - if (tempmem_size_in_bytes < memory_required) - return 0; - - memset(tempmem, 0, tempmem_size_in_bytes); - - info->input_data = input_data; - info->input_stride_bytes = width_stride_input; - - info->output_data = output_data; - info->output_stride_bytes = width_stride_output; - - info->alpha_channel = alpha_channel; - info->flags = flags; - info->type = type; - info->edge_horizontal = edge_horizontal; - info->edge_vertical = edge_vertical; - info->colorspace = colorspace; - - info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); - - info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); - info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; - -#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) - - info->horizontal_contributors = (stbir__contributors *) tempmem; - info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); - info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); - info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); - info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); - - if (stbir__use_height_upsampling(info)) - { - info->horizontal_buffer = NULL; - info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - else - { - info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); - info->encode_buffer = NULL; - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - -#undef STBIR__NEXT_MEMPTR - - // This signals that the ring buffer is empty - info->ring_buffer_begin_index = -1; - - stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); - stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); - - STBIR_PROGRESS_REPORT(0); - - if (stbir__use_height_upsampling(info)) - stbir__buffer_loop_upsample(info); - else - stbir__buffer_loop_downsample(info); - - STBIR_PROGRESS_REPORT(1); - -#ifdef STBIR_DEBUG_OVERWRITE_TEST - STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); -#endif - - return 1; -} - - -static int stbir__resize_arbitrary( - void *alloc_context, - const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, float *transform, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_filter h_filter, stbir_filter v_filter, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) -{ - stbir__info info; - int result; - size_t memory_required; - void* extra_memory; - - stbir__setup(&info, input_w, input_h, output_w, output_h, channels); - stbir__calculate_transform(&info, s0,t0,s1,t1,transform); - stbir__choose_filter(&info, h_filter, v_filter); - memory_required = stbir__calculate_memory(&info); - extra_memory = STBIR_MALLOC(memory_required, alloc_context); - - if (!extra_memory) - return 0; - - result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, - output_data, output_stride_in_bytes, - alpha_channel, flags, type, - edge_horizontal, edge_vertical, - colorspace, extra_memory, memory_required); - - STBIR_FREE(extra_memory, alloc_context); - - return result; -} - -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); -} - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); -} - -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, - float x_offset, float y_offset) -{ - float transform[4]; - transform[0] = x_scale; - transform[1] = y_scale; - transform[2] = x_offset; - transform[3] = y_offset; - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -#endif // STB_IMAGE_RESIZE_IMPLEMENTATION - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -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/src/external/stb_image_resize2.h b/src/external/stb_image_resize2.h new file mode 100644 index 000000000..e0c428246 --- /dev/null +++ b/src/external/stb_image_resize2.h @@ -0,0 +1,10303 @@ +/* stb_image_resize2 - v2.01 - public domain image resizing + + by Jeff Roberts (v2) and Jorge L Rodriguez + http://github.com/nothings/stb + + Can be threaded with the extended API. SSE2, AVX, Neon and WASM SIMD support. Only + scaling and translation is supported, no rotations or shears. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #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. + + stbir_resize_uint8_srgb( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + stbir_resize_uint8_linear( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + stbir_resize_float_linear( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + If you pass NULL or zero for the output_pixels, we will allocate the output buffer + for you and return it from the function (free with free() or STBIR_FREE). + As a special case, XX_stride_in_bytes of 0 means packed continuously in memory. + + API LEVELS + There are three levels of API - easy-to-use, medium-complexity and extended-complexity. + + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + MEMORY ALLOCATION + By default, we use malloc and free for memory allocation. To override the + memory allocation, before the implementation #include, add a: + + #define STBIR_MALLOC(size,user_data) ... + #define STBIR_FREE(ptr,user_data) ... + + Each resize makes exactly one call to malloc/free (unless you use the + extended API where you can do one allocation for many resizes). Under + address sanitizer, we do separate allocations to find overread/writes. + + PERFORMANCE + This library was written with an emphasis on performance. When testing + stb_image_resize with RGBA, the fastest mode is STBIR_4CHANNEL with + STBIR_TYPE_UINT8 pixels and CLAMPed edges (which is what many other resize + libs do by default). Also, make sure SIMD is turned on of course (default + for 64-bit targets). Avoid WRAP edge mode if you want the fastest speed. + + This library also comes with profiling built-in. If you define STBIR_PROFILE, + you can use the advanced API and get low-level profiling information by + calling stbir_resize_extended_profile_info() or stbir_resize_split_profile_info() + after a resize. + + SIMD + Most of the routines have optimized SSE2, AVX, NEON and WASM versions. + + On Microsoft compilers, we automatically turn on SIMD for 64-bit x64 and + ARM; for 32-bit x86 and ARM, you select SIMD mode by defining STBIR_SSE2 or + STBIR_NEON. For AVX and AVX2, we auto-select it by detecting the /arch:AVX + or /arch:AVX2 switches. You can also always manually turn SSE2, AVX or AVX2 + support on by defining STBIR_SSE2, STBIR_AVX or STBIR_AVX2. + + On Linux, SSE2 and Neon is on by default for 64-bit x64 or ARM64. For 32-bit, + we select x86 SIMD mode by whether you have -msse2, -mavx or -mavx2 enabled + on the command line. For 32-bit ARM, you must pass -mfpu=neon-vfpv4 for both + clang and GCC, but GCC also requires an additional -mfp16-format=ieee to + automatically enable NEON. + + On x86 platforms, you can also define STBIR_FP16C to turn on FP16C instructions + for converting back and forth to half-floats. This is autoselected when we + are using AVX2. Clang and GCC also require the -mf16c switch. ARM always uses + the built-in half float hardware NEON instructions. + + You can also tell us to use multiply-add instructions with STBIR_USE_FMA. + Because x86 doesn't always have fma, we turn it off by default to maintain + determinism across all platforms. If you don't care about non-FMA determinism + and are willing to restrict yourself to more recent x86 CPUs (around the AVX + timeframe), then fma will give you around a 15% speedup. + + You can force off SIMD in all cases by defining STBIR_NO_SIMD. You can turn + off AVX or AVX2 specifically with STBIR_NO_AVX or STBIR_NO_AVX2. AVX is 10% + to 40% faster, and AVX2 is generally another 12%. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how the alpha + channel of an image is processed. + + When alpha represents transparency, it is important that when combining + colors with filtering, the pixels should not be treated equally; they + should use a weighted average based on their alpha values. For example, + if a pixel is 1% opaque bright green and another pixel is 99% opaque + black and you average them, the average will be 50% opaque, but the + unweighted average and will be a middling green color, while the weighted + average will be nearly black. This means the unweighted version introduced + green energy that didn't exist in the source image. + + (If you want to know why this makes sense, you can work out the math for + the following: consider what happens if you alpha composite a source image + over a fixed color and then average the output, vs. if you average the + source image pixels and then composite that over the same fixed color. + Only the weighted average produces the same result as the ground truth + composite-then-average result.) + + Therefore, it is in general best to "alpha weight" the pixels when applying + filters to them. This essentially means multiplying the colors by the alpha + values before combining them, and then dividing by the alpha value at the + end. + + The computer graphics industry introduced a technique called "premultiplied + alpha" or "associated alpha" in which image colors are stored in image files + already multiplied by their alpha. This saves some math when compositing, + and also avoids the need to divide by the alpha at the end (which is quite + inefficient). However, while premultiplied alpha is common in the movie CGI + industry, it is not commonplace in other industries like videogames, and most + consumer file formats are generally expected to contain not-premultiplied + colors. For example, Photoshop saves PNG files "unpremultiplied", and web + browsers like Chrome and Firefox expect PNG images to be unpremultiplied. + + Note that there are three possibilities that might describe your image + and resize expectation: + + 1. images are not premultiplied, alpha weighting is desired + 2. images are not premultiplied, alpha weighting is not desired + 3. images are premultiplied + + Both case #2 and case #3 require the exact same math: no alpha weighting + should be applied or removed. Only case 1 requires extra math operations; + the other two cases can be handled identically. + + stb_image_resize expects case #1 by default, applying alpha weighting to + images, expecting the input images to be unpremultiplied. This is what the + COLOR+ALPHA buffer types tell the resizer to do. + + When you use the pixel layouts STBIR_RGBA, STBIR_BGRA, STBIR_ARGB, + STBIR_ABGR, STBIR_RX, or STBIR_XR you are telling us that the pixels are + non-premultiplied. In these cases, the resizer will alpha weight the colors + (effectively creating the premultiplied image), do the filtering, and then + convert back to non-premult on exit. + + When you use the pixel layouts STBIR_RGBA_PM, STBIR_RGBA_PM, STBIR_RGBA_PM, + STBIR_RGBA_PM, STBIR_RX_PM or STBIR_XR_PM, you are telling that the pixels + ARE premultiplied. In this case, the resizer doesn't have to do the + premultipling - it can filter directly on the input. This about twice as + fast as the non-premultiplied case, so it's the right option if your data is + already setup correctly. + + When you use the pixel layout STBIR_4CHANNEL or STBIR_2CHANNEL, you are + telling us that there is no channel that represents transparency; it may be + RGB and some unrelated fourth channel that has been stored in the alpha + channel, but it is actually not alpha. No special processing will be + performed. + + The difference between the generic 4 or 2 channel layouts, and the + specialized _PM versions is with the _PM versions you are telling us that + the data *is* alpha, just don't premultiply it. That's important when + using SRGB pixel formats, we need to know where the alpha is, because + it is converted linearly (rather than with the SRGB converters). + + Because alpha weighting produces the same effect as premultiplying, you + even have the option with non-premultiplied inputs to let the resizer + produce a premultiplied output. Because the intially computed alpha-weighted + output image is effectively premultiplied, this is actually more performant + than the normal path which un-premultiplies the output image as a final step. + + Finally, when converting both in and out of non-premulitplied space (for + example, when using STBIR_RGBA), we go to somewhat heroic measures to + ensure that areas with zero alpha value pixels get something reasonable + in the RGB values. If you don't care about the RGB values of zero alpha + pixels, you can call the stbir_set_non_pm_alpha_speed_over_quality() + function - this runs a premultiplied resize about 25% faster. That said, + when you really care about speed, using premultiplied pixels for both in + and out (STBIR_RGBA_PM, etc) much faster than both of these premultiplied + options. + + PIXEL LAYOUT CONVERSION + The resizer can convert from some pixel layouts to others. When using the + stbir_set_pixel_layouts(), you can, for example, specify STBIR_RGBA + on input, and STBIR_ARGB on output, and it will re-organize the channels + during the resize. Currently, you can only convert between two pixel + layouts with the same number of channels. + + DETERMINISM + We commit to being deterministic (from x64 to ARM to scalar to SIMD, etc). + This requires compiling with fast-math off (using at least /fp:precise). + Also, you must turn off fp-contracting (which turns mult+adds into fmas)! + We attempt to do this with pragmas, but with Clang, you usually want to add + -ffp-contract=off to the command line as well. + + For 32-bit x86, you must use SSE and SSE2 codegen for determinism. That is, + if the scalar x87 unit gets used at all, we immediately lose determinism. + On Microsoft Visual Studio 2008 and earlier, from what we can tell there is + no way to be deterministic in 32-bit x86 (some x87 always leaks in, even + with fp:strict). On 32-bit x86 GCC, determinism requires both -msse2 and + -fpmath=sse. + + Note that we will not be deterministic with float data containing NaNs - + the NaNs will propagate differently on different SIMD and platforms. + + If you turn on STBIR_USE_FMA, then we will be deterministic with other + fma targets, but we will differ from non-fma targets (this is unavoidable, + because a fma isn't simply an add with a mult - it also introduces a + rounding difference compared to non-fma instruction sequences. + + FLOAT PIXEL FORMAT RANGE + Any range of values can be used for the non-alpha float data that you pass + in (0 to 1, -1 to 1, whatever). However, if you are inputting float values + but *outputting* bytes or shorts, you must use a range of 0 to 1 so that we + scale back properly. The alpha channel must also be 0 to 1 for any format + that does premultiplication prior to resizing. + + Note also that with float output, using filters with negative lobes, the + output filtered values might go slightly out of range. You can define + STBIR_FLOAT_LOW_CLAMP and/or STBIR_FLOAT_HIGH_CLAMP to specify the range + to clamp to on output, if that's important. + + MAX/MIN SCALE FACTORS + The input pixel resolutions are in integers, and we do the internal pointer + resolution in size_t sized integers. However, the scale ratio from input + resolution to output resolution is calculated in float form. This means + the effective possible scale ratio is limited to 24 bits (or 16 million + to 1). As you get close to the size of the float resolution (again, 16 + million pixels wide or high), you might start seeing float inaccuracy + issues in general in the pipeline. If you have to do extreme resizes, + you can usually do this is multiple stages (using float intermediate + buffers). + + FLIPPED IMAGES + Stride is just the delta from one scanline to the next. This means you can + use a negative stride to handle inverted images (point to the final + scanline and use a negative stride). You can invert the input or output, + using negative strides. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters to + use, you can change the compile-time defaults with: + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are supplied. For a list of supported + filters, see the stbir_filter enum. You can install your own filters by + using the stbir_set_filter_callbacks function. + + PROGRESS + For interactive use with slow resize operations, you can use the the + scanline callbacks in the extended API. It would have to be a *very* large + image resample to need progress though - we're very fast. + + CEIL and FLOOR + In scalar mode, the only functions we use from math.h are ceilf and floorf, + but if you have your own versions, you can define the STBIR_CEILF(v) and + STBIR_FLOORF(v) macros and we'll use them instead. In SIMD, we just use + our own versions. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + FUTURE TODOS + * For polyphase integral filters, we just memcpy the coeffs to dupe + them, but we should indirect and use the same coeff memory. + * Add pixel layout conversions for sensible different channel counts + (maybe, 1->3/4, 3->4, 4->1, 3->1). + * For SIMD encode and decode scanline routines, do any pre-aligning + for bad input/output buffer alignments and pitch? + * For very wide scanlines, we should we do vertical strips to stay within + L2 cache. Maybe do chunks of 1K pixels at a time. There would be + some pixel reconversion, but probably dwarfed by things falling out + of cache. Probably also something possible with alternating between + scattering and gathering at high resize scales? + * Rewrite the coefficient generator to do many at once. + * AVX-512 vertical kernels - worried about downclocking here. + * Convert the reincludes to macros when we know they aren't changing. + * Experiment with pivoting the horizontal and always using the + vertical filters (which are faster, but perhaps not enough to overcome + the pivot cost and the extra memory touches). Need to buffer the whole + image so have to balance memory use. + * Most of our code is internally function pointers, should we compile + all the SIMD stuff always and dynamically dispatch? + + CONTRIBUTORS + Jeff Roberts: 2.0 implementation, optimizations, SIMD + Martins Mozeiko: NEON simd, WASM simd, clang and GCC whisperer. + Fabian Giesen: half float and srgb converters + Sean Barrett: API design, optimizations + Jorge L Rodriguez: Original 1.0 implementation + Aras Pranckevicius: bugfixes for 1.0 + Nathan Reed: warning fixes for 1.0 + + REVISIONS + 2.00 (2022-02-20) mostly new source: new api, optimizations, simd, vertical-first, etc + (2x-5x faster without simd, 4x-12x faster with simd) + (in some cases, 20x to 40x faster - resizing to very small for example) + 0.96 (2019-03-04) fixed warnings + 0.95 (2017-07-23) fixed warnings + 0.94 (2017-03-18) fixed warnings + 0.93 (2017-03-03) fixed bug with certain combinations of heights + 0.92 (2017-01-02) fix integer overflow on large (>2GB) images + 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions + 0.90 (2014-09-17) first released version + + LICENSE + See end of file for license information. +*/ + +#if !defined(STB_IMAGE_RESIZE_DO_HORIZONTALS) && !defined(STB_IMAGE_RESIZE_DO_VERTICALS) && !defined(STB_IMAGE_RESIZE_DO_CODERS) // for internal re-includes + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE2_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE2_H + +#include +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +typedef unsigned __int64 stbir_uint64; +#else +#include +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +typedef uint64_t stbir_uint64; +#endif + +#ifdef _M_IX86_FP +#if ( _M_IX86_FP >= 1 ) +#ifndef STBIR_SSE +#define STBIR_SSE +#endif +#endif +#endif + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(_M_AMD64) || defined(__SSE2__) || defined(STBIR_SSE) || defined(STBIR_SSE2) + #ifndef STBIR_SSE2 + #define STBIR_SSE2 + #endif + #if defined(__AVX__) || defined(STBIR_AVX2) + #ifndef STBIR_AVX + #ifndef STBIR_NO_AVX + #define STBIR_AVX + #endif + #endif + #endif + #if defined(__AVX2__) || defined(STBIR_AVX2) + #ifndef STBIR_NO_AVX2 + #ifndef STBIR_AVX2 + #define STBIR_AVX2 + #endif + #if defined( _MSC_VER ) && !defined(__clang__) + #ifndef STBIR_FP16C // FP16C instructions are on all AVX2 cpus, so we can autoselect it here on microsoft - clang needs -m16c + #define STBIR_FP16C + #endif + #endif + #endif + #endif + #ifdef __F16C__ + #ifndef STBIR_FP16C // turn on FP16C instructions if the define is set (for clang and gcc) + #define STBIR_FP16C + #endif + #endif +#endif + +#if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(_M_ARM) || (__ARM_NEON_FP & 4) != 0 && __ARM_FP16_FORMAT_IEEE != 0 +#ifndef STBIR_NEON +#define STBIR_NEON +#endif +#endif + +#if defined(_M_ARM) +#ifdef STBIR_USE_FMA +#undef STBIR_USE_FMA // no FMA for 32-bit arm on MSVC +#endif +#endif + +#if defined(__wasm__) && defined(__wasm_simd128__) +#ifndef STBIR_WASM +#define STBIR_WASM +#endif +#endif + +#ifndef STBIRDEF +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +//// start "header file" /////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * stride is the offset between successive rows of image data +// in memory, in bytes. specify 0 for packed continuously in memory +// * colorspace is linear or sRGB as specified by function name +// * Uses the default filters +// * Uses edge mode clamped +// * returned result is 1 for success or 0 in case of an error. + + +// stbir_pixel_layout specifies: +// number of channels +// order of channels +// whether color is premultiplied by alpha +// for back compatibility, you can cast the old channel count to an stbir_pixel_layout +typedef enum +{ + STBIR_BGR = 0, // 3-chan, with order specified (for channel flipping) + STBIR_1CHANNEL = 1, + STBIR_2CHANNEL = 2, + STBIR_RGB = 3, // 3-chan, with order specified (for channel flipping) + STBIR_RGBA = 4, // alpha formats, alpha is NOT premultiplied into color channels + + STBIR_4CHANNEL = 5, + STBIR_BGRA = 6, + STBIR_ARGB = 7, + STBIR_ABGR = 8, + STBIR_RA = 9, + STBIR_AR = 10, + + STBIR_RGBA_PM = 11, // alpha formats, alpha is premultiplied into color channels + STBIR_BGRA_PM = 12, + STBIR_ARGB_PM = 13, + STBIR_ABGR_PM = 14, + STBIR_RA_PM = 15, + STBIR_AR_PM = 16, +} stbir_pixel_layout; + +//=============================================================== +// Simple-complexity API +// +// If output_pixels is NULL (0), then we will allocate the buffer and return it to you. +//-------------------------------- + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); +//=============================================================== + +//=============================================================== +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Can specify the datatype - U8, U8_SRGB, U16, FLOAT, HALF_FLOAT +// * Edge wrap can selected explicitly +// * Filter can be selected explicitly +//-------------------------------- + +typedef enum +{ + STBIR_EDGE_CLAMP = 0, + STBIR_EDGE_REFLECT = 1, + STBIR_EDGE_WRAP = 2, // this edge mode is slower and uses more memory + STBIR_EDGE_ZERO = 3, +} stbir_edge; + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 + STBIR_FILTER_POINT_SAMPLE = 6, // Simple point sampling + STBIR_FILTER_OTHER = 7, // User callback specified +} stbir_filter; + +typedef enum +{ + STBIR_TYPE_UINT8 = 0, + STBIR_TYPE_UINT8_SRGB = 1, + STBIR_TYPE_UINT8_SRGB_ALPHA = 2, // alpha channel, when present, should also be SRGB (this is very unusual) + STBIR_TYPE_UINT16 = 3, + STBIR_TYPE_FLOAT = 4, + STBIR_TYPE_HALF_FLOAT = 5 +} stbir_datatype; + +// medium api +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ); +//=============================================================== + + + +//=============================================================== +// Extended-complexity API +// +// This API exposes all resize functionality. +// +// * Separate filter types for each axis +// * Separate edge modes for each axis +// * Separate input and output data types +// * Can specify regions with subpixel correctness +// * Can specify alpha flags +// * Can specify a memory callback +// * Can specify a callback data type for pixel input and output +// * Can be threaded for a single resize +// * Can be used to resize many frames without recalculating the sampler info +// +// Use this API as follows: +// 1) Call the stbir_resize_init function on a local STBIR_RESIZE structure +// 2) Call any of the stbir_set functions +// 3) Optionally call stbir_build_samplers() if you are going to resample multiple times +// with the same input and output dimensions (like resizing video frames) +// 4) Resample by calling stbir_resize_extended(). +// 5) Call stbir_free_samplers() if you called stbir_build_samplers() +//-------------------------------- + + +// Types: + +// INPUT CALLBACK: this callback is used for input scanlines +typedef void const * stbir_input_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ); + +// OUTPUT CALLBACK: this callback is used for output scanlines +typedef void stbir_output_callback( void const * output_ptr, int num_pixels, int y, void * context ); + +// callbacks for user installed filters +typedef float stbir__kernel_callback( float x, float scale, void * user_data ); // centered at zero +typedef float stbir__support_callback( float scale, void * user_data ); + +// internal structure with precomputed scaling +typedef struct stbir__info stbir__info; + +typedef struct STBIR_RESIZE // use the stbir_resize_init and stbir_override functions to set these values for future compatibility +{ + void * user_data; + void const * input_pixels; + int input_w, input_h; + double input_s0, input_t0, input_s1, input_t1; + stbir_input_callback * input_cb; + void * output_pixels; + int output_w, output_h; + int output_subx, output_suby, output_subw, output_subh; + stbir_output_callback * output_cb; + int input_stride_in_bytes; + int output_stride_in_bytes; + int splits; + int fast_alpha; + int needs_rebuild; + int called_alloc; + stbir_pixel_layout input_pixel_layout_public; + stbir_pixel_layout output_pixel_layout_public; + stbir_datatype input_data_type; + stbir_datatype output_data_type; + stbir_filter horizontal_filter, vertical_filter; + stbir_edge horizontal_edge, vertical_edge; + stbir__kernel_callback * horizontal_filter_kernel; stbir__support_callback * horizontal_filter_support; + stbir__kernel_callback * vertical_filter_kernel; stbir__support_callback * vertical_filter_support; + stbir__info * samplers; +} STBIR_RESIZE; + +// extended complexity api + + +// First off, you must ALWAYS call stbir_resize_init on your resize structure before any of the other calls! +STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize, + const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero + stbir_pixel_layout pixel_layout, stbir_datatype data_type ); + +//=============================================================== +// You can update these parameters any time after resize_init and there is no cost +//-------------------------------- + +STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ); +STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ); // no callbacks by default +STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ); // pass back STBIR_RESIZE* by default +STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes ); + +//=============================================================== + + +//=============================================================== +// If you call any of these functions, you will trigger a sampler rebuild! +//-------------------------------- + +STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ); // sets new buffer layouts +STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ); // CLAMP by default + +STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ); // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default +STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support ); + +STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets both sub-regions (full regions by default) +STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ); // sets input sub-region (full region by default) +STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets output sub-region (full region by default) + +// when inputting AND outputting non-premultiplied alpha pixels, we use a slower but higher quality technique +// that fills the zero alpha pixel's RGB values with something plausible. If you don't care about areas of +// zero alpha, you can call this function to get about a 25% speed improvement for STBIR_RGBA to STBIR_RGBA +// types of resizes. +STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ); +//=============================================================== + + +//=============================================================== +// You can call build_samplers to prebuild all the internal data we need to resample. +// Then, if you call resize_extended many times with the same resize, you only pay the +// cost once. +// If you do call build_samplers, you MUST call free_samplers eventually. +//-------------------------------- + +// This builds the samplers and does one allocation +STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize ); + +// You MUST call this, if you call stbir_build_samplers or stbir_build_samplers_with_splits +STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize ); +//=============================================================== + + +// And this is the main function to perform the resize synchronously on one thread. +STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize ); + + +//=============================================================== +// Use these functions for multithreading. +// 1) You call stbir_build_samplers_with_splits first on the main thread +// 2) Then stbir_resize_with_split on each thread +// 3) stbir_free_samplers when done on the main thread +//-------------------------------- + +// This will build samplers for threading. +// You can pass in the number of threads you'd like to use (try_splits). +// It returns the number of splits (threads) that you can call it with. +/// It might be less if the image resize can't be split up that many ways. + +STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int try_splits ); + +// This function does a split of the resizing (you call this fuction for each +// split, on multiple threads). A split is a piece of the output resize pixel space. + +// Note that you MUST call stbir_build_samplers_with_splits before stbir_resize_extended_split! + +// Usually, you will always call stbir_resize_split with split_start as the thread_index +// and "1" for the split_count. +// But, if you have a weird situation where you MIGHT want 8 threads, but sometimes +// only 4 threads, you can use 0,2,4,6 for the split_start's and use "2" for the +// split_count each time to turn in into a 4 thread resize. (This is unusual). + +STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count ); +//=============================================================== + + +//=============================================================== +// Pixel Callbacks info: +//-------------------------------- + +// The input callback is super flexible - it calls you with the input address +// (based on the stride and base pointer), it gives you an optional_output +// pointer that you can fill, or you can just return your own pointer into +// your own data. +// +// You can also do conversion from non-supported data types if necessary - in +// this case, you ignore the input_ptr and just use the x and y parameters to +// calculate your own input_ptr based on the size of each non-supported pixel. +// (Something like the third example below.) +// +// You can also install just an input or just an output callback by setting the +// callback that you don't want to zero. +// +// First example, progress: (getting a callback that you can monitor the progress): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// percentage_done = y / input_height; +// return input_ptr; // use buffer from call +// } +// +// Next example, copying: (copy from some other buffer or stream): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// CopyOrStreamData( optional_output, other_data_src, num_pixels * pixel_width_in_bytes ); +// return optional_output; // return the optional buffer that we filled +// } +// +// Third example, input another buffer without copying: (zero-copy from other buffer): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// void * pixels = ( (char*) other_image_base ) + ( y * other_image_stride ) + ( x * other_pixel_width_in_bytes ); +// return pixels; // return pointer to your data without copying +// } +// +// +// The output callback is considerably simpler - it just calls you so that you can dump +// out each scanline. You could even directly copy out to disk if you have a simple format +// like TGA or BMP. You can also convert to other output types here if you want. +// +// Simple example: +// void const * my_output( void * output_ptr, int num_pixels, int y, void * context ) +// { +// percentage_done = y / output_height; +// fwrite( output_ptr, pixel_width_in_bytes, num_pixels, output_file ); +// } +//=============================================================== + + + + +//=============================================================== +// optional built-in profiling API +//-------------------------------- + +#ifdef STBIR_PROFILE + +typedef struct STBIR_PROFILE_INFO +{ + stbir_uint64 total_clocks; + + // how many clocks spent (of total_clocks) in the various resize routines, along with a string description + // there are "resize_count" number of zones + stbir_uint64 clocks[ 8 ]; + char const ** descriptions; + + // count of clocks and descriptions + stbir_uint32 count; +} STBIR_PROFILE_INFO; + +// use after calling stbir_resize_extended (or stbir_build_samplers or stbir_build_samplers_with_splits) +STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize ); + +// use after calling stbir_resize_extended +STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize ); + +// use after calling stbir_resize_extended_split +STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize, int split_start, int split_num ); + +//=============================================================== + +#endif + + +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE2_H + +#if defined(STB_IMAGE_RESIZE_IMPLEMENTATION) || defined(STB_IMAGE_RESIZE2_IMPLEMENTATION) + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +#ifndef STBIR_MALLOC +#include +#define STBIR_MALLOC(size,user_data) ((void)(user_data), malloc(size)) +#define STBIR_FREE(ptr,user_data) ((void)(user_data), free(ptr)) +// (we used the comma operator to evaluate user_data, to avoid "unused parameter" warnings) +#endif + +#ifdef _MSC_VER + +#define stbir__inline __forceinline + +#else + +#define stbir__inline __inline__ + +// Clang address sanitizer +#if defined(__has_feature) + #if __has_feature(address_sanitizer) || __has_feature(memory_sanitizer) + #ifndef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__SEPARATE_ALLOCATIONS + #endif + #endif +#endif + +#endif + +// GCC and MSVC +#if defined(__SANITIZE_ADDRESS__) + #ifndef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__SEPARATE_ALLOCATIONS + #endif +#endif + +// Always turn off automatic FMA use - use STBIR_USE_FMA if you want. +// Otherwise, this is a determinism disaster. +#ifndef STBIR_DONT_CHANGE_FP_CONTRACT // override in case you don't want this behavior +#if defined(_MSC_VER) && !defined(__clang__) +#if _MSC_VER > 1200 +#pragma fp_contract(off) +#endif +#elif defined(__GNUC__) && !defined(__clang__) +#pragma GCC optimize("fp-contract=off") +#else +#pragma STDC FP_CONTRACT OFF +#endif +#endif + +#ifdef _MSC_VER +#define STBIR__UNUSED(v) (void)(v) +#else +#define STBIR__UNUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + + +#ifndef STBIR__HEADER_FILENAME +#define STBIR__HEADER_FILENAME "stb_image_resize2.h" +#endif + +// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types +// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible +typedef enum +{ + STBIRI_1CHANNEL = 0, + STBIRI_2CHANNEL = 1, + STBIRI_RGB = 2, + STBIRI_BGR = 3, + STBIRI_4CHANNEL = 4, + + STBIRI_RGBA = 5, + STBIRI_BGRA = 6, + STBIRI_ARGB = 7, + STBIRI_ABGR = 8, + STBIRI_RA = 9, + STBIRI_AR = 10, + + STBIRI_RGBA_PM = 11, + STBIRI_BGRA_PM = 12, + STBIRI_ARGB_PM = 13, + STBIRI_ABGR_PM = 14, + STBIRI_RA_PM = 15, + STBIRI_AR_PM = 16, +} stbir_internal_pixel_layout; + +// define the public pixel layouts to not compile inside the implementation (to avoid accidental use) +#define STBIR_BGR bad_dont_use_in_implementation +#define STBIR_1CHANNEL STBIR_BGR +#define STBIR_2CHANNEL STBIR_BGR +#define STBIR_RGB STBIR_BGR +#define STBIR_RGBA STBIR_BGR +#define STBIR_4CHANNEL STBIR_BGR +#define STBIR_BGRA STBIR_BGR +#define STBIR_ARGB STBIR_BGR +#define STBIR_ABGR STBIR_BGR +#define STBIR_RA STBIR_BGR +#define STBIR_AR STBIR_BGR +#define STBIR_RGBA_PM STBIR_BGR +#define STBIR_BGRA_PM STBIR_BGR +#define STBIR_ARGB_PM STBIR_BGR +#define STBIR_ABGR_PM STBIR_BGR +#define STBIR_RA_PM STBIR_BGR +#define STBIR_AR_PM STBIR_BGR + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1,1,1,2,4,2 // STBIR_TYPE_UINT8,STBIR_TYPE_UINT8_SRGB,STBIR_TYPE_UINT8_SRGB_ALPHA,STBIR_TYPE_UINT16,STBIR_TYPE_FLOAT,STBIR_TYPE_HALF_FLOAT +}; + +// When gathering, the contributors are which source pixels contribute. +// When scattering, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + int lowest; // First sample index for whole filter + int highest; // Last sample index for whole filter + int widest; // widest single set of samples for an output +} stbir__filter_extent_info; + +typedef struct +{ + int n0; // First pixel of decode buffer to write to + int n1; // Last pixel of decode that will be written to + int pixel_offset_for_input; // Pixel offset into input_scanline +} stbir__span; + +typedef struct stbir__scale_info +{ + int input_full_size; + int output_sub_size; + float scale; + float inv_scale; + float pixel_shift; // starting shift in output pixel space (in pixels) + int scale_is_rational; + stbir_uint32 scale_numerator, scale_denominator; +} stbir__scale_info; + +typedef struct +{ + stbir__contributors * contributors; + float* coefficients; + stbir__contributors * gather_prescatter_contributors; + float * gather_prescatter_coefficients; + stbir__scale_info scale_info; + float support; + stbir_filter filter_enum; + stbir__kernel_callback * filter_kernel; + stbir__support_callback * filter_support; + stbir_edge edge; + int coefficient_width; + int filter_pixel_width; + int filter_pixel_margin; + int num_contributors; + int contributors_size; + int coefficients_size; + stbir__filter_extent_info extent_info; + int is_gather; // 0 = scatter, 1 = gather with scale >= 1, 2 = gather with scale < 1 + int gather_prescatter_num_contributors; + int gather_prescatter_coefficient_width; + int gather_prescatter_contributors_size; + int gather_prescatter_coefficients_size; +} stbir__sampler; + +typedef struct +{ + stbir__contributors conservative; + int edge_sizes[2]; // this can be less than filter_pixel_margin, if the filter and scaling falls off + stbir__span spans[2]; // can be two spans, if doing input subrect with clamp mode WRAP +} stbir__extents; + +typedef struct +{ +#ifdef STBIR_PROFILE + union + { + struct { stbir_uint64 total, looping, vertical, horizontal, decode, encode, alpha, unalpha; } named; + stbir_uint64 array[8]; + } profile; + stbir_uint64 * current_zone_excluded_ptr; +#endif + float* decode_buffer; + + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer + int start_output_y, end_output_y; + int start_input_y, end_input_y; // used in scatter only + + #ifdef STBIR__SEPARATE_ALLOCATIONS + float** ring_buffers; // one pointer for each ring buffer + #else + float* ring_buffer; // one big buffer that we index into + #endif + + float* vertical_buffer; + + char no_cache_straddle[64]; +} stbir__per_split_info; + +typedef void stbir__decode_pixels_func( float * decode, int width_times_channels, void const * input ); +typedef void stbir__alpha_weight_func( float * decode_buffer, int width_times_channels ); +typedef void stbir__horizontal_gather_channels_func( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, + stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ); +typedef void stbir__alpha_unweight_func(float * encode_buffer, int width_times_channels ); +typedef void stbir__encode_pixels_func( void * output, int width_times_channels, float const * encode ); + +struct stbir__info +{ +#ifdef STBIR_PROFILE + union + { + struct { stbir_uint64 total, build, alloc, horizontal, vertical, cleanup, pivot; } named; + stbir_uint64 array[7]; + } profile; + stbir_uint64 * current_zone_excluded_ptr; +#endif + stbir__sampler horizontal; + stbir__sampler vertical; + + void const * input_data; + void * output_data; + + int input_stride_bytes; + int output_stride_bytes; + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_num_entries; // Total number of entries in the ring buffer. + + stbir_datatype input_type; + stbir_datatype output_type; + + stbir_input_callback * in_pixels_cb; + void * user_data; + stbir_output_callback * out_pixels_cb; + + stbir__extents scanline_extents; + + void * alloced_mem; + stbir__per_split_info * split_info; // by default 1, but there will be N of these allocated based on the thread init you did + + stbir__decode_pixels_func * decode_pixels; + stbir__alpha_weight_func * alpha_weight; + stbir__horizontal_gather_channels_func * horizontal_gather_channels; + stbir__alpha_unweight_func * alpha_unweight; + stbir__encode_pixels_func * encode_pixels; + + int alloced_total; + int splits; // count of splits + + stbir_internal_pixel_layout input_pixel_layout_internal; + stbir_internal_pixel_layout output_pixel_layout_internal; + + int input_color_and_type; + int offset_x, offset_y; // offset within output_data + int vertical_first; + int channels; + int effective_channels; // same as channels, except on RGBA/ARGB (7), or XA/AX (3) + int alloc_ring_buffer_num_entries; // Number of entries in the ring buffer that will be allocated +}; + + +#define stbir__max_uint8_as_float 255.0f +#define stbir__max_uint16_as_float 65535.0f +#define stbir__max_uint8_as_float_inverted (1.0f/255.0f) +#define stbir__max_uint16_as_float_inverted (1.0f/65535.0f) +#define stbir__small_float ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) + +// min/max friendly +#define STBIR_CLAMP(x, xmin, xmax) do { \ + if ( (x) < (xmin) ) (x) = (xmin); \ + if ( (x) > (xmax) ) (x) = (xmax); \ +} while (0) + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline int stbir__max(int a, int b) +{ + return a > b ? a : b; +} + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +typedef union +{ + unsigned int u; + float f; +} stbir__FP32; + +// From https://gist.github.com/rygorous/2203834 + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir__inline stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + return 0; + if (in > almostone.f) + return 255; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#ifndef STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT +#define STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT 32 // when downsampling and <= 32 scanlines of buffering, use gather. gather used down to 1/8th scaling for 25% win. +#endif + +// restrict pointers for the output pointers +#if defined( _MSC_VER ) && !defined(__clang__) + #define STBIR_STREAMOUT_PTR( star ) star __restrict + #define STBIR_NO_UNROLL( ptr ) __assume(ptr) // this oddly keeps msvc from unrolling a loop +#elif defined( __clang__ ) + #define STBIR_STREAMOUT_PTR( star ) star __restrict__ + #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) +#elif defined( __GNUC__ ) + #define STBIR_STREAMOUT_PTR( star ) star __restrict__ + #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) +#else + #define STBIR_STREAMOUT_PTR( star ) star + #define STBIR_NO_UNROLL( ptr ) +#endif + +#ifdef STBIR_NO_SIMD // force simd off for whatever reason + +// force simd off overrides everything else, so clear it all + +#ifdef STBIR_SSE2 +#undef STBIR_SSE2 +#endif + +#ifdef STBIR_AVX +#undef STBIR_AVX +#endif + +#ifdef STBIR_NEON +#undef STBIR_NEON +#endif + +#ifdef STBIR_AVX2 +#undef STBIR_AVX2 +#endif + +#ifdef STBIR_FP16C +#undef STBIR_FP16C +#endif + +#ifdef STBIR_WASM +#undef STBIR_WASM +#endif + +#ifdef STBIR_SIMD +#undef STBIR_SIMD +#endif + +#else // STBIR_SIMD + +#ifdef STBIR_SSE2 + #include + + #define stbir__simdf __m128 + #define stbir__simdi __m128i + + #define stbir_simdi_castf( reg ) _mm_castps_si128(reg) + #define stbir_simdf_casti( reg ) _mm_castsi128_ps(reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = _mm_loadu_ps( (float const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = _mm_loadu_si128 ( (stbir__simdi const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = _mm_castps_si128( _mm_load_ss( (float const*)(ptr) )) + #define stbir__simdf_load1z( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values must be zero + #define stbir__simdf_frep4( fvar ) _mm_set_ps1( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = _mm_set_ps1( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = _mm_castpd_ps(_mm_loadh_pd( _mm_castps_pd(reg), (double*)(ptr) )) + + #define stbir__simdf_zeroP() _mm_setzero_ps() + #define stbir__simdf_zero( reg ) (reg) = _mm_setzero_ps() + + #define stbir__simdf_store( ptr, reg ) _mm_storeu_ps( (float*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), reg ) + #define stbir__simdf_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), _mm_castps_si128(reg) ) + #define stbir__simdf_store2h( ptr, reg ) _mm_storeh_pd( (double*)(ptr), _mm_castps_pd(reg) ) + + #define stbir__simdi_store( ptr, reg ) _mm_storeu_si128( (__m128i*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), _mm_castsi128_ps(reg) ) + #define stbir__simdi_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), (reg) ) + + #define stbir__prefetch( ptr ) _mm_prefetch((char*)(ptr), _MM_HINT_T0 ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out2 = _mm_unpacklo_epi8( ireg, zero ); \ + out3 = _mm_unpackhi_epi8( ireg, zero ); \ + out0 = _mm_unpacklo_epi16( out2, zero ); \ + out1 = _mm_unpackhi_epi16( out2, zero ); \ + out2 = _mm_unpacklo_epi16( out3, zero ); \ + out3 = _mm_unpackhi_epi16( out3, zero ); \ + } + +#define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out = _mm_unpacklo_epi8( ireg, zero ); \ + out = _mm_unpacklo_epi16( out, zero ); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out0 = _mm_unpacklo_epi16( ireg, zero ); \ + out1 = _mm_unpackhi_epi16( ireg, zero ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = _mm_cvttps_epi32(f) + #define stbir__simdf_convert_float_to_int( f ) _mm_cvtt_ss2si(f) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),_mm_setzero_ps())))) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())))) + + #define stbir__simdi_to_int( i ) _mm_cvtsi128_si32(i) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = _mm_cvtepi32_ps( ireg ) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = _mm_add_ps( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = _mm_mul_ps( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = _mm_mul_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = _mm_mul_ss( reg, _mm_load_ss( (float const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = _mm_add_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = _mm_add_ss( reg, _mm_load_ss( (float const*)(ptr) ) ) + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd + #include + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_fmadd_ps( mul1, mul2, add ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_fmadd_ss( mul1, mul2, add ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ps( mul, _mm_loadu_ps( (float const*)(ptr) ), add ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ss( mul, _mm_load_ss( (float const*)(ptr) ), add ) + #else + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_add_ps( add, _mm_mul_ps( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_add_ss( add, _mm_mul_ss( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_add_ps( add, _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_add_ss( add, _mm_mul_ss( mul, _mm_load_ss( (float const*)(ptr) ) ) ) + #endif + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = _mm_add_ss( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = _mm_mul_ss( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = _mm_and_ps( reg0, reg1 ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = _mm_or_ps( reg0, reg1 ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = _mm_min_ps( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = _mm_max_ps( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = _mm_min_ss( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = _mm_max_ss( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (3<<0) + (0<<2) + (1<<4) + (2<<6) ) ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (2<<0) + (3<<2) + (0<<4) + (1<<6) ) ) + + static const stbir__simdf STBIR_zeroones = { 0.0f,1.0f,0.0f,1.0f }; + static const stbir__simdf STBIR_onezeros = { 1.0f,0.0f,1.0f,0.0f }; + #define stbir__simdf_aaa1( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movehl_ps( ones, alp ) ), (1<<0) + (1<<2) + (1<<4) + (2<<6) ) ) + #define stbir__simdf_1aaa( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movelh_ps( ones, alp ) ), (0<<0) + (2<<2) + (2<<4) + (2<<6) ) ) + #define stbir__simdf_a1a1( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_srli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_zeroones ) + #define stbir__simdf_1a1a( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_slli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_onezeros ) + + #define stbir__simdf_swiz( reg, one, two, three, four ) _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( reg ), (one<<0) + (two<<2) + (three<<4) + (four<<6) ) ) + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = _mm_and_si128( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = _mm_or_si128( reg0, reg1 ) + #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = _mm_madd_epi16( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + stbir__simdf af,bf; \ + stbir__simdi a,b; \ + af = _mm_min_ps( aa, STBIR_max_uint8_as_float ); \ + bf = _mm_min_ps( bb, STBIR_max_uint8_as_float ); \ + af = _mm_max_ps( af, _mm_setzero_ps() ); \ + bf = _mm_max_ps( bf, _mm_setzero_ps() ); \ + a = _mm_cvttps_epi32( af ); \ + b = _mm_cvttps_epi32( bf ); \ + a = _mm_packs_epi32( a, b ); \ + out = _mm_packus_epi16( a, a ); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + stbir__simdf_load( o0, (ptr) ); \ + stbir__simdf_load( o1, (ptr)+4 ); \ + stbir__simdf_load( o2, (ptr)+8 ); \ + stbir__simdf_load( o3, (ptr)+12 ); \ + { \ + __m128 tmp0, tmp1, tmp2, tmp3; \ + tmp0 = _mm_unpacklo_ps(o0, o1); \ + tmp2 = _mm_unpacklo_ps(o2, o3); \ + tmp1 = _mm_unpackhi_ps(o0, o1); \ + tmp3 = _mm_unpackhi_ps(o2, o3); \ + o0 = _mm_movelh_ps(tmp0, tmp2); \ + o1 = _mm_movehl_ps(tmp2, tmp0); \ + o2 = _mm_movelh_ps(tmp1, tmp3); \ + o3 = _mm_movehl_ps(tmp3, tmp1); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + r0 = _mm_packs_epi32( r0, r1 ); \ + r2 = _mm_packs_epi32( r2, r3 ); \ + r1 = _mm_unpacklo_epi16( r0, r2 ); \ + r3 = _mm_unpackhi_epi16( r0, r2 ); \ + r0 = _mm_unpacklo_epi16( r1, r3 ); \ + r2 = _mm_unpackhi_epi16( r1, r3 ); \ + r0 = _mm_packus_epi16( r0, r2 ); \ + stbir__simdi_store( ptr, r0 ); \ + + #define stbir__simdi_32shr( out, reg, imm ) out = _mm_srli_epi32( reg, imm ) + + #if defined(_MSC_VER) && !defined(__clang__) + // msvc inits with 8 bytes + #define STBIR__CONST_32_TO_8( v ) (char)(unsigned char)((v)&255),(char)(unsigned char)(((v)>>8)&255),(char)(unsigned char)(((v)>>16)&255),(char)(unsigned char)(((v)>>24)&255) + #define STBIR__CONST_4_32i( v ) STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ) + #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) STBIR__CONST_32_TO_8( v0 ), STBIR__CONST_32_TO_8( v1 ), STBIR__CONST_32_TO_8( v2 ), STBIR__CONST_32_TO_8( v3 ) + #else + // everything else inits with long long's + #define STBIR__CONST_4_32i( v ) (long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))),(long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))) + #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) (long long)((((stbir_uint64)(stbir_uint32)(v1))<<32)|((stbir_uint64)(stbir_uint32)(v0))),(long long)((((stbir_uint64)(stbir_uint32)(v3))<<32)|((stbir_uint64)(stbir_uint32)(v2))) + #endif + + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { STBIR__CONST_4_32i(x) } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + + #if defined(STBIR_AVX) || defined(__SSE4_1__) + #include + #define stbir__simdf_pack_to_8words(out,reg0,reg1) out = _mm_packus_epi32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())), _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps()))) + #else + STBIR__SIMDI_CONST(stbir__s32_32768, 32768); + STBIR__SIMDI_CONST(stbir__s16_32768, ((32768<<16)|32768)); + + #define stbir__simdf_pack_to_8words(out,reg0,reg1) \ + { \ + stbir__simdi tmp0,tmp1; \ + tmp0 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \ + tmp1 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \ + tmp0 = _mm_sub_epi32( tmp0, stbir__s32_32768 ); \ + tmp1 = _mm_sub_epi32( tmp1, stbir__s32_32768 ); \ + out = _mm_packs_epi32( tmp0, tmp1 ); \ + out = _mm_sub_epi16( out, stbir__s16_32768 ); \ + } + + #endif + + #define STBIR_SIMD + + // if we detect AVX, set the simd8 defines + #ifdef STBIR_AVX + #include + #define STBIR_SIMD8 + #define stbir__simdf8 __m256 + #define stbir__simdi8 __m256i + #define stbir__simdf8_load( out, ptr ) (out) = _mm256_loadu_ps( (float const *)(ptr) ) + #define stbir__simdi8_load( out, ptr ) (out) = _mm256_loadu_si256( (__m256i const *)(ptr) ) + #define stbir__simdf8_mult( out, a, b ) (out) = _mm256_mul_ps( (a), (b) ) + #define stbir__simdf8_store( ptr, out ) _mm256_storeu_ps( (float*)(ptr), out ) + #define stbir__simdi8_store( ptr, reg ) _mm256_storeu_si256( (__m256i*)(ptr), reg ) + #define stbir__simdf8_frep8( fval ) _mm256_set1_ps( fval ) + + #define stbir__simdf8_min( out, reg0, reg1 ) (out) = _mm256_min_ps( reg0, reg1 ) + #define stbir__simdf8_max( out, reg0, reg1 ) (out) = _mm256_max_ps( reg0, reg1 ) + + #define stbir__simdf8_add4halves( out, bot4, top8 ) (out) = _mm_add_ps( bot4, _mm256_extractf128_ps( top8, 1 ) ) + #define stbir__simdf8_mult_mem( out, reg, ptr ) (out) = _mm256_mul_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf8_add_mem( out, reg, ptr ) (out) = _mm256_add_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf8_add( out, a, b ) (out) = _mm256_add_ps( a, b ) + #define stbir__simdf8_load1b( out, ptr ) (out) = _mm256_broadcast_ss( ptr ) + #define stbir__simdf_load1rep4( out, ptr ) (out) = _mm_broadcast_ss( ptr ) // avx load instruction + + #define stbir__simdi8_convert_i32_to_float(out, ireg) (out) = _mm256_cvtepi32_ps( ireg ) + #define stbir__simdf8_convert_float_to_i32( i, f ) (i) = _mm256_cvttps_epi32(f) + + #define stbir__simdf8_bot4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (0<<0)+(2<<4) ) + #define stbir__simdf8_top4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (1<<0)+(3<<4) ) + + #define stbir__simdf8_gettop4( reg ) _mm256_extractf128_ps(reg,1) + + #ifdef STBIR_AVX2 + + #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi8 a, zero =_mm256_setzero_si256();\ + a = _mm256_permute4x64_epi64( _mm256_unpacklo_epi8( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), zero ),(0<<0)+(2<<2)+(1<<4)+(3<<6)); \ + out0 = _mm256_unpacklo_epi16( a, zero ); \ + out1 = _mm256_unpackhi_epi16( a, zero ); \ + } + + #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \ + { \ + stbir__simdi8 t; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + t = _mm256_permute4x64_epi64( _mm256_packs_epi32( a, b ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \ + out = _mm256_castsi256_si128( _mm256_permute4x64_epi64( _mm256_packus_epi16( t, t ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ) ); \ + } + + #define stbir__simdi8_expand_u16_to_u32(out,ireg) out = _mm256_unpacklo_epi16( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), _mm256_setzero_si256() ); + + #define stbir__simdf8_pack_to_16words(out,aa,bb) \ + { \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + (out) = _mm256_permute4x64_epi64( _mm256_packus_epi32(a, b), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \ + } + + #else + + #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi a,zero = _mm_setzero_si128(); \ + a = _mm_unpacklo_epi8( ireg, zero ); \ + out0 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \ + a = _mm_unpackhi_epi8( ireg, zero ); \ + out1 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \ + } + + #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \ + { \ + stbir__simdi t; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + out = _mm_packs_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \ + out = _mm_packus_epi16( out, out ); \ + t = _mm_packs_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \ + t = _mm_packus_epi16( t, t ); \ + out = _mm_castps_si128( _mm_shuffle_ps( _mm_castsi128_ps(out), _mm_castsi128_ps(t), (0<<0)+(1<<2)+(0<<4)+(1<<6) ) ); \ + } + + #define stbir__simdi8_expand_u16_to_u32(out,ireg) \ + { \ + stbir__simdi a,b,zero = _mm_setzero_si128(); \ + a = _mm_unpacklo_epi16( ireg, zero ); \ + b = _mm_unpackhi_epi16( ireg, zero ); \ + out = _mm256_insertf128_si256( _mm256_castsi128_si256( a ), b, 1 ); \ + } + + #define stbir__simdf8_pack_to_16words(out,aa,bb) \ + { \ + stbir__simdi t0,t1; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + t0 = _mm_packus_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \ + t1 = _mm_packus_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \ + out = _mm256_setr_m128i( t0, t1 ); \ + } + + #endif + + static __m256i stbir_00001111 = { STBIR__CONST_4d_32i( 0, 0, 0, 0 ), STBIR__CONST_4d_32i( 1, 1, 1, 1 ) }; + #define stbir__simdf8_0123to00001111( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00001111 ) + + static __m256i stbir_22223333 = { STBIR__CONST_4d_32i( 2, 2, 2, 2 ), STBIR__CONST_4d_32i( 3, 3, 3, 3 ) }; + #define stbir__simdf8_0123to22223333( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_22223333 ) + + #define stbir__simdf8_0123to2222( out, in ) (out) = stbir__simdf_swiz(_mm256_castps256_ps128(in), 2,2,2,2 ) + + #define stbir__simdf8_load2( out, ptr ) (out) = _mm256_castsi256_ps(_mm256_castsi128_si256( _mm_loadl_epi64( (__m128i*)(ptr)) )) // top values can be random (not denormal or nan for perf) + #define stbir__simdf8_load4b( out, ptr ) (out) = _mm256_broadcast_ps( (__m128 const *)(ptr) ) + + static __m256i stbir_00112233 = { STBIR__CONST_4d_32i( 0, 0, 1, 1 ), STBIR__CONST_4d_32i( 2, 2, 3, 3 ) }; + #define stbir__simdf8_0123to00112233( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00112233 ) + #define stbir__simdf8_add4( out, a8, b ) (out) = _mm256_add_ps( a8, _mm256_castps128_ps256( b ) ) + + static __m256i stbir_load6 = { STBIR__CONST_4_32i( 0x80000000 ), STBIR__CONST_4d_32i( 0x80000000, 0x80000000, 0, 0 ) }; + #define stbir__simdf8_load6z( out, ptr ) (out) = _mm256_maskload_ps( ptr, stbir_load6 ) + + #define stbir__simdf8_0123to00000000( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(0<<4)+(0<<6) ) + #define stbir__simdf8_0123to11111111( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(1<<4)+(1<<6) ) + #define stbir__simdf8_0123to22222222( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(2<<2)+(2<<4)+(2<<6) ) + #define stbir__simdf8_0123to33333333( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(3<<2)+(3<<4)+(3<<6) ) + #define stbir__simdf8_0123to21032103( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(1<<2)+(0<<4)+(3<<6) ) + #define stbir__simdf8_0123to32103210( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(2<<2)+(1<<4)+(0<<6) ) + #define stbir__simdf8_0123to12301230( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(2<<2)+(3<<4)+(0<<6) ) + #define stbir__simdf8_0123to10321032( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(0<<2)+(3<<4)+(2<<6) ) + #define stbir__simdf8_0123to30123012( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(0<<2)+(1<<4)+(2<<6) ) + + #define stbir__simdf8_0123to11331133( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(3<<4)+(3<<6) ) + #define stbir__simdf8_0123to00220022( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(2<<4)+(2<<6) ) + + #define stbir__simdf8_aaa1( out, alp, ones ) (out) = _mm256_blend_ps( alp, ones, (1<<0)+(1<<1)+(1<<2)+(0<<3)+(1<<4)+(1<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (3<<0) + (3<<2) + (3<<4) + (0<<6) ) + #define stbir__simdf8_1aaa( out, alp, ones ) (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(1<<2)+(1<<3)+(0<<4)+(1<<5)+(1<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (0<<4) + (0<<6) ) + #define stbir__simdf8_a1a1( out, alp, ones) (out) = _mm256_blend_ps( alp, ones, (1<<0)+(0<<1)+(1<<2)+(0<<3)+(1<<4)+(0<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ) + #define stbir__simdf8_1a1a( out, alp, ones) (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(0<<2)+(1<<3)+(0<<4)+(1<<5)+(0<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ) + + #define stbir__simdf8_zero( reg ) (reg) = _mm256_setzero_ps() + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd + #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_fmadd_ps( mul1, mul2, add ) + #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ), add ) + #define stbir__simdf8_madd_mem4( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( _mm256_castps128_ps256( mul ), _mm256_castps128_ps256( _mm_loadu_ps( (float const*)(ptr) ) ), add ) + #else + #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul1, mul2 ) ) + #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ) ) ) + #define stbir__simdf8_madd_mem4( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_castps128_ps256( _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) ) ) + #endif + #define stbir__if_simdf8_cast_to_simdf4( val ) _mm256_castps256_ps128( val ) + + #endif + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) // martins floorf + { + #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41) + __m128 t = _mm_set_ss(x); + return _mm_cvtss_f32( _mm_floor_ss(t, t) ); + #else + __m128 f = _mm_set_ss(x); + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f)); + __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(f, t), _mm_set_ss(-1.0f))); + return _mm_cvtss_f32(r); + #endif + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) // martins ceilf + { + #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41) + __m128 t = _mm_set_ss(x); + return _mm_cvtss_f32( _mm_ceil_ss(t, t) ); + #else + __m128 f = _mm_set_ss(x); + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f)); + __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(t, f), _mm_set_ss(1.0f))); + return _mm_cvtss_f32(r); + #endif + } + +#elif defined(STBIR_NEON) + + #include + + #define stbir__simdf float32x4_t + #define stbir__simdi uint32x4_t + + #define stbir_simdi_castf( reg ) vreinterpretq_u32_f32(reg) + #define stbir_simdf_casti( reg ) vreinterpretq_f32_u32(reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = vld1q_f32( (float const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = vld1q_u32( (uint32_t const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = vld1q_dup_f32( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = vld1q_dup_u32( (uint32_t const*)(ptr) ) + #define stbir__simdf_load1z( out, ptr ) (out) = vld1q_lane_f32( (float const*)(ptr), vdupq_n_f32(0), 0 ) // top values must be zero + #define stbir__simdf_frep4( fvar ) vdupq_n_f32( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = vdupq_n_f32( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = vcombine_f32( vget_low_f32(reg), vld1_f32( (float const*)(ptr) ) ) + + #define stbir__simdf_zeroP() vdupq_n_f32(0) + #define stbir__simdf_zero( reg ) (reg) = vdupq_n_f32(0) + + #define stbir__simdf_store( ptr, reg ) vst1q_f32( (float*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) vst1q_lane_f32( (float*)(ptr), reg, 0) + #define stbir__simdf_store2( ptr, reg ) vst1_f32( (float*)(ptr), vget_low_f32(reg) ) + #define stbir__simdf_store2h( ptr, reg ) vst1_f32( (float*)(ptr), vget_high_f32(reg) ) + + #define stbir__simdi_store( ptr, reg ) vst1q_u32( (uint32_t*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) vst1q_lane_u32( (uint32_t*)(ptr), reg, 0 ) + #define stbir__simdi_store2( ptr, reg ) vst1_u32( (uint32_t*)(ptr), vget_low_u32(reg) ) + + #define stbir__prefetch( ptr ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + uint16x8_t l = vmovl_u8( vget_low_u8 ( vreinterpretq_u8_u32(ireg) ) ); \ + uint16x8_t h = vmovl_u8( vget_high_u8( vreinterpretq_u8_u32(ireg) ) ); \ + out0 = vmovl_u16( vget_low_u16 ( l ) ); \ + out1 = vmovl_u16( vget_high_u16( l ) ); \ + out2 = vmovl_u16( vget_low_u16 ( h ) ); \ + out3 = vmovl_u16( vget_high_u16( h ) ); \ + } + + #define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + uint16x8_t tmp = vmovl_u8( vget_low_u8( vreinterpretq_u8_u32(ireg) ) ); \ + out = vmovl_u16( vget_low_u16( tmp ) ); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + uint16x8_t tmp = vreinterpretq_u16_u32(ireg); \ + out0 = vmovl_u16( vget_low_u16 ( tmp ) ); \ + out1 = vmovl_u16( vget_high_u16( tmp ) ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = vreinterpretq_u32_s32( vcvtq_s32_f32(f) ) + #define stbir__simdf_convert_float_to_int( f ) vgetq_lane_s32(vcvtq_s32_f32(f), 0) + #define stbir__simdi_to_int( i ) (int)vgetq_lane_u32(i, 0) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),vdupq_n_f32(0))), 0)) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),vdupq_n_f32(0))), 0)) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = vcvtq_f32_s32( vreinterpretq_s32_u32(ireg) ) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) ) + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd (and also x64 no madd to arm madd) + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_dup_f32( (float const*)(ptr) ) ) + #else + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_f32( (float const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_dup_f32( (float const*)(ptr) ) ) ) + #endif + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 3 ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 2 ) + + #define stbir__simdf_a1a1( out, alp, ones ) (out) = vzipq_f32(vuzpq_f32(alp, alp).val[1], ones).val[0] + #define stbir__simdf_1a1a( out, alp, ones ) (out) = vzipq_f32(ones, vuzpq_f32(alp, alp).val[0]).val[0] + + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + + #define stbir__simdf_aaa1( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3, ones, 3) + #define stbir__simdf_1aaa( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0, ones, 0) + + #if defined( _MSC_VER ) && !defined(__clang__) + #define stbir_make16(a,b,c,d) vcombine_u8( \ + vcreate_u8( (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \ + ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56)), \ + vcreate_u8( (4*c+0) | ((4*c+1)<<8) | ((4*c+2)<<16) | ((4*c+3)<<24) | \ + ((stbir_uint64)(4*d+0)<<32) | ((stbir_uint64)(4*d+1)<<40) | ((stbir_uint64)(4*d+2)<<48) | ((stbir_uint64)(4*d+3)<<56) ) ) + #else + #define stbir_make16(a,b,c,d) (uint8x16_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3,4*c+0,4*c+1,4*c+2,4*c+3,4*d+0,4*d+1,4*d+2,4*d+3} + #endif + + #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vqtbl1q_u8( vreinterpretq_u8_f32(reg), stbir_make16(one, two, three, four) ) ) + + #define stbir__simdi_16madd( out, reg0, reg1 ) \ + { \ + int16x8_t r0 = vreinterpretq_s16_u32(reg0); \ + int16x8_t r1 = vreinterpretq_s16_u32(reg1); \ + int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \ + int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \ + (out) = vreinterpretq_u32_s32( vpaddq_s32(tmp0, tmp1) ); \ + } + + #else + + #define stbir__simdf_aaa1( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3) + #define stbir__simdf_1aaa( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0) + + #if defined( _MSC_VER ) && !defined(__clang__) + static stbir__inline uint8x8x2_t stbir_make8x2(float32x4_t reg) + { + uint8x8x2_t r = { { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } }; + return r; + } + #define stbir_make8(a,b) vcreate_u8( \ + (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \ + ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56) ) + #else + #define stbir_make8x2(reg) (uint8x8x2_t){ { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } } + #define stbir_make8(a,b) (uint8x8_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3} + #endif + + #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vcombine_u8( \ + vtbl2_u8( stbir_make8x2( reg ), stbir_make8( one, two ) ), \ + vtbl2_u8( stbir_make8x2( reg ), stbir_make8( three, four ) ) ) ) + + #define stbir__simdi_16madd( out, reg0, reg1 ) \ + { \ + int16x8_t r0 = vreinterpretq_s16_u32(reg0); \ + int16x8_t r1 = vreinterpretq_s16_u32(reg1); \ + int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \ + int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \ + int32x2_t out0 = vpadd_s32( vget_low_s32(tmp0), vget_high_s32(tmp0) ); \ + int32x2_t out1 = vpadd_s32( vget_low_s32(tmp1), vget_high_s32(tmp1) ); \ + (out) = vreinterpretq_u32_s32( vcombine_s32(out0, out1) ); \ + } + + #endif + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = vandq_u32( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = vorrq_u32( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \ + float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \ + int16x4_t ai = vqmovn_s32( vcvtq_s32_f32( af ) ); \ + int16x4_t bi = vqmovn_s32( vcvtq_s32_f32( bf ) ); \ + uint8x8_t out8 = vqmovun_s16( vcombine_s16(ai, bi) ); \ + out = vreinterpretq_u32_u8( vcombine_u8(out8, out8) ); \ + } + + #define stbir__simdf_pack_to_8words(out,aa,bb) \ + { \ + float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \ + float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \ + int32x4_t ai = vcvtq_s32_f32( af ); \ + int32x4_t bi = vcvtq_s32_f32( bf ); \ + out = vreinterpretq_u32_u16( vcombine_u16(vqmovun_s32(ai), vqmovun_s32(bi)) ); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + { \ + int16x4x2_t tmp0 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r0)), vqmovn_s32(vreinterpretq_s32_u32(r2)) ); \ + int16x4x2_t tmp1 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r1)), vqmovn_s32(vreinterpretq_s32_u32(r3)) ); \ + uint8x8x2_t out = \ + { { \ + vqmovun_s16( vcombine_s16(tmp0.val[0], tmp0.val[1]) ), \ + vqmovun_s16( vcombine_s16(tmp1.val[0], tmp1.val[1]) ), \ + } }; \ + vst2_u8(ptr, out); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + { \ + float32x4x4_t tmp = vld4q_f32(ptr); \ + o0 = tmp.val[0]; \ + o1 = tmp.val[1]; \ + o2 = tmp.val[2]; \ + o3 = tmp.val[3]; \ + } + + #define stbir__simdi_32shr( out, reg, imm ) out = vshrq_n_u32( reg, imm ) + + #if defined( _MSC_VER ) && !defined(__clang__) + #define STBIR__SIMDF_CONST(var, x) __declspec(align(8)) float var[] = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) __declspec(align(8)) uint32_t var[] = { x, x, x, x } + #define STBIR__CONSTF(var) (*(const float32x4_t*)var) + #define STBIR__CONSTI(var) (*(const uint32x4_t*)var) + #else + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + #endif + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) + { + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + return vget_lane_f32( vrndm_f32( vdup_n_f32(x) ), 0); + #else + float32x2_t f = vdup_n_f32(x); + float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f)); + uint32x2_t a = vclt_f32(f, t); + uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(-1.0f)); + float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b))); + return vget_lane_f32(r, 0); + #endif + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) + { + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + return vget_lane_f32( vrndp_f32( vdup_n_f32(x) ), 0); + #else + float32x2_t f = vdup_n_f32(x); + float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f)); + uint32x2_t a = vclt_f32(t, f); + uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(1.0f)); + float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b))); + return vget_lane_f32(r, 0); + #endif + } + + #define STBIR_SIMD + +#elif defined(STBIR_WASM) + + #include + + #define stbir__simdf v128_t + #define stbir__simdi v128_t + + #define stbir_simdi_castf( reg ) (reg) + #define stbir_simdf_casti( reg ) (reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) + #define stbir__simdf_load1z( out, ptr ) (out) = wasm_v128_load32_zero( (void const*)(ptr) ) // top values must be zero + #define stbir__simdf_frep4( fvar ) wasm_f32x4_splat( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = wasm_f32x4_splat( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = wasm_v128_load64_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = wasm_v128_load64_zero( (void const*)(ptr) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = wasm_v128_load64_lane( (void const*)(ptr), reg, 1 ) + + #define stbir__simdf_zeroP() wasm_f32x4_const_splat(0) + #define stbir__simdf_zero( reg ) (reg) = wasm_f32x4_const_splat(0) + + #define stbir__simdf_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdf_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdf_store2h( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 1 ) + + #define stbir__simdi_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdi_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 ) + + #define stbir__prefetch( ptr ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + v128_t l = wasm_u16x8_extend_low_u8x16 ( ireg ); \ + v128_t h = wasm_u16x8_extend_high_u8x16( ireg ); \ + out0 = wasm_u32x4_extend_low_u16x8 ( l ); \ + out1 = wasm_u32x4_extend_high_u16x8( l ); \ + out2 = wasm_u32x4_extend_low_u16x8 ( h ); \ + out3 = wasm_u32x4_extend_high_u16x8( h ); \ + } + + #define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + v128_t tmp = wasm_u16x8_extend_low_u8x16(ireg); \ + out = wasm_u32x4_extend_low_u16x8(tmp); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + out0 = wasm_u32x4_extend_low_u16x8 ( ireg ); \ + out1 = wasm_u32x4_extend_high_u16x8( ireg ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = wasm_i32x4_trunc_sat_f32x4(f) + #define stbir__simdf_convert_float_to_int( f ) wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(f), 0) + #define stbir__simdi_to_int( i ) wasm_i32x4_extract_lane(i, 0) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint8_as_float),wasm_f32x4_const_splat(0))), 0)) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint16_as_float),wasm_f32x4_const_splat(0))), 0)) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = wasm_f32x4_convert_i32x4(ireg) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load( (void const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load32_splat( (void const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load( (void const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load32_splat( (void const*)(ptr) ) ) + + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load( (void const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load32_splat( (void const*)(ptr) ) ) ) + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 3, 4, 5, -1 ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 2, 3, 4, -1 ) + + #define stbir__simdf_aaa1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 3, 3, 3, 4) + #define stbir__simdf_1aaa(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 0, 0) + #define stbir__simdf_a1a1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 1, 4, 3, 4) + #define stbir__simdf_1a1a(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 4, 2) + + #define stbir__simdf_swiz( reg, one, two, three, four ) wasm_i32x4_shuffle(reg, reg, one, two, three, four) + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 ) + #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = wasm_i32x4_dot_i16x8( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \ + v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \ + v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \ + v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \ + v128_t out16 = wasm_i16x8_narrow_i32x4( ai, bi ); \ + out = wasm_u8x16_narrow_i16x8( out16, out16 ); \ + } + + #define stbir__simdf_pack_to_8words(out,aa,bb) \ + { \ + v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \ + v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \ + v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \ + v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \ + out = wasm_u16x8_narrow_i32x4( ai, bi ); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + { \ + v128_t tmp0 = wasm_i16x8_narrow_i32x4(r0, r1); \ + v128_t tmp1 = wasm_i16x8_narrow_i32x4(r2, r3); \ + v128_t tmp = wasm_u8x16_narrow_i16x8(tmp0, tmp1); \ + tmp = wasm_i8x16_shuffle(tmp, tmp, 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); \ + wasm_v128_store( (void*)(ptr), tmp); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + { \ + v128_t t0 = wasm_v128_load( ptr ); \ + v128_t t1 = wasm_v128_load( ptr+4 ); \ + v128_t t2 = wasm_v128_load( ptr+8 ); \ + v128_t t3 = wasm_v128_load( ptr+12 ); \ + v128_t s0 = wasm_i32x4_shuffle(t0, t1, 0, 4, 2, 6); \ + v128_t s1 = wasm_i32x4_shuffle(t0, t1, 1, 5, 3, 7); \ + v128_t s2 = wasm_i32x4_shuffle(t2, t3, 0, 4, 2, 6); \ + v128_t s3 = wasm_i32x4_shuffle(t2, t3, 1, 5, 3, 7); \ + o0 = wasm_i32x4_shuffle(s0, s2, 0, 1, 4, 5); \ + o1 = wasm_i32x4_shuffle(s1, s3, 0, 1, 4, 5); \ + o2 = wasm_i32x4_shuffle(s0, s2, 2, 3, 6, 7); \ + o3 = wasm_i32x4_shuffle(s1, s3, 2, 3, 6, 7); \ + } + + #define stbir__simdi_32shr( out, reg, imm ) out = wasm_u32x4_shr( reg, imm ) + + typedef float stbir__f32x4 __attribute__((__vector_size__(16), __aligned__(16))); + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = (v128_t)(stbir__f32x4){ x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) + { + return wasm_f32x4_extract_lane( wasm_f32x4_floor( wasm_f32x4_splat(x) ), 0); + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) + { + return wasm_f32x4_extract_lane( wasm_f32x4_ceil( wasm_f32x4_splat(x) ), 0); + } + + #define STBIR_SIMD + +#endif // SSE2/NEON/WASM + +#endif // NO SIMD + +#ifdef STBIR_SIMD8 + #define stbir__simdfX stbir__simdf8 + #define stbir__simdiX stbir__simdi8 + #define stbir__simdfX_load stbir__simdf8_load + #define stbir__simdiX_load stbir__simdi8_load + #define stbir__simdfX_mult stbir__simdf8_mult + #define stbir__simdfX_add_mem stbir__simdf8_add_mem + #define stbir__simdfX_madd_mem stbir__simdf8_madd_mem + #define stbir__simdfX_store stbir__simdf8_store + #define stbir__simdiX_store stbir__simdi8_store + #define stbir__simdf_frepX stbir__simdf8_frep8 + #define stbir__simdfX_madd stbir__simdf8_madd + #define stbir__simdfX_min stbir__simdf8_min + #define stbir__simdfX_max stbir__simdf8_max + #define stbir__simdfX_aaa1 stbir__simdf8_aaa1 + #define stbir__simdfX_1aaa stbir__simdf8_1aaa + #define stbir__simdfX_a1a1 stbir__simdf8_a1a1 + #define stbir__simdfX_1a1a stbir__simdf8_1a1a + #define stbir__simdfX_convert_float_to_i32 stbir__simdf8_convert_float_to_i32 + #define stbir__simdfX_pack_to_words stbir__simdf8_pack_to_16words + #define stbir__simdfX_zero stbir__simdf8_zero + #define STBIR_onesX STBIR_ones8 + #define STBIR_max_uint8_as_floatX STBIR_max_uint8_as_float8 + #define STBIR_max_uint16_as_floatX STBIR_max_uint16_as_float8 + #define STBIR_simd_point5X STBIR_simd_point58 + #define stbir__simdfX_float_count 8 + #define stbir__simdfX_0123to1230 stbir__simdf8_0123to12301230 + #define stbir__simdfX_0123to2103 stbir__simdf8_0123to21032103 + static const stbir__simdf8 STBIR_max_uint16_as_float_inverted8 = { stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted }; + static const stbir__simdf8 STBIR_max_uint8_as_float_inverted8 = { stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted }; + static const stbir__simdf8 STBIR_ones8 = { 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }; + static const stbir__simdf8 STBIR_simd_point58 = { 0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5 }; + static const stbir__simdf8 STBIR_max_uint8_as_float8 = { stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float, stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float }; + static const stbir__simdf8 STBIR_max_uint16_as_float8 = { stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float, stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float }; +#else + #define stbir__simdfX stbir__simdf + #define stbir__simdiX stbir__simdi + #define stbir__simdfX_load stbir__simdf_load + #define stbir__simdiX_load stbir__simdi_load + #define stbir__simdfX_mult stbir__simdf_mult + #define stbir__simdfX_add_mem stbir__simdf_add_mem + #define stbir__simdfX_madd_mem stbir__simdf_madd_mem + #define stbir__simdfX_store stbir__simdf_store + #define stbir__simdiX_store stbir__simdi_store + #define stbir__simdf_frepX stbir__simdf_frep4 + #define stbir__simdfX_madd stbir__simdf_madd + #define stbir__simdfX_min stbir__simdf_min + #define stbir__simdfX_max stbir__simdf_max + #define stbir__simdfX_aaa1 stbir__simdf_aaa1 + #define stbir__simdfX_1aaa stbir__simdf_1aaa + #define stbir__simdfX_a1a1 stbir__simdf_a1a1 + #define stbir__simdfX_1a1a stbir__simdf_1a1a + #define stbir__simdfX_convert_float_to_i32 stbir__simdf_convert_float_to_i32 + #define stbir__simdfX_pack_to_words stbir__simdf_pack_to_8words + #define stbir__simdfX_zero stbir__simdf_zero + #define STBIR_onesX STBIR__CONSTF(STBIR_ones) + #define STBIR_simd_point5X STBIR__CONSTF(STBIR_simd_point5) + #define STBIR_max_uint8_as_floatX STBIR__CONSTF(STBIR_max_uint8_as_float) + #define STBIR_max_uint16_as_floatX STBIR__CONSTF(STBIR_max_uint16_as_float) + #define stbir__simdfX_float_count 4 + #define stbir__if_simdf8_cast_to_simdf4( val ) ( val ) + #define stbir__simdfX_0123to1230 stbir__simdf_0123to1230 + #define stbir__simdfX_0123to2103 stbir__simdf_0123to2103 +#endif + + +#if defined(STBIR_NEON) && !defined(_M_ARM) + + #if defined( _MSC_VER ) && !defined(__clang__) + typedef __int16 stbir__FP16; + #else + typedef float16_t stbir__FP16; + #endif + +#else // no NEON, or 32-bit ARM for MSVC + + typedef union stbir__FP16 + { + unsigned short u; + } stbir__FP16; + +#endif + +#if !defined(STBIR_NEON) && !defined(STBIR_FP16C) || defined(STBIR_NEON) && defined(_M_ARM) + + // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668 + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + static const stbir__FP32 magic = { (254 - 15) << 23 }; + static const stbir__FP32 was_infnan = { (127 + 16) << 23 }; + stbir__FP32 o; + + o.u = (h.u & 0x7fff) << 13; // exponent/mantissa bits + o.f *= magic.f; // exponent adjust + if (o.f >= was_infnan.f) // make sure Inf/NaN survive + o.u |= 255 << 23; + o.u |= (h.u & 0x8000) << 16; // sign bit + return o.f; + } + + static stbir__inline stbir__FP16 stbir__float_to_half(float val) + { + stbir__FP32 f32infty = { 255 << 23 }; + stbir__FP32 f16max = { (127 + 16) << 23 }; + stbir__FP32 denorm_magic = { ((127 - 15) + (23 - 10) + 1) << 23 }; + unsigned int sign_mask = 0x80000000u; + stbir__FP16 o = { 0 }; + stbir__FP32 f; + unsigned int sign; + + f.f = val; + sign = f.u & sign_mask; + f.u ^= sign; + + if (f.u >= f16max.u) // result is Inf or NaN (all exponent bits set) + o.u = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + else // (De)normalized number or zero + { + if (f.u < (113 << 23)) // resulting FP16 is subnormal or zero + { + // use a magic value to align our 10 mantissa bits at the bottom of + // the float. as long as FP addition is round-to-nearest-even this + // just works. + f.f += denorm_magic.f; + // and one integer subtract of the bias later, we have our final float! + o.u = (unsigned short) ( f.u - denorm_magic.u ); + } + else + { + unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd + // update exponent, rounding bias part 1 + f.u = f.u + ((15u - 127) << 23) + 0xfff; + // rounding bias part 2 + f.u += mant_odd; + // take the bits! + o.u = (unsigned short) ( f.u >> 13 ); + } + } + + o.u |= sign >> 16; + return o; + } + +#endif + + +#if defined(STBIR_FP16C) + + #include + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + _mm256_storeu_ps( (float*)output, _mm256_cvtph_ps( _mm_loadu_si128( (__m128i const* )input ) ) ); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + _mm_storeu_si128( (__m128i*)output, _mm256_cvtps_ph( _mm256_loadu_ps( input ), 0 ) ); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return _mm_cvtss_f32( _mm_cvtph_ps( _mm_cvtsi32_si128( (int)h.u ) ) ); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + stbir__FP16 h; + h.u = (unsigned short) _mm_cvtsi128_si32( _mm_cvtps_ph( _mm_set_ss( f ), 0 ) ); + return h; + } + +#elif defined(STBIR_SSE2) + + // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668 + stbir__inline static void stbir__half_to_float_SIMD(float * output, void const * input) + { + static const STBIR__SIMDI_CONST(mask_nosign, 0x7fff); + static const STBIR__SIMDI_CONST(smallest_normal, 0x0400); + static const STBIR__SIMDI_CONST(infinity, 0x7c00); + static const STBIR__SIMDI_CONST(expadjust_normal, (127 - 15) << 23); + static const STBIR__SIMDI_CONST(magic_denorm, 113 << 23); + + __m128i i = _mm_loadu_si128 ( (__m128i const*)(input) ); + __m128i h = _mm_unpacklo_epi16 ( i, _mm_setzero_si128() ); + __m128i mnosign = STBIR__CONSTI(mask_nosign); + __m128i eadjust = STBIR__CONSTI(expadjust_normal); + __m128i smallest = STBIR__CONSTI(smallest_normal); + __m128i infty = STBIR__CONSTI(infinity); + __m128i expmant = _mm_and_si128(mnosign, h); + __m128i justsign = _mm_xor_si128(h, expmant); + __m128i b_notinfnan = _mm_cmpgt_epi32(infty, expmant); + __m128i b_isdenorm = _mm_cmpgt_epi32(smallest, expmant); + __m128i shifted = _mm_slli_epi32(expmant, 13); + __m128i adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust); + __m128i adjusted = _mm_add_epi32(eadjust, shifted); + __m128i den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm)); + __m128i adjusted2 = _mm_add_epi32(adjusted, adj_infnan); + __m128 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm); + __m128 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm)); + __m128 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2)); + __m128 adjusted5 = _mm_or_ps(adjusted3, adjusted4); + __m128i sign = _mm_slli_epi32(justsign, 16); + __m128 final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign)); + stbir__simdf_store( output + 0, final ); + + h = _mm_unpackhi_epi16 ( i, _mm_setzero_si128() ); + expmant = _mm_and_si128(mnosign, h); + justsign = _mm_xor_si128(h, expmant); + b_notinfnan = _mm_cmpgt_epi32(infty, expmant); + b_isdenorm = _mm_cmpgt_epi32(smallest, expmant); + shifted = _mm_slli_epi32(expmant, 13); + adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust); + adjusted = _mm_add_epi32(eadjust, shifted); + den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm)); + adjusted2 = _mm_add_epi32(adjusted, adj_infnan); + den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm); + adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm)); + adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2)); + adjusted5 = _mm_or_ps(adjusted3, adjusted4); + sign = _mm_slli_epi32(justsign, 16); + final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign)); + stbir__simdf_store( output + 4, final ); + + // ~38 SSE2 ops for 8 values + } + + // Fabian's round-to-nearest-even float to half + // ~48 SSE2 ops for 8 output + stbir__inline static void stbir__float_to_half_SIMD(void * output, float const * input) + { + static const STBIR__SIMDI_CONST(mask_sign, 0x80000000u); + static const STBIR__SIMDI_CONST(c_f16max, (127 + 16) << 23); // all FP32 values >=this round to +inf + static const STBIR__SIMDI_CONST(c_nanbit, 0x200); + static const STBIR__SIMDI_CONST(c_infty_as_fp16, 0x7c00); + static const STBIR__SIMDI_CONST(c_min_normal, (127 - 14) << 23); // smallest FP32 that yields a normalized FP16 + static const STBIR__SIMDI_CONST(c_subnorm_magic, ((127 - 15) + (23 - 10) + 1) << 23); + static const STBIR__SIMDI_CONST(c_normal_bias, 0xfff - ((127 - 15) << 23)); // adjust exponent and add mantissa rounding + + __m128 f = _mm_loadu_ps(input); + __m128 msign = _mm_castsi128_ps(STBIR__CONSTI(mask_sign)); + __m128 justsign = _mm_and_ps(msign, f); + __m128 absf = _mm_xor_ps(f, justsign); + __m128i absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit) + __m128i f16max = STBIR__CONSTI(c_f16max); + __m128 b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN? + __m128i b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special? + __m128i nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), STBIR__CONSTI(c_nanbit)); + __m128i inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials + + __m128i min_normal = STBIR__CONSTI(c_min_normal); + __m128i b_issub = _mm_cmpgt_epi32(min_normal, absf_int); + + // "result is subnormal" path + __m128 subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa + __m128i subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias + + // "result is normal" path + __m128i mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign + __m128i mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0 + + __m128i round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias)); + __m128i round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE) + __m128i normal = _mm_srli_epi32(round2, 13); // rounded result + + // combine the two non-specials + __m128i nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal)); + + // merge in specials as well + __m128i joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan)); + + __m128i sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16); + __m128i final2, final= _mm_or_si128(joined, sign_shift); + + f = _mm_loadu_ps(input+4); + justsign = _mm_and_ps(msign, f); + absf = _mm_xor_ps(f, justsign); + absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit) + b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN? + b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special? + nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), c_nanbit); + inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials + + b_issub = _mm_cmpgt_epi32(min_normal, absf_int); + + // "result is subnormal" path + subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa + subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias + + // "result is normal" path + mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign + mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0 + + round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias)); + round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE) + normal = _mm_srli_epi32(round2, 13); // rounded result + + // combine the two non-specials + nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal)); + + // merge in specials as well + joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan)); + + sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16); + final2 = _mm_or_si128(joined, sign_shift); + final = _mm_packs_epi32(final, final2); + stbir__simdi_store( output,final ); + } + +#elif defined(STBIR_WASM) || (defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM)) // WASM or 32-bit ARM on MSVC/clang + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + for (int i=0; i<8; i++) + { + output[i] = stbir__half_to_float(input[i]); + } + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + for (int i=0; i<8; i++) + { + output[i] = stbir__float_to_half(input[i]); + } + } + +#elif defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) // 64-bit ARM on MSVC (not clang) + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + float16x4_t in0 = vld1_f16(input + 0); + float16x4_t in1 = vld1_f16(input + 4); + vst1q_f32(output + 0, vcvt_f32_f16(in0)); + vst1q_f32(output + 4, vcvt_f32_f16(in1)); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0)); + float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4)); + vst1_f16(output+0, out0); + vst1_f16(output+4, out1); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return vgetq_lane_f32(vcvt_f32_f16(vld1_dup_f16(&h)), 0); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0).n16_u16[0]; + } + +#elif defined(STBIR_NEON) // 64-bit ARM + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + float16x8_t in = vld1q_f16(input); + vst1q_f32(output + 0, vcvt_f32_f16(vget_low_f16(in))); + vst1q_f32(output + 4, vcvt_f32_f16(vget_high_f16(in))); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0)); + float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4)); + vst1q_f16(output, vcombine_f16(out0, out1)); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return vgetq_lane_f32(vcvt_f32_f16(vdup_n_f16(h)), 0); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0); + } + +#endif + + +#ifdef STBIR_SIMD + +#define stbir__simdf_0123to3333( out, reg ) (out) = stbir__simdf_swiz( reg, 3,3,3,3 ) +#define stbir__simdf_0123to2222( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,2,2 ) +#define stbir__simdf_0123to1111( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,1,1 ) +#define stbir__simdf_0123to0000( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,0 ) +#define stbir__simdf_0123to0003( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,3 ) +#define stbir__simdf_0123to0001( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,1 ) +#define stbir__simdf_0123to1122( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,2,2 ) +#define stbir__simdf_0123to2333( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,3,3 ) +#define stbir__simdf_0123to0023( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,3 ) +#define stbir__simdf_0123to1230( out, reg ) (out) = stbir__simdf_swiz( reg, 1,2,3,0 ) +#define stbir__simdf_0123to2103( out, reg ) (out) = stbir__simdf_swiz( reg, 2,1,0,3 ) +#define stbir__simdf_0123to3210( out, reg ) (out) = stbir__simdf_swiz( reg, 3,2,1,0 ) +#define stbir__simdf_0123to2301( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,0,1 ) +#define stbir__simdf_0123to3012( out, reg ) (out) = stbir__simdf_swiz( reg, 3,0,1,2 ) +#define stbir__simdf_0123to0011( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,1,1 ) +#define stbir__simdf_0123to1100( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,0,0 ) +#define stbir__simdf_0123to2233( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,3,3 ) +#define stbir__simdf_0123to1133( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,3,3 ) +#define stbir__simdf_0123to0022( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,2 ) +#define stbir__simdf_0123to1032( out, reg ) (out) = stbir__simdf_swiz( reg, 1,0,3,2 ) + +typedef union stbir__simdi_u32 +{ + stbir_uint32 m128i_u32[4]; + int m128i_i32[4]; + stbir__simdi m128i_i128; +} stbir__simdi_u32; + +static const int STBIR_mask[9] = { 0,0,0,-1,-1,-1,0,0,0 }; + +static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float, stbir__max_uint8_as_float); +static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float, stbir__max_uint16_as_float); +static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted); +static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted); + +static const STBIR__SIMDF_CONST(STBIR_simd_point5, 0.5f); +static const STBIR__SIMDF_CONST(STBIR_ones, 1.0f); +static const STBIR__SIMDI_CONST(STBIR_almost_zero, (127 - 13) << 23); +static const STBIR__SIMDI_CONST(STBIR_almost_one, 0x3f7fffff); +static const STBIR__SIMDI_CONST(STBIR_mastissa_mask, 0xff); +static const STBIR__SIMDI_CONST(STBIR_topscale, 0x02000000); + +// Basically, in simd mode, we unroll the proper amount, and we don't want +// the non-simd remnant loops to be unroll because they only run a few times +// Adding this switch saves about 5K on clang which is Captain Unroll the 3rd. +#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star ) +#define STBIR_SIMD_NO_UNROLL(ptr) STBIR_NO_UNROLL(ptr) + +#ifdef STBIR_MEMCPY +#undef STBIR_MEMCPY +#define STBIR_MEMCPY stbir_simd_memcpy +#endif + +// override normal use of memcpy with much simpler copy (faster and smaller with our sized copies) +static void stbir_simd_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) d = (char*) dest; + char STBIR_SIMD_STREAMOUT_PTR( * ) d_end = ((char*) dest) + bytes; + ptrdiff_t ofs_to_src = (char*)src - (char*)dest; + + // check overlaps + STBIR_ASSERT( ( ( d >= ( (char*)src) + bytes ) ) || ( ( d + bytes ) <= (char*)src ) ); + + if ( bytes < (16*stbir__simdfX_float_count) ) + { + if ( bytes < 16 ) + { + if ( bytes ) + { + do + { + STBIR_SIMD_NO_UNROLL(d); + d[ 0 ] = d[ ofs_to_src ]; + ++d; + } while ( d < d_end ); + } + } + else + { + stbir__simdf x; + // do one unaligned to get us aligned for the stream out below + stbir__simdf_load( x, ( d + ofs_to_src ) ); + stbir__simdf_store( d, x ); + d = (char*)( ( ( (ptrdiff_t)d ) + 16 ) & ~15 ); + + for(;;) + { + STBIR_SIMD_NO_UNROLL(d); + + if ( d > ( d_end - 16 ) ) + { + if ( d == d_end ) + return; + d = d_end - 16; + } + + stbir__simdf_load( x, ( d + ofs_to_src ) ); + stbir__simdf_store( d, x ); + d += 16; + } + } + } + else + { + stbir__simdfX x0,x1,x2,x3; + + // do one unaligned to get us aligned for the stream out below + stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count ); + stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count ); + stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count ); + stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count ); + stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 ); + stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 ); + stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 ); + stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 ); + d = (char*)( ( ( (ptrdiff_t)d ) + (16*stbir__simdfX_float_count) ) & ~((16*stbir__simdfX_float_count)-1) ); + + for(;;) + { + STBIR_SIMD_NO_UNROLL(d); + + if ( d > ( d_end - (16*stbir__simdfX_float_count) ) ) + { + if ( d == d_end ) + return; + d = d_end - (16*stbir__simdfX_float_count); + } + + stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count ); + stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count ); + stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count ); + stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count ); + stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 ); + stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 ); + stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 ); + stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 ); + d += (16*stbir__simdfX_float_count); + } + } +} + +// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be +// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to +// the diff between dest and src) +static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src; + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; + ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; + + if ( ofs_to_dest >= 16 ) // is the overlap more than 16 away? + { + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end16 = ((char*) src) + (bytes&~15); + do + { + stbir__simdf x; + STBIR_SIMD_NO_UNROLL(sd); + stbir__simdf_load( x, sd ); + stbir__simdf_store( ( sd + ofs_to_dest ), x ); + sd += 16; + } while ( sd < s_end16 ); + + if ( sd == s_end ) + return; + } + + do + { + STBIR_SIMD_NO_UNROLL(sd); + *(int*)( sd + ofs_to_dest ) = *(int*) sd; + sd += 4; + } while ( sd < s_end ); +} + +#else // no SSE2 + +// when in scalar mode, we let unrolling happen, so this macro just does the __restrict +#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star ) +#define STBIR_SIMD_NO_UNROLL(ptr) + +#endif // SSE2 + + +#ifdef STBIR_PROFILE + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(__SSE2__) || defined(STBIR_SSE) || defined( _M_IX86_FP ) || defined(__i386) || defined( __i386__ ) || defined( _M_IX86 ) || defined( _X86_ ) + +#ifdef _MSC_VER + + STBIRDEF stbir_uint64 __rdtsc(); + #define STBIR_PROFILE_FUNC() __rdtsc() + +#else // non msvc + + static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC() + { + stbir_uint32 lo, hi; + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi) ); + return ( ( (stbir_uint64) hi ) << 32 ) | ( (stbir_uint64) lo ); + } + +#endif // msvc + +#elif defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(__ARM_NEON__) + +#if defined( _MSC_VER ) && !defined(__clang__) + + #define STBIR_PROFILE_FUNC() _ReadStatusReg(ARM64_CNTVCT) + +#else + + static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC() + { + stbir_uint64 tsc; + asm volatile("mrs %0, cntvct_el0" : "=r" (tsc)); + return tsc; + } + +#endif + +#else // x64, arm + +#error Unknown platform for profiling. + +#endif //x64 and + + +#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO ,stbir__per_split_info * split_info +#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO ,split_info + +#define STBIR_ONLY_PROFILE_BUILD_GET_INFO ,stbir__info * profile_info +#define STBIR_ONLY_PROFILE_BUILD_SET_INFO ,profile_info + +// super light-weight micro profiler +#define STBIR_PROFILE_START_ll( info, wh ) { stbir_uint64 wh##thiszonetime = STBIR_PROFILE_FUNC(); stbir_uint64 * wh##save_parent_excluded_ptr = info->current_zone_excluded_ptr; stbir_uint64 wh##current_zone_excluded = 0; info->current_zone_excluded_ptr = &wh##current_zone_excluded; +#define STBIR_PROFILE_END_ll( info, wh ) wh##thiszonetime = STBIR_PROFILE_FUNC() - wh##thiszonetime; info->profile.named.wh += wh##thiszonetime - wh##current_zone_excluded; *wh##save_parent_excluded_ptr += wh##thiszonetime; info->current_zone_excluded_ptr = wh##save_parent_excluded_ptr; } +#define STBIR_PROFILE_FIRST_START_ll( info, wh ) { int i; info->current_zone_excluded_ptr = &info->profile.named.total; for(i=0;iprofile.array);i++) info->profile.array[i]=0; } STBIR_PROFILE_START_ll( info, wh ); +#define STBIR_PROFILE_CLEAR_EXTRAS_ll( info, num ) { int extra; for(extra=1;extra<(num);extra++) { int i; for(i=0;iprofile.array);i++) (info)[extra].profile.array[i]=0; } } + +// for thread data +#define STBIR_PROFILE_START( wh ) STBIR_PROFILE_START_ll( split_info, wh ) +#define STBIR_PROFILE_END( wh ) STBIR_PROFILE_END_ll( split_info, wh ) +#define STBIR_PROFILE_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( split_info, wh ) +#define STBIR_PROFILE_CLEAR_EXTRAS() STBIR_PROFILE_CLEAR_EXTRAS_ll( split_info, split_count ) + +// for build data +#define STBIR_PROFILE_BUILD_START( wh ) STBIR_PROFILE_START_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_END( wh ) STBIR_PROFILE_END_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_CLEAR( info ) { int i; for(i=0;iprofile.array);i++) info->profile.array[i]=0; } + +#else // no profile + +#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO +#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO + +#define STBIR_ONLY_PROFILE_BUILD_GET_INFO +#define STBIR_ONLY_PROFILE_BUILD_SET_INFO + +#define STBIR_PROFILE_START( wh ) +#define STBIR_PROFILE_END( wh ) +#define STBIR_PROFILE_FIRST_START( wh ) +#define STBIR_PROFILE_CLEAR_EXTRAS( ) + +#define STBIR_PROFILE_BUILD_START( wh ) +#define STBIR_PROFILE_BUILD_END( wh ) +#define STBIR_PROFILE_BUILD_FIRST_START( wh ) +#define STBIR_PROFILE_BUILD_CLEAR( info ) + +#endif // stbir_profile + +#ifndef STBIR_CEILF +#include +#if _MSC_VER <= 1200 // support VC6 for Sean +#define STBIR_CEILF(x) ((float)ceil((float)(x))) +#define STBIR_FLOORF(x) ((float)floor((float)(x))) +#else +#define STBIR_CEILF(x) ceilf(x) +#define STBIR_FLOORF(x) floorf(x) +#endif +#endif + +#ifndef STBIR_MEMCPY +// For memcpy +#include +#define STBIR_MEMCPY( dest, src, len ) memcpy( dest, src, len ) +#endif + +#ifndef STBIR_SIMD + +// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be +// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to +// the diff between dest and src) +static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src; + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; + ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; + + if ( ofs_to_dest >= 8 ) // is the overlap more than 8 away? + { + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end8 = ((char*) src) + (bytes&~7); + do + { + STBIR_NO_UNROLL(sd); + *(stbir_uint64*)( sd + ofs_to_dest ) = *(stbir_uint64*) sd; + sd += 8; + } while ( sd < s_end8 ); + + if ( sd == s_end ) + return; + } + + do + { + STBIR_NO_UNROLL(sd); + *(int*)( sd + ofs_to_dest ) = *(int*) sd; + sd += 4; + } while ( sd < s_end ); +} + +#endif + +static float stbir__filter_trapezoid(float x, float scale, void * user_data) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x >= t) + return 0.0f; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1.0f; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale, void * user_data) +{ + STBIR__UNUSED(user_data); + return 0.5f + scale / 2.0f; +} + +static float stbir__filter_triangle(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x <= 1.0f) + return 1.0f - x; + else + return 0.0f; +} + +static float stbir__filter_point(float x, float s, void * user_data) +{ + STBIR__UNUSED(x); + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + return 1.0f; +} + +static float stbir__filter_cubic(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return (4.0f + x*x*(3.0f*x - 6.0f))/6.0f; + else if (x < 2.0f) + return (8.0f + x*(-12.0f + x*(6.0f - x)))/6.0f; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return 1.0f - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2.0f - x*(4.0f + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return (16.0f + x*x*(21.0f * x - 36.0f))/18.0f; + else if (x < 2.0f) + return (32.0f + x*(-60.0f + x*(36.0f - 7.0f*x)))/18.0f; + + return (0.0f); +} + +static float stbir__support_zero(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 0; +} + +static float stbir__support_zeropoint5(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 0.5f; +} + +static float stbir__support_one(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 1; +} + +static float stbir__support_two(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 2; +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter from the output pixel's perspective +static int stbir__get_filter_pixel_width(stbir__support_callback * support, float scale, void * user_data) +{ + STBIR_ASSERT(support != 0); + + if ( scale >= ( 1.0f-stbir__small_float ) ) // upscale + return (int)STBIR_CEILF(support(1.0f/scale,user_data) * 2.0f); + else + return (int)STBIR_CEILF(support(scale,user_data) * 2.0f / scale); +} + +// this is how many coefficents per run of the filter (which is different +// from the filter_pixel_width depending on if we are scattering or gathering) +static int stbir__get_coefficient_width(stbir__sampler * samp, int is_gather, void * user_data) +{ + float scale = samp->scale_info.scale; + stbir__support_callback * support = samp->filter_support; + + switch( is_gather ) + { + case 1: + return (int)STBIR_CEILF(support(1.0f / scale, user_data) * 2.0f); + case 2: + return (int)STBIR_CEILF(support(scale, user_data) * 2.0f / scale); + case 0: + return (int)STBIR_CEILF(support(scale, user_data) * 2.0f); + default: + STBIR_ASSERT( (is_gather >= 0 ) && (is_gather <= 2 ) ); + return 0; + } +} + +static int stbir__get_contributors(stbir__sampler * samp, int is_gather) +{ + if (is_gather) + return samp->scale_info.output_sub_size; + else + return (samp->scale_info.input_full_size + samp->filter_pixel_margin * 2); +} + +static int stbir__edge_zero_full( int n, int max ) +{ + STBIR__UNUSED(n); + STBIR__UNUSED(max); + return 0; // NOTREACHED +} + +static int stbir__edge_clamp_full( int n, int max ) +{ + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED +} + +static int stbir__edge_reflect_full( int n, int max ) +{ + if (n < 0) + { + if (n > -max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED +} + +static int stbir__edge_wrap_full( int n, int max ) +{ + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } +} + +typedef int stbir__edge_wrap_func( int n, int max ); +static stbir__edge_wrap_func * stbir__edge_wrap_slow[] = +{ + stbir__edge_clamp_full, // STBIR_EDGE_CLAMP + stbir__edge_reflect_full, // STBIR_EDGE_REFLECT + stbir__edge_wrap_full, // STBIR_EDGE_WRAP + stbir__edge_zero_full, // STBIR_EDGE_ZERO +}; + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow[edge]( n, max ); +} + +#define STBIR__MERGE_RUNS_PIXEL_THRESHOLD 16 + +// get information on the extents of a sampler +static void stbir__get_extents( stbir__sampler * samp, stbir__extents * scanline_extents ) +{ + int j, stop; + int left_margin, right_margin; + int min_n = 0x7fffffff, max_n = -0x7fffffff; + int min_left = 0x7fffffff, max_left = -0x7fffffff; + int min_right = 0x7fffffff, max_right = -0x7fffffff; + stbir_edge edge = samp->edge; + stbir__contributors* contributors = samp->contributors; + int output_sub_size = samp->scale_info.output_sub_size; + int input_full_size = samp->scale_info.input_full_size; + int filter_pixel_margin = samp->filter_pixel_margin; + + STBIR_ASSERT( samp->is_gather ); + + stop = output_sub_size; + for (j = 0; j < stop; j++ ) + { + STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 ); + if ( contributors[j].n0 < min_n ) + { + min_n = contributors[j].n0; + stop = j + filter_pixel_margin; // if we find a new min, only scan another filter width + if ( stop > output_sub_size ) stop = output_sub_size; + } + } + + stop = 0; + for (j = output_sub_size - 1; j >= stop; j-- ) + { + STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 ); + if ( contributors[j].n1 > max_n ) + { + max_n = contributors[j].n1; + stop = j - filter_pixel_margin; // if we find a new max, only scan another filter width + if (stop<0) stop = 0; + } + } + + STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n ); + STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n ); + + // now calculate how much into the margins we really read + left_margin = 0; + if ( min_n < 0 ) + { + left_margin = -min_n; + min_n = 0; + } + + right_margin = 0; + if ( max_n >= input_full_size ) + { + right_margin = max_n - input_full_size + 1; + max_n = input_full_size - 1; + } + + // index 1 is margin pixel extents (how many pixels we hang over the edge) + scanline_extents->edge_sizes[0] = left_margin; + scanline_extents->edge_sizes[1] = right_margin; + + // index 2 is pixels read from the input + scanline_extents->spans[0].n0 = min_n; + scanline_extents->spans[0].n1 = max_n; + scanline_extents->spans[0].pixel_offset_for_input = min_n; + + // default to no other input range + scanline_extents->spans[1].n0 = 0; + scanline_extents->spans[1].n1 = -1; + scanline_extents->spans[1].pixel_offset_for_input = 0; + + // don't have to do edge calc for zero clamp + if ( edge == STBIR_EDGE_ZERO ) + return; + + // convert margin pixels to the pixels within the input (min and max) + for( j = -left_margin ; j < 0 ; j++ ) + { + int p = stbir__edge_wrap( edge, j, input_full_size ); + if ( p < min_left ) + min_left = p; + if ( p > max_left ) + max_left = p; + } + + for( j = input_full_size ; j < (input_full_size + right_margin) ; j++ ) + { + int p = stbir__edge_wrap( edge, j, input_full_size ); + if ( p < min_right ) + min_right = p; + if ( p > max_right ) + max_right = p; + } + + // merge the left margin pixel region if it connects within 4 pixels of main pixel region + if ( min_left != 0x7fffffff ) + { + if ( ( ( min_left <= min_n ) && ( ( max_left + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) || + ( ( min_n <= min_left ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_left ) ) ) + { + scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_left ); + scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_left ); + scanline_extents->spans[0].pixel_offset_for_input = min_n; + left_margin = 0; + } + } + + // merge the right margin pixel region if it connects within 4 pixels of main pixel region + if ( min_right != 0x7fffffff ) + { + if ( ( ( min_right <= min_n ) && ( ( max_right + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) || + ( ( min_n <= min_right ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_right ) ) ) + { + scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_right ); + scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_right ); + scanline_extents->spans[0].pixel_offset_for_input = min_n; + right_margin = 0; + } + } + + STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n ); + STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n ); + + // you get two ranges when you have the WRAP edge mode and you are doing just the a piece of the resize + // so you need to get a second run of pixels from the opposite side of the scanline (which you + // wouldn't need except for WRAP) + + + // if we can't merge the min_left range, add it as a second range + if ( ( left_margin ) && ( min_left != 0x7fffffff ) ) + { + stbir__span * newspan = scanline_extents->spans + 1; + STBIR_ASSERT( right_margin == 0 ); + if ( min_left < scanline_extents->spans[0].n0 ) + { + scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1; + --newspan; + } + newspan->pixel_offset_for_input = min_left; + newspan->n0 = -left_margin; + newspan->n1 = ( max_left - min_left ) - left_margin; + scanline_extents->edge_sizes[0] = 0; // don't need to copy the left margin, since we are directly decoding into the margin + return; + } + + // if we can't merge the min_left range, add it as a second range + if ( ( right_margin ) && ( min_right != 0x7fffffff ) ) + { + stbir__span * newspan = scanline_extents->spans + 1; + if ( min_right < scanline_extents->spans[0].n0 ) + { + scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1; + --newspan; + } + newspan->pixel_offset_for_input = min_right; + newspan->n0 = scanline_extents->spans[1].n1 + 1; + newspan->n1 = scanline_extents->spans[1].n1 + 1 + ( max_right - min_right ); + scanline_extents->edge_sizes[1] = 0; // don't need to copy the right margin, since we are directly decoding into the margin + return; + } +} + +static void stbir__calculate_in_pixel_range( int * first_pixel, int * last_pixel, float out_pixel_center, float out_filter_radius, float inv_scale, float out_shift, int input_size, stbir_edge edge ) +{ + int first, last; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) * inv_scale; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) * inv_scale; + + first = (int)(STBIR_FLOORF(in_pixel_influence_lowerbound + 0.5f)); + last = (int)(STBIR_FLOORF(in_pixel_influence_upperbound - 0.5f)); + + if ( edge == STBIR_EDGE_WRAP ) + { + if ( first <= -input_size ) + first = -(input_size-1); + if ( last >= (input_size*2)) + last = (input_size*2) - 1; + } + + *first_pixel = first; + *last_pixel = last; +} + +static void stbir__calculate_coefficients_for_gather_upsample( float out_filter_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float* coefficient_group, int coefficient_width, stbir_edge edge, void * user_data ) +{ + int n, end; + float inv_scale = scale_info->inv_scale; + float out_shift = scale_info->pixel_shift; + int input_size = scale_info->input_full_size; + int numerator = scale_info->scale_numerator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) ); + + // Looping through out pixels + end = num_contributors; if ( polyphase ) end = numerator; + for (n = 0; n < end; n++) + { + int i; + int last_non_zero; + float out_pixel_center = (float)n + 0.5f; + float in_center_of_out = (out_pixel_center + out_shift) * inv_scale; + + int in_first_pixel, in_last_pixel; + + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, out_pixel_center, out_filter_radius, inv_scale, out_shift, input_size, edge ); + + last_non_zero = -1; + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + float coeff = kernel(in_center_of_out - in_pixel_center, inv_scale, user_data); + + // kill denormals + if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) ) + { + if ( i == 0 ) // if we're at the front, just eat zero contributors + { + STBIR_ASSERT ( ( in_last_pixel - in_first_pixel ) != 0 ); // there should be at least one contrib + ++in_first_pixel; + i--; + continue; + } + coeff = 0; // make sure is fully zero (should keep denormals away) + } + else + last_non_zero = i; + + coefficient_group[i] = coeff; + } + + in_last_pixel = last_non_zero+in_first_pixel; // kills trailing zeros + contributors->n0 = in_first_pixel; + contributors->n1 = in_last_pixel; + + STBIR_ASSERT(contributors->n1 >= contributors->n0); + + ++contributors; + coefficient_group += coefficient_width; + } +} + +static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff ) +{ + 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; + } + else + { + coeffs[ new_pixel - contribs->n0 ] += new_coeff; + } + } + 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; + + coeffs[ e ] = new_coeff; + contribs->n1 = new_pixel; + } +} + +static void stbir__calculate_out_pixel_range( int * first_pixel, int * last_pixel, float in_pixel_center, float in_pixels_radius, float scale, float out_shift, int out_size ) +{ + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale - out_shift; + int out_first_pixel = (int)(STBIR_FLOORF(out_pixel_influence_lowerbound + 0.5f)); + int out_last_pixel = (int)(STBIR_FLOORF(out_pixel_influence_upperbound - 0.5f)); + + if ( out_first_pixel < 0 ) + out_first_pixel = 0; + if ( out_last_pixel >= out_size ) + out_last_pixel = out_size - 1; + *first_pixel = out_first_pixel; + *last_pixel = out_last_pixel; +} + +static void stbir__calculate_coefficients_for_gather_downsample( int start, int end, float in_pixels_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int coefficient_width, int num_contributors, stbir__contributors * contributors, float * coefficient_group, void * user_data ) +{ + int in_pixel; + int i; + int first_out_inited = -1; + float scale = scale_info->scale; + float out_shift = scale_info->pixel_shift; + int out_size = scale_info->output_sub_size; + int numerator = scale_info->scale_numerator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < out_size ) ); + + STBIR__UNUSED(num_contributors); + + // Loop through the input pixels + for (in_pixel = start; in_pixel < end; in_pixel++) + { + float in_pixel_center = (float)in_pixel + 0.5f; + float out_center_of_in = in_pixel_center * scale - out_shift; + int out_first_pixel, out_last_pixel; + + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, in_pixel_center, in_pixels_radius, scale, out_shift, out_size ); + + if ( out_first_pixel > out_last_pixel ) + continue; + + // clamp or exit if we are using polyphase filtering, and the limit is up + if ( polyphase ) + { + // when polyphase, you only have to do coeffs up to the numerator count + if ( out_first_pixel == numerator ) + break; + + // don't do any extra work, clamp last pixel at numerator too + if ( out_last_pixel >= numerator ) + out_last_pixel = numerator - 1; + } + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + float coeff = kernel(x, scale, user_data) * scale; + + // kill the coeff if it's too small (avoid denormals) + if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) ) + coeff = 0.0f; + + { + int out = i + out_first_pixel; + float * coeffs = coefficient_group + out * coefficient_width; + stbir__contributors * contribs = contributors + out; + + // is this the first time this output pixel has been seen? Init it. + if ( out > first_out_inited ) + { + STBIR_ASSERT( out == ( first_out_inited + 1 ) ); // ensure we have only advanced one at time + first_out_inited = out; + contribs->n0 = in_pixel; + contribs->n1 = in_pixel; + coeffs[0] = coeff; + } + else + { + // insert on end (always in order) + if ( coeffs[0] == 0.0f ) // if the first coefficent is zero, then zap it for this coeffs + { + STBIR_ASSERT( ( in_pixel - contribs->n0 ) == 1 ); // ensure that when we zap, we're at the 2nd pos + contribs->n0 = in_pixel; + } + contribs->n1 = in_pixel; + STBIR_ASSERT( ( in_pixel - contribs->n0 ) < coefficient_width ); + coeffs[in_pixel - contribs->n0] = coeff; + } + } + } + } +} + +static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter_extent_info* filter_info, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float * coefficient_group, int coefficient_width ) +{ + int input_size = scale_info->input_full_size; + int input_last_n1 = input_size - 1; + int n, end; + int lowest = 0x7fffffff; + int highest = -0x7fffffff; + int widest = -1; + int numerator = scale_info->scale_numerator; + int denominator = scale_info->scale_denominator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) ); + float * coeffs; + stbir__contributors * contribs; + + // weight all the coeffs for each sample + coeffs = coefficient_group; + contribs = contributors; + end = num_contributors; if ( polyphase ) end = numerator; + for (n = 0; n < end; n++) + { + int i; + float filter_scale, total_filter = 0; + int e; + + // add all contribs + e = contribs->n1 - contribs->n0; + for( i = 0 ; i <= e ; i++ ) + { + total_filter += coeffs[i]; + STBIR_ASSERT( ( coeffs[i] >= -2.0f ) && ( coeffs[i] <= 2.0f ) ); // check for wonky weights + } + + // rescale + if ( ( total_filter < stbir__small_float ) && ( total_filter > -stbir__small_float ) ) + { + // all coeffs are extremely small, just zero it + contribs->n1 = contribs->n0; + coeffs[0] = 0.0f; + } + else + { + // if the total isn't 1.0, rescale everything + if ( ( total_filter < (1.0f-stbir__small_float) ) || ( total_filter > (1.0f+stbir__small_float) ) ) + { + filter_scale = 1.0f / total_filter; + // scale them all + for (i = 0; i <= e; i++) + coeffs[i] *= filter_scale; + } + } + ++contribs; + coeffs += coefficient_width; + } + + // if we have a rational for the scale, we can exploit the polyphaseness to not calculate + // most of the coefficients, so we copy them here + if ( polyphase ) + { + stbir__contributors * prev_contribs = contributors; + stbir__contributors * cur_contribs = contributors + numerator; + + for( n = numerator ; n < num_contributors ; n++ ) + { + cur_contribs->n0 = prev_contribs->n0 + denominator; + cur_contribs->n1 = prev_contribs->n1 + denominator; + ++cur_contribs; + ++prev_contribs; + } + stbir_overlapping_memcpy( coefficient_group + numerator * coefficient_width, coefficient_group, ( num_contributors - numerator ) * coefficient_width * sizeof( coeffs[ 0 ] ) ); + } + + coeffs = coefficient_group; + contribs = contributors; + for (n = 0; n < num_contributors; n++) + { + int i; + + // in zero edge mode, just remove out of bounds contribs completely (since their weights are accounted for now) + if ( edge == STBIR_EDGE_ZERO ) + { + // shrink the right side if necessary + if ( contribs->n1 > input_last_n1 ) + contribs->n1 = input_last_n1; + + // shrink the left side + if ( contribs->n0 < 0 ) + { + int j, left, skips = 0; + + skips = -contribs->n0; + contribs->n0 = 0; + + // now move down the weights + left = contribs->n1 - contribs->n0 + 1; + if ( left > 0 ) + { + for( j = 0 ; j < left ; j++ ) + coeffs[ j ] = coeffs[ j + skips ]; + } + } + } + else if ( ( edge == STBIR_EDGE_CLAMP ) || ( edge == STBIR_EDGE_REFLECT ) ) + { + // for clamp and reflect, calculate the true inbounds position (based on edge type) and just add that to the existing weight + + // right hand side first + if ( contribs->n1 > input_last_n1 ) + { + int start = contribs->n0; + 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] ); + } + + // now check left hand edge + if ( contribs->n0 < 0 ) + { + int save_n0; + float save_n0_coeff; + float * c = coeffs - ( contribs->n0 + 1 ); + + // 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-- ); + 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)! + + // now slide all the coeffs down (since we have accumulated them in the positive contribs) and reset the first contrib + contribs->n0 = 0; + for(i = 0 ; i <= contribs->n1 ; i++ ) + 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 ); + } + } + + if ( contribs->n0 <= contribs->n1 ) + { + 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 ) + { + if ( contribs->n0 < lowest ) + lowest = contribs->n0; + if ( contribs->n1 > highest ) + highest = contribs->n1; + if ( diff > widest ) + widest = diff; + } + + // re-zero out unused coefficients (if any) + for( i = diff ; i < coefficient_width ; i++ ) + coeffs[i] = 0.0f; + } + + ++contribs; + coeffs += coefficient_width; + } + filter_info->lowest = lowest; + filter_info->highest = highest; + filter_info->widest = widest; +} + +static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficents, int coefficient_width, int widest, int row_width ) +{ + #define STBIR_MOVE_1( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint32*)(dest))[0] = ((stbir_uint32*)(src))[0]; } + #define STBIR_MOVE_2( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; } + #ifdef STBIR_SIMD + #define STBIR_MOVE_4( dest, src ) { stbir__simdf t; STBIR_NO_UNROLL(dest); stbir__simdf_load( t, src ); stbir__simdf_store( dest, t ); } + #else + #define STBIR_MOVE_4( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; ((stbir_uint64*)(dest))[1] = ((stbir_uint64*)(src))[1]; } + #endif + if ( coefficient_width != widest ) + { + float * pc = coefficents; + float * coeffs = coefficents; + float * pc_end = coefficents + num_contributors * widest; + switch( widest ) + { + case 1: + do { + STBIR_MOVE_1( pc, coeffs ); + ++pc; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 2: + do { + STBIR_MOVE_2( pc, coeffs ); + pc += 2; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 3: + do { + STBIR_MOVE_2( pc, coeffs ); + STBIR_MOVE_1( pc+2, coeffs+2 ); + pc += 3; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 4: + do { + STBIR_MOVE_4( pc, coeffs ); + pc += 4; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 5: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_1( pc+4, coeffs+4 ); + pc += 5; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 6: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_2( pc+4, coeffs+4 ); + pc += 6; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 7: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_2( pc+4, coeffs+4 ); + STBIR_MOVE_1( pc+6, coeffs+6 ); + pc += 7; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 8: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + pc += 8; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 9: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_1( pc+8, coeffs+8 ); + pc += 9; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 10: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_2( pc+8, coeffs+8 ); + pc += 10; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 11: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_2( pc+8, coeffs+8 ); + STBIR_MOVE_1( pc+10, coeffs+10 ); + pc += 11; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 12: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_4( pc+8, coeffs+8 ); + pc += 12; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + default: + do { + float * copy_end = pc + widest - 4; + float * c = coeffs; + do { + STBIR_NO_UNROLL( pc ); + STBIR_MOVE_4( pc, c ); + pc += 4; + c += 4; + } while ( pc <= copy_end ); + copy_end += 4; + while ( pc < copy_end ) + { + STBIR_MOVE_1( pc, c ); + ++pc; ++c; + } + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + } + } + + // some horizontal routines read one float off the end (which is then masked off), so put in a sentinal so we don't read an snan or denormal + coefficents[ widest * num_contributors ] = 8888.0f; + + // the minimum we might read for unrolled filters widths is 12. So, we need to + // make sure we never read outside the decode buffer, by possibly moving + // the sample area back into the scanline, and putting zeros weights first. + // we start on the right edge and check until we're well past the possible + // clip area (2*widest). + { + stbir__contributors * contribs = contributors + num_contributors - 1; + float * coeffs = coefficents + widest * ( num_contributors - 1 ); + + // go until no chance of clipping (this is usually less than 8 lops) + while ( ( ( contribs->n0 + widest*2 ) >= row_width ) && ( contribs >= contributors ) ) + { + // might we clip?? + if ( ( contribs->n0 + widest ) > row_width ) + { + int stop_range = widest; + + // if range is larger than 12, it will be handled by generic loops that can terminate on the exact length + // of this contrib n1, instead of a fixed widest amount - so calculate this + if ( widest > 12 ) + { + int mod; + + // how far will be read in the n_coeff loop (which depends on the widest count mod4); + mod = widest & 3; + stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod; + + // the n_coeff loops do a minimum amount of coeffs, so factor that in! + if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod; + } + + // now see if we still clip with the refined range + if ( ( contribs->n0 + stop_range ) > row_width ) + { + int new_n0 = row_width - stop_range; + int num = contribs->n1 - contribs->n0 + 1; + int backup = contribs->n0 - new_n0; + float * from_co = coeffs + num - 1; + float * to_co = from_co + backup; + + STBIR_ASSERT( ( new_n0 >= 0 ) && ( new_n0 < contribs->n0 ) ); + + // move the coeffs over + while( num ) + { + *to_co-- = *from_co--; + --num; + } + // zero new positions + while ( to_co >= coeffs ) + *to_co-- = 0; + // set new start point + contribs->n0 = new_n0; + if ( widest > 12 ) + { + int mod; + + // how far will be read in the n_coeff loop (which depends on the widest count mod4); + mod = widest & 3; + stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod; + + // the n_coeff loops do a minimum amount of coeffs, so factor that in! + if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod; + } + } + } + --contribs; + coeffs -= widest; + } + } + + return widest; + #undef STBIR_MOVE_1 + #undef STBIR_MOVE_2 + #undef STBIR_MOVE_4 +} + +static void stbir__calculate_filters( stbir__sampler * samp, stbir__sampler * other_axis_for_pivot, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO ) +{ + int n; + float scale = samp->scale_info.scale; + stbir__kernel_callback * kernel = samp->filter_kernel; + stbir__support_callback * support = samp->filter_support; + float inv_scale = samp->scale_info.inv_scale; + int input_full_size = samp->scale_info.input_full_size; + int gather_num_contributors = samp->num_contributors; + stbir__contributors* gather_contributors = samp->contributors; + float * gather_coeffs = samp->coefficients; + int gather_coefficient_width = samp->coefficient_width; + + switch ( samp->is_gather ) + { + case 1: // gather upsample + { + float out_pixels_radius = support(inv_scale,user_data) * scale; + + stbir__calculate_coefficients_for_gather_upsample( out_pixels_radius, kernel, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width, samp->edge, user_data ); + + STBIR_PROFILE_BUILD_START( cleanup ); + stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width ); + STBIR_PROFILE_BUILD_END( cleanup ); + } + break; + + case 0: // scatter downsample (only on vertical) + case 2: // gather downsample + { + float in_pixels_radius = support(scale,user_data) * inv_scale; + int filter_pixel_margin = samp->filter_pixel_margin; + int input_end = input_full_size + filter_pixel_margin; + + // if this is a scatter, we do a downsample gather to get the coeffs, and then pivot after + if ( !samp->is_gather ) + { + // check if we are using the same gather downsample on the horizontal as this vertical, + // if so, then we don't have to generate them, we can just pivot from the horizontal. + if ( other_axis_for_pivot ) + { + gather_contributors = other_axis_for_pivot->contributors; + gather_coeffs = other_axis_for_pivot->coefficients; + gather_coefficient_width = other_axis_for_pivot->coefficient_width; + gather_num_contributors = other_axis_for_pivot->num_contributors; + samp->extent_info.lowest = other_axis_for_pivot->extent_info.lowest; + samp->extent_info.highest = other_axis_for_pivot->extent_info.highest; + samp->extent_info.widest = other_axis_for_pivot->extent_info.widest; + goto jump_right_to_pivot; + } + + gather_contributors = samp->gather_prescatter_contributors; + gather_coeffs = samp->gather_prescatter_coefficients; + gather_coefficient_width = samp->gather_prescatter_coefficient_width; + gather_num_contributors = samp->gather_prescatter_num_contributors; + } + + stbir__calculate_coefficients_for_gather_downsample( -filter_pixel_margin, input_end, in_pixels_radius, kernel, &samp->scale_info, gather_coefficient_width, gather_num_contributors, gather_contributors, gather_coeffs, user_data ); + + STBIR_PROFILE_BUILD_START( cleanup ); + stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width ); + STBIR_PROFILE_BUILD_END( cleanup ); + + if ( !samp->is_gather ) + { + // if this is a scatter (vertical only), then we need to pivot the coeffs + stbir__contributors * scatter_contributors; + int highest_set; + + jump_right_to_pivot: + + STBIR_PROFILE_BUILD_START( pivot ); + + highest_set = (-filter_pixel_margin) - 1; + for (n = 0; n < gather_num_contributors; n++) + { + int k; + int gn0 = gather_contributors->n0, gn1 = gather_contributors->n1; + int scatter_coefficient_width = samp->coefficient_width; + float * scatter_coeffs = samp->coefficients + ( gn0 + filter_pixel_margin ) * scatter_coefficient_width; + float * g_coeffs = gather_coeffs; + scatter_contributors = samp->contributors + ( gn0 + filter_pixel_margin ); + + for (k = gn0 ; k <= gn1 ; k++ ) + { + float gc = *g_coeffs++; + if ( ( k > highest_set ) || ( scatter_contributors->n0 > scatter_contributors->n1 ) ) + { + { + // if we are skipping over several contributors, we need to clear the skipped ones + stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1); + while ( clear_contributors < scatter_contributors ) + { + clear_contributors->n0 = 0; + clear_contributors->n1 = -1; + ++clear_contributors; + } + } + scatter_contributors->n0 = n; + scatter_contributors->n1 = n; + scatter_coeffs[0] = gc; + highest_set = k; + } + else + { + stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc ); + } + ++scatter_contributors; + scatter_coeffs += scatter_coefficient_width; + } + + ++gather_contributors; + gather_coeffs += gather_coefficient_width; + } + + // now clear any unset contribs + { + stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1); + stbir__contributors * end_contributors = samp->contributors + samp->num_contributors; + while ( clear_contributors < end_contributors ) + { + clear_contributors->n0 = 0; + clear_contributors->n1 = -1; + ++clear_contributors; + } + } + + STBIR_PROFILE_BUILD_END( pivot ); + } + } + break; + } +} + + +//======================================================================================================== +// scanline decoders and encoders + +#define stbir__coder_min_num 1 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix BGRA +#define stbir__decode_swizzle +#define stbir__decode_order0 2 +#define stbir__decode_order1 1 +#define stbir__decode_order2 0 +#define stbir__decode_order3 3 +#define stbir__encode_order0 2 +#define stbir__encode_order1 1 +#define stbir__encode_order2 0 +#define stbir__encode_order3 3 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix ARGB +#define stbir__decode_swizzle +#define stbir__decode_order0 1 +#define stbir__decode_order1 2 +#define stbir__decode_order2 3 +#define stbir__decode_order3 0 +#define stbir__encode_order0 3 +#define stbir__encode_order1 0 +#define stbir__encode_order2 1 +#define stbir__encode_order3 2 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix ABGR +#define stbir__decode_swizzle +#define stbir__decode_order0 3 +#define stbir__decode_order1 2 +#define stbir__decode_order2 1 +#define stbir__decode_order3 0 +#define stbir__encode_order0 3 +#define stbir__encode_order1 2 +#define stbir__encode_order2 1 +#define stbir__encode_order3 0 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix AR +#define stbir__decode_swizzle +#define stbir__decode_order0 1 +#define stbir__decode_order1 0 +#define stbir__decode_order2 3 +#define stbir__decode_order3 2 +#define stbir__encode_order0 1 +#define stbir__encode_order1 0 +#define stbir__encode_order2 3 +#define stbir__encode_order3 2 +#define stbir__coder_min_num 2 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + + +// fancy alpha means we expand to keep both premultipied and non-premultiplied color channels +static void stbir__fancy_alpha_weight_4ch( float * out_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) out = out_buffer; + float const * end_decode = out_buffer + ( width_times_channels / 4 ) * 7; // decode buffer aligned to end of out_buffer + float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels; + + // fancy alpha is stored internally as R G B A Rpm Gpm Bpm + + #ifdef STBIR_SIMD + + #ifdef STBIR_SIMD8 + decode += 16; + while ( decode <= end_decode ) + { + stbir__simdf8 d0,d1,a0,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf8_load( d0, decode-16 ); + stbir__simdf8_load( d1, decode-16+8 ); + stbir__simdf8_0123to33333333( a0, d0 ); + stbir__simdf8_0123to33333333( a1, d1 ); + stbir__simdf8_mult( p0, a0, d0 ); + stbir__simdf8_mult( p1, a1, d1 ); + stbir__simdf8_bot4s( a0, d0, p0 ); + stbir__simdf8_bot4s( a1, d1, p1 ); + stbir__simdf8_top4s( d0, d0, p0 ); + stbir__simdf8_top4s( d1, d1, p1 ); + stbir__simdf8_store ( out, a0 ); + stbir__simdf8_store ( out+7, d0 ); + stbir__simdf8_store ( out+14, a1 ); + stbir__simdf8_store ( out+21, d1 ); + decode += 16; + out += 28; + } + decode -= 16; + #else + decode += 8; + while ( decode <= end_decode ) + { + stbir__simdf d0,a0,d1,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf_load( d0, decode-8 ); + stbir__simdf_load( d1, decode-8+4 ); + stbir__simdf_0123to3333( a0, d0 ); + stbir__simdf_0123to3333( a1, d1 ); + stbir__simdf_mult( p0, a0, d0 ); + stbir__simdf_mult( p1, a1, d1 ); + stbir__simdf_store ( out, d0 ); + stbir__simdf_store ( out+4, p0 ); + stbir__simdf_store ( out+7, d1 ); + stbir__simdf_store ( out+7+4, p1 ); + decode += 8; + out += 14; + } + decode -= 8; + #endif + + // might be one last odd pixel + #ifdef STBIR_SIMD8 + while ( decode < end_decode ) + #else + if ( decode < end_decode ) + #endif + { + stbir__simdf d,a,p; + stbir__simdf_load( d, decode ); + stbir__simdf_0123to3333( a, d ); + stbir__simdf_mult( p, a, d ); + stbir__simdf_store ( out, d ); + stbir__simdf_store ( out+4, p ); + decode += 4; + out += 7; + } + + #else + + while( decode < end_decode ) + { + float r = decode[0], g = decode[1], b = decode[2], alpha = decode[3]; + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = alpha; + out[4] = r * alpha; + out[5] = g * alpha; + out[6] = b * alpha; + out += 7; + decode += 4; + } + + #endif +} + +static void stbir__fancy_alpha_weight_2ch( float * out_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) out = out_buffer; + float const * end_decode = out_buffer + ( width_times_channels / 2 ) * 3; + float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels; + + // for fancy alpha, turns into: [X A Xpm][X A Xpm],etc + + #ifdef STBIR_SIMD + + decode += 8; + if ( decode <= end_decode ) + { + do { + #ifdef STBIR_SIMD8 + stbir__simdf8 d0,a0,p0; + STBIR_NO_UNROLL(decode); + stbir__simdf8_load( d0, decode-8 ); + stbir__simdf8_0123to11331133( p0, d0 ); + stbir__simdf8_0123to00220022( a0, d0 ); + stbir__simdf8_mult( p0, p0, a0 ); + + stbir__simdf_store2( out, stbir__if_simdf8_cast_to_simdf4( d0 ) ); + stbir__simdf_store( out+2, stbir__if_simdf8_cast_to_simdf4( p0 ) ); + stbir__simdf_store2h( out+3, stbir__if_simdf8_cast_to_simdf4( d0 ) ); + + stbir__simdf_store2( out+6, stbir__simdf8_gettop4( d0 ) ); + stbir__simdf_store( out+8, stbir__simdf8_gettop4( p0 ) ); + stbir__simdf_store2h( out+9, stbir__simdf8_gettop4( d0 ) ); + #else + stbir__simdf d0,a0,d1,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf_load( d0, decode-8 ); + stbir__simdf_load( d1, decode-8+4 ); + stbir__simdf_0123to1133( p0, d0 ); + stbir__simdf_0123to1133( p1, d1 ); + stbir__simdf_0123to0022( a0, d0 ); + stbir__simdf_0123to0022( a1, d1 ); + stbir__simdf_mult( p0, p0, a0 ); + stbir__simdf_mult( p1, p1, a1 ); + + stbir__simdf_store2( out, d0 ); + stbir__simdf_store( out+2, p0 ); + stbir__simdf_store2h( out+3, d0 ); + + stbir__simdf_store2( out+6, d1 ); + stbir__simdf_store( out+8, p1 ); + stbir__simdf_store2h( out+9, d1 ); + #endif + decode += 8; + out += 12; + } while ( decode <= end_decode ); + } + decode -= 8; + #endif + + while( decode < end_decode ) + { + float x = decode[0], y = decode[1]; + STBIR_SIMD_NO_UNROLL(decode); + out[0] = x; + out[1] = y; + out[2] = x * y; + out += 3; + decode += 2; + } +} + +static void stbir__fancy_alpha_unweight_4ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + // fancy RGBA is stored internally as R G B A Rpm Gpm Bpm + + do { + float alpha = input[3]; +#ifdef STBIR_SIMD + stbir__simdf i,ia; + STBIR_SIMD_NO_UNROLL(encode); + if ( alpha < stbir__small_float ) + { + stbir__simdf_load( i, input ); + stbir__simdf_store( encode, i ); + } + else + { + stbir__simdf_load1frep4( ia, 1.0f / alpha ); + stbir__simdf_load( i, input+4 ); + stbir__simdf_mult( i, i, ia ); + stbir__simdf_store( encode, i ); + encode[3] = alpha; + } +#else + if ( alpha < stbir__small_float ) + { + encode[0] = input[0]; + encode[1] = input[1]; + encode[2] = input[2]; + } + else + { + float ialpha = 1.0f / alpha; + encode[0] = input[4] * ialpha; + encode[1] = input[5] * ialpha; + encode[2] = input[6] * ialpha; + } + encode[3] = alpha; +#endif + + input += 7; + encode += 4; + } while ( encode < end_output ); +} + +// format: [X A Xpm][X A Xpm] etc +static void stbir__fancy_alpha_unweight_2ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = input[1]; + encode[0] = input[0]; + if ( alpha >= stbir__small_float ) + encode[0] = input[2] / alpha; + encode[1] = alpha; + + input += 3; + encode += 2; + } while ( encode < end_output ); +} + +static void stbir__simple_alpha_weight_4ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + #ifdef STBIR_SIMD + { + decode += 2 * stbir__simdfX_float_count; + while ( decode <= end_decode ) + { + stbir__simdfX d0,a0,d1,a1; + STBIR_NO_UNROLL(decode); + stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count ); + stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count ); + stbir__simdfX_aaa1( a0, d0, STBIR_onesX ); + stbir__simdfX_aaa1( a1, d1, STBIR_onesX ); + stbir__simdfX_mult( d0, d0, a0 ); + stbir__simdfX_mult( d1, d1, a1 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 ); + decode += 2 * stbir__simdfX_float_count; + } + decode -= 2 * stbir__simdfX_float_count; + + // few last pixels remnants + #ifdef STBIR_SIMD8 + while ( decode < end_decode ) + #else + if ( decode < end_decode ) + #endif + { + stbir__simdf d,a; + stbir__simdf_load( d, decode ); + stbir__simdf_aaa1( a, d, STBIR__CONSTF(STBIR_ones) ); + stbir__simdf_mult( d, d, a ); + stbir__simdf_store ( decode, d ); + decode += 4; + } + } + + #else + + while( decode < end_decode ) + { + float alpha = decode[3]; + decode[0] *= alpha; + decode[1] *= alpha; + decode[2] *= alpha; + decode += 4; + } + + #endif +} + +static void stbir__simple_alpha_weight_2ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + #ifdef STBIR_SIMD + decode += 2 * stbir__simdfX_float_count; + while ( decode <= end_decode ) + { + stbir__simdfX d0,a0,d1,a1; + STBIR_NO_UNROLL(decode); + stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count ); + stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count ); + stbir__simdfX_a1a1( a0, d0, STBIR_onesX ); + stbir__simdfX_a1a1( a1, d1, STBIR_onesX ); + stbir__simdfX_mult( d0, d0, a0 ); + stbir__simdfX_mult( d1, d1, a1 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 ); + decode += 2 * stbir__simdfX_float_count; + } + decode -= 2 * stbir__simdfX_float_count; + #endif + + while( decode < end_decode ) + { + float alpha = decode[1]; + STBIR_SIMD_NO_UNROLL(decode); + decode[0] *= alpha; + decode += 2; + } +} + +static void stbir__simple_alpha_unweight_4ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = encode[3]; + +#ifdef STBIR_SIMD + stbir__simdf i,ia; + STBIR_SIMD_NO_UNROLL(encode); + if ( alpha >= stbir__small_float ) + { + stbir__simdf_load1frep4( ia, 1.0f / alpha ); + stbir__simdf_load( i, encode ); + stbir__simdf_mult( i, i, ia ); + stbir__simdf_store( encode, i ); + encode[3] = alpha; + } +#else + if ( alpha >= stbir__small_float ) + { + float ialpha = 1.0f / alpha; + encode[0] *= ialpha; + encode[1] *= ialpha; + encode[2] *= ialpha; + } +#endif + encode += 4; + } while ( encode < end_output ); +} + +static void stbir__simple_alpha_unweight_2ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = encode[1]; + if ( alpha >= stbir__small_float ) + encode[0] /= alpha; + encode += 2; + } while ( encode < end_output ); +} + + +// only used in RGB->BGR or BGR->RGB +static void stbir__simple_flip_3ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + decode += 12; + while( decode <= end_decode ) + { + float t0,t1,t2,t3; + STBIR_NO_UNROLL(decode); + t0 = decode[0]; t1 = decode[3]; t2 = decode[6]; t3 = decode[9]; + decode[0] = decode[2]; decode[3] = decode[5]; decode[6] = decode[8]; decode[9] = decode[11]; + decode[2] = t0; decode[5] = t1; decode[8] = t2; decode[11] = t3; + decode += 12; + } + decode -= 12; + + while( decode < end_decode ) + { + float t = decode[0]; + STBIR_NO_UNROLL(decode); + decode[0] = decode[2]; + decode[2] = t; + decode += 3; + } +} + + + +static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float * output_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + int channels = stbir_info->channels; + int effective_channels = stbir_info->effective_channels; + int input_sample_in_bytes = stbir__type_size[stbir_info->input_type] * channels; + stbir_edge edge_horizontal = stbir_info->horizontal.edge; + stbir_edge edge_vertical = stbir_info->vertical.edge; + int row = stbir__edge_wrap(edge_vertical, n, stbir_info->vertical.scale_info.input_full_size); + const void* input_plane_data = ( (char *) stbir_info->input_data ) + (ptrdiff_t)row * (ptrdiff_t) stbir_info->input_stride_bytes; + stbir__span const * spans = stbir_info->scanline_extents.spans; + float* full_decode_buffer = output_buffer - stbir_info->scanline_extents.conservative.n0 * effective_channels; + + // if we are on edge_zero, and we get in here with an out of bounds n, then the calculate filters has failed + STBIR_ASSERT( !(edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->vertical.scale_info.input_full_size)) ); + + do + { + float * decode_buffer; + void const * input_data; + float * end_decode; + int width_times_channels; + int width; + + if ( spans->n1 < spans->n0 ) + break; + + width = spans->n1 + 1 - spans->n0; + decode_buffer = full_decode_buffer + spans->n0 * effective_channels; + end_decode = full_decode_buffer + ( spans->n1 + 1 ) * effective_channels; + width_times_channels = width * channels; + + // read directly out of input plane by default + input_data = ( (char*)input_plane_data ) + spans->pixel_offset_for_input * input_sample_in_bytes; + + // if we have an input callback, call it to get the input data + if ( stbir_info->in_pixels_cb ) + { + // call the callback with a temp buffer (that they can choose to use or not). the temp is just right aligned memory in the decode_buffer itself + input_data = stbir_info->in_pixels_cb( ( (char*) end_decode ) - ( width * input_sample_in_bytes ), input_plane_data, width, spans->pixel_offset_for_input, row, stbir_info->user_data ); + } + + STBIR_PROFILE_START( decode ); + // convert the pixels info the float decode_buffer, (we index from end_decode, so that when channelsdecode_pixels( (float*)end_decode - width_times_channels, width_times_channels, input_data ); + STBIR_PROFILE_END( decode ); + + if (stbir_info->alpha_weight) + { + STBIR_PROFILE_START( alpha ); + stbir_info->alpha_weight( decode_buffer, width_times_channels ); + STBIR_PROFILE_END( alpha ); + } + + ++spans; + } while ( spans <= ( &stbir_info->scanline_extents.spans[1] ) ); + + // handle the edge_wrap filter (all other types are handled back out at the calculate_filter stage) + // basically the idea here is that if we have the whole scanline in memory, we don't redecode the + // wrapped edge pixels, and instead just memcpy them from the scanline into the edge positions + if ( ( edge_horizontal == STBIR_EDGE_WRAP ) && ( stbir_info->scanline_extents.edge_sizes[0] | stbir_info->scanline_extents.edge_sizes[1] ) ) + { + // this code only runs if we're in edge_wrap, and we're doing the entire scanline + int e, start_x[2]; + int input_full_size = stbir_info->horizontal.scale_info.input_full_size; + + start_x[0] = -stbir_info->scanline_extents.edge_sizes[0]; // left edge start x + start_x[1] = input_full_size; // right edge + + for( e = 0; e < 2 ; e++ ) + { + // do each margin + int margin = stbir_info->scanline_extents.edge_sizes[e]; + if ( margin ) + { + int x = start_x[e]; + float * marg = full_decode_buffer + x * effective_channels; + float const * src = full_decode_buffer + stbir__edge_wrap(edge_horizontal, x, input_full_size) * effective_channels; + STBIR_MEMCPY( marg, src, margin * effective_channels * sizeof(float) ); + } + } + } +} + + +//================= +// Do 1 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_mult1_mem( tot, c, decode ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2z( c, hc ); \ + stbir__simdf_load2( d, decode ); \ + stbir__simdf_mult( tot, c, d ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_add1( tot, tot, c ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_0123to2301( t, tot ); \ + stbir__simdf_add1( tot, tot, c ); \ + stbir__simdf_add1( tot, tot, t ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store1( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#define stbir__4_coeff_start() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc + (ofs) ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load1z( c, hc + (ofs) ); \ + stbir__simdf_load1( d, decode + (ofs) ); \ + stbir__simdf_madd( tot, tot, d, c ); } + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load2z( c, hc+(ofs) ); \ + stbir__simdf_load2( d, decode+(ofs) ); \ + stbir__simdf_madd( tot, tot, d, c ); } + +#define stbir__3_coeff_setup() \ + stbir__simdf mask; \ + stbir__simdf_load( mask, STBIR_mask + 3 ); + +#define stbir__3_coeff_remnant( ofs ) \ + stbir__simdf_load( c, hc+(ofs) ); \ + stbir__simdf_and( c, c, mask ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) ); + +#define stbir__store_output() \ + stbir__simdf_0123to2301( c, tot ); \ + stbir__simdf_add( tot, tot, c ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_add1( tot, tot, c ); \ + stbir__simdf_store1( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#else + +#define stbir__1_coeff_only() \ + float tot; \ + tot = decode[0]*hc[0]; + +#define stbir__2_coeff_only() \ + float tot; \ + tot = decode[0] * hc[0]; \ + tot += decode[1] * hc[1]; + +#define stbir__3_coeff_only() \ + float tot; \ + tot = decode[0] * hc[0]; \ + tot += decode[1] * hc[1]; \ + tot += decode[2] * hc[2]; + +#define stbir__store_output_tiny() \ + output[0] = tot; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#define stbir__4_coeff_start() \ + float tot0,tot1,tot2,tot3; \ + tot0 = decode[0] * hc[0]; \ + tot1 = decode[1] * hc[1]; \ + tot2 = decode[2] * hc[2]; \ + tot3 = decode[3] * hc[3]; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + tot2 += decode[2+(ofs)] * hc[2+(ofs)]; \ + tot3 += decode[3+(ofs)] * hc[3+(ofs)]; + +#define stbir__1_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; + +#define stbir__2_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + +#define stbir__3_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + tot2 += decode[2+(ofs)] * hc[2+(ofs)]; + +#define stbir__store_output() \ + output[0] = (tot0+tot2)+(tot1+tot3); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#endif + +#define STBIR__horizontal_channels 1 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +//================= +// Do 2 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc ); \ + stbir__simdf_0123to0011( c, c ); \ + stbir__simdf_load2( d, decode ); \ + stbir__simdf_mult( tot, d, c ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( c, hc ); \ + stbir__simdf_0123to0011( c, c ); \ + stbir__simdf_mult_mem( tot, c, decode ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,cs,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load2z( d, decode+4 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__store_output_tiny() \ + stbir__simdf_0123to2301( c, tot ); \ + stbir__simdf_add( tot, tot, c ); \ + stbir__simdf_store2( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf t; \ + stbir__simdf_load1z( t, hc + (ofs) ); \ + stbir__simdf_0123to0011( t, t ); \ + stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \ + stbir__simdf8_add4( tot0, tot0, t ); } + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf t; \ + stbir__simdf_load2( t, hc + (ofs) ); \ + stbir__simdf_0123to0011( t, t ); \ + stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \ + stbir__simdf8_add4( tot0, tot0, t ); } + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf8 d; \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_load6z( d, decode+(ofs)*2 ); \ + stbir__simdf8_madd( tot0, tot0, c, d ); } + +#define stbir__store_output() \ + { stbir__simdf t,c; \ + stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \ + stbir__simdf_0123to2301( c, t ); \ + stbir__simdf_add( t, t, c ); \ + stbir__simdf_store2( output, t ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; } + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to2233( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \ + stbir__simdf_0123to2233( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*2+4 ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load1z( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_load2( d, decode + (ofs) * 2 ); \ + stbir__simdf_madd( tot0, tot0, d, c ); } + +#define stbir__2_coeff_remnant( ofs ) \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load2z( d, decode + (ofs) * 2 + 4 ); \ + stbir__simdf_madd( tot1, tot1, d, c ); } + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot1 ); \ + stbir__simdf_0123to2301( c, tot0 ); \ + stbir__simdf_add( tot0, tot0, c ); \ + stbir__simdf_store2( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; + +#define stbir__2_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; \ + c = hc[1]; \ + tota += decode[2]*c; \ + totb += decode[3]*c; + +// this weird order of add matches the simd +#define stbir__3_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; \ + c = hc[2]; \ + tota += decode[4]*c; \ + totb += decode[5]*c; \ + c = hc[1]; \ + tota += decode[2]*c; \ + totb += decode[3]*c; + +#define stbir__store_output_tiny() \ + output[0] = tota; \ + output[1] = totb; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#define stbir__4_coeff_start() \ + float tota0,tota1,tota2,tota3,totb0,totb1,totb2,totb3,c; \ + c = hc[0]; \ + tota0 = decode[0]*c; \ + totb0 = decode[1]*c; \ + c = hc[1]; \ + tota1 = decode[2]*c; \ + totb1 = decode[3]*c; \ + c = hc[2]; \ + tota2 = decode[4]*c; \ + totb2 = decode[5]*c; \ + c = hc[3]; \ + tota3 = decode[6]*c; \ + totb3 = decode[7]*c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2]*c; \ + totb0 += decode[1+(ofs)*2]*c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2]*c; \ + totb1 += decode[3+(ofs)*2]*c; \ + c = hc[2+(ofs)]; \ + tota2 += decode[4+(ofs)*2]*c; \ + totb2 += decode[5+(ofs)*2]*c; \ + c = hc[3+(ofs)]; \ + tota3 += decode[6+(ofs)*2]*c; \ + totb3 += decode[7+(ofs)*2]*c; + +#define stbir__1_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; + +#define stbir__2_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2] * c; \ + totb1 += decode[3+(ofs)*2] * c; + +#define stbir__3_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2] * c; \ + totb1 += decode[3+(ofs)*2] * c; \ + c = hc[2+(ofs)]; \ + tota2 += decode[4+(ofs)*2] * c; \ + totb2 += decode[5+(ofs)*2] * c; + +#define stbir__store_output() \ + output[0] = (tota0+tota2)+(tota1+tota3); \ + output[1] = (totb0+totb2)+(totb1+totb3); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#endif + +#define STBIR__horizontal_channels 2 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +//================= +// Do 3 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc ); \ + stbir__simdf_0123to0001( c, c ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,cs,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_load( d, decode+3 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,d,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_load( d, decode+3 ); \ + stbir__simdf_madd( tot, tot, d, c ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load( d, decode+6 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store2( output, tot ); \ + stbir__simdf_0123to2301( tot, tot ); \ + stbir__simdf_store1( output+2, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#ifdef STBIR_SIMD8 + +// we're loading from the XXXYYY decode by -1 to get the XXXYYY into different halves of the AVX reg fyi +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,tot1,c,cs; stbir__simdf t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode - 1 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_mult_mem( tot1, c, decode+6 - 1 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*3 + 6 - 1 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1rep4( t, hc + (ofs) ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*3 - 1 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); + + #define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \ + stbir__simdf8_0123to2222( t, cs ); \ + stbir__simdf8_madd_mem4( tot1, tot1, t, decode+(ofs)*3 + 6 - 1 ); + +#define stbir__store_output() \ + stbir__simdf8_add( tot0, tot0, tot1 ); \ + stbir__simdf_0123to1230( t, stbir__if_simdf8_cast_to_simdf4( tot0 ) ); \ + stbir__simdf8_add4halves( t, t, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; \ + if ( output < output_end ) \ + { \ + stbir__simdf_store( output-3, t ); \ + continue; \ + } \ + { stbir__simdf tt; stbir__simdf_0123to2301( tt, t ); \ + stbir__simdf_store2( output-3, t ); \ + stbir__simdf_store1( output+2-3, tt ); } \ + break; + + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,tot2,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); \ + stbir__simdf_0123to2333( c, cs ); \ + stbir__simdf_mult_mem( tot2, c, decode+8 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \ + stbir__simdf_0123to2333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*3+8 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2z( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_load2z( d, decode+(ofs)*3+4 ); \ + stbir__simdf_madd( tot1, tot1, c, d ); } + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load1z( d, decode+(ofs)*3+8 ); \ + stbir__simdf_madd( tot2, tot2, c, d ); } + +#define stbir__store_output() \ + stbir__simdf_0123ABCDto3ABx( c, tot0, tot1 ); \ + stbir__simdf_0123ABCDto23Ax( cs, tot1, tot2 ); \ + stbir__simdf_0123to1230( tot2, tot2 ); \ + stbir__simdf_add( tot0, tot0, cs ); \ + stbir__simdf_add( c, c, tot2 ); \ + stbir__simdf_add( tot0, tot0, c ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; \ + if ( output < output_end ) \ + { \ + stbir__simdf_store( output-3, tot0 ); \ + continue; \ + } \ + stbir__simdf_0123to2301( tot1, tot0 ); \ + stbir__simdf_store2( output-3, tot0 ); \ + stbir__simdf_store1( output+2-3, tot1 ); \ + break; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; + +#define stbir__2_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + c = hc[1]; \ + tot0 += decode[3]*c; \ + tot1 += decode[4]*c; \ + tot2 += decode[5]*c; + +#define stbir__3_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + c = hc[1]; \ + tot0 += decode[3]*c; \ + tot1 += decode[4]*c; \ + tot2 += decode[5]*c; \ + c = hc[2]; \ + tot0 += decode[6]*c; \ + tot1 += decode[7]*c; \ + tot2 += decode[8]*c; + +#define stbir__store_output_tiny() \ + output[0] = tot0; \ + output[1] = tot1; \ + output[2] = tot2; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#define stbir__4_coeff_start() \ + float tota0,tota1,tota2,totb0,totb1,totb2,totc0,totc1,totc2,totd0,totd1,totd2,c; \ + c = hc[0]; \ + tota0 = decode[0]*c; \ + tota1 = decode[1]*c; \ + tota2 = decode[2]*c; \ + c = hc[1]; \ + totb0 = decode[3]*c; \ + totb1 = decode[4]*c; \ + totb2 = decode[5]*c; \ + c = hc[2]; \ + totc0 = decode[6]*c; \ + totc1 = decode[7]*c; \ + totc2 = decode[8]*c; \ + c = hc[3]; \ + totd0 = decode[9]*c; \ + totd1 = decode[10]*c; \ + totd2 = decode[11]*c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + c = hc[2+(ofs)]; \ + totc0 += decode[6+(ofs)*3]*c; \ + totc1 += decode[7+(ofs)*3]*c; \ + totc2 += decode[8+(ofs)*3]*c; \ + c = hc[3+(ofs)]; \ + totd0 += decode[9+(ofs)*3]*c; \ + totd1 += decode[10+(ofs)*3]*c; \ + totd2 += decode[11+(ofs)*3]*c; + +#define stbir__1_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; + +#define stbir__2_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + +#define stbir__3_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + c = hc[2+(ofs)]; \ + totc0 += decode[6+(ofs)*3]*c; \ + totc1 += decode[7+(ofs)*3]*c; \ + totc2 += decode[8+(ofs)*3]*c; + +#define stbir__store_output() \ + output[0] = (tota0+totc0)+(totb0+totd0); \ + output[1] = (tota1+totc1)+(totb1+totd1); \ + output[2] = (tota2+totc2)+(totb2+totd2); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#endif + +#define STBIR__horizontal_channels 3 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + +//================= +// Do 4 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_mult_mem( tot, c, decode ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+4 ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+8 ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,c,cs; stbir__simdf t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+8 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1rep4( t, hc + (ofs) ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); + + #define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf8_0123to2222( t, cs ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4+8 ); + +#define stbir__store_output() \ + stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \ + stbir__simdf_store( output, t ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+8 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+12 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+12 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; + +#define stbir__2_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; \ + c = hc[1]; \ + p0 += decode[4] * c; \ + p1 += decode[5] * c; \ + p2 += decode[6] * c; \ + p3 += decode[7] * c; + +#define stbir__3_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; \ + c = hc[1]; \ + p0 += decode[4] * c; \ + p1 += decode[5] * c; \ + p2 += decode[6] * c; \ + p3 += decode[7] * c; \ + c = hc[2]; \ + p0 += decode[8] * c; \ + p1 += decode[9] * c; \ + p2 += decode[10] * c; \ + p3 += decode[11] * c; + +#define stbir__store_output_tiny() \ + output[0] = p0; \ + output[1] = p1; \ + output[2] = p2; \ + output[3] = p3; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#define stbir__4_coeff_start() \ + float x0,x1,x2,x3,y0,y1,y2,y3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + x0 = decode[0] * c; \ + x1 = decode[1] * c; \ + x2 = decode[2] * c; \ + x3 = decode[3] * c; \ + c = hc[1]; \ + y0 = decode[4] * c; \ + y1 = decode[5] * c; \ + y2 = decode[6] * c; \ + y3 = decode[7] * c; \ + c = hc[2]; \ + x0 += decode[8] * c; \ + x1 += decode[9] * c; \ + x2 += decode[10] * c; \ + x3 += decode[11] * c; \ + c = hc[3]; \ + y0 += decode[12] * c; \ + y1 += decode[13] * c; \ + y2 += decode[14] * c; \ + y3 += decode[15] * c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[8+(ofs)*4] * c; \ + x1 += decode[9+(ofs)*4] * c; \ + x2 += decode[10+(ofs)*4] * c; \ + x3 += decode[11+(ofs)*4] * c; \ + c = hc[3+(ofs)]; \ + y0 += decode[12+(ofs)*4] * c; \ + y1 += decode[13+(ofs)*4] * c; \ + y2 += decode[14+(ofs)*4] * c; \ + y3 += decode[15+(ofs)*4] * c; + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[8+(ofs)*4] * c; \ + x1 += decode[9+(ofs)*4] * c; \ + x2 += decode[10+(ofs)*4] * c; \ + x3 += decode[11+(ofs)*4] * c; + +#define stbir__store_output() \ + output[0] = x0 + y0; \ + output[1] = x1 + y1; \ + output[2] = x2 + y2; \ + output[3] = x3 + y3; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#endif + +#define STBIR__horizontal_channels 4 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + + +//================= +// Do 7 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot0,tot1,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c,decode+10 ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store( output+3, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_mult_mem( tot1, c, decode+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf8_0123to33333333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+21 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf8_0123to33333333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+21 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load1b( c, hc + (ofs) ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load1b( c, hc + (ofs) ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_load1b( c, hc + (ofs)+1 ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); + +#define stbir__store_output() \ + stbir__simdf8_add( tot0, tot0, tot1 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; \ + if ( output < output_end ) \ + { \ + stbir__simdf8_store( output-7, tot0 ); \ + continue; \ + } \ + stbir__simdf_store( output-7+3, stbir__simdf_swiz(stbir__simdf8_gettop4(tot0),0,0,1,2) ); \ + stbir__simdf_store( output-7, stbir__if_simdf8_cast_to_simdf4(tot0) ); \ + break; + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,tot2,tot3,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_mult_mem( tot2, c, decode+7 ); \ + stbir__simdf_mult_mem( tot3, c, decode+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+21 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+24 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+21 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+24 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot2 ); \ + stbir__simdf_add( tot1, tot1, tot3 ); \ + stbir__simdf_store( output+3, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; + +#define stbir__2_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; \ + c = hc[1]; \ + tot0 += decode[7]*c; \ + tot1 += decode[8]*c; \ + tot2 += decode[9]*c; \ + tot3 += decode[10]*c; \ + tot4 += decode[11]*c; \ + tot5 += decode[12]*c; \ + tot6 += decode[13]*c; \ + +#define stbir__3_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; \ + c = hc[1]; \ + tot0 += decode[7]*c; \ + tot1 += decode[8]*c; \ + tot2 += decode[9]*c; \ + tot3 += decode[10]*c; \ + tot4 += decode[11]*c; \ + tot5 += decode[12]*c; \ + tot6 += decode[13]*c; \ + c = hc[2]; \ + tot0 += decode[14]*c; \ + tot1 += decode[15]*c; \ + tot2 += decode[16]*c; \ + tot3 += decode[17]*c; \ + tot4 += decode[18]*c; \ + tot5 += decode[19]*c; \ + tot6 += decode[20]*c; \ + +#define stbir__store_output_tiny() \ + output[0] = tot0; \ + output[1] = tot1; \ + output[2] = tot2; \ + output[3] = tot3; \ + output[4] = tot4; \ + output[5] = tot5; \ + output[6] = tot6; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#define stbir__4_coeff_start() \ + float x0,x1,x2,x3,x4,x5,x6,y0,y1,y2,y3,y4,y5,y6,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + x0 = decode[0] * c; \ + x1 = decode[1] * c; \ + x2 = decode[2] * c; \ + x3 = decode[3] * c; \ + x4 = decode[4] * c; \ + x5 = decode[5] * c; \ + x6 = decode[6] * c; \ + c = hc[1]; \ + y0 = decode[7] * c; \ + y1 = decode[8] * c; \ + y2 = decode[9] * c; \ + y3 = decode[10] * c; \ + y4 = decode[11] * c; \ + y5 = decode[12] * c; \ + y6 = decode[13] * c; \ + c = hc[2]; \ + x0 += decode[14] * c; \ + x1 += decode[15] * c; \ + x2 += decode[16] * c; \ + x3 += decode[17] * c; \ + x4 += decode[18] * c; \ + x5 += decode[19] * c; \ + x6 += decode[20] * c; \ + c = hc[3]; \ + y0 += decode[21] * c; \ + y1 += decode[22] * c; \ + y2 += decode[23] * c; \ + y3 += decode[24] * c; \ + y4 += decode[25] * c; \ + y5 += decode[26] * c; \ + y6 += decode[27] * c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[14+(ofs)*7] * c; \ + x1 += decode[15+(ofs)*7] * c; \ + x2 += decode[16+(ofs)*7] * c; \ + x3 += decode[17+(ofs)*7] * c; \ + x4 += decode[18+(ofs)*7] * c; \ + x5 += decode[19+(ofs)*7] * c; \ + x6 += decode[20+(ofs)*7] * c; \ + c = hc[3+(ofs)]; \ + y0 += decode[21+(ofs)*7] * c; \ + y1 += decode[22+(ofs)*7] * c; \ + y2 += decode[23+(ofs)*7] * c; \ + y3 += decode[24+(ofs)*7] * c; \ + y4 += decode[25+(ofs)*7] * c; \ + y5 += decode[26+(ofs)*7] * c; \ + y6 += decode[27+(ofs)*7] * c; + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[14+(ofs)*7] * c; \ + x1 += decode[15+(ofs)*7] * c; \ + x2 += decode[16+(ofs)*7] * c; \ + x3 += decode[17+(ofs)*7] * c; \ + x4 += decode[18+(ofs)*7] * c; \ + x5 += decode[19+(ofs)*7] * c; \ + x6 += decode[20+(ofs)*7] * c; \ + +#define stbir__store_output() \ + output[0] = x0 + y0; \ + output[1] = x1 + y1; \ + output[2] = x2 + y2; \ + output[3] = x3 + y3; \ + output[4] = x4 + y4; \ + output[5] = x5 + y5; \ + output[6] = x6 + y6; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#endif + +#define STBIR__horizontal_channels 7 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +// include all of the vertical resamplers (both scatter and gather versions) + +#define STBIR__vertical_channels 1 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 1 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 2 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 2 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 3 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 3 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 4 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 4 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 5 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 5 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 6 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 6 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 7 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 7 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 8 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 8 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +typedef void STBIR_VERTICAL_GATHERFUNC( float * output, float const * coeffs, float const ** inputs, float const * input0_end ); + +static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers[ 8 ] = +{ + stbir__vertical_gather_with_1_coeffs,stbir__vertical_gather_with_2_coeffs,stbir__vertical_gather_with_3_coeffs,stbir__vertical_gather_with_4_coeffs,stbir__vertical_gather_with_5_coeffs,stbir__vertical_gather_with_6_coeffs,stbir__vertical_gather_with_7_coeffs,stbir__vertical_gather_with_8_coeffs +}; + +static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers_continues[ 8 ] = +{ + stbir__vertical_gather_with_1_coeffs_cont,stbir__vertical_gather_with_2_coeffs_cont,stbir__vertical_gather_with_3_coeffs_cont,stbir__vertical_gather_with_4_coeffs_cont,stbir__vertical_gather_with_5_coeffs_cont,stbir__vertical_gather_with_6_coeffs_cont,stbir__vertical_gather_with_7_coeffs_cont,stbir__vertical_gather_with_8_coeffs_cont +}; + +typedef void STBIR_VERTICAL_SCATTERFUNC( float ** outputs, float const * coeffs, float const * input, float const * input_end ); + +static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_sets[ 8 ] = +{ + stbir__vertical_scatter_with_1_coeffs,stbir__vertical_scatter_with_2_coeffs,stbir__vertical_scatter_with_3_coeffs,stbir__vertical_scatter_with_4_coeffs,stbir__vertical_scatter_with_5_coeffs,stbir__vertical_scatter_with_6_coeffs,stbir__vertical_scatter_with_7_coeffs,stbir__vertical_scatter_with_8_coeffs +}; + +static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_blends[ 8 ] = +{ + stbir__vertical_scatter_with_1_coeffs_cont,stbir__vertical_scatter_with_2_coeffs_cont,stbir__vertical_scatter_with_3_coeffs_cont,stbir__vertical_scatter_with_4_coeffs_cont,stbir__vertical_scatter_with_5_coeffs_cont,stbir__vertical_scatter_with_6_coeffs_cont,stbir__vertical_scatter_with_7_coeffs_cont,stbir__vertical_scatter_with_8_coeffs_cont +}; + + +static void stbir__encode_scanline( stbir__info const * stbir_info, void *output_buffer_data, float * encode_buffer, int row STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + int num_pixels = stbir_info->horizontal.scale_info.output_sub_size; + int channels = stbir_info->channels; + int width_times_channels = num_pixels * channels; + void * output_buffer; + + // un-alpha weight if we need to + if ( stbir_info->alpha_unweight ) + { + STBIR_PROFILE_START( unalpha ); + stbir_info->alpha_unweight( encode_buffer, width_times_channels ); + STBIR_PROFILE_END( unalpha ); + } + + // write directly into output by default + output_buffer = output_buffer_data; + + // if we have an output callback, we first convert the decode buffer in place (and then hand that to the callback) + if ( stbir_info->out_pixels_cb ) + output_buffer = encode_buffer; + + STBIR_PROFILE_START( encode ); + // convert into the output buffer + stbir_info->encode_pixels( output_buffer, width_times_channels, encode_buffer ); + STBIR_PROFILE_END( encode ); + + // if we have an output callback, call it to send the data + if ( stbir_info->out_pixels_cb ) + stbir_info->out_pixels_cb( output_buffer_data, num_pixels, row, stbir_info->user_data ); +} + + +// Get the ring buffer pointer for an index +static float* stbir__get_ring_buffer_entry(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int index ) +{ + STBIR_ASSERT( index < stbir_info->ring_buffer_num_entries ); + + #ifdef STBIR__SEPARATE_ALLOCATIONS + return split_info->ring_buffers[ index ]; + #else + return (float*) ( ( (char*) split_info->ring_buffer ) + ( index * stbir_info->ring_buffer_length_bytes ) ); + #endif +} + +// Get the specified scan line from the ring buffer +static float* stbir__get_ring_buffer_scanline(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int get_scanline) +{ + int ring_buffer_index = (split_info->ring_buffer_begin_index + (get_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + return stbir__get_ring_buffer_entry( stbir_info, split_info, ring_buffer_index ); +} + +static void stbir__resample_horizontal_gather(stbir__info const * stbir_info, float* output_buffer, float const * input_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + float const * decode_buffer = input_buffer - ( stbir_info->scanline_extents.conservative.n0 * stbir_info->effective_channels ); + + STBIR_PROFILE_START( horizontal ); + if ( ( stbir_info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( stbir_info->horizontal.scale_info.scale == 1.0f ) ) + STBIR_MEMCPY( output_buffer, input_buffer, stbir_info->horizontal.scale_info.output_sub_size * sizeof( float ) * stbir_info->effective_channels ); + else + stbir_info->horizontal_gather_channels( output_buffer, stbir_info->horizontal.scale_info.output_sub_size, decode_buffer, stbir_info->horizontal.contributors, stbir_info->horizontal.coefficients, stbir_info->horizontal.coefficient_width ); + STBIR_PROFILE_END( horizontal ); +} + +static void stbir__resample_vertical_gather(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n, int contrib_n0, int contrib_n1, float const * vertical_coefficients ) +{ + float* encode_buffer = split_info->vertical_buffer; + float* decode_buffer = split_info->decode_buffer; + int vertical_first = stbir_info->vertical_first; + int width = (vertical_first) ? ( stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1 ) : stbir_info->horizontal.scale_info.output_sub_size; + int width_times_channels = stbir_info->effective_channels * width; + + STBIR_ASSERT( stbir_info->vertical.is_gather ); + + // loop over the contributing scanlines and scale into the buffer + STBIR_PROFILE_START( vertical ); + { + int k = 0, total = contrib_n1 - contrib_n0 + 1; + STBIR_ASSERT( total > 0 ); + do { + float const * inputs[8]; + int i, cnt = total; if ( cnt > 8 ) cnt = 8; + for( i = 0 ; i < cnt ; i++ ) + inputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+contrib_n0 ); + + // call the N scanlines at a time function (up to 8 scanlines of blending at once) + ((k==0)?stbir__vertical_gathers:stbir__vertical_gathers_continues)[cnt-1]( (vertical_first) ? decode_buffer : encode_buffer, vertical_coefficients + k, inputs, inputs[0] + width_times_channels ); + k += cnt; + total -= cnt; + } while ( total ); + } + STBIR_PROFILE_END( vertical ); + + if ( vertical_first ) + { + // Now resample the gathered vertical data in the horizontal axis into the encode buffer + stbir__resample_horizontal_gather(stbir_info, encode_buffer, decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + } + + stbir__encode_scanline( stbir_info, ( (char *) stbir_info->output_data ) + ((ptrdiff_t)n * (ptrdiff_t)stbir_info->output_stride_bytes), + encode_buffer, n STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); +} + +static void stbir__decode_and_resample_for_vertical_gather_loop(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, n, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // update new end scanline + split_info->ring_buffer_last_scanline = n; + + // get ring buffer + ring_buffer_index = (split_info->ring_buffer_begin_index + (split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + ring_buffer = stbir__get_ring_buffer_entry(stbir_info, split_info, ring_buffer_index); + + // Now resample it into the ring buffer. + stbir__resample_horizontal_gather( stbir_info, ring_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__vertical_gather_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count ) +{ + int y, start_output_y, end_output_y; + stbir__contributors* vertical_contributors = stbir_info->vertical.contributors; + float const * vertical_coefficients = stbir_info->vertical.coefficients; + + STBIR_ASSERT( stbir_info->vertical.is_gather ); + + start_output_y = split_info->start_output_y; + end_output_y = split_info[split_count-1].end_output_y; + + vertical_contributors += start_output_y; + vertical_coefficients += start_output_y * stbir_info->vertical.coefficient_width; + + // initialize the ring buffer for gathering + split_info->ring_buffer_begin_index = 0; + split_info->ring_buffer_first_scanline = stbir_info->vertical.extent_info.lowest; + split_info->ring_buffer_last_scanline = split_info->ring_buffer_first_scanline - 1; // means "empty" + + for (y = start_output_y; y < end_output_y; y++) + { + int in_first_scanline, in_last_scanline; + + in_first_scanline = vertical_contributors->n0; + in_last_scanline = vertical_contributors->n1; + + // make sure the indexing hasn't broken + STBIR_ASSERT( in_first_scanline >= split_info->ring_buffer_first_scanline ); + + // Load in new scanlines + while (in_last_scanline > split_info->ring_buffer_last_scanline) + { + STBIR_ASSERT( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) <= stbir_info->ring_buffer_num_entries ); + + // make sure there was room in the ring buffer when we add new scanlines + if ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) + { + split_info->ring_buffer_first_scanline++; + split_info->ring_buffer_begin_index++; + } + + if ( stbir_info->vertical_first ) + { + float * ring_buffer = stbir__get_ring_buffer_scanline( stbir_info, split_info, ++split_info->ring_buffer_last_scanline ); + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, split_info->ring_buffer_last_scanline, ring_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + } + else + { + stbir__decode_and_resample_for_vertical_gather_loop(stbir_info, split_info, split_info->ring_buffer_last_scanline + 1); + } + } + + // Now all buffers should be ready to write a row of vertical sampling, so do it. + stbir__resample_vertical_gather(stbir_info, split_info, y, in_first_scanline, in_last_scanline, vertical_coefficients ); + + ++vertical_contributors; + vertical_coefficients += stbir_info->vertical.coefficient_width; + } +} + +#define STBIR__FLOAT_EMPTY_MARKER 3.0e+38F +#define STBIR__FLOAT_BUFFER_IS_EMPTY(ptr) ((ptr)[0]==STBIR__FLOAT_EMPTY_MARKER) + +static void stbir__encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info) +{ + // evict a scanline out into the output buffer + float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index ); + + // dump the scanline out + stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (ptrdiff_t)split_info->ring_buffer_first_scanline * (ptrdiff_t)stbir_info->output_stride_bytes ), ring_buffer_entry, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // mark it as empty + ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER; + + // advance the first scanline + split_info->ring_buffer_first_scanline++; + if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries ) + split_info->ring_buffer_begin_index = 0; +} + +static void stbir__horizontal_resample_and_encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info) +{ + // evict a scanline out into the output buffer + + float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index ); + + // Now resample it into the buffer. + stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, ring_buffer_entry STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // dump the scanline out + stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (ptrdiff_t)split_info->ring_buffer_first_scanline * (ptrdiff_t)stbir_info->output_stride_bytes ), split_info->vertical_buffer, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // mark it as empty + ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER; + + // advance the first scanline + split_info->ring_buffer_first_scanline++; + if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries ) + split_info->ring_buffer_begin_index = 0; +} + +static void stbir__resample_vertical_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n0, int n1, float const * vertical_coefficients, float const * vertical_buffer, float const * vertical_buffer_end ) +{ + STBIR_ASSERT( !stbir_info->vertical.is_gather ); + + STBIR_PROFILE_START( vertical ); + { + int k = 0, total = n1 - n0 + 1; + STBIR_ASSERT( total > 0 ); + do { + float * outputs[8]; + int i, n = total; if ( n > 8 ) n = 8; + for( i = 0 ; i < n ; i++ ) + { + outputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+n0 ); + if ( ( i ) && ( STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[i] ) != STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ) ) ) // make sure runs are of the same type + { + n = i; + break; + } + } + // call the scatter to N scanlines at a time function (up to 8 scanlines of scattering at once) + ((STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ))?stbir__vertical_scatter_sets:stbir__vertical_scatter_blends)[n-1]( outputs, vertical_coefficients + k, vertical_buffer, vertical_buffer_end ); + k += n; + total -= n; + } while ( total ); + } + + STBIR_PROFILE_END( vertical ); +} + +typedef void stbir__handle_scanline_for_scatter_func(stbir__info const * stbir_info, stbir__per_split_info* split_info); + +static void stbir__vertical_scatter_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count ) +{ + int y, start_output_y, end_output_y, start_input_y, end_input_y; + stbir__contributors* vertical_contributors = stbir_info->vertical.contributors; + float const * vertical_coefficients = stbir_info->vertical.coefficients; + stbir__handle_scanline_for_scatter_func * handle_scanline_for_scatter; + void * scanline_scatter_buffer; + void * scanline_scatter_buffer_end; + int on_first_input_y, last_input_y; + + STBIR_ASSERT( !stbir_info->vertical.is_gather ); + + start_output_y = split_info->start_output_y; + end_output_y = split_info[split_count-1].end_output_y; // may do multiple split counts + + start_input_y = split_info->start_input_y; + end_input_y = split_info[split_count-1].end_input_y; + + // adjust for starting offset start_input_y + y = start_input_y + stbir_info->vertical.filter_pixel_margin; + vertical_contributors += y ; + vertical_coefficients += stbir_info->vertical.coefficient_width * y; + + if ( stbir_info->vertical_first ) + { + handle_scanline_for_scatter = stbir__horizontal_resample_and_encode_first_scanline_from_scatter; + scanline_scatter_buffer = split_info->decode_buffer; + scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * (stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1); + } + else + { + handle_scanline_for_scatter = stbir__encode_first_scanline_from_scatter; + scanline_scatter_buffer = split_info->vertical_buffer; + scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * stbir_info->horizontal.scale_info.output_sub_size; + } + + // initialize the ring buffer for scattering + split_info->ring_buffer_first_scanline = start_output_y; + split_info->ring_buffer_last_scanline = -1; + split_info->ring_buffer_begin_index = -1; + + // mark all the buffers as empty to start + for( y = 0 ; y < stbir_info->ring_buffer_num_entries ; y++ ) + stbir__get_ring_buffer_entry( stbir_info, split_info, y )[0] = STBIR__FLOAT_EMPTY_MARKER; // only used on scatter + + // do the loop in input space + on_first_input_y = 1; last_input_y = start_input_y; + for (y = start_input_y ; y < end_input_y; y++) + { + int out_first_scanline, out_last_scanline; + + out_first_scanline = vertical_contributors->n0; + out_last_scanline = vertical_contributors->n1; + + STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if ( ( out_last_scanline >= out_first_scanline ) && ( ( ( out_first_scanline >= start_output_y ) && ( out_first_scanline < end_output_y ) ) || ( ( out_last_scanline >= start_output_y ) && ( out_last_scanline < end_output_y ) ) ) ) + { + float const * vc = vertical_coefficients; + + // keep track of the range actually seen for the next resize + last_input_y = y; + if ( ( on_first_input_y ) && ( y > start_input_y ) ) + split_info->start_input_y = y; + on_first_input_y = 0; + + // clip the region + if ( out_first_scanline < start_output_y ) + { + vc += start_output_y - out_first_scanline; + out_first_scanline = start_output_y; + } + + if ( out_last_scanline >= end_output_y ) + out_last_scanline = end_output_y - 1; + + // if very first scanline, init the index + if (split_info->ring_buffer_begin_index < 0) + split_info->ring_buffer_begin_index = out_first_scanline - start_output_y; + + STBIR_ASSERT( split_info->ring_buffer_begin_index <= out_first_scanline ); + + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, y, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // When horizontal first, we resample horizontally into the vertical buffer before we scatter it out + if ( !stbir_info->vertical_first ) + stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // Now it's sitting in the buffer ready to be distributed into the ring buffers. + + // evict from the ringbuffer, if we need are full + if ( ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) && + ( out_last_scanline > split_info->ring_buffer_last_scanline ) ) + handle_scanline_for_scatter( stbir_info, split_info ); + + // Now the horizontal buffer is ready to write to all ring buffer rows, so do it. + stbir__resample_vertical_scatter(stbir_info, split_info, out_first_scanline, out_last_scanline, vc, (float*)scanline_scatter_buffer, (float*)scanline_scatter_buffer_end ); + + // update the end of the buffer + if ( out_last_scanline > split_info->ring_buffer_last_scanline ) + split_info->ring_buffer_last_scanline = out_last_scanline; + } + ++vertical_contributors; + vertical_coefficients += stbir_info->vertical.coefficient_width; + } + + // now evict the scanlines that are left over in the ring buffer + while ( split_info->ring_buffer_first_scanline < end_output_y ) + handle_scanline_for_scatter(stbir_info, split_info); + + // update the end_input_y if we do multiple resizes with the same data + ++last_input_y; + for( y = 0 ; y < split_count; y++ ) + if ( split_info[y].end_input_y > last_input_y ) + split_info[y].end_input_y = last_input_y; +} + + +static stbir__kernel_callback * stbir__builtin_kernels[] = { 0, stbir__filter_trapezoid, stbir__filter_triangle, stbir__filter_cubic, stbir__filter_catmullrom, stbir__filter_mitchell, stbir__filter_point }; +static stbir__support_callback * stbir__builtin_supports[] = { 0, stbir__support_trapezoid, stbir__support_one, stbir__support_two, stbir__support_two, stbir__support_two, stbir__support_zeropoint5 }; + +static void stbir__set_sampler(stbir__sampler * samp, stbir_filter filter, stbir__kernel_callback * kernel, stbir__support_callback * support, stbir_edge edge, stbir__scale_info * scale_info, int always_gather, void * user_data ) +{ + // set filter + if (filter == 0) + { + filter = STBIR_DEFAULT_FILTER_DOWNSAMPLE; // default to downsample + if (scale_info->scale >= ( 1.0f - stbir__small_float ) ) + { + if ( (scale_info->scale <= ( 1.0f + stbir__small_float ) ) && ( STBIR_CEILF(scale_info->pixel_shift) == scale_info->pixel_shift ) ) + filter = STBIR_FILTER_POINT_SAMPLE; + else + filter = STBIR_DEFAULT_FILTER_UPSAMPLE; + } + } + samp->filter_enum = filter; + + STBIR_ASSERT(samp->filter_enum != 0); + STBIR_ASSERT((unsigned)samp->filter_enum < STBIR_FILTER_OTHER); + samp->filter_kernel = stbir__builtin_kernels[ filter ]; + samp->filter_support = stbir__builtin_supports[ filter ]; + + if ( kernel && support ) + { + samp->filter_kernel = kernel; + samp->filter_support = support; + samp->filter_enum = STBIR_FILTER_OTHER; + } + + samp->edge = edge; + samp->filter_pixel_width = stbir__get_filter_pixel_width (samp->filter_support, scale_info->scale, user_data ); + // Gather is always better, but in extreme downsamples, you have to most or all of the data in memory + // For horizontal, we always have all the pixels, so we always use gather here (always_gather==1). + // For vertical, we use gather if scaling up (which means we will have samp->filter_pixel_width + // scanlines in memory at once). + samp->is_gather = 0; + if ( scale_info->scale >= ( 1.0f - stbir__small_float ) ) + samp->is_gather = 1; + else if ( ( always_gather ) || ( samp->filter_pixel_width <= STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT ) ) + samp->is_gather = 2; + + // pre calculate stuff based on the above + samp->coefficient_width = stbir__get_coefficient_width(samp, samp->is_gather, user_data); + + if ( edge == STBIR_EDGE_WRAP ) + if ( samp->filter_pixel_width > ( scale_info->input_full_size * 2 ) ) // this can only happen when shrinking to a single pixel + samp->filter_pixel_width = scale_info->input_full_size * 2; + + // This is how much to expand buffers to account for filters seeking outside + // the image boundaries. + samp->filter_pixel_margin = samp->filter_pixel_width / 2; + + samp->num_contributors = stbir__get_contributors(samp, samp->is_gather); + samp->contributors_size = samp->num_contributors * sizeof(stbir__contributors); + samp->coefficients_size = samp->num_contributors * samp->coefficient_width * sizeof(float) + sizeof(float); // extra sizeof(float) is padding + + samp->gather_prescatter_contributors = 0; + samp->gather_prescatter_coefficients = 0; + if ( samp->is_gather == 0 ) + { + samp->gather_prescatter_coefficient_width = samp->filter_pixel_width; + samp->gather_prescatter_num_contributors = stbir__get_contributors(samp, 2); + samp->gather_prescatter_contributors_size = samp->gather_prescatter_num_contributors * sizeof(stbir__contributors); + samp->gather_prescatter_coefficients_size = samp->gather_prescatter_num_contributors * samp->gather_prescatter_coefficient_width * sizeof(float); + } +} + +static void stbir__get_conservative_extents( stbir__sampler * samp, stbir__contributors * range, void * user_data ) +{ + float scale = samp->scale_info.scale; + float out_shift = samp->scale_info.pixel_shift; + stbir__support_callback * support = samp->filter_support; + int input_full_size = samp->scale_info.input_full_size; + stbir_edge edge = samp->edge; + float inv_scale = samp->scale_info.inv_scale; + + STBIR_ASSERT( samp->is_gather != 0 ); + + if ( samp->is_gather == 1 ) + { + int in_first_pixel, in_last_pixel; + float out_filter_radius = support(inv_scale, user_data) * scale; + + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0.5, out_filter_radius, inv_scale, out_shift, input_full_size, edge ); + range->n0 = in_first_pixel; + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, ( (float)(samp->scale_info.output_sub_size-1) ) + 0.5f, out_filter_radius, inv_scale, out_shift, input_full_size, edge ); + range->n1 = in_last_pixel; + } + else if ( samp->is_gather == 2 ) // downsample gather, refine + { + float in_pixels_radius = support(scale, user_data) * inv_scale; + int filter_pixel_margin = samp->filter_pixel_margin; + int output_sub_size = samp->scale_info.output_sub_size; + int input_end; + int n; + int in_first_pixel, in_last_pixel; + + // get a conservative area of the input range + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0, 0, inv_scale, out_shift, input_full_size, edge ); + range->n0 = in_first_pixel; + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, (float)output_sub_size, 0, inv_scale, out_shift, input_full_size, edge ); + range->n1 = in_last_pixel; + + // now go through the margin to the start of area to find bottom + n = range->n0 + 1; + input_end = -filter_pixel_margin; + while( n >= input_end ) + { + int out_first_pixel, out_last_pixel; + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size ); + if ( out_first_pixel > out_last_pixel ) + break; + + if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) ) + range->n0 = n; + --n; + } + + // now go through the end of the area through the margin to find top + n = range->n1 - 1; + input_end = n + 1 + filter_pixel_margin; + while( n <= input_end ) + { + int out_first_pixel, out_last_pixel; + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size ); + if ( out_first_pixel > out_last_pixel ) + break; + if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) ) + range->n1 = n; + ++n; + } + } + + if ( samp->edge == STBIR_EDGE_WRAP ) + { + // if we are wrapping, and we are very close to the image size (so the edges might merge), just use the scanline up to the edge + if ( ( range->n0 > 0 ) && ( range->n1 >= input_full_size ) ) + { + int marg = range->n1 - input_full_size + 1; + if ( ( marg + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= range->n0 ) + range->n0 = 0; + } + if ( ( range->n0 < 0 ) && ( range->n1 < (input_full_size-1) ) ) + { + int marg = -range->n0; + if ( ( input_full_size - marg - STBIR__MERGE_RUNS_PIXEL_THRESHOLD - 1 ) <= range->n1 ) + range->n1 = input_full_size - 1; + } + } + else + { + // for non-edge-wrap modes, we never read over the edge, so clamp + if ( range->n0 < 0 ) + range->n0 = 0; + if ( range->n1 >= input_full_size ) + range->n1 = input_full_size - 1; + } +} + +static void stbir__get_split_info( stbir__per_split_info* split_info, int splits, int output_height, int vertical_pixel_margin, int input_full_height ) +{ + int i, cur; + int left = output_height; + + cur = 0; + for( i = 0 ; i < splits ; i++ ) + { + int each; + split_info[i].start_output_y = cur; + each = left / ( splits - i ); + split_info[i].end_output_y = cur + each; + cur += each; + left -= each; + + // scatter range (updated to minimum as you run it) + split_info[i].start_input_y = -vertical_pixel_margin; + split_info[i].end_input_y = input_full_height + vertical_pixel_margin; + } +} + +static void stbir__free_internal_mem( stbir__info *info ) +{ + #define STBIR__FREE_AND_CLEAR( ptr ) { if ( ptr ) { void * p = (ptr); (ptr) = 0; STBIR_FREE( p, info->user_data); } } + + if ( info ) + { + #ifndef STBIR__SEPARATE_ALLOCATIONS + STBIR__FREE_AND_CLEAR( info->alloced_mem ); + #else + int i,j; + + if ( ( info->vertical.gather_prescatter_contributors ) && ( (void*)info->vertical.gather_prescatter_contributors != (void*)info->split_info[0].decode_buffer ) ) + { + STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_coefficients ); + STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_contributors ); + } + for( i = 0 ; i < info->splits ; i++ ) + { + for( j = 0 ; j < info->alloc_ring_buffer_num_entries ; j++ ) + { + #ifdef STBIR_SIMD8 + if ( info->effective_channels == 3 ) + --info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer + #endif + STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers[j] ); + } + + #ifdef STBIR_SIMD8 + if ( info->effective_channels == 3 ) + --info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer + #endif + STBIR__FREE_AND_CLEAR( info->split_info[i].decode_buffer ); + STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers ); + STBIR__FREE_AND_CLEAR( info->split_info[i].vertical_buffer ); + } + STBIR__FREE_AND_CLEAR( info->split_info ); + if ( info->vertical.coefficients != info->horizontal.coefficients ) + { + STBIR__FREE_AND_CLEAR( info->vertical.coefficients ); + STBIR__FREE_AND_CLEAR( info->vertical.contributors ); + } + 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 ); + #endif + } + + #undef STBIR__FREE_AND_CLEAR +} + +static int stbir__get_max_split( int splits, int height ) +{ + int i; + int max = 0; + + for( i = 0 ; i < splits ; i++ ) + { + int each = height / ( splits - i ); + if ( each > max ) + max = each; + height -= each; + } + return max; +} + +static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_n_coeffs_funcs[8] = +{ + 0, stbir__horizontal_gather_1_channels_with_n_coeffs_funcs, stbir__horizontal_gather_2_channels_with_n_coeffs_funcs, stbir__horizontal_gather_3_channels_with_n_coeffs_funcs, stbir__horizontal_gather_4_channels_with_n_coeffs_funcs, 0,0, stbir__horizontal_gather_7_channels_with_n_coeffs_funcs +}; + +static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_channels_funcs[8] = +{ + 0, stbir__horizontal_gather_1_channels_funcs, stbir__horizontal_gather_2_channels_funcs, stbir__horizontal_gather_3_channels_funcs, stbir__horizontal_gather_4_channels_funcs, 0,0, stbir__horizontal_gather_7_channels_funcs +}; + +// there are six resize classifications: 0 == vertical scatter, 1 == vertical gather < 1x scale, 2 == vertical gather 1x-2x scale, 4 == vertical gather < 3x scale, 4 == vertical gather > 3x scale, 5 == <=4 pixel height, 6 == <=4 pixel wide column +#define STBIR_RESIZE_CLASSIFICATIONS 8 + +static float stbir__compute_weights[5][STBIR_RESIZE_CLASSIFICATIONS][4]= // 5 = 0=1chan, 1=2chan, 2=3chan, 3=4chan, 4=7chan +{ + { + { 1.00000f, 1.00000f, 0.31250f, 1.00000f }, + { 0.56250f, 0.59375f, 0.00000f, 0.96875f }, + { 1.00000f, 0.06250f, 0.00000f, 1.00000f }, + { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.03125f }, + }, { + { 0.00000f, 0.84375f, 0.00000f, 0.03125f }, + { 0.09375f, 0.93750f, 0.00000f, 0.78125f }, + { 0.87500f, 0.21875f, 0.00000f, 0.96875f }, + { 0.09375f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.53125f }, + }, { + { 0.00000f, 0.53125f, 0.00000f, 0.03125f }, + { 0.06250f, 0.96875f, 0.00000f, 0.53125f }, + { 0.87500f, 0.18750f, 0.00000f, 0.93750f }, + { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.56250f }, + }, { + { 0.00000f, 0.50000f, 0.00000f, 0.71875f }, + { 0.06250f, 0.84375f, 0.00000f, 0.87500f }, + { 1.00000f, 0.50000f, 0.50000f, 0.96875f }, + { 1.00000f, 0.09375f, 0.31250f, 0.50000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 1.00000f, 0.03125f, 0.03125f, 0.53125f }, + { 0.18750f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.03125f, 0.18750f }, + }, { + { 0.00000f, 0.59375f, 0.00000f, 0.96875f }, + { 0.06250f, 0.81250f, 0.06250f, 0.59375f }, + { 0.75000f, 0.43750f, 0.12500f, 0.96875f }, + { 0.87500f, 0.06250f, 0.18750f, 0.43750f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.15625f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.03125f, 0.34375f }, + } +}; + +// structure that allow us to query and override info for training the costs +typedef struct STBIR__V_FIRST_INFO +{ + double v_cost, h_cost; + int control_v_first; // 0 = no control, 1 = force hori, 2 = force vert + int v_first; + int v_resize_classification; + int is_gather; +} STBIR__V_FIRST_INFO; + +#ifdef STBIR__V_FIRST_INFO_BUFFER +static STBIR__V_FIRST_INFO STBIR__V_FIRST_INFO_BUFFER = {0}; +#define STBIR__V_FIRST_INFO_POINTER &STBIR__V_FIRST_INFO_BUFFER +#else +#define STBIR__V_FIRST_INFO_POINTER 0 +#endif + +// Figure out whether to scale along the horizontal or vertical first. +// This only *super* important when you are scaling by a massively +// different amount in the vertical vs the horizontal (for example, if +// you are scaling by 2x in the width, and 0.5x in the height, then you +// want to do the vertical scale first, because it's around 3x faster +// in that order. +// +// In more normal circumstances, this makes a 20-40% differences, so +// it's good to get right, but not critical. The normal way that you +// decide which direction goes first is just figuring out which +// direction does more multiplies. But with modern CPUs with their +// fancy caches and SIMD and high IPC abilities, so there's just a lot +// more that goes into it. +// +// My handwavy sort of solution is to have an app that does a whole +// bunch of timing for both vertical and horizontal first modes, +// and then another app that can read lots of these timing files +// and try to search for the best weights to use. Dotimings.c +// is the app that does a bunch of timings, and vf_train.c is the +// app that solves for the best weights (and shows how well it +// does currently). + +static int stbir__should_do_vertical_first( float weights_table[STBIR_RESIZE_CLASSIFICATIONS][4], int horizontal_filter_pixel_width, float horizontal_scale, int horizontal_output_size, int vertical_filter_pixel_width, float vertical_scale, int vertical_output_size, int is_gather, STBIR__V_FIRST_INFO * info ) +{ + double v_cost, h_cost; + float * weights; + int vertical_first; + int v_classification; + + // categorize the resize into buckets + if ( ( vertical_output_size <= 4 ) || ( horizontal_output_size <= 4 ) ) + v_classification = ( vertical_output_size < horizontal_output_size ) ? 6 : 7; + else if ( vertical_scale <= 1.0f ) + v_classification = ( is_gather ) ? 1 : 0; + else if ( vertical_scale <= 2.0f) + v_classification = 2; + else if ( vertical_scale <= 3.0f) + v_classification = 3; + else if ( vertical_scale <= 4.0f) + v_classification = 5; + else + v_classification = 6; + + // use the right weights + weights = weights_table[ v_classification ]; + + // this is the costs when you don't take into account modern CPUs with high ipc and simd and caches - wish we had a better estimate + h_cost = (float)horizontal_filter_pixel_width * weights[0] + horizontal_scale * (float)vertical_filter_pixel_width * weights[1]; + v_cost = (float)vertical_filter_pixel_width * weights[2] + vertical_scale * (float)horizontal_filter_pixel_width * weights[3]; + + // use computation estimate to decide vertical first or not + vertical_first = ( v_cost <= h_cost ) ? 1 : 0; + + // save these, if requested + if ( info ) + { + info->h_cost = h_cost; + info->v_cost = v_cost; + info->v_resize_classification = v_classification; + info->v_first = vertical_first; + info->is_gather = is_gather; + } + + // and this allows us to override everything for testing (see dotiming.c) + if ( ( info ) && ( info->control_v_first ) ) + vertical_first = ( info->control_v_first == 2 ) ? 1 : 0; + + return vertical_first; +} + +// layout lookups - must match stbir_internal_pixel_layout +static unsigned char stbir__pixel_channels[] = { + 1,2,3,3,4, // 1ch, 2ch, rgb, bgr, 4ch + 4,4,4,4,2,2, // RGBA,BGRA,ARGB,ABGR,RA,AR + 4,4,4,4,2,2, // RGBA_PM,BGRA_PM,ARGB_PM,ABGR_PM,RA_PM,AR_PM +}; + +// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types +// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible +static stbir_internal_pixel_layout stbir__pixel_layout_convert_public_to_internal[] = { + STBIRI_BGR, STBIRI_1CHANNEL, STBIRI_2CHANNEL, STBIRI_RGB, STBIRI_RGBA, + STBIRI_4CHANNEL, STBIRI_BGRA, STBIRI_ARGB, STBIRI_ABGR, STBIRI_RA, STBIRI_AR, + STBIRI_RGBA_PM, STBIRI_BGRA_PM, STBIRI_ARGB_PM, STBIRI_ABGR_PM, STBIRI_RA_PM, STBIRI_AR_PM, +}; + +static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sampler * horizontal, stbir__sampler * vertical, stbir__contributors * conservative, stbir_pixel_layout input_pixel_layout_public, stbir_pixel_layout output_pixel_layout_public, int splits, int new_x, int new_y, int fast_alpha, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO ) +{ + static char stbir_channel_count_index[8]={ 9,0,1,2, 3,9,9,4 }; + + stbir__info * info = 0; + void * alloced = 0; + int alloced_total = 0; + int vertical_first; + int decode_buffer_size, ring_buffer_length_bytes, ring_buffer_size, vertical_buffer_size, alloc_ring_buffer_num_entries; + + int alpha_weighting_type = 0; // 0=none, 1=simple, 2=fancy + int conservative_split_output_size = stbir__get_max_split( splits, vertical->scale_info.output_sub_size ); + stbir_internal_pixel_layout input_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ input_pixel_layout_public ]; + stbir_internal_pixel_layout output_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ output_pixel_layout_public ]; + int channels = stbir__pixel_channels[ input_pixel_layout ]; + int effective_channels = channels; + + // first figure out what type of alpha weighting to use (if any) + if ( ( horizontal->filter_enum != STBIR_FILTER_POINT_SAMPLE ) || ( vertical->filter_enum != STBIR_FILTER_POINT_SAMPLE ) ) // no alpha weighting on point sampling + { + if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) ) + { + if ( fast_alpha ) + { + alpha_weighting_type = 4; + } + else + { + static int fancy_alpha_effective_cnts[6] = { 7, 7, 7, 7, 3, 3 }; + alpha_weighting_type = 2; + effective_channels = fancy_alpha_effective_cnts[ input_pixel_layout - STBIRI_RGBA ]; + } + } + else if ( ( input_pixel_layout >= STBIRI_RGBA_PM ) && ( input_pixel_layout <= STBIRI_AR_PM ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) ) + { + // input premult, output non-premult + alpha_weighting_type = 3; + } + else if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA_PM ) && ( output_pixel_layout <= STBIRI_AR_PM ) ) + { + // input non-premult, output premult + alpha_weighting_type = 1; + } + } + + // channel in and out count must match currently + if ( channels != stbir__pixel_channels[ output_pixel_layout ] ) + return 0; + + // get vertical first + vertical_first = stbir__should_do_vertical_first( stbir__compute_weights[ (int)stbir_channel_count_index[ effective_channels ] ], horizontal->filter_pixel_width, horizontal->scale_info.scale, horizontal->scale_info.output_sub_size, vertical->filter_pixel_width, vertical->scale_info.scale, vertical->scale_info.output_sub_size, vertical->is_gather, STBIR__V_FIRST_INFO_POINTER ); + + // sometimes read one float off in some of the unrolled loops (with a weight of zero coeff, so it doesn't have an effect) + decode_buffer_size = ( conservative->n1 - conservative->n0 + 1 ) * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + +#if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8) + if ( effective_channels == 3 ) + decode_buffer_size += sizeof(float); // avx in 3 channel mode needs one float at the start of the buffer (only with separate allocations) +#endif + + ring_buffer_length_bytes = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + + // if we do vertical first, the ring buffer holds a whole decoded line + if ( vertical_first ) + ring_buffer_length_bytes = ( decode_buffer_size + 15 ) & ~15; + + if ( ( ring_buffer_length_bytes & 4095 ) == 0 ) ring_buffer_length_bytes += 64*3; // avoid 4k alias + + // One extra entry because floating point precision problems sometimes cause an extra to be necessary. + alloc_ring_buffer_num_entries = vertical->filter_pixel_width + 1; + + // we never need more ring buffer entries than the scanlines we're outputting when in scatter mode + if ( ( !vertical->is_gather ) && ( alloc_ring_buffer_num_entries > conservative_split_output_size ) ) + alloc_ring_buffer_num_entries = conservative_split_output_size; + + ring_buffer_size = alloc_ring_buffer_num_entries * ring_buffer_length_bytes; + + // The vertical buffer is used differently, depending on whether we are scattering + // the vertical scanlines, or gathering them. + // If scattering, it's used at the temp buffer to accumulate each output. + // If gathering, it's just the output buffer. + vertical_buffer_size = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + + // we make two passes through this loop, 1st to add everything up, 2nd to allocate and init + for(;;) + { + int i; + void * advance_mem = alloced; + int copy_horizontal = 0; + stbir__sampler * possibly_use_horizontal_for_pivot = 0; + +#ifdef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__NEXT_PTR( ptr, size, ntype ) if ( alloced ) { void * p = STBIR_MALLOC( size, user_data); if ( p == 0 ) { stbir__free_internal_mem( info ); return 0; } (ptr) = (ntype*)p; } +#else + #define STBIR__NEXT_PTR( ptr, size, ntype ) advance_mem = (void*) ( ( ((size_t)advance_mem) + 15 ) & ~15 ); if ( alloced ) ptr = (ntype*)advance_mem; advance_mem = ((char*)advance_mem) + (size); +#endif + + STBIR__NEXT_PTR( info, sizeof( stbir__info ), stbir__info ); + + STBIR__NEXT_PTR( info->split_info, sizeof( stbir__per_split_info ) * splits, stbir__per_split_info ); + + if ( info ) + { + static stbir__alpha_weight_func * fancy_alpha_weights[6] = { stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_2ch, stbir__fancy_alpha_weight_2ch }; + static stbir__alpha_unweight_func * fancy_alpha_unweights[6] = { stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_2ch, stbir__fancy_alpha_unweight_2ch }; + static stbir__alpha_weight_func * simple_alpha_weights[6] = { stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_2ch, stbir__simple_alpha_weight_2ch }; + static stbir__alpha_unweight_func * simple_alpha_unweights[6] = { stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_2ch, stbir__simple_alpha_unweight_2ch }; + + // initialize info fields + info->alloced_mem = alloced; + info->alloced_total = alloced_total; + + info->channels = channels; + info->effective_channels = effective_channels; + + info->offset_x = new_x; + info->offset_y = new_y; + info->alloc_ring_buffer_num_entries = alloc_ring_buffer_num_entries; + info->ring_buffer_num_entries = 0; + info->ring_buffer_length_bytes = ring_buffer_length_bytes; + info->splits = splits; + info->vertical_first = vertical_first; + + info->input_pixel_layout_internal = input_pixel_layout; + info->output_pixel_layout_internal = output_pixel_layout; + + // setup alpha weight functions + info->alpha_weight = 0; + info->alpha_unweight = 0; + + // handle alpha weighting functions and overrides + if ( alpha_weighting_type == 2 ) + { + // high quality alpha multiplying on the way in, dividing on the way out + info->alpha_weight = fancy_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + info->alpha_unweight = fancy_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 4 ) + { + // fast alpha multiplying on the way in, dividing on the way out + info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 1 ) + { + // fast alpha on the way in, leave in premultiplied form on way out + info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 3 ) + { + // incoming is premultiplied, fast alpha dividing on the way out - non-premultiplied output + info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + + // handle 3-chan color flipping, using the alpha weight path + if ( ( ( input_pixel_layout == STBIRI_RGB ) && ( output_pixel_layout == STBIRI_BGR ) ) || + ( ( input_pixel_layout == STBIRI_BGR ) && ( output_pixel_layout == STBIRI_RGB ) ) ) + { + // do the flipping on the smaller of the two ends + if ( horizontal->scale_info.scale < 1.0f ) + info->alpha_unweight = stbir__simple_flip_3ch; + else + info->alpha_weight = stbir__simple_flip_3ch; + } + + } + + // get all the per-split buffers + for( i = 0 ; i < splits ; i++ ) + { + STBIR__NEXT_PTR( info->split_info[i].decode_buffer, decode_buffer_size, float ); + +#ifdef STBIR__SEPARATE_ALLOCATIONS + + #ifdef STBIR_SIMD8 + if ( ( info ) && ( effective_channels == 3 ) ) + ++info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer + #endif + + STBIR__NEXT_PTR( info->split_info[i].ring_buffers, alloc_ring_buffer_num_entries * sizeof(float*), float* ); + { + int j; + for( j = 0 ; j < alloc_ring_buffer_num_entries ; j++ ) + { + STBIR__NEXT_PTR( info->split_info[i].ring_buffers[j], ring_buffer_length_bytes, float ); + #ifdef STBIR_SIMD8 + if ( ( info ) && ( effective_channels == 3 ) ) + ++info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer + #endif + } + } +#else + STBIR__NEXT_PTR( info->split_info[i].ring_buffer, ring_buffer_size, float ); +#endif + STBIR__NEXT_PTR( info->split_info[i].vertical_buffer, vertical_buffer_size, float ); + } + + // alloc memory for to-be-pivoted coeffs (if necessary) + if ( vertical->is_gather == 0 ) + { + int both; + int temp_mem_amt; + + // when in vertical scatter mode, we first build the coefficients in gather mode, and then pivot after, + // that means we need two buffers, so we try to use the decode buffer and ring buffer for this. if that + // is too small, we just allocate extra memory to use as this temp. + + both = vertical->gather_prescatter_contributors_size + vertical->gather_prescatter_coefficients_size; + +#ifdef STBIR__SEPARATE_ALLOCATIONS + temp_mem_amt = decode_buffer_size; +#else + temp_mem_amt = ( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * splits; +#endif + if ( temp_mem_amt >= both ) + { + if ( info ) + { + vertical->gather_prescatter_contributors = (stbir__contributors*)info->split_info[0].decode_buffer; + vertical->gather_prescatter_coefficients = (float*) ( ( (char*)info->split_info[0].decode_buffer ) + vertical->gather_prescatter_contributors_size ); + } + } + else + { + // ring+decode memory is too small, so allocate temp memory + STBIR__NEXT_PTR( vertical->gather_prescatter_contributors, vertical->gather_prescatter_contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( vertical->gather_prescatter_coefficients, vertical->gather_prescatter_coefficients_size, float ); + } + } + + STBIR__NEXT_PTR( horizontal->contributors, horizontal->contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( horizontal->coefficients, horizontal->coefficients_size, float ); + + // are the two filters identical?? (happens a lot with mipmap generation) + if ( ( horizontal->filter_kernel == vertical->filter_kernel ) && ( horizontal->filter_support == vertical->filter_support ) && ( horizontal->edge == vertical->edge ) && ( horizontal->scale_info.output_sub_size == vertical->scale_info.output_sub_size ) ) + { + float diff_scale = horizontal->scale_info.scale - vertical->scale_info.scale; + float diff_shift = horizontal->scale_info.pixel_shift - vertical->scale_info.pixel_shift; + if ( diff_scale < 0.0f ) diff_scale = -diff_scale; + if ( diff_shift < 0.0f ) diff_shift = -diff_shift; + if ( ( diff_scale <= stbir__small_float ) && ( diff_shift <= stbir__small_float ) ) + { + if ( horizontal->is_gather == vertical->is_gather ) + { + copy_horizontal = 1; + goto no_vert_alloc; + } + // everything matches, but vertical is scatter, horizontal is gather, use horizontal coeffs for vertical pivot coeffs + possibly_use_horizontal_for_pivot = horizontal; + } + } + + STBIR__NEXT_PTR( vertical->contributors, vertical->contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( vertical->coefficients, vertical->coefficients_size, float ); + + no_vert_alloc: + + if ( info ) + { + STBIR_PROFILE_BUILD_START( horizontal ); + + stbir__calculate_filters( horizontal, 0, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + + // setup the horizontal gather functions + // start with defaulting to the n_coeffs functions (specialized on channels and remnant leftover) + info->horizontal_gather_channels = stbir__horizontal_gather_n_coeffs_funcs[ effective_channels ][ horizontal->extent_info.widest & 3 ]; + // but if the number of coeffs <= 12, use another set of special cases. <=12 coeffs is any enlarging resize, or shrinking resize down to about 1/3 size + if ( horizontal->extent_info.widest <= 12 ) + info->horizontal_gather_channels = stbir__horizontal_gather_channels_funcs[ effective_channels ][ horizontal->extent_info.widest - 1 ]; + + info->scanline_extents.conservative.n0 = conservative->n0; + info->scanline_extents.conservative.n1 = conservative->n1; + + // get exact extents + stbir__get_extents( horizontal, &info->scanline_extents ); + + // pack the horizontal coeffs + horizontal->coefficient_width = stbir__pack_coefficients(horizontal->num_contributors, horizontal->contributors, horizontal->coefficients, horizontal->coefficient_width, horizontal->extent_info.widest, info->scanline_extents.conservative.n1 + 1 ); + + STBIR_MEMCPY( &info->horizontal, horizontal, sizeof( stbir__sampler ) ); + + STBIR_PROFILE_BUILD_END( horizontal ); + + if ( copy_horizontal ) + { + STBIR_MEMCPY( &info->vertical, horizontal, sizeof( stbir__sampler ) ); + } + else + { + STBIR_PROFILE_BUILD_START( vertical ); + + stbir__calculate_filters( vertical, possibly_use_horizontal_for_pivot, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + STBIR_MEMCPY( &info->vertical, vertical, sizeof( stbir__sampler ) ); + + STBIR_PROFILE_BUILD_END( vertical ); + } + + // setup the vertical split ranges + stbir__get_split_info( info->split_info, info->splits, info->vertical.scale_info.output_sub_size, info->vertical.filter_pixel_margin, info->vertical.scale_info.input_full_size ); + + // now we know precisely how many entries we need + info->ring_buffer_num_entries = info->vertical.extent_info.widest; + + // we never need more ring buffer entries than the scanlines we're outputting + if ( ( !info->vertical.is_gather ) && ( info->ring_buffer_num_entries > conservative_split_output_size ) ) + info->ring_buffer_num_entries = conservative_split_output_size; + STBIR_ASSERT( info->ring_buffer_num_entries <= info->alloc_ring_buffer_num_entries ); + + // a few of the horizontal gather functions read one dword past the end (but mask it out), so put in a normal value so no snans or denormals accidentally sneak in + for( i = 0 ; i < splits ; i++ ) + { + int width, ofs; + + // find the right most span + if ( info->scanline_extents.spans[0].n1 > info->scanline_extents.spans[1].n1 ) + width = info->scanline_extents.spans[0].n1 - info->scanline_extents.spans[0].n0; + else + width = info->scanline_extents.spans[1].n1 - info->scanline_extents.spans[1].n0; + + // this calc finds the exact end of the decoded scanline for all filter modes. + // usually this is just the width * effective channels. But we have to account + // for the area to the left of the scanline for wrap filtering and alignment, this + // is stored as a negative value in info->scanline_extents.conservative.n0. Next, + // we need to skip the exact size of the right hand size filter area (again for + // wrap mode), this is in info->scanline_extents.edge_sizes[1]). + ofs = ( width + 1 - info->scanline_extents.conservative.n0 + info->scanline_extents.edge_sizes[1] ) * effective_channels; + + // place a known, but numerically valid value in the decode buffer + info->split_info[i].decode_buffer[ ofs ] = 9999.0f; + + // if vertical filtering first, place a known, but numerically valid value in the all + // of the ring buffer accumulators + if ( vertical_first ) + { + int j; + for( j = 0; j < info->ring_buffer_num_entries ; j++ ) + { + stbir__get_ring_buffer_entry( info, info->split_info + i, j )[ ofs ] = 9999.0f; + } + } + } + } + + #undef STBIR__NEXT_PTR + + + // is this the first time through loop? + if ( info == 0 ) + { + alloced_total = (int) ( 15 + (size_t)advance_mem ); + alloced = STBIR_MALLOC( alloced_total, user_data ); + if ( alloced == 0 ) + return 0; + } + else + return info; // success + } +} + +static int stbir__perform_resize( stbir__info const * info, int split_start, int split_count ) +{ + stbir__per_split_info * split_info = info->split_info + split_start; + + STBIR_PROFILE_CLEAR_EXTRAS(); + + STBIR_PROFILE_FIRST_START( looping ); + if (info->vertical.is_gather) + stbir__vertical_gather_loop( info, split_info, split_count ); + else + stbir__vertical_scatter_loop( info, split_info, split_count ); + STBIR_PROFILE_END( looping ); + + return 1; +} + +static void stbir__update_info_from_resize( stbir__info * info, STBIR_RESIZE * resize ) +{ + static stbir__decode_pixels_func * decode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + /* 1ch-4ch */ stbir__decode_uint8_srgb, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear, + }; + + static stbir__decode_pixels_func * decode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + { /* RGBA */ stbir__decode_uint8_srgb4_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear }, + { /* BGRA */ stbir__decode_uint8_srgb4_linearalpha_BGRA, stbir__decode_uint8_srgb_BGRA, 0, stbir__decode_float_linear_BGRA, stbir__decode_half_float_linear_BGRA }, + { /* ARGB */ stbir__decode_uint8_srgb4_linearalpha_ARGB, stbir__decode_uint8_srgb_ARGB, 0, stbir__decode_float_linear_ARGB, stbir__decode_half_float_linear_ARGB }, + { /* ABGR */ stbir__decode_uint8_srgb4_linearalpha_ABGR, stbir__decode_uint8_srgb_ABGR, 0, stbir__decode_float_linear_ABGR, stbir__decode_half_float_linear_ABGR }, + { /* RA */ stbir__decode_uint8_srgb2_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear }, + { /* AR */ stbir__decode_uint8_srgb2_linearalpha_AR, stbir__decode_uint8_srgb_AR, 0, stbir__decode_float_linear_AR, stbir__decode_half_float_linear_AR }, + }; + + static stbir__decode_pixels_func * decode_simple_scaled_or_not[2][2]= + { + { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear }, + }; + + static stbir__decode_pixels_func * decode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]= + { + { /* RGBA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } }, + { /* BGRA */ { stbir__decode_uint8_linear_scaled_BGRA, stbir__decode_uint8_linear_BGRA }, { stbir__decode_uint16_linear_scaled_BGRA, stbir__decode_uint16_linear_BGRA } }, + { /* ARGB */ { stbir__decode_uint8_linear_scaled_ARGB, stbir__decode_uint8_linear_ARGB }, { stbir__decode_uint16_linear_scaled_ARGB, stbir__decode_uint16_linear_ARGB } }, + { /* ABGR */ { stbir__decode_uint8_linear_scaled_ABGR, stbir__decode_uint8_linear_ABGR }, { stbir__decode_uint16_linear_scaled_ABGR, stbir__decode_uint16_linear_ABGR } }, + { /* RA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } }, + { /* AR */ { stbir__decode_uint8_linear_scaled_AR, stbir__decode_uint8_linear_AR }, { stbir__decode_uint16_linear_scaled_AR, stbir__decode_uint16_linear_AR } } + }; + + static stbir__encode_pixels_func * encode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + /* 1ch-4ch */ stbir__encode_uint8_srgb, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear, + }; + + static stbir__encode_pixels_func * encode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + { /* RGBA */ stbir__encode_uint8_srgb4_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear }, + { /* BGRA */ stbir__encode_uint8_srgb4_linearalpha_BGRA, stbir__encode_uint8_srgb_BGRA, 0, stbir__encode_float_linear_BGRA, stbir__encode_half_float_linear_BGRA }, + { /* ARGB */ stbir__encode_uint8_srgb4_linearalpha_ARGB, stbir__encode_uint8_srgb_ARGB, 0, stbir__encode_float_linear_ARGB, stbir__encode_half_float_linear_ARGB }, + { /* ABGR */ stbir__encode_uint8_srgb4_linearalpha_ABGR, stbir__encode_uint8_srgb_ABGR, 0, stbir__encode_float_linear_ABGR, stbir__encode_half_float_linear_ABGR }, + { /* RA */ stbir__encode_uint8_srgb2_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear }, + { /* AR */ stbir__encode_uint8_srgb2_linearalpha_AR, stbir__encode_uint8_srgb_AR, 0, stbir__encode_float_linear_AR, stbir__encode_half_float_linear_AR } + }; + + static stbir__encode_pixels_func * encode_simple_scaled_or_not[2][2]= + { + { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear }, + }; + + static stbir__encode_pixels_func * encode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]= + { + { /* RGBA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } }, + { /* BGRA */ { stbir__encode_uint8_linear_scaled_BGRA, stbir__encode_uint8_linear_BGRA }, { stbir__encode_uint16_linear_scaled_BGRA, stbir__encode_uint16_linear_BGRA } }, + { /* ARGB */ { stbir__encode_uint8_linear_scaled_ARGB, stbir__encode_uint8_linear_ARGB }, { stbir__encode_uint16_linear_scaled_ARGB, stbir__encode_uint16_linear_ARGB } }, + { /* ABGR */ { stbir__encode_uint8_linear_scaled_ABGR, stbir__encode_uint8_linear_ABGR }, { stbir__encode_uint16_linear_scaled_ABGR, stbir__encode_uint16_linear_ABGR } }, + { /* RA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } }, + { /* AR */ { stbir__encode_uint8_linear_scaled_AR, stbir__encode_uint8_linear_AR }, { stbir__encode_uint16_linear_scaled_AR, stbir__encode_uint16_linear_AR } } + }; + + stbir__decode_pixels_func * decode_pixels = 0; + stbir__encode_pixels_func * encode_pixels = 0; + stbir_datatype input_type, output_type; + + input_type = resize->input_data_type; + output_type = resize->output_data_type; + info->input_data = resize->input_pixels; + info->input_stride_bytes = resize->input_stride_in_bytes; + info->output_stride_bytes = resize->output_stride_in_bytes; + + // if we're completely point sampling, then we can turn off SRGB + if ( ( info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( info->vertical.filter_enum == STBIR_FILTER_POINT_SAMPLE ) ) + { + if ( ( ( input_type == STBIR_TYPE_UINT8_SRGB ) || ( input_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) && + ( ( output_type == STBIR_TYPE_UINT8_SRGB ) || ( output_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) ) + { + input_type = STBIR_TYPE_UINT8; + output_type = STBIR_TYPE_UINT8; + } + } + + // recalc the output and input strides + if ( info->input_stride_bytes == 0 ) + info->input_stride_bytes = info->channels * info->horizontal.scale_info.input_full_size * stbir__type_size[input_type]; + + if ( info->output_stride_bytes == 0 ) + info->output_stride_bytes = info->channels * info->horizontal.scale_info.output_sub_size * stbir__type_size[output_type]; + + // calc offset + info->output_data = ( (char*) resize->output_pixels ) + ( (ptrdiff_t) info->offset_y * (ptrdiff_t) resize->output_stride_in_bytes ) + ( info->offset_x * info->channels * stbir__type_size[output_type] ); + + info->in_pixels_cb = resize->input_cb; + info->user_data = resize->user_data; + info->out_pixels_cb = resize->output_cb; + + // setup the input format converters + if ( ( input_type == STBIR_TYPE_UINT8 ) || ( input_type == STBIR_TYPE_UINT16 ) ) + { + int non_scaled = 0; + + // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16) + if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual) + if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) ) + non_scaled = 1; + + if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL ) + decode_pixels = decode_simple_scaled_or_not[ input_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + else + decode_pixels = decode_alphas_scaled_or_not[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + } + else + { + if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL ) + decode_pixels = decode_simple[ input_type - STBIR_TYPE_UINT8_SRGB ]; + else + decode_pixels = decode_alphas[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type - STBIR_TYPE_UINT8_SRGB ]; + } + + // setup the output format converters + if ( ( output_type == STBIR_TYPE_UINT8 ) || ( output_type == STBIR_TYPE_UINT16 ) ) + { + int non_scaled = 0; + + // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16) + if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual) + if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) ) + non_scaled = 1; + + if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL ) + encode_pixels = encode_simple_scaled_or_not[ output_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + else + encode_pixels = encode_alphas_scaled_or_not[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + } + else + { + if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL ) + encode_pixels = encode_simple[ output_type - STBIR_TYPE_UINT8_SRGB ]; + else + encode_pixels = encode_alphas[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type - STBIR_TYPE_UINT8_SRGB ]; + } + + info->input_type = input_type; + info->output_type = output_type; + info->decode_pixels = decode_pixels; + info->encode_pixels = encode_pixels; +} + +static void stbir__clip( int * outx, int * outsubw, int outw, double * u0, double * u1 ) +{ + double per, adj; + int over; + + // do left/top edge + if ( *outx < 0 ) + { + per = ( (double)*outx ) / ( (double)*outsubw ); // is negative + adj = per * ( *u1 - *u0 ); + *u0 -= adj; // increases u0 + *outx = 0; + } + + // do right/bot edge + over = outw - ( *outx + *outsubw ); + if ( over < 0 ) + { + per = ( (double)over ) / ( (double)*outsubw ); // is negative + adj = per * ( *u1 - *u0 ); + *u1 += adj; // decrease u1 + *outsubw = outw - *outx; + } +} + +// converts a double to a rational that has less than one float bit of error (returns 0 if unable to do so) +static int stbir__double_to_rational(double f, stbir_uint32 limit, stbir_uint32 *numer, stbir_uint32 *denom, int limit_denom ) // limit_denom (1) or limit numer (0) +{ + double err; + stbir_uint64 top, bot; + stbir_uint64 numer_last = 0; + stbir_uint64 denom_last = 1; + stbir_uint64 numer_estimate = 1; + stbir_uint64 denom_estimate = 0; + + // scale to past float error range + top = (stbir_uint64)( f * (double)(1 << 25) ); + bot = 1 << 25; + + // keep refining, but usually stops in a few loops - usually 5 for bad cases + for(;;) + { + stbir_uint64 est, temp; + + // hit limit, break out and do best full range estimate + if ( ( ( limit_denom ) ? denom_estimate : numer_estimate ) >= limit ) + break; + + // is the current error less than 1 bit of a float? if so, we're done + if ( denom_estimate ) + { + err = ( (double)numer_estimate / (double)denom_estimate ) - f; + if ( err < 0.0 ) err = -err; + if ( err < ( 1.0 / (double)(1<<24) ) ) + { + // yup, found it + *numer = (stbir_uint32) numer_estimate; + *denom = (stbir_uint32) denom_estimate; + return 1; + } + } + + // no more refinement bits left? break out and do full range estimate + if ( bot == 0 ) + break; + + // gcd the estimate bits + est = top / bot; + temp = top % bot; + top = bot; + bot = temp; + + // move remainders + temp = est * denom_estimate + denom_last; + denom_last = denom_estimate; + denom_estimate = temp; + + // move remainders + temp = est * numer_estimate + numer_last; + numer_last = numer_estimate; + numer_estimate = temp; + } + + // we didn't fine anything good enough for float, use a full range estimate + if ( limit_denom ) + { + numer_estimate= (stbir_uint64)( f * (double)limit + 0.5 ); + denom_estimate = limit; + } + else + { + numer_estimate = limit; + denom_estimate = (stbir_uint64)( ( (double)limit / f ) + 0.5 ); + } + + *numer = (stbir_uint32) numer_estimate; + *denom = (stbir_uint32) denom_estimate; + + err = ( denom_estimate ) ? ( ( (double)(stbir_uint32)numer_estimate / (double)(stbir_uint32)denom_estimate ) - f ) : 1.0; + if ( err < 0.0 ) err = -err; + return ( err < ( 1.0 / (double)(1<<24) ) ) ? 1 : 0; +} + +static int stbir__calculate_region_transform( stbir__scale_info * scale_info, int output_full_range, int * output_offset, int output_sub_range, int input_full_range, double input_s0, double input_s1 ) +{ + double output_range, input_range, output_s, input_s, ratio, scale; + + input_s = input_s1 - input_s0; + + // null area + if ( ( output_full_range == 0 ) || ( input_full_range == 0 ) || + ( output_sub_range == 0 ) || ( input_s <= stbir__small_float ) ) + return 0; + + // are either of the ranges completely out of bounds? + if ( ( *output_offset >= output_full_range ) || ( ( *output_offset + output_sub_range ) <= 0 ) || ( input_s0 >= (1.0f-stbir__small_float) ) || ( input_s1 <= stbir__small_float ) ) + return 0; + + output_range = (double)output_full_range; + input_range = (double)input_full_range; + + output_s = ( (double)output_sub_range) / output_range; + + // figure out the scaling to use + ratio = output_s / input_s; + + // save scale before clipping + scale = ( output_range / input_range ) * ratio; + scale_info->scale = (float)scale; + scale_info->inv_scale = (float)( 1.0 / scale ); + + // clip output area to left/right output edges (and adjust input area) + stbir__clip( output_offset, &output_sub_range, output_full_range, &input_s0, &input_s1 ); + + // recalc input area + input_s = input_s1 - input_s0; + + // after clipping do we have zero input area? + if ( input_s <= stbir__small_float ) + return 0; + + // calculate and store the starting source offsets in output pixel space + scale_info->pixel_shift = (float) ( input_s0 * ratio * output_range ); + + scale_info->scale_is_rational = stbir__double_to_rational( scale, ( scale <= 1.0 ) ? output_full_range : input_full_range, &scale_info->scale_numerator, &scale_info->scale_denominator, ( scale >= 1.0 ) ); + + scale_info->input_full_size = input_full_range; + scale_info->output_sub_size = output_sub_range; + + return 1; +} + + +static void stbir__init_and_set_layout( STBIR_RESIZE * resize, stbir_pixel_layout pixel_layout, stbir_datatype data_type ) +{ + resize->input_cb = 0; + resize->output_cb = 0; + resize->user_data = resize; + resize->samplers = 0; + resize->needs_rebuild = 1; + resize->called_alloc = 0; + resize->horizontal_filter = STBIR_FILTER_DEFAULT; + resize->horizontal_filter_kernel = 0; resize->horizontal_filter_support = 0; + resize->vertical_filter = STBIR_FILTER_DEFAULT; + resize->vertical_filter_kernel = 0; resize->vertical_filter_support = 0; + resize->horizontal_edge = STBIR_EDGE_CLAMP; + resize->vertical_edge = STBIR_EDGE_CLAMP; + resize->input_s0 = 0; resize->input_t0 = 0; resize->input_s1 = 1; resize->input_t1 = 1; + resize->output_subx = 0; resize->output_suby = 0; resize->output_subw = resize->output_w; resize->output_subh = resize->output_h; + resize->input_data_type = data_type; + resize->output_data_type = data_type; + resize->input_pixel_layout_public = pixel_layout; + resize->output_pixel_layout_public = pixel_layout; +} + +STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize, + const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero + stbir_pixel_layout pixel_layout, stbir_datatype data_type ) +{ + resize->input_pixels = input_pixels; + resize->input_w = input_w; + resize->input_h = input_h; + resize->input_stride_in_bytes = input_stride_in_bytes; + resize->output_pixels = output_pixels; + resize->output_w = output_w; + resize->output_h = output_h; + resize->output_stride_in_bytes = output_stride_in_bytes; + resize->fast_alpha = 0; + + stbir__init_and_set_layout( resize, pixel_layout, data_type ); +} + +// You can update parameters any time after resize_init +STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ) // by default, datatype from resize_init +{ + resize->input_data_type = input_type; + resize->output_data_type = output_type; +} + +STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ) // no callbacks by default +{ + resize->input_cb = input_cb; + resize->output_cb = output_cb; +} + +STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ) // pass back STBIR_RESIZE* by default +{ + resize->user_data = user_data; +} + +STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes ) +{ + resize->input_pixels = input_pixels; + resize->input_stride_in_bytes = input_stride_in_bytes; + resize->output_pixels = output_pixels; + resize->output_stride_in_bytes = output_stride_in_bytes; +} + + +STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ) // CLAMP by default +{ + resize->horizontal_edge = horizontal_edge; + resize->vertical_edge = vertical_edge; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ) // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default +{ + resize->horizontal_filter = horizontal_filter; + resize->vertical_filter = vertical_filter; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support ) +{ + resize->horizontal_filter_kernel = horizontal_filter; resize->horizontal_filter_support = horizontal_support; + resize->vertical_filter_kernel = vertical_filter; resize->vertical_filter_support = vertical_support; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ) // sets new pixel layouts +{ + resize->input_pixel_layout_public = input_pixel_layout; + resize->output_pixel_layout_public = output_pixel_layout; + resize->needs_rebuild = 1; + return 1; +} + + +STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ) // sets alpha speed +{ + resize->fast_alpha = non_pma_alpha_speed_over_quality; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ) // sets input region (full region by default) +{ + resize->input_s0 = s0; + resize->input_t0 = t0; + resize->input_s1 = s1; + resize->input_t1 = t1; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( s1 < stbir__small_float ) || ( (s1-s0) < stbir__small_float ) || + ( t1 < stbir__small_float ) || ( (t1-t0) < stbir__small_float ) || + ( s0 > (1.0f-stbir__small_float) ) || + ( t0 > (1.0f-stbir__small_float) ) ) + return 0; + + return 1; +} + +STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets input region (full region by default) +{ + resize->output_subx = subx; + resize->output_suby = suby; + resize->output_subw = subw; + resize->output_subh = subh; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) ) + return 0; + + return 1; +} + +STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets both regions (full regions by default) +{ + double s0, t0, s1, t1; + + s0 = ( (double)subx ) / ( (double)resize->output_w ); + t0 = ( (double)suby ) / ( (double)resize->output_h ); + s1 = ( (double)(subx+subw) ) / ( (double)resize->output_w ); + t1 = ( (double)(suby+subh) ) / ( (double)resize->output_h ); + + resize->input_s0 = s0; + resize->input_t0 = t0; + resize->input_s1 = s1; + resize->input_t1 = t1; + resize->output_subx = subx; + resize->output_suby = suby; + resize->output_subw = subw; + resize->output_subh = subh; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) ) + return 0; + + return 1; +} + +static int stbir__perform_build( STBIR_RESIZE * resize, int splits ) +{ + stbir__contributors conservative = { 0, 0 }; + stbir__sampler horizontal, vertical; + int new_output_subx, new_output_suby; + stbir__info * out_info; + #ifdef STBIR_PROFILE + stbir__info profile_infod; // used to contain building profile info before everything is allocated + stbir__info * profile_info = &profile_infod; + #endif + + // have we already built the samplers? + if ( resize->samplers ) + return 0; + + #define STBIR_RETURN_ERROR_AND_ASSERT( exp ) STBIR_ASSERT( !(exp) ); if (exp) return 0; + STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->horizontal_filter >= STBIR_FILTER_OTHER) + STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->vertical_filter >= STBIR_FILTER_OTHER) + #undef STBIR_RETURN_ERROR_AND_ASSERT + + if ( splits <= 0 ) + return 0; + + STBIR_PROFILE_BUILD_FIRST_START( build ); + + new_output_subx = resize->output_subx; + new_output_suby = resize->output_suby; + + // do horizontal clip and scale calcs + if ( !stbir__calculate_region_transform( &horizontal.scale_info, resize->output_w, &new_output_subx, resize->output_subw, resize->input_w, resize->input_s0, resize->input_s1 ) ) + return 0; + + // do vertical clip and scale calcs + if ( !stbir__calculate_region_transform( &vertical.scale_info, resize->output_h, &new_output_suby, resize->output_subh, resize->input_h, resize->input_t0, resize->input_t1 ) ) + return 0; + + // if nothing to do, just return + if ( ( horizontal.scale_info.output_sub_size == 0 ) || ( vertical.scale_info.output_sub_size == 0 ) ) + return 0; + + stbir__set_sampler(&horizontal, resize->horizontal_filter, resize->horizontal_filter_kernel, resize->horizontal_filter_support, resize->horizontal_edge, &horizontal.scale_info, 1, resize->user_data ); + stbir__get_conservative_extents( &horizontal, &conservative, resize->user_data ); + stbir__set_sampler(&vertical, resize->vertical_filter, resize->horizontal_filter_kernel, resize->vertical_filter_support, resize->vertical_edge, &vertical.scale_info, 0, resize->user_data ); + + if ( ( vertical.scale_info.output_sub_size / splits ) < 4 ) // each split should be a minimum of 4 scanlines (handwavey choice) + { + splits = vertical.scale_info.output_sub_size / 4; + if ( splits == 0 ) splits = 1; + } + + STBIR_PROFILE_BUILD_START( alloc ); + out_info = stbir__alloc_internal_mem_and_build_samplers( &horizontal, &vertical, &conservative, resize->input_pixel_layout_public, resize->output_pixel_layout_public, splits, new_output_subx, new_output_suby, resize->fast_alpha, resize->user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + STBIR_PROFILE_BUILD_END( alloc ); + STBIR_PROFILE_BUILD_END( build ); + + if ( out_info ) + { + resize->splits = splits; + resize->samplers = out_info; + resize->needs_rebuild = 0; + #ifdef STBIR_PROFILE + STBIR_MEMCPY( &out_info->profile, &profile_infod.profile, sizeof( out_info->profile ) ); + #endif + return splits; + } + + return 0; +} + +void stbir_free_samplers( STBIR_RESIZE * resize ) +{ + if ( resize->samplers ) + { + stbir__free_internal_mem( resize->samplers ); + resize->samplers = 0; + resize->called_alloc = 0; + } +} + +STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int splits ) +{ + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + { + if ( resize->samplers ) + stbir_free_samplers( resize ); + + resize->called_alloc = 1; + return stbir__perform_build( resize, splits ); + } + + STBIR_PROFILE_BUILD_CLEAR( resize->samplers ); + + return 1; +} + +STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize ) +{ + return stbir_build_samplers_with_splits( resize, 1 ); +} + +STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize ) +{ + int result; + + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + { + int alloc_state = resize->called_alloc; // remember allocated state + + if ( resize->samplers ) + { + stbir__free_internal_mem( resize->samplers ); + resize->samplers = 0; + } + + if ( !stbir_build_samplers( resize ) ) + return 0; + + resize->called_alloc = alloc_state; + + // if build_samplers succeeded (above), but there are no samplers set, then + // the area to stretch into was zero pixels, so don't do anything and return + // success + if ( resize->samplers == 0 ) + return 1; + } + else + { + // didn't build anything - clear it + STBIR_PROFILE_BUILD_CLEAR( resize->samplers ); + } + + + // update anything that can be changed without recalcing samplers + stbir__update_info_from_resize( resize->samplers, resize ); + + // do resize + result = stbir__perform_resize( resize->samplers, 0, resize->splits ); + + // if we alloced, then free + if ( !resize->called_alloc ) + { + stbir_free_samplers( resize ); + resize->samplers = 0; + } + + return result; +} + +STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count ) +{ + STBIR_ASSERT( resize->samplers ); + + // if we're just doing the whole thing, call full + if ( ( split_start == -1 ) || ( ( split_start == 0 ) && ( split_count == resize->splits ) ) ) + return stbir_resize_extended( resize ); + + // you **must** build samplers first when using split resize + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + return 0; + + if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) ) + return 0; + + // update anything that can be changed without recalcing samplers + stbir__update_info_from_resize( resize->samplers, resize ); + + // do resize + return stbir__perform_resize( resize->samplers, split_start, split_count ); +} + +static int stbir__check_output_stuff( void ** ret_ptr, int * ret_pitch, void * output_pixels, int type_size, int output_w, int output_h, int output_stride_in_bytes, stbir_internal_pixel_layout pixel_layout ) +{ + size_t size; + int pitch; + void * ptr; + + pitch = output_w * type_size * stbir__pixel_channels[ pixel_layout ]; + if ( pitch == 0 ) + return 0; + + if ( output_stride_in_bytes == 0 ) + output_stride_in_bytes = pitch; + + if ( output_stride_in_bytes < pitch ) + return 0; + + size = output_stride_in_bytes * output_h; + if ( size == 0 ) + return 0; + + *ret_ptr = 0; + *ret_pitch = output_stride_in_bytes; + + if ( output_pixels == 0 ) + { + ptr = STBIR_MALLOC( size, 0 ); + if ( ptr == 0 ) + return 0; + + *ret_ptr = ptr; + *ret_pitch = pitch; + } + + return 1; +} + + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + unsigned char * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_UINT8 ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + unsigned char * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_UINT8_SRGB ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + float * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( float ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_FLOAT ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + + +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ) +{ + STBIR_RESIZE resize; + float * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, stbir__type_size[data_type], output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout, data_type ); + + resize.horizontal_edge = edge; + resize.vertical_edge = edge; + resize.horizontal_filter = filter; + resize.vertical_filter = filter; + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + +#ifdef STBIR_PROFILE + +STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) +{ + static char const * bdescriptions[6] = { "Building", "Allocating", "Horizontal sampler", "Vertical sampler", "Coefficient cleanup", "Coefficient piovot" } ; + stbir__info* samp = resize->samplers; + int i; + + typedef int testa[ (STBIR__ARRAY_SIZE( bdescriptions ) == (STBIR__ARRAY_SIZE( samp->profile.array )-1) )?1:-1]; + typedef int testb[ (sizeof( samp->profile.array ) == (sizeof(samp->profile.named)) )?1:-1]; + typedef int testc[ (sizeof( info->clocks ) >= (sizeof(samp->profile.named)) )?1:-1]; + + for( i = 0 ; i < STBIR__ARRAY_SIZE( bdescriptions ) ; i++) + info->clocks[i] = samp->profile.array[i+1]; + + info->total_clocks = samp->profile.named.total; + info->descriptions = bdescriptions; + info->count = STBIR__ARRAY_SIZE( bdescriptions ); +} + +STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize, int split_start, int split_count ) +{ + static char const * descriptions[7] = { "Looping", "Vertical sampling", "Horizontal sampling", "Scanline input", "Scanline output", "Alpha weighting", "Alpha unweighting" }; + stbir__per_split_info * split_info; + int s, i; + + typedef int testa[ (STBIR__ARRAY_SIZE( descriptions ) == (STBIR__ARRAY_SIZE( split_info->profile.array )-1) )?1:-1]; + typedef int testb[ (sizeof( split_info->profile.array ) == (sizeof(split_info->profile.named)) )?1:-1]; + typedef int testc[ (sizeof( info->clocks ) >= (sizeof(split_info->profile.named)) )?1:-1]; + + if ( split_start == -1 ) + { + split_start = 0; + split_count = resize->samplers->splits; + } + + if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) ) + { + info->total_clocks = 0; + info->descriptions = 0; + info->count = 0; + return; + } + + split_info = resize->samplers->split_info + split_start; + + // sum up the profile from all the splits + for( i = 0 ; i < STBIR__ARRAY_SIZE( descriptions ) ; i++ ) + { + stbir_uint64 sum = 0; + for( s = 0 ; s < split_count ; s++ ) + sum += split_info[s].profile.array[i+1]; + info->clocks[i] = sum; + } + + info->total_clocks = split_info->profile.named.total; + info->descriptions = descriptions; + info->count = STBIR__ARRAY_SIZE( descriptions ); +} + +STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) +{ + stbir_resize_split_profile_info( info, resize, -1, 0 ); +} + +#endif // STBIR_PROFILE + +#undef STBIR_BGR +#undef STBIR_1CHANNEL +#undef STBIR_2CHANNEL +#undef STBIR_RGB +#undef STBIR_RGBA +#undef STBIR_4CHANNEL +#undef STBIR_BGRA +#undef STBIR_ARGB +#undef STBIR_ABGR +#undef STBIR_RA +#undef STBIR_AR +#undef STBIR_RGBA_PM +#undef STBIR_BGRA_PM +#undef STBIR_ARGB_PM +#undef STBIR_ABGR_PM +#undef STBIR_RA_PM +#undef STBIR_AR_PM + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + +#else // STB_IMAGE_RESIZE_HORIZONTALS&STB_IMAGE_RESIZE_DO_VERTICALS + +// we reinclude the header file to define all the horizontal functions +// specializing each function for the number of coeffs is 20-40% faster *OVERALL* + +// by including the header file again this way, we can still debug the functions + +#define STBIR_strs_join2( start, mid, end ) start##mid##end +#define STBIR_strs_join1( start, mid, end ) STBIR_strs_join2( start, mid, end ) + +#define STBIR_strs_join24( start, mid1, mid2, end ) start##mid1##mid2##end +#define STBIR_strs_join14( start, mid1, mid2, end ) STBIR_strs_join24( start, mid1, mid2, end ) + +#ifdef STB_IMAGE_RESIZE_DO_CODERS + +#ifdef stbir__decode_suffix +#define STBIR__CODER_NAME( name ) STBIR_strs_join1( name, _, stbir__decode_suffix ) +#else +#define STBIR__CODER_NAME( name ) name +#endif + +#ifdef stbir__decode_swizzle +#define stbir__decode_simdf8_flip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3),stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg) +#define stbir__decode_simdf4_flip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg) +#define stbir__encode_simdf8_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3),stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) +#define stbir__encode_simdf4_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) +#else +#define stbir__decode_order0 0 +#define stbir__decode_order1 1 +#define stbir__decode_order2 2 +#define stbir__decode_order3 3 +#define stbir__encode_order0 0 +#define stbir__encode_order1 1 +#define stbir__encode_order2 2 +#define stbir__encode_order3 3 +#define stbir__decode_simdf8_flip(reg) +#define stbir__decode_simdf4_flip(reg) +#define stbir__encode_simdf8_unflip(reg) +#define stbir__encode_simdf4_unflip(reg) +#endif + +#ifdef STBIR_SIMD8 +#define stbir__encode_simdfX_unflip stbir__encode_simdf8_unflip +#else +#define stbir__encode_simdfX_unflip stbir__encode_simdf4_unflip +#endif + +static void STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const*)inputp; + + #ifdef STBIR_SIMD + unsigned char const * end_input_m16 = input + width_times_channels - 16; + if ( width_times_channels >= 16 ) + { + decode_end -= 16; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o0,o1; + stbir__simdf8 of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u8_to_u32( o0, o1, i ); + stbir__simdi8_convert_i32_to_float( of0, o0 ); + stbir__simdi8_convert_i32_to_float( of1, o1 ); + stbir__simdf8_mult( of0, of0, STBIR_max_uint8_as_float_inverted8); + stbir__simdf8_mult( of1, of1, STBIR_max_uint8_as_float_inverted8); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode + 0, of0 ); + stbir__simdf8_store( decode + 8, of1 ); + #else + stbir__simdi i, o0, o1, o2, o3; + stbir__simdf of0, of1, of2, of3; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdi_convert_i32_to_float( of2, o2 ); + stbir__simdi_convert_i32_to_float( of3, o3 ); + stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of2, of2, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of3, of3, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + stbir__simdf_store( decode + 8, of2 ); + stbir__simdf_store( decode + 12, of3 ); + #endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted; + decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted; + decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted; + decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint8_as_float_inverted; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted; + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_linear_scaled )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp; + unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdi i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode ); + stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + #ifdef STBIR_SIMD8 + stbir__simdf8_pack_to_16bytes( i, e0, e1 ); + stbir__simdi_store( output, i ); + #else + stbir__simdf_pack_to_8bytes( i, e0, e1 ); + stbir__simdi_store2( output, i ); + #endif + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + stbir__simdi i0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); + stbir__simdf_madd( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), e0 ); + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4 + *(int*)(output-4) = stbir__simdi_to_int( i0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + stbir__simdf e0; + STBIR_NO_UNROLL(encode); + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_uint8( e0 ); + #if stbir__coder_min_num >= 2 + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_uint8( e0 ); + #endif + #if stbir__coder_min_num >= 3 + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_uint8( e0 ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f; + f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f; + f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f; + f = encode[stbir__encode_order3] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const*)inputp; + + #ifdef STBIR_SIMD + unsigned char const * end_input_m16 = input + width_times_channels - 16; + if ( width_times_channels >= 16 ) + { + decode_end -= 16; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o0,o1; + stbir__simdf8 of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u8_to_u32( o0, o1, i ); + stbir__simdi8_convert_i32_to_float( of0, o0 ); + stbir__simdi8_convert_i32_to_float( of1, o1 ); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode + 0, of0 ); + stbir__simdf8_store( decode + 8, of1 ); + #else + stbir__simdi i, o0, o1, o2, o3; + stbir__simdf of0, of1, of2, of3; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdi_convert_i32_to_float( of2, o2 ); + stbir__simdi_convert_i32_to_float( of3, o3 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + stbir__simdf_store( decode + 8, of2 ); + stbir__simdf_store( decode + 12, of3 ); +#endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])); + decode[1-4] = ((float)(input[stbir__decode_order1])); + decode[2-4] = ((float)(input[stbir__decode_order2])); + decode[3-4] = ((float)(input[stbir__decode_order3])); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])); + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp; + unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdi i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode ); + stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + #ifdef STBIR_SIMD8 + stbir__simdf8_pack_to_16bytes( i, e0, e1 ); + stbir__simdi_store( output, i ); + #else + stbir__simdf_pack_to_8bytes( i, e0, e1 ); + stbir__simdi_store2( output, i ); + #endif + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + stbir__simdi i0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); + stbir__simdf_add( e0, STBIR__CONSTF(STBIR_simd_point5), e0 ); + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4 + *(int*)(output-4) = stbir__simdi_to_int( i0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f; + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f; + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f; + f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ]; + decode[1-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ]; + decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ]; + decode[3-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order3 ] ]; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ]; + #if stbir__coder_min_num >= 2 + decode[1] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ]; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ]; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +#define stbir__min_max_shift20( i, f ) \ + stbir__simdf_max( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_zero )) ); \ + stbir__simdf_min( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_one )) ); \ + stbir__simdi_32shr( i, stbir_simdi_castf( f ), 20 ); + +#define stbir__scale_and_convert( i, f ) \ + stbir__simdf_madd( f, STBIR__CONSTF( STBIR_simd_point5 ), STBIR__CONSTF( STBIR_max_uint8_as_float ), f ); \ + stbir__simdf_max( f, f, stbir__simdf_zeroP() ); \ + stbir__simdf_min( f, f, STBIR__CONSTF( STBIR_max_uint8_as_float ) ); \ + stbir__simdf_convert_float_to_i32( i, f ); + +#define stbir__linear_to_srgb_finish( i, f ) \ +{ \ + stbir__simdi temp; \ + stbir__simdi_32shr( temp, stbir_simdi_castf( f ), 12 ) ; \ + stbir__simdi_and( temp, temp, STBIR__CONSTI(STBIR_mastissa_mask) ); \ + stbir__simdi_or( temp, temp, STBIR__CONSTI(STBIR_topscale) ); \ + stbir__simdi_16madd( i, i, temp ); \ + stbir__simdi_32shr( i, i, 16 ); \ +} + +#define stbir__simdi_table_lookup2( v0,v1, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ +} + +#define stbir__simdi_table_lookup3( v0,v1,v2, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1,temp2; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp2.m128i_i128 = v2; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ + v2 = temp2.m128i_i128; \ +} + +#define stbir__simdi_table_lookup4( v0,v1,v2,v3, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1,temp2,temp3; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp2.m128i_i128 = v2; \ + temp3.m128i_i128 = v3; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \ + temp3.m128i_u32[0] = table[temp3.m128i_i32[0]]; temp3.m128i_u32[1] = table[temp3.m128i_i32[1]]; temp3.m128i_u32[2] = table[temp3.m128i_i32[2]]; temp3.m128i_u32[3] = table[temp3.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ + v2 = temp2.m128i_i128; \ + v3 = temp3.m128i_i128; \ +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + STBIR_SIMD_NO_UNROLL(encode); + + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__min_max_shift20( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__min_max_shift20( i3, f3 ); + + stbir__simdi_table_lookup4( i0, i1, i2, i3, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i1, f1 ); + stbir__linear_to_srgb_finish( i2, f2 ); + stbir__linear_to_srgb_finish( i3, f3 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + encode += 16; + output += 16; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while ( output <= end_output ) + { + STBIR_SIMD_NO_UNROLL(encode); + + output[0-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] ); + output[1-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] ); + output[2-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] ); + output[3-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order3] ); + + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + STBIR_NO_UNROLL(encode); + output[0] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] ); + #if stbir__coder_min_num >= 2 + output[1] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] ); + #endif + #if stbir__coder_min_num >= 3 + output[2] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +#if ( stbir__coder_min_num == 4 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + do { + decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; + decode[1] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order1] ]; + decode[2] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order2] ]; + decode[3] = ( (float) input[stbir__decode_order3] ) * stbir__max_uint8_as_float_inverted; + input += 4; + decode += 4; + } while( decode < decode_end ); +} + + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb4_linearalpha )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__min_max_shift20( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__scale_and_convert( i3, f3 ); + + stbir__simdi_table_lookup3( i0, i1, i2, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i1, f1 ); + stbir__linear_to_srgb_finish( i2, f2 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + output += 16; + encode += 16; + + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + do { + float f; + STBIR_SIMD_NO_UNROLL(encode); + + output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] ); + output[stbir__decode_order1] = stbir__linear_to_srgb_uchar( encode[1] ); + output[stbir__decode_order2] = stbir__linear_to_srgb_uchar( encode[2] ); + + f = encode[3] * stbir__max_uint8_as_float + 0.5f; + STBIR_CLAMP(f, 0, 255); + output[stbir__decode_order3] = (unsigned char) f; + + output += 4; + encode += 4; + } while( output < end_output ); +} + +#endif + +#if ( stbir__coder_min_num == 2 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + decode += 4; + while( decode <= decode_end ) + { + decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; + decode[1-4] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; + decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0+2] ]; + decode[3-4] = ( (float) input[stbir__decode_order1+2] ) * stbir__max_uint8_as_float_inverted; + input += 4; + decode += 4; + } + decode -= 4; + if( decode < decode_end ) + { + decode[0] = stbir__srgb_uchar_to_linear_float[ stbir__decode_order0 ]; + decode[1] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; + } +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb2_linearalpha )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__scale_and_convert( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__scale_and_convert( i3, f3 ); + + stbir__simdi_table_lookup2( i0, i2, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i2, f2 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + output += 16; + encode += 16; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + do { + float f; + STBIR_SIMD_NO_UNROLL(encode); + + output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] ); + + f = encode[1] * stbir__max_uint8_as_float + 0.5f; + STBIR_CLAMP(f, 0, 255); + output[stbir__decode_order1] = (unsigned char) f; + + output += 2; + encode += 2; + } while( output < end_output ); +} + +#endif + +static void STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned short const * input = (unsigned short const *)inputp; + + #ifdef STBIR_SIMD + unsigned short const * end_input_m8 = input + width_times_channels - 8; + if ( width_times_channels >= 8 ) + { + decode_end -= 8; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o; + stbir__simdf8 of; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u16_to_u32( o, i ); + stbir__simdi8_convert_i32_to_float( of, o ); + stbir__simdf8_mult( of, of, STBIR_max_uint16_as_float_inverted8); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode + 0, of ); + #else + stbir__simdi i, o0, o1; + stbir__simdf of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u16_to_u32( o0,o1,i ); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted) ); + stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted)); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted; + decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted; + decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted; + decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint16_as_float_inverted; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted; + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + + +static void STBIR__CODER_NAME(stbir__encode_uint16_linear_scaled)( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp; + unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + { + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdiX i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode ); + stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_pack_to_words( i, e0, e1 ); + stbir__simdiX_store( output, i ); + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e; + stbir__simdi i; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e, encode ); + stbir__simdf_madd( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), e ); + stbir__encode_simdf4_unflip( e ); + stbir__simdf_pack_to_8words( i, e, e ); // only use first 4 + stbir__simdi_store2( output-4, i ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + stbir__simdf e; + STBIR_NO_UNROLL(encode); + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_short( e ); + #if stbir__coder_min_num >= 2 + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_short( e ); + #endif + #if stbir__coder_min_num >= 3 + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_short( e ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + STBIR_SIMD_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f; + f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f; + f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f; + f = encode[stbir__encode_order3] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned short const * input = (unsigned short const *)inputp; + + #ifdef STBIR_SIMD + unsigned short const * end_input_m8 = input + width_times_channels - 8; + if ( width_times_channels >= 8 ) + { + decode_end -= 8; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o; + stbir__simdf8 of; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u16_to_u32( o, i ); + stbir__simdi8_convert_i32_to_float( of, o ); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode + 0, of ); + #else + stbir__simdi i, o0, o1; + stbir__simdf of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u16_to_u32( o0, o1, i ); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])); + decode[1-4] = ((float)(input[stbir__decode_order1])); + decode[2-4] = ((float)(input[stbir__decode_order2])); + decode[3-4] = ((float)(input[stbir__decode_order3])); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])); + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__encode_uint16_linear)( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp; + unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + { + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdiX i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode ); + stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_pack_to_words( i, e0, e1 ); + stbir__simdiX_store( output, i ); + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e; + stbir__simdi i; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e, encode ); + stbir__simdf_add( e, STBIR__CONSTF(STBIR_simd_point5), e ); + stbir__encode_simdf4_unflip( e ); + stbir__simdf_pack_to_8words( i, e, e ); // only use first 4 + stbir__simdi_store2( output-4, i ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + STBIR_SIMD_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f; + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f; + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f; + f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + stbir__FP16 const * input = (stbir__FP16 const *)inputp; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 8 ) + { + stbir__FP16 const * end_input_m8 = input + width_times_channels - 8; + decode_end -= 8; + for(;;) + { + STBIR_NO_UNROLL(decode); + + stbir__half_to_float_SIMD( decode, input ); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of; + stbir__simdf8_load( of, decode ); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode, of ); + } + #else + { + stbir__simdf of0,of1; + stbir__simdf_load( of0, decode ); + stbir__simdf_load( of1, decode+4 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode, of0 ); + stbir__simdf_store( decode+4, of1 ); + } + #endif + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = stbir__half_to_float(input[stbir__decode_order0]); + decode[1-4] = stbir__half_to_float(input[stbir__decode_order1]); + decode[2-4] = stbir__half_to_float(input[stbir__decode_order2]); + decode[3-4] = stbir__half_to_float(input[stbir__decode_order3]); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = stbir__half_to_float(input[stbir__decode_order0]); + #if stbir__coder_min_num >= 2 + decode[1] = stbir__half_to_float(input[stbir__decode_order1]); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = stbir__half_to_float(input[stbir__decode_order2]); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_half_float_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + stbir__FP16 STBIR_SIMD_STREAMOUT_PTR( * ) output = (stbir__FP16*) outputp; + stbir__FP16 * end_output = ( (stbir__FP16*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 8 ) + { + float const * end_encode_m8 = encode + width_times_channels - 8; + end_output -= 8; + for(;;) + { + STBIR_SIMD_NO_UNROLL(encode); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of; + stbir__simdf8_load( of, encode ); + stbir__encode_simdf8_unflip( of ); + stbir__float_to_half_SIMD( output, (float*)&of ); + } + #else + { + stbir__simdf of[2]; + stbir__simdf_load( of[0], encode ); + stbir__simdf_load( of[1], encode+4 ); + stbir__encode_simdf4_unflip( of[0] ); + stbir__encode_simdf4_unflip( of[1] ); + stbir__float_to_half_SIMD( output, (float*)of ); + } + #endif + #else + stbir__float_to_half_SIMD( output, encode ); + #endif + encode += 8; + output += 8; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 8 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + STBIR_SIMD_NO_UNROLL(output); + output[0-4] = stbir__float_to_half(encode[stbir__encode_order0]); + output[1-4] = stbir__float_to_half(encode[stbir__encode_order1]); + output[2-4] = stbir__float_to_half(encode[stbir__encode_order2]); + output[3-4] = stbir__float_to_half(encode[stbir__encode_order3]); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + STBIR_NO_UNROLL(output); + output[0] = stbir__float_to_half(encode[stbir__encode_order0]); + #if stbir__coder_min_num >= 2 + output[1] = stbir__float_to_half(encode[stbir__encode_order1]); + #endif + #if stbir__coder_min_num >= 3 + output[2] = stbir__float_to_half(encode[stbir__encode_order2]); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + #ifdef stbir__decode_swizzle + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + float const * input = (float const *)inputp; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 16 ) + { + float const * end_input_m16 = input + width_times_channels - 16; + decode_end -= 16; + for(;;) + { + STBIR_NO_UNROLL(decode); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of0,of1; + stbir__simdf8_load( of0, input ); + stbir__simdf8_load( of1, input+8 ); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode, of0 ); + stbir__simdf8_store( decode+8, of1 ); + } + #else + { + stbir__simdf of0,of1,of2,of3; + stbir__simdf_load( of0, input ); + stbir__simdf_load( of1, input+4 ); + stbir__simdf_load( of2, input+8 ); + stbir__simdf_load( of3, input+12 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode, of0 ); + stbir__simdf_store( decode+4, of1 ); + stbir__simdf_store( decode+8, of2 ); + stbir__simdf_store( decode+12, of3 ); + } + #endif + #endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = input[stbir__decode_order0]; + decode[1-4] = input[stbir__decode_order1]; + decode[2-4] = input[stbir__decode_order2]; + decode[3-4] = input[stbir__decode_order3]; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = input[stbir__decode_order0]; + #if stbir__coder_min_num >= 2 + decode[1] = input[stbir__decode_order1]; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = input[stbir__decode_order2]; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif + + #else + + if ( (void*)decodep != inputp ) + STBIR_MEMCPY( decodep, inputp, width_times_channels * sizeof( float ) ); + + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_float_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + #if !defined( STBIR_FLOAT_HIGH_CLAMP ) && !defined(STBIR_FLOAT_LO_CLAMP) && !defined(stbir__decode_swizzle) + + if ( (void*)outputp != (void*) encode ) + STBIR_MEMCPY( outputp, encode, width_times_channels * sizeof( float ) ); + + #else + + float STBIR_SIMD_STREAMOUT_PTR( * ) output = (float*) outputp; + float * end_output = ( (float*) output ) + width_times_channels; + + #ifdef STBIR_FLOAT_HIGH_CLAMP + #define stbir_scalar_hi_clamp( v ) if ( v > STBIR_FLOAT_HIGH_CLAMP ) v = STBIR_FLOAT_HIGH_CLAMP; + #else + #define stbir_scalar_hi_clamp( v ) + #endif + #ifdef STBIR_FLOAT_LOW_CLAMP + #define stbir_scalar_lo_clamp( v ) if ( v < STBIR_FLOAT_LOW_CLAMP ) v = STBIR_FLOAT_LOW_CLAMP; + #else + #define stbir_scalar_lo_clamp( v ) + #endif + + #ifdef STBIR_SIMD + + #ifdef STBIR_FLOAT_HIGH_CLAMP + const stbir__simdfX high_clamp = stbir__simdf_frepX(STBIR_FLOAT_HIGH_CLAMP); + #endif + #ifdef STBIR_FLOAT_LOW_CLAMP + const stbir__simdfX low_clamp = stbir__simdf_frepX(STBIR_FLOAT_LOW_CLAMP); + #endif + + if ( width_times_channels >= ( stbir__simdfX_float_count * 2 ) ) + { + float const * end_encode_m8 = encode + width_times_channels - ( stbir__simdfX_float_count * 2 ); + end_output -= ( stbir__simdfX_float_count * 2 ); + for(;;) + { + stbir__simdfX e0, e1; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_load( e0, encode ); + stbir__simdfX_load( e1, encode+stbir__simdfX_float_count ); +#ifdef STBIR_FLOAT_HIGH_CLAMP + stbir__simdfX_min( e0, e0, high_clamp ); + stbir__simdfX_min( e1, e1, high_clamp ); +#endif +#ifdef STBIR_FLOAT_LOW_CLAMP + stbir__simdfX_max( e0, e0, low_clamp ); + stbir__simdfX_max( e1, e1, low_clamp ); +#endif + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_store( output, e0 ); + stbir__simdfX_store( output+stbir__simdfX_float_count, e1 ); + encode += stbir__simdfX_float_count * 2; + output += stbir__simdfX_float_count * 2; + if ( output < end_output ) + continue; + if ( output == ( end_output + ( stbir__simdfX_float_count * 2 ) ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); +#ifdef STBIR_FLOAT_HIGH_CLAMP + stbir__simdf_min( e0, e0, high_clamp ); +#endif +#ifdef STBIR_FLOAT_LOW_CLAMP + stbir__simdf_max( e0, e0, low_clamp ); +#endif + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_store( output-4, e0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float e; + STBIR_SIMD_NO_UNROLL(encode); + e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0-4] = e; + e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1-4] = e; + e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2-4] = e; + e = encode[ stbir__encode_order3 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[3-4] = e; + output += 4; + encode += 4; + } + output -= 4; + + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float e; + STBIR_NO_UNROLL(encode); + e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0] = e; + #if stbir__coder_min_num >= 2 + e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1] = e; + #endif + #if stbir__coder_min_num >= 3 + e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2] = e; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #endif +} + +#undef stbir__decode_suffix +#undef stbir__decode_simdf8_flip +#undef stbir__decode_simdf4_flip +#undef stbir__decode_order0 +#undef stbir__decode_order1 +#undef stbir__decode_order2 +#undef stbir__decode_order3 +#undef stbir__encode_order0 +#undef stbir__encode_order1 +#undef stbir__encode_order2 +#undef stbir__encode_order3 +#undef stbir__encode_simdf8_unflip +#undef stbir__encode_simdf4_unflip +#undef stbir__encode_simdfX_unflip +#undef STBIR__CODER_NAME +#undef stbir__coder_min_num +#undef stbir__decode_swizzle +#undef stbir_scalar_hi_clamp +#undef stbir_scalar_lo_clamp +#undef STB_IMAGE_RESIZE_DO_CODERS + +#elif defined( STB_IMAGE_RESIZE_DO_VERTICALS) + +#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#define STBIR_chans( start, end ) STBIR_strs_join14(start,STBIR__vertical_channels,end,_cont) +#else +#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__vertical_channels,end) +#endif + +#if STBIR__vertical_channels >= 1 +#define stbIF0( code ) code +#else +#define stbIF0( code ) +#endif +#if STBIR__vertical_channels >= 2 +#define stbIF1( code ) code +#else +#define stbIF1( code ) +#endif +#if STBIR__vertical_channels >= 3 +#define stbIF2( code ) code +#else +#define stbIF2( code ) +#endif +#if STBIR__vertical_channels >= 4 +#define stbIF3( code ) code +#else +#define stbIF3( code ) +#endif +#if STBIR__vertical_channels >= 5 +#define stbIF4( code ) code +#else +#define stbIF4( code ) +#endif +#if STBIR__vertical_channels >= 6 +#define stbIF5( code ) code +#else +#define stbIF5( code ) +#endif +#if STBIR__vertical_channels >= 7 +#define stbIF6( code ) code +#else +#define stbIF6( code ) +#endif +#if STBIR__vertical_channels >= 8 +#define stbIF7( code ) code +#else +#define stbIF7( code ) +#endif + +static void STBIR_chans( stbir__vertical_scatter_with_,_coeffs)( float ** outputs, float const * vertical_coefficients, float const * input, float const * input_end ) +{ + stbIF0( float STBIR_SIMD_STREAMOUT_PTR( * ) output0 = outputs[0]; float c0s = vertical_coefficients[0]; ) + stbIF1( float STBIR_SIMD_STREAMOUT_PTR( * ) output1 = outputs[1]; float c1s = vertical_coefficients[1]; ) + stbIF2( float STBIR_SIMD_STREAMOUT_PTR( * ) output2 = outputs[2]; float c2s = vertical_coefficients[2]; ) + stbIF3( float STBIR_SIMD_STREAMOUT_PTR( * ) output3 = outputs[3]; float c3s = vertical_coefficients[3]; ) + stbIF4( float STBIR_SIMD_STREAMOUT_PTR( * ) output4 = outputs[4]; float c4s = vertical_coefficients[4]; ) + stbIF5( float STBIR_SIMD_STREAMOUT_PTR( * ) output5 = outputs[5]; float c5s = vertical_coefficients[5]; ) + stbIF6( float STBIR_SIMD_STREAMOUT_PTR( * ) output6 = outputs[6]; float c6s = vertical_coefficients[6]; ) + stbIF7( float STBIR_SIMD_STREAMOUT_PTR( * ) output7 = outputs[7]; float c7s = vertical_coefficients[7]; ) + + #ifdef STBIR_SIMD + { + stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); ) + stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); ) + stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); ) + stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); ) + stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); ) + stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); ) + stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); ) + stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); ) + while ( ( (char*)input_end - (char*) input ) >= (16*stbir__simdfX_float_count) ) + { + stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3; + STBIR_SIMD_NO_UNROLL(output0); + + stbir__simdfX_load( r0, input ); stbir__simdfX_load( r1, input+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input+(3*stbir__simdfX_float_count) ); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdfX_load( o0, output0 ); stbir__simdfX_load( o1, output0+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); + stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); ) + stbIF1( stbir__simdfX_load( o0, output1 ); stbir__simdfX_load( o1, output1+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output1+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); + stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); ) + stbIF2( stbir__simdfX_load( o0, output2 ); stbir__simdfX_load( o1, output2+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output2+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); + stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); ) + stbIF3( stbir__simdfX_load( o0, output3 ); stbir__simdfX_load( o1, output3+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output3+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); + stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); ) + stbIF4( stbir__simdfX_load( o0, output4 ); stbir__simdfX_load( o1, output4+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output4+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); + stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); ) + stbIF5( stbir__simdfX_load( o0, output5 ); stbir__simdfX_load( o1, output5+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output5+(2*stbir__simdfX_float_count)); stbir__simdfX_load( o3, output5+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); + stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); ) + stbIF6( stbir__simdfX_load( o0, output6 ); stbir__simdfX_load( o1, output6+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output6+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); + stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); ) + stbIF7( stbir__simdfX_load( o0, output7 ); stbir__simdfX_load( o1, output7+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output7+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); + stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); ) + #else + stbIF0( stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); + stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); ) + stbIF1( stbir__simdfX_mult( o0, r0, c1 ); stbir__simdfX_mult( o1, r1, c1 ); stbir__simdfX_mult( o2, r2, c1 ); stbir__simdfX_mult( o3, r3, c1 ); + stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); ) + stbIF2( stbir__simdfX_mult( o0, r0, c2 ); stbir__simdfX_mult( o1, r1, c2 ); stbir__simdfX_mult( o2, r2, c2 ); stbir__simdfX_mult( o3, r3, c2 ); + stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); ) + stbIF3( stbir__simdfX_mult( o0, r0, c3 ); stbir__simdfX_mult( o1, r1, c3 ); stbir__simdfX_mult( o2, r2, c3 ); stbir__simdfX_mult( o3, r3, c3 ); + stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); ) + stbIF4( stbir__simdfX_mult( o0, r0, c4 ); stbir__simdfX_mult( o1, r1, c4 ); stbir__simdfX_mult( o2, r2, c4 ); stbir__simdfX_mult( o3, r3, c4 ); + stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); ) + stbIF5( stbir__simdfX_mult( o0, r0, c5 ); stbir__simdfX_mult( o1, r1, c5 ); stbir__simdfX_mult( o2, r2, c5 ); stbir__simdfX_mult( o3, r3, c5 ); + stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); ) + stbIF6( stbir__simdfX_mult( o0, r0, c6 ); stbir__simdfX_mult( o1, r1, c6 ); stbir__simdfX_mult( o2, r2, c6 ); stbir__simdfX_mult( o3, r3, c6 ); + stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); ) + stbIF7( stbir__simdfX_mult( o0, r0, c7 ); stbir__simdfX_mult( o1, r1, c7 ); stbir__simdfX_mult( o2, r2, c7 ); stbir__simdfX_mult( o3, r3, c7 ); + stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); ) + #endif + + input += (4*stbir__simdfX_float_count); + stbIF0( output0 += (4*stbir__simdfX_float_count); ) stbIF1( output1 += (4*stbir__simdfX_float_count); ) stbIF2( output2 += (4*stbir__simdfX_float_count); ) stbIF3( output3 += (4*stbir__simdfX_float_count); ) stbIF4( output4 += (4*stbir__simdfX_float_count); ) stbIF5( output5 += (4*stbir__simdfX_float_count); ) stbIF6( output6 += (4*stbir__simdfX_float_count); ) stbIF7( output7 += (4*stbir__simdfX_float_count); ) + } + while ( ( (char*)input_end - (char*) input ) >= 16 ) + { + stbir__simdf o0, r0; + STBIR_SIMD_NO_UNROLL(output0); + + stbir__simdf_load( r0, input ); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdf_load( o0, output0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); ) + stbIF1( stbir__simdf_load( o0, output1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); ) + stbIF2( stbir__simdf_load( o0, output2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); ) + stbIF3( stbir__simdf_load( o0, output3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); ) + stbIF4( stbir__simdf_load( o0, output4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); ) + stbIF5( stbir__simdf_load( o0, output5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); ) + stbIF6( stbir__simdf_load( o0, output6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); ) + stbIF7( stbir__simdf_load( o0, output7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); ) + #else + stbIF0( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); ) + stbIF1( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); ) + stbIF2( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); ) + stbIF3( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); ) + stbIF4( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); ) + stbIF5( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); ) + stbIF6( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); ) + stbIF7( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); ) + #endif + + input += 4; + stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; ) + } + } + #else + while ( ( (char*)input_end - (char*) input ) >= 16 ) + { + float r0, r1, r2, r3; + STBIR_NO_UNROLL(input); + + r0 = input[0], r1 = input[1], r2 = input[2], r3 = input[3]; + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( output0[0] += ( r0 * c0s ); output0[1] += ( r1 * c0s ); output0[2] += ( r2 * c0s ); output0[3] += ( r3 * c0s ); ) + stbIF1( output1[0] += ( r0 * c1s ); output1[1] += ( r1 * c1s ); output1[2] += ( r2 * c1s ); output1[3] += ( r3 * c1s ); ) + stbIF2( output2[0] += ( r0 * c2s ); output2[1] += ( r1 * c2s ); output2[2] += ( r2 * c2s ); output2[3] += ( r3 * c2s ); ) + stbIF3( output3[0] += ( r0 * c3s ); output3[1] += ( r1 * c3s ); output3[2] += ( r2 * c3s ); output3[3] += ( r3 * c3s ); ) + stbIF4( output4[0] += ( r0 * c4s ); output4[1] += ( r1 * c4s ); output4[2] += ( r2 * c4s ); output4[3] += ( r3 * c4s ); ) + stbIF5( output5[0] += ( r0 * c5s ); output5[1] += ( r1 * c5s ); output5[2] += ( r2 * c5s ); output5[3] += ( r3 * c5s ); ) + stbIF6( output6[0] += ( r0 * c6s ); output6[1] += ( r1 * c6s ); output6[2] += ( r2 * c6s ); output6[3] += ( r3 * c6s ); ) + stbIF7( output7[0] += ( r0 * c7s ); output7[1] += ( r1 * c7s ); output7[2] += ( r2 * c7s ); output7[3] += ( r3 * c7s ); ) + #else + stbIF0( output0[0] = ( r0 * c0s ); output0[1] = ( r1 * c0s ); output0[2] = ( r2 * c0s ); output0[3] = ( r3 * c0s ); ) + stbIF1( output1[0] = ( r0 * c1s ); output1[1] = ( r1 * c1s ); output1[2] = ( r2 * c1s ); output1[3] = ( r3 * c1s ); ) + stbIF2( output2[0] = ( r0 * c2s ); output2[1] = ( r1 * c2s ); output2[2] = ( r2 * c2s ); output2[3] = ( r3 * c2s ); ) + stbIF3( output3[0] = ( r0 * c3s ); output3[1] = ( r1 * c3s ); output3[2] = ( r2 * c3s ); output3[3] = ( r3 * c3s ); ) + stbIF4( output4[0] = ( r0 * c4s ); output4[1] = ( r1 * c4s ); output4[2] = ( r2 * c4s ); output4[3] = ( r3 * c4s ); ) + stbIF5( output5[0] = ( r0 * c5s ); output5[1] = ( r1 * c5s ); output5[2] = ( r2 * c5s ); output5[3] = ( r3 * c5s ); ) + stbIF6( output6[0] = ( r0 * c6s ); output6[1] = ( r1 * c6s ); output6[2] = ( r2 * c6s ); output6[3] = ( r3 * c6s ); ) + stbIF7( output7[0] = ( r0 * c7s ); output7[1] = ( r1 * c7s ); output7[2] = ( r2 * c7s ); output7[3] = ( r3 * c7s ); ) + #endif + + input += 4; + stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; ) + } + #endif + while ( input < input_end ) + { + float r = input[0]; + STBIR_NO_UNROLL(output0); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( output0[0] += ( r * c0s ); ) + stbIF1( output1[0] += ( r * c1s ); ) + stbIF2( output2[0] += ( r * c2s ); ) + stbIF3( output3[0] += ( r * c3s ); ) + stbIF4( output4[0] += ( r * c4s ); ) + stbIF5( output5[0] += ( r * c5s ); ) + stbIF6( output6[0] += ( r * c6s ); ) + stbIF7( output7[0] += ( r * c7s ); ) + #else + stbIF0( output0[0] = ( r * c0s ); ) + stbIF1( output1[0] = ( r * c1s ); ) + stbIF2( output2[0] = ( r * c2s ); ) + stbIF3( output3[0] = ( r * c3s ); ) + stbIF4( output4[0] = ( r * c4s ); ) + stbIF5( output5[0] = ( r * c5s ); ) + stbIF6( output6[0] = ( r * c6s ); ) + stbIF7( output7[0] = ( r * c7s ); ) + #endif + + ++input; + stbIF0( ++output0; ) stbIF1( ++output1; ) stbIF2( ++output2; ) stbIF3( ++output3; ) stbIF4( ++output4; ) stbIF5( ++output5; ) stbIF6( ++output6; ) stbIF7( ++output7; ) + } +} + +static void STBIR_chans( stbir__vertical_gather_with_,_coeffs)( float * outputp, float const * vertical_coefficients, float const ** inputs, float const * input0_end ) +{ + float STBIR_SIMD_STREAMOUT_PTR( * ) output = outputp; + + stbIF0( float const * input0 = inputs[0]; float c0s = vertical_coefficients[0]; ) + stbIF1( float const * input1 = inputs[1]; float c1s = vertical_coefficients[1]; ) + stbIF2( float const * input2 = inputs[2]; float c2s = vertical_coefficients[2]; ) + stbIF3( float const * input3 = inputs[3]; float c3s = vertical_coefficients[3]; ) + stbIF4( float const * input4 = inputs[4]; float c4s = vertical_coefficients[4]; ) + stbIF5( float const * input5 = inputs[5]; float c5s = vertical_coefficients[5]; ) + stbIF6( float const * input6 = inputs[6]; float c6s = vertical_coefficients[6]; ) + stbIF7( float const * input7 = inputs[7]; float c7s = vertical_coefficients[7]; ) + +#if ( STBIR__vertical_channels == 1 ) && !defined(STB_IMAGE_RESIZE_VERTICAL_CONTINUE) + // check single channel one weight + if ( ( c0s >= (1.0f-0.000001f) ) && ( c0s <= (1.0f+0.000001f) ) ) + { + STBIR_MEMCPY( output, input0, (char*)input0_end - (char*)input0 ); + return; + } +#endif + + #ifdef STBIR_SIMD + { + stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); ) + stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); ) + stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); ) + stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); ) + stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); ) + stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); ) + stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); ) + stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); ) + + while ( ( (char*)input0_end - (char*) input0 ) >= (16*stbir__simdfX_float_count) ) + { + stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3; + STBIR_SIMD_NO_UNROLL(output); + + // prefetch four loop iterations ahead (doesn't affect much for small resizes, but helps with big ones) + stbIF0( stbir__prefetch( input0 + (16*stbir__simdfX_float_count) ); ) + stbIF1( stbir__prefetch( input1 + (16*stbir__simdfX_float_count) ); ) + stbIF2( stbir__prefetch( input2 + (16*stbir__simdfX_float_count) ); ) + stbIF3( stbir__prefetch( input3 + (16*stbir__simdfX_float_count) ); ) + stbIF4( stbir__prefetch( input4 + (16*stbir__simdfX_float_count) ); ) + stbIF5( stbir__prefetch( input5 + (16*stbir__simdfX_float_count) ); ) + stbIF6( stbir__prefetch( input6 + (16*stbir__simdfX_float_count) ); ) + stbIF7( stbir__prefetch( input7 + (16*stbir__simdfX_float_count) ); ) + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdfX_load( o0, output ); stbir__simdfX_load( o1, output+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output+(3*stbir__simdfX_float_count) ); + stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); ) + #else + stbIF0( stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); ) + #endif + + stbIF1( stbir__simdfX_load( r0, input1 ); stbir__simdfX_load( r1, input1+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input1+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); ) + stbIF2( stbir__simdfX_load( r0, input2 ); stbir__simdfX_load( r1, input2+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input2+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); ) + stbIF3( stbir__simdfX_load( r0, input3 ); stbir__simdfX_load( r1, input3+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input3+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); ) + stbIF4( stbir__simdfX_load( r0, input4 ); stbir__simdfX_load( r1, input4+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input4+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); ) + stbIF5( stbir__simdfX_load( r0, input5 ); stbir__simdfX_load( r1, input5+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input5+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input5+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); ) + stbIF6( stbir__simdfX_load( r0, input6 ); stbir__simdfX_load( r1, input6+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input6+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); ) + stbIF7( stbir__simdfX_load( r0, input7 ); stbir__simdfX_load( r1, input7+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input7+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); ) + + stbir__simdfX_store( output, o0 ); stbir__simdfX_store( output+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output+(3*stbir__simdfX_float_count), o3 ); + output += (4*stbir__simdfX_float_count); + stbIF0( input0 += (4*stbir__simdfX_float_count); ) stbIF1( input1 += (4*stbir__simdfX_float_count); ) stbIF2( input2 += (4*stbir__simdfX_float_count); ) stbIF3( input3 += (4*stbir__simdfX_float_count); ) stbIF4( input4 += (4*stbir__simdfX_float_count); ) stbIF5( input5 += (4*stbir__simdfX_float_count); ) stbIF6( input6 += (4*stbir__simdfX_float_count); ) stbIF7( input7 += (4*stbir__simdfX_float_count); ) + } + + while ( ( (char*)input0_end - (char*) input0 ) >= 16 ) + { + stbir__simdf o0, r0; + STBIR_SIMD_NO_UNROLL(output); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdf_load( o0, output ); stbir__simdf_load( r0, input0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); ) + #else + stbIF0( stbir__simdf_load( r0, input0 ); stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); ) + #endif + stbIF1( stbir__simdf_load( r0, input1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); ) + stbIF2( stbir__simdf_load( r0, input2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); ) + stbIF3( stbir__simdf_load( r0, input3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); ) + stbIF4( stbir__simdf_load( r0, input4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); ) + stbIF5( stbir__simdf_load( r0, input5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); ) + stbIF6( stbir__simdf_load( r0, input6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); ) + stbIF7( stbir__simdf_load( r0, input7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); ) + + stbir__simdf_store( output, o0 ); + output += 4; + stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; ) + } + } + #else + while ( ( (char*)input0_end - (char*) input0 ) >= 16 ) + { + float o0, o1, o2, o3; + STBIR_NO_UNROLL(output); + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( o0 = output[0] + input0[0] * c0s; o1 = output[1] + input0[1] * c0s; o2 = output[2] + input0[2] * c0s; o3 = output[3] + input0[3] * c0s; ) + #else + stbIF0( o0 = input0[0] * c0s; o1 = input0[1] * c0s; o2 = input0[2] * c0s; o3 = input0[3] * c0s; ) + #endif + stbIF1( o0 += input1[0] * c1s; o1 += input1[1] * c1s; o2 += input1[2] * c1s; o3 += input1[3] * c1s; ) + stbIF2( o0 += input2[0] * c2s; o1 += input2[1] * c2s; o2 += input2[2] * c2s; o3 += input2[3] * c2s; ) + stbIF3( o0 += input3[0] * c3s; o1 += input3[1] * c3s; o2 += input3[2] * c3s; o3 += input3[3] * c3s; ) + stbIF4( o0 += input4[0] * c4s; o1 += input4[1] * c4s; o2 += input4[2] * c4s; o3 += input4[3] * c4s; ) + stbIF5( o0 += input5[0] * c5s; o1 += input5[1] * c5s; o2 += input5[2] * c5s; o3 += input5[3] * c5s; ) + stbIF6( o0 += input6[0] * c6s; o1 += input6[1] * c6s; o2 += input6[2] * c6s; o3 += input6[3] * c6s; ) + stbIF7( o0 += input7[0] * c7s; o1 += input7[1] * c7s; o2 += input7[2] * c7s; o3 += input7[3] * c7s; ) + output[0] = o0; output[1] = o1; output[2] = o2; output[3] = o3; + output += 4; + stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; ) + } + #endif + while ( input0 < input0_end ) + { + float o0; + STBIR_NO_UNROLL(output); + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( o0 = output[0] + input0[0] * c0s; ) + #else + stbIF0( o0 = input0[0] * c0s; ) + #endif + stbIF1( o0 += input1[0] * c1s; ) + stbIF2( o0 += input2[0] * c2s; ) + stbIF3( o0 += input3[0] * c3s; ) + stbIF4( o0 += input4[0] * c4s; ) + stbIF5( o0 += input5[0] * c5s; ) + stbIF6( o0 += input6[0] * c6s; ) + stbIF7( o0 += input7[0] * c7s; ) + output[0] = o0; + ++output; + stbIF0( ++input0; ) stbIF1( ++input1; ) stbIF2( ++input2; ) stbIF3( ++input3; ) stbIF4( ++input4; ) stbIF5( ++input5; ) stbIF6( ++input6; ) stbIF7( ++input7; ) + } +} + +#undef stbIF0 +#undef stbIF1 +#undef stbIF2 +#undef stbIF3 +#undef stbIF4 +#undef stbIF5 +#undef stbIF6 +#undef stbIF7 +#undef STB_IMAGE_RESIZE_DO_VERTICALS +#undef STBIR__vertical_channels +#undef STB_IMAGE_RESIZE_DO_HORIZONTALS +#undef STBIR_strs_join24 +#undef STBIR_strs_join14 +#undef STBIR_chans +#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#undef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#endif + +#else // !STB_IMAGE_RESIZE_DO_VERTICALS + +#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__horizontal_channels,end) + +#ifndef stbir__2_coeff_only +#define stbir__2_coeff_only() \ + stbir__1_coeff_only(); \ + stbir__1_coeff_remnant(1); +#endif + +#ifndef stbir__2_coeff_remnant +#define stbir__2_coeff_remnant( ofs ) \ + stbir__1_coeff_remnant(ofs); \ + stbir__1_coeff_remnant((ofs)+1); +#endif + +#ifndef stbir__3_coeff_only +#define stbir__3_coeff_only() \ + stbir__2_coeff_only(); \ + stbir__1_coeff_remnant(2); +#endif + +#ifndef stbir__3_coeff_remnant +#define stbir__3_coeff_remnant( ofs ) \ + stbir__2_coeff_remnant(ofs); \ + stbir__1_coeff_remnant((ofs)+2); +#endif + +#ifndef stbir__3_coeff_setup +#define stbir__3_coeff_setup() +#endif + +#ifndef stbir__4_coeff_start +#define stbir__4_coeff_start() \ + stbir__2_coeff_only(); \ + stbir__2_coeff_remnant(2); +#endif + +#ifndef stbir__4_coeff_continue_from_4 +#define stbir__4_coeff_continue_from_4( ofs ) \ + stbir__2_coeff_remnant(ofs); \ + stbir__2_coeff_remnant((ofs)+2); +#endif + +#ifndef stbir__store_output_tiny +#define stbir__store_output_tiny stbir__store_output +#endif + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_1_coeff)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__1_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_2_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__2_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_3_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__3_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_4_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_5_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__1_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_6_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__2_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_7_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + stbir__3_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_8_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_9_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__1_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_10_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__2_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_11_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__3_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_12_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__4_coeff_continue_from_4(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod0 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 4 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod1 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 5 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__1_coeff_remnant( 4 ); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod2 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 6 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__2_coeff_remnant( 4 ); + + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod3 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 7 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__3_coeff_remnant( 4 ); + + stbir__store_output(); + } while ( output < output_end ); +} + +static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_funcs)[4]= +{ + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod0), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod1), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod2), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod3), +}; + +static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_funcs)[12]= +{ + STBIR_chans(stbir__horizontal_gather_,_channels_with_1_coeff), + STBIR_chans(stbir__horizontal_gather_,_channels_with_2_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_3_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_4_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_5_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_6_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_7_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_8_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_9_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_10_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_11_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_12_coeffs), +}; + +#undef STBIR__horizontal_channels +#undef STB_IMAGE_RESIZE_DO_HORIZONTALS +#undef stbir__1_coeff_only +#undef stbir__1_coeff_remnant +#undef stbir__2_coeff_only +#undef stbir__2_coeff_remnant +#undef stbir__3_coeff_only +#undef stbir__3_coeff_remnant +#undef stbir__3_coeff_setup +#undef stbir__4_coeff_start +#undef stbir__4_coeff_continue_from_4 +#undef stbir__store_output +#undef stbir__store_output_tiny +#undef STBIR_chans + +#endif // HORIZONALS + +#undef STBIR_strs_join2 +#undef STBIR_strs_join1 + +#endif // STB_IMAGE_RESIZE_DO_HORIZONTALS/VERTICALS/CODERS + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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/src/rtextures.c b/src/rtextures.c index 8624bbd48..bf1b35e40 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -213,7 +213,7 @@ #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size)) #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr)) #define STB_IMAGE_RESIZE_IMPLEMENTATION -#include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()] +#include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()] #if defined(SUPPORT_FILEFORMAT_SVG) #define NANOSVG_IMPLEMENTATION // Expands implementation @@ -1624,10 +1624,10 @@ void ImageResize(Image *image, int newWidth, int newHeight) switch (image->format) { - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 1); break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 2); break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 3); break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 4); break; + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)1); break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)2); break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)3); break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)4); break; default: break; } @@ -1643,7 +1643,7 @@ void ImageResize(Image *image, int newWidth, int newHeight) Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); // NOTE: Color data is cast to (unsigned char *), there shouldn't been any problem... - stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4); + stbir_resize_uint8_linear((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, (stbir_pixel_layout)4); int format = image->format; From f3c27ec157f3a3e914f8872714173f87e7ea7751 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:53:31 -0300 Subject: [PATCH 0711/1710] Fix `android`, `drm` compilation issue on `InitWindow` (#3407) * Fix drm compilation issue on InitWindow * Fix android compilation issue on InitWindow --- src/rcore_android.c | 10 +++++----- src/rcore_drm.c | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 5f6f34bab..39e4694c4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -190,7 +190,7 @@ void InitWindow(int width, int height, const char *title) CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - + // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -228,12 +228,12 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; - + // 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 + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); diff --git a/src/rcore_drm.c b/src/rcore_drm.c index c0e88c723..2644c5e73 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -228,16 +228,16 @@ void InitWindow(int width, int height, const char *title) // 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 - + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + // Initialize raw input system InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init From 36abc48cf8abe008f7f02516dcf1c1985517aee4 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Fri, 13 Oct 2023 19:54:00 +0200 Subject: [PATCH 0712/1710] Normalize `gestureEvent.position` coordinates (#3406) Fixed the fact that coordinates were not normalized on Android, preventing detection of `GESTURE_DOUBLE_TAP` --- src/rcore_android.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rcore_android.c b/src/rcore_android.c index 39e4694c4..3d9fab06a 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1152,6 +1152,8 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { 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(); } // Gesture data is sent to gestures system for processing From 5a0d9c8d43d212892e421e1cbb532fea508d8692 Mon Sep 17 00:00:00 2001 From: Daniil Kisel <56605335+KislyjKisel@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:54:43 +0300 Subject: [PATCH 0713/1710] Fix `UpdateSound` parameter name (#3405) --- src/raudio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index c2590e302..a8d1b40e2 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -981,14 +981,14 @@ void UnloadSoundAlias(Sound alias) } // Update sound buffer with new data -void UpdateSound(Sound sound, const void *data, int sampleCount) +void UpdateSound(Sound sound, const void *data, int frameCount) { if (sound.stream.buffer != NULL) { StopAudioBuffer(sound.stream.buffer); // TODO: May want to lock/unlock this since this data buffer is read at mixing time - memcpy(sound.stream.buffer->data, data, sampleCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); + memcpy(sound.stream.buffer->data, data, frameCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); } } From 4981acb241d18afc4d0db3497ecbe37d1f31808e Mon Sep 17 00:00:00 2001 From: Purple4pur <49893724+purple4pur@users.noreply.github.com> Date: Sat, 14 Oct 2023 01:55:52 +0800 Subject: [PATCH 0714/1710] fix zig syntax errors in examples, and make it install executables correctly (#3395) --- build.zig | 2 +- examples/build.zig | 36 ++++++++++++++++++++---------------- src/build.zig | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/build.zig b/build.zig index 12c0513f6..21638601d 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn build(b: *std.Build) void { raylib.build(b); } diff --git a/examples/build.zig b/examples/build.zig index 6e13ab3da..94ecc6783 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .File) continue; + if (entry.kind != .file) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(path, &[_][]const u8{}); + exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => "../src/zig-out/lib/raylib.lib", - .linux => "../src/zig-out/lib/libraylib.a", - .macos => "../src/zig-out/lib/libraylib.a", - .emscripten => "../src/zig-out/lib/libraylib.a", + .windows => .{ .path = "../zig-out/lib/raylib.lib" }, + .linux => .{ .path = "../zig-out/lib/libraylib.a" }, + .macos => .{ .path = "../zig-out/lib/libraylib.a" }, + .emscripten => .{ .path = "../zig-out/lib/libraylib.a" }, else => @panic("Unsupported OS"), }); - exe.addIncludePath("../src"); - exe.addIncludePath("../src/external"); - exe.addIncludePath("../src/external/glfw/include"); + exe.addIncludePath(.{ .path = "../src" }); + exe.addIncludePath(.{ .path = "../src/external" }); + exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath("external/glfw/deps/mingw"); + exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,11 +71,15 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - b.installArtifact(exe); - var run = b.addRunArtifact(exe); - run.cwd = module; - b.step(name, name).dependOn(&run.step); - all.dependOn(&exe.step); + const install_cmd = b.addInstallArtifact(exe, .{}); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(&install_cmd.step); + + const run_step = b.step(name, name); + run_step.dependOn(&run_cmd.step); + + all.dependOn(&install_cmd.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 27250f5ff..53e074245 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", From 2f08f435b91a08c57bf7e381a46381770a00791e Mon Sep 17 00:00:00 2001 From: Daniil Kisel <56605335+KislyjKisel@users.noreply.github.com> Date: Fri, 13 Oct 2023 21:54:15 +0300 Subject: [PATCH 0715/1710] Add Raylib.lean to BINDINGS.md (#3409) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 47a1c0a27..e08f8f79e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -81,6 +81,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | | rayjs | 4.6-dev | [QuickJS](https://bellard.org/quickjs/) | MIT | https://github.com/mode777/rayjs | | raylib-raku | **auto** | [Raku](https://www.raku.org/) | Artistic License 2.0 | https://github.com/vushu/raylib-raku | +| Raylib.lean | 4.5 | [Lean4](https://lean-lang.org/) | BSD-3-Clause | https://github.com/KislyjKisel/Raylib.lean | ### Utility Wrapers These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. From 005ba155c0b4d8e065e6e3ffe2cdd82bb41bf200 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 10:56:09 +0200 Subject: [PATCH 0716/1710] Minor tweaks --- src/rcore.c | 4 ++-- src/rcore_desktop.c | 2 +- src/rcore_web.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 3efa67b2c..b3e07f95a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2812,7 +2812,7 @@ static void RecordAutomationEvent(unsigned int frame) // INPUT_GAMEPAD_CONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && - (CORE.Input.Gamepad.currentState[gamepad] == true)) // Check if changed to ready + (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready { // TODO: Save gamepad connect event } @@ -2821,7 +2821,7 @@ static void RecordAutomationEvent(unsigned int frame) // INPUT_GAMEPAD_DISCONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && - (CORE.Input.Gamepad.currentState[gamepad] == false)) // Check if changed to not-ready + (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready { // TODO: Save gamepad disconnect event } diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index c2e5b23f1..8fa1edd78 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1966,7 +1966,7 @@ static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffs // GLFW3 CursorEnter Callback, when cursor enters the window static void CursorEnterCallback(GLFWwindow *window, int enter) { - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + if (enter) CORE.Input.Mouse.cursorOnScreen = true; else CORE.Input.Mouse.cursorOnScreen = false; } diff --git a/src/rcore_web.c b/src/rcore_web.c index 261498a5b..c0b7079b0 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1336,7 +1336,7 @@ static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffs // GLFW3 CursorEnter Callback, when cursor enters the window static void CursorEnterCallback(GLFWwindow *window, int enter) { - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + if (enter) CORE.Input.Mouse.cursorOnScreen = true; else CORE.Input.Mouse.cursorOnScreen = false; } From 4521a142c35fe9fe1386d79ae783363c9c164827 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 11:48:20 +0200 Subject: [PATCH 0717/1710] tweaks --- src/rcore.c | 2 +- src/rcore_web.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b3e07f95a..d7aaa30b3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2360,7 +2360,7 @@ int GetTouchPointCount(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//static bool InitGraphicsDevice(int width, int height) +//static bool InitPlatform(void) // Initialize hi-resolution timer void InitTimer(void) diff --git a/src/rcore_web.c b/src/rcore_web.c index c0b7079b0..277d1378f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1119,7 +1119,7 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified) // 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 @@ -1157,7 +1157,6 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths } } - // GLFW3 Keyboard Callback, runs on key pressed static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { @@ -1340,7 +1339,6 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) else CORE.Input.Mouse.cursorOnScreen = false; } - // Register fullscreen change events static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) { From b34c2ecbcb9da1d438b70acf8125ef5424744d11 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 12:49:54 +0200 Subject: [PATCH 0718/1710] WARNING: REDESIGN: `InitPlatform()` to initialize all platform data #3313 `InitGraphicsDevice()` could be confusing because the function actually initialized many things: window, graphics, inputs, callbacks, timming, storage... restructured it. --- src/rcore.c | 8 +- src/rcore_android.c | 281 ++++++++++++++++++---------------- src/rcore_desktop.c | 146 ++++++++---------- src/rcore_drm.c | 354 +++++++++++++++++++++---------------------- src/rcore_template.c | 71 ++++----- src/rcore_web.c | 198 +++++++++++------------- 6 files changed, 508 insertions(+), 550 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index d7aaa30b3..e6015b33f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2365,10 +2365,10 @@ int GetTouchPointCount(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. + // 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) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif diff --git a/src/rcore_android.c b/src/rcore_android.c index 3d9fab06a..3e3b8cd12 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -82,7 +82,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs @@ -172,88 +173,23 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // 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)); + 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 = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Platform specific init window - //-------------------------------------------------------------- - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - - int orientation = AConfiguration_getOrientation(platform.app->config); - - if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); - else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); - - // TODO: Automatic orientation doesn't seem to work - if (width <= height) - { - AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); - } - else - { - AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); - } - - //AConfiguration_getDensity(platform.app->config); - //AConfiguration_getKeyboard(platform.app->config); - //AConfiguration_getScreenSize(platform.app->config); - //AConfiguration_getScreenLong(platform.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - platform.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - platform.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = platform.app->activity->internalDataPath; - - // 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 - - TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Wait for window to be initialized (display and context) - while (!CORE.Window.ready) - { - // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) - { - // Process this event - if (platform.source != NULL) platform.source->process(platform.app, platform.source); - - // NOTE: Never close window, native activity is controlled by the system! - //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; - } - } + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- } @@ -279,28 +215,9 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - // Platform specific close window - //-------------------------------------------------------------- - // Close surface, context and display - if (platform.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - 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; - } + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -690,25 +607,108 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) +{ + CORE.Window.currentFbo.width = CORE.Window.screen.width; + CORE.Window.currentFbo.height = CORE.Window.screen.width; + + // Set desired windows flags before initializing anything + ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + + int orientation = AConfiguration_getOrientation(platform.app->config); + + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); + + // TODO: Automatic orientation doesn't seem to work + if (width <= height) + { + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); + } + else + { + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); + } + + //AConfiguration_getDensity(platform.app->config); + //AConfiguration_getKeyboard(platform.app->config); + //AConfiguration_getScreenSize(platform.app->config); + //AConfiguration_getScreenLong(platform.app->config); + + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + platform.app->onAppCmd = AndroidCommandCallback; + + // Initialize input events system + platform.app->onInputEvent = AndroidInputCallback; + + // Initialize assets manager + InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); + + // Initialize base path for storage + CORE.Storage.basePath = platform.app->activity->internalDataPath; + + // 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 + + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Wait for window to be initialized (display and context) + while (!CORE.Window.ready) + { + // Process events loop + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) + { + // Process this event + if (platform.source != NULL) platform.source->process(platform.app, platform.source); + + // NOTE: Never close window, native activity is controlled by the system! + //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; + } + } +} + +// Close platform +static void ClosePlatform(void) +{ + // Close surface, context and display + if (platform.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + 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; + } +} + // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size // NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +static bool InitGraphicsDevice(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -748,7 +748,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } // Initialize the EGL device connection @@ -756,7 +756,7 @@ static bool InitGraphicsDevice(int width, int height) { // 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; + return -1; } // Get an appropriate EGL framebuffer configuration @@ -770,7 +770,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -799,7 +799,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -819,19 +819,11 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // 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); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - CORE.Window.ready = true; if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - return true; + return 0; } // ANDROID: Process activity lifecycle commands @@ -874,24 +866,49 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window); // Initialize graphics device (display device and OpenGL context) - InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); + InitGraphicsDevice(); + + // 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); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); // Initialize hi-res timer InitTimer(); - // Initialize random seed - srand((unsigned int)time(NULL)); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext LoadFontDefault(); - Rectangle rec = GetFontDefault().recs[95]; - // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering #if defined(SUPPORT_MODULE_RSHAPES) - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: 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 + #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 + + // 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 diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 8fa1edd78..81da488e3 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -111,7 +111,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error @@ -176,53 +177,31 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // 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 structure to 0 + 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 = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // Platform specific init window - //-------------------------------------------------------------- - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ - - // Initialize graphics device - // NOTE: returns true if window and graphic device has been initialized successfully - // WARNING: Actually, all window initialization and input callbacks initialization is - // done inside InitGraphicsDevice(), this functionality should be changed! - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); + // 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); - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -266,6 +245,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } @@ -287,14 +269,9 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - // Platform specific close window - //-------------------------------------------------------------- - glfwDestroyWindow(platform.handle); - glfwTerminate(); - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -1379,34 +1356,28 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) + glfwInitAllocator(&allocator); +*/ #if defined(__APPLE__) glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); #endif - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } + // Initialize GLFW internal global state + int result = glfwInit(); + if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } glfwDefaultWindowHints(); // Set default windows hints //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits @@ -1528,7 +1499,7 @@ static bool InitGraphicsDevice(int width, int height) if (!monitor) { TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); - return false; + return -1; } const GLFWvidmode *mode = glfwGetVideoMode(monitor); @@ -1617,7 +1588,7 @@ static bool InitGraphicsDevice(int width, int height) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; + return -1; } // Set window callback events @@ -1683,22 +1654,35 @@ static bool InitGraphicsDevice(int width, int height) // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); - // 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); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - 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); - return true; + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + return 0; } +// Close platform +static void ClosePlatform(void) +{ + glfwDestroyWindow(platform.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif +} + + // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2644c5e73..213f2c546 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -140,21 +140,22 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform -static void InitKeyboard(void); // Initialize raw keyboard system -static void RestoreKeyboard(void); // Restore keyboard system +static void InitKeyboard(void); // Initialize raw keyboard system +static void RestoreKeyboard(void); // Restore keyboard system #if defined(SUPPORT_SSH_KEYBOARD_RPI) -static void ProcessKeyboard(void); // Process keyboard events +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 *EventThread(void *arg); // Input device events reading thread +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 *EventThread(void *arg); // Input device events reading thread -static void InitGamepad(void); // Initialize raw gamepad input -static void *GamepadThread(void *arg); // Mouse reading thread +static void InitGamepad(void); // Initialize raw gamepad input +static void *GamepadThread(void *arg); // Mouse reading thread 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 @@ -204,49 +205,31 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // 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)); + 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.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // Platform specific init window + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); + + // 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); - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - 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 - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - //-------------------------------------------------------------- - - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -289,7 +272,10 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif - + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -315,93 +301,9 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - // Platform specific close window - //-------------------------------------------------------------- - 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; - } - - // Wait for mouse and gamepad threads to finish before closing - // NOTE: Those threads should already have finished at this point - // because they are controlled by CORE.Window.shouldClose variable - - CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called - - // Close the evdev keyboard - if (platform.keyboardFd != -1) - { - close(platform.keyboardFd); - platform.keyboardFd = -1; - } - - for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (platform.eventWorker[i].threadId) - { - pthread_join(platform.eventWorker[i].threadId, NULL); - } - } - - if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -808,28 +710,9 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the window minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - - CORE.Window.fullscreen = true; - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - platform.fd = -1; platform.connector = NULL; platform.modeIndex = -1; @@ -838,6 +721,9 @@ static bool InitGraphicsDevice(int width, int height) platform.gbmSurface = NULL; platform.prevBO = NULL; platform.prevFB = 0; + + 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); @@ -861,14 +747,14 @@ static bool InitGraphicsDevice(int width, int height) if (platform.fd == -1) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); - return false; + return -1; } drmModeRes *res = drmModeGetResources(platform.fd); if (!res) { TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); @@ -897,7 +783,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); drmModeFreeResources(res); - return false; + return -1; } drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); @@ -905,7 +791,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); drmModeFreeResources(res); - return false; + return -1; } platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); @@ -914,7 +800,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } // If InitWindow should use the current mode find it in the connector's mode list @@ -929,7 +815,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } CORE.Window.screen.width = CORE.Window.display.width; @@ -957,7 +843,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay; @@ -982,7 +868,7 @@ static bool InitGraphicsDevice(int width, int height) if (!platform.gbmDevice) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); - return false; + return -1; } platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay, @@ -990,7 +876,7 @@ static bool InitGraphicsDevice(int width, int height) if (!platform.gbmSurface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); - return false; + return -1; } EGLint samples = 0; @@ -1030,7 +916,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } // Initialize the EGL device connection @@ -1038,13 +924,13 @@ static bool InitGraphicsDevice(int width, int height) { // 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; + return -1; } if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); @@ -1053,7 +939,7 @@ static bool InitGraphicsDevice(int width, int height) if (!configs) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); - return false; + return -1; } EGLint matchingNumConfigs = 0; @@ -1061,7 +947,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); free(configs); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs); @@ -1091,7 +977,7 @@ static bool InitGraphicsDevice(int width, int height) if (!found) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config"); - return false; + return -1; } // Set rendering API @@ -1102,7 +988,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -1111,7 +997,7 @@ static bool InitGraphicsDevice(int width, int height) if (EGL_NO_SURFACE == platform.surface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); - return false; + return -1; } // At this point we need to manage render size vs screen size @@ -1127,7 +1013,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -1147,19 +1033,123 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // 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); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - 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); - return true; + // 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 + + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + + return 0; } +// Close platform +static 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; + } + + // Wait for mouse and gamepad threads to finish before closing + // NOTE: Those threads should already have finished at this point + // because they are controlled by CORE.Window.shouldClose variable + + CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called + + // Close the evdev keyboard + if (platform.keyboardFd != -1) + { + close(platform.keyboardFd); + platform.keyboardFd = -1; + } + + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (platform.eventWorker[i].threadId) + { + pthread_join(platform.eventWorker[i].threadId, NULL); + } + } + + if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); +} + + // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 88b3c4a7e..3929de4b3 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -73,7 +73,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static bool InitGraphicsDevice(void); // Initialize graphics device //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -142,19 +143,15 @@ void InitWindow(int width, int height, const char *title) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - // Initialize hi-res timer - InitTimer(); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - //-------------------------------------------------------------- + // 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); - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -198,6 +195,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } @@ -597,25 +597,9 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -677,7 +661,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -706,7 +690,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -726,19 +710,24 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // 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); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - CORE.Window.ready = true; + + // 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; } - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); - return true; + return 0; +} + +// Close platform +static void ClosePlatform(void) +{ + // TODO: De-initialize graphics, inputs and more } // EOF diff --git a/src/rcore_web.c b/src/rcore_web.c index 277d1378f..3b9373bbf 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -87,17 +87,18 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static 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 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 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 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 @@ -107,11 +108,12 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); 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 -// Emscripten callback events +// 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 EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); +// Emscripten input callback events static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); @@ -160,67 +162,32 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // 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)); + 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.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Platform specific init window - //-------------------------------------------------------------- - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // 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) - // 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) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - - // Trigger this once to get initial window sizing - 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); - - // 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_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- + // 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); - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -264,6 +231,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } @@ -285,10 +255,9 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - // Platform specific close window - //-------------------------------------------------------------- - glfwDestroyWindow(platform.handle); - glfwTerminate(); + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -814,43 +783,14 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - glfwInitAllocator(&allocator); -*/ - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } + // Initialize GLFW internal global state + int result = glfwInit(); + if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } glfwDefaultWindowHints(); // Set default windows hints // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits @@ -1016,7 +956,7 @@ static bool InitGraphicsDevice(int width, int height) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; + return -1; } // WARNING: glfwCreateWindow() title doesn't work with emscripten @@ -1037,6 +977,12 @@ static bool InitGraphicsDevice(int width, int height) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); // 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 @@ -1055,22 +1001,54 @@ static bool InitGraphicsDevice(int width, int 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 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); - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - // 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); + // 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) + // 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) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + // Trigger this once to get initial window sizing + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + // 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); - return true; + // Support mouse events + emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); + + // 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_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + + return 0; +} + +// Close platform +static void ClosePlatform(void) +{ + glfwDestroyWindow(platform.handle); + glfwTerminate(); } // GLFW3 Error Callback, runs on GLFW3 error From 54950f9a3d2df2e0f9907715d6c355133d011456 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 12:55:31 +0200 Subject: [PATCH 0719/1710] Make sure CORE.Window.ready is set --- src/rcore_desktop.c | 2 ++ src/rcore_drm.c | 2 ++ src/rcore_web.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 81da488e3..fe5a02aac 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1657,6 +1657,8 @@ static int InitPlatform(void) rlLoadExtensions(glfwGetProcAddress); if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation // 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; } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 213f2c546..8f1bfa429 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -1035,6 +1035,8 @@ static int InitPlatform(void) if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation + // 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); diff --git a/src/rcore_web.c b/src/rcore_web.c index 3b9373bbf..15c7626dd 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1002,6 +1002,8 @@ static int InitPlatform(void) 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: Proper validation on windows/context creation + // 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); From d31b439e04d311ea8068a1d20d573b73c812c645 Mon Sep 17 00:00:00 2001 From: BeardedBread Date: Sat, 14 Oct 2023 21:10:33 +0800 Subject: [PATCH 0720/1710] Implement SetMouseCursor for PLATFORM_WEB (#3414) --- src/rcore_web.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 15c7626dd..71e818ea8 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,7 +659,34 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); + const char *cursorName; + switch (cursor) + { + case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; + case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; + case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; + case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; + case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; + case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; + case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; + case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; + case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + + case MOUSE_CURSOR_ARROW: // can't find a name specifically for arrow cursor + case MOUSE_CURSOR_DEFAULT: + { + cursorName = "default"; + } break; + + default: + { + TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); + cursorName = "default"; + } break; + } + + // Set the cursor element on the CSS + EM_ASM({document.body.style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 2498170b9525df447f63da4056e5c8c0e6d1d8fc Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:11:56 +0200 Subject: [PATCH 0721/1710] Fix screen size check in `InitPlatform()` (#3415) --- src/rcore_android.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 3e3b8cd12..b7680adbc 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -611,7 +611,7 @@ void PollInputEvents(void) static int InitPlatform(void) { CORE.Window.currentFbo.width = CORE.Window.screen.width; - CORE.Window.currentFbo.height = CORE.Window.screen.width; + CORE.Window.currentFbo.height = CORE.Window.screen.height; // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -622,7 +622,7 @@ static int InitPlatform(void) else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); // TODO: Automatic orientation doesn't seem to work - if (width <= height) + if (CORE.Window.screen.width <= CORE.Window.screen.height) { AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); From bf639f02a8a05f678c6d2459871a9e466be34b97 Mon Sep 17 00:00:00 2001 From: Blue <69832658+bluesillybeard@users.noreply.github.com> Date: Sat, 14 Oct 2023 14:38:36 -0600 Subject: [PATCH 0722/1710] Fix raygui.c leftover from zig build (#3417) --- src/build.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/build.zig b/src/build.zig index 53e074245..5d7ddbf3f 100644 --- a/src/build.zig +++ b/src/build.zig @@ -53,12 +53,12 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, raylib_flags); } - var gen_step = std.build.Step.WriteFile.create(b); + var gen_step = b.addWriteFiles(); raylib.step.dependOn(&gen_step.step); if (options.raygui) { - _ = gen_step.add(srcdir ++ "/raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); - raylib.addCSourceFile(.{ .file = .{ .path = srcdir ++ "/raygui.c" }, .flags = raylib_flags }); + const raygui_c_path = gen_step.add("raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); + raylib.addCSourceFile(.{ .file = raygui_c_path, .flags = raylib_flags }); raylib.addIncludePath(.{ .path = srcdir }); raylib.addIncludePath(.{ .path = srcdir ++ "/../../raygui/src" }); } From b79e38109268bc23eeceb05212017d3ed1359170 Mon Sep 17 00:00:00 2001 From: BeardedBread Date: Sun, 15 Oct 2023 04:42:03 +0800 Subject: [PATCH 0723/1710] Fix SetMouseCursor implementation for PLATFORM_WEB (#3416) * Fix SetMouseCursor implementation for PLATFORM_WEB - Restrict function to only set the cursor inside the canvas * Set the CORE input mouse --- src/rcore_web.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 71e818ea8..34836b7f0 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,6 +659,7 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { + CORE.Input.Mouse.cursor = cursor; const char *cursorName; switch (cursor) { @@ -682,11 +683,13 @@ void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); cursorName = "default"; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; } break; } - // Set the cursor element on the CSS - EM_ASM({document.body.style.cursor = UTF8ToString($0);}, cursorName); + // Set the cursor element on the canvas CSS + // The canvas is coded to the Id "canvas" on init + EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 37e3ffcaac9be9c07a9724b334d9bca3ec8b7052 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 22:45:56 +0200 Subject: [PATCH 0724/1710] REVIEWED: `SetMouseCursor()` #3416 --- src/rcore_web.c | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 34836b7f0..af2b6e36f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,37 +659,35 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - CORE.Input.Mouse.cursor = cursor; - const char *cursorName; - switch (cursor) + if (CORE.Input.Mouse.cursor != cursor) { - case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; - case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; - case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; - case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; - case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; - case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; - case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; - case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; - case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + const char *cursorName = NULL; + CORE.Input.Mouse.cursor = cursor; - case MOUSE_CURSOR_ARROW: // can't find a name specifically for arrow cursor - case MOUSE_CURSOR_DEFAULT: + switch (cursor) { - cursorName = "default"; - } break; + case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; + case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; + case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; + case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; + case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; + case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; + case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; + case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; + case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + case MOUSE_CURSOR_ARROW: // WARNING: It does not seem t be a specific cursor for arrow + case MOUSE_CURSOR_DEFAULT: cursorName = "default"; break; + default: + { + cursorName = "default"; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; + } break; + } - default: - { - TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); - cursorName = "default"; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; - } break; + // Set the cursor element on the canvas CSS + // The canvas is coded to the Id "canvas" on init + EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } - - // Set the cursor element on the canvas CSS - // The canvas is coded to the Id "canvas" on init - EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 6d7112fde7ee2f3372f7cdc95a9f474328973cdc Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 14 Oct 2023 22:46:46 +0200 Subject: [PATCH 0725/1710] Fix some omissions (#3418) Changes the return type of `InitGraphicsDevice()` from `bool` to `int`. Adds a return at the end of `InitPlatform()`. --- src/rcore_android.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index b7680adbc..4dc0e26c4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -676,6 +676,8 @@ static int InitPlatform(void) //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } + + return 0; } // Close platform @@ -707,7 +709,7 @@ static void ClosePlatform(void) // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size // NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(void) +static int InitGraphicsDevice(void) { CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; From 781f7175308f0b708393b1135657aa191ca2b508 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 14 Oct 2023 17:47:35 -0300 Subject: [PATCH 0726/1710] Remove the rcore.h include from drm, web, template (#3420) --- src/rcore_drm.c | 22 ++++++++++------------ src/rcore_template.c | 12 +++++------- src/rcore_web.c | 18 ++++++++---------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 8f1bfa429..3f03bfa12 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -47,8 +47,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include // POSIX file control definitions - open(), creat(), fcntl() #include // POSIX standard function definitions - read(), close(), STDIN_FILENO #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() @@ -218,12 +216,12 @@ void InitWindow(int width, int height, const char *title) 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); @@ -272,10 +270,10 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif - + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -302,7 +300,7 @@ void CloseWindow(void) #endif // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -721,7 +719,7 @@ static int InitPlatform(void) platform.gbmSurface = NULL; platform.prevBO = NULL; platform.prevFB = 0; - + CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -1034,9 +1032,9 @@ static int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - + // 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); @@ -1057,7 +1055,7 @@ static int InitPlatform(void) InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init InitKeyboard(); // Keyboard init (stdin) - + return 0; } diff --git a/src/rcore_template.c b/src/rcore_template.c index 3929de4b3..89be8ac00 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -46,8 +46,6 @@ * **********************************************************************************************/ -#include "rcore.h" - // TODO: Include the platform specific libraries //---------------------------------------------------------------------------------- @@ -130,8 +128,8 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; - - + + // TODO: Platform specific init window //-------------------------------------------------------------- CORE.Window.screen.width = width; @@ -144,7 +142,7 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); - + // 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); @@ -711,13 +709,13 @@ static int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); CORE.Window.ready = true; - + // 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; } // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); diff --git a/src/rcore_web.c b/src/rcore_web.c index af2b6e36f..9e4ce5b97 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -45,8 +45,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) // #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management @@ -177,7 +175,7 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- @@ -256,7 +254,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -1005,7 +1003,7 @@ static int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); @@ -1029,19 +1027,19 @@ static int InitPlatform(void) 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: Proper validation on windows/context creation - + // 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); // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -1070,7 +1068,7 @@ static int InitPlatform(void) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); - + return 0; } From 18bedbd0952c27b0eb8bc5df0df4acf589cef181 Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Sat, 14 Oct 2023 16:51:35 -0400 Subject: [PATCH 0727/1710] [core] Change axisCount to be an array (#3421) * Update `PLATFORM_DRM` implementation of `GetGamepadAxisCount` * Update * Update `PLATFORM_DRM` implementation of `GetGamepadName` * Add example to test gamepad info functions Fix typo * Update new gamepad info example * Move axis count update out of GamepadThread - race condition * Remove pointless if statement * Start integrating stuff from the mikesinput lib * Add more logging * Add semicolon * Add forgotten static * More fixes * Update axisCount to be array * More debugging * Add forgotten index to ready check * Add path logging * Missing parenthesis * Add missing slash * Fix axis count being reset to 0 * Fix missing paren * Test polling joystick button events * Major updates * Fix missing array index * Fix another missing array index * Update example * dumb logging * Wrong constant for ev.code handling * More dumb logging * Remove some logging * Add FPS to gamepad info example and try for max FPS * tweak * Revert example * Add fps back * Clean up after merge * Switch axisCount to be an array --- examples/core/core_input_gamepad_info.c | 20 +++++++++++++++++--- src/rcore.c | 2 +- src/rcore.h | 2 +- src/rcore_android.c | 2 +- src/rcore_desktop.c | 4 ++-- src/rcore_drm.c | 4 ++-- src/rcore_template.c | 2 +- src/rcore_web.c | 4 ++-- 8 files changed, 27 insertions(+), 13 deletions(-) diff --git a/examples/core/core_input_gamepad_info.c b/examples/core/core_input_gamepad_info.c index 84a687cd8..55f0354b5 100644 --- a/examples/core/core_input_gamepad_info.c +++ b/examples/core/core_input_gamepad_info.c @@ -41,15 +41,29 @@ int main(void) ClearBackground(RAYWHITE); - for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. + for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. { if (IsGamepadAvailable(i)) { - DrawText(TextFormat("Gamepad:\n\tName: %s\n\tAxes: %d", GetGamepadName(i), GetGamepadAxisCount(i)), 10, y, 20, BLACK); - y += 40; + DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 20, BLACK); + y += 30; + DrawText(TextFormat("\tAxis count: %d", GetGamepadAxisCount(i)), 10, y, 20, BLACK); + y += 30; + for (int axis = 0; axis < GetGamepadAxisCount(i); axis++) + { + DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 20, BLACK); + y += 30; + } + for (int button = 0; button < 32; button++) + { + DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 20, BLACK); + y += 30; + } } } + DrawFPS(GetScreenWidth() - 100, 100); + EndDrawing(); } diff --git a/src/rcore.c b/src/rcore.c index e6015b33f..f94731f90 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2168,7 +2168,7 @@ int GetGamepadButtonPressed(void) // Get gamepad axis count int GetGamepadAxisCount(int gamepad) { - return CORE.Input.Gamepad.axisCount; + return CORE.Input.Gamepad.axisCount[gamepad]; } // Get axis movement vector for a gamepad diff --git a/src/rcore.h b/src/rcore.h index dbff6ab13..1127585a0 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -179,7 +179,7 @@ typedef struct CoreData { } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed - int axisCount; // Register number of available gamepad axis + 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 diff --git a/src/rcore_android.c b/src/rcore_android.c index 4dc0e26c4..98ce64a6b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -566,7 +566,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //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]; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index fe5a02aac..4039bbd84 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1234,7 +1234,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) // Register previous keys states @@ -1341,7 +1341,7 @@ void PollInputEvents(void) 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); - CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; + CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1; } } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 3f03bfa12..1fb83c8fe 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -655,7 +655,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Register previous keys states for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) @@ -1847,7 +1847,7 @@ static void InitGamepad(void) } ioctl(platform.gamepadStreamFd[i], JSIOCGNAME(64), &CORE.Input.Gamepad.name[i]); - ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount); + ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount[i]); } } } diff --git a/src/rcore_template.c b/src/rcore_template.c index 89be8ac00..4d2db9d30 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -569,7 +569,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //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]; diff --git a/src/rcore_web.c b/src/rcore_web.c index 9e4ce5b97..f8e1e5b2d 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -705,7 +705,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) // Register previous keys states @@ -799,7 +799,7 @@ void PollInputEvents(void) CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; } - CORE.Input.Gamepad.axisCount = gamepadState.numAxes; + CORE.Input.Gamepad.axisCount[i] = gamepadState.numAxes; } } } From a75251f0a9732f42cfeb8d018210a358f46c910d Mon Sep 17 00:00:00 2001 From: Johnathan Corkery Date: Sun, 15 Oct 2023 18:25:39 -0400 Subject: [PATCH 0728/1710] Inclusion of Matte to BINDINGS.md (#3427) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index e08f8f79e..400bc3846 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -41,6 +41,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | KaylibKit | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/KaylibKit | | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | +| raylib-matte | 4.6-dev | [Matte](https://github.com/jcorks/matte/) | MIT | https://github.com/jcorks/raylib-matte | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | From 84818c96f236cc4ff172dda2013aba231f68ff13 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Oct 2023 00:51:44 +0200 Subject: [PATCH 0729/1710] ADDED: NEW PLATFORM: SDL (DESKTOP) `rcore_desktop_sdl` #3313 --- src/rcore.c | 22 +- src/rcore_desktop_sdl.c | 755 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 769 insertions(+), 8 deletions(-) create mode 100644 src/rcore_desktop_sdl.c diff --git a/src/rcore.c b/src/rcore.c index f94731f90..b813b0197 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,14 +3,18 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: Windows (Win32, Win64) -* - PLATFORM_DESKTOP: Linux (X11 desktop mode) -* - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_DESKTOP: OSX/macOS -* - PLATFORM_WEB: HTML5 (WebAssembly) -* - PLATFORM_DRM: Raspberry Pi 0-5 -* - PLATFORM_DRM: Linux native mode (KMS driver) -* - PLATFORM_ANDROID: Android (ARM, ARM64) +* - PLATFORM_DESKTOP: +* > Windows (Win32, Win64) +* > Linux (X11/Wayland desktop mode) +* > macOS/OSX (x64, arm64) +* > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - PLATFORM_WEB: +* > HTML5 (WebAssembly) +* - PLATFORM_DRM: +* > Raspberry Pi 0-5 +* > Linux native mode (KMS driver) +* - PLATFORM_ANDROID: +* > Android (ARM, ARM64) * * CONFIGURATION: * #define SUPPORT_DEFAULT_FONT (default) @@ -303,6 +307,8 @@ const char *TextFormat(const char *text, ...); // Formatting of text with // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) #include "rcore_desktop.c" +#elif defined(PLATFORM_DESKTOP_SDL) + #include "rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) #include "rcore_web.c" #elif defined(PLATFORM_DRM) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c new file mode 100644 index 000000000..e85d18965 --- /dev/null +++ b/src/rcore_desktop_sdl.c @@ -0,0 +1,755 @@ +/********************************************************************************************** +* +* rcore_desktop_sdl - Functions to manage window, graphics device and inputs +* +* PLATFORM: DESKTOP: SDL +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - OSX/macOS (x64, arm64) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - 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) +* - Dependency 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +#include "SDL.h" // SDL base library (window/rendered, input, timming... functionality) +#include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { + SDL_Window *window; + SDL_GLContext glContext; + + SDL_Joystick *gamepad; +} PlatformData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + 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); + + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + +#if defined(SUPPORT_MODULE_RTEXT) && 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 +#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 +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); +} + +// 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(); + //-------------------------------------------------------------- + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// 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) +{ + //SDL_SetWindowFullscreen +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + //SDL_SetWindowFullscreen +} + +// 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) +{ + //SDL_HideWindow(platform.window); +} + +// 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) +{ + 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; +} + +// 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) +{ + 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) +void OpenURL(const char *url) +{ + SDL_OpenURL(url); +} + +//---------------------------------------------------------------------------------- +// 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 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"); +} + +// 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 = GAMEPAD_BUTTON_UNKNOWN; + for (int i = 0; i < MAX_GAMEPADS; i++) CORE.Input.Gamepad.axisCount[i] = 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; + } + + // Poll input events for current plaform + //----------------------------------------------------------------------------- + /* + // 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); + } + */ + + 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; + + // Window events are also polled (Minimized, maximized, close...) + case SDL_WINDOWEVENT: + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_LEAVE: + case SDL_WINDOWEVENT_HIDDEN: + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_FOCUS_LOST: + case SDL_WINDOWEVENT_ENTER: + 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: + { + CORE.Input.Keyboard.currentKeyState[event.key.keysym.sym] = 1; + + if (event.key.keysym.sym == SDLK_ESCAPE) + { + CORE.Window.shouldClose = true; + } + } break; + case SDL_KEYUP: break; + + // Check mouse events + case SDL_MOUSEBUTTONDOWN: + { + CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; + } break; + case SDL_MOUSEBUTTONUP: 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: + { + CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; + CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; + } break; + + // Check gamepad events + case SDL_JOYAXISMOTION: + { + // Motion on gamepad 0 + if (event.jaxis.which == 0) + { + // X axis motion + if (event.jaxis.axis == 0) + { + //... + } + // Y axis motion + else if (event.jaxis.axis == 1) + { + //... + } + } + } break; + default: break; + } + } + //----------------------------------------------------------------------------- +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) +{ + // Initialize SDL internal global state + int result = SDL_Init(SDL_INIT_EVERYTHING); + if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } + + 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 + 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); + //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + 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 succesfully + if ((platform.window != NULL) && (platform.glContext != NULL)) + { + 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 graphic device"); return -1; } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(SDL_GL_GetProcAddress); + + + // Init input gamepad + if (SDL_NumJoysticks() >= 1) + { + SDL_Joystick *gamepad = SDL_JoystickOpen(0); + //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); + } + + // Initialize hi-res timer + //InitTimer(); + CORE.Time.previous = GetTime(); // Get time as double + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + return 0; +} + +static void ClosePlatform(void) +{ + SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context + SDL_DestroyWindow(platform.window); + SDL_Quit(); // Deinitialize SDL internal global state +} +// EOF From 73363f829b709dddedf21a5e6b696f37e44bce19 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 16 Oct 2023 04:43:20 -0300 Subject: [PATCH 0730/1710] [core] Fix some mouse issues on `SDL` (#3428) * Fix mouse wheel getting stucked scrolling up or down * Fix mouse movement on 3D * Fix mouse button presses --- src/rcore_desktop_sdl.c | 57 +++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index e85d18965..4ce09cf80 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -60,7 +60,7 @@ typedef struct { SDL_Window *window; SDL_GLContext glContext; - + SDL_Joystick *gamepad; } PlatformData; @@ -134,12 +134,12 @@ void InitWindow(int width, int height, const char *title) 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); @@ -214,7 +214,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -481,7 +481,7 @@ void SwapScreenBuffer(void) double GetTime(void) { unsigned int ms = SDL_GetTicks(); // Elapsed time in milliseconds since SDL_Init() - double time = (double)ms/1000; + double time = (double)ms/1000; return time; } @@ -531,6 +531,13 @@ void PollInputEvents(void) // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + // Reset mouse wheel + CORE.Input.Mouse.currentWheelMove.x = 0; + CORE.Input.Mouse.currentWheelMove.y = 0; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; for (int i = 0; i < MAX_GAMEPADS; i++) CORE.Input.Gamepad.axisCount[i] = 0; @@ -551,6 +558,9 @@ void PollInputEvents(void) 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 plaform //----------------------------------------------------------------------------- /* @@ -602,12 +612,15 @@ void PollInputEvents(void) case SDL_KEYUP: break; // Check mouse events - case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONDOWN: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; } break; - case SDL_MOUSEBUTTONUP: break; - case SDL_MOUSEWHEEL: + case SDL_MOUSEBUTTONUP: + { + CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 0; + } break; + case SDL_MOUSEWHEEL: { CORE.Input.Mouse.currentWheelMove.x = (float)event.wheel.x; CORE.Input.Mouse.currentWheelMove.y = (float)event.wheel.y; @@ -653,7 +666,7 @@ static int InitPlatform(void) // Initialize SDL internal global state int result = SDL_Init(SDL_INIT_EVERYTHING); if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } - + unsigned int flags = 0; flags |= SDL_WINDOW_SHOWN; flags |= SDL_WINDOW_OPENGL; @@ -662,12 +675,12 @@ static int InitPlatform(void) flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured // Check window creation flags - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) + 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; @@ -684,11 +697,11 @@ static int InitPlatform(void) 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 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); @@ -702,12 +715,12 @@ static int InitPlatform(void) // 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 succesfully - if ((platform.window != NULL) && (platform.glContext != NULL)) + if ((platform.window != NULL) && (platform.glContext != NULL)) { CORE.Window.ready = true; @@ -728,21 +741,21 @@ static int InitPlatform(void) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(SDL_GL_GetProcAddress); - + // Init input gamepad if (SDL_NumJoysticks() >= 1) { SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } - + // Initialize hi-res timer //InitTimer(); CORE.Time.previous = GetTime(); // Get time as double - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + return 0; } From c4296b166af5f1859707234dddae73d4dce84628 Mon Sep 17 00:00:00 2001 From: neyrox Date: Mon, 16 Oct 2023 15:06:12 +0300 Subject: [PATCH 0731/1710] Fix GenMeshPlane when resX != resZ (#3425) Co-authored-by: Stanislav Yablonskiy --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index a11ecf799..f9018eaaf 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2249,7 +2249,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) for (int face = 0; face < numFaces; face++) { // Retrieve lower left corner from face ind - int i = face % (resX - 1) + (face/(resZ - 1)*resX); + int i = face + face / (resX - 1); triangles[t++] = i + resX; triangles[t++] = i + 1; From 859c67792a839021b98b7abf25a664b7109cff3f Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Mon, 16 Oct 2023 13:08:55 +0100 Subject: [PATCH 0732/1710] Make sure rcore.o gets compiled in more situations (#3423) Currently doing the following: ``` make touch rcore_desktop.c make ``` Will not result in rcore.o getting compiled again, despite that rcore_desktop.c has changed This commit resolves that --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index e75ae5368..56b6d0632 100644 --- a/src/Makefile +++ b/src/Makefile @@ -632,6 +632,9 @@ endif # Compile all modules with their prerequisites +# Prerequisites of core module +rcore.o : rcore_android.c rcore_desktop.c rcore_drm.c rcore_template.c rcore_web.c + # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) From fab99b8309b483f81c8a7b2524da0e0e0079560f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:59:08 -0300 Subject: [PATCH 0733/1710] Remove rcore.h include from android (#3429) --- src/rcore_android.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 98ce64a6b..cfc8edf61 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -46,8 +46,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include // Required for: android_app struct and activity management #include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others //#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) @@ -188,7 +186,7 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- } @@ -216,7 +214,7 @@ void CloseWindow(void) #endif // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -869,7 +867,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Initialize graphics device (display device and OpenGL context) InitGraphicsDevice(); - + // 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); @@ -908,7 +906,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes #endif #endif - + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); From af83764f4fe83d9f036fab94a6ab886ffdf82587 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:53:53 +0300 Subject: [PATCH 0734/1710] Implement GetCurrentMonitor in rcore_desktop_sdl (#3431) * Implemented GetCurrentMonitor * remove traceloog in GetCurrentMonitor --- src/rcore_desktop_sdl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 4ce09cf80..7f593abcb 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -356,8 +356,7 @@ int GetMonitorCount(void) // Get number of monitors int GetCurrentMonitor(void) { - TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); - return 0; + return SDL_GetWindowDisplayIndex(platform.window); } // Get selected monitor position From 7290ea9bfbccd62ab5e4870ca84868c2aecf5382 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Oct 2023 10:59:25 +0200 Subject: [PATCH 0735/1710] Update models_mesh_generation.c --- examples/models/models_mesh_generation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index d17a20a12..94e0a4c48 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -36,7 +36,7 @@ int main(void) Model models[NUM_MODELS] = { 0 }; - models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 5, 5)); + models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 4, 3)); models[1] = LoadModelFromMesh(GenMeshCube(2.0f, 1.0f, 2.0f)); models[2] = LoadModelFromMesh(GenMeshSphere(2, 32, 32)); models[3] = LoadModelFromMesh(GenMeshHemiSphere(2, 16, 16)); From 99ede0f747f6d8a0b0f7d0d9371b9d46af16cb80 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Oct 2023 11:09:56 +0200 Subject: [PATCH 0736/1710] Added some notes for alternative implementations #3362 --- src/rtext.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 2d72bbe6b..5b43bfb92 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1344,10 +1344,12 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint) // Get text length in bytes, check for \0 character unsigned int TextLength(const char *text) { - unsigned int length = 0; //strlen(text) + unsigned int length = 0; if (text != NULL) { + // NOTE: Alternative: use strlen(text) + while (*text++) length++; } @@ -1415,6 +1417,8 @@ int TextCopy(char *dst, const char *src) if ((src != NULL) && (dst != NULL)) { + // NOTE: Alternative: use strcpy(dst, src) + while (*src != '\0') { *dst = *src; @@ -1459,6 +1463,8 @@ const char *TextSubtext(const char *text, int position, int length) } if (length >= textLength) length = textLength; + + // NOTE: Alternative: memcpy(buffer, text + position, length) for (int c = 0 ; c < length ; c++) { From f353cd1c3a884d6b80af311d40586ad414e09efd Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 17 Oct 2023 07:01:01 -0300 Subject: [PATCH 0737/1710] [core] Add some missing implementations to `SDL` (#3432) * Add missing implementations * Add missing implementations 2 * Add missing implementations 3 * Add missing implementations 4 * Add missing implementations 5 --- src/rcore_desktop_sdl.c | 87 ++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 7f593abcb..0910343db 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -292,13 +292,18 @@ void SetWindowIcons(Image *images, int count) // 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) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); + SDL_SetWindowPosition(platform.window, x, y); + + CORE.Window.position.x = x; + CORE.Window.position.y = y; } // Set monitor for the current window @@ -324,13 +329,19 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform"); + 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) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); + if (opacity >= 1.0f) opacity = 1.0f; + else if (opacity <= 0.0f) opacity = 0.0f; + + SDL_SetWindowOpacity(platform.window, opacity); } // Set window focused @@ -349,8 +360,11 @@ void *GetWindowHandle(void) // Get number of monitors int GetMonitorCount(void) { - TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); - return 1; + int monitorCount = 0; + + monitorCount = SDL_GetNumVideoDisplays(); + + return monitorCount; } // Get number of monitors @@ -369,15 +383,39 @@ Vector2 GetMonitorPosition(int monitor) // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); - return 0; + int width = 0; + + int monitorCount = 0; + 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) { - TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); - return 0; + int height = 0; + + int monitorCount = 0; + 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 @@ -397,22 +435,43 @@ int GetMonitorPhysicalHeight(int monitor) // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); - return 0; + int refresh = 0; + + int monitorCount = 0; + 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) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); + int monitorCount = 0; + 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) { - TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform"); - return (Vector2){ 0, 0 }; + 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 From 80432fde62a31ff5e7ad61f8a0352de9642cd97f Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 17 Oct 2023 23:29:28 +0200 Subject: [PATCH 0738/1710] Fix SDL keyboard issue (#3435) * Fix SDL keyboard issue We have added a mapping table between raylib keys and SDL scancodes. * Change `ScancodeToKey` array type --- src/rcore_desktop_sdl.c | 134 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 0910343db..b66167c89 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -71,11 +71,119 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data +//---------------------------------------------------------------------------------- +// Local Variables Definition +//---------------------------------------------------------------------------------- +#define SCANCODE_MAPPED_NUM 100 +static const KeyboardKey ScancodeToKey[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 +}; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform +static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help convert SDL scancodes to raylib key //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -660,14 +768,21 @@ void PollInputEvents(void) // Keyboard events case SDL_KEYDOWN: { - CORE.Input.Keyboard.currentKeyState[event.key.keysym.sym] = 1; + KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); + if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 1; - if (event.key.keysym.sym == SDLK_ESCAPE) + // TODO: Put exitKey verification outside the switch? + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) { CORE.Window.shouldClose = true; } } break; - case SDL_KEYUP: break; + + case SDL_KEYUP: + { + KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); + if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0; + } break; // Check mouse events case SDL_MOUSEBUTTONDOWN: @@ -823,4 +938,13 @@ static void ClosePlatform(void) SDL_DestroyWindow(platform.window); SDL_Quit(); // Deinitialize SDL internal global state } + +static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) +{ + if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) + { + return ScancodeToKey[sdlScancode]; + } + return KEY_NULL; // No equivalent key in Raylib +} // EOF From d7d04a07a28a80569f440459f2a1ca4ea2fe2497 Mon Sep 17 00:00:00 2001 From: Alexey Kutepov Date: Wed, 18 Oct 2023 04:35:38 +0700 Subject: [PATCH 0739/1710] [raudio] Implement GetMasterVolume() (#3434) It feels a little unfinished when you can SetMasterVolume but can't really Get it. So to finish the symmetry here is the GetMasterVolume implementation. --- src/raudio.c | 8 ++++++++ src/raylib.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index a8d1b40e2..6a1096767 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -536,6 +536,14 @@ void SetMasterVolume(float volume) ma_device_set_master_volume(&AUDIO.System.device, volume); } +// Get master volume (listener) +float GetMasterVolume(void) +{ + float volume = 0.0f; + ma_device_get_master_volume(&AUDIO.System.device, &volume); + return volume; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Buffer management //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index c03e0a576..331bf5253 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1538,6 +1538,7 @@ RLAPI void InitAudioDevice(void); // Initial RLAPI void CloseAudioDevice(void); // Close the audio device and context RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully RLAPI void SetMasterVolume(float volume); // Set master volume (listener) +RLAPI float GetMasterVolume(void); // Get master volume (listener) // Wave/Sound loading/unloading functions RLAPI Wave LoadWave(const char *fileName); // Load wave data from file From 9534f48425bb87e205406fe950b606a2186ccaac Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Tue, 17 Oct 2023 23:36:42 +0200 Subject: [PATCH 0740/1710] fix build.zig (#3433) for zig master (2023-10-17) --- src/build.zig | 83 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/src/build.zig b/src/build.zig index 5d7ddbf3f..5e2b5916e 100644 --- a/src/build.zig +++ b/src/build.zig @@ -20,37 +20,55 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles(&.{ - srcdir ++ "/rcore.c", - srcdir ++ "/utils.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rcore.c", + srcdir ++ "/utils.c", + }, + .flags = raylib_flags, + }); if (options.raudio) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/raudio.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/raudio.c", + }, + .flags = raylib_flags, + }); } if (options.rmodels) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rmodels.c", - }, &[_][]const u8{ - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 - } ++ raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rmodels.c", + }, + .flags = &[_][]const u8{ + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 + } ++ raylib_flags, + }); } if (options.rshapes) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rshapes.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rshapes.c", + }, + .flags = raylib_flags, + }); } if (options.rtext) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rtext.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rtext.c", + }, + .flags = raylib_flags, + }); } if (options.rtextures) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rtextures.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rtextures.c", + }, + .flags = raylib_flags, + }); } var gen_step = b.addWriteFiles(); @@ -65,7 +83,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -75,7 +96,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -103,7 +127,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -122,10 +149,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags ++ raylib_flags_extra_macos, - ); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags ++ raylib_flags_extra_macos, + }); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); From 53cd60bb29c97951cd0379aa230d43f794f055ce Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Oct 2023 00:03:47 +0200 Subject: [PATCH 0741/1710] REVIEWED: Move `InitWindow()`/`CloseWindow()` to `rcore.c` #3313 --- src/rcore.c | 143 ++++++++++++++++++++++++++++++++++ src/rcore_android.c | 90 --------------------- src/rcore_desktop.c | 161 +++----------------------------------- src/rcore_desktop_sdl.c | 140 --------------------------------- src/rcore_drm.c | 144 ---------------------------------- src/rcore_template.c | 169 +--------------------------------------- src/rcore_web.c | 141 --------------------------------- 7 files changed, 153 insertions(+), 835 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b813b0197..c6770a5bb 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -281,6 +281,9 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font on extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform + static void InitTimer(void); // Initialize timer (hi-resolution if available) static void SetupFramebuffer(int width, int height); // Setup main framebuffer static void SetupViewport(int width, int height); // Set viewport for a provided width and height @@ -369,6 +372,146 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void EnableCursor(void) //void DisableCursor(void) +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + 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); + + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + +#if defined(SUPPORT_MODULE_RTEXT) && 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 +#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 +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); +} + +// 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(); + //-------------------------------------------------------------- + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + // Check if window has been initialized successfully bool IsWindowReady(void) { diff --git a/src/rcore_android.c b/src/rcore_android.c index cfc8edf61..bf55ece27 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -136,96 +136,6 @@ struct android_app *GetAndroidApp(void) // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - 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(); - //-------------------------------------------------------------- -} - -// 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 - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 4039bbd84..29969d4ca 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -142,162 +142,11 @@ static void JoystickCallback(int jid, int event); // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - 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); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && 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 -#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 -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); -} - -// 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(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close // NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked bool WindowShouldClose(void) { - if (CORE.Window.ready) - { - // 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 - glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); - - return CORE.Window.shouldClose; - } + if (CORE.Window.ready) return CORE.Window.shouldClose; else return true; } @@ -1349,6 +1198,14 @@ void PollInputEvents(void) 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) + + // 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 + glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); } diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index b66167c89..15c79cb87 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -194,146 +194,6 @@ static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help conv // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - 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); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && 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 -#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 -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); -} - -// 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(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 1fb83c8fe..3302ec362 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -168,150 +168,6 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - 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); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && 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 -#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 -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); -} - -// 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 - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close // NOTE: By default, if KEY_ESCAPE pressed bool WindowShouldClose(void) diff --git a/src/rcore_template.c b/src/rcore_template.c index 4d2db9d30..f94cca8c1 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -83,153 +83,6 @@ static bool InitGraphicsDevice(void); // Initialize graphics device // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - 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 - - // NOTE: Keep internal pointer to input title string (no copy) - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); - 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 = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // TODO: Platform specific init window - //-------------------------------------------------------------- - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Initialize graphics device - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - - - // 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); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && 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 -#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 -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); -} - -// 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 - - // Platform specific close window - //-------------------------------------------------------------- - // TODO. - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { @@ -503,27 +356,7 @@ void OpenURL(const char *url) if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { - JNIEnv *env = NULL; - JavaVM *vm = platform.app->activity->vm; - (*vm)->AttachCurrentThread(vm, &env, NULL); - - jstring urlString = (*env)->NewStringUTF(env, url); - jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); - jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); - jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); - - jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); - jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); - jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); - jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); - jobject intent = (*env)->AllocObject(env, intentClass); - - (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); - jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); - jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); - - (*vm)->DetachCurrentThread(vm); + // TODO: } } diff --git a/src/rcore_web.c b/src/rcore_web.c index f8e1e5b2d..465bb8957 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -125,147 +125,6 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - 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 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); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && 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 -#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 -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); -} - -// 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(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { From fc6152613f4bb97708935a9f8096d2ca4d9fbb76 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Oct 2023 00:33:05 +0200 Subject: [PATCH 0742/1710] REVIEWED: `raylib 5.0-dev` version for a future release --- examples/Makefile | 2 +- examples/Makefile.Web | 2 +- src/Makefile | 4 ++-- src/raylib.dll.rc | 8 ++++---- src/raylib.dll.rc.data | Bin 11246 -> 11246 bytes src/raylib.h | 6 +++--- src/raylib.rc | 8 ++++---- src/raylib.rc.data | Bin 11182 -> 11182 bytes 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 6031f05e9..74b3b874d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,7 +30,7 @@ PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 4.5.0 +RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. # Locations of raylib.h and libraylib.a/libraylib.so diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 267e02396..ac8d5af89 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -30,7 +30,7 @@ PLATFORM ?= PLATFORM_WEB # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 4.5.0 +RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. # Locations of raylib.h and libraylib.a/libraylib.so diff --git a/src/Makefile b/src/Makefile index 56b6d0632..e6c5106cc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -44,8 +44,8 @@ PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables -RAYLIB_VERSION = 4.5.0 -RAYLIB_API_VERSION = 450 +RAYLIB_VERSION = 5.0.0 +RAYLIB_API_VERSION = 500 # Define raylib source code path RAYLIB_SRC_PATH ?= ../src diff --git a/src/raylib.dll.rc b/src/raylib.dll.rc index c2a42dca2..e1455af7a 100644 --- a/src/raylib.dll.rc +++ b/src/raylib.dll.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 4,5,0,0 -PRODUCTVERSION 4,5,0,0 +FILEVERSION 5,0,0,0 +PRODUCTVERSION 5,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib dynamic library (www.raylib.com)" - VALUE "FileVersion", "4.5.0" + VALUE "FileVersion", "5.0.0" VALUE "InternalName", "raylib.dll" VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "raylib.dll" VALUE "ProductName", "raylib" - VALUE "ProductVersion", "4.5.0" + VALUE "ProductVersion", "5.0.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.dll.rc.data b/src/raylib.dll.rc.data index 7403bd020b0658ed1c8ee569850e2eff9a0b3a50..e93edcffbcb1e7363078ec6d16a9c83b79e50b3c 100644 GIT binary patch delta 51 zcmaDC{w{pO9Sv><237_LV4eI?Lwu4Z$7D9mYpkXWdJG1eA8I-?LYR{^wT}V-aI6ha delta 51 zcmaDC{w{pO9Sv?)1{MYo0Me5`YKTwL // Required for: va_list - Only used by TraceLogCallback -#define RAYLIB_VERSION_MAJOR 4 -#define RAYLIB_VERSION_MINOR 6 +#define RAYLIB_VERSION_MAJOR 5 +#define RAYLIB_VERSION_MINOR 0 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "4.6-dev" +#define RAYLIB_VERSION "5.0-dev" // 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 diff --git a/src/raylib.rc b/src/raylib.rc index 6a9546537..8612a9561 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 4,5,0,0 -PRODUCTVERSION 4,5,0,0 +FILEVERSION 5,0,0,0 +PRODUCTVERSION 5,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib application (www.raylib.com)" - VALUE "FileVersion", "4.5.0" + VALUE "FileVersion", "5.0.0" VALUE "InternalName", "raylib app" VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" //VALUE "OriginalFilename", "raylib_app.exe" VALUE "ProductName", "raylib app" - VALUE "ProductVersion", "4.5.0" + VALUE "ProductVersion", "5.0.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.rc.data b/src/raylib.rc.data index 1485d3aa4d9470bc98c786e0699c49dd0c9c3899..1476a1cbaeb46b6b4ddc19647cb7fe87993a5e97 100644 GIT binary patch delta 51 zcmZ1%zAk*j9Sv><237_LV4eI?Lwu4Z$7D9mbF8KedJG1eZ)$QgLYR|ZY8?dtRw@lf delta 51 zcmZ1%zAk*j9Sv?)1{MYo0Me5`YKTwL Date: Wed, 18 Oct 2023 03:05:35 -0300 Subject: [PATCH 0743/1710] [core] Add more missing implementations to `SDL` (#3436) * Add more missing implementations 1 * Add more missing implementations 2 * Add more missing implementations 3 * Add more missing implementations 4 * Add more missing implementations 5 * Add more missing implementations 6 --- src/rcore_desktop_sdl.c | 87 ++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 15c79cb87..cddee7448 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -62,6 +62,7 @@ typedef struct { SDL_GLContext glContext; SDL_Joystick *gamepad; + SDL_Cursor *cursor; } PlatformData; //---------------------------------------------------------------------------------- @@ -178,6 +179,22 @@ static const KeyboardKey ScancodeToKey[SCANCODE_MAPPED_NUM] = { KEY_KP_DECIMAL // SDL_SCANCODE_KP_PERIOD }; +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 //---------------------------------------------------------------------------------- @@ -315,14 +332,13 @@ void SetWindowOpacity(float opacity) // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); + SDL_RaiseWindow(platform.window); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); - return NULL; + return (void *)platform.window; } // Get number of monitors @@ -389,15 +405,45 @@ int GetMonitorHeight(int monitor) // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); - return 0; + int width = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + float vdpi = 0.0f; + SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + // Calculate size on inches, then convert to millimeter + if (vdpi > 0.0f) width = (mode.w/vdpi)*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) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); - return 0; + int height = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + float vdpi = 0.0f; + SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + // Calculate size on inches, then convert to millimeter + if (vdpi > 0.0f) height = (mode.h/vdpi)*25.4f; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return height; } // Get selected monitor refresh rate @@ -452,34 +498,37 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform"); + SDL_SetClipboardText(text); } // Get clipboard text content -// NOTE: returned string is allocated and freed by GLFW +// NOTE: returned string must be freed with SDL_free() const char *GetClipboardText(void) { - TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); - return NULL; + return SDL_GetClipboardText(); } // 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) { - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_ShowCursor(SDL_ENABLE); CORE.Input.Mouse.cursorHidden = false; } @@ -487,8 +536,7 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + SDL_SetRelativeMouseMode(SDL_TRUE); CORE.Input.Mouse.cursorHidden = true; } @@ -524,8 +572,7 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); - return 0; + SDL_GameControllerAddMapping(mappings); } // Set mouse position XY @@ -538,7 +585,10 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); + platform.cursor = SDL_CreateSystemCursor(CursorsLUT[cursor]); + SDL_SetCursor(platform.cursor); + + CORE.Input.Mouse.cursor = cursor; } // Register all input events @@ -794,6 +844,7 @@ static int InitPlatform(void) static 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 From d7a098ebd31c3235711a473b0894bfa3e6ec0fc9 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Thu, 19 Oct 2023 00:09:00 +0200 Subject: [PATCH 0744/1710] [core] Add more missing implementations to SDL (#3439) * [core] Add more missing implementations to SDL Add functions: `SetWindowState`, `ClearWindowState`, `SetWindowIcon` * Completing `SetWIndowState` and `ClearWindowState` * Add VSync support for SDL * Fix `CORE.Window.display` size issue * Fix getting monitor size We now get the size of the monitor where the window is located * Add `ToggleBorderlessWindowed` * Add `ToggleFullscreen` * Add `GetMonitorPosition` * Add `SetWindowMonitor` NOTE: The function is implemented but incomplete * Replace `TraceLog` by `TRACELOG` * Fixed mouse delta issue in relative mode Fixed a delta retrieval issue with `GetMouseDelta` when the mouse is in relative mode. Solution by @ubkp * Fix `IsKeyPressed` issue An issue caused `IsKeyPressed` to continuously return true for most keys when pressed * Fix `SetGamepadMappings` returning --- src/rcore_desktop_sdl.c | 383 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 360 insertions(+), 23 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index cddee7448..fedebeee4 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -63,6 +63,7 @@ typedef struct { SDL_Joystick *gamepad; SDL_Cursor *cursor; + bool cursorRelative; } PlatformData; //---------------------------------------------------------------------------------- @@ -221,13 +222,72 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - //SDL_SetWindowFullscreen + if (!IsWindowState(FLAG_FULLSCREEN_MODE)) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + } + else + { + SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + } } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - //SDL_SetWindowFullscreen + // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it + bool wasOnFullscreen = false; + if (CORE.Window.fullscreen) + { + CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + wasOnFullscreen = true; + } + + if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) + { + // Store the window's current position and size + SDL_GetWindowPosition(platform.window, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + CORE.Window.previousScreen = CORE.Window.screen; + + // Set screen position and size inside valid bounds + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(GetCurrentMonitor(), &displayBounds) == 0) + { + SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); + SDL_SetWindowSize(platform.window, displayBounds.w, displayBounds.h); + } + + // Set borderless mode and flag + SDL_SetWindowBordered(platform.window, SDL_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + + // Set topmost modes and flag + SDL_SetWindowAlwaysOnTop(platform.window, SDL_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + + // Set borderless windowed flag + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + // Remove borderless mode and flag + SDL_SetWindowBordered(platform.window, SDL_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + + // Remove topmost modes and flag + SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + + // Restore the window's previous size and position + SDL_SetWindowSize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + SDL_SetWindowPosition(platform.window, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + + // Remove borderless windowed flag + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } } // Set window state: maximized, if resizable @@ -253,19 +313,247 @@ void RestoreWindow(void) // Set window configuration state using flags void SetWindowState(unsigned int flags) { - //SDL_HideWindow(platform.window); + CORE.Window.flags |= flags; + + if (flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(1); + } + if (flags & FLAG_FULLSCREEN_MODE) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + } + 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) + { + // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? + SDL_SetWindowBordered(platform.window, SDL_FALSE); + } + 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) { - TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); + CORE.Window.flags &= ~flags; + + if (flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(0); + } + if (flags & FLAG_FULLSCREEN_MODE) + { + SDL_SetWindowFullscreen(platform.window, 0); + } + 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) + { + // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? + SDL_SetWindowBordered(platform.window, SDL_TRUE); + } + 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) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); + SDL_Surface* iconSurface = NULL; + + Uint32 rmask, gmask, bmask, amask; + 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: + 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: + // Compressed formats are not supported + return; + } + + 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 @@ -294,7 +582,20 @@ void SetWindowPosition(int x, int y) // Set monitor for the current window void SetWindowMonitor(int monitor) { - TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); + if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + { + TRACELOG(LOG_ERROR, "Invalid monitor index"); + return; + } + + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) + { + TRACELOG(LOG_ERROR, "Failed to get display bounds"); + return; + } + + SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -360,8 +661,20 @@ int GetCurrentMonitor(void) // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); - return (Vector2){ 0, 0 }; + if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + { + TRACELOG(LOG_ERROR, "Invalid monitor index"); + return (Vector2) { 0, 0 }; + } + + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) + { + TRACELOG(LOG_ERROR, "Failed to get display bounds"); + return (Vector2) { 0, 0 }; + } + + return (Vector2) { displayBounds.x, displayBounds.y }; } // Get selected monitor width (currently used by monitor) @@ -530,6 +843,7 @@ void EnableCursor(void) SDL_SetRelativeMouseMode(SDL_FALSE); SDL_ShowCursor(SDL_ENABLE); + platform.cursorRelative = false; CORE.Input.Mouse.cursorHidden = false; } @@ -538,6 +852,7 @@ void DisableCursor(void) { SDL_SetRelativeMouseMode(SDL_TRUE); + platform.cursorRelative = true; CORE.Input.Mouse.cursorHidden = true; } @@ -572,7 +887,7 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - SDL_GameControllerAddMapping(mappings); + return SDL_GameControllerAddMapping(mappings); } // Set mouse position XY @@ -612,7 +927,8 @@ void PollInputEvents(void) CORE.Input.Mouse.currentWheelMove.y = 0; // Register previous mouse position - CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + 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 CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; @@ -628,7 +944,7 @@ void PollInputEvents(void) // Register previous keys states // NOTE: Android supports up to 260 keys - for (int i = 0; i < 260; i++) + 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; @@ -662,16 +978,16 @@ void PollInputEvents(void) { switch (event.window.event) { - case SDL_WINDOWEVENT_LEAVE: - case SDL_WINDOWEVENT_HIDDEN: - case SDL_WINDOWEVENT_MINIMIZED: - case SDL_WINDOWEVENT_FOCUS_LOST: - case SDL_WINDOWEVENT_ENTER: - case SDL_WINDOWEVENT_SHOWN: - case SDL_WINDOWEVENT_FOCUS_GAINED: - case SDL_WINDOWEVENT_MAXIMIZED: - case SDL_WINDOWEVENT_RESTORED: - default: break; + case SDL_WINDOWEVENT_LEAVE: + case SDL_WINDOWEVENT_HIDDEN: + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_FOCUS_LOST: + case SDL_WINDOWEVENT_ENTER: + case SDL_WINDOWEVENT_SHOWN: + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_MAXIMIZED: + case SDL_WINDOWEVENT_RESTORED: + default: break; } } break; @@ -710,8 +1026,17 @@ void PollInputEvents(void) } break; case SDL_MOUSEMOTION: { - CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; - CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; + 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; + } } break; // Check gamepad events @@ -790,6 +1115,12 @@ static int InitPlatform(void) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + + if (CORE.Window.flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(1); + } + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); @@ -807,6 +1138,12 @@ static int InitPlatform(void) { CORE.Window.ready = true; + SDL_DisplayMode displayMode; + 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; From 19ff0e5fb1bbac46921ab34f922e77461c13c11f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:05:50 +0200 Subject: [PATCH 0745/1710] REVIEWED: `rlLoadTexture()` #3440 --- src/rlgl.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 40e7b4785..e38642ec9 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3000,7 +3000,8 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipOffset = 0; // Mipmap data offset, only used for tracelog // NOTE: Added pointer math separately from function to avoid UBSAN complaining - unsigned char *dataPtr = (unsigned char *)data; + unsigned char *dataPtr = NULL; + if (data != NULL) dataPtr = (unsigned char *)data; // Load the different mipmap levels for (int i = 0; i < mipmapCount; i++) @@ -3040,7 +3041,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, mipWidth /= 2; mipHeight /= 2; mipOffset += mipSize; // Increment offset position to next mipmap - dataPtr += mipSize; // Increment data pointer to next mipmap + if (data != NULL) dataPtr += mipSize; // Increment data pointer to next mipmap // Security check for NPOT textures if (mipWidth < 1) mipWidth = 1; From a64d606cb3aa55df0f01f75b8db0ca6b9a06b562 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 19 Oct 2023 08:09:27 -0300 Subject: [PATCH 0746/1710] Fix GetMonitorPhysical* dpi (#3442) --- src/rcore_desktop_sdl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index fedebeee4..c574b8199 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -230,7 +230,7 @@ void ToggleFullscreen(void) else { SDL_SetWindowFullscreen(platform.window, 0); - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; } } @@ -725,12 +725,12 @@ int GetMonitorPhysicalWidth(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - float vdpi = 0.0f; - SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + 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 (vdpi > 0.0f) width = (mode.w/vdpi)*25.4f; + if (ddpi > 0.0f) width = (mode.w/ddpi)*25.4f; } else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); @@ -747,12 +747,12 @@ int GetMonitorPhysicalHeight(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - float vdpi = 0.0f; - SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + 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 (vdpi > 0.0f) height = (mode.h/vdpi)*25.4f; + if (ddpi > 0.0f) height = (mode.h/ddpi)*25.4f; } else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); From 982641228c6ee5732ae99a9c26895305143b21d9 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:36:10 +0200 Subject: [PATCH 0747/1710] REDESIGNED: Move platforms to separate directory #3313 --- src/{ => platforms}/rcore_android.c | 0 src/{ => platforms}/rcore_desktop.c | 0 src/{ => platforms}/rcore_desktop_sdl.c | 0 src/{ => platforms}/rcore_drm.c | 0 src/{ => platforms}/rcore_template.c | 0 src/{ => platforms}/rcore_web.c | 0 src/rcore.c | 32 ++++++++++++++++++------- 7 files changed, 23 insertions(+), 9 deletions(-) rename src/{ => platforms}/rcore_android.c (100%) rename src/{ => platforms}/rcore_desktop.c (100%) rename src/{ => platforms}/rcore_desktop_sdl.c (100%) rename src/{ => platforms}/rcore_drm.c (100%) rename src/{ => platforms}/rcore_template.c (100%) rename src/{ => platforms}/rcore_web.c (100%) diff --git a/src/rcore_android.c b/src/platforms/rcore_android.c similarity index 100% rename from src/rcore_android.c rename to src/platforms/rcore_android.c diff --git a/src/rcore_desktop.c b/src/platforms/rcore_desktop.c similarity index 100% rename from src/rcore_desktop.c rename to src/platforms/rcore_desktop.c diff --git a/src/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c similarity index 100% rename from src/rcore_desktop_sdl.c rename to src/platforms/rcore_desktop_sdl.c diff --git a/src/rcore_drm.c b/src/platforms/rcore_drm.c similarity index 100% rename from src/rcore_drm.c rename to src/platforms/rcore_drm.c diff --git a/src/rcore_template.c b/src/platforms/rcore_template.c similarity index 100% rename from src/rcore_template.c rename to src/platforms/rcore_template.c diff --git a/src/rcore_web.c b/src/platforms/rcore_web.c similarity index 100% rename from src/rcore_web.c rename to src/platforms/rcore_web.c diff --git a/src/rcore.c b/src/rcore.c index c6770a5bb..fd0335c6e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -281,8 +281,8 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font on extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +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) static void SetupFramebuffer(int width, int height); // Setup main framebuffer @@ -309,15 +309,15 @@ const char *TextFormat(const char *text, ...); // Formatting of text with // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) - #include "rcore_desktop.c" + #include "platforms/rcore_desktop.c" #elif defined(PLATFORM_DESKTOP_SDL) - #include "rcore_desktop_sdl.c" + #include "platforms/rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) - #include "rcore_web.c" + #include "platforms/rcore_web.c" #elif defined(PLATFORM_DRM) - #include "rcore_drm.c" + #include "platforms/rcore_drm.c" #elif defined(PLATFORM_ANDROID) - #include "rcore_android.c" + #include "platforms/rcore_android.c" #else // TODO: Include your custom platform backend! // i.e software rendering backend or console backend! @@ -328,8 +328,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//void InitWindow(int width, int height, const char *title) -//void CloseWindow(void) //bool WindowShouldClose(void) //void ToggleFullscreen(void) //void ToggleBorderlessWindowed(void) @@ -378,6 +376,22 @@ void InitWindow(int width, int height, const char *title) { TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); +#if defined(PLATFORM_DESKTOP) + TRACELOG(LOG_INFO, "Platform backend: DESKTOP (GLFW)"); +#elif defined(PLATFORM_DESKTOP_SDL) + TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)"); +#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)"); From 65dd0afb60770f88d863283aed7098a474535183 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:41:09 +0200 Subject: [PATCH 0748/1710] Update Makefile --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index e6c5106cc..ea8bdd5e5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -633,7 +633,7 @@ endif # Compile all modules with their prerequisites # Prerequisites of core module -rcore.o : rcore_android.c rcore_desktop.c rcore_drm.c rcore_template.c rcore_web.c +rcore.o : platforms/rcore_android.c platforms/rcore_desktop.c platforms/rcore_drm.c platforms/rcore_template.c platforms/rcore_web.c # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h From b674e344a878613881d5e8d0e9f86176e4c0a56f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:46:02 +0200 Subject: [PATCH 0749/1710] REVIEWED: Issue with symbols exposure --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_desktop.c | 4 ++-- src/platforms/rcore_desktop_sdl.c | 5 +++-- src/platforms/rcore_drm.c | 4 ++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 4 ++-- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index bf55ece27..ddf0c7a45 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -80,8 +80,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 29969d4ca..4dec2c77b 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -111,8 +111,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c574b8199..b64724334 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -199,8 +199,9 @@ static const int CursorsLUT[] = { //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +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 //---------------------------------------------------------------------------------- diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 3302ec362..4df40e5b0 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -138,8 +138,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform static void InitKeyboard(void); // Initialize raw keyboard system static void RestoreKeyboard(void); // Restore keyboard system diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index f94cca8c1..87aca16da 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -71,8 +71,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static bool InitGraphicsDevice(void); // Initialize graphics device +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +bool InitGraphicsDevice(void); // Initialize graphics device //---------------------------------------------------------------------------------- // Module Functions Declaration diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 465bb8957..bfc5bd790 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -85,8 +85,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error From 081fffd46e56ae1e6f80b12bf8fe5728828def22 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:57:31 +0200 Subject: [PATCH 0750/1710] REVIEWED: Issue with functions definitions --- src/platforms/rcore_android.c | 5 +++-- src/platforms/rcore_desktop.c | 4 ++-- src/platforms/rcore_desktop_sdl.c | 5 +++-- src/platforms/rcore_drm.c | 4 ++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 5 +++-- src/rcore.c | 3 ++- 7 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index ddf0c7a45..1272eecc3 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -516,7 +516,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; @@ -589,7 +589,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) @@ -613,6 +613,7 @@ static void ClosePlatform(void) } } + // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 4dec2c77b..ea1b15fad 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1214,7 +1214,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { glfwSetErrorCallback(ErrorCallback); /* @@ -1531,7 +1531,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { glfwDestroyWindow(platform.handle); glfwTerminate(); diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index b64724334..c57a94fe9 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1070,7 +1070,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { // Initialize SDL internal global state int result = SDL_Init(SDL_INIT_EVERYTHING); @@ -1180,7 +1180,7 @@ static int InitPlatform(void) return 0; } -static void ClosePlatform(void) +void ClosePlatform(void) { SDL_FreeCursor(platform.cursor); // Free cursor SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context @@ -1188,6 +1188,7 @@ static void ClosePlatform(void) SDL_Quit(); // Deinitialize SDL internal global state } + static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) { if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 4df40e5b0..2ecfc2a3a 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -565,7 +565,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { platform.fd = -1; platform.connector = NULL; @@ -916,7 +916,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { if (platform.prevFB) { diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 87aca16da..dc71a66ca 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -429,7 +429,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -556,7 +556,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { // TODO: De-initialize graphics, inputs and more } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index bfc5bd790..e918e7d34 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -669,7 +669,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { glfwSetErrorCallback(ErrorCallback); @@ -932,12 +932,13 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { glfwDestroyWindow(platform.handle); glfwTerminate(); } + // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore.c b/src/rcore.c index fd0335c6e..e3cd08188 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2523,7 +2523,8 @@ int GetTouchPointCount(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//static bool InitPlatform(void) +//int InitPlatform(void) +//void ClosePlatform(void) // Initialize hi-resolution timer void InitTimer(void) From c66eb491992bebf4462d61cfb9c232f7cfefba28 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:38:14 -0300 Subject: [PATCH 0751/1710] [core] Complement implementations for `SDL` (#3444) * Complement SetWindowMonitor SDL implementation * Complement SetWindowMonitor SDL implementation 2 * Complement SetWindowMonitor SDL implementation 3 * Complement GetMonitorPosition SDL implementation * Small tweaks to various SDL implementation * Small tweaks to various SDL implementation 2 --- src/platforms/rcore_desktop_sdl.c | 104 ++++++++++++++++++------------ 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c57a94fe9..1b52a960d 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -583,20 +583,51 @@ void SetWindowPosition(int x, int y) // Set monitor for the current window void SetWindowMonitor(int monitor) { - if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - TRACELOG(LOG_ERROR, "Invalid monitor index"); - return; - } + // 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 workround 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; - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) - { - TRACELOG(LOG_ERROR, "Failed to get display bounds"); - return; - } + 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. - SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); + // 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 was'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) @@ -656,26 +687,28 @@ int GetMonitorCount(void) // Get number of monitors int GetCurrentMonitor(void) { - return SDL_GetWindowDisplayIndex(platform.window); + int currentMonitor = 0; + + currentMonitor = SDL_GetWindowDisplayIndex(platform.window); + + return currentMonitor; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - TRACELOG(LOG_ERROR, "Invalid monitor index"); - return (Vector2) { 0, 0 }; + 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"); } - - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) - { - TRACELOG(LOG_ERROR, "Failed to get display bounds"); - return (Vector2) { 0, 0 }; - } - - return (Vector2) { displayBounds.x, displayBounds.y }; + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + return (Vector2){ 0.0f, 0.0f }; } // Get selected monitor width (currently used by monitor) @@ -683,9 +716,7 @@ int GetMonitorWidth(int monitor) { int width = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -702,9 +733,7 @@ int GetMonitorHeight(int monitor) { int height = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -721,9 +750,7 @@ int GetMonitorPhysicalWidth(int monitor) { int width = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { float ddpi = 0.0f; @@ -743,9 +770,7 @@ int GetMonitorPhysicalHeight(int monitor) { int height = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { float ddpi = 0.0f; @@ -765,9 +790,7 @@ int GetMonitorRefreshRate(int monitor) { int refresh = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -782,8 +805,7 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) return SDL_GetDisplayName(monitor); else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); From e5993c4a4b27cb30617f1d07b0d9583e8f556902 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 21 Oct 2023 07:11:54 -0300 Subject: [PATCH 0752/1710] [core] Complement implementations for `SDL` (2) (#3447) * Add note and todo to GetWindowScaleDPI * Complement ToggleFullscreen and change ToggleBorderlessWindowed * Complement SetWindowState and ClearWindowState --- src/platforms/rcore_desktop_sdl.c | 113 ++++++++++++++---------------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 1b52a960d..b261b0428 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -223,72 +223,45 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - if (!IsWindowState(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.flags |= FLAG_FULLSCREEN_MODE; - } - else - { - SDL_SetWindowFullscreen(platform.window, 0); - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + 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) { - // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it - bool wasOnFullscreen = false; - if (CORE.Window.fullscreen) + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - CORE.Window.previousPosition = CORE.Window.position; - ToggleFullscreen(); - wasOnFullscreen = true; - } - - if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) - { - // Store the window's current position and size - SDL_GetWindowPosition(platform.window, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); - CORE.Window.previousScreen = CORE.Window.screen; - - // Set screen position and size inside valid bounds - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(GetCurrentMonitor(), &displayBounds) == 0) + if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) { - SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); - SDL_SetWindowSize(platform.window, displayBounds.w, displayBounds.h); + 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; } - - // Set borderless mode and flag - SDL_SetWindowBordered(platform.window, SDL_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - - // Set topmost modes and flag - SDL_SetWindowAlwaysOnTop(platform.window, SDL_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; - - // Set borderless windowed flag - CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; - } - else - { - // Remove borderless mode and flag - SDL_SetWindowBordered(platform.window, SDL_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - - // Remove topmost modes and flag - SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - - // Restore the window's previous size and position - SDL_SetWindowSize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - SDL_SetWindowPosition(platform.window, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); - - // Remove borderless windowed flag - CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } // Set window state: maximized, if resizable @@ -322,7 +295,14 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_FULLSCREEN_MODE) { - SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + 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) { @@ -374,8 +354,13 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { - // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? - SDL_SetWindowBordered(platform.window, SDL_FALSE); + 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) { @@ -400,6 +385,7 @@ void ClearWindowState(unsigned int flags) if (flags & FLAG_FULLSCREEN_MODE) { SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.fullscreen = false; } if (flags & FLAG_WINDOW_RESIZABLE) { @@ -450,8 +436,7 @@ void ClearWindowState(unsigned int flags) } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { - // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? - SDL_SetWindowBordered(platform.window, SDL_TRUE); + SDL_SetWindowFullscreen(platform.window, 0); } if (flags & FLAG_MSAA_4X_HINT) { @@ -827,8 +812,14 @@ Vector2 GetWindowPosition(void) // 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 (Vector2){ 1.0f, 1.0f }; + + return scale; } // Set clipboard text content From 8cda4273ece19fe349bad73cb7e118352ea1cc42 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 22 Oct 2023 04:45:04 -0300 Subject: [PATCH 0753/1710] [core] Complement implementations for `SDL` (3) (#3450) * Fix SetWindowMinSize and SetWindowMaxSize * Fix window resizes to update the viewport * Fix window resizes to update the viewport 2 --- src/platforms/rcore_desktop_sdl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index b261b0428..d2f550ace 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -618,6 +618,8 @@ void SetWindowMonitor(int 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; } @@ -625,6 +627,8 @@ void SetWindowMinSize(int width, int 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; } @@ -979,6 +983,8 @@ void PollInputEvents(void) } */ + CORE.Window.resizedLastFrame = false; + SDL_Event event = { 0 }; while (SDL_PollEvent(&event) != 0) { @@ -992,6 +998,18 @@ void PollInputEvents(void) { 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_LEAVE: case SDL_WINDOWEVENT_HIDDEN: case SDL_WINDOWEVENT_MINIMIZED: From bcfa7c6718202d7697c1af4019d0dd4f30eb12e5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:08:39 +0200 Subject: [PATCH 0754/1710] Update rcore_desktop.c --- src/platforms/rcore_desktop.c | 43 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index ea1b15fad..9e653dd0e 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1349,7 +1349,7 @@ int InitPlatform(void) // 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 - if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); + glfwSetJoystickCallback(NULL); // Find monitor resolution GLFWmonitor *monitor = glfwGetPrimaryMonitor(); @@ -1404,6 +1404,7 @@ int InitPlatform(void) } } } + 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, @@ -1447,27 +1448,8 @@ int InitPlatform(void) TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); return -1; } - - // Set window callback events - glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); - glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); - glfwSetDropCallback(platform.handle, WindowDropCallback); - - // Set input callback events - glfwSetKeyCallback(platform.handle, KeyCallback); - glfwSetCharCallback(platform.handle, CharCallback); - glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); - glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(platform.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwSetJoystickCallback(JoystickCallback); - + glfwMakeContextCurrent(platform.handle); - - glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) - glfwSwapInterval(0); // No V-Sync by default // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) @@ -1520,6 +1502,25 @@ int InitPlatform(void) // 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 window callback events + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + 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) // Initialize hi-res timer InitTimer(); From 1aad6a2fc0eb35c9aff845c84b2a4d798be67b27 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:09:03 +0200 Subject: [PATCH 0755/1710] REVIEWED: New platform backend template comments --- src/platforms/rcore_template.c | 75 +++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index dc71a66ca..08210289e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -431,6 +431,12 @@ void PollInputEvents(void) // Initialize platform: graphics, inputs and more int InitPlatform(void) { + // TODO: Initialize graphic device: display/window + // It usually requires setting up the platform display system configuration + // and connexion with the GPU through some system graphic API + // 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; @@ -496,61 +502,66 @@ int InitPlatform(void) } // 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); - // 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); - - ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size + // 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); + eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) + EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); + + // Enabling current display surface and context failed + if (result == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return -1; } - else - { - 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; + else CORE.Window.ready = true; + //---------------------------------------------------------------------------- + + // 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); - } + 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); - // Load OpenGL extensions + // TODO: Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions + //---------------------------------------------------------------------------- rlLoadExtensions(eglGetProcAddress); + //---------------------------------------------------------------------------- + + // TODO: Initialize input system + // It could imply keyboard, mouse, gamepad, touch... + // Depending on the platform libraries/SDK it could use a callbacks mechanims + // For system events and inputs evens polling on a per-frame basis, use PollInputEvents() + //---------------------------------------------------------------------------- + // ... + //---------------------------------------------------------------------------- - CORE.Window.ready = true; - - // 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; } - - // Initialize hi-res timer + // TODO: Initialize hi-res timer + //---------------------------------------------------------------------------- InitTimer(); + //---------------------------------------------------------------------------- - // Initialize base path for storage + // TODO: Initialize base path for storage + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- return 0; } From c4fb6c8517d8480afffafdff81155dd35a600f73 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:27:22 +0200 Subject: [PATCH 0756/1710] REVIEWED: sinfl, fix #3349 --- src/external/sinfl.h | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 8979fcd7f..03f0b1536 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -5,6 +5,8 @@ which implements the Deflate (RFC 1951) compressed data format specification sta It is mainly tuned to get as much speed and compression ratio from as little code as needed to keep the implementation as concise as possible. +@raysan5: this file has been reviewed as per https://github.com/raysan5/raylib/issues/3349 + ## Features - Portable single header and source file duo written in ANSI C (ISO C90) - Dual license with either MIT or public domain @@ -122,6 +124,7 @@ extern "C" { struct sinfl { const unsigned char *bitptr; + const unsigned char *bitend; // @raysan5, added unsigned long long bitbuf; int bitcnt; @@ -177,17 +180,23 @@ sinfl_bsr(unsigned n) { return 31 - __builtin_clz(n); #endif } -static unsigned long long -sinfl_read64(const void *p) { - unsigned long long n; - memcpy(&n, p, 8); - return n; -} +// @raysan5, commented +//static unsigned long long +//sinfl_read64(const void *p) { +// unsigned long long n; +// memcpy(&n, p, 8); +// return n; +//} static void sinfl_copy64(unsigned char **dst, unsigned char **src) { - unsigned long long n; - memcpy(&n, *src, 8); - memcpy(*dst, &n, 8); + // @raysan5, reviewed + //---------------------------- + //unsigned long long n; + //memcpy(&n, *src, 8); + //memcpy(*dst, &n, 8); + memcpy(*dst, *src, 8); + //---------------------------- + *dst += 8, *src += 8; } static unsigned char* @@ -210,7 +219,14 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) { #endif static void sinfl_refill(struct sinfl *s) { - s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; + // @raysan5, reviewed + //--------------------------------------------------- + //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; + unsigned long long n = 0; + memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + s->bitbuf |= n << s->bitcnt; + //--------------------------------------------------- + s->bitptr += (63 - s->bitcnt) >> 3; s->bitcnt |= 56; /* bitcount in range [56,63] */ } @@ -384,6 +400,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) int last = 0; s.bitptr = in; + s.bitend = e; // @raysan5, added + while (1) { switch (state) { case hdr: { From da9bc564d2534ac447b0a22761e38ed2fd3e717b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:31:35 +0200 Subject: [PATCH 0757/1710] Update sinfl.h --- src/external/sinfl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 03f0b1536..fd9668dcc 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -223,7 +223,7 @@ sinfl_refill(struct sinfl *s) { //--------------------------------------------------- //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; unsigned long long n = 0; - memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + memcpy(&n, s->bitptr, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); s->bitbuf |= n << s->bitcnt; //--------------------------------------------------- From ea325c54e89743432eef7b22e724515b53501767 Mon Sep 17 00:00:00 2001 From: Keith Stellyes Date: Sun, 22 Oct 2023 05:58:35 -0700 Subject: [PATCH 0758/1710] fix examples Makefile to use Makefile.Web when building for web (#3449) Co-authored-by: Keith Stellyes --- examples/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 74b3b874d..136a0ab47 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -156,7 +156,7 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif ifeq ($(PLATFORM),PLATFORM_WEB) - MAKE = mingw32-make + MAKE = emmake make endif # Define compiler flags: CFLAGS @@ -533,6 +533,8 @@ others: $(OTHERS) %: %.c ifeq ($(PLATFORM),PLATFORM_ANDROID) $(MAKE) -f Makefile.Android PROJECT_NAME=$@ PROJECT_SOURCE_FILES=$< +else ifeq ($(PLATFORM),PLATFORM_WEB) + $(MAKE) -f Makefile.Web $@ else $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) endif From 2b90b5600c28a6478eff7c2114eb8f7bdb6600dd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:15:56 +0200 Subject: [PATCH 0759/1710] Revert "Update sinfl.h" This reverts commit da9bc564d2534ac447b0a22761e38ed2fd3e717b. --- src/external/sinfl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index fd9668dcc..03f0b1536 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -223,7 +223,7 @@ sinfl_refill(struct sinfl *s) { //--------------------------------------------------- //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; unsigned long long n = 0; - memcpy(&n, s->bitptr, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); s->bitbuf |= n << s->bitcnt; //--------------------------------------------------- From 0e029f719b4a4afb8fee4438cf011e26fa8bdc15 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:17:35 +0200 Subject: [PATCH 0760/1710] Revert "REVIEWED: sinfl, fix #3349" This reverts commit c4fb6c8517d8480afffafdff81155dd35a600f73. --- src/external/sinfl.h | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 03f0b1536..8979fcd7f 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -5,8 +5,6 @@ which implements the Deflate (RFC 1951) compressed data format specification sta It is mainly tuned to get as much speed and compression ratio from as little code as needed to keep the implementation as concise as possible. -@raysan5: this file has been reviewed as per https://github.com/raysan5/raylib/issues/3349 - ## Features - Portable single header and source file duo written in ANSI C (ISO C90) - Dual license with either MIT or public domain @@ -124,7 +122,6 @@ extern "C" { struct sinfl { const unsigned char *bitptr; - const unsigned char *bitend; // @raysan5, added unsigned long long bitbuf; int bitcnt; @@ -180,23 +177,17 @@ sinfl_bsr(unsigned n) { return 31 - __builtin_clz(n); #endif } -// @raysan5, commented -//static unsigned long long -//sinfl_read64(const void *p) { -// unsigned long long n; -// memcpy(&n, p, 8); -// return n; -//} +static unsigned long long +sinfl_read64(const void *p) { + unsigned long long n; + memcpy(&n, p, 8); + return n; +} static void sinfl_copy64(unsigned char **dst, unsigned char **src) { - // @raysan5, reviewed - //---------------------------- - //unsigned long long n; - //memcpy(&n, *src, 8); - //memcpy(*dst, &n, 8); - memcpy(*dst, *src, 8); - //---------------------------- - + unsigned long long n; + memcpy(&n, *src, 8); + memcpy(*dst, &n, 8); *dst += 8, *src += 8; } static unsigned char* @@ -219,14 +210,7 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) { #endif static void sinfl_refill(struct sinfl *s) { - // @raysan5, reviewed - //--------------------------------------------------- - //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; - unsigned long long n = 0; - memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); - s->bitbuf |= n << s->bitcnt; - //--------------------------------------------------- - + s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; s->bitptr += (63 - s->bitcnt) >> 3; s->bitcnt |= 56; /* bitcount in range [56,63] */ } @@ -400,8 +384,6 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) int last = 0; s.bitptr = in; - s.bitend = e; // @raysan5, added - while (1) { switch (state) { case hdr: { From cdb394fac619c5573b3c4328a5e1a9652d4186ff Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:21:25 +0200 Subject: [PATCH 0761/1710] Update CHANGELOG for **raylib 5.0** -WIP- --- CHANGELOG | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 43ab3ab44..cad709bef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,203 @@ changelog Current Release: raylib 4.5.0 (18 March 2023) +------------------------------------------------------------------------- +Release: raylib 5.0 (xx November 2023) +------------------------------------------------------------------------- +KEY CHANGES: + - REDESIGNED: rcore module split per-platform, by @ubkp, @michaelfiber, @Bigfoot71, @raysan5 + - REMOVED: Deprecated platform: PLATFORM_RPI, use PLATFORM_DRM instead + - ADDED: New platform backend supported: SDL + - ADDED: New platform backend supported: Nintendo Switch (closed source) + - REDESIGNED: Automation Events System (exposed API) + +Detailed changes: +[rcore] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach +[rcore] ADDED: IsKeyPressedRepeat() on PLATFORM_DESKTOP (#3245) by @actondev +[rcore] ADDED: SetWindowTitle() for PLATFORM_WEB (#3222) by @VitusVeit +[rcore] ADDED: FLAG_WINDOW_RESIZABLE for web (#3305) by @Peter0x44 +[rcore] ADDED: SetWindowMaxSize() for desktop and web (#3309) by @ubkp +[rcore] REMOVED: PLATFORM_RPI (#3232) by @michaelfiber +[rcore] REVIEWED: GetFileLength(), added comment #3262 by @raysan5 +[rcore] REVIEWED: Default shaders precission issue on PLATFORM_WEB (#3261) by @branc116 +[rcore] REVIEWED: IsKey*() key validation checks (#3256) by @n77y +[rcore] REVIEWED: SetClipboardText() for PLATFORM_WEB (#3257) by @ubkp +[rcore] REVIEWED: Check if Ctrl modifier is among the currently set modifiers (#3230) by @mohad12211 +[rcore] REVIEWED: Android app black screen when reopening by @Bigfoot71 +[rcore] REVIEWED: Warnings when casting int to floats (#3218) by @JeffM2501 +[rcore] REVIEWED: GetCurrentMonitor() detection inconsistency issue (#3215) by @ubkp +[rcore] REVIEWED: SetWindowMonitor() to no longer force fullscreen (#3209) by @ubkp +[rcore] REVIEWED: Fix mouse wheel not working in PLATFORM_RPI or PLATFORM_DRM (#3193) by @ubkp +[rcore] REVIEWED: GetMonitorName() description (#3184) (#3189) by @danilwhale +[rcore] REVIEWED: Window flags order (#3114) by @lesleyrs +[rcore] REVIEWED: Full movement for right analog stick (#3095) by @PixelPhobicGames +[rcore] REVIEWED: Fix Android app freeze after calling CloseWindow() (#3067) by @Bigfoot71 +[rcore] REVIEWED: Lazy loading of default font used on image drawing (no InitWindow) by @raysan5 +[rcore] REVIEWED: Minor tweaks to raylib events automation system @raysan5 +[rcore] REVIEWED: GetCurrentMonitor() bugfix (#3058) by @hamyyy +[rcore] REVIEWED: Update CORE.Input.Touch.pointCount #3024 by @raysan5 +[rcore] REVIEWED: Mouse offset and scaling must be considered also on web! +[rcore] REVIEWED: CompressData(), possible stack overflow +[rcore] REVIEWED: GetWorldToScreenEx() (#3351) by @Brian-ED +[rlgl] ADDED: Experimental support for OpenGL ES 3.0 by @raysan5 +[rlgl] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik +[rlgl] REVIEWED: Improved support for ES3/WebGL2 (#3107) by @chemaguerra +[rlgl] REVIEWED: OpenGL 2.1 half floats support as part of an extension by @Not-Nik +[rlgl] REVIEWED: Avoid shader attribute not found log by @raysan5 +[rlgl] REVIEWED: Avoid tracelog about not found uniforms #3003 by @raysan5 +[rlgl] REVIEWED: rLoadTexture() UBSAN complaints #1891 (#3321) by @Codom +[rlgl] REVIEWED: glInternalFormat as unsigned int +[rshapes] ADDED: Spline drawing functions by @raysan5 +[rshapes] REVIEWED: DrawLineCatmullRom() by @raysan5 +[rshapes] REVIEWED: Minor fix in DrawLineBezier* (#3006) by @eternalStudent +[rshapes] REVIEWED: GetCollisionRec(), more performant (#3052) by @manuel5975p +[rshapes] REVIEWED: Fix off-by-one error in CheckCollisionPointRec() (#3022) by @dbechrd +[rtextures] ADDED: Basic SVG loading support (#2738) by @bXi +[rtextures] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik +[rtextures] ADDED: ExportImageToMemory() by @raysan5 +[rtextures] ADDED: ImageRotate() (#3078) by @danemadsen +[rtextures] ADDED: GenImageGradientSquare() (#3077) by @danemadsen +[rtextures] ADDED: GenImageLinearGradient() by @danemadsen +[rtextures] REMOVED: GenImageGradientH() and GenImageGradientV() by @danemadsen +[rtextures] REVIEWED: LoadImageSvg() by @raysan5 +[rtextures] REVIEWED: Uninitialized thread-locals in stbi #3282 (#3283) by @jbarthelmes +[rtextures] REVIEWED: ImageDrawRectangleRec(), validate drawing inside bounds by @JeffM2501 +[rtextures] REVIEWED: LoadTextureCubemap() for manual layouts (#3204) by @Not-Nik +[rtextures] REVIEWED: Optimization of ImageDrawRectangleRec() (#3185) by @smalltimewizard +[rtextures] REVIEWED: ImageRotate() formatting by @raysan5 +[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values #3071 by @raysan5 +[rtextures] REVIEWED: Packing logic error in GenImageFontAtlas() (#2979) by @hanaxar +[rtextures] REVIEWED: Calculate exact image size in GenImageFontAtlas() (#2963) by @hanaxar +[rtextures] REVIEWED: ImageDrawRectangleRec() #3027 by @raysan5 +[rtextures] REVIEWED: ImageDraw() source clipping when drawing beyond top left (#3306) by @RobLoach +[rtextures] REVIEWED: UnloadRenderTexture(), additional checks +[rtext] ADDED: Font altas white rectangle and flag SUPPORT_FONT_ATLAS_WHITE_REC by @raysan5 +[rtext] ADDED: SetTextLineSpacing() to define line breaks text drawing spacing by @raysan5 +[rtext] RENAMED: LoadFont*() parameter names for consistency and coherence by @raysan5 +[rtext] REVIEWED: GetCodepointCount(), ignore unused return value of GetCodepointNext by @ashn-dot-dev +[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured. (#3399) by @Murlocohol +[rtext] REVIEWED: GetGlyphIndex() #3000 by @raysan5 +[rtext] REVIEWED: GetCodepointNext() to return default value on invalid inp… by @chocolate42 +[rtext] REVIEWED: TextToPascal() issue when first char is uppercase +[rmodels] ADDED: ModelAnimation.name field, initially with GLTF animation names loaded (… by @alfredbaudisch +[rmodels] REVIEWED: Support .vox model files version 200 (#3097) by @Bigfoot71 +[rmodels] REVIEWED: Materials loading #3126 @raysan5 +[rmodels] REVIEWED: DrawBillboardPro() to allow source of negative size (#3197) by @bohonghuang +[rmodels] REVIEWED: glTF loading segfault in animNormals memcpy by @charles-l +[rmodels] REVIEWED: LoadModelAnimationsGLTF(), free fileData after use (#3065) by @crynux +[rmodels] REVIEWED: GenMeshCubicmap(), correction of values (#3032) by @Bigfoot71 +[rmodels] REVIEWED: DrawMesh() to avoid UBSAN complaining (#1891) +[raudio] ADDED: LoadSoundAlias() by @JeffM2501 +[raudio] ADDED: Missing structure on standalone mode #3160 by @raysan5 +[raudio] REVIEWED: Comments about sample format to AttachAudioStreamProcessor() (#3188) by @AlbertoGP +[raudio] REVIEWED: Documented buffer format for audio processors (#3186) by @AlbertoGP +[raudio] REVIEWED: ExportWaveAsCode() file saving by @RadsammyT +[raudio] REVIEWED: Fix warning on discarded const qualifier (#2967) by @RobLoach +[raudio] REVIEWED: Move mutex initialization before ma_device_start() (#3325) by @Bigfoot71 +[rcamera] REVIEWED: File-macros for consistency #3161 by @raysan5 +[rcamera] REVIEWED: Support analog stick camera controls (#3066) by @PixelPhobicGames +[rcamera] REVIEWED: CameraMoveToTarget(), ensure distance is greater than 0 (#3031) by @kolunmi +[rcamera] REVIEWED: Exposing rcamera functions to the dll (#3355) by @JeffM2501 +[raymath] ADDED: Vector3Projection() and Vector3Rejection() (#3263) by @Dial0 +[raymath] ADDED: EPSILON macro to each function requiring it (#3330) by @Brian-ED +[raymath] REVIEWED: Usage of 'sinf()' and 'cosf()' to be correct (#3181) by @RokasPuzonas +[raymath] REVIEWED: Slightly optimized Vector3Normalize() (#2982) by @RicoP +[raymath] REVIEWED: Comment to clarify raymath semantics by @raysan5 +[raymath] REVIEWED: Comment about Matrix conventions by @raysan5 +[raymath] REVIEWED: Vector2Angle() and Vector2LineAngle() (#3396) by @Murlocohol +[rgestures] REVIEWED: Optimize and simplify the gesture system (#3190) by @ubkp +[rgestures] REVIEWED: GESTURE_DRAG and GESTURE_SWIPE_* issues (mostly) for web (#3183) by @ubkp +[rgestures] REVIEWED: Touch pointCount for web (#3163) by @ubkp +[rgestures] REVIEWED: IsGestureDetected() parameter type +[utils] ADDED: Security checks to file reading (memory allocations) by @raysan5 +[utils] REVIEWED: LoadFileData() potential issues with dataSize +[examples] ADDED: shaders_lightmap (#3043) by @nullstare +[examples] ADDED: core_2d_camera_split_screen (#3298) by @gabrielssanches +[examples] ADDED: LoadSoundAlias() usage example (#3223) by @JeffM2501 +[examples] ADDED: textures_tiling (#3353) by @luis605 +[examples] RENAMED: 2d_camera examples for consistency +[examples] REVIEWED: Text examples SetTextLineSpacing() to multiline examples by @raysan5 +[examples] REVIEWED: examples/shapes/shapes_collision_area.c help instructions (#3279) by @asdqwe +[examples] REVIEWED: examples/shaders/shaders_texture_outline.c help instructions (#3278) by @asdqwe +[examples] REVIEWED: examples/others/easings_testbed.c help instructions and small twe… by @asdqwe +[examples] REVIEWED: example/audio/audio_module_player.c help instructions and small b… by @asdqwe +[examples] REVIEWED: example/models/models_loading_m3d.c controls (#3269) by @asdqwe +[examples] REVIEWED: example/models/models_loading_gltf.c controls (#3268) by @asdqwe +[examples] REVIEWED: text_unicode.c example crashing (#3250) by @ubkp +[examples] REVIEWED: rlgl_standalone.c compilation issue (#3242) by @ubkp +[examples] REVIEWED: core_input_gestures for Web (#3172) by @ubkp +[examples] REVIEWED: core_input_gamepad (#3110) by @iacore +[examples] REVIEWED: examples using raygui to raygui 4.0 by @raysan5 +[build] ADDED: CMake option for SUPPORT_CUSTOM_FRAME_CONTROL (#3221) by @ubkp +[build] ADDED: New BORDERLESS_WINDOWED_MODE for PLATFORM_DESKTOP (#3216) by @ubkp +[build] REVIEWED: Fix CMake extraneous -lglfw (#3266) by @iacore +[build] REVIEWED: Add missing cmake options (#3267) by @asdqwe +[build] REVIEWED: Match CMakeOptions.txt options default values (#3258) by @asdqwe +[build] REVIEWED: Add build.zig options for individual modules (#3254) by @actondev +[build] REVIEWED: build.zig to work with cross-compiling (#3225) by @yujiri8 +[build] REVIEWED: Makefile build on PLATFORM_ANDROID, soname (#3211) by @ndytts +[build] REVIEWED: src/Makefile, fix misleading indentation (#3202) by @ashn-dot-dev +[build] REVIEWED: build.zig: Support for building with PLAFORM_DRM (#3191) by @jakubvf +[build] REVIEWED: Update CMakeOptions.txt by @raysan5 +[build] REVIEWED: fix: cmake option "OPENGL_VERSION" doesn't work (#3170) by @royqh1979 +[build] REVIEWED: Add error if raylib.h is included in a C++98 program (#3093) by @Peter0x44 +[build] REVIEWED: Cross compilation for PLATFORM_DRM (#3091) by @TheLastBilly +[build] REVIEWED: build.zigm fixed cross-compiling from Linux (#3090)by @yujiri8 +[build] REVIEWED: Enhanced cmake part for OpenBSD (#3086) by @rayit +[build] REVIEWED: Fixed compile on OpenBSD (#3085)by @rayit +[build] REVIEWED: CMake project example: fix a couple of typos (#3014) by @benjamin-thomas +[build] REVIEWED: Fix warnings in raylib for MSVC (#3004) by @JeffM2501 +[build] REVIEWED: Update cmake example project (#3062) by @lesleyrs +[build] REVIEWED: Update build.zig be be able to build with current zig master (#3064) by @ryupold +[build] REVIEWED: VSCode project template (#3048) by @Shoozza +[build] REVIEWED: Fixed broken build.zig files. Now works with latest stable compiler (… by @Gamer-Kold +[build] REVIEWED: Fix missing symbol when rglfw.c on BSD platforms (#2968) by @Koromix +[build] REVIEWED: Update Makefile comment to indicate arm64 as a supported Linux deskto… @ashn-dot-dev +[build] REVIEWED: Update Makefile : clean raygui.c & physac.c (#3296) by @SuperUserNameMan +[build] REVIEWED: Update webassembly.yml and linux.yml +[build] REVIEWED: Update zig build system to zig version 0.11.0 (#3393) by @purple4pur +[build] REVIEWED: Fix for latest zig master (#3037) by @star-tek-mb +[bindings] ADDED: fortran-raylib +[bindings] ADDED: raylib-raku to bindings (#3299) by @vushu +[bindings] ADDED: claw-raylib to BINDINGS.md (#3310) by @bohonghuang +[bindings] ADDED: vaiorabbit/raylib-bindings (#3318) by @wilsonsilva +[bindings] ADDED: TurboRaylib (#3317) by @turborium +[bindings] ADDED: raylib-ffi to bindings list (#3164) by @ewpratten +[bindings] ADDED: raylib-pkpy-bindings (#3361) by @blueloveTH +[bindings] UPDATED: BINDINGS.md (#3217) by @joseph-montanez +[bindings] UPDATED: BINDINGS.md to include rayjs (#3212) by @mode777 +[bindings] UPDATED: latest h-raylib version (#3166) by @Anut-py +[bindings] UPDATED: bindbd-raylib3 to raylib 4.5 (#3157) by @o3o +[bindings] UPDATED: Janet bindings supported version update (#3083)by @archydragon +[bindings] UPDATED: BINDINGS.md (raylib-py -> 4.5) (#2992) by @overdev +[bindings] UPDATED: BINDINGS.md (raylib-lua -> 4.5) (#2989) by @TSnake41 +[bindings] UPDATED: raylib-d binding version to 4.5 (#2988) by @schveiguy +[bindings] UPDATED: raylib-freebasic to 4.5 (#2986) by @WIITD +[bindings] UPDATED: BINDINGS.md (#2983) by @jarroddavis68 +[bindings] UPDATED: BINDINGS.md for raylib Odin 4.5 (#2981) by @gingerBill +[bindings] UPDATED: BINDINGS.md (#2980) by @GuvaCode +[bindings] UPDATED: BINDINGS.md (#3002) by @fubark +[bindings] UPDATED: BINDINGS.md (#3053) by @JupiterRider +[bindings] UPDATED: BINDINGS.md (#3050) by @Its-Kenta +[bindings] UPDATED: CL bindings version (#3049) by @shelvick +[bindings] UPDATED: BINDINGS.md (#3026) by @ChrisDill +[bindings] UPDATED: BINDINGS.md (#3023) by @sDos280 +[bindings] UPDATED: BINDINGS.md (#3017) by @Soutaisei +[bindings] UPDATED: Various versions to 4.5 (#2974) by @RobLoach +[bindings] UPDATED: raylib.zig version to 4.5 (#2971) by @ryupold +[bindings] UPDATED: h-raylib version (#2970) by @Anut-py +[bindings] UPDATED: Factor's raylib binding to v4.5 (#3350) by @WraithGlade +[bindings] UPDATED: raylib-ocaml bindings to 4.5 version (#3322) by @tjammer +[external] UPDATED: sdefl and sinfl DEFLATE compression libraries by @raysan5 +[external] UPDATED: miniaudio v0.11.12 --> v0.11.18 by @raysan5 +[external] UPDATED: rl_gputex.h compressed images loading library by @raysan5 +[external] REVIEWED: msf_gif.h, some warnings +[misc] ADDED: New task point to issue template about checking the wiki (#3169) by @ubkp +[misc] REVIEWED: Update FAQ.md by @raysan5 +[misc] REVIEWED: Fix a link in the FAQ (#3082)by @jasonliang-dev +[misc] REVIEWED: New file formats to FAQ (#3079) by @Luramoth +[misc] REVIEWED: Make assets loading extension case insensitive #3008 by @raysan5 + ------------------------------------------------------------------------- Release: raylib 4.5 (18 March 2023) ------------------------------------------------------------------------- From f0124df0e8bbeb3e9ad2acf08b0f3610e812c8d6 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:34:24 +0200 Subject: [PATCH 0762/1710] Update CHANGELOG --- CHANGELOG | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cad709bef..1da04cc73 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ changelog Current Release: raylib 4.5.0 (18 March 2023) ------------------------------------------------------------------------- -Release: raylib 5.0 (xx November 2023) +Release: raylib 5.0 (xx November 2023) -WIP- ------------------------------------------------------------------------- KEY CHANGES: - REDESIGNED: rcore module split per-platform, by @ubkp, @michaelfiber, @Bigfoot71, @raysan5 @@ -19,8 +19,9 @@ Detailed changes: [rcore] ADDED: SetWindowTitle() for PLATFORM_WEB (#3222) by @VitusVeit [rcore] ADDED: FLAG_WINDOW_RESIZABLE for web (#3305) by @Peter0x44 [rcore] ADDED: SetWindowMaxSize() for desktop and web (#3309) by @ubkp +[rcore] ADDED: SetMouseCursor() for PLATFORM_WEB (#3414) by @BeardedBread [rcore] REMOVED: PLATFORM_RPI (#3232) by @michaelfiber -[rcore] REVIEWED: GetFileLength(), added comment #3262 by @raysan5 +[rcore] REVIEWED: GetFileLength(), added comment (#3262) by @raysan5 [rcore] REVIEWED: Default shaders precission issue on PLATFORM_WEB (#3261) by @branc116 [rcore] REVIEWED: IsKey*() key validation checks (#3256) by @n77y [rcore] REVIEWED: SetClipboardText() for PLATFORM_WEB (#3257) by @ubkp @@ -37,16 +38,17 @@ Detailed changes: [rcore] REVIEWED: Lazy loading of default font used on image drawing (no InitWindow) by @raysan5 [rcore] REVIEWED: Minor tweaks to raylib events automation system @raysan5 [rcore] REVIEWED: GetCurrentMonitor() bugfix (#3058) by @hamyyy -[rcore] REVIEWED: Update CORE.Input.Touch.pointCount #3024 by @raysan5 +[rcore] REVIEWED: Update CORE.Input.Touch.pointCount (#3024) by @raysan5 [rcore] REVIEWED: Mouse offset and scaling must be considered also on web! [rcore] REVIEWED: CompressData(), possible stack overflow [rcore] REVIEWED: GetWorldToScreenEx() (#3351) by @Brian-ED +[rcore] REVIEWED: Fix GetMouseDelta() issue for Android (#3404) by @Bigfoot71 [rlgl] ADDED: Experimental support for OpenGL ES 3.0 by @raysan5 [rlgl] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik [rlgl] REVIEWED: Improved support for ES3/WebGL2 (#3107) by @chemaguerra [rlgl] REVIEWED: OpenGL 2.1 half floats support as part of an extension by @Not-Nik [rlgl] REVIEWED: Avoid shader attribute not found log by @raysan5 -[rlgl] REVIEWED: Avoid tracelog about not found uniforms #3003 by @raysan5 +[rlgl] REVIEWED: Avoid tracelog about not found uniforms (#3003) by @raysan5 [rlgl] REVIEWED: rLoadTexture() UBSAN complaints #1891 (#3321) by @Codom [rlgl] REVIEWED: glInternalFormat as unsigned int [rshapes] ADDED: Spline drawing functions by @raysan5 @@ -62,41 +64,45 @@ Detailed changes: [rtextures] ADDED: GenImageLinearGradient() by @danemadsen [rtextures] REMOVED: GenImageGradientH() and GenImageGradientV() by @danemadsen [rtextures] REVIEWED: LoadImageSvg() by @raysan5 -[rtextures] REVIEWED: Uninitialized thread-locals in stbi #3282 (#3283) by @jbarthelmes +[rtextures] REVIEWED: Uninitialized thread-locals in stbi (#3282) (#3283) by @jbarthelmes [rtextures] REVIEWED: ImageDrawRectangleRec(), validate drawing inside bounds by @JeffM2501 [rtextures] REVIEWED: LoadTextureCubemap() for manual layouts (#3204) by @Not-Nik [rtextures] REVIEWED: Optimization of ImageDrawRectangleRec() (#3185) by @smalltimewizard [rtextures] REVIEWED: ImageRotate() formatting by @raysan5 -[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values #3071 by @raysan5 +[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values (#3071) by @raysan5 [rtextures] REVIEWED: Packing logic error in GenImageFontAtlas() (#2979) by @hanaxar [rtextures] REVIEWED: Calculate exact image size in GenImageFontAtlas() (#2963) by @hanaxar -[rtextures] REVIEWED: ImageDrawRectangleRec() #3027 by @raysan5 +[rtextures] REVIEWED: ImageDrawRectangleRec() (#3027) by @raysan5 [rtextures] REVIEWED: ImageDraw() source clipping when drawing beyond top left (#3306) by @RobLoach [rtextures] REVIEWED: UnloadRenderTexture(), additional checks [rtext] ADDED: Font altas white rectangle and flag SUPPORT_FONT_ATLAS_WHITE_REC by @raysan5 [rtext] ADDED: SetTextLineSpacing() to define line breaks text drawing spacing by @raysan5 [rtext] RENAMED: LoadFont*() parameter names for consistency and coherence by @raysan5 [rtext] REVIEWED: GetCodepointCount(), ignore unused return value of GetCodepointNext by @ashn-dot-dev -[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured. (#3399) by @Murlocohol -[rtext] REVIEWED: GetGlyphIndex() #3000 by @raysan5 +[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured (#3399) by @Murlocohol +[rtext] REVIEWED: TextFormat(), added "..." for truncation (#3366) by @raysan5 +[rtext] REVIEWED: GetGlyphIndex() (#3000) by @raysan5 [rtext] REVIEWED: GetCodepointNext() to return default value on invalid inp… by @chocolate42 [rtext] REVIEWED: TextToPascal() issue when first char is uppercase [rmodels] ADDED: ModelAnimation.name field, initially with GLTF animation names loaded (… by @alfredbaudisch [rmodels] REVIEWED: Support .vox model files version 200 (#3097) by @Bigfoot71 -[rmodels] REVIEWED: Materials loading #3126 @raysan5 +[rmodels] REVIEWED: Materials loading (#3126) @raysan5 [rmodels] REVIEWED: DrawBillboardPro() to allow source of negative size (#3197) by @bohonghuang [rmodels] REVIEWED: glTF loading segfault in animNormals memcpy by @charles-l [rmodels] REVIEWED: LoadModelAnimationsGLTF(), free fileData after use (#3065) by @crynux [rmodels] REVIEWED: GenMeshCubicmap(), correction of values (#3032) by @Bigfoot71 [rmodels] REVIEWED: DrawMesh() to avoid UBSAN complaining (#1891) +[rmodels] REVIEWED: GenMeshPlane() when resX != resZ (#3425) by @neyrox, @s-yablonskiy [raudio] ADDED: LoadSoundAlias() by @JeffM2501 -[raudio] ADDED: Missing structure on standalone mode #3160 by @raysan5 +[raudio] ADDED: Missing structure on standalone mode (#3160) by @raysan5 +[raudio] ADDED: GetMasterVolume() (#3434) by @rexim [raudio] REVIEWED: Comments about sample format to AttachAudioStreamProcessor() (#3188) by @AlbertoGP [raudio] REVIEWED: Documented buffer format for audio processors (#3186) by @AlbertoGP [raudio] REVIEWED: ExportWaveAsCode() file saving by @RadsammyT [raudio] REVIEWED: Fix warning on discarded const qualifier (#2967) by @RobLoach [raudio] REVIEWED: Move mutex initialization before ma_device_start() (#3325) by @Bigfoot71 -[rcamera] REVIEWED: File-macros for consistency #3161 by @raysan5 +[raudio] REVIEWED: Fix UpdateSound() parameter name (#3405) by @KislyjKisel +[rcamera] REVIEWED: File-macros for consistency (#3161) by @raysan5 [rcamera] REVIEWED: Support analog stick camera controls (#3066) by @PixelPhobicGames [rcamera] REVIEWED: CameraMoveToTarget(), ensure distance is greater than 0 (#3031) by @kolunmi [rcamera] REVIEWED: Exposing rcamera functions to the dll (#3355) by @JeffM2501 @@ -159,6 +165,7 @@ Detailed changes: [build] REVIEWED: Update webassembly.yml and linux.yml [build] REVIEWED: Update zig build system to zig version 0.11.0 (#3393) by @purple4pur [build] REVIEWED: Fix for latest zig master (#3037) by @star-tek-mb +[build] REVIEWED: Examples Makefile to use Makefile.Web when building for web (#3449) by @keithstellyes [bindings] ADDED: fortran-raylib [bindings] ADDED: raylib-raku to bindings (#3299) by @vushu [bindings] ADDED: claw-raylib to BINDINGS.md (#3310) by @bohonghuang @@ -166,6 +173,7 @@ Detailed changes: [bindings] ADDED: TurboRaylib (#3317) by @turborium [bindings] ADDED: raylib-ffi to bindings list (#3164) by @ewpratten [bindings] ADDED: raylib-pkpy-bindings (#3361) by @blueloveTH +[bindings] ADDED: Raylib.lean to BINDINGS.md (#3409) by @KislyjKisel [bindings] UPDATED: BINDINGS.md (#3217) by @joseph-montanez [bindings] UPDATED: BINDINGS.md to include rayjs (#3212) by @mode777 [bindings] UPDATED: latest h-raylib version (#3166) by @Anut-py @@ -193,6 +201,7 @@ Detailed changes: [external] UPDATED: sdefl and sinfl DEFLATE compression libraries by @raysan5 [external] UPDATED: miniaudio v0.11.12 --> v0.11.18 by @raysan5 [external] UPDATED: rl_gputex.h compressed images loading library by @raysan5 +[external] UPDATED: Replaced stb_image_resize.c by stb_image_resize2.h (#3403) by @BabakSamimi [external] REVIEWED: msf_gif.h, some warnings [misc] ADDED: New task point to issue template about checking the wiki (#3169) by @ubkp [misc] REVIEWED: Update FAQ.md by @raysan5 From e33e9da277865207123158430ebf42cc5626e5b7 Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Sun, 22 Oct 2023 16:13:49 +0100 Subject: [PATCH 0763/1710] Add DrawCircleLinesV for consistency (#3452) ImageDrawCircleLinesV already existed, so I'm not sure why this was missing. It is trivial to implement, anyway --- src/raylib.h | 1 + src/rshapes.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index e701f0454..b172562d0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1207,6 +1207,7 @@ RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // 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) RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring diff --git a/src/rshapes.c b/src/rshapes.c index f3061f8b3..de64f1593 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -670,6 +670,12 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) +{ + DrawCircleLinesV((Vector2){ (float)centerX, (float)centerY }, radius, color); +} + +// Draw circle outline (Vector version) +void DrawCircleLinesV(Vector2 center, float radius, Color color) { rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -677,8 +683,8 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) for (int i = 0; i < 360; i += 10) { - rlVertex2f(centerX + cosf(DEG2RAD*i)*radius, centerY + sinf(DEG2RAD*i)*radius); - rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radius, centerY + sinf(DEG2RAD*(i + 10))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius); } rlEnd(); } From b3028e4891083c077236df437011184b16b6d293 Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Sun, 22 Oct 2023 18:45:49 +0100 Subject: [PATCH 0764/1710] Review prerequisites of rcore.c (#3453) rcore_desktop_sdl.c was not present in the list of prerequisites this patch changes them to use a wildcard, so any other platforms added in future will be tracked properly --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index ea8bdd5e5..c9094f096 100644 --- a/src/Makefile +++ b/src/Makefile @@ -633,7 +633,7 @@ endif # Compile all modules with their prerequisites # Prerequisites of core module -rcore.o : platforms/rcore_android.c platforms/rcore_desktop.c platforms/rcore_drm.c platforms/rcore_template.c platforms/rcore_web.c +rcore.o : platforms/*.c # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h From 8f517b76516c207c89cabf055fa7db768ba6d960 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 23 Oct 2023 05:05:40 -0300 Subject: [PATCH 0765/1710] Fix compilation for PLATFORM_WEB examples (#3454) --- examples/Makefile.Web | 60 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index ac8d5af89..3512f7407 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -149,7 +149,7 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif ifeq ($(PLATFORM),PLATFORM_WEB) - MAKE = mingw32-make + MAKE = emmake make endif # Define compiler flags: CFLAGS @@ -268,7 +268,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # are specified per-example for optimization # Define a custom shell .html and output extension - LDFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html + LDFLAGS += --shell-file $(RAYLIB_PATH)/src/minshell.html EXT = .html endif @@ -340,6 +340,7 @@ CORE = \ core/core_input_mouse \ core/core_input_mouse_wheel \ core/core_input_gamepad \ + core/core_input_gamepad_info \ core/core_input_multitouch \ core/core_input_gestures \ core/core_input_gestures_web \ @@ -392,6 +393,7 @@ TEXTURES = \ textures/textures_image_generation \ textures/textures_image_loading \ textures/textures_image_processing \ + textures/textures_image_rotate \ textures/textures_image_text \ textures/textures_to_image \ textures/textures_raw_data \ @@ -473,9 +475,18 @@ AUDIO = \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ + audio/audio_sound_multi \ audio/audio_stream_effects \ audio/audio_mixed_processor +OTHERS = \ + others/easings_testbed \ + others/embedded_files_loading \ + others/raylib_opengl_interop \ + others/raymath_vector_angle \ + others/rlgl_compute_shader \ + others/rlgl_standalone + CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) # Define processes to execute @@ -512,6 +523,9 @@ core/core_input_gamepad: core/core_input_gamepad.c --preload-file core/resources/ps3.png@resources/ps3.png \ --preload-file core/resources/xbox.png@resources/xbox.png +core/core_input_gamepad_info: core/core_input_gamepad.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_input_multitouch: core/core_input_multitouch.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -529,7 +543,7 @@ core/core_2d_camera_platformer: core/core_2d_camera_platformer.c core/core_2d_camera_mouse_zoom: core/core_2d_camera_mouse_zoom.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + core/core_2d_camera_split_screen: core/core_2d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -541,7 +555,7 @@ core/core_3d_camera_free: core/core_3d_camera_free.c core/core_3d_camera_first_person: core/core_3d_camera_first_person.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -675,6 +689,10 @@ textures/textures_image_processing: textures/textures_image_processing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/parrots.png@resources/parrots.png +textures/textures_image_rotate: textures/textures_image_rotate.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/resources/raylib_logo.png + textures/textures_image_text: textures/textures_image_text.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file textures/resources/parrots.png@resources/parrots.png \ @@ -740,6 +758,10 @@ textures/textures_gif_player: textures/textures_gif_player.c textures/textures_fog_of_war: textures/textures_fog_of_war.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +textures/textures_svg_loading: textures/textures_svg_loading.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/resources/test.svg + # Compile TEXT examples text/text_raylib_fonts: text/text_raylib_fonts.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -963,6 +985,13 @@ shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s FORCE_FILESYSTEM=1 \ --preload-file shaders/resources/shaders/glsl100/reload.fs@resources/shaders/glsl100/reload.fs +shaders/shaders_lightmap: shaders/shaders_lightmap.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s FORCE_FILESYSTEM=1 \ + --preload-file shaders/resources/shaders/glsl330/lightmap.vs \ + --preload-file shaders/resources/shaders/glsl330/lightmap.fs \ + --preload-file shaders/resources/cubicmap_atlas.png \ + --preload-file shaders/resources/spark_flame.png + shaders/shaders_mesh_instancing: shaders/shaders_mesh_instancing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/lighting_instancing.vs@resources/shaders/glsl100/lighting_instancing.vs \ @@ -1003,6 +1032,10 @@ audio/audio_sound_loading: audio/audio_sound_loading.c --preload-file audio/resources/sound.wav@resources/sound.wav \ --preload-file audio/resources/target.ogg@resources/target.ogg +audio/audio_sound_multi: audio/audio_sound_multi.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file audio/resources/sound.wav@resources/sound.wav + audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 @@ -1012,6 +1045,25 @@ audio/audio_mixed_processor: audio/audio_mixed_processor.c --preload-file audio/resources/country.mp3@resources/country.mp3 \ --preload-file audio/resources/coin.wav@resources/coin.wav +# Compile OTHERS examples +others/easings_testbed: others/easings_testbed.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/embedded_files_loading: others/embedded_files_loading.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/raylib_opengl_interop: + $(info Skipping_others_raylib_opengl_interop) + +others/raymath_vector_angle: others/raymath_vector_angle.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/rlgl_compute_shader: + $(info Skipping_others_rlgl_compute_shader) + +others/rlgl_standalone: + $(info Skipping_others_rlgl_standalone) + # Clean everything clean: ifeq ($(PLATFORM),PLATFORM_DESKTOP) From 4ed776368a488b242781d77753f4b11396f97ce7 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Mon, 23 Oct 2023 01:11:50 -0700 Subject: [PATCH 0766/1710] When the frame counter gets to 0, reset the FPS average counter. This allows the window to be closed and reopened with clean FPS stats. (#3445) --- src/rcore.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index e3cd08188..9bd47983a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -484,9 +484,9 @@ void InitWindow(int width, int height, const char *title) } #endif + CORE.Time.frameCounter = 0; #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; #endif // Initialize random seed @@ -1396,6 +1396,16 @@ int GetFPS(void) 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) From daf227a185808d1e37e4269cf4a5be6b690226f6 Mon Sep 17 00:00:00 2001 From: Lukas <116672956+gk646@users.noreply.github.com> Date: Mon, 23 Oct 2023 18:16:28 +0200 Subject: [PATCH 0767/1710] Fixes a memory leak as a result of creating an AudioBuffer* with the old source.frameCount. This internally allocates memory to the structs data pointer which is then later overridden by the correct sound data of the source sound. (#3458) Additionally added a volume assignment from old to new as currently there is no way to get the volume of a sound and the AudioBuffer struct is not reachable from user code due to opaque definition. --- src/raudio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 6a1096767..dcc9f706a 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -928,7 +928,6 @@ Sound LoadSoundFromWave(Wave wave) } // Clone sound from existing sound data, clone does not own wave data -// Wave data must // NOTE: Wave data must be unallocated manually and will be shared across all clones Sound LoadSoundAlias(Sound source) { @@ -936,13 +935,16 @@ Sound LoadSoundAlias(Sound source) if (source.stream.buffer->data != NULL) { - AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, source.frameCount, AUDIO_BUFFER_USAGE_STATIC); + AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); if (audioBuffer == NULL) { TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); return sound; // early return to avoid dereferencing the audioBuffer null pointer } + audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames; + audioBuffer->volume = source.stream.buffer->volume; audioBuffer->data = source.stream.buffer->data; + sound.frameCount = source.frameCount; sound.stream.sampleRate = AUDIO.System.device.sampleRate; sound.stream.sampleSize = 32; @@ -953,6 +955,7 @@ Sound LoadSoundAlias(Sound source) return sound; } + // Checks if a sound is ready bool IsSoundReady(Sound sound) { From 3ff60269174d0f264c152875be4d1808b7fe0195 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 18:32:24 +0200 Subject: [PATCH 0768/1710] REVIEWED: Move screen capture logic to `rcore.c`, available for all platforms --- src/platforms/rcore_desktop.c | 55 -------------------------------- src/platforms/rcore_web.c | 59 ----------------------------------- src/rcore.c | 40 +++++++++++++++++++++++- 3 files changed, 39 insertions(+), 115 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 9e653dd0e..b7def6e67 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1658,61 +1658,6 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // Check the exit key to set close window if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_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 - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif } // GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index e918e7d34..7943e18f8 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1044,65 +1044,6 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // Check the exit key to set close window if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_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); - - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); - - 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 - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif } // GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) diff --git a/src/rcore.c b/src/rcore.c index 9bd47983a..0cd3f4408 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -739,6 +739,44 @@ void EndDrawing(void) 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 + #if defined(SUPPORT_EVENTS_AUTOMATION) // Events recording and playing logic if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter); @@ -748,7 +786,7 @@ void EndDrawing(void) if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false; PlayAutomationEvent(CORE.Time.frameCounter); } -#endif +#endif // SUPPORT_EVENTS_AUTOMATION CORE.Time.frameCounter++; } From a0f00343523a8898dfed46ee21afaa99d0c15f66 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 19:15:40 +0200 Subject: [PATCH 0769/1710] REVIEWED: `InitPlatform()` organization and code-gardening --- src/platforms/rcore_android.c | 39 +++++----- src/platforms/rcore_desktop.c | 115 ++++++++++++++++++------------ src/platforms/rcore_desktop_sdl.c | 34 ++++++--- src/platforms/rcore_drm.c | 56 +++++++++------ src/platforms/rcore_template.c | 30 ++++++-- src/platforms/rcore_web.c | 79 ++++++++++++-------- src/rcore.c | 2 - 7 files changed, 221 insertions(+), 134 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 1272eecc3..4faf73b7d 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -518,6 +518,8 @@ void PollInputEvents(void) // Initialize platform: graphics, inputs and more int InitPlatform(void) { + // Initialize display basic configuration + //---------------------------------------------------------------------------- CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; @@ -545,27 +547,33 @@ int InitPlatform(void) //AConfiguration_getKeyboard(platform.app->config); //AConfiguration_getScreenSize(platform.app->config); //AConfiguration_getScreenLong(platform.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - platform.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - platform.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = platform.app->activity->internalDataPath; - + // 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 + //---------------------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + //---------------------------------------------------------------------------- + platform.app->onAppCmd = AndroidCommandCallback; + //---------------------------------------------------------------------------- + + // Initialize input events system + //---------------------------------------------------------------------------- + platform.app->onInputEvent = AndroidInputCallback; + //---------------------------------------------------------------------------- + + // 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 + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Initialized successfully"); // Android ALooper_pollAll() variables int pollResult = 0; @@ -613,7 +621,6 @@ void ClosePlatform(void) } } - // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index b7def6e67..f1ad8a9a3 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1236,6 +1236,8 @@ int InitPlatform(void) int result = glfwInit(); if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- glfwDefaultWindowHints(); // Set default windows hints //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits @@ -1450,60 +1452,73 @@ int InitPlatform(void) } glfwMakeContextCurrent(platform.handle); - 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) + result = glfwGetError(NULL); + + // Check context activation + if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { - // WARNING: It seems to hit a critical render path in Intel HD Graphics - glfwSwapInterval(1); - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + CORE.Window.ready = true; + + 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) + { + // WARNING: It seems to hit a critical render path in Intel HD Graphics + glfwSwapInterval(1); + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + } + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // 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__) + 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); + + // Mouse input scaling for the new screen size + SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); + #endif + } + + 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); } - - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // 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__) - 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); - - // Mouse input scaling for the new screen size - SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); -#endif + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; } - 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); - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // 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); + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + //---------------------------------------------------------------------------- + // Initialize input events callbacks + //---------------------------------------------------------------------------- // Set window callback events glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); @@ -1521,12 +1536,19 @@ int InitPlatform(void) glfwSetJoystickCallback(JoystickCallback); glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + //---------------------------------------------------------------------------- - // Initialize hi-res timer + // Initialize timming system + //---------------------------------------------------------------------------- InitTimer(); - - // Initialize base path for storage + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully"); return 0; } @@ -1542,7 +1564,6 @@ void ClosePlatform(void) #endif } - // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index d2f550ace..7fe9673fa 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1107,6 +1107,8 @@ int InitPlatform(void) int result = SDL_Init(SDL_INIT_EVERYTHING); 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; @@ -1143,6 +1145,7 @@ int InitPlatform(void) //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // NOTE: Some OpenGL context attributes must be set before window creation + 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); @@ -1170,7 +1173,7 @@ int InitPlatform(void) { CORE.Window.ready = true; - SDL_DisplayMode displayMode; + SDL_DisplayMode displayMode = { 0 }; SDL_GetCurrentDisplayMode(GetCurrentMonitor(), &displayMode); CORE.Window.display.width = displayMode.w; @@ -1187,30 +1190,43 @@ int InitPlatform(void) 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 graphic device"); return -1; } + 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); + //---------------------------------------------------------------------------- - - // Init input gamepad + // Initialize input events system + //---------------------------------------------------------------------------- if (SDL_NumJoysticks() >= 1) { SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } + //---------------------------------------------------------------------------- - // Initialize hi-res timer - //InitTimer(); + // Initialize timming system + //---------------------------------------------------------------------------- + // NOTE: No need to call InitTimer(), let SDL manage it internally CORE.Time.previous = GetTime(); // Get time as double + //---------------------------------------------------------------------------- - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); + // Initialize storage system + //---------------------------------------------------------------------------- + CORE.Storage.basePath = GetWorkingDirectory(); // Define base path for storage + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); return 0; } +// Close platform void ClosePlatform(void) { SDL_FreeCursor(platform.cursor); // Free cursor @@ -1219,7 +1235,7 @@ void ClosePlatform(void) SDL_Quit(); // Deinitialize SDL internal global state } - +// Scancode to keycode mapping static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) { if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 2ecfc2a3a..9609c50d2 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -576,6 +576,8 @@ int InitPlatform(void) platform.prevBO = NULL; platform.prevFB = 0; + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -846,7 +848,6 @@ int InitPlatform(void) } // Create an EGL window surface - //--------------------------------------------------------------------------------- platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL); if (EGL_NO_SURFACE == platform.surface) { @@ -863,14 +864,14 @@ int InitPlatform(void) // 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); - if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return -1; - } - else + // 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; @@ -882,16 +883,15 @@ int InitPlatform(void) 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); } - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(eglGetProcAddress); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - - // If graphic device is no properly initialized, we end program + // 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); @@ -901,16 +901,29 @@ int InitPlatform(void) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false - // Initialize hi-res timer + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + //---------------------------------------------------------------------------- + + // Initialize input events system + //---------------------------------------------------------------------------- + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //---------------------------------------------------------------------------- + + // Initialize timming system + //---------------------------------------------------------------------------- InitTimer(); + //---------------------------------------------------------------------------- - // Initialize base path for storage + // Initialize storage system + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); - - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; } @@ -1005,7 +1018,6 @@ void ClosePlatform(void) if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); } - // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 08210289e..11ce45c1e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -518,13 +518,27 @@ int InitPlatform(void) EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); - // Enabling current display surface and context failed - if (result == EGL_FALSE) + // Check surface and context activation + if (result != EGL_FALSE) { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + 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; } - else CORE.Window.ready = true; //---------------------------------------------------------------------------- // If everything work as expected, we can continue @@ -545,7 +559,7 @@ int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - // TODO: Initialize input system + // TODO: Initialize input events system // It could imply keyboard, mouse, gamepad, touch... // Depending on the platform libraries/SDK it could use a callbacks mechanims // For system events and inputs evens polling on a per-frame basis, use PollInputEvents() @@ -553,15 +567,17 @@ int InitPlatform(void) // ... //---------------------------------------------------------------------------- - // TODO: Initialize hi-res timer + // TODO: Initialize timming system //---------------------------------------------------------------------------- InitTimer(); //---------------------------------------------------------------------------- - // TODO: Initialize base path for storage + // TODO: Initialize storage system //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); return 0; } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 7943e18f8..3da23a054 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -677,6 +677,8 @@ int InitPlatform(void) int result = glfwInit(); if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- glfwDefaultWindowHints(); // Set default windows hints // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits // glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits @@ -862,43 +864,46 @@ int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); + result = glfwGetError(NULL); + + // Check context activation + if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) + { + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); + 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; + } if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - // 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. - - 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); - - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // 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); - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + //---------------------------------------------------------------------------- + + // Initialize input events callbacks + //---------------------------------------------------------------------------- // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -927,6 +932,19 @@ int InitPlatform(void) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //---------------------------------------------------------------------------- + + // Initialize timming system + //---------------------------------------------------------------------------- + InitTimer(); + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- + CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: WEB: Initialized successfully"); return 0; } @@ -938,7 +956,6 @@ void ClosePlatform(void) glfwTerminate(); } - // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore.c b/src/rcore.c index 0cd3f4408..ef3beaee3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -491,8 +491,6 @@ void InitWindow(int width, int height, const char *title) // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } // Close window and unload OpenGL context From 803b1a910e0b23986688e9d4e53b77d18ba41767 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 19:59:29 +0200 Subject: [PATCH 0770/1710] REVIEWED: Check OpenGL version required, fix #3457 --- src/platforms/rcore_desktop_sdl.c | 43 +++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 7fe9673fa..ad051a95b 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1145,11 +1145,44 @@ int InitPlatform(void) //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // NOTE: Some OpenGL context attributes must be set before window creation - - 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); - //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + + // 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); +#if defined(__APPLE__) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // OSX Requires forward compatibility +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#endif + } + 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_VSYNC_HINT) { From 8fbd42d592c22612e18d2c6f9bcef8a107984675 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Oct 2023 10:14:17 +0200 Subject: [PATCH 0771/1710] Fix #3461 --- src/platforms/rcore_desktop.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f1ad8a9a3..de34c8711 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1120,7 +1120,11 @@ void PollInputEvents(void) // NOTE: We do it here in case of disconnection for (int i = 0; i < MAX_GAMEPADS; i++) { - if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; + if (glfwJoystickPresent(i)) + { + CORE.Input.Gamepad.ready[i] = true; + strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); + } else CORE.Input.Gamepad.ready[i] = false; } From 7e5eff8a29525df247110268133dcf11f9e72b11 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Oct 2023 10:15:19 +0200 Subject: [PATCH 0772/1710] Revert "Fix #3461" This reverts commit 8fbd42d592c22612e18d2c6f9bcef8a107984675. --- src/platforms/rcore_desktop.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index de34c8711..f1ad8a9a3 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1120,11 +1120,7 @@ void PollInputEvents(void) // NOTE: We do it here in case of disconnection for (int i = 0; i < MAX_GAMEPADS; i++) { - if (glfwJoystickPresent(i)) - { - CORE.Input.Gamepad.ready[i] = true; - strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); - } + if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; else CORE.Input.Gamepad.ready[i] = false; } From b0c0f2e5606f129175a10919a16b9eaea248f150 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 25 Oct 2023 07:17:54 -0300 Subject: [PATCH 0773/1710] Fix OpenURL on SDL (#3460) --- src/platforms/rcore_desktop_sdl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index ad051a95b..ccc2acf0a 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -893,9 +893,15 @@ double GetTime(void) } // 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) { - SDL_OpenURL(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); } //---------------------------------------------------------------------------------- @@ -1145,7 +1151,7 @@ int InitPlatform(void) //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) { @@ -1224,9 +1230,9 @@ int InitPlatform(void) 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; + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; } // Load OpenGL extensions @@ -1253,7 +1259,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); // Define base path for storage //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); return 0; From cb1c2ffda133a43b6dd7237c9139110954712d75 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:13:51 -0300 Subject: [PATCH 0774/1710] Fix gamepad names for PLATFORM_DESKTOP/GLFW (#3462) --- src/platforms/rcore_desktop.c | 36 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f1ad8a9a3..b43a6b5ae 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -649,12 +649,12 @@ void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; - + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; - + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -663,12 +663,12 @@ void SetWindowMaxSize(int width, int height) { CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; - + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; - + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -1108,7 +1108,7 @@ void PollInputEvents(void) // 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 // WARNING: If the target desktop device supports touch screen, this behavious should be reviewed! // TODO: GLFW does not support multi-touch input just yet @@ -1406,7 +1406,7 @@ int InitPlatform(void) } } } - + 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, @@ -1450,10 +1450,10 @@ int InitPlatform(void) TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); return -1; } - + glfwMakeContextCurrent(platform.handle); result = glfwGetError(NULL); - + // Check context activation if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { @@ -1500,9 +1500,9 @@ int InitPlatform(void) 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"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -1511,12 +1511,12 @@ int InitPlatform(void) // 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); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events callbacks //---------------------------------------------------------------------------- // Set window callback events @@ -1536,6 +1536,12 @@ int InitPlatform(void) glfwSetJoystickCallback(JoystickCallback); 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)); + } //---------------------------------------------------------------------------- // Initialize timming system @@ -1547,9 +1553,9 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully"); - + return 0; } From 9a687e3153633479067fbb0439a7e0214dec665b Mon Sep 17 00:00:00 2001 From: 2Bear Date: Thu, 26 Oct 2023 16:15:25 +0800 Subject: [PATCH 0775/1710] Fix missing `PLATFORM_DESKTOP_SDL` checks. (#3469) --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index e38642ec9..7c93bf929 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -803,7 +803,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 - #if defined(PLATFORM_DESKTOP) + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) #define GLAD_GLES2_IMPLEMENTATION #include "external/glad_gles2.h" #else @@ -2248,7 +2248,7 @@ void rlLoadExtensions(void *loader) // RLGL.ExtSupported.maxAnisotropyLevel #elif defined(GRAPHICS_API_OPENGL_ES2) - #if defined(PLATFORM_DESKTOP) + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) // TODO: Support OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); From 2f6b2897fe9d6a777b4f32ff6490436fbbb1b54b Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Thu, 26 Oct 2023 05:18:00 -0300 Subject: [PATCH 0776/1710] GetCurrentMonitor() - check window center instead of top-left corner (#3468) --- src/platforms/rcore_desktop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index b43a6b5ae..1114181a4 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -755,6 +755,8 @@ int GetCurrentMonitor(void) int y = 0; glfwGetWindowPos(platform.handle, &x, &y); + x += (int)CORE.Window.screen.width / 2; + y += (int)CORE.Window.screen.height / 2; for (int i = 0; i < monitorCount; i++) { From 804f1a83eba7aa5cbb701457e8d6cda372b1a10d Mon Sep 17 00:00:00 2001 From: jestarray <34615798+jestarray@users.noreply.github.com> Date: Thu, 26 Oct 2023 01:24:21 -0700 Subject: [PATCH 0777/1710] Fix IsGestureDetected parameter inconsistency in raylib.h with rgextures.h (#3464) closes https://github.com/raysan5/raylib/issues/3463 --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index b172562d0..40fcd98fd 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1167,7 +1167,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t // 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 bool IsGestureDetected(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 Vector2 GetGestureDragVector(void); // Get gesture drag vector From eddeafd2ed3be0b738a9ebcb5083448bd9640542 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 10:28:00 +0200 Subject: [PATCH 0778/1710] Revert "Fix IsGestureDetected parameter inconsistency in raylib.h with rgextures.h (#3464)" This reverts commit 804f1a83eba7aa5cbb701457e8d6cda372b1a10d. --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 40fcd98fd..b172562d0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1167,7 +1167,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected +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 Vector2 GetGestureDragVector(void); // Get gesture drag vector From 77730c80d903083751cc81a6912593c95f4f7165 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 10:34:39 +0200 Subject: [PATCH 0779/1710] Updated to miniaudio v0.11.19 #3448 --- src/external/miniaudio.h | 168 +++++++++++++++++++++++++-------------- 1 file changed, 110 insertions(+), 58 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 181f45289..518e3c43a 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.18 - 2023-08-07 +miniaudio - v0.11.19 - TBD David Reid - mackron@gmail.com @@ -87,7 +87,7 @@ device on the stack, but you could allocate it on the heap if that suits your si // Do something here. Probably your program's main loop. - ma_device_uninit(&device); // This will stop the device so no need to do that manually. + ma_device_uninit(&device); return 0; } ``` @@ -1675,7 +1675,7 @@ an example for initializing a data source: // ... - ma_resource_manager_data_source_uninit(pResourceManager, &dataSource); + ma_resource_manager_data_source_uninit(&dataSource); ``` The `flags` parameter specifies how you want to perform loading of the sound file. It can be a @@ -1912,10 +1912,10 @@ once after the other: ```c ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded. + ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded. + ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. ``` A binary search tree (BST) is used for storing data buffers as it has good balance between @@ -3409,7 +3409,7 @@ miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buf read from memory that's managed by the application, but can also handle the memory management for you internally. Memory management is flexible and should support most use cases. -Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: +Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: ```c ma_audio_buffer_config config = ma_audio_buffer_config_init( @@ -3716,7 +3716,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 18 +#define MA_VERSION_REVISION 19 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -4267,7 +4267,7 @@ typedef enum ma_standard_sample_rate_192000 = 192000, ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11250, + ma_standard_sample_rate_11025 = 11025, ma_standard_sample_rate_8000 = 8000, ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ @@ -5390,7 +5390,7 @@ MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_ca /* Converts the given input data. -Both the input and output frames must be in the format specified in the config when the resampler was initilized. +Both the input and output frames must be in the format specified in the config when the resampler was initialized. On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use @@ -9133,8 +9133,6 @@ speakers or received from the microphone which can in turn result in de-syncs. Do not call this in any callback. -This will be called implicitly by `ma_device_uninit()`. - See Also -------- @@ -10171,7 +10169,7 @@ MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels typedef struct { - ma_data_source_vtable ds; + ma_data_source_base ds; ma_noise_config config; ma_lcg lcg; union @@ -10569,7 +10567,7 @@ typedef struct /* Extended processing callback. This callback is used for effects that process input and output at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two seperate frame counts: one for input, and one for output. + they take two separate frame counts: one for input, and one for output. On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. @@ -12238,7 +12236,7 @@ static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) #define ma_abs(x) (((x) > 0) ? (x) : -(x)) #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) +#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) #define ma_align_64(x) ma_align(x, 8) #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) @@ -13639,7 +13637,7 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); if (length < 0) { - return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */ + return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ } if ((size_t)length < sizeof(pFormattedMessageStack)) { @@ -16180,7 +16178,15 @@ static void ma_thread_wait__posix(ma_thread* pThread) static ma_result ma_mutex_init__posix(ma_mutex* pMutex) { - int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); + int result; + + if (pMutex == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pMutex); + + result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); if (result != 0) { return ma_result_from_errno(result); } @@ -18452,7 +18458,7 @@ Timing *******************************************************************************/ #if defined(MA_WIN32) && !defined(MA_POSIX) static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - void ma_timer_init(ma_timer* pTimer) + static void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; @@ -18464,7 +18470,7 @@ Timing pTimer->counter = counter.QuadPart; } - double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) { @@ -18637,30 +18643,36 @@ static void ma_device__on_notification(ma_device_notification notification) } } -void ma_device__on_notification_started(ma_device* pDevice) +static void ma_device__on_notification_started(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); } -void ma_device__on_notification_stopped(ma_device* pDevice) +static void ma_device__on_notification_stopped(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); } -void ma_device__on_notification_rerouted(ma_device* pDevice) +/* Not all platforms support reroute notifications. */ +#if !defined(MA_EMSCRIPTEN) +static void ma_device__on_notification_rerouted(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); } +#endif -void ma_device__on_notification_interruption_began(ma_device* pDevice) +/* Interruptions are only used on some platforms. */ +#if defined(MA_APPLE_MOBILE) +static void ma_device__on_notification_interruption_began(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); } -void ma_device__on_notification_interruption_ended(ma_device* pDevice) +static void ma_device__on_notification_interruption_ended(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); } +#endif static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) @@ -19115,10 +19127,10 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state n #if defined(MA_WIN32) - GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ #endif @@ -23270,7 +23282,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ } else { - /* An error occured and we need to abort. */ + /* An error occurred and we need to abort. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); result = ma_result_from_HRESULT(hr); break; @@ -34834,7 +34846,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "CoreFoundation.framework/CoreFoundation"); + pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } @@ -34843,7 +34855,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "CoreAudio.framework/CoreAudio"); + pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; @@ -34861,7 +34873,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to AudioToolbox. */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioUnit.framework/AudioUnit"); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); if (pContext->coreaudio.hAudioUnit == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); @@ -34871,7 +34883,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioToolbox.framework/AudioToolbox"); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); if (pContext->coreaudio.hAudioUnit == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); @@ -39956,7 +39968,7 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a /* With the audio worklet initialized we can now attach it to the graph. */ if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { - ma_result attachmentResult = EM_ASM_INT({ + ma_result attachmentResult = (ma_result)EM_ASM_INT({ var getUserMediaResult = 0; var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); @@ -39987,7 +39999,7 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ if (pParameters->pConfig->deviceType == ma_device_type_playback) { - ma_result attachmentResult = EM_ASM_INT({ + ma_result attachmentResult = (ma_result)EM_ASM_INT({ var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); audioWorklet.connect(audioContext.destination); @@ -40202,7 +40214,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co /* First thing we need is an AudioContext. */ var audioContextOptions = {}; - if (deviceType == window.miniaudio.device_type.playback) { + if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { audioContextOptions.sampleRate = sampleRate; } @@ -42100,10 +42112,23 @@ MA_API void ma_device_uninit(ma_device* pDevice) return; } - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); + /* + It's possible for the miniaudio side of the device and the backend to not be in sync due to + system-level situations such as the computer being put into sleep mode and the backend not + notifying miniaudio of the fact the device has stopped. It's possible for this to result in a + deadlock due to miniaudio thinking the device is in a running state, when in fact it's not + running at all. For this reason I am no longer explicitly stopping the device. I don't think + this should affect anyone in practice since uninitializing the backend will naturally stop the + device anyway. + */ + #if 0 + { + /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ + if (ma_device_is_started(pDevice)) { + ma_device_stop(pDevice); + } } + #endif /* Putting the device into an uninitialized state will make the worker thread return. */ ma_device__set_state(pDevice, ma_device_state_uninitialized); @@ -52835,7 +52860,7 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); } /* Tail. */ @@ -52861,7 +52886,7 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); } @@ -59701,7 +59726,7 @@ extern "C" { #define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) #define MA_DR_WAV_VERSION_MAJOR 0 #define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 12 +#define MA_DR_WAV_VERSION_REVISION 13 #define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) #include #define MA_DR_WAVE_FORMAT_PCM 0x1 @@ -64826,7 +64851,7 @@ MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_co /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); if (result != MA_SUCCESS) { - return MA_SUCCESS; + return result; } } @@ -64976,7 +65001,7 @@ MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decod /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); if (result != MA_SUCCESS) { - return MA_SUCCESS; + return result; } } @@ -68744,7 +68769,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; /* - Fences need to be acquired before doing anything. These must be aquired and released outside of + Fences need to be acquired before doing anything. These must be acquired and released outside of the node to ensure there's no holes where ma_fence_wait() could prematurely return before the data buffer has completed initialization. @@ -72016,7 +72041,7 @@ MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_n } if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); + pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); } else { pNodeBase->pOutputBuses = pNodeBase->_outputBuses; } @@ -72507,11 +72532,11 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde /* At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accomodate since we could be straddling the time period + our frame count and output pointer to accommodate since we could be straddling the time period that this function is getting called for. It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accomodate. The same thing applies for + therefore need to offset it by a number of frames to accommodate. The same thing applies for the stop time. */ timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; @@ -74097,7 +74122,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { fadeStartOffsetInFrames = 0; } else { - fadeStartOffsetInFrames -= ma_engine_get_time(pEngineNode->pEngine); + fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); } ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); @@ -75534,6 +75559,10 @@ MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 list *pOuterGain = 0; } + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } @@ -76103,7 +76132,7 @@ MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint } /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); return MA_SUCCESS; } @@ -76476,6 +76505,10 @@ MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadian *pOuterGain = 0; } + if (pSound == NULL) { + return; + } + ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } @@ -76757,6 +76790,8 @@ MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) { + ma_uint64 seekTarget; + if (pSound == NULL) { return MA_INVALID_ARGS; } @@ -76766,7 +76801,12 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* return MA_INVALID_OPERATION; } - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); + seekTarget = ma_atomic_load_64(&pSound->seekTarget); + if (seekTarget != MA_SEEK_TARGET_NONE) { + *pCursor = seekTarget; + } else { + return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); + } } MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) @@ -76785,16 +76825,28 @@ MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) { - if (pSound == NULL) { - return MA_INVALID_ARGS; + ma_result result; + ma_uint64 cursorInPCMFrames; + ma_uint32 sampleRate; + + if (pCursor != NULL) { + *pCursor = 0; } - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; + result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); + if (result != MA_SUCCESS) { + return result; } - return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor); + result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; } MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) @@ -77193,8 +77245,8 @@ code below please report the bug to the respective repository for the relevant p #define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) #define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) #define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 -#define MA_DR_WAV_INT64_MIN ((ma_int64)0x80000000 << 32) -#define MA_DR_WAV_INT64_MAX ((((ma_int64)0x7FFFFFFF) << 32) | 0xFFFFFFFF) +#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) +#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) #if defined(_MSC_VER) && _MSC_VER >= 1400 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC From 1cef62cf052432755e9955aa8798eca339dcf1d3 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 11:10:41 +0200 Subject: [PATCH 0780/1710] REVIEWED: `glfwGetError()` not availbale on `PLATFORM_WEB` fix #3470 --- src/platforms/rcore_web.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 3da23a054..e2373e45c 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -864,12 +864,12 @@ int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); - result = glfwGetError(NULL); + result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web // Check context activation - if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) + if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation + CORE.Window.ready = true; int fbWidth = CORE.Window.screen.width; int fbHeight = CORE.Window.screen.height; From e4547eb4225189eadd2c6f4e87b5e32c4a285b88 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:56:03 +0200 Subject: [PATCH 0781/1710] Remove trail spaces --- HISTORY.md | 22 +++++++++++----------- src/rshapes.c | 6 +++--- src/rtext.c | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 735e66514..0a0b1ce2b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -379,13 +379,13 @@ Some numbers to start with: Highlights for `raylib 4.2`: - **raylib extra libraries cleanup**: raylib has been on diet and all the _extra_ libraries included on previous releases have been removed from raylib. Now raylib only includes the original **7** raylib modules: `rcore`, `rlgl`, `rshapes`, `rtextures`, `rtext`, `rmodels` and `raudio`. But no worries, _extra_ libraries have not been deleted, they have been moved to their own repos for better maintainability and more focus on its functionality. The libraries moved out from raylib repo are: [`raygui`](https://github.com/raysan5/raygui), [`physac`](https://github.com/raysan5/physac), [`rmem`](https://github.com/raylib-extras/rmem), [`reasings`](https://github.com/raylib-extras/reasings) and [`raudio`](https://github.com/raysan5/raudio) (standalone mode). On that same line, a new **amazing GitHub group:** [`raylib-extras`](https://github.com/raylib-extras) has been created by @JeffM2501 to contain raylib extra libraries as well as other raylib add-ons provided by the community. Jeff has done an amazing work on that line, providing multiple libraries and examples for raylib, like [custom first-person and third person camera systems](https://github.com/raylib-extras/extras-c/tree/main/cameras), [Dear ImGui raylib integration](https://github.com/raylib-extras/rlImGui), [multiple specific examples](https://github.com/raylib-extras/examples-c) and even a complete [RPG Game Example](https://github.com/raylib-extras/RPGExample)! Great work Jeff! :D - - - **raylib examples review**: The +120 raylib examples have been reviewed to add clearer information about when the were first created (raylib version used) and when they were updated for the last time. But the greatest improvement for users has been the **addition of an estimated difficulty level** for every example, [web has been updated accordingly](https://www.raylib.com/examples.html) to reflect those difficulty levels. Now examples are classified with **1 to 4 stars** depending on difficulty to help users with their learning process. Personally, I think this "small" addition could be a game-changer to better guide new users on the library adoption! Additionally, this new raylib release includes 7 new examples; the most interesting one: [`text_codepoints_loading`](https://www.raylib.com/examples/text/loader.html?name=text_codepoints_loading) that illustrates how to load and draw custom codepoints from a font file, very useful for Asian languages. + + - **raylib examples review**: The +120 raylib examples have been reviewed to add clearer information about when the were first created (raylib version used) and when they were updated for the last time. But the greatest improvement for users has been the **addition of an estimated difficulty level** for every example, [web has been updated accordingly](https://www.raylib.com/examples.html) to reflect those difficulty levels. Now examples are classified with **1 to 4 stars** depending on difficulty to help users with their learning process. Personally, I think this "small" addition could be a game-changer to better guide new users on the library adoption! Additionally, this new raylib release includes 7 new examples; the most interesting one: [`text_codepoints_loading`](https://www.raylib.com/examples/text/loader.html?name=text_codepoints_loading) that illustrates how to load and draw custom codepoints from a font file, very useful for Asian languages. - [**`rres 1.0`**](https://github.com/raysan5/rres): New `rres` **resources packaging file-format**, including a [`rres-raylib`](https://github.com/raysan5/rres/blob/master/src/rres-raylib.h) library implementation and [`rrespacker`](https://raylibtech.itch.io/rrespacker) tool. `rres` file format has been [under development for +8 years](https://github.com/raysan5/rres#design-history) and it was originally created to be part of raylib. It was highly inspired by _XNA XNB_ resources file format but design has changed a lot along the years. This first release of the format specs is engine-agnostic and has been designed to be portable to any engine, including lots of professional features like data processing, compression and encryption. - [**`raygui 3.2`**](https://github.com/raysan5/raygui): The **official raylib immediate-mode gui library** designed for tools development has been updated to a new version aligned with raylib 4.2. Multiple controls have been reviewed for library consistency, now all controls follow a similar function signature. It has been battle-tested with the development of +8 published tools in the last months. The tools can be seen and used for free in the [raylib technologies tools page](https://raylibtech.itch.io/). Worth mentioning that several of those **tools have been open sourced** for anyone to use, compile, contribute or learn how the code works. - + - [**`raylib_parser`**](https://github.com/raysan5/raylib/tree/master/parser): Multiple contributors **using the tool to automatize bindings creation** have contributed with improvements of this **tool to parse `raylib.h`** (and other raylib-style headers) to tokenize its enums, structs and functions. Processed data can be exported to custom file formats (i.e XML, JSON, LUA) for bindings generation or even docs generation if required. - **New file system API**: Current API has been redesigned to be more comprehensive and better aligned with raylib naming conventions, two new functions are provided `LoadDirectoryFiles()`/`LoadDirectoryFilesEx()` to load a `FilePathList` for provided path, supporting extension filtering and recursive directory scan. `LoadDroppedFiles()` has been renamed to better reflect its internal functionality. Now, all raylib functions that start with `Load*()` allocate memory internally and a equivalent `Unload*()` function is defined to take care of that memory internally when not required any more! @@ -414,21 +414,21 @@ Highlights for `raylib 4.5`: - **`NEW` Improved ANGLE support on Desktop platforms**: Support for OpenGL ES 2.0 on Desktop platforms (Windows, Linux, macOS) has been reviewed by @wtnbgo GitHub user. Now raylib can be compiled on desktop for OpenGL ES 2.0 and linked against [`ANGLE`](https://github.com/google/angle). This _small_ addition open the door to building raylib for all **ANGLE supported backends: Direct3D 11, Vulkan and Metal**. Please note that this new feature is still experimental and requires further testing! - **`NEW` Camera module**: A brand new implementation from scratch for `rcamera` module, contributed by @Crydsch GitHub user! **New camera system is simpler, more flexible, more granular and more extendable**. Specific camera math transformations (movement/rotation) have been moved to individual functions, exposing them to users if required. Global state has been removed from the module and standalone usage has been greatly improved; now `rcamera.h` single-file header-only library can be used externally, independently of raylib. A new `UpdateCameraPro()` function has been added to address input-dependency of `UpdateCamera()`, now advance users have **full control over camera inputs and movement/rotation speeds**! - + - **`NEW` Support for M3D models and M3D/GLTF animations**: 3d models animations support has been a limited aspect of raylib for long time, some versions ago IQM animations were supported but raylib 4.5 also adds support for the brand new [M3D file format](https://bztsrc.gitlab.io/model3d/), including animations and the long expected support for **GLTF animations**! The new M3D file format is **simple, portable, feature complete, extensible and open source**. It also provides a complete set of tools to export/visualize M3D models from/to Blender! Now raylib supports up to **3 model file-formats with animations**: `IQM`, `GLTF` and `M3D`. - + - **`NEW` Support QOA audio format (import/export)**: Just a couple of months ago the new [QOA file format](https://qoaformat.org/) was published, a very simple, portable and open source quite-ok-audio file format. raylib already supports it, added to `raudio` module and including audio loading from file, loading from memory, streaming from file, streaming from memory and **exporting to QOA** audio format. **Because simplicity really matters to raylib!** - + - **`NEW` Module for compressed textures loading**: [`rl_gputex`](https://github.com/raysan5/raylib/blob/master/src/external/rl_gputex.h), a portable single-file header-only small library to load compressed texture file-formats (DDS, PKM, KTX, PVR, ASTC). Provided functionality is not new to raylib but it was part of the raylib `rtextures` module, now it has been moved into a separate self-contained library, **improving portability**. Note that this module is only intended to **load compressed data from files, ready to be uploaded to GPU**, no compression/decompression functionality is provided. This change is a first step towards a better modularization of raylib library. - + - **Reviewed `rlgl` module for automatic limits checking**: Again, [`rlgl`](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) has been reviewed to simplify usage. Now users do not need to worry about reaching the internal render-batch limits when they send their triangles to draw 2d/3d, `rlgl` manages it automatically! This change allows a **great simplification for other modules** like `rshapes`, `rtextures` and `rmodels` that do not need to worry about bufffer overflows and can just define as many vertex as desired! - - - **Reviewed `rshapes` module to minimize the rlgl dependency**: Now `rshapes` 2d shapes drawing functions **only depend on 6 low-level functions**: `rlBegin()`, `rlEnd()`, `rlVertex3f()`, `rlTexCoord2f()`, `rlNormal3f()`, `rlSetTexture()`. With only those pseudo-OpenGl 1.1 minimal functionality, everything can be drawn! This improvement converts `rshapes` module in a **self-contained, portable shapes-drawing library that can be used independently of raylib**, as far as entry points for those 6 functions are provided by the user. It even allows to be used for software rendering, with the proper backend! + + - **Reviewed `rshapes` module to minimize the rlgl dependency**: Now `rshapes` 2d shapes drawing functions **only depend on 6 low-level functions**: `rlBegin()`, `rlEnd()`, `rlVertex3f()`, `rlTexCoord2f()`, `rlNormal3f()`, `rlSetTexture()`. With only those pseudo-OpenGl 1.1 minimal functionality, everything can be drawn! This improvement converts `rshapes` module in a **self-contained, portable shapes-drawing library that can be used independently of raylib**, as far as entry points for those 6 functions are provided by the user. It even allows to be used for software rendering, with the proper backend! - **Added data structures validation functions**: Multiple functions have been added by @RobLoach GitHub user to ease the validation of raylib data structures: `IsImageReady()`, `IsTextureReady()`, `IsSoundReady()`... Now users have a simple mechanism to **make sure data has been correctly loaded**, instead of checking internal structure values by themselfs. - + As usual, those are only some highlights but there is much more! New image generators, new color transformation functionality, improved blending support for color/alpha, etc... Make sure to check raylib [CHANGELOG]([CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG)) for a detailed list of changes! Please, note that all breaking changes have been flagged with a `WARNING` in the CHANGELOG, specially useful for binding creators! -**raylib keeps improving one more version** with a special focus on maintainability and sustainability. Always working towards making the library more **simple and easy-to-use**. +**raylib keeps improving one more version** with a special focus on maintainability and sustainability. Always working towards making the library more **simple and easy-to-use**. Let's keep **enjoying games/tools/graphics programming!** :) diff --git a/src/rshapes.c b/src/rshapes.c index de64f1593..e8e533d30 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -190,7 +190,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) 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 }, @@ -255,7 +255,7 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - + float a = powf(1.0f - t, 2); float b = 2.0f*(1.0f - t)*t; float c = powf(t, 2); @@ -301,7 +301,7 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - + float a = powf(1.0f - t, 3); float b = 3.0f*powf(1.0f - t, 2)*t; float c = 3.0f*(1.0f - t)*powf(t, 2); diff --git a/src/rtext.c b/src/rtext.c index 5b43bfb92..b83eb171b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1349,7 +1349,7 @@ unsigned int TextLength(const char *text) if (text != NULL) { // NOTE: Alternative: use strlen(text) - + while (*text++) length++; } @@ -1418,7 +1418,7 @@ int TextCopy(char *dst, const char *src) if ((src != NULL) && (dst != NULL)) { // NOTE: Alternative: use strcpy(dst, src) - + while (*src != '\0') { *dst = *src; @@ -1463,7 +1463,7 @@ const char *TextSubtext(const char *text, int position, int length) } if (length >= textLength) length = textLength; - + // NOTE: Alternative: memcpy(buffer, text + position, length) for (int c = 0 ; c < length ; c++) From d0141bd105b491fbef9ea5fb8c3ba26ba0432717 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:56:38 +0200 Subject: [PATCH 0782/1710] Remove trail spaces --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_drm.c | 14 +++++++------- src/platforms/rcore_template.c | 14 +++++++------- src/platforms/rcore_web.c | 12 ++++++------ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 4faf73b7d..83450bb0a 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -547,7 +547,7 @@ int InitPlatform(void) //AConfiguration_getKeyboard(platform.app->config); //AConfiguration_getScreenSize(platform.app->config); //AConfiguration_getScreenLong(platform.app->config); - + // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false @@ -569,7 +569,7 @@ int InitPlatform(void) // 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 //---------------------------------------------------------------------------- diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 9609c50d2..6f459b1af 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -864,14 +864,14 @@ int InitPlatform(void) // 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; @@ -883,9 +883,9 @@ int InitPlatform(void) 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"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -905,7 +905,7 @@ int InitPlatform(void) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events system //---------------------------------------------------------------------------- InitEvdevInput(); // Evdev inputs initialization @@ -922,7 +922,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 11ce45c1e..1cebfa798 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -522,7 +522,7 @@ int InitPlatform(void) 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; @@ -534,13 +534,13 @@ int InitPlatform(void) 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"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } //---------------------------------------------------------------------------- - + // If everything work as expected, we can continue CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; @@ -558,7 +558,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - + // TODO: Initialize input events system // It could imply keyboard, mouse, gamepad, touch... // Depending on the platform libraries/SDK it could use a callbacks mechanims @@ -576,7 +576,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); return 0; diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index e2373e45c..d797d99d7 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -865,7 +865,7 @@ int InitPlatform(void) glfwMakeContextCurrent(platform.handle); result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web - + // Check context activation if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { @@ -885,9 +885,9 @@ int InitPlatform(void) 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"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -896,12 +896,12 @@ int InitPlatform(void) // 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); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events callbacks //---------------------------------------------------------------------------- // Setup callback functions for the DOM events From 067dbe8657436e4778a91ea69c260f5beba48ec6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:57:07 +0200 Subject: [PATCH 0783/1710] ADDED: Drop files support to `PLATFORM_DESKTOP_SDL` --- src/platforms/rcore_desktop_sdl.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index ccc2acf0a..8543c751c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -998,6 +998,33 @@ void PollInputEvents(void) 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: @@ -1247,6 +1274,8 @@ int InitPlatform(void) SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } + + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); //---------------------------------------------------------------------------- // Initialize timming system From 99dac5451cad1c45c0a6b1da5c6175a7575f3403 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:59:19 +0200 Subject: [PATCH 0784/1710] ADDED: Automation Events System, exposed to users Added new API to record and play events Added examples illustrating functionality --- examples/core/core_automation_events.c | 289 +++++++ .../examples/core_automation_events.vcxproj | 390 +++++++++ projects/VS2022/raylib.sln | 19 + src/config.h | 3 +- src/raylib.h | 23 + src/rcore.c | 761 ++++++++++-------- src/rcore.h | 4 + 7 files changed, 1155 insertions(+), 334 deletions(-) create mode 100644 examples/core/core_automation_events.c create mode 100644 projects/VS2022/examples/core_automation_events.vcxproj diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c new file mode 100644 index 000000000..d011d6304 --- /dev/null +++ b/examples/core/core_automation_events.c @@ -0,0 +1,289 @@ +/******************************************************************************************* +* +* raylib [core] example - automation events +* +* Example originally created with raylib 5.0, last time updated with raylib 5.0 +* +* Example based on 2d_camera_platformer example by arvyy (@arvyy) +* +* 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" + +#define GRAVITY 400 +#define PLAYER_JUMP_SPD 350.0f +#define PLAYER_HOR_SPD 200.0f + +#define MAX_ENVIRONMENT_ELEMENTS 5 + +typedef struct Player { + Vector2 position; + float speed; + bool canJump; +} Player; + +typedef struct EnvElement { + Rectangle rect; + int blocking; + Color color; +} EnvElement; + + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - automation events"); + + // Define player + Player player = { 0 }; + 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 }, + {{ 0, 400, 1000, 200 }, 1, GRAY }, + {{ 300, 200, 400, 10 }, 1, GRAY }, + {{ 250, 300, 100, 10 }, 1, GRAY }, + {{ 650, 300, 100, 10 }, 1, GRAY } + }; + + // Define camera + Camera2D camera = { 0 }; + camera.target = player.position; + 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; + + int frameCounter = 0; + int playFrameCounter = 0; + int currentFrame = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + float deltaTime = GetFrameTime(); + + // Dropped files logic + //---------------------------------------------------------------------------------- + if (IsFileDropped()) + { + FilePathList droppedFiles = LoadDroppedFiles(); + + // Supports loading .rgs style files (text or binary) and .png style palette images + if (IsFileExtension(droppedFiles.paths[0], ".txt;.rae")) + { + UnloadAutomationEventList(&aelist); + aelist = LoadAutomationEventList(droppedFiles.paths[0]); + + eventRecording = false; + + // Reset scene state to play + eventPlaying = true; + playFrameCounter = 0; + + player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + } + + UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory + } + //---------------------------------------------------------------------------------- + + // Update player + //---------------------------------------------------------------------------------- + if (IsKeyDown(KEY_LEFT)) player.position.x -= PLAYER_HOR_SPD*deltaTime; + if (IsKeyDown(KEY_RIGHT)) player.position.x += PLAYER_HOR_SPD*deltaTime; + if (IsKeyDown(KEY_SPACE) && player.canJump) + { + player.speed = -PLAYER_JUMP_SPD; + player.canJump = false; + } + + int hitObstacle = 0; + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + EnvElement *element = &envElements[i]; + Vector2 *p = &(player.position); + if (element->blocking && + element->rect.x <= p->x && + element->rect.x + element->rect.width >= p->x && + element->rect.y >= p->y && + element->rect.y <= p->y + player.speed*deltaTime) + { + hitObstacle = 1; + player.speed = 0.0f; + p->y = element->rect.y; + } + } + + if (!hitObstacle) + { + player.position.y += player.speed*deltaTime; + player.speed += GRAVITY*deltaTime; + player.canJump = false; + } + else player.canJump = true; + + camera.zoom += ((float)GetMouseWheelMove()*0.05f); + + if (camera.zoom > 3.0f) camera.zoom = 3.0f; + else if (camera.zoom < 0.25f) camera.zoom = 0.25f; + + if (IsKeyPressed(KEY_R)) + { + camera.zoom = 1.0f; + player.position = (Vector2){ 400, 280 }; + } + //---------------------------------------------------------------------------------- + + // Update camera + //---------------------------------------------------------------------------------- + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000; + + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + EnvElement *element = &envElements[i]; + minX = fminf(element->rect.x, minX); + maxX = fmaxf(element->rect.x + element->rect.width, maxX); + minY = fminf(element->rect.y, minY); + maxY = fmaxf(element->rect.y + element->rect.height, maxY); + } + + Vector2 max = GetWorldToScreen2D((Vector2){ maxX, maxY }, camera); + Vector2 min = GetWorldToScreen2D((Vector2){ minX, minY }, camera); + + if (max.x < screenWidth) camera.offset.x = screenWidth - (max.x - screenWidth/2); + if (max.y < screenHeight) camera.offset.y = screenHeight - (max.y - screenHeight/2); + if (min.x > 0) camera.offset.x = screenWidth/2 - min.x; + if (min.y > 0) camera.offset.y = screenHeight/2 - min.y; + //---------------------------------------------------------------------------------- + + // Toggle events recording + if (IsKeyPressed(KEY_ONE)) + { + if (!eventPlaying) + { + if (eventRecording) + { + StopAutomationEventRecording(); + eventRecording = false; + + ExportAutomationEventList(aelist, "automation.rae"); + } + else + { + StartAutomationEventRecording(); + eventRecording = true; + } + } + } + + if (eventPlaying) + { + if (playFrameCounter == aelist.events[currentFrame].frame) + { + PlayAutomationEvent(aelist.events[currentFrame]); + currentFrame++; + + if (currentFrame == aelist.count) + { + eventPlaying = false; + currentFrame = 0; + playFrameCounter = 0; + } + } + + playFrameCounter++; + } + + if (eventRecording || eventPlaying) frameCounter++; + else frameCounter = 0; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(LIGHTGRAY); + + BeginMode2D(camera); + + // Draw environment elements + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + DrawRectangleRec(envElements[i].rect, envElements[i].color); + } + + // Draw player rectangle + DrawRectangleRec((Rectangle){ player.position.x - 20, player.position.y - 40, 40, 40 }, RED); + + EndMode2D(); + + // Draw automation events recording indicator + if (eventRecording) + { + if (((frameCounter/15)%2) == 1) + { + DrawCircle(GetScreenWidth() - 200, 20, 10, MAROON); + DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), GetScreenWidth() - 180, 15, 10, RED); + } + } + else if (eventPlaying) + { + if (((frameCounter/15)%2) == 1) + { + DrawTriangle((Vector2){ GetScreenWidth() - 200, 10 }, (Vector2){ GetScreenWidth() - 200, 30 }, (Vector2){ GetScreenWidth() - 200 + 20, 20 }, DARKGREEN); + DrawText(TextFormat("PLAYING EVENTS... [%i]", currentFrame), GetScreenWidth() - 170, 15, 10, LIME); + } + } + + DrawText("Controls:", 20, 20, 10, BLACK); + DrawText("- Right/Left to move", 30, 40, 10, DARKGRAY); + DrawText("- Space to jump", 30, 60, 10, DARKGRAY); + DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 30, 80, 10, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/projects/VS2022/examples/core_automation_events.vcxproj b/projects/VS2022/examples/core_automation_events.vcxproj new file mode 100644 index 000000000..e70a6b1ea --- /dev/null +++ b/projects/VS2022/examples/core_automation_events.vcxproj @@ -0,0 +1,390 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} + Win32Proj + core_automation_events + 10.0 + core_automation_events + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 007f796f9..167c60f3f 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -273,6 +273,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "exampl EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_split_screen", "examples\core_2d_camera_split_screen.vcxproj", "{CC62F7DB-D089-4677-8575-CAB7A7815C43}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_automation_events", "examples\core_automation_events.vcxproj", "{7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -2297,6 +2299,22 @@ Global {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.Build.0 = Release|x64 {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.ActiveCfg = Release|Win32 {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.Build.0 = Release|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.ActiveCfg = Debug|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.Build.0 = Debug|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.ActiveCfg = Debug|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.Build.0 = Debug|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.ActiveCfg = Release|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.Build.0 = Release|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.ActiveCfg = Release|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2435,6 +2453,7 @@ Global {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {CC62F7DB-D089-4677-8575-CAB7A7815C43} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/src/config.h b/src/config.h index 7886aeeba..96fdd065f 100644 --- a/src/config.h +++ b/src/config.h @@ -63,7 +63,7 @@ // Support CompressData() and DecompressData() functions #define SUPPORT_COMPRESSION_API 1 // Support automatic generated events, loading and recording of those events when required -//#define SUPPORT_EVENTS_AUTOMATION 1 +#define SUPPORT_AUTOMATION_EVENTS 1 // Support custom frame control, only for advance users // 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 @@ -85,6 +85,7 @@ #define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB +#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record //------------------------------------------------------------------------------------ // Module: rlgl - Configuration values diff --git a/src/raylib.h b/src/raylib.h index b172562d0..7b3eddaee 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -506,6 +506,20 @@ typedef struct FilePathList { char **paths; // Filepaths entries } FilePathList; +// Automation event (opaque struct) +typedef struct AutomationEvent { + unsigned int frame; // Event frame + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) +} AutomationEvent; + +// Automation event list +typedef struct AutomationEventList { + unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS) + unsigned int count; // Events entries count + AutomationEvent *events; // Events entries +} AutomationEventList; + //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- @@ -1114,6 +1128,15 @@ RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataS 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() +// 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 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) //------------------------------------------------------------------------------------ diff --git a/src/rcore.c b/src/rcore.c index ef3beaee3..67c79efa9 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,17 +3,17 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: +* - PLATFORM_DESKTOP: * > Windows (Win32, Win64) * > Linux (X11/Wayland desktop mode) * > macOS/OSX (x64, arm64) * > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_WEB: +* - PLATFORM_WEB: * > HTML5 (WebAssembly) -* - PLATFORM_DRM: +* - PLATFORM_DRM: * > Raspberry Pi 0-5 * > Linux native mode (KMS driver) -* - PLATFORM_ANDROID: +* - PLATFORM_ANDROID: * > Android (ARM, ARM64) * * CONFIGURATION: @@ -48,8 +48,8 @@ * provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module * for linkage * -* #define SUPPORT_EVENTS_AUTOMATION -* Support automatic generated events, loading and recording of those events when required +* #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) @@ -183,9 +183,8 @@ bool gifRecording = false; // GIF recording state MsfGifState gifState = { 0 }; // MSGIF context state #endif -#if defined(SUPPORT_EVENTS_AUTOMATION) -#define MAX_CODE_AUTOMATION_EVENTS 16384 - +#if defined(SUPPORT_AUTOMATION_EVENTS) +// Automation events type typedef enum AutomationEventType { EVENT_NONE = 0, // Input events @@ -212,12 +211,12 @@ typedef enum AutomationEventType { WINDOW_MINIMIZE, // no params WINDOW_RESIZE, // param[0]: width, param[1]: height // Custom events - ACTION_TAKE_SCREENSHOT, - ACTION_SETTARGETFPS + ACTION_TAKE_SCREENSHOT, // no params + ACTION_SETTARGETFPS // param[0]: fps } AutomationEventType; -// Event type -// Used to enable events flags +// Event type to config events flags +// TODO: Not used at the moment typedef enum { EVENT_INPUT_KEYBOARD = 0, EVENT_INPUT_MOUSE = 1, @@ -228,6 +227,7 @@ typedef enum { EVENT_CUSTOM = 32 } EventType; +// Event type name strings, required for export static const char *autoEventTypeName[] = { "EVENT_NONE", "INPUT_KEY_UP", @@ -255,19 +255,19 @@ static const char *autoEventTypeName[] = { "ACTION_SETTARGETFPS" }; +/* // Automation event (24 bytes) -typedef struct AutomationEvent { +// 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) -} AutomationEvent; +}; +*/ -static AutomationEvent *events = NULL; // Events array -static unsigned int eventCount = 0; // Events count -static bool eventsPlaying = false; // Play events -static bool eventsRecording = false; // Record events - -//static short eventsEnabled = 0b0000001111111111; // Events enabled for checking +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 //----------------------------------------------------------------------------------- @@ -291,11 +291,8 @@ static void SetupViewport(int width, int height); // Set viewport for 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_EVENTS_AUTOMATION) -static void LoadAutomationEvents(const char *fileName); // Load automation events from file -static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file -static void RecordAutomationEvent(unsigned int frame); // Record frame events (to internal events array) -static void PlayAutomationEvent(unsigned int frame); // Play frame events (from internal events array) +#if defined(SUPPORT_AUTOMATION_EVENTS) +static void RecordAutomationEvent(void); // Record frame events (to internal events array) #endif #if defined(_WIN32) @@ -304,14 +301,14 @@ void __stdcall Sleep(unsigned long msTimeout); // Required for: Wai #endif #if !defined(SUPPORT_MODULE_RTEXT) -const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' +const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' #endif // !SUPPORT_MODULE_RTEXT // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) #include "platforms/rcore_desktop.c" #elif defined(PLATFORM_DESKTOP_SDL) - #include "platforms/rcore_desktop_sdl.c" + #include "platforms/rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) #include "platforms/rcore_web.c" #elif defined(PLATFORM_DRM) @@ -434,12 +431,12 @@ void InitWindow(int width, int height, const char *title) 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); @@ -485,9 +482,6 @@ void InitWindow(int width, int height, const char *title) #endif CORE.Time.frameCounter = 0; -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); -#endif // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); @@ -512,14 +506,10 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - CORE.Window.ready = false; TRACELOG(LOG_INFO, "Window closed successfully"); } @@ -684,34 +674,6 @@ void EndDrawing(void) } #endif -#if defined(SUPPORT_EVENTS_AUTOMATION) - // Draw record/play indicator - if (eventsRecording) - { - gifFrameCounter++; - - if (((gifFrameCounter/15)%2) == 1) - { - DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON); - DrawText("EVENTS RECORDING", 50, CORE.Window.screen.height - 25, 10, RED); - } - - rlDrawRenderBatchActive(); // Update and draw internal render batch - } - else if (eventsPlaying) - { - gifFrameCounter++; - - if (((gifFrameCounter/15)%2) == 1) - { - DrawCircle(30, CORE.Window.screen.height - 20, 10, LIME); - DrawText("EVENTS PLAYING", 50, CORE.Window.screen.height - 25, 10, GREEN); - } - - rlDrawRenderBatchActive(); // Update and draw internal render batch - } -#endif - #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL) SwapScreenBuffer(); // Copy back buffer to front buffer (screen) @@ -775,16 +737,9 @@ void EndDrawing(void) } #endif // SUPPORT_SCREEN_CAPTURE -#if defined(SUPPORT_EVENTS_AUTOMATION) - // Events recording and playing logic - if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter); - else if (eventsPlaying) - { - // TODO: When should we play? After/before/replace PollInputEvents()? - if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false; - PlayAutomationEvent(CORE.Time.frameCounter); - } -#endif // SUPPORT_EVENTS_AUTOMATION +#if defined(SUPPORT_AUTOMATION_EVENTS) + if (automationEventRecording) RecordAutomationEvent(); // Event recording +#endif CORE.Time.frameCounter++; } @@ -1470,7 +1425,7 @@ float GetFrameTime(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//void SwapScreenBuffer(void); +//void SwapScreenBuffer(void); //void PollInputEvents(void); // Wait for some time (stop program execution) @@ -1481,7 +1436,7 @@ float GetFrameTime(void) void WaitTime(double seconds) { if (seconds < 0) return; - + #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) double destinationTime = GetTime() + seconds; #endif @@ -2180,6 +2135,237 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) return decodedData; } +//---------------------------------------------------------------------------------- +// 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); + list->events = NULL; + list->count = 0; + list->capacity = 0; +#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 \n"); + byteCount += sprintf(txtData + byteCount, "# e // \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 (int i = 0; i < list.count; i++) + { + byteCount += sprintf(txtData + byteCount, "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 +} + +// 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: CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; break; // param[0]: key + 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]; break; + CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1]; break; + } 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; + case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current + + // 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 + case ACTION_TAKE_SCREENSHOT: + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } break; + case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break; + default: break; + } + } +#endif +} + //---------------------------------------------------------------------------------- // Module Functions Definition: Input Handling: Keyboard //---------------------------------------------------------------------------------- @@ -2793,233 +2979,180 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath); } -#if defined(SUPPORT_EVENTS_AUTOMATION) -// NOTE: Loading happens over AutomationEvent *events -// TODO: This system should probably be redesigned -static void LoadAutomationEvents(const char *fileName) +#if defined(SUPPORT_AUTOMATION_EVENTS) +// Automation event recording +// NOTE: Recording is by default done at EndDrawing(), after PollInputEvents() +static void RecordAutomationEvent(void) { - // Load events file (binary) - /* - FILE *repFile = fopen(fileName, "rb"); - unsigned char fileId[4] = { 0 }; + // 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 - fread(fileId, 1, 4, repFile); - - if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) - { - fread(&eventCount, sizeof(int), 1, repFile); - TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount); - fread(events, sizeof(AutomationEvent), eventCount, repFile); - } - - fclose(repFile); - */ - - // Load events file (text) - FILE *repFile = fopen(fileName, "rt"); - - if (repFile != NULL) - { - unsigned int count = 0; - char buffer[256] = { 0 }; - - fgets(buffer, 256, repFile); - - while (!feof(repFile)) - { - if (buffer[0] == 'c') sscanf(buffer, "c %i", &eventCount); - else if (buffer[0] == 'e') - { - sscanf(buffer, "e %d %d %d %d %d", &events[count].frame, &events[count].type, - &events[count].params[0], &events[count].params[1], &events[count].params[2]); - - count++; - } - - fgets(buffer, 256, repFile); - } - - if (count != eventCount) TRACELOG(LOG_WARNING, "Events count provided is different than count"); - - fclose(repFile); - } - - TRACELOG(LOG_WARNING, "Events loaded: %i", eventCount); -} - -// Export recorded events into a file -static void ExportAutomationEvents(const char *fileName) -{ - unsigned char fileId[4] = "rEP "; - - // Save as binary - /* - FILE *repFile = fopen(fileName, "wb"); - fwrite(fileId, sizeof(unsigned char), 4, repFile); - fwrite(&eventCount, sizeof(int), 1, repFile); - fwrite(events, sizeof(AutomationEvent), eventCount, repFile); - fclose(repFile); - */ - - // Export events as text - FILE *repFile = fopen(fileName, "wt"); - - if (repFile != NULL) - { - fprintf(repFile, "# Automation events list\n"); - fprintf(repFile, "# c \n"); - fprintf(repFile, "# e // \n"); - - fprintf(repFile, "c %i\n", eventCount); - for (int i = 0; i < eventCount; i++) - { - fprintf(repFile, "e %i %i %i %i %i // %s\n", events[i].frame, events[i].type, - events[i].params[0], events[i].params[1], events[i].params[2], autoEventTypeName[events[i].type]); - } - - fclose(repFile); - } -} - -// EndDrawing() -> After PollInputEvents() -// Check event in current frame and save into the events[i] array -static void RecordAutomationEvent(unsigned int frame) -{ + // Keyboard input events recording + //------------------------------------------------------------------------------------- for (int key = 0; key < MAX_KEYBOARD_KEYS; key++) { - // INPUT_KEY_UP (only saved once) + // Event type: INPUT_KEY_UP (only saved once) if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_KEY_UP; - events[eventCount].params[0] = key; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_KEY_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 - // INPUT_KEY_DOWN + // Event type: INPUT_KEY_DOWN if (CORE.Input.Keyboard.currentKeyState[key]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_KEY_DOWN; - events[eventCount].params[0] = key; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_KEY_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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++) { - // INPUT_MOUSE_BUTTON_UP + // Event type: INPUT_MOUSE_BUTTON_UP if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_BUTTON_UP; - events[eventCount].params[0] = button; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_MOUSE_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 - // INPUT_MOUSE_BUTTON_DOWN + // Event type: INPUT_MOUSE_BUTTON_DOWN if (CORE.Input.Mouse.currentButtonState[button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_BUTTON_DOWN; - events[eventCount].params[0] = button; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_MOUSE_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 } - // INPUT_MOUSE_POSITION (only saved if changed) + // 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)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_POSITION; - events[eventCount].params[0] = (int)CORE.Input.Mouse.currentPosition.x; - events[eventCount].params[1] = (int)CORE.Input.Mouse.currentPosition.y; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_MOUSE_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 } - // INPUT_MOUSE_WHEEL_MOTION + // 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)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_WHEEL_MOTION; - events[eventCount].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x; - events[eventCount].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_MOUSE_WHEEL_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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++) { - // INPUT_TOUCH_UP + // Event type: INPUT_TOUCH_UP if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_UP; - events[eventCount].params[0] = id; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_TOUCH_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 - // INPUT_TOUCH_DOWN + // Event type: INPUT_TOUCH_DOWN if (CORE.Input.Touch.currentTouchState[id]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_DOWN; - events[eventCount].params[0] = id; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_TOUCH_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 - // INPUT_TOUCH_POSITION + // 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)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_POSITION; - events[eventCount].params[0] = id; - events[eventCount].params[1] = (int)CORE.Input.Touch.currentPosition[id].x; - events[eventCount].params[2] = (int)CORE.Input.Touch.currentPosition[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, "[%i] INPUT_TOUCH_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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++) { - // INPUT_GAMEPAD_CONNECT + // 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 @@ -3028,7 +3161,7 @@ static void RecordAutomationEvent(unsigned int frame) } */ - // INPUT_GAMEPAD_DISCONNECT + // 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 @@ -3039,122 +3172,84 @@ static void RecordAutomationEvent(unsigned int frame) for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++) { - // INPUT_GAMEPAD_BUTTON_UP + // Event type: INPUT_GAMEPAD_BUTTON_UP if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_BUTTON_UP; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = button; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_GAMEPAD_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 - // INPUT_GAMEPAD_BUTTON_DOWN + // Event type: INPUT_GAMEPAD_BUTTON_DOWN if (CORE.Input.Gamepad.currentButtonState[gamepad][button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_BUTTON_DOWN; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = button; - events[eventCount].params[2] = 0; + 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, "[%i] INPUT_GAMEPAD_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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++) { - // INPUT_GAMEPAD_AXIS_MOTION + // Event type: INPUT_GAMEPAD_AXIS_MOTION if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_AXIS_MOTION; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = axis; - events[eventCount].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f); + 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, "[%i] INPUT_GAMEPAD_AXIS_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 } } + //------------------------------------------------------------------------------------- - // INPUT_GESTURE + // Gestures input currentEventList->events recording + //------------------------------------------------------------------------------------- if (GESTURES.current != GESTURE_NONE) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GESTURE; - events[eventCount].params[0] = GESTURES.current; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + // 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, "[%i] INPUT_GESTURE: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + 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 } -} + //------------------------------------------------------------------------------------- -// Play automation event -static void PlayAutomationEvent(unsigned int frame) -{ - for (unsigned int i = 0; i < eventCount; i++) - { - if (events[i].frame == frame) - { - switch (events[i].type) - { - // Input events - case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = false; break; // param[0]: key - case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = true; break; // param[0]: key - case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = false; break; // param[0]: key - case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = true; break; // param[0]: key - case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y - { - CORE.Input.Mouse.currentPosition.x = (float)events[i].params[0]; - CORE.Input.Mouse.currentPosition.y = (float)events[i].params[1]; - } break; - case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta - { - CORE.Input.Mouse.currentWheelMove.x = (float)events[i].params[0]; break; - CORE.Input.Mouse.currentWheelMove.y = (float)events[i].params[1]; break; - } break; - case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[events[i].params[0]] = false; break; // param[0]: id - case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[events[i].params[0]] = true; break; // param[0]: id - case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y - { - CORE.Input.Touch.position[events[i].params[0]].x = (float)events[i].params[1]; - CORE.Input.Touch.position[events[i].params[0]].y = (float)events[i].params[2]; - } break; - case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = true; break; // param[0]: gamepad - case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = false; break; // param[0]: gamepad - case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = false; break; // param[0]: gamepad, param[1]: button - case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].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[events[i].params[0]][events[i].params[1]] = ((float)events[i].params[2]/32768.0f); - } break; - case INPUT_GESTURE: GESTURES.current = events[i].params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current + // Window events recording + //------------------------------------------------------------------------------------- + // TODO. + //------------------------------------------------------------------------------------- - // Window events - case WINDOW_CLOSE: CORE.Window.shouldClose = true; break; - case WINDOW_MAXIMIZE: MaximizeWindow(); break; - case WINDOW_MINIMIZE: MinimizeWindow(); break; - case WINDOW_RESIZE: SetWindowSize(events[i].params[0], events[i].params[1]); break; - - // Custom events - case ACTION_TAKE_SCREENSHOT: - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } break; - case ACTION_SETTARGETFPS: SetTargetFPS(events[i].params[0]); break; - default: break; - } - } - } + // Custom actions events recording + //------------------------------------------------------------------------------------- + // TODO. + //------------------------------------------------------------------------------------- } #endif diff --git a/src/rcore.h b/src/rcore.h index 1127585a0..a6955f8cc 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -89,6 +89,10 @@ #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 + // Flags operation macros #define FLAG_SET(n, f) ((n) |= (f)) #define FLAG_CLEAR(n, f) ((n) &= ~(f)) From 654b4e62579aea3dd7dc6ae0018271e2df1c2b55 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 00:45:00 +0200 Subject: [PATCH 0785/1710] Update core_automation_events.c --- examples/core/core_automation_events.c | 85 ++++++++++++++++++-------- 1 file changed, 60 insertions(+), 25 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index d011d6304..c35ce485a 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -77,7 +77,7 @@ int main(void) int frameCounter = 0; int playFrameCounter = 0; - int currentFrame = 0; + int currentPlayFrame = 0; SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -87,7 +87,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - float deltaTime = GetFrameTime(); + float deltaTime = 0.015f;//GetFrameTime(); // Dropped files logic //---------------------------------------------------------------------------------- @@ -106,6 +106,7 @@ int main(void) // Reset scene state to play eventPlaying = true; playFrameCounter = 0; + currentPlayFrame = 0; player.position = (Vector2){ 400, 280 }; player.speed = 0; @@ -163,8 +164,15 @@ int main(void) if (IsKeyPressed(KEY_R)) { - camera.zoom = 1.0f; + // Reset game state player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; } //---------------------------------------------------------------------------------- @@ -193,7 +201,7 @@ int main(void) //---------------------------------------------------------------------------------- // Toggle events recording - if (IsKeyPressed(KEY_ONE)) + if (IsKeyPressed(KEY_F2)) { if (!eventPlaying) { @@ -211,22 +219,41 @@ int main(void) } } } + else if (IsKeyPressed(KEY_F3)) + { + if (!eventRecording && (aelist.count > 0)) + { + // Reset scene state to play + eventPlaying = true; + playFrameCounter = 0; + currentPlayFrame = 0; + + player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + } + } if (eventPlaying) { - if (playFrameCounter == aelist.events[currentFrame].frame) + if (playFrameCounter >= aelist.events[currentPlayFrame].frame) { - PlayAutomationEvent(aelist.events[currentFrame]); - currentFrame++; + PlayAutomationEvent(aelist.events[currentPlayFrame]); + currentPlayFrame++; - if (currentFrame == aelist.count) + if (currentPlayFrame == aelist.count) { eventPlaying = false; - currentFrame = 0; + currentPlayFrame = 0; playFrameCounter = 0; } } - + playFrameCounter++; } @@ -253,28 +280,36 @@ int main(void) EndMode2D(); + // Draw game controls + DrawRectangle(10, 10, 290, 145, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(10, 10, 290, 145, Fade(BLUE, 0.8f)); + + DrawText("Controls:", 20, 20, 10, BLACK); + DrawText("- RIGHT | LEFT: Player movement", 30, 40, 10, DARKGRAY); + DrawText("- SPACE: Player jump", 30, 60, 10, DARKGRAY); + DrawText("- R: Reset game state", 30, 80, 10, DARKGRAY); + + DrawText("- F2: START/STOP RECORDING INPUT EVENTS", 30, 110, 10, BLACK); + DrawText("- F3: REPLAY LAST RECORDED INPUT EVENTS", 30, 130, 10, BLACK); + // Draw automation events recording indicator if (eventRecording) { - if (((frameCounter/15)%2) == 1) - { - DrawCircle(GetScreenWidth() - 200, 20, 10, MAROON); - DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), GetScreenWidth() - 180, 15, 10, RED); - } + DrawRectangle(10, 160, 290, 30, Fade(RED, 0.3f)); + DrawRectangleLines(10, 160, 290, 30, Fade(MAROON, 0.8f)); + DrawCircle(30, 175, 10, MAROON); + + if (((frameCounter/15)%2) == 1) DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), 50, 170, 10, MAROON); } else if (eventPlaying) { - if (((frameCounter/15)%2) == 1) - { - DrawTriangle((Vector2){ GetScreenWidth() - 200, 10 }, (Vector2){ GetScreenWidth() - 200, 30 }, (Vector2){ GetScreenWidth() - 200 + 20, 20 }, DARKGREEN); - DrawText(TextFormat("PLAYING EVENTS... [%i]", currentFrame), GetScreenWidth() - 170, 15, 10, LIME); - } - } + DrawRectangle(10, 160, 290, 30, Fade(LIME, 0.3f)); + DrawRectangleLines(10, 160, 290, 30, Fade(DARKGREEN, 0.8f)); + DrawTriangle((Vector2){ 20, 155 + 10 }, (Vector2){ 20, 155 + 30 }, (Vector2){ 40, 155 + 20 }, DARKGREEN); - DrawText("Controls:", 20, 20, 10, BLACK); - DrawText("- Right/Left to move", 30, 40, 10, DARKGRAY); - DrawText("- Space to jump", 30, 60, 10, DARKGRAY); - DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 30, 80, 10, DARKGRAY); + if (((frameCounter/15)%2) == 1) DrawText(TextFormat("PLAYING RECORDED EVENTS... [%i]", currentPlayFrame), 50, 170, 10, DARKGREEN); + } + EndDrawing(); //---------------------------------------------------------------------------------- From 98fcbe3fe2bc2f6a20f8c2415251e9164af8661f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 00:50:02 +0200 Subject: [PATCH 0786/1710] Update core_automation_events.c --- examples/core/core_automation_events.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index c35ce485a..adea0e88b 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -241,7 +241,8 @@ int main(void) if (eventPlaying) { - if (playFrameCounter >= aelist.events[currentPlayFrame].frame) + // NOTE: Multiple events could be executed in a single frame + while (playFrameCounter == aelist.events[currentPlayFrame].frame) { PlayAutomationEvent(aelist.events[currentPlayFrame]); currentPlayFrame++; From f721429f2584b2d2769c7031eb8823f4d768d979 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 01:19:10 +0200 Subject: [PATCH 0787/1710] ADDED: `SetAutomationEventBaseFrame(int frame)` --- examples/core/core_automation_events.c | 1 + src/raylib.h | 7 ++++--- src/rcore.c | 6 ++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index adea0e88b..27711b39d 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -214,6 +214,7 @@ int main(void) } else { + SetAutomationEventBaseFrame(180); StartAutomationEventRecording(); eventRecording = true; } diff --git a/src/raylib.h b/src/raylib.h index 7b3eddaee..d8bf1e07c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -508,9 +508,9 @@ typedef struct FilePathList { // Automation event (opaque struct) typedef struct AutomationEvent { - unsigned int frame; // Event frame - unsigned int type; // Event type (AutomationEventType) - int params[4]; // Event parameters (if required) + unsigned int frame; // Event frame + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) } AutomationEvent; // Automation event list @@ -1133,6 +1133,7 @@ RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); 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 diff --git a/src/rcore.c b/src/rcore.c index 67c79efa9..c3b69ae88 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2289,6 +2289,12 @@ void SetAutomationEventList(AutomationEventList *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) { From 3afd0a55b9f243d1a3f0fb6be026deb085828c9d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 16:55:27 +0200 Subject: [PATCH 0788/1710] Update miniaudio to latest dev #3471 --- src/external/miniaudio.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 518e3c43a..ac2da690d 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -2675,9 +2675,16 @@ outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frame example below: ```c - framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite); + ma_uint64 framesWritten; + result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); + if (result != MA_SUCCESS) { + ... handle error ... + } ``` +The `framesWritten` variable will contain the number of PCM frames that were actually written. This +is optionally and you can pass in `NULL` if you need this. + Encoders must be uninitialized with `ma_encoder_uninit()`. @@ -40902,6 +40909,11 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ma_device__on_notification_stopped(pDevice); } + /* If we stopped because the device has been uninitialized, abort now. */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + break; + } + /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ ma_device__set_state(pDevice, ma_device_state_stopped); ma_event_signal(&pDevice->stopEvent); @@ -76804,6 +76816,7 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* seekTarget = ma_atomic_load_64(&pSound->seekTarget); if (seekTarget != MA_SEEK_TARGET_NONE) { *pCursor = seekTarget; + return MA_SUCCESS; } else { return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); } From 2db7c727b653fc526b7da07fd337de02b7156e37 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Fri, 27 Oct 2023 12:01:05 -0300 Subject: [PATCH 0789/1710] GetCurrentMonitor() - use closest monitor (#3472) --- src/platforms/rcore_desktop.c | 55 ++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 1114181a4..f60a0e56c 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -751,15 +751,19 @@ int GetCurrentMonitor(void) } else { - int x = 0; - int y = 0; + int closestDist = 0x7FFFFFFF; - glfwGetWindowPos(platform.handle, &x, &y); - x += (int)CORE.Window.screen.width / 2; - y += (int)CORE.Window.screen.height / 2; + // Window center position + int wcx = 0; + int wcy = 0; + + glfwGetWindowPos(platform.handle, &wcx, &wcy); + wcx += (int)CORE.Window.screen.width / 2; + wcy += (int)CORE.Window.screen.height / 2; for (int i = 0; i < monitorCount; i++) { + // Monitor top-left position int mx = 0; int my = 0; @@ -769,17 +773,46 @@ int GetCurrentMonitor(void) if (mode) { - const int width = mode->width; - const int height = mode->height; + const int right = mx + mode->width - 1; + const int bottom = my + mode->height - 1; - if ((x >= mx) && - (x < (mx + width)) && - (y >= my) && - (y < (my + height))) + if ((wcx >= mx) && + (wcx <= right) && + (wcy >= my) && + (wcy <= bottom)) { index = i; break; } + + int xclosest = wcx; + if (wcx < mx) + { + xclosest = mx; + } + else if (wcx > right) + { + xclosest = right; + } + + int yclosest = wcy; + if (wcy < my) + { + yclosest = my; + } + else if (wcy > bottom) + { + yclosest = bottom; + } + + int dx = wcx - xclosest; + int dy = wcy - yclosest; + int dist = (dx * dx) + (dy * dy); + if (dist < closestDist) + { + index = i; + closestDist = dist; + } } else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } From b46505b13d4a85e26d1d5b6f9fc2a4264bf8b02f Mon Sep 17 00:00:00 2001 From: SuperUserNameMan Date: Fri, 27 Oct 2023 17:13:10 +0200 Subject: [PATCH 0790/1710] Update tinyobj_loader_c.h (#3474) temporary quickfix for issue #3473 --- src/external/tinyobj_loader_c.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index 502a55a7e..55d595a69 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -1269,6 +1269,11 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, if (is_line_ending(buf, i, end_idx)) { line_infos[line_no].pos = prev_pos; line_infos[line_no].len = i - prev_pos; + +// ---- QUICK BUG FIX : https://github.com/raysan5/raylib/issues/3473 + if ( i > 0 && buf[i-1] == '\r' ) line_infos[line_no].len--; +// -------- + prev_pos = i + 1; line_no++; } From effa3ee249ed393fb7b283092c4023e919be3061 Mon Sep 17 00:00:00 2001 From: Khalid Abdullah Date: Sun, 29 Oct 2023 21:22:59 +0600 Subject: [PATCH 0791/1710] [Typo fixed] in CHANGELOG (#3477) --- CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1da04cc73..244d329da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1871,9 +1871,9 @@ other changes: [models] Added function DrawCubeTexture() [models] Added function DrawQuad() [models] Added function DrawRay() -[models] Simplified funtion DrawPlane() +[models] Simplified function DrawPlane() [models] Removed function DrawPlaneEx() -[models] Simplified funtion DrawGizmo() +[models] Simplified function DrawGizmo() [models] Removed function DrawGizmoEx() [models] Added function LoadModelEx() [models] Review of function LoadCubicMap() From 01c264123d7bb12d166069a7f5c7403e98e51cfe Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 12:23:38 -0300 Subject: [PATCH 0792/1710] Remove rcore.h include from SDL (#3475) --- src/platforms/rcore_desktop_sdl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 8543c751c..566d75d8c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -49,8 +49,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include "SDL.h" // SDL base library (window/rendered, input, timming... functionality) #include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer) @@ -998,7 +996,7 @@ void PollInputEvents(void) switch (event.type) { case SDL_QUIT: CORE.Window.shouldClose = true; break; - + case SDL_DROPFILE: // Dropped file { if (CORE.Window.dropFileCount == 0) @@ -1019,7 +1017,7 @@ void PollInputEvents(void) 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!"); @@ -1274,7 +1272,7 @@ int InitPlatform(void) SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } - + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); //---------------------------------------------------------------------------- From b4865588f84288b0c52f143a91c6e12908d1237f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 16:36:46 +0100 Subject: [PATCH 0793/1710] REVIEWED: `GetCurrentMonitor()` #3472 --- src/platforms/rcore_desktop.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f60a0e56c..08b329e85 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -751,6 +751,11 @@ int GetCurrentMonitor(void) } else { + // In case the window is between two monitors, we use below logic + // to try to detect the "current monitor" for that window, note that + // this is probably an overengineered solution for a very side case + // trying to match SDL behaviour + int closestDist = 0x7FFFFFFF; // Window center position @@ -758,8 +763,8 @@ int GetCurrentMonitor(void) int wcy = 0; glfwGetWindowPos(platform.handle, &wcx, &wcy); - wcx += (int)CORE.Window.screen.width / 2; - wcy += (int)CORE.Window.screen.height / 2; + wcx += (int)CORE.Window.screen.width/2; + wcy += (int)CORE.Window.screen.height/2; for (int i = 0; i < monitorCount; i++) { @@ -786,28 +791,16 @@ int GetCurrentMonitor(void) } int xclosest = wcx; - if (wcx < mx) - { - xclosest = mx; - } - else if (wcx > right) - { - xclosest = right; - } + if (wcx < mx) xclosest = mx; + else if (wcx > right) xclosest = right; int yclosest = wcy; - if (wcy < my) - { - yclosest = my; - } - else if (wcy > bottom) - { - yclosest = bottom; - } + if (wcy < my) yclosest = my; + else if (wcy > bottom) yclosest = bottom; int dx = wcx - xclosest; int dy = wcy - yclosest; - int dist = (dx * dx) + (dy * dy); + int dist = (dx*dx) + (dy*dy); if (dist < closestDist) { index = i; From 975d4154e6739fc7fcbe6e3406d4974aa5c4705e Mon Sep 17 00:00:00 2001 From: Josh Colclough Date: Sun, 29 Oct 2023 15:41:02 +0000 Subject: [PATCH 0794/1710] Fix the Julia set shader example (#3467) * Simplify POI selection * Improve mouse logic * Add colour cycles to the shader to show finer details. Works well with high iteration numbers * Testing things... * Actually fix zoom. Also allow user to reset camera with 'R' * Reset max iterations * Tidying & comments * Revert to original if statement * Make mouse logic more readable * Style conventions * Coding conventions - f postifx on floating points * Missed a few f postfixes --- .../resources/shaders/glsl100/julia_set.fs | 32 +++--- .../resources/shaders/glsl330/julia_set.fs | 34 +++--- examples/shaders/shaders_julia_set.c | 102 +++++++++--------- 3 files changed, 89 insertions(+), 79 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/julia_set.fs b/examples/shaders/resources/shaders/glsl100/julia_set.fs index 44d083459..82d0a75ab 100644 --- a/examples/shaders/resources/shaders/glsl100/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl100/julia_set.fs @@ -6,30 +6,30 @@ precision mediump float; varying vec2 fragTexCoord; varying vec4 fragColor; -uniform vec2 screenDims; // Dimensions of the screen uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c uniform vec2 offset; // Offset of the scale. uniform float zoom; // Zoom of the scale. // NOTE: Maximum number of shader for-loop iterations depend on GPU, // for example, on RasperryPi for this examply only supports up to 60 -const int MAX_ITERATIONS = 48; // Max iterations to do +const int maxIterations = 48; // Max iterations to do. +const float colorCycles = 1.0f; // Number of times the color palette repeats. // Square a complex number vec2 ComplexSquare(vec2 z) { return vec2( - z.x * z.x - z.y * z.y, - z.x * z.y * 2.0 + z.x*z.x - z.y*z.y, + z.x*z.y*2.0f ); } // Convert Hue Saturation Value (HSV) color into RGB vec3 Hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + vec4 K = vec4(1.0f, 2.0f/3.0f, 1.0f/3.0f, 3.0f); + vec3 p = abs(fract(c.xxx + K.xyz)*6.0f - K.www); + return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y); } void main() @@ -45,8 +45,8 @@ void main() If the number is below 2, we keep iterating. But when do we stop iterating if the number is always below 2 (it converges)? - That is what MAX_ITERATIONS is for. - Then we can divide the iterations by the MAX_ITERATIONS value to get a normalized value that we can + That is what maxIterations is for. + Then we can divide the iterations by the maxIterations value to get a normalized value that we can then map to a color. We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. @@ -55,13 +55,15 @@ void main() // The pixel coordinates are scaled so they are on the mandelbrot scale // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom - vec2 z = vec2((fragTexCoord.x + offset.x/screenDims.x)*2.5/zoom, (fragTexCoord.y + offset.y/screenDims.y)*1.5/zoom); + vec2 z = vec2((fragTexCoord.x - 0.5f)*2.5f, (fragTexCoord.y - 0.5f)*1.5f)/zoom; + z.x += offset.x; + z.y += offset.y; int iter = 0; for (int iterations = 0; iterations < 60; iterations++) { z = ComplexSquare(z) + c; // Iterate function - if (dot(z, z) > 4.0) break; + if (dot(z, z) > 4.0f) break; iter = iterations; } @@ -72,12 +74,12 @@ void main() z = ComplexSquare(z) + c; // This last part smooths the color (again see link above). - float smoothVal = float(iter) + 1.0 - (log(log(length(z)))/log(2.0)); + float smoothVal = float(iter) + 1.0f - (log(log(length(z)))/log(2.0f)); // Normalize the value so it is between 0 and 1. - float norm = smoothVal/float(MAX_ITERATIONS); + float norm = smoothVal/float(maxIterations); // If in set, color black. 0.999 allows for some float accuracy error. - if (norm > 0.999) gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); - else gl_FragColor = vec4(Hsv2rgb(vec3(norm, 1.0, 1.0)), 1.0); + if (norm > 0.999f) gl_FragColor = vec4(0.0f, 0.0f, 0.0f, 1.0f); + else gl_FragColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0f, 1.0f)), 1.0f); } diff --git a/examples/shaders/resources/shaders/glsl330/julia_set.fs b/examples/shaders/resources/shaders/glsl330/julia_set.fs index c5ee0da60..7a6f069c8 100644 --- a/examples/shaders/resources/shaders/glsl330/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl330/julia_set.fs @@ -7,28 +7,28 @@ in vec4 fragColor; // Output fragment color out vec4 finalColor; -uniform vec2 screenDims; // Dimensions of the screen uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c uniform vec2 offset; // Offset of the scale. uniform float zoom; // Zoom of the scale. -const int MAX_ITERATIONS = 255; // Max iterations to do. +const int maxIterations = 255; // Max iterations to do. +const float colorCycles = 2.0f; // Number of times the color palette repeats. Can show higher detail for higher iteration numbers. // Square a complex number vec2 ComplexSquare(vec2 z) { return vec2( - z.x * z.x - z.y * z.y, - z.x * z.y * 2.0 + z.x*z.x - z.y*z.y, + z.x*z.y*2.0f ); } // Convert Hue Saturation Value (HSV) color into RGB vec3 Hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + vec4 K = vec4(1.0f, 2.0f/3.0f, 1.0f/3.0f, 3.0f); + vec3 p = abs(fract(c.xxx + K.xyz)*6.0f - K.www); + return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y); } void main() @@ -44,8 +44,8 @@ void main() If the number is below 2, we keep iterating. But when do we stop iterating if the number is always below 2 (it converges)? - That is what MAX_ITERATIONS is for. - Then we can divide the iterations by the MAX_ITERATIONS value to get a normalized value that we can + That is what maxIterations is for. + Then we can divide the iterations by the maxIterations value to get a normalized value that we can then map to a color. We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. @@ -54,14 +54,16 @@ void main() // The pixel coordinates are scaled so they are on the mandelbrot scale // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom - vec2 z = vec2((fragTexCoord.x + offset.x/screenDims.x)*2.5/zoom, (fragTexCoord.y + offset.y/screenDims.y)*1.5/zoom); + vec2 z = vec2((fragTexCoord.x - 0.5f)*2.5f, (fragTexCoord.y - 0.5f)*1.5f)/zoom; + z.x += offset.x; + z.y += offset.y; int iterations = 0; - for (iterations = 0; iterations < MAX_ITERATIONS; iterations++) + for (iterations = 0; iterations < maxIterations; iterations++) { z = ComplexSquare(z) + c; // Iterate function - if (dot(z, z) > 4.0) break; + if (dot(z, z) > 4.0f) break; } // Another few iterations decreases errors in the smoothing calculation. @@ -70,12 +72,12 @@ void main() z = ComplexSquare(z) + c; // This last part smooths the color (again see link above). - float smoothVal = float(iterations) + 1.0 - (log(log(length(z)))/log(2.0)); + float smoothVal = float(iterations) + 1.0f - (log(log(length(z)))/log(2.0f)); // Normalize the value so it is between 0 and 1. - float norm = smoothVal/float(MAX_ITERATIONS); + float norm = smoothVal/float(maxIterations); // If in set, color black. 0.999 allows for some float accuracy error. - if (norm > 0.999) finalColor = vec4(0.0, 0.0, 0.0, 1.0); - else finalColor = vec4(Hsv2rgb(vec3(norm, 1.0, 1.0)), 1.0); + if (norm > 0.999f) finalColor = vec4(0.0f, 0.0f, 0.0f, 1.0f); + else finalColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0f, 1.0f)), 1.0f); } diff --git a/examples/shaders/shaders_julia_set.c b/examples/shaders/shaders_julia_set.c index aebb287a1..608d3b52b 100644 --- a/examples/shaders/shaders_julia_set.c +++ b/examples/shaders/shaders_julia_set.c @@ -9,12 +9,12 @@ * * Example originally created with raylib 2.5, last time updated with raylib 4.0 * -* Example contributed by eggmund (@eggmund) and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by Josh Colclough (@joshcol9232) 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-2023 eggmund (@eggmund) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Josh Colclough (@joshcol9232) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -37,6 +37,13 @@ const float pointsOfInterest[6][2] = { -0.70176f, -0.3842f }, }; +const int screenWidth = 800; +const int screenHeight = 450; +const float zoomSpeed = 1.01f; +const float offsetSpeedMul = 2.0f; + +const float startingZoom = 0.75f; + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -44,10 +51,6 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - //SetConfigFlags(FLAG_WINDOW_HIGHDPI); InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia sets"); // Load julia set shader @@ -61,10 +64,8 @@ int main(void) float c[2] = { pointsOfInterest[0][0], pointsOfInterest[0][1] }; // Offset and zoom to draw the julia set at. (centered on screen and default size) - float offset[2] = { -(float)GetScreenWidth()/2, -(float)GetScreenHeight()/2 }; - float zoom = 1.0f; - - Vector2 offsetSpeed = { 0.0f, 0.0f }; + float offset[2] = { 0.0f, 0.0f }; + float zoom = startingZoom; // 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 @@ -72,17 +73,13 @@ int main(void) int zoomLoc = GetShaderLocation(shader, "zoom"); int offsetLoc = GetShaderLocation(shader, "offset"); - // Tell the shader what the screen dimensions, zoom, offset and c are - float screenDims[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() }; - SetShaderValue(shader, GetShaderLocation(shader, "screenDims"), screenDims, SHADER_UNIFORM_VEC2); - + // Upload the shader uniform values! SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); int incrementSpeed = 0; // Multiplier of speed to change c value bool showControls = true; // Show controls - bool pause = false; // Pause animation SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -110,42 +107,50 @@ int main(void) SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); } - if (IsKeyPressed(KEY_SPACE)) pause = !pause; // Pause animation (c change) - if (IsKeyPressed(KEY_F1)) showControls = !showControls; // Toggle whether or not to show controls - - if (!pause) + // If "R" is pressed, reset zoom and offset. + if (IsKeyPressed(KEY_R)) { - if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++; - else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--; - - // TODO: The idea is to zoom and move around with mouse - // Probably offset movement should be proportional to zoom level - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) - { - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) zoom += zoom*0.003f; - if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) zoom -= zoom*0.003f; - - Vector2 mousePos = GetMousePosition(); - - offsetSpeed.x = mousePos.x -(float)screenWidth/2; - offsetSpeed.y = mousePos.y -(float)screenHeight/2; - - // Slowly move camera to targetOffset - offset[0] += GetFrameTime()*offsetSpeed.x*0.8f; - offset[1] += GetFrameTime()*offsetSpeed.y*0.8f; - } - else offsetSpeed = (Vector2){ 0.0f, 0.0f }; - + zoom = startingZoom; + offset[0] = 0.0f; + offset[1] = 0.0f; SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); - - // Increment c value with time - float amount = GetFrameTime()*incrementSpeed*0.0005f; - c[0] += amount; - c[1] += amount; - - SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); } + + if (IsKeyPressed(KEY_SPACE)) incrementSpeed = 0; // Pause animation (c change) + if (IsKeyPressed(KEY_F1)) showControls = !showControls; // Toggle whether or not to show controls + + if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++; + else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--; + + // 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; + + // Update the shader uniform values! + SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); + SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); + } + + // Increment c value with time + const float dc = GetFrameTime()*(float)incrementSpeed*0.0005f; + c[0] += dc; + c[1] += dc; + SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); //---------------------------------------------------------------------------------- // Draw @@ -178,7 +183,8 @@ int main(void) DrawText("Press KEY_F1 to toggle these controls", 10, 30, 10, RAYWHITE); DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, RAYWHITE); DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, RAYWHITE); - DrawText("Press KEY_SPACE to pause movement animation", 10, 75, 10, RAYWHITE); + DrawText("Press KEY_SPACE to stop movement animation", 10, 75, 10, RAYWHITE); + DrawText("Press KEY_R to recenter the camera", 10, 90, 10, RAYWHITE); } EndDrawing(); //---------------------------------------------------------------------------------- From e3363e9ad0904f87cf038d7f7eeb379d988678c3 Mon Sep 17 00:00:00 2001 From: Khalid Abdullah Date: Mon, 30 Oct 2023 00:34:50 +0600 Subject: [PATCH 0795/1710] Typo fixed in HISTORY.md (#3481) * [Typo fixed] in CHANGELOG * [Typo fixed] in HISTORY.md --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 0a0b1ce2b..10f0dfb77 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -140,7 +140,7 @@ notes on raylib 1.7 On May 2017, around 6 month after raylib 1.6, comes another raylib instalment, raylib 1.7. This time library has been improved a lot in terms of consistency and cleanness. As stated in [this patreon article](https://www.patreon.com/posts/raylib-future-7501034), this new raylib version has focused efforts in becoming more simple and easy-to-use to learn videogames programming. Some highlights of this new version are: - - More than 30 new functions added to the library, functions to control Window, utils to work with filenames and extensions, functions to draw lines with custom thick, mesh loading, functions for 3d ray collisions detailed detection, funtions for VR simulation and much more... Just check [CHANGELOG](CHANGELOG) for a detailed list of additions! + - More than 30 new functions added to the library, functions to control Window, utils to work with filenames and extensions, functions to draw lines with custom thick, mesh loading, functions for 3d ray collisions detailed detection, functions for VR simulation and much more... Just check [CHANGELOG](CHANGELOG) for a detailed list of additions! - Support of [configuration flags](https://github.com/raysan5/raylib/issues/200) on every raylib module. Advance users can customize raylib just choosing desired features, defining some configuration flags on modules compilation. That way users can control library size and available functionality. From 2da8cc383cba42af830f251912263db6eb9298fd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 19:46:34 +0100 Subject: [PATCH 0796/1710] Update Makefile.Web --- examples/Makefile.Web | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 3512f7407..9c2cadc37 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -138,7 +138,7 @@ endif # Define default make program: MAKE #------------------------------------------------------------------------------------------------ -MAKE ?= make +MAKE ?= emmake make ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) From 12f3bc10c2def80abfc76c8158785ac91c0ca1d1 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:20:19 -0300 Subject: [PATCH 0797/1710] [core] Move `rcore.h` content to inside `rcore.c` (#3479) * Move rcore.h content inside rcore.c * Remove extern CoreData CORE --- src/rcore.c | 200 +++++++++++++++++++++++++++++++++++++++++++----- src/rcore.h | 213 ---------------------------------------------------- 2 files changed, 181 insertions(+), 232 deletions(-) delete mode 100644 src/rcore.h diff --git a/src/rcore.c b/src/rcore.c index c3b69ae88..b2eb78675 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -85,12 +85,19 @@ #include "config.h" // Defines module configuration flags #endif -#include "rcore.h" // Defines types and globals +#include "utils.h" // Required for: TRACELOG() macros + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // 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 -#include "raymath.h" // Vector3, Quaternion and Matrix functionality +#define RAYMATH_IMPLEMENTATION +#include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality #if defined(SUPPORT_GESTURES_SYSTEM) #define RGESTURES_IMPLEMENTATION @@ -166,6 +173,161 @@ __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigne #define CHDIR chdir #endif +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef MAX_FILEPATH_CAPACITY + #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath +#endif +#ifndef MAX_FILEPATH_LENGTH + #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) +#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_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 + +// 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)) + +#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 + +//---------------------------------------------------------------------------------- +// 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 + + 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 //---------------------------------------------------------------------------------- @@ -2156,7 +2318,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) /* //int dataSize = 0; //unsigned char *data = LoadFileData(fileName, &dataSize); - + FILE *raeFile = fopen(fileName, "rb"); unsigned char fileId[4] = { 0 }; @@ -2202,7 +2364,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) fgets(buffer, 256, raeFile); } - if (counter != list.count) + 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; @@ -2234,7 +2396,7 @@ void UnloadAutomationEventList(AutomationEventList *list) 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() @@ -2992,7 +3154,7 @@ 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 @@ -3011,7 +3173,7 @@ static void RecordAutomationEvent(void) 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 @@ -3026,7 +3188,7 @@ static void RecordAutomationEvent(void) 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 } //------------------------------------------------------------------------------------- @@ -3047,7 +3209,7 @@ static void RecordAutomationEvent(void) 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 @@ -3062,7 +3224,7 @@ static void RecordAutomationEvent(void) 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 } @@ -3078,7 +3240,7 @@ static void RecordAutomationEvent(void) 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 } @@ -3094,7 +3256,7 @@ static void RecordAutomationEvent(void) 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 } //------------------------------------------------------------------------------------- @@ -3115,7 +3277,7 @@ static void RecordAutomationEvent(void) 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 @@ -3130,7 +3292,7 @@ static void RecordAutomationEvent(void) 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 @@ -3149,7 +3311,7 @@ static void RecordAutomationEvent(void) currentEventList->count++; } */ - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- @@ -3190,7 +3352,7 @@ static void RecordAutomationEvent(void) 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 @@ -3205,7 +3367,7 @@ static void RecordAutomationEvent(void) 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 } @@ -3223,7 +3385,7 @@ static void RecordAutomationEvent(void) 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 } } @@ -3242,7 +3404,7 @@ static void RecordAutomationEvent(void) 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 } //------------------------------------------------------------------------------------- diff --git a/src/rcore.h b/src/rcore.h deleted file mode 100644 index a6955f8cc..000000000 --- a/src/rcore.h +++ /dev/null @@ -1,213 +0,0 @@ -/********************************************************************************************** -* -* rcore - Common types and globals (all platforms) -* -* LIMITATIONS: -* - Limitation 01 -* - Limitation 02 -* -* POSSIBLE IMPROVEMENTS: -* - Improvement 01 -* - Improvement 02 -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2013-2023 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. -* -**********************************************************************************************/ - -#ifndef RCORE_H -#define RCORE_H - -#include "raylib.h" - -#include "utils.h" // Required for: TRACELOG() macros - -#include "rlgl.h" // Required for: graphics layer functionality - -#define RAYMATH_IMPLEMENTATION -#include "raymath.h" // Required for: Vector2/Vector3/Matrix functionality - -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef MAX_FILEPATH_CAPACITY - #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath -#endif -#ifndef MAX_FILEPATH_LENGTH - #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) -#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_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 - -// 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)) - -#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 - -//---------------------------------------------------------------------------------- -// 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 - - 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 -//---------------------------------------------------------------------------------- -extern CoreData CORE; - -#endif From 1fd61a00e4423c68f2b32c46148847b27a6eb94f Mon Sep 17 00:00:00 2001 From: JaanDev <67502867+JaanDev@users.noreply.github.com> Date: Sun, 29 Oct 2023 22:21:00 +0300 Subject: [PATCH 0798/1710] Fix compressed DDS texture loading issues (#3483) --- src/external/rl_gputex.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index fa39fe29c..fb7b5e8de 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -261,11 +261,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ } 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*2; - else data_size = header->pitch_or_linear_size; + // NOTE: This forces only 1 mipmap to be loaded which is not really correct but it works + int data_size = (header->pitch_or_linear_size < file_size - 0x80) ? header->pitch_or_linear_size : file_size - 0x80; + *mips = 1; image_data = RL_MALLOC(data_size*sizeof(unsigned char)); From 21243c82344a132c23fc7fe24094a2f4933ab92a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:42:29 +0100 Subject: [PATCH 0799/1710] Update rcore_desktop_sdl.c --- src/platforms/rcore_desktop_sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 566d75d8c..8793e6c77 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1269,8 +1269,8 @@ int InitPlatform(void) //---------------------------------------------------------------------------- if (SDL_NumJoysticks() >= 1) { - SDL_Joystick *gamepad = SDL_JoystickOpen(0); - //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); + platform.gamepad = SDL_JoystickOpen(0); + //if (platform.gamepadgamepad == NULL) TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError()); } SDL_EventState(SDL_DROPFILE, SDL_ENABLE); From 601e391b068c9bc043b801486e02586012d58f20 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:43:52 +0100 Subject: [PATCH 0800/1710] Remove physac library from raylib building At this moment, physac is an external unmaintained library, better move out of raylib. --- src/Makefile | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/Makefile b/src/Makefile index c9094f096..d5726f5ed 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,13 +78,10 @@ RAYLIB_CONFIG_FLAGS ?= NONE RAYLIB_MODULE_AUDIO ?= TRUE RAYLIB_MODULE_MODELS ?= TRUE RAYLIB_MODULE_RAYGUI ?= FALSE -RAYLIB_MODULE_PHYSAC ?= FALSE # NOTE: Additional libraries have been moved to their own repos: # raygui: https://github.com/raysan5/raygui -# physac: https://github.com/raysan5/physac RAYLIB_MODULE_RAYGUI_PATH ?= $(RAYLIB_SRC_PATH)/../../raygui/src -RAYLIB_MODULE_PHYSAC_PATH ?= $(RAYLIB_SRC_PATH)/../../physac/src # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE @@ -553,9 +550,6 @@ endif ifeq ($(RAYLIB_MODULE_RAYGUI),TRUE) OBJS += raygui.o endif -ifeq ($(RAYLIB_MODULE_PHYSAC),TRUE) - OBJS += physac.o -endif ifeq ($(PLATFORM),PLATFORM_ANDROID) OBJS += android_native_app_glue.o @@ -680,23 +674,10 @@ else @echo "#include \"$(RAYLIB_MODULE_RAYGUI_PATH)/raygui.h\"" >> raygui.c endif -# Compile physac module -# NOTE: physac header should be distributed with raylib.h -physac.o : physac.c - $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -physac.c: -ifeq ($(PLATFORM_SHELL), cmd) - @echo #define PHYSAC_IMPLEMENTATION > physac.c - @echo #include "$(RAYLIB_MODULE_PHYSAC_PATH)/physac.h" >> physac.c -else - @echo "#define PHYSAC_IMPLEMENTATION" > physac.c - @echo "#include \"$(RAYLIB_MODULE_PHYSAC_PATH)/physac.h\"" >> physac.c -endif # Compile android_native_app_glue module android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) - # Install generated and needed files to desired directories. # On GNU/Linux and BSDs, there are some standard directories that contain extra # libraries and header files. These directories (often /usr/local/lib and @@ -780,7 +761,7 @@ clean: clean_shell_$(PLATFORM_SHELL) @echo "removed all generated files!" clean_shell_sh: - rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c physac.c + rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c ifeq ($(PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif @@ -794,4 +775,3 @@ clean_shell_cmd: del lib$(RAYLIB_LIB_NAME)dll.a /s & \ del $(RAYLIB_LIB_NAME).dll /s & \ del raygui.c /s & \ - del physac.c /s From 4625c414319fbcb06fc5f1b633b8cf31c563522c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:44:18 +0100 Subject: [PATCH 0801/1710] ADDED: Support for SDL building on Makefile --- examples/Makefile | 74 ++++++++++++++++++++++++++++++++++++++--- src/Makefile | 84 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 128 insertions(+), 30 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 136a0ab47..d10a35be4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,6 +1,25 @@ #************************************************************************************************** # -# raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 +# raylib makefile for multiple platforms +# +# This file supports building raylib examples for the following platforms: +# +# - PLATFORM_DESKTOP (GLFW backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > macOS/OSX (x64, arm64) +# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# - PLATFORM_DESKTOP_SDL (SDL backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > Others (not tested) +# - PLATFORM_WEB: +# > HTML5 (WebAssembly) +# - PLATFORM_DRM: +# > Raspberry Pi 0-5 (no X11/Wayland) +# > Linux native mode (KMS driver) +# - PLATFORM_ANDROID: +# > Android (ARM, ARM64) # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # @@ -25,7 +44,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DESKTOP_SDL, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables @@ -47,6 +66,11 @@ BUILD_MODE ?= RELEASE # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE +# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally +# WARNING: Library is not included in raylib, it MUST be configured by users +SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/include +SDL_LIBRARY_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/lib/x64 + # Use Wayland display server protocol on Linux desktop (by default it uses X11 windowing system) # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE @@ -58,8 +82,8 @@ BUILD_WEB_HEAP_SIZE ?= 134217728 BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources -# Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) +# Determine PLATFORM_OS when required +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -224,6 +248,9 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) +endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) INCLUDE_PATHS += -I/usr/include/libdrm @@ -254,6 +281,17 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Lsrc -L$(RAYLIB_LIB_PATH) endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + # NOTE: The resource .rc file contains windows executable icon and properties + LDFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data + # -Wl,--subsystem,windows hides the console window + ifeq ($(BUILD_MODE), RELEASE) + LDFLAGS += -Wl,--subsystem,windows + endif + endif + LDFLAGS += -L$(SDL_LIBRARY_PATH) +endif ifeq ($(PLATFORM),PLATFORM_WEB) # -Os # size optimization # -O2 # optimization level 2, if used, also set --memory-init-file 0 @@ -346,6 +384,34 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + # Libraries for Windows desktop compilation + LDLIBS = -lraylib -lSDL2 -lSDL2main -lopengl32 -lgdi32 + endif + ifeq ($(PLATFORM_OS),LINUX) + # Libraries for Debian GNU/Linux desktop compiling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = -lraylib -lSDL2 -lSDL2main -lGL -lm -lpthread -ldl -lrt + + # On X11 requires also below libraries + LDLIBS += -lX11 + # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them + #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor + + # On Wayland windowing system, additional libraries requires + ifeq ($(USE_WAYLAND_DISPLAY),TRUE) + LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon + endif + # Explicit link to libc + ifeq ($(RAYLIB_LIBTYPE),SHARED) + LDLIBS += -lc + endif + + # NOTE: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic + endif +endif ifeq ($(PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) diff --git a/src/Makefile b/src/Makefile index d5726f5ed..2406588b8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,18 +1,28 @@ #****************************************************************************** # -# raylib makefile +# raylib makefile # -# Platforms supported: -# PLATFORM_DESKTOP: Windows (Win32, Win64) -# PLATFORM_DESKTOP: Linux (arm64, i386, x64) -# PLATFORM_DESKTOP: OSX/macOS (arm64, x86_64) -# PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly -# PLATFORM_ANDROID: Android (arm, i686, arm64, x86_64) -# PLATFORM_DRM: Linux native mode, including Raspberry Pi (RPI OS Bullseye) -# PLATFORM_WEB: HTML5 (Chrome, Firefox) +# This file supports building raylib library for the following platforms: # -# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. -# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. +# - PLATFORM_DESKTOP (GLFW backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > macOS/OSX (x64, arm64) +# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# - PLATFORM_DESKTOP_SDL (SDL backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > Others (not tested) +# - PLATFORM_WEB: +# > HTML5 (WebAssembly) +# - PLATFORM_DRM: +# > Raspberry Pi 0-5 (no X11/Wayland) +# > Linux native mode (KMS driver) +# - PLATFORM_ANDROID: +# > Android (ARM, ARM64) +# +# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. +# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # @@ -86,6 +96,11 @@ RAYLIB_MODULE_RAYGUI_PATH ?= $(RAYLIB_SRC_PATH)/../../raygui/src # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE +# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally +# WARNING: Library is not included in raylib, it MUST be configured by users +SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/include +SDL_LIBRARY_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/lib/x64 + # Use Wayland display server protocol on Linux desktop (by default it uses X11 windowing system) # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE @@ -98,8 +113,8 @@ ROOT = $(shell whoami) HOST_PLATFORM_OS ?= WINDOWS PLATFORM_OS ?= WINDOWS -# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +# Determine PLATFORM_OS when required +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -142,16 +157,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) endif endif ifeq ($(PLATFORM),PLATFORM_WEB) - ifeq ($(OS),Windows_NT) - PLATFORM_OS = WINDOWS - ifndef PLATFORM_SHELL - PLATFORM_SHELL = cmd - endif - else - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif + ifeq ($(PLATFORM_OS),LINUX) ifndef PLATFORM_SHELL PLATFORM_SHELL = sh endif @@ -214,7 +220,10 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif - +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + # By default use OpenGL 3.3 on desktop platform with SDL backend + GRAPHICS ?= GRAPHICS_API_OPENGL_33 +endif ifeq ($(PLATFORM),PLATFORM_DRM) # On DRM OpenGL ES 2.0 must be used GRAPHICS = GRAPHICS_API_OPENGL_ES2 @@ -413,14 +422,21 @@ endif # Define include paths for required headers: INCLUDE_PATHS # NOTE: Several external required libraries (stb and others) #------------------------------------------------------------------------------------------------ -INCLUDE_PATHS = -I. -Iexternal/glfw/include -Iexternal/glfw/deps/mingw +INCLUDE_PATHS = -I. # Define additional directories containing required header files ifeq ($(PLATFORM),PLATFORM_DESKTOP) + INCLUDE_PATHS += -Iexternal/glfw/include -Iexternal/glfw/deps/mingw ifeq ($(PLATFORM_OS),BSD) INCLUDE_PATHS += -I/usr/local/include endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + INCLUDE_PATHS += -Iexternal/glfw/include -Iexternal/glfw/deps/mingw +endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) @@ -470,10 +486,14 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) + LDFLAGS += -L$(SDL_LIBRARY_PATH) +endif ifeq ($(PLATFORM),PLATFORM_DRM) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) - INCLUDE_PATHS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib + LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) @@ -518,6 +538,18 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS = -lglfw endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + LDLIBS = -static-libgcc -lopengl32 -lgdi32 + endif + ifeq ($(PLATFORM_OS),LINUX) + LDLIBS = -lGL -lc -lm -lpthread -ldl -lrt + ifeq ($(USE_WAYLAND_DISPLAY),FALSE) + LDLIBS += -lX11 + endif + endif + LDLIBS += -lSDL2 -lSDL2main +endif ifeq ($(PLATFORM),PLATFORM_DRM) LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) From 09075d515af4fcf3caca8c494e7bcf71d64060da Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:44:32 +0100 Subject: [PATCH 0802/1710] Some notes and comments --- src/rcore.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b2eb78675..1a680ee9a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,11 +3,15 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: +* - PLATFORM_DESKTOP (GLFW backend): * > Windows (Win32, Win64) * > Linux (X11/Wayland desktop mode) * > macOS/OSX (x64, arm64) * > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - PLATFORM_DESKTOP_SDL (SDL backend): +* > Windows (Win32, Win64) +* > Linux (X11/Wayland desktop mode) +* > Others (not tested) * - PLATFORM_WEB: * > HTML5 (WebAssembly) * - PLATFORM_DRM: @@ -446,8 +450,8 @@ extern void UnloadFontDefault(void); // [Module: text] Unloads default font f 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) -static void SetupFramebuffer(int width, int height); // Setup main framebuffer +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 @@ -2933,7 +2937,7 @@ void InitTimer(void) // 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) +#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 From 049a6d475ddb10b81ccd896c05c7c6b878d499d8 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:55:02 -0300 Subject: [PATCH 0803/1710] Fix drm hang up on exit and mouse input issues (#3484) --- src/platforms/rcore_drm.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 6f459b1af..82701e310 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -906,6 +906,12 @@ int InitPlatform(void) 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 @@ -913,11 +919,6 @@ int InitPlatform(void) InitKeyboard(); // Keyboard init (stdin) //---------------------------------------------------------------------------- - // Initialize timming system - //---------------------------------------------------------------------------- - InitTimer(); - //---------------------------------------------------------------------------- - // Initialize storage system //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); From fc7dcff4a72e3ce9ea970ad4e03cfabed026fbad Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 21:11:30 +0100 Subject: [PATCH 0804/1710] ADDED: Pseudo-random numbers generator! --- CMakeOptions.txt | 1 + src/config.h | 2 + src/external/rprand.h | 306 ++++++++++++++++++++++++++++++++++++++++++ src/raylib.h | 3 +- src/rcore.c | 41 ++++-- 5 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 src/external/rprand.h diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 799530951..823128b0f 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -37,6 +37,7 @@ cmake_dependent_option(SUPPORT_MODULE_RAUDIO "Include module: raudio" ON CUSTOMI # rcore.c cmake_dependent_option(SUPPORT_CAMERA_SYSTEM "Provide camera module (rcamera.h) with multiple predefined cameras: free, 1st/3rd person, orbital" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_GESTURES_SYSTEM "Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_RPRAND_GENERATOR "Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_MOUSE_GESTURES "Mouse gestures are directly mapped like touches and processed by gestures system" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_DEFAULT_FONT "Default font is loaded on window initialization to be available for the user to render simple text. If enabled, uses external module functions to load default raylib font (module: text)" ON CUSTOMIZE_BUILD ON) diff --git a/src/config.h b/src/config.h index 96fdd065f..c6d553a5e 100644 --- a/src/config.h +++ b/src/config.h @@ -45,6 +45,8 @@ #define SUPPORT_CAMERA_SYSTEM 1 // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag #define SUPPORT_GESTURES_SYSTEM 1 +// Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64 +#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. diff --git a/src/external/rprand.h b/src/external/rprand.h new file mode 100644 index 000000000..a9b3a6eaa --- /dev/null +++ b/src/external/rprand.h @@ -0,0 +1,306 @@ +/********************************************************************************************** +* +* rprand v1.0 - A simple and easy-to-use pseudo-random numbers generator (PRNG) +* +* FEATURES: +* - Pseudo-random values generation, 32 bits: [0..4294967295] +* - Sequence generation avoiding duplicate values +* - Using standard and proven prng algorithm (Xoshiro128**) +* - State initialized with a separate generator (SplitMix64) +* +* LIMITATIONS: +* - No negative numbers, up to the user to manage them +* +* POSSIBLE IMPROVEMENTS: +* - Support 64 bits generation +* +* ADDITIONAL NOTES: +* 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 +* radically different in nature from the one initialized to avoid correlation on similar seeds. +* +* CONFIGURATION: +* #define RPRAND_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. +* +* DEPENDENCIES: none +* +* VERSIONS HISTORY: +* 1.0 (01-Jun-2023) First version +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2023 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 RPRAND_H +#define RPRAND_H + +#define RPRAND_VERSION "1.0" + +// 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 RPRAND __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RPRAND __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #endif +#endif + +// Function specifiers definition +#ifndef RPRANDAPI + #define RPRANDAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Allow custom memory allocators +#ifndef RPRAND_CALLOC + #define RPRAND_CALLOC(ptr,sz) calloc(ptr,sz) +#endif +#ifndef RPRAND_FREE + #define RPRAND_FREE(ptr) free(ptr) +#endif + +// Simple log system to avoid RPNG_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) + #define RPRAND_LOG(...) printf(__VA_ARGS__) +#else + #define RPRAND_LOG(...) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +RPRANDAPI void rprand_set_seed(unsigned long long seed); // Set rprand_state for Xoshiro128**, seed is 64bit +RPRANDAPI unsigned int rprand_get_value(int min, int max); // Get random value within a range, min and max included + +RPRANDAPI unsigned int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates +RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo-random numbers sequence + +#ifdef __cplusplus +} +#endif + +#endif // RPRAND_H + +/*********************************************************************************** +* +* RPRAND IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RPRAND_IMPLEMENTATION) + +#include // Required for: calloc(), free() +#include // Required for data types: uint32_t, uint64_t + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static uint64_t rprand_seed = 0; // SplitMix64 actual seed +static uint32_t rprand_state[4] = { 0 }; // Xoshiro128** state, nitialized by SplitMix64 + +//---------------------------------------------------------------------------------- +// 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 +//---------------------------------------------------------------------------------- +// Set rprand_state for Xoshiro128** +// NOTE: We use a custom generation algorithm using SplitMix64 +void rprand_set_seed(unsigned long long seed) +{ + rprand_seed = (uint64_t)seed; // Set SplitMix64 seed for further use + + // To generate the Xoshiro128** state, we use SplitMix64 generator first + // We generate 4 pseudo-random 64bit numbers that we combine using their LSB|MSB + rprand_state[0] = (uint32_t)(rprand_splitmix64() & 0xffffffff); + rprand_state[1] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32); + rprand_state[2] = (uint32_t)(rprand_splitmix64() & 0xffffffff); + rprand_state[3] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32); +} + +// Get random value within a range, min and max included +unsigned int rprand_get_value(int min, int max) +{ + unsigned int value = rprand_xoshiro()%(max - min) + min; + + return value; +} + +// Load pseudo-random numbers sequence with no duplicates +unsigned int *rprand_load_sequence(unsigned int count, int min, int max) +{ + unsigned int *sequence = NULL; + + if (count > (max - min)) + { + RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n"); + //count = (max - min); + return sequence; + } + + sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); + + uint32_t value = 0; + int value_count = 0; + bool value_is_dup = false; + + for (int i = 0; value_count < count; i++) + { + value = rprand_xoshiro()%(max - min) + min; + value_is_dup = false; + + for (int j = 0; j < value_count; j++) + { + if (sequence[j] == value) + { + value_is_dup = true; + break; + } + } + + if (!value_is_dup) + { + sequence[value_count] = value; + value_count++; + } + } + + return sequence; +} + +// Unload pseudo-random numbers sequence +void rprand_unload_sequence(unsigned int *sequence) +{ + RPRAND_FREE(sequence); + sequence = NULL; +} + +//---------------------------------------------------------------------------------- +// Module internal functions definition +//---------------------------------------------------------------------------------- +static inline uint32_t rprand_rotate_left(const uint32_t x, int k) +{ + return (x << k) | (x >> (32 - k)); +} + +// 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 . +// +// 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) +{ + const uint32_t result = rprand_rotate_left(rprand_state[1]*5, 7)*9; + const uint32_t t = rprand_state[1] << 9; + + rprand_state[2] ^= rprand_state[0]; + 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 . +// +// +// 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() +{ + uint64_t z = (rprand_seed += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30))*0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27))*0x94d049bb133111eb; + return z ^ (z >> 31); +} + +#endif // RPRAND_IMPLEMENTATION \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index d8bf1e07c..115e2be99 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1068,8 +1068,9 @@ RLAPI void PollInputEvents(void); // Register al RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) // Misc. functions -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 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) diff --git a/src/rcore.c b/src/rcore.c index 1a680ee9a..902f84c65 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -131,6 +131,11 @@ #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 @@ -1647,31 +1652,43 @@ void WaitTime(double seconds) // NOTE: Functions with a platform-specific implementation on rcore_.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 (both included) -// 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 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 if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); + + value = (rand()%(abs(max - min) + 1) + min); +#endif + return value; } // Takes a screenshot of current screen (saved a .png) From 15632876f7d27857888d5eeab5caf0e322a5b791 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 30 Oct 2023 08:02:35 -0300 Subject: [PATCH 0805/1710] Fix examples Makefile for SDL (#3486) --- examples/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index d10a35be4..5af5a5590 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -52,6 +52,9 @@ PROJECT_NAME ?= raylib_examples RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. +# Define raylib source code path +RAYLIB_SRC_PATH ?= ../src + # Locations of raylib.h and libraylib.a/libraylib.so # NOTE: Those variables are only used for PLATFORM_OS: LINUX, BSD RAYLIB_INCLUDE_PATH ?= /usr/local/include From 9642fffbbbdda0d31bedae6126e7b3b9073ec407 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:13:31 +0100 Subject: [PATCH 0806/1710] REVIEWED: `GetRender*()` issue on macOS highDPI #3367 --- src/rcore.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 902f84c65..415fecfda 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -748,13 +748,27 @@ int GetScreenHeight(void) // Get current render width which is equal to screen width*dpi scale int GetRenderWidth(void) { - return CORE.Window.render.width; + 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) { - return CORE.Window.render.height; + int height = 0; +#if defined(__APPLE__) + Vector2 scale = GetWindowScaleDPI(); + height = (int)((float)CORE.Window.render.width*scale.y); +#else + height = CORE.Window.render.height; +#endif + return height; } // Enable waiting for events on EndDrawing(), no automatic event polling From abdebc244d41508c246a3a604f9f8b94d5758704 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:14:15 +0100 Subject: [PATCH 0807/1710] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 415fecfda..fdc521207 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -764,7 +764,7 @@ int GetRenderHeight(void) int height = 0; #if defined(__APPLE__) Vector2 scale = GetWindowScaleDPI(); - height = (int)((float)CORE.Window.render.width*scale.y); + height = (int)((float)CORE.Window.render.height*scale.y); #else height = CORE.Window.render.height; #endif From b8fce54c0fc443c76dcac7c7e1adba9fd7e2a4df Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:29:14 +0100 Subject: [PATCH 0808/1710] Minor tweaks --- src/raylib.h | 2 +- src/rcore.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 115e2be99..3f6328ada 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -506,7 +506,7 @@ typedef struct FilePathList { char **paths; // Filepaths entries } FilePathList; -// Automation event (opaque struct) +// Automation event typedef struct AutomationEvent { unsigned int frame; // Event frame unsigned int type; // Event type (AutomationEventType) diff --git a/src/rcore.c b/src/rcore.c index fdc521207..333fa4bfa 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2995,8 +2995,6 @@ void SetupViewport(int width, int 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__) - //float xScale = 1.0f, yScale = 1.0f; - //glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); 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 From 7677e4b92842a317f883c91ce9ad0cd6963d9341 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 20:41:33 +0100 Subject: [PATCH 0809/1710] REVIEWED: `GetModelBoundingBox()` #3485 --- src/rmodels.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rmodels.c b/src/rmodels.c index f9018eaaf..0a997f858 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1170,6 +1170,12 @@ BoundingBox GetModelBoundingBox(Model model) 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; } From ff04d52f12c95b0b25faaffc4e68abed9ba2b474 Mon Sep 17 00:00:00 2001 From: Jett <30197659+JettMonstersGoBoom@users.noreply.github.com> Date: Tue, 31 Oct 2023 03:43:32 -0400 Subject: [PATCH 0810/1710] Added rlEnablePointMode (#3490) for rendering meshes with points. similar to wire mode. (NOTE) they still backface cull, so disable that if you want to show the entire mesh. --- src/rlgl.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 7c93bf929..707555dd5 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -632,7 +632,8 @@ 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 rlDisableWireMode(void); // Disable wire mode +RLAPI void rlEnablePointMode(void); // Enable point mode +RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename 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 @@ -1817,6 +1818,14 @@ void rlEnableWireMode(void) #endif } +void rlEnablePointMode(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_POINT); + glEnable(GL_PROGRAM_POINT_SIZE); +#endif +} // Disable wire mode void rlDisableWireMode(void) { From d8acceca14da2ee5f8e03cd23347b28e438334bd Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 31 Oct 2023 06:10:43 -0300 Subject: [PATCH 0811/1710] Fix example core_3d_camera_free (#3488) --- examples/core/core_3d_camera_free.c | 10 ++++------ examples/core/core_3d_camera_free.png | Bin 25317 -> 25956 bytes 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index 78200a642..ec849758a 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -65,15 +65,13 @@ int main(void) EndMode3D(); - DrawRectangle( 10, 10, 320, 133, Fade(SKYBLUE, 0.5f)); - DrawRectangleLines( 10, 10, 320, 133, BLUE); + DrawRectangle( 10, 10, 320, 93, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines( 10, 10, 320, 93, BLUE); DrawText("Free camera default controls:", 20, 20, 10, BLACK); DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, DARKGRAY); DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, DARKGRAY); - DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, DARKGRAY); - //DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, DARKGRAY); - DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, DARKGRAY); + DrawText("- Z to zoom to (0, 0, 0)", 40, 80, 10, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -85,4 +83,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/core/core_3d_camera_free.png b/examples/core/core_3d_camera_free.png index 7874eedcfd3a820bb10af294058e688e313481a0..71dfc1c80a307772d2c1a30c7039a428e4450308 100644 GIT binary patch literal 25956 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 Date: Tue, 31 Oct 2023 06:16:12 -0300 Subject: [PATCH 0812/1710] Fix relative mouse mode for DRM (#3492) --- src/platforms/rcore_drm.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 82701e310..9212d7e0c 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -121,6 +121,7 @@ typedef struct { 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 // Gamepad data pthread_t gamepadThreadId; // Gamepad reading thread id @@ -400,6 +401,7 @@ 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; } @@ -409,6 +411,7 @@ void DisableCursor(void) // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + platform.cursorRelative = true; CORE.Input.Mouse.cursorHidden = true; } @@ -522,6 +525,13 @@ void PollInputEvents(void) PollKeyboardEvents(); + // Register previous mouse position + if (platform.cursorRelative) + { + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; + } + // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove; @@ -1535,8 +1545,16 @@ static void *EventThread(void *arg) { if (event.code == REL_X) { - CORE.Input.Mouse.currentPosition.x += event.value; - CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + if (platform.cursorRelative) + { + CORE.Input.Mouse.currentPosition.x -= event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + } + else + { + CORE.Input.Mouse.currentPosition.x += event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + } touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; @@ -1544,8 +1562,16 @@ static void *EventThread(void *arg) if (event.code == REL_Y) { - CORE.Input.Mouse.currentPosition.y += event.value; - CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + if (platform.cursorRelative) + { + CORE.Input.Mouse.currentPosition.y -= event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + } + else + { + CORE.Input.Mouse.currentPosition.y += event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + } touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; From 0d186a0557a3c74d34ddce1daa7a66ae0fc1e699 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 11:32:07 +0100 Subject: [PATCH 0813/1710] REVIEWED: `LoadModel()`, removed cube fallback mechanism #3459 --- src/rmodels.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0a997f858..b0120932a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1052,26 +1052,16 @@ Model LoadModel(const char *fileName) // Make sure model transform is set to identity matrix! model.transform = MatrixIdentity(); - if (model.meshCount == 0) + if ((model.meshCount != 0) && (model.meshes != NULL)) { - model.meshCount = 1; - model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); -#if defined(SUPPORT_MESH_GENERATION) - TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data, default to cube mesh", fileName); - model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); -#else - TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data", fileName); -#endif - } - else - { - // Upload vertex data to GPU (static mesh) + // 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 material data, default to white material", fileName); + 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)); From 68420127485990746eab874a4f9d7b08e5acfd13 Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:48:24 +0500 Subject: [PATCH 0814/1710] Fix QOA seeking (#3494) --- src/raudio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index dcc9f706a..476676049 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1797,7 +1797,9 @@ void SeekMusicStream(Music music, float position) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; #endif #if defined(SUPPORT_FILEFORMAT_QOA) - case MUSIC_AUDIO_QOA: qoaplay_seek_frame((qoaplay_desc *)music.ctxData, positionInFrames); break; + //qoaplay_seek_frame seeks to QOA frame, not PCM frame, therefore we need to compute QOA frame number and change positionInFrames + case MUSIC_AUDIO_QOA: { int qoaFrame = positionInFrames/QOA_FRAME_LEN; qoaplay_seek_frame((qoaplay_desc*)music.ctxData, qoaFrame); + positionInFrames = ((qoaplay_desc*)music.ctxData)->sample_position; break; } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; From 0a3567439d90bb1a4955b13441f157ec9c956998 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:49:42 +0100 Subject: [PATCH 0815/1710] Comments tweaks --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_desktop.c | 5 ++--- src/platforms/rcore_desktop_sdl.c | 4 ++-- src/platforms/rcore_drm.c | 5 +++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 83450bb0a..5d6f7d5ba 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -21,8 +21,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* Android NDK - Provides C API to access Android functionality -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - Android NDK: Provides C API to access Android functionality +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 08b329e85..267262c99 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -24,8 +24,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - rglfw: Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng @@ -1766,7 +1766,6 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int // Gesture data is sent to gestures-system for processing ProcessGestureEvent(gestureEvent); - #endif } diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 8793e6c77..7245b2d5c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -24,8 +24,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - SDL 2 (main library) -* - Dependency 02 +* - SDL 2 (main library): Windowing and inputs management +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 9212d7e0c..632686767 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -3,7 +3,7 @@ * rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM -* - Raspberry Pi 0-5 +* - Raspberry Pi 0-5 (native mode) * - Linux native mode (KMS driver) * * LIMITATIONS: @@ -23,7 +23,8 @@ * running processes orblocking the device if not restored properly. Use with care. * * DEPENDENCIES: -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - 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 diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 1cebfa798..9a815ec4e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -21,8 +21,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - Dependency 01 -* - Dependency 02 +* - +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index d797d99d7..959f60332 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -20,8 +20,8 @@ * 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) +* - emscripten: Allow interaction between browser API and C +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng From f88604e6d5e0b17280b05081450a0e9b31d8621d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:54:38 +0100 Subject: [PATCH 0816/1710] Reviewed QOA seek PR --- src/external/qoaplay.c | 4 ++-- src/raudio.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index 7f937f4fa..039e27974 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -36,7 +36,7 @@ // QOA streaming data descriptor typedef struct { qoa_desc info; // QOA descriptor data - + FILE *file; // QOA file to read, if NULL, using memory buffer -> file_data unsigned char *file_data; // QOA file data on memory unsigned int file_data_size; // QOA file data on memory size @@ -107,7 +107,7 @@ qoaplay_desc *qoaplay_open(const char *path) 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); memset(qoa_ctx, 0, sizeof(qoaplay_desc)); - + qoa_ctx->file = file; qoa_ctx->file_data = NULL; qoa_ctx->file_data_size = 0; diff --git a/src/raudio.c b/src/raudio.c index 476676049..e38e51e6c 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1797,9 +1797,14 @@ void SeekMusicStream(Music music, float position) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; #endif #if defined(SUPPORT_FILEFORMAT_QOA) - //qoaplay_seek_frame seeks to QOA frame, not PCM frame, therefore we need to compute QOA frame number and change positionInFrames - case MUSIC_AUDIO_QOA: { int qoaFrame = positionInFrames/QOA_FRAME_LEN; qoaplay_seek_frame((qoaplay_desc*)music.ctxData, qoaFrame); - positionInFrames = ((qoaplay_desc*)music.ctxData)->sample_position; break; } + case MUSIC_AUDIO_QOA: + { + int qoaFrame = positionInFrames/QOA_FRAME_LEN; + qoaplay_seek_frame((qoaplay_desc *)music.ctxData, qoaFrame); // Seeks to QOA frame, not PCM frame + + // We need to compute QOA frame number and update positionInFrames + positionInFrames = ((qoaplay_desc *)music.ctxData)->sample_position; + } break; #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; From de7beef05d56a23c4ab06202013965e3da014ef3 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:54:52 +0100 Subject: [PATCH 0817/1710] Remove trailing spaces --- src/platforms/rcore_desktop.c | 2 +- src/platforms/rcore_template.c | 2 +- src/rcore.c | 8 ++++---- src/rlgl.h | 4 ++-- src/rmodels.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 267262c99..3d38dc898 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -755,7 +755,7 @@ int GetCurrentMonitor(void) // to try to detect the "current monitor" for that window, note that // this is probably an overengineered solution for a very side case // trying to match SDL behaviour - + int closestDist = 0x7FFFFFFF; // Window center position diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 9a815ec4e..5d4721c84 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -21,7 +21,7 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - +* - * - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * diff --git a/src/rcore.c b/src/rcore.c index 333fa4bfa..175d68611 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1681,17 +1681,17 @@ void SetRandomSeed(unsigned int seed) 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 +#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 @@ -1699,7 +1699,7 @@ int GetRandomValue(int min, int 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; diff --git a/src/rlgl.h b/src/rlgl.h index 707555dd5..fcb8feeb6 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -633,7 +633,7 @@ 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 mode ( and point ) maybe rename +RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename 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 @@ -1823,7 +1823,7 @@ void rlEnablePointMode(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_POINT); - glEnable(GL_PROGRAM_POINT_SIZE); + glEnable(GL_PROGRAM_POINT_SIZE); #endif } // Disable wire mode diff --git a/src/rmodels.c b/src/rmodels.c index b0120932a..68c2d75bc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1160,7 +1160,7 @@ BoundingBox GetModelBoundingBox(Model model) 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) From 3645244f9fc1874e72252138ca4ed0ab849a2fd9 Mon Sep 17 00:00:00 2001 From: Justin <72092018+27justin@users.noreply.github.com> Date: Tue, 31 Oct 2023 20:13:12 +0100 Subject: [PATCH 0818/1710] examples/shaders: Add an example for deferred shading (#3496) * add example for deferred rendering/shading * adapt convention --------- Co-authored-by: 27justin --- examples/Makefile | 3 +- examples/Makefile.Web | 3 +- examples/README.md | 9 +- .../shaders/glsl330/deferred_shading.fs | 55 +++ .../shaders/glsl330/deferred_shading.vs | 11 + .../resources/shaders/glsl330/gbuffer.fs | 22 ++ .../resources/shaders/glsl330/gbuffer.vs | 24 ++ examples/shaders/shaders_deferred_render.c | 321 ++++++++++++++++++ examples/shaders/shaders_deferred_render.png | Bin 0 -> 90692 bytes 9 files changed, 442 insertions(+), 6 deletions(-) create mode 100644 examples/shaders/resources/shaders/glsl330/deferred_shading.fs create mode 100644 examples/shaders/resources/shaders/glsl330/deferred_shading.vs create mode 100644 examples/shaders/resources/shaders/glsl330/gbuffer.fs create mode 100644 examples/shaders/resources/shaders/glsl330/gbuffer.vs create mode 100644 examples/shaders/shaders_deferred_render.c create mode 100644 examples/shaders/shaders_deferred_render.png diff --git a/examples/Makefile b/examples/Makefile index 5af5a5590..fe0ee9fbd 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -562,7 +562,8 @@ SHADERS = \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ - shaders/shaders_hybrid_render + shaders/shaders_hybrid_render \ + shaders/shaders_deferred_render AUDIO = \ audio/audio_module_playing \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 9c2cadc37..c57453ab3 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -468,7 +468,8 @@ SHADERS = \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ - shaders/shaders_hybrid_render + shaders/shaders_hybrid_render \ + shaders/shaders_deferred_render AUDIO = \ audio/audio_module_playing \ diff --git a/examples/README.md b/examples/README.md index 82e4b7825..0fed8acd0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -176,6 +176,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete | 114 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) | | 115 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | | 116 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 117 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | ### category: audio @@ -183,10 +184,10 @@ Examples using raylib audio functionality, including sound/music loading and pla | ## | example | image | difficulty
      level | version
      created | last version
      updated | original
      developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 117 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 118 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 119 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | -| 120 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 118 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 119 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 120 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | +| 121 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | ### category: others diff --git a/examples/shaders/resources/shaders/glsl330/deferred_shading.fs b/examples/shaders/resources/shaders/glsl330/deferred_shading.fs new file mode 100644 index 000000000..c9c6a313f --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/deferred_shading.fs @@ -0,0 +1,55 @@ +#version 330 core +out vec4 finalColor; + +in vec2 texCoord; +in vec2 texCoord2; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D gAlbedoSpec; + +struct Light { + int enabled; + int type; // Unused in this demo. + vec3 position; + vec3 target; // Unused in this demo. + vec4 color; +}; + +const int NR_LIGHTS = 4; +uniform Light lights[NR_LIGHTS]; +uniform vec3 viewPosition; + +const float QUADRATIC = 0.032; +const float LINEAR = 0.09; + +void main() { + vec3 fragPosition = texture(gPosition, texCoord).rgb; + vec3 normal = texture(gNormal, texCoord).rgb; + vec3 albedo = texture(gAlbedoSpec, texCoord).rgb; + float specular = texture(gAlbedoSpec, texCoord).a; + + vec3 ambient = albedo * vec3(0.1f); + vec3 viewDirection = normalize(viewPosition - fragPosition); + + for(int i = 0; i < NR_LIGHTS; ++i) + { + if(lights[i].enabled == 0) continue; + vec3 lightDirection = lights[i].position - fragPosition; + vec3 diffuse = max(dot(normal, lightDirection), 0.0) * albedo * lights[i].color.xyz; + + vec3 halfwayDirection = normalize(lightDirection + viewDirection); + float spec = pow(max(dot(normal, halfwayDirection), 0.0), 32.0); + vec3 specular = specular * spec * lights[i].color.xyz; + + // Attenuation + float distance = length(lights[i].position - fragPosition); + float attenuation = 1.0 / (1.0 + LINEAR * distance + QUADRATIC * distance * distance); + diffuse *= attenuation; + specular *= attenuation; + ambient += diffuse + specular; + } + + finalColor = vec4(ambient, 1.0); +} + diff --git a/examples/shaders/resources/shaders/glsl330/deferred_shading.vs b/examples/shaders/resources/shaders/glsl330/deferred_shading.vs new file mode 100644 index 000000000..f2b1bd7c4 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/deferred_shading.vs @@ -0,0 +1,11 @@ +#version 330 core + +layout (location = 0) in vec3 vertexPosition; +layout (location = 1) in vec2 vertexTexCoord; + +out vec2 texCoord; + +void main() { + gl_Position = vec4(vertexPosition, 1.0); + texCoord = vertexTexCoord; +} diff --git a/examples/shaders/resources/shaders/glsl330/gbuffer.fs b/examples/shaders/resources/shaders/glsl330/gbuffer.fs new file mode 100644 index 000000000..c86e20a9e --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/gbuffer.fs @@ -0,0 +1,22 @@ +#version 330 core +layout (location = 0) out vec3 gPosition; +layout (location = 1) out vec3 gNormal; +layout (location = 2) out vec4 gAlbedoSpec; + +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec3 fragNormal; + +uniform sampler2D diffuseTexture; +uniform sampler2D specularTexture; + +void main() { + // store the fragment position vector in the first gbuffer texture + gPosition = fragPosition; + // also store the per-fragment normals into the gbuffer + gNormal = normalize(fragNormal); + // and the diffuse per-fragment color + gAlbedoSpec.rgb = texture(diffuseTexture, fragTexCoord).rgb; + // store specular intensity in gAlbedoSpec's alpha component + gAlbedoSpec.a = texture(specularTexture, fragTexCoord).r; +} diff --git a/examples/shaders/resources/shaders/glsl330/gbuffer.vs b/examples/shaders/resources/shaders/glsl330/gbuffer.vs new file mode 100644 index 000000000..7d264ba64 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/gbuffer.vs @@ -0,0 +1,24 @@ +#version 330 core +layout (location = 0) in vec3 vertexPosition; +layout (location = 1) in vec2 vertexTexCoord; +layout (location = 2) in vec3 vertexNormal; + +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec3 fragNormal; + +uniform mat4 matModel; +uniform mat4 matView; +uniform mat4 matProjection; + +void main() +{ + vec4 worldPos = matModel * vec4(vertexPosition, 1.0); + fragPosition = worldPos.xyz; + fragTexCoord = vertexTexCoord; + + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + fragNormal = normalMatrix * vertexNormal; + + gl_Position = matProjection * matView * worldPos; +} diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c new file mode 100644 index 000000000..58d4b1dd9 --- /dev/null +++ b/examples/shaders/shaders_deferred_render.c @@ -0,0 +1,321 @@ +/******************************************************************************************* +* +* raylib [shaders] example - deferred rendering +* +* NOTE: This example requires raylib OpenGL 3.3 or ES 3 versions. +* +* 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 +#include + +#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 + +typedef struct { + unsigned int framebuffer; + + unsigned int positionTexture; + unsigned int normalTexture; + unsigned int albedoSpecTexture; + + unsigned int depthRenderbuffer; +} GBuffer; + +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(screenWidth, screenHeight); + + 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) != true) + { + 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 int MAX_CUBES = 30; + const float CUBE_SCALE = 0.25; + Vector3 cubePositions[MAX_CUBES]; + float cubeRotations[MAX_CUBES]; + 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); + } + + enum { + POSITION, + NORMAL, + ALBEDO, + DEFERRED_SHADING + } activeTexture = 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)) activeTexture = POSITION; + if(IsKeyPressed(KEY_TWO)) activeTexture = NORMAL; + if(IsKeyPressed(KEY_THREE)) activeTexture = ALBEDO; + if(IsKeyPressed(KEY_FOUR)) activeTexture = 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.framebuffer); + rlClearScreenBuffers(); // Clear color & 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(activeTexture) + { + 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. + glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + 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 POSITION: + DrawTextureRec((Texture2D) { + .id = gBuffer.positionTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + case NORMAL: + DrawTextureRec((Texture2D) { + .id = gBuffer.normalTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + + case ALBEDO: + DrawTextureRec((Texture2D) { + .id = gBuffer.albedoSpecTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + } + + DrawFPS(10, 10); + + DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, DARKGRAY); + DrawText("Use keys [1]-[4] to switch between G-buffer textures", 10, 70, 20, DARKGRAY); + 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/examples/shaders/shaders_deferred_render.png b/examples/shaders/shaders_deferred_render.png new file mode 100644 index 0000000000000000000000000000000000000000..44129f0ff0ecb67f96e04d841e6e23c220f21dc8 GIT binary patch literal 90692 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�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 Date: Tue, 31 Oct 2023 20:15:45 +0100 Subject: [PATCH 0819/1710] REDESIGNED: `LoadOBJ()`, fix #3398 - Now triangulated meshes are properly supported - Simplified code to consider issue situation - Removed mesh split per material, just define separate mesh if multiple materials are required --- src/rmodels.c | 155 +++++++++++++++++++++----------------------------- 1 file changed, 65 insertions(+), 90 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 68c2d75bc..0fc305ddc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3931,9 +3931,10 @@ static Model LoadOBJ(const char *fileName) if (fileText != NULL) { unsigned int dataSize = (unsigned int)strlen(fileText); + char currentDir[1024] = { 0 }; - strcpy(currentDir, GetWorkingDirectory()); - const char *workingDir = GetDirectoryPath(fileName); + 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); @@ -3945,117 +3946,91 @@ static Model LoadOBJ(const char *fileName) if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName); else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes/%i materials", fileName, meshCount, materialCount); - model.meshCount = materialCount; + // WARNING: We are not splitting meshes by materials (previous implementation) + // Depending on the provided OBJ that was not the best option and it just crashed + // so, implementation was simplified to prioritize parsed meshes + model.meshCount = meshCount; - // Init model materials array - if (materialCount > 0) + // Set number of materials available + // NOTE: There could be more materials available than meshes but it will be resolved at + // model.meshMaterial, just assigning the right material to corresponding mesh + model.materialCount = materialCount; + if (model.materialCount == 0) { - model.materialCount = materialCount; - model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", materialCount); - } - else - { - model.meshCount = 1; - TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); + model.materialCount = 1; + TRACELOG(LOG_INFO, "MODEL: No materials provided, setting one default material for all meshes"); } + // Init model meshes and materials model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); - model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Material index assigned to each mesh + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - // Count the faces for each material - int *matFaces = RL_CALLOC(model.meshCount, sizeof(int)); - - // if no materials are present use all faces on one mesh - if (materialCount > 0) + // Process each provided mesh + for (int i = 0; i < model.meshCount; i++) { - for (unsigned int fi = 0; fi < attrib.num_faces; fi++) + // WARNING: We need to calculate the mesh triangles manually using meshes[i].face_offset + // because in case of triangulated quads, meshes[i].length actually report quads, + // despite the triangulation that is efectively considered on attrib.num_faces + unsigned int tris = 0; + if (i == model.meshCount - 1) tris = attrib.num_faces - meshes[i].face_offset; + else tris = meshes[i + 1].face_offset; + + model.meshes[i].vertexCount = tris*3; + model.meshes[i].triangleCount = tris; // Face count (triangulated) + model.meshes[i].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); + model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshMaterial[i] = 0; // By default, assign material 0 to each mesh + + // Process all mesh faces + for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 2, vn += 3) { - //tinyobj_vertex_index_t face = attrib.faces[fi]; - int idx = attrib.material_ids[fi]; - matFaces[idx]++; - } + // Get indices for the face + tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0]; + tinyobj_vertex_index_t idx1 = attrib.faces[f*3 + 1]; + tinyobj_vertex_index_t idx2 = attrib.faces[f*3 + 2]; - } - else - { - matFaces[0] = attrib.num_faces; - } + // Fill vertices buffer (float) using vertex index of the face + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[v*3 + n] = attrib.vertices[idx0.v_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 1)*3 + n] = attrib.vertices[idx1.v_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 2)*3 + n] = attrib.vertices[idx2.v_idx*3 + n]; } - //-------------------------------------- - // Create the material meshes + if (attrib.num_texcoords > 0) + { + // Fill texcoords buffer (float) using vertex index of the face + // NOTE: Y-coordinate must be flipped upside-down + model.meshes[i].texcoords[vt*2 + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; + model.meshes[i].texcoords[vt*2 + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; - // Running counts/indexes for each material mesh as we are - // building them at the same time - int *vCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *vtCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *vnCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *faceCount = RL_CALLOC(model.meshCount, sizeof(int)); + model.meshes[i].texcoords[(vt + 1)*2 + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; + model.meshes[i].texcoords[(vt + 1)*2 + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; - // Allocate space for each of the material meshes - for (int mi = 0; mi < model.meshCount; mi++) - { - model.meshes[mi].vertexCount = matFaces[mi]*3; - model.meshes[mi].triangleCount = matFaces[mi]; - model.meshes[mi].vertices = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshes[mi].texcoords = (float *)RL_CALLOC(model.meshes[mi].vertexCount*2, sizeof(float)); - model.meshes[mi].normals = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshMaterial[mi] = mi; - } + model.meshes[i].texcoords[(vt + 2)*2 + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; + model.meshes[i].texcoords[(vt + 2)*2 + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; + } - // Scan through the combined sub meshes and pick out each material mesh - for (unsigned int af = 0; af < attrib.num_faces; af++) - { - int mm = attrib.material_ids[af]; // mesh material for this face - if (mm == -1) { mm = 0; } // no material object.. - - // Get indices for the face - tinyobj_vertex_index_t idx0 = attrib.faces[3*af + 0]; - tinyobj_vertex_index_t idx1 = attrib.faces[3*af + 1]; - tinyobj_vertex_index_t idx2 = attrib.faces[3*af + 2]; - - // Fill vertices buffer (float) using vertex index of the face - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount[mm] +=3; - - if (attrib.num_texcoords > 0) - { - // Fill texcoords buffer (float) using vertex index of the face - // NOTE: Y-coordinate must be flipped upside-down to account for - // raylib's upside down textures... - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount[mm] += 2; - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount[mm] += 2; - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount[mm] += 2; - } - - if (attrib.num_normals > 0) - { - // Fill normals buffer (float) using vertex index of the face - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount[mm] +=3; + if (attrib.num_normals > 0) + { + // Fill normals buffer (float) using vertex index of the face + for (int n = 0; n < 3; n++) { model.meshes[i].normals[vn*3 + n] = attrib.normals[idx0.vn_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 1)*3 + n] = attrib.normals[idx1.vn_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 2)*3 + n] = attrib.normals[idx2.vn_idx*3 + n]; } + } } } // Init model materials - ProcessMaterialsOBJ(model.materials, materials, materialCount); + if (materialCount > 0) ProcessMaterialsOBJ(model.materials, materials, materialCount); + else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh tinyobj_attrib_free(&attrib); - tinyobj_shapes_free(meshes, meshCount); + tinyobj_shapes_free(meshes, model.meshCount); tinyobj_materials_free(materials, materialCount); UnloadFileText(fileText); - RL_FREE(matFaces); - RL_FREE(vCount); - RL_FREE(vtCount); - RL_FREE(vnCount); - RL_FREE(faceCount); - + // Restore current working directory if (CHDIR(currentDir) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir); From 15142a30f5e371b4a8246d9a8671ae260bb4c78f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:20:11 +0100 Subject: [PATCH 0820/1710] Update rmodels.c --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0fc305ddc..3cae43d12 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3984,7 +3984,7 @@ static Model LoadOBJ(const char *fileName) model.meshMaterial[i] = 0; // By default, assign material 0 to each mesh // Process all mesh faces - for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 2, vn += 3) + for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 3, vn += 3) { // Get indices for the face tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0]; From 1407f6eb4673dbf480ce9c7c33cfe54703bad599 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:46:06 +0100 Subject: [PATCH 0821/1710] ADDED: `rlBlitFramebuffer()`, required for deferred render --- src/rlgl.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index fcb8feeb6..da6573d62 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -617,6 +617,7 @@ RLAPI void rlDisableShader(void); // Disable shader progra RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo) RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers +RLAPI void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask); // Blit active framebuffer to main framebuffer // General render state RLAPI void rlEnableColorBlend(void); // Enable color blending @@ -1713,6 +1714,14 @@ void rlDisableFramebuffer(void) #endif } +// 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) + glBlitFramebuffer(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask, GL_NEAREST); +#endif +} + // Activate multiple draw color buffers // NOTE: One color buffer is always active by default void rlActiveDrawBuffers(int count) From aca854ccbf1fe9fac04f2067ffc90960e621e927 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:46:10 +0100 Subject: [PATCH 0822/1710] Update shaders_deferred_render.c --- examples/shaders/shaders_deferred_render.c | 117 ++++++++++++--------- 1 file changed, 65 insertions(+), 52 deletions(-) diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c index 58d4b1dd9..5bb4db1d2 100644 --- a/examples/shaders/shaders_deferred_render.c +++ b/examples/shaders/shaders_deferred_render.c @@ -2,7 +2,7 @@ * * raylib [shaders] example - deferred rendering * -* NOTE: This example requires raylib OpenGL 3.3 or ES 3 versions. +* 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 * @@ -15,12 +15,9 @@ * ********************************************************************************************/ -#include -#include - #include "raylib.h" -#include "rlgl.h" +#include "rlgl.h" #include "raymath.h" #define RLIGHTS_IMPLEMENTATION @@ -32,7 +29,9 @@ #define GLSL_VERSION 100 #endif -typedef struct { +#include // Required for: NULL + +typedef struct GBuffer { unsigned int framebuffer; unsigned int positionTexture; @@ -42,7 +41,18 @@ typedef struct { unsigned int depthRenderbuffer; } GBuffer; -int main(void) { +typedef enum { + DEFERRED_POSITION, + DEFERRED_NORMAL, + DEFERRED_ALBEDO, + DEFERRED_SHADING +} DeferredMode; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ // Initialization // ------------------------------------------------------------------------------------- const int screenWidth = 800; @@ -73,11 +83,12 @@ int main(void) { GBuffer gBuffer = { 0 }; gBuffer.framebuffer = rlLoadFramebuffer(screenWidth, screenHeight); - if(!gBuffer.framebuffer) + 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, @@ -104,7 +115,7 @@ int main(void) { // Make sure our framebuffer is complete. // NOTE: rlFramebufferComplete() automatically unbinds the framebuffer, so we don't have // to rlDisableFramebuffer() here. - if(rlFramebufferComplete(gBuffer.framebuffer) != true) + if (!rlFramebufferComplete(gBuffer.framebuffer)) { TraceLog(LOG_WARNING, "Framebuffer is not complete"); exit(1); @@ -137,22 +148,18 @@ int main(void) { const float CUBE_SCALE = 0.25; Vector3 cubePositions[MAX_CUBES]; float cubeRotations[MAX_CUBES]; + 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, + cubePositions[i] = (Vector3){ + .x = (float)(rand()%10) - 5, + .y = (float)(rand()%5), + .z = (float)(rand()%10) - 5, }; - cubeRotations[i] = (float)(rand() % 360); + cubeRotations[i] = (float)(rand()%360); } - enum { - POSITION, - NORMAL, - ALBEDO, - DEFERRED_SHADING - } activeTexture = DEFERRED_SHADING; + DeferredMode mode = DEFERRED_SHADING; rlEnableDepthTest(); @@ -177,12 +184,11 @@ int main(void) { if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; } // Check key inputs to switch between G-buffer textures - if(IsKeyPressed(KEY_ONE)) activeTexture = POSITION; - if(IsKeyPressed(KEY_TWO)) activeTexture = NORMAL; - if(IsKeyPressed(KEY_THREE)) activeTexture = ALBEDO; - if(IsKeyPressed(KEY_FOUR)) activeTexture = DEFERRED_SHADING; + 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]); //---------------------------------------------------------------------------------- @@ -190,14 +196,16 @@ int main(void) { // Draw // --------------------------------------------------------------------------------- BeginDrawing(); - // Draw to the geometry buffer by first activating it. + + ClearBackground(RAYWHITE); + + // Draw to the geometry buffer by first activating it rlEnableFramebuffer(gBuffer.framebuffer); - rlClearScreenBuffers(); // Clear color & depth buffer - + rlClearScreenBuffers(); // Clear color and depth buffer + rlDisableColorBlend(); BeginMode3D(camera); - // NOTE: - // We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` + // 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 @@ -205,10 +213,9 @@ int main(void) { 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++) + 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); } @@ -220,9 +227,10 @@ int main(void) { rlDisableFramebuffer(); rlClearScreenBuffers(); // Clear color & depth buffer - switch(activeTexture) + switch (mode) { case DEFERRED_SHADING: + { BeginMode3D(camera); rlDisableColorBlend(); rlEnableShader(deferredShader.id); @@ -243,11 +251,10 @@ int main(void) { rlEnableColorBlend(); EndMode3D(); - // As a last step, we now copy over the depth buffer from our g-buffer to the - // default framebuffer. - glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + // As a last step, we now copy over the depth buffer from our g-buffer to the default framebuffer. + rlEnableFramebuffer(gBuffer.framebuffer); //glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); + rlEnableFramebuffer(0); //glBindFramebuffer(GL_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 @@ -256,44 +263,50 @@ int main(void) { 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); + 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 POSITION: - DrawTextureRec((Texture2D) { + } break; + + case DEFERRED_POSITION: + { + DrawTextureRec((Texture2D){ .id = gBuffer.positionTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; - case NORMAL: - DrawTextureRec((Texture2D) { + } break; + + case DEFERRED_NORMAL: + { + DrawTextureRec((Texture2D){ .id = gBuffer.normalTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; + } break; - case ALBEDO: - DrawTextureRec((Texture2D) { + case DEFERRED_ALBEDO: + { + DrawTextureRec((Texture2D){ .id = gBuffer.albedoSpecTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; + } break; } - DrawFPS(10, 10); + 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); - DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, DARKGRAY); - DrawText("Use keys [1]-[4] to switch between G-buffer textures", 10, 70, 20, DARKGRAY); + DrawFPS(10, 10); + EndDrawing(); // ----------------------------------------------------------------------------- } From df0f7ba61e6a89e493d8b6313001ecf6a7231e5a Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:59:08 +0100 Subject: [PATCH 0823/1710] Update shaders_deferred_render.c --- examples/shaders/shaders_deferred_render.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c index 5bb4db1d2..21d2af346 100644 --- a/examples/shaders/shaders_deferred_render.c +++ b/examples/shaders/shaders_deferred_render.c @@ -31,6 +31,8 @@ #include // Required for: NULL +#define MAX_CUBES 30 + typedef struct GBuffer { unsigned int framebuffer; @@ -144,18 +146,18 @@ int main(void) 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 int MAX_CUBES = 30; const float CUBE_SCALE = 0.25; - Vector3 cubePositions[MAX_CUBES]; - float cubeRotations[MAX_CUBES]; + Vector3 cubePositions[MAX_CUBES] = { 0 }; + float cubeRotations[MAX_CUBES] = { 0 }; - for(int i = 0; i < MAX_CUBES; i++) + 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); } From d4c0d3bebe9e1f7bdaa197b77fc868aca683ad35 Mon Sep 17 00:00:00 2001 From: Techuuu <135726634+Techuuu@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:45:47 +0530 Subject: [PATCH 0824/1710] Update README.md (#3495) * Update README.md Fixed docs and improved. * Update README.md Done fixed it! * Update README.md Fixed! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bff6980b0..fadc3b659 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ features basic example -------------- -This is a basic raylib example, it creates a window and it draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window). +This is a basic raylib example, it creates a window and draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window). ```c #include "raylib.h" From 38205d67da4cf1216bbc4824e4c3af25e51792e0 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 1 Nov 2023 04:16:14 -0700 Subject: [PATCH 0825/1710] Remove unused structures from lighting fragment shaders (#3497) --- examples/shaders/resources/shaders/glsl100/lighting.fs | 6 ------ examples/shaders/resources/shaders/glsl120/lighting.fs | 6 ------ examples/shaders/resources/shaders/glsl330/lighting.fs | 6 ------ 3 files changed, 18 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/lighting.fs b/examples/shaders/resources/shaders/glsl100/lighting.fs index 736716198..73531a8bb 100644 --- a/examples/shaders/resources/shaders/glsl100/lighting.fs +++ b/examples/shaders/resources/shaders/glsl100/lighting.fs @@ -18,12 +18,6 @@ uniform vec4 colDiffuse; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; diff --git a/examples/shaders/resources/shaders/glsl120/lighting.fs b/examples/shaders/resources/shaders/glsl120/lighting.fs index d9cfb4472..8dda5a549 100644 --- a/examples/shaders/resources/shaders/glsl120/lighting.fs +++ b/examples/shaders/resources/shaders/glsl120/lighting.fs @@ -16,12 +16,6 @@ uniform vec4 colDiffuse; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; diff --git a/examples/shaders/resources/shaders/glsl330/lighting.fs b/examples/shaders/resources/shaders/glsl330/lighting.fs index 58845c86b..d1f3140a1 100644 --- a/examples/shaders/resources/shaders/glsl330/lighting.fs +++ b/examples/shaders/resources/shaders/glsl330/lighting.fs @@ -19,12 +19,6 @@ out vec4 finalColor; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; From ba21b8d2744b39dadc6b17b9091a30cc5e00f0b8 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 1 Nov 2023 08:20:33 -0300 Subject: [PATCH 0826/1710] Moves keymapUS[] and fixes GetCharPressed for DRM (#3498) --- src/platforms/rcore_drm.c | 66 ++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 632686767..c6307773f 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -137,6 +137,39 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data +//---------------------------------------------------------------------------------- +// Local Variables Definition +//---------------------------------------------------------------------------------- +// Scancode to keycode mapping for US keyboards +// 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 +static const int keymapUS[] = { + 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 +}; + +// 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 +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 +}; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -1454,27 +1487,6 @@ static void ConfigureEvdevDevice(char *device) // Poll and process evdev keyboard events static void PollKeyboardEvents(void) { - // Scancode to keycode mapping for US keyboards - // 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 - static const int keymapUS[] = { - 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 - }; - int fd = platform.keyboardFd; if (fd == -1) return; @@ -1518,6 +1530,18 @@ static void PollKeyboardEvents(void) } #endif + // Detect char presses (unicode) + if (event.value == 1) + { + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = EvkeyToUnicodeLUT[event.code]; + CORE.Input.Keyboard.charPressedQueueCount++; + } + } + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", (event.value == 0)? "UP" : "DOWN", event.code, keycode); From 64d64cc18114c02ecb068b20b6c4820df6182415 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 15:28:18 +0100 Subject: [PATCH 0827/1710] REVIEWED: Potential code issues reported by CodeQL #3476 --- examples/core/core_automation_events.c | 6 +-- examples/core/core_loading_thread.c | 2 +- examples/core/core_random_values.c | 2 +- examples/models/models_skybox.c | 13 ++--- examples/others/raymath_vector_angle.c | 2 +- examples/shaders/shaders_raymarching.c | 3 +- examples/text/text_unicode.c | 18 +++---- src/rcore.c | 6 +-- src/rlgl.h | 63 ++++++++++++------------ src/rmodels.c | 4 +- src/rtext.c | 67 ++++++++++++++------------ src/rtextures.c | 4 +- 12 files changed, 98 insertions(+), 92 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index 27711b39d..0739a6e7d 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -75,9 +75,9 @@ int main(void) bool eventRecording = false; bool eventPlaying = false; - int frameCounter = 0; - int playFrameCounter = 0; - int currentPlayFrame = 0; + unsigned int frameCounter = 0; + unsigned int playFrameCounter = 0; + unsigned int currentPlayFrame = 0; SetTargetFPS(60); //-------------------------------------------------------------------------------------- diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 0538dcee3..8451ff037 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -41,7 +41,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - loading thread"); - pthread_t threadId; // Loading data thread id + pthread_t threadId = { 0 }; // Loading data thread id enum { STATE_WAITING, STATE_LOADING, STATE_FINISHED } state = STATE_WAITING; int framesCounter = 0; diff --git a/examples/core/core_random_values.c b/examples/core/core_random_values.c index c2225bcaf..bec1de27b 100644 --- a/examples/core/core_random_values.c +++ b/examples/core/core_random_values.c @@ -29,7 +29,7 @@ int main(void) int randValue = GetRandomValue(-8, 5); // Get a random integer number between -8 and 5 (both included) - int framesCounter = 0; // Variable used to count frames + unsigned int framesCounter = 0; // Variable used to count frames SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 7a500e04d..c583128b6 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -68,14 +68,12 @@ int main(void) char skyboxFileName[256] = { 0 }; - Texture2D panorama; - if (useHDR) { TextCopy(skyboxFileName, "resources/dresden_square_2k.hdr"); // Load HDR panorama (sphere) texture - panorama = LoadTexture(skyboxFileName); + 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 @@ -83,7 +81,7 @@ int main(void) // 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 + UnloadTexture(panorama); // Texture not required anymore, cubemap already generated } else { @@ -113,15 +111,18 @@ int main(void) { if (IsFileExtension(droppedFiles.paths[0], ".png;.jpg;.hdr;.bmp;.tga")) { - // Unload current cubemap texture and load new one + // 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); + + UnloadTexture(panorama); // Texture not required anymore, cubemap already generated } else { diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index ad573f54d..d0f81548c 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -42,7 +42,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - float startangle; + float startangle = 0.0f; if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index e9b7755ad..ff403e619 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -82,7 +82,8 @@ int main(void) // Check if screen is resized if (IsWindowResized()) { - float resolution[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() }; + resolution[0] = (float)GetScreenWidth(); + resolution[1] = (float)GetScreenHeight(); SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2); } //---------------------------------------------------------------------------------- diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index b25e3273b..42227f89c 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -195,7 +195,7 @@ int main(void) } Vector2 mouse = GetMousePosition(); - Vector2 pos = { 28.8f, 10.0f }; + Vector2 position = { 28.8f, 10.0f }; hovered = -1; //---------------------------------------------------------------------------------- @@ -210,21 +210,21 @@ int main(void) for (int i = 0; i < SIZEOF(emoji); ++i) { const char *txt = &emojiCodepoints[emoji[i].index]; - Rectangle emojiRect = { pos.x, pos.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize }; + Rectangle emojiRect = { position.x, position.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize }; if (!CheckCollisionPointRec(mouse, emojiRect)) { - DrawTextEx(fontEmoji, txt, pos, (float)fontEmoji.baseSize, 1.0f, selected == i ? emoji[i].color : Fade(LIGHTGRAY, 0.4f)); + DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, selected == i ? emoji[i].color : Fade(LIGHTGRAY, 0.4f)); } else { - DrawTextEx(fontEmoji, txt, pos, (float)fontEmoji.baseSize, 1.0f, emoji[i].color ); + DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, emoji[i].color ); hovered = i; - hoveredPos = pos; + hoveredPos = position; } - if ((i != 0) && (i%EMOJI_PER_WIDTH == 0)) { pos.y += fontEmoji.baseSize + 24.25f; pos.x = 28.8f; } - else pos.x += fontEmoji.baseSize + 28.8f; + 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; } //------------------------------------------------------------------------------ @@ -282,8 +282,8 @@ int main(void) 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); - Vector2 pos = { textRect.x + textRect.width - sz.x, msgRect.y + msgRect.height - sz.y - 2 }; - DrawText(info, (int)pos.x, (int)pos.y, 10, RAYWHITE); + + DrawText(info, (int)(textRect.x + textRect.width - sz.x), (int)(msgRect.y + msgRect.height - sz.y - 2), 10, RAYWHITE); } //------------------------------------------------------------------------------ diff --git a/src/rcore.c b/src/rcore.c index 175d68611..d4e2573b0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -436,8 +436,8 @@ struct AutomationEvent { }; */ -static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer -static bool automationEventRecording = false; // Recording automation events flag +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 //----------------------------------------------------------------------------------- @@ -2465,7 +2465,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) byteCount += sprintf(txtData + byteCount, "c %i\n", list.count); for (int i = 0; i < list.count; i++) { - byteCount += sprintf(txtData + byteCount, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type, + 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]); } diff --git a/src/rlgl.h b/src/rlgl.h index da6573d62..0c695c20c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -516,28 +516,28 @@ typedef enum { // Framebuffer attachment type // NOTE: By default up to 8 color channels defined, but it can be more typedef enum { - RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 - RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1 - RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachment type: color 2 - RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachment type: color 3 - RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachment type: color 4 - RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachment type: color 5 - RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachment type: color 6 - RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachment type: color 7 - RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth - RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil + RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 + RL_ATTACHMENT_COLOR_CHANNEL1 = 1, // Framebuffer attachment type: color 1 + RL_ATTACHMENT_COLOR_CHANNEL2 = 2, // Framebuffer attachment type: color 2 + RL_ATTACHMENT_COLOR_CHANNEL3 = 3, // Framebuffer attachment type: color 3 + RL_ATTACHMENT_COLOR_CHANNEL4 = 4, // Framebuffer attachment type: color 4 + RL_ATTACHMENT_COLOR_CHANNEL5 = 5, // Framebuffer attachment type: color 5 + RL_ATTACHMENT_COLOR_CHANNEL6 = 6, // Framebuffer attachment type: color 6 + RL_ATTACHMENT_COLOR_CHANNEL7 = 7, // Framebuffer attachment type: color 7 + RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth + RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil } rlFramebufferAttachType; // Framebuffer texture attachment type typedef enum { - RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side - RL_ATTACHMENT_CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side - RL_ATTACHMENT_CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side - RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d - RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer + RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_X = 1, // Framebuffer texture attachment type: cubemap, -X side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Y = 2, // Framebuffer texture attachment type: cubemap, +Y side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y = 3, // Framebuffer texture attachment type: cubemap, -Y side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Z = 4, // Framebuffer texture attachment type: cubemap, +Z side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z = 5, // Framebuffer texture attachment type: cubemap, -Z side + RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d + RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer } rlFramebufferAttachTextureType; // Face culling mode @@ -823,6 +823,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); #endif #endif +#if defined(GRAPHICS_API_OPENGL_ES3) + #include +#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] @@ -2243,7 +2246,7 @@ void rlLoadExtensions(void *loader) #if defined(GRAPHICS_API_OPENGL_ES3) // Register supported extensions flags - // OpenGL ES 3.0 extensions supported by default + // OpenGL ES 3.0 extensions supported by default (or it should be) RLGL.ExtSupported.vao = true; RLGL.ExtSupported.instancing = true; RLGL.ExtSupported.texNPOT = true; @@ -2254,20 +2257,20 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.maxDepthBits = 24; RLGL.ExtSupported.texAnisoFilter = true; RLGL.ExtSupported.texMirrorClamp = true; - // TODO: Make sure that the ones above are actually present by default - // TODO: Check for these... - // RLGL.ExtSupported.texCompDXT - // RLGL.ExtSupported.texCompETC1 - // RLGL.ExtSupported.texCompETC2 - // RLGL.ExtSupported.texCompPVRT - // RLGL.ExtSupported.texCompASTC - // RLGL.ExtSupported.computeShader - // RLGL.ExtSupported.ssbo - // RLGL.ExtSupported.maxAnisotropyLevel + // TODO: Check for additional OpenGL ES 3.0 supported extensions: + //RLGL.ExtSupported.texCompDXT = true; + //RLGL.ExtSupported.texCompETC1 = true; + //RLGL.ExtSupported.texCompETC2 = true; + //RLGL.ExtSupported.texCompPVRT = true; + //RLGL.ExtSupported.texCompASTC = true; + //RLGL.ExtSupported.maxAnisotropyLevel = true; + //RLGL.ExtSupported.computeShader = true; + //RLGL.ExtSupported.ssbo = true; + #elif defined(GRAPHICS_API_OPENGL_ES2) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) - // TODO: Support OpenGL ES 3.0 + // TODO: Support GLAD loader for OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); #endif diff --git a/src/rmodels.c b/src/rmodels.c index 3cae43d12..229d373fc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2142,11 +2142,11 @@ Mesh GenMeshPoly(int sides, float radius) Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); float d = 0.0f, dStep = 360.0f/sides; - for (int v = 0; v < vertexCount; v += 3) + 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 }; + vertices[v + 2] = (Vector3){ sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius }; d += dStep; } diff --git a/src/rtext.c b/src/rtext.c index b83eb171b..dd7c83f39 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -2002,7 +2002,6 @@ int GetCodepointPrevious(const char *text, int *codepointSize) // Module specific Functions Definition //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) - // Read a line from memory // REQUIRES: memcpy() // NOTE: Returns the number of bytes read @@ -2032,7 +2031,9 @@ static Font LoadBMFont(const char *fileName) int imHeight = 0; char imFileName[129] = { 0 }; - int base = 0; // Useless data + int base = 0; // Useless data + int readBytes = 0; // Data bytes read + int readVars = 0; // Variables filled by sscanf() char *fileText = LoadFileText(fileName); @@ -2041,32 +2042,30 @@ static Font LoadBMFont(const char *fileName) char *fileTextPtr = fileText; // NOTE: We skip first line, it contains no useful information - int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); - fileTextPtr += (lineBytes + 1); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + fileTextPtr += (readBytes + 1); // Read line data - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "lineHeight"); - sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight); + fileTextPtr += (readBytes + 1); + + if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed - TRACELOGD("FONT: [%s] Loaded font info:", fileName); - TRACELOGD(" > Base size: %i", fontSize); - TRACELOGD(" > Texture scale: %ix%i", imWidth, imHeight); - - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "file"); - sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName); + fileTextPtr += (readBytes + 1); - TRACELOGD(" > Texture filename: %s", imFileName); + if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "count"); - sscanf(searchPoint, "count=%i", &glyphCount); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "count=%i", &glyphCount); + fileTextPtr += (readBytes + 1); - TRACELOGD(" > Chars count: %i", glyphCount); + if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read // Compose correct path using route of .fnt file (fileName) and imFileName char *imPath = NULL; @@ -2124,22 +2123,26 @@ static Font LoadBMFont(const char *fileName) for (int i = 0; i < glyphCount; i++) { - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); - sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); - fileTextPtr += (lineBytes + 1); + fileTextPtr += (readBytes + 1); + + if (readVars == 8) // Make sure all char data has been properly read + { + // Get character rectangle in the font atlas texture + font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; - // Get character rectangle in the font atlas texture - font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; + // Save data properly in sprite font + font.glyphs[i].value = charId; + font.glyphs[i].offsetX = charOffsetX; + font.glyphs[i].offsetY = charOffsetY; + font.glyphs[i].advanceX = charAdvanceX; - // Save data properly in sprite font - font.glyphs[i].value = charId; - font.glyphs[i].offsetX = charOffsetX; - font.glyphs[i].offsetY = charOffsetY; - font.glyphs[i].advanceX = charAdvanceX; - - // Fill character image data from imFont data - font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]); + // Fill character image data from imFont data + font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]); + } + else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName); } UnloadImage(imFont); diff --git a/src/rtextures.c b/src/rtextures.c index bf1b35e40..9465c5b39 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1288,8 +1288,6 @@ void ImageFormat(Image *image, int newFormat) image->data = NULL; image->format = newFormat; - int k = 0; - switch (image->format) { case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: @@ -1306,7 +1304,7 @@ void ImageFormat(Image *image, int newFormat) { image->data = (unsigned char *)RL_MALLOC(image->width*image->height*2*sizeof(unsigned char)); - for (int i = 0; i < image->width*image->height*2; i += 2, k++) + for (int i = 0, k = 0; i < image->width*image->height*2; i += 2, k++) { ((unsigned char *)image->data)[i] = (unsigned char)((pixels[k].x*0.299f + (float)pixels[k].y*0.587f + (float)pixels[k].z*0.114f)*255.0f); ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].w*255.0f); From c563490cf8370f5741e37bdd72d6b14c65240c8c Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 15:28:31 +0100 Subject: [PATCH 0828/1710] Update rcore_web.c --- src/platforms/rcore_web.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 959f60332..4d0bfcc21 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -46,7 +46,7 @@ **********************************************************************************************/ #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (translated to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C From d77c918d51d3be901759d5f3755f7beee1469658 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 16:24:06 +0100 Subject: [PATCH 0829/1710] Update rcore_web.c --- src/platforms/rcore_web.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 4d0bfcc21..c5bd55369 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -45,8 +45,6 @@ * **********************************************************************************************/ -#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (translated to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C From 35d27fb11b916d77075ec22dc07c4c411108d02d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 09:04:05 +0100 Subject: [PATCH 0830/1710] REVIEWED: Pointers exposing not required for ES3 --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 0c695c20c..8f2b5258f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1042,7 +1042,7 @@ typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions static rlglData RLGL = { 0 }; #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3) // NOTE: VAO functionality is exposed through extensions (OES) static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL; static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL; From de1ceae4b0ab5c68d75959490e9a81fa0c9f0593 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 2 Nov 2023 05:13:41 -0300 Subject: [PATCH 0831/1710] [core] Fix gestures for `PLATFORM_DESKTOP_SDL` (#3499) * Fix gestures for SDL * Review the gesture handling for SDL * Review 2 --- src/platforms/rcore_desktop_sdl.c | 48 ++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 7245b2d5c..91442d8e2 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -964,6 +964,15 @@ void PollInputEvents(void) // 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 }; + // Map touch position to mouse position for convenience + // WARNING: If the target desktop device supports touch screen, this behavious should be reviewed! + // 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 + 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 gestureUpdate = false; // Flag to note gestures require to update + // Register previous keys states // NOTE: Android supports up to 260 keys for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) @@ -1077,10 +1086,16 @@ void PollInputEvents(void) case SDL_MOUSEBUTTONDOWN: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; + + touchAction = 1; + gestureUpdate = true; } break; case SDL_MOUSEBUTTONUP: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 0; + + touchAction = 0; + gestureUpdate = true; } break; case SDL_MOUSEWHEEL: { @@ -1100,6 +1115,10 @@ void PollInputEvents(void) 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; + gestureUpdate = true; } break; // Check gamepad events @@ -1122,11 +1141,38 @@ void PollInputEvents(void) } break; default: break; } + +#if defined(SUPPORT_GESTURES_SYSTEM) + if (gestureUpdate) + { + // 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) 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); + } +#endif } //----------------------------------------------------------------------------- } - //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- From ba75a7a23bbfcae1747ab6a87ecb155979ce6bc5 Mon Sep 17 00:00:00 2001 From: Caleb Barger <59521636+cabarger@users.noreply.github.com> Date: Thu, 2 Nov 2023 01:14:34 -0700 Subject: [PATCH 0832/1710] build.zig updates for 0.11.0 release. (#3501) --- build.zig | 2 +- src/build.zig | 80 +++++++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/build.zig b/build.zig index 21638601d..f4d7d1590 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig 0.11.0 pub fn build(b: *std.Build) void { raylib.build(b); } diff --git a/src/build.zig b/src/build.zig index 5e2b5916e..08f4b6bdf 100644 --- a/src/build.zig +++ b/src/build.zig @@ -20,55 +20,55 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rcore.c", srcdir ++ "/utils.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); if (options.raudio) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/raudio.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rmodels) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rmodels.c", }, - .flags = &[_][]const u8{ + &[_][]const u8{ "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 } ++ raylib_flags, - }); + ); } if (options.rshapes) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rshapes.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rtext) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rtext.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rtextures) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rtextures.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } var gen_step = b.addWriteFiles(); @@ -83,10 +83,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -96,10 +96,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -127,10 +127,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -149,10 +149,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags ++ raylib_flags_extra_macos, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags ++ raylib_flags_extra_macos, + ); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); From fe34fc7c6b7e3731ce7f68b379f9847eaaaf2a8f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:35:11 -0300 Subject: [PATCH 0833/1710] Partial fix the gesture system for DRM (#3502) --- src/platforms/rcore_drm.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index c6307773f..ac5a1b6ca 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -170,6 +170,11 @@ static const int EvkeyToUnicodeLUT[] = { // LUT currently incomplete, just mapped the most essential keys }; +#if defined(SUPPORT_GESTURES_SYSTEM) +GestureEvent gestureEvent = { 0 }; // Gesture event to hold data between EventThread() and PollInputEvents() +bool newGesture = false; // Var to trigger ProcessGestureEvent(gestureEvent) on PollInputEvents() +#endif + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -592,6 +597,18 @@ void PollInputEvents(void) // 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; + +#if defined(SUPPORT_GESTURES_SYSTEM) + // Call the ProcessGestureEvent here instead of on EventThread() to workaround the threads not matching + if (newGesture) + { + ProcessGestureEvent(gestureEvent); + newGesture = false; + } +#endif + #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 @@ -603,7 +620,6 @@ void PollInputEvents(void) #endif } - //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- @@ -1715,7 +1731,7 @@ static void *EventThread(void *arg) #if defined(SUPPORT_GESTURES_SYSTEM) if (gestureUpdate) { - GestureEvent gestureEvent = { 0 }; + //GestureEvent gestureEvent = { 0 }; gestureEvent.touchAction = touchAction; gestureEvent.pointCount = CORE.Input.Touch.pointCount; @@ -1726,7 +1742,8 @@ static void *EventThread(void *arg) gestureEvent.position[i] = CORE.Input.Touch.position[i]; } - ProcessGestureEvent(gestureEvent); + //ProcessGestureEvent(gestureEvent); + newGesture = true; } #endif } From 01bbd425196631ea16e3c8fd4666f801fe8e4692 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:11:13 +0100 Subject: [PATCH 0834/1710] Update rprand.h --- src/external/rprand.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/external/rprand.h b/src/external/rprand.h index a9b3a6eaa..b8dc4a279 100644 --- a/src/external/rprand.h +++ b/src/external/rprand.h @@ -196,15 +196,14 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); uint32_t value = 0; - int value_count = 0; bool value_is_dup = false; - for (int i = 0; value_count < count; i++) + for (int i = 0; i < count;) { value = rprand_xoshiro()%(max - min) + min; value_is_dup = false; - for (int j = 0; j < value_count; j++) + for (int j = 0; j < i; j++) { if (sequence[j] == value) { @@ -215,8 +214,8 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) if (!value_is_dup) { - sequence[value_count] = value; - value_count++; + sequence[i] = value; + i++; } } From b40f93b9e35ca8b2aa2e897eadb90187c2f93d4a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:12:22 +0100 Subject: [PATCH 0835/1710] Comments tweaks --- examples/Makefile | 32 +++++++++++++++---------------- src/Makefile | 32 +++++++++++++++---------------- src/platforms/rcore_desktop.c | 2 +- src/platforms/rcore_desktop_sdl.c | 3 +-- src/platforms/rcore_drm.c | 4 ++-- src/rcore.c | 32 +++++++++++++++---------------- 6 files changed, 52 insertions(+), 53 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index fe0ee9fbd..e126ab609 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -4,22 +4,22 @@ # # This file supports building raylib examples for the following platforms: # -# - PLATFORM_DESKTOP (GLFW backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > macOS/OSX (x64, arm64) -# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -# - PLATFORM_DESKTOP_SDL (SDL backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > Others (not tested) -# - PLATFORM_WEB: -# > HTML5 (WebAssembly) -# - PLATFORM_DRM: -# > Raspberry Pi 0-5 (no X11/Wayland) -# > Linux native mode (KMS driver) -# - PLATFORM_ANDROID: -# > Android (ARM, ARM64) +# > PLATFORM_DESKTOP (GLFW backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - macOS/OSX (x64, arm64) +# - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# > PLATFORM_DESKTOP_SDL (SDL backend): +# - 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) # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # diff --git a/src/Makefile b/src/Makefile index 2406588b8..7bcece7c8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,22 +4,22 @@ # # This file supports building raylib library for the following platforms: # -# - PLATFORM_DESKTOP (GLFW backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > macOS/OSX (x64, arm64) -# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -# - PLATFORM_DESKTOP_SDL (SDL backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > Others (not tested) -# - PLATFORM_WEB: -# > HTML5 (WebAssembly) -# - PLATFORM_DRM: -# > Raspberry Pi 0-5 (no X11/Wayland) -# > Linux native mode (KMS driver) -# - PLATFORM_ANDROID: -# > Android (ARM, ARM64) +# > PLATFORM_DESKTOP (GLFW backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - macOS/OSX (x64, arm64) +# - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# > PLATFORM_DESKTOP_SDL (SDL backend): +# - 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) # # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 3d38dc898..426d9fc01 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -6,7 +6,7 @@ * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - OSX/macOS +* - OSX/macOS (x64, arm64) * * LIMITATIONS: * - Limitation 01 diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 91442d8e2..87ceacb86 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -5,8 +5,7 @@ * PLATFORM: DESKTOP: SDL * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) -* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - OSX/macOS (x64, arm64) +* - Others (not tested) * * LIMITATIONS: * - Limitation 01 diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index ac5a1b6ca..7bdc2d00d 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -3,8 +3,8 @@ * rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM -* - Raspberry Pi 0-5 (native mode) -* - Linux native mode (KMS driver) +* - Raspberry Pi 0-5 (DRM/KMS) +* - Linux DRM subsystem (KMS mode) * * LIMITATIONS: * - Most of the window/monitor functions are not implemented (not required) diff --git a/src/rcore.c b/src/rcore.c index d4e2573b0..e4d8eae5c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,22 +3,22 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP (GLFW backend): -* > Windows (Win32, Win64) -* > Linux (X11/Wayland desktop mode) -* > macOS/OSX (x64, arm64) -* > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_DESKTOP_SDL (SDL backend): -* > Windows (Win32, Win64) -* > Linux (X11/Wayland desktop mode) -* > Others (not tested) -* - PLATFORM_WEB: -* > HTML5 (WebAssembly) -* - PLATFORM_DRM: -* > Raspberry Pi 0-5 -* > Linux native mode (KMS driver) -* - PLATFORM_ANDROID: -* > Android (ARM, ARM64) +* > PLATFORM_DESKTOP (GLFW backend): +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - macOS/OSX (x64, arm64) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* > PLATFORM_DESKTOP_SDL (SDL backend): +* - 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) From 807516a991deef0eae23980b9a829721028ab8c8 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:13:37 +0100 Subject: [PATCH 0836/1710] Support OpenGL ES 3.0 building on Web For some reason, the equivalent requested context (WebGL 2.0) is not provided, despite being properly requested. --- src/platforms/rcore_web.c | 5 ++++- src/rlgl.h | 17 ++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index c5bd55369..233616182 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -45,7 +45,9 @@ * **********************************************************************************************/ -#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C #include // Emscripten HTML5 library @@ -759,6 +761,7 @@ int InitPlatform(void) } 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); diff --git a/src/rlgl.h b/src/rlgl.h index 8f2b5258f..76704dfc2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -799,10 +799,14 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #define GLAD_FREE RL_FREE #define GLAD_GL_IMPLEMENTATION - #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers #endif -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES3) + #include // OpenGL ES 3.0 library + #define GL_GLEXT_PROTOTYPES + #include // OpenGL ES 2.0 extensions library +#elif defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) @@ -823,9 +827,6 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); #endif #endif -#if defined(GRAPHICS_API_OPENGL_ES3) - #include -#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] @@ -902,8 +903,10 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_ES2) #define glClearDepth glClearDepthf - #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER - #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER + #if !defined(GRAPHICS_API_OPENGL_ES3) + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER + #endif #endif // Default shader vertex attribute names to set location points From 3c3e311190215451f31b7ea868f7950a8bed909f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:18:05 +0100 Subject: [PATCH 0837/1710] Remove unneeded line on web platform --- src/platforms/rcore_web.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 233616182..1fe4d5841 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -896,7 +896,6 @@ int InitPlatform(void) // 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); // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions From 301d1b85ab62b13d072a0430668602398b382104 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:18:27 +0100 Subject: [PATCH 0838/1710] Update raylib version to **raylib 5.0** --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 3f6328ada..c732d8ef1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -84,7 +84,7 @@ #define RAYLIB_VERSION_MAJOR 5 #define RAYLIB_VERSION_MINOR 0 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "5.0-dev" +#define RAYLIB_VERSION "5.0" // 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 From 5da0074fed50f3a63d3c47aa0f2e1fbdc9051059 Mon Sep 17 00:00:00 2001 From: AndreaBoroni <38136330+AndreaBoroni@users.noreply.github.com> Date: Fri, 3 Nov 2023 19:12:42 +0100 Subject: [PATCH 0839/1710] Fixed Issue 3504 (#3505) --- src/rcore.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index e4d8eae5c..ac5dea842 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2520,7 +2520,16 @@ void PlayAutomationEvent(AutomationEvent event) { // Input event case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break; // param[0]: key - case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; 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 From 2d1b2119202d08245ca4d313b846a6d0a8b2139b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Nov 2023 20:21:43 +0100 Subject: [PATCH 0840/1710] ADDED: `LoadRandomSequence()`/`UnloadRandomSequence()` --- src/external/rprand.h | 28 +++++++++++------------ src/raylib.h | 5 ++++- src/rcore.c | 52 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/external/rprand.h b/src/external/rprand.h index b8dc4a279..acdeac671 100644 --- a/src/external/rprand.h +++ b/src/external/rprand.h @@ -117,10 +117,10 @@ extern "C" { // Prevents name mangling of functions // Module Functions Declaration //---------------------------------------------------------------------------------- RPRANDAPI void rprand_set_seed(unsigned long long seed); // Set rprand_state for Xoshiro128**, seed is 64bit -RPRANDAPI unsigned int rprand_get_value(int min, int max); // Get random value within a range, min and max included +RPRANDAPI int rprand_get_value(int min, int max); // Get random value within a range, min and max included -RPRANDAPI unsigned int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates -RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo-random numbers sequence +RPRANDAPI int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates +RPRANDAPI void rprand_unload_sequence(int *sequence); // Unload pseudo-random numbers sequence #ifdef __cplusplus } @@ -136,7 +136,7 @@ RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo #if defined(RPRAND_IMPLEMENTATION) -#include // Required for: calloc(), free() +#include // Required for: calloc(), free(), abs() #include // Required for data types: uint32_t, uint64_t //---------------------------------------------------------------------------------- @@ -174,33 +174,33 @@ void rprand_set_seed(unsigned long long seed) } // Get random value within a range, min and max included -unsigned int rprand_get_value(int min, int max) +int rprand_get_value(int min, int max) { - unsigned int value = rprand_xoshiro()%(max - min) + min; + int value = rprand_xoshiro()%(abs(max - min) + 1) + min; return value; } -// Load pseudo-random numbers sequence with no duplicates -unsigned int *rprand_load_sequence(unsigned int count, int min, int max) +// Load pseudo-random numbers sequence with no duplicates, min and max included +int *rprand_load_sequence(unsigned int count, int min, int max) { - unsigned int *sequence = NULL; + int *sequence = NULL; - if (count > (max - min)) + if (count > (abs(max - min) + 1)) { RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n"); //count = (max - min); return sequence; } - sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); + sequence = (int *)RPRAND_CALLOC(count, sizeof(int)); - uint32_t value = 0; + int value = 0; bool value_is_dup = false; for (int i = 0; i < count;) { - value = rprand_xoshiro()%(max - min) + min; + value = (rprand_xoshiro()%(abs(max - min) + 1)) + min; value_is_dup = false; for (int j = 0; j < i; j++) @@ -223,7 +223,7 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) } // Unload pseudo-random numbers sequence -void rprand_unload_sequence(unsigned int *sequence) +void rprand_unload_sequence(int *sequence) { RPRAND_FREE(sequence); sequence = NULL; diff --git a/src/raylib.h b/src/raylib.h index c732d8ef1..4a353a70f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1067,10 +1067,13 @@ RLAPI void SwapScreenBuffer(void); // Swap back b RLAPI void PollInputEvents(void); // Register all input events RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) -// Misc. functions +// 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 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 +// 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) diff --git a/src/rcore.c b/src/rcore.c index ac5dea842..b09dffe67 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1677,7 +1677,7 @@ void SetRandomSeed(unsigned int seed) #endif } -// Get a random value between min and max (both included) +// Get a random value between min and max included int GetRandomValue(int min, int max) { int value = 0; @@ -1695,6 +1695,7 @@ int GetRandomValue(int min, int max) // 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); @@ -1705,6 +1706,55 @@ int GetRandomValue(int min, int max) 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) + rprand_load_sequence(count, min, max); +#else + if (count > (abs(max - min) + 1)) return values; + + values = (int *)RL_CALLOC(count, sizeof(int)); + + int value = 0; + bool dupValue = false; + + for (int i = 0; i < 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 (saved a .png) void TakeScreenshot(const char *fileName) { From 32e4be6fb9ec23b325d64b651a945a234351bb8f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Nov 2023 22:23:50 +0100 Subject: [PATCH 0841/1710] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index b09dffe67..9a9ac3f44 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1712,7 +1712,7 @@ int *LoadRandomSequence(unsigned int count, int min, int max) int *values = NULL; #if defined(SUPPORT_RPRAND_GENERATOR) - rprand_load_sequence(count, min, max); + values = rprand_load_sequence(count, min, max); #else if (count > (abs(max - min) + 1)) return values; From 39b539c42f61270631c501e481076902b7f8fd7c Mon Sep 17 00:00:00 2001 From: freakmangd <53349189+freakmangd@users.noreply.github.com> Date: Sat, 4 Nov 2023 16:05:21 -0400 Subject: [PATCH 0842/1710] Allow raylib to be built with zig 0.11.0 and zig 0.12.0dev (master) (#3506) * allow zig 0.11.0 and 0.12.0dev to build raylib * update comment --- src/build.zig | 102 +++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/src/build.zig b/src/build.zig index 08f4b6bdf..12d4a7a58 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); +const builtin = @import("builtin"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig 0.11.0 and zig 0.12.0-dev.1390+94cee4fb2 pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -20,55 +21,37 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rcore.c", - srcdir ++ "/utils.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rcore.c", + srcdir ++ "/utils.c", + }, raylib_flags); if (options.raudio) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/raudio.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/raudio.c", + }, raylib_flags); } if (options.rmodels) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rmodels.c", - }, - &[_][]const u8{ - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 - } ++ raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rmodels.c", + }, &[_][]const u8{ + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 + } ++ raylib_flags); } if (options.rshapes) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rshapes.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rshapes.c", + }, raylib_flags); } if (options.rtext) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rtext.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rtext.c", + }, raylib_flags); } if (options.rtextures) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rtextures.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rtextures.c", + }, raylib_flags); } var gen_step = b.addWriteFiles(); @@ -83,10 +66,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -96,10 +78,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -127,10 +108,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -149,10 +129,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags ++ raylib_flags_extra_macos, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags ++ raylib_flags_extra_macos); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); @@ -235,3 +214,14 @@ const srcdir = struct { return std.fs.path.dirname(@src().file).?; } }.getSrcDir(); + +fn addCSourceFilesVersioned(exe: *std.Build.Step.Compile, files: []const []const u8, flags: []const []const u8) void { + if (comptime builtin.zig_version.minor >= 12) { + exe.addCSourceFiles(.{ + .files = files, + .flags = flags, + }); + } else { + exe.addCSourceFiles(files, flags); + } +} From cbf0324c3469df0303f2b00ed67b194a9dd98f0a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 12:18:54 +0100 Subject: [PATCH 0843/1710] Updating web shells open-graph info --- src/minshell.html | 7 ++++--- src/shell.html | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/minshell.html b/src/minshell.html index 2aa75578f..7a944b7b9 100644 --- a/src/minshell.html +++ b/src/minshell.html @@ -8,10 +8,11 @@ - + + @@ -20,12 +21,12 @@ - + - + diff --git a/src/shell.html b/src/shell.html index b373394bb..d4ef3a03c 100644 --- a/src/shell.html +++ b/src/shell.html @@ -8,10 +8,11 @@ - + + @@ -20,12 +21,12 @@ - + - + From 53451e98a72934c8619afc5e934b95a9e5fd9c1c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 13:02:29 +0100 Subject: [PATCH 0844/1710] Updated open graph for examples --- src/minshell.html | 4 ++-- src/shell.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/minshell.html b/src/minshell.html index 7a944b7b9..e18974ba1 100644 --- a/src/minshell.html +++ b/src/minshell.html @@ -15,8 +15,8 @@ - - + + diff --git a/src/shell.html b/src/shell.html index d4ef3a03c..15c3f39c0 100644 --- a/src/shell.html +++ b/src/shell.html @@ -15,8 +15,8 @@ - - + + From 17a968d2ed327eeaac406b9c123b70f14963cc81 Mon Sep 17 00:00:00 2001 From: glowiak <52356948+glowiak@users.noreply.github.com> Date: Sun, 5 Nov 2023 15:58:48 +0100 Subject: [PATCH 0845/1710] Jaylib is now up-to-date (#3508) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 400bc3846..63c9566d9 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -34,7 +34,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | 4.5 | [Janet](https://janet-lang.org/) | MIT | https://github.com/janet-lang/jaylib | -| jaylib | 4.2 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | +| jaylib | **4.5** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | From 127d69e8870ab4cf3269b367191f3655f472794c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:24:45 +0100 Subject: [PATCH 0846/1710] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index fadc3b659..4604abff6 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) [![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows) [![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux) [![macOS](https://github.com/raysan5/raylib/workflows/macOS/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS) -[![Android](https://github.com/raysan5/raylib/workflows/Android/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AAndroid) [![WebAssembly](https://github.com/raysan5/raylib/workflows/WebAssembly/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly) [![CMakeBuilds](https://github.com/raysan5/raylib/workflows/CMakeBuilds/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds) From 57f77c38589b3ee84b8fbeafef54fd307d41fc0c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:39:57 +0100 Subject: [PATCH 0847/1710] Update qoi.h --- src/external/qoi.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/external/qoi.h b/src/external/qoi.h index 6734ac46e..f2800b0cc 100644 --- a/src/external/qoi.h +++ b/src/external/qoi.h @@ -594,7 +594,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { FILE *f = fopen(filename, "wb"); - int size; + int size, err; void *encoded; if (!f) { @@ -608,10 +608,12 @@ int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { } fwrite(encoded, 1, size, f); + fflush(f); + err = ferror(f); fclose(f); QOI_FREE(encoded); - return size; + return err ? 0 : size; } void *qoi_read(const char *filename, qoi_desc *desc, int channels) { @@ -625,11 +627,10 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { fseek(f, 0, SEEK_END); size = ftell(f); - if (size <= 0) { + if (size <= 0 || fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; } - fseek(f, 0, SEEK_SET); data = QOI_MALLOC(size); if (!data) { @@ -639,8 +640,7 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { bytes_read = fread(data, 1, size, f); fclose(f); - - pixels = qoi_decode(data, bytes_read, desc, channels); + pixels = (bytes_read != size) ? NULL : qoi_decode(data, bytes_read, desc, channels); QOI_FREE(data); return pixels; } From 6ebfc6023b24c297de3286a7fd8b0ffdcffc4c1a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:40:00 +0100 Subject: [PATCH 0848/1710] Update rl_gputex.h --- src/external/rl_gputex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index fb7b5e8de..2cbe59636 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rl_gputex - GPU compressed textures loading and saving +* rl_gputex v1.0 - GPU compressed textures loading and saving * * DESCRIPTION: * From 925978ffde371d5cc1b049d8cf7b861ebd6bb2b5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:41:22 +0100 Subject: [PATCH 0849/1710] Update qoa.h --- src/external/qoa.h | 170 +++++++++++++++++++++++++++++++-------------- 1 file changed, 119 insertions(+), 51 deletions(-) diff --git a/src/external/qoa.h b/src/external/qoa.h index 59d90aded..fc62f4765 100644 --- a/src/external/qoa.h +++ b/src/external/qoa.h @@ -8,71 +8,96 @@ QOA - The "Quite OK Audio" format for fast, lossy audio compression -- Data Format -A QOA file has an 8 byte file header, followed by a number of frames. Each frame -consists of an 8 byte frame header, the current 8 byte en-/decoder state per -channel and 256 slices per channel. Each slice is 8 bytes wide and encodes 20 -samples of audio data. +QOA encodes pulse-code modulated (PCM) audio data with up to 255 channels, +sample rates from 1 up to 16777215 hertz and a bit depth of 16 bits. -Note that the last frame of a file may contain less than 256 slices per channel. -The last slice (per channel) in the last frame may contain less 20 samples, but -the slice will still be 8 bytes wide, with the unused samples zeroed out. +The compression method employed in QOA is lossy; it discards some information +from the uncompressed PCM data. For many types of audio signals this compression +is "transparent", i.e. the difference from the original file is often not +audible. -The samplerate and number of channels is only stated in the frame headers, but -not in the file header. A decoder may peek into the first frame of the file to -find these values. +QOA encodes 20 samples of 16 bit PCM data into slices of 64 bits. A single +sample therefore requires 3.2 bits of storage space, resulting in a 5x +compression (16 / 3.2). -In a valid QOA file all frames have the same number of channels and the same -samplerate. These restrictions may be relaxed for streaming. This remains to -be decided. +A QOA file consists of an 8 byte file header, followed by a number of frames. +Each frame contains an 8 byte frame header, the current 16 byte en-/decoder +state per channel and 256 slices per channel. Each slice is 8 bytes wide and +encodes 20 samples of audio data. -All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file, -including the headers, is 64 bit aligned, so it's possible to read files with -just a read_u64() that does the byte swapping if necessary. - -In pseudocode, the file layout is as follows: +All values, including the slices, are big endian. The file layout is as follows: struct { struct { - char magic[4]; // magic bytes 'qoaf' - uint32_t samples; // number of samples per channel in this file - } file_header; // = 64 bits + char magic[4]; // magic bytes "qoaf" + uint32_t samples; // samples per channel in this file + } file_header; struct { struct { - uint8_t num_channels; // number of channels + uint8_t num_channels; // no. of channels uint24_t samplerate; // samplerate in hz - uint16_t fsamples; // sample count per channel in this frame - uint16_t fsize; // frame size (including the frame header) - } frame_header; // = 64 bits + uint16_t fsamples; // samples per channel in this frame + uint16_t fsize; // frame size (includes this header) + } frame_header; struct { - int16_t history[4]; // = 64 bits - int16_t weights[4]; // = 64 bits + int16_t history[4]; // most recent last + int16_t weights[4]; // most recent last } lms_state[num_channels]; - qoa_slice_t slices[256][num_channels]; // = 64 bits each - } frames[samples * channels / qoa_max_framesize()]; -} qoa_file; + qoa_slice_t slices[256][num_channels]; -Wheras the 64bit qoa_slice_t is defined as follows: + } frames[ceil(samples / (256 * 20))]; +} qoa_file_t; + +Each `qoa_slice_t` contains a quantized scalefactor `sf_quant` and 20 quantized +residuals `qrNN`: .- QOA_SLICE -- 64 bits, 20 samples --------------------------/ /------------. | Byte[0] | Byte[1] | Byte[2] \ \ Byte[7] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 / / 2 1 0 | |------------+--------+--------+--------+---------+---------+-\ \--+---------| -| sf_index | r00 | r01 | r02 | r03 | r04 | / / | r19 | +| sf_quant | qr00 | qr01 | qr02 | qr03 | qr04 | / / | qr19 | `-------------------------------------------------------------\ \------------` -`sf_index` defines the scalefactor to use for this slice as an index into the -qoa_scalefactor_tab[16] +Each frame except the last must contain exactly 256 slices per channel. The last +frame may contain between 1 .. 256 (inclusive) slices per channel. The last +slice (for each channel) in the last frame may contain less than 20 samples; the +slice still must be 8 bytes wide, with the unused samples zeroed out. -`r00`--`r19` are the residuals for the individual samples, divided by the -scalefactor and quantized by the qoa_quant_tab[]. +Channels are interleaved per slice. E.g. for 2 channel stereo: +slice[0] = L, slice[1] = R, slice[2] = L, slice[3] = R ... -In the decoder, a prediction of the next sample is computed by multiplying the -state (the last four output samples) with the predictor. The residual from the -slice is then dequantized using the qoa_dequant_tab[] and added to the -prediction. The result is clamped to int16 to form the final output sample. +A valid QOA file or stream must have at least one frame. Each frame must contain +at least one channel and one sample with a samplerate between 1 .. 16777215 +(inclusive). + +If the total number of samples is not known by the encoder, the samples in the +file header may be set to 0x00000000 to indicate that the encoder is +"streaming". In a streaming context, the samplerate and number of channels may +differ from frame to frame. For static files (those with samples set to a +non-zero value), each frame must have the same number of channels and same +samplerate. + +Note that this implementation of QOA only handles files with a known total +number of samples. + +A decoder should support at least 8 channels. The channel layout for channel +counts 1 .. 8 is: + + 1. Mono + 2. L, R + 3. L, R, C + 4. FL, FR, B/SL, B/SR + 5. FL, FR, C, B/SL, B/SR + 6. FL, FR, C, LFE, B/SL, B/SR + 7. FL, FR, C, LFE, B, SL, SR + 8. FL, FR, C, LFE, BL, BR, SL, SR + +QOA predicts each audio sample based on the previously decoded ones using a +"Sign-Sign Least Mean Squares Filter" (LMS). This prediction plus the +dequantized residual forms the final output sample. */ @@ -158,7 +183,7 @@ the higher end. Note that the residual zero is identical to the lowest positive value. This is mostly fine, since the qoa_div() function always rounds away from zero. */ -static int qoa_quant_tab[17] = { +static const int qoa_quant_tab[17] = { 7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */ 0, /* 0 */ 0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */ @@ -169,13 +194,13 @@ static int qoa_quant_tab[17] = { less accurate at the higher end. In theory, the highest scalefactor that we would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we rely on the LMS filter to predict samples accurately enough that a maximum -residual of one quarter of the 16 bit range is high sufficient. I.e. with the +residual of one quarter of the 16 bit range is sufficient. I.e. with the scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14. The scalefactor values are computed as: scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */ -static int qoa_scalefactor_tab[16] = { +static const int qoa_scalefactor_tab[16] = { 1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048 }; @@ -188,7 +213,7 @@ do this in .16 fixed point with integers, instead of floats. The reciprocal_tab is computed as: reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */ -static int qoa_reciprocal_tab[16] = { +static const int qoa_reciprocal_tab[16] = { 65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32 }; @@ -200,9 +225,13 @@ Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4 instead of 1. The dequant_tab assumes the following dequantized values for each of the quant_tab indices and is computed as: float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7}; -dequant_tab[s][q] <- round(scalefactor_tab[s] * dqt[q]) */ +dequant_tab[s][q] <- round_ties_away_from_zero(scalefactor_tab[s] * dqt[q]) -static int qoa_dequant_tab[16][8] = { +The rounding employed here is "to nearest, ties away from zero", i.e. positive +and negative values are treated symmetrically. +*/ + +static const int qoa_dequant_tab[16][8] = { { 1, -1, 3, -3, 5, -5, 7, -7}, { 5, -5, 18, -18, 32, -32, 49, -49}, { 16, -16, 53, -53, 95, -95, 147, -147}, @@ -270,7 +299,21 @@ static inline int qoa_div(int v, int scalefactor) { } static inline int qoa_clamp(int v, int min, int max) { - return (v < min) ? min : (v > max) ? max : v; + if (v < min) { return min; } + if (v > max) { return max; } + return v; +} + +/* This specialized clamp function for the signed 16 bit range improves decode +performance quite a bit. The extra if() statement works nicely with the CPUs +branch prediction as this branch is rarely taken. */ + +static inline int qoa_clamp_s16(int v) { + if ((unsigned int)(v + 32768) > 65535) { + if (v < -32768) { return -32768; } + if (v > 32767) { return 32767; } + } + return v; } static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) { @@ -312,6 +355,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned unsigned int p = 0; unsigned int slices = (frame_len + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN; unsigned int frame_size = QOA_FRAME_SIZE(channels, slices); + int prev_scalefactor[QOA_MAX_CHANNELS] = {0}; /* Write the frame header */ qoa_write_u64(( @@ -321,8 +365,24 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned (qoa_uint64_t)frame_size ), bytes, &p); - /* Write the current LMS state */ + 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; + } + + /* Write the current LMS state */ qoa_uint64_t weights = 0; qoa_uint64_t history = 0; for (int i = 0; i < QOA_LMS_LEN; i++) { @@ -348,8 +408,13 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned qoa_uint64_t best_error = -1; qoa_uint64_t best_slice; qoa_lms_t best_lms; + int best_scalefactor; - for (int scalefactor = 0; scalefactor < 16; scalefactor++) { + for (int sfi = 0; sfi < 16; sfi++) { + /* There is a strong correlation between the scalefactors of + neighboring slices. As an optimization, start testing + the best scalefactor of the previous slice first. */ + int scalefactor = (sfi + prev_scalefactor[c]) % 16; /* We have to reset the LMS state to the last known good one before trying each scalefactor, as each pass updates the LMS @@ -367,7 +432,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int clamped = qoa_clamp(scaled, -8, 8); int quantized = qoa_quant_tab[clamped + 8]; int dequantized = qoa_dequant_tab[scalefactor][quantized]; - int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); + int reconstructed = qoa_clamp_s16(predicted + dequantized); long long error = (sample - reconstructed); current_error += error * error; @@ -383,9 +448,12 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned best_error = current_error; best_slice = slice; best_lms = lms; + best_scalefactor = scalefactor; } } + prev_scalefactor[c] = best_scalefactor; + qoa->lms[c] = best_lms; #ifdef QOA_RECORD_TOTAL_ERROR qoa->error += best_error; @@ -553,7 +621,7 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa int predicted = qoa_lms_predict(&qoa->lms[c]); int quantized = (slice >> 57) & 0x7; int dequantized = qoa_dequant_tab[scalefactor][quantized]; - int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); + int reconstructed = qoa_clamp_s16(predicted + dequantized); sample_data[si] = reconstructed; slice <<= 3; From 2252f747b734e3f5f9b78a46aa1c8e667c44ab0c Mon Sep 17 00:00:00 2001 From: JupiterRider <60042618+JupiterRider@users.noreply.github.com> Date: Sun, 5 Nov 2023 19:42:56 +0100 Subject: [PATCH 0850/1710] Update Makefile (#3509) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 7bcece7c8..2ee0c5a06 100644 --- a/src/Makefile +++ b/src/Makefile @@ -602,7 +602,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) @echo "raylib library generated (lib$(RAYLIB_LIB_NAME).a)!" else ifeq ($(RAYLIB_LIBTYPE),SHARED) - ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL)) ifeq ($(PLATFORM_OS),WINDOWS) # NOTE: Linking with provided resource file $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/$(RAYLIB_LIB_NAME).dll $(OBJS) $(RAYLIB_RES_FILE) $(LDFLAGS) $(LDLIBS) From 343e92322bac845511651dc6e15a2e16fdeb94c1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:46:25 +0100 Subject: [PATCH 0851/1710] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4604abff6..a88e0f5ce 100644 --- a/README.md +++ b/README.md @@ -38,20 +38,20 @@ features - **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external) - Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!** - Written in plain C code (C99) using PascalCase/camelCase notation - - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3 or ES 2.0**) + - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0**) - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - - Multiple **Fonts** formats supported (TTF, Image fonts, AngelCode fonts) + - Multiple **Fonts** formats supported (TTF, OTF, Image fonts, AngelCode fonts) - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - Flexible Materials system, supporting classic maps and **PBR maps** - - **Animated 3D models** supported (skeletal bones animation) (IQM) - - Shaders support, including model and **postprocessing** shaders. + - **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](https://github.com/raysan5/raylib/blob/master/src/raymath.h) - - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) + - Audio loading and playing with streaming support (WAV, QOA, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - - Huge examples collection with [+120 code examples](https://github.com/raysan5/raylib/tree/master/examples)! - - Bindings to [+60 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! - - **Free and open source**. + - Huge examples collection with [+130 code examples](https://github.com/raysan5/raylib/tree/master/examples)! + - Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! + - **Free and open source** basic example -------------- From 8b3c18de545eb63b7dc2d08621ba83a215a07463 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 20:11:14 +0100 Subject: [PATCH 0852/1710] Updated GLFW for 64bit --- examples/others/external/include/GLFW/glfw3.h | 1064 +++++++++++------ .../external/include/GLFW/glfw3native.h | 226 ++-- examples/others/external/lib/libglfw3.a | Bin 269591 -> 290550 bytes 3 files changed, 860 insertions(+), 430 deletions(-) diff --git a/examples/others/external/include/GLFW/glfw3.h b/examples/others/external/include/GLFW/glfw3.h index 990fe3f7f..31b201ae5 100644 --- a/examples/others/external/include/GLFW/glfw3.h +++ b/examples/others/external/include/GLFW/glfw3.h @@ -3,7 +3,7 @@ * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Löwy + * Copyright (c) 2006-2019 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -52,7 +52,7 @@ extern "C" { * This is the reference documentation for OpenGL and OpenGL ES context related * functions. For more task-oriented information, see the @ref context_guide. */ -/*! @defgroup vulkan Vulkan reference +/*! @defgroup vulkan Vulkan support reference * @brief Functions and types related to Vulkan. * * This is the reference documentation for Vulkan related functions and types. @@ -96,11 +96,30 @@ extern "C" { #define _WIN32 #endif /* _WIN32 */ +/* Include because most Windows GLU headers need wchar_t and + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +/* Include because it is needed by Vulkan and related functions. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif /* Vulkan header */ + +/* The Vulkan header may have indirectly included windows.h (because of + * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. + */ + /* It is customary to use APIENTRY for OpenGL function pointer declarations on * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. */ -#ifndef APIENTRY - #ifdef _WIN32 +#if !defined(APIENTRY) + #if defined(_WIN32) #define APIENTRY __stdcall #else #define APIENTRY @@ -122,17 +141,6 @@ extern "C" { #define GLFW_CALLBACK_DEFINED #endif /* CALLBACK */ -/* Include because most Windows GLU headers need wchar_t and - * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. - * Include it unconditionally to avoid surprising side-effects. - */ -#include - -/* Include because it is needed by Vulkan and related functions. - * Include it unconditionally to avoid surprising side-effects. - */ -#include - /* Include the chosen OpenGL or OpenGL ES headers. */ #if defined(GLFW_INCLUDE_ES1) @@ -182,10 +190,44 @@ extern "C" { #else /*__APPLE__*/ #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif #endif /*__APPLE__*/ -#elif !defined(GLFW_INCLUDE_NONE) +#elif defined(GLFW_INCLUDE_GLU) + + #if defined(__APPLE__) + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #else /*__APPLE__*/ + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) && \ + !defined(__gl_h_) && \ + !defined(__gles1_gl_h_) && \ + !defined(__gles2_gl2_h_) && \ + !defined(__gles2_gl3_h_) && \ + !defined(__gles2_gl31_h_) && \ + !defined(__gles2_gl32_h_) && \ + !defined(__gl_glcorearb_h_) && \ + !defined(__gl2_h_) /*legacy*/ && \ + !defined(__gl3_h_) /*legacy*/ && \ + !defined(__gl31_h_) /*legacy*/ && \ + !defined(__gl32_h_) /*legacy*/ && \ + !defined(__glcorearb_h_) /*legacy*/ && \ + !defined(__GL_H__) /*non-standard*/ && \ + !defined(__gltypes_h_) /*non-standard*/ && \ + !defined(__glee_h_) /*non-standard*/ #if defined(__APPLE__) @@ -193,9 +235,6 @@ extern "C" { #define GL_GLEXT_LEGACY #endif #include - #if defined(GLFW_INCLUDE_GLU) - #include - #endif #else /*__APPLE__*/ @@ -203,18 +242,11 @@ extern "C" { #if defined(GLFW_INCLUDE_GLEXT) #include #endif - #if defined(GLFW_INCLUDE_GLU) - #include - #endif #endif /*__APPLE__*/ #endif /* OpenGL and OpenGL ES headers */ -#if defined(GLFW_INCLUDE_VULKAN) - #include -#endif /* Vulkan header */ - #if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) /* GLFW_DLL must be defined by applications that are linking against the DLL * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW @@ -230,13 +262,12 @@ extern "C" { /* We are building GLFW as a Win32 DLL */ #define GLFWAPI __declspec(dllexport) #elif defined(_WIN32) && defined(GLFW_DLL) - /* We are calling GLFW as a Win32 DLL */ + /* We are calling a GLFW Win32 DLL */ #define GLFWAPI __declspec(dllimport) #elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) - /* We are building GLFW as a shared / dynamic library */ + /* We are building GLFW as a Unix shared library */ #define GLFWAPI __attribute__((visibility("default"))) #else - /* We are building or calling GLFW as a static library */ #define GLFWAPI #endif @@ -247,45 +278,47 @@ extern "C" { /*! @name GLFW version macros * @{ */ -/*! @brief The major version number of the GLFW library. +/*! @brief The major version number of the GLFW header. * - * This is incremented when the API is changed in non-compatible ways. + * The major version number of the GLFW header. This is incremented when the + * API is changed in non-compatible ways. * @ingroup init */ #define GLFW_VERSION_MAJOR 3 -/*! @brief The minor version number of the GLFW library. +/*! @brief The minor version number of the GLFW header. * - * This is incremented when features are added to the API but it remains - * backward-compatible. + * The minor version number of the GLFW header. This is incremented when + * features are added to the API but it remains backward-compatible. * @ingroup init */ #define GLFW_VERSION_MINOR 3 -/*! @brief The revision number of the GLFW library. +/*! @brief The revision number of the GLFW header. * - * This is incremented when a bug fix release is made that does not contain any - * API changes. + * The revision number of the GLFW header. This is incremented when a bug fix + * release is made that does not contain any API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 0 +#define GLFW_VERSION_REVISION 8 /*! @} */ -/*! @name Boolean values - * @{ */ /*! @brief One. * - * One. Seriously. You don't _need_ to use this symbol in your code. It's - * semantic sugar for the number 1. You can also use `1` or `true` or `_True` - * or `GL_TRUE` or whatever you want. + * This is only semantic sugar for the number 1. You can instead use `1` or + * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal + * to one. + * + * @ingroup init */ #define GLFW_TRUE 1 /*! @brief Zero. * - * Zero. Seriously. You don't _need_ to use this symbol in your code. It's - * semantic sugar for the number 0. You can also use `0` or `false` or - * `_False` or `GL_FALSE` or whatever you want. + * This is only semantic sugar for the number 0. You can instead use `0` or + * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is + * equal to zero. + * + * @ingroup init */ #define GLFW_FALSE 0 -/*! @} */ /*! @name Key and button actions * @{ */ @@ -313,6 +346,7 @@ extern "C" { /*! @} */ /*! @defgroup hat_state Joystick hat states + * @brief Joystick hat states. * * See [joystick hat input](@ref joystick_hat) for how these are used. * @@ -915,70 +949,87 @@ extern "C" { #define GLFW_CLIENT_API 0x00022001 /*! @brief Context client API major version hint and attribute. * - * Context client API major version [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib). */ #define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 /*! @brief Context client API minor version hint and attribute. * - * Context client API minor version [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). */ #define GLFW_CONTEXT_VERSION_MINOR 0x00022003 -/*! @brief Context client API revision number hint and attribute. +/*! @brief Context client API revision number attribute. * - * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API revision number + * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). */ #define GLFW_CONTEXT_REVISION 0x00022004 /*! @brief Context robustness hint and attribute. * - * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint) + * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib). */ #define GLFW_CONTEXT_ROBUSTNESS 0x00022005 /*! @brief OpenGL forward-compatibility hint and attribute. * - * OpenGL forward-compatibility [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) + * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). */ #define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 -/*! @brief OpenGL debug context hint and attribute. +/*! @brief Debug mode context hint and attribute. * - * OpenGL debug context [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and + * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). */ #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 /*! @brief OpenGL profile hint and attribute. * - * OpenGL profile [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and + * [attribute](@ref GLFW_OPENGL_PROFILE_attrib). */ #define GLFW_OPENGL_PROFILE 0x00022008 /*! @brief Context flush-on-release hint and attribute. * - * Context flush-on-release [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and + * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib). */ #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 /*! @brief Context error suppression hint and attribute. * - * Context error suppression [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and + * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib). */ #define GLFW_CONTEXT_NO_ERROR 0x0002200A /*! @brief Context creation API hint and attribute. * - * Context creation API [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and + * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib). */ #define GLFW_CONTEXT_CREATION_API 0x0002200B - +/*! @brief Window content area scaling window + * [window hint](@ref GLFW_SCALE_TO_MONITOR). + */ +#define GLFW_SCALE_TO_MONITOR 0x0002200C +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). + */ #define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). + */ #define GLFW_COCOA_FRAME_NAME 0x00023002 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). + */ #define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 - +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ #define GLFW_X11_CLASS_NAME 0x00024001 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ #define GLFW_X11_INSTANCE_NAME 0x00024002 /*! @} */ @@ -998,6 +1049,7 @@ extern "C" { #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 #define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 @@ -1056,9 +1108,20 @@ extern "C" { /*! @addtogroup init * @{ */ +/*! @brief Joystick hat buttons init hint. + * + * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). + */ #define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 - +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). + */ #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). + */ #define GLFW_COCOA_MENUBAR 0x00051002 /*! @} */ @@ -1129,17 +1192,25 @@ typedef struct GLFWwindow GLFWwindow; * * @since Added in version 3.1. * - * @ingroup cursor + * @ingroup input */ typedef struct GLFWcursor GLFWcursor; -/*! @brief The function signature for error callbacks. +/*! @brief The function pointer type for error callbacks. * - * This is the function signature for error callback functions. + * This is the function pointer type for error callbacks. An error callback + * function has the following signature: + * @code + * void callback_name(int error_code, const char* description) + * @endcode * - * @param[in] error An [error code](@ref errors). + * @param[in] error_code An [error code](@ref errors). Future releases may add + * more error codes. * @param[in] description A UTF-8 encoded string describing the error. * + * @pointer_lifetime The error description string is valid until the callback + * function returns. + * * @sa @ref error_handling * @sa @ref glfwSetErrorCallback * @@ -1147,17 +1218,21 @@ typedef struct GLFWcursor GLFWcursor; * * @ingroup init */ -typedef void (* GLFWerrorfun)(int,const char*); +typedef void (* GLFWerrorfun)(int error_code, const char* description); -/*! @brief The function signature for window position callbacks. +/*! @brief The function pointer type for window position callbacks. * - * This is the function signature for window position callback functions. + * This is the function pointer type for window position callbacks. A window + * position callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int xpos, int ypos) + * @endcode * * @param[in] window The window that was moved. * @param[in] xpos The new x-coordinate, in screen coordinates, of the - * upper-left corner of the client area of the window. + * upper-left corner of the content area of the window. * @param[in] ypos The new y-coordinate, in screen coordinates, of the - * upper-left corner of the client area of the window. + * upper-left corner of the content area of the window. * * @sa @ref window_pos * @sa @ref glfwSetWindowPosCallback @@ -1166,11 +1241,15 @@ typedef void (* GLFWerrorfun)(int,const char*); * * @ingroup window */ -typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); +typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos); -/*! @brief The function signature for window resize callbacks. +/*! @brief The function pointer type for window size callbacks. * - * This is the function signature for window size callback functions. + * This is the function pointer type for window size callbacks. A window size + * callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int width, int height) + * @endcode * * @param[in] window The window that was resized. * @param[in] width The new width, in screen coordinates, of the window. @@ -1184,11 +1263,15 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); +typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int width, int height); -/*! @brief The function signature for window close callbacks. +/*! @brief The function pointer type for window close callbacks. * - * This is the function signature for window close callback functions. + * This is the function pointer type for window close callbacks. A window + * close callback function has the following signature: + * @code + * void function_name(GLFWwindow* window) + * @endcode * * @param[in] window The window that the user attempted to close. * @@ -1200,11 +1283,15 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowclosefun)(GLFWwindow*); +typedef void (* GLFWwindowclosefun)(GLFWwindow* window); -/*! @brief The function signature for window content refresh callbacks. +/*! @brief The function pointer type for window content refresh callbacks. * - * This is the function signature for window refresh callback functions. + * This is the function pointer type for window content refresh callbacks. + * A window content refresh callback function has the following signature: + * @code + * void function_name(GLFWwindow* window); + * @endcode * * @param[in] window The window whose content needs to be refreshed. * @@ -1216,11 +1303,15 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*); * * @ingroup window */ -typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); +typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window); -/*! @brief The function signature for window focus/defocus callbacks. +/*! @brief The function pointer type for window focus callbacks. * - * This is the function signature for window focus callback functions. + * This is the function pointer type for window focus callbacks. A window + * focus callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode * * @param[in] window The window that gained or lost input focus. * @param[in] focused `GLFW_TRUE` if the window was given input focus, or @@ -1233,12 +1324,15 @@ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); * * @ingroup window */ -typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); +typedef void (* GLFWwindowfocusfun)(GLFWwindow* window, int focused); -/*! @brief The function signature for window iconify/restore callbacks. +/*! @brief The function pointer type for window iconify callbacks. * - * This is the function signature for window iconify/restore callback - * functions. + * This is the function pointer type for window iconify callbacks. A window + * iconify callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode * * @param[in] window The window that was iconified or restored. * @param[in] iconified `GLFW_TRUE` if the window was iconified, or @@ -1251,15 +1345,18 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); +typedef void (* GLFWwindowiconifyfun)(GLFWwindow* window, int iconified); -/*! @brief The function signature for window maximize/restore callbacks. +/*! @brief The function pointer type for window maximize callbacks. * - * This is the function signature for window maximize/restore callback - * functions. + * This is the function pointer type for window maximize callbacks. A window + * maximize callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode * * @param[in] window The window that was maximized or restored. - * @param[in] iconified `GLFW_TRUE` if the window was maximized, or + * @param[in] maximized `GLFW_TRUE` if the window was maximized, or * `GLFW_FALSE` if it was restored. * * @sa @ref window_maximize @@ -1269,12 +1366,15 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); +typedef void (* GLFWwindowmaximizefun)(GLFWwindow* window, int maximized); -/*! @brief The function signature for framebuffer resize callbacks. +/*! @brief The function pointer type for framebuffer size callbacks. * - * This is the function signature for framebuffer resize callback - * functions. + * This is the function pointer type for framebuffer size callbacks. + * A framebuffer size callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode * * @param[in] window The window whose framebuffer was resized. * @param[in] width The new width, in pixels, of the framebuffer. @@ -1287,12 +1387,15 @@ typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); +typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height); -/*! @brief The function signature for window content scale callbacks. +/*! @brief The function pointer type for window content scale callbacks. * - * This is the function signature for window content scale callback - * functions. + * This is the function pointer type for window content scale callbacks. + * A window content scale callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode * * @param[in] window The window whose content scale changed. * @param[in] xscale The new x-axis content scale of the window. @@ -1305,16 +1408,21 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale); -/*! @brief The function signature for mouse button callbacks. +/*! @brief The function pointer type for mouse button callbacks. * - * This is the function signature for mouse button callback functions. + * This is the function pointer type for mouse button callback functions. + * A mouse button callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] button The [mouse button](@ref buttons) that was pressed or * released. - * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases + * may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * @@ -1326,17 +1434,21 @@ typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); * * @ingroup input */ -typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); +typedef void (* GLFWmousebuttonfun)(GLFWwindow* window, int button, int action, int mods); -/*! @brief The function signature for cursor position callbacks. +/*! @brief The function pointer type for cursor position callbacks. * - * This is the function signature for cursor position callback functions. + * This is the function pointer type for cursor position callbacks. A cursor + * position callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode * * @param[in] window The window that received the event. * @param[in] xpos The new cursor x-coordinate, relative to the left edge of - * the client area. + * the content area. * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the - * client area. + * content area. * * @sa @ref cursor_pos * @sa @ref glfwSetCursorPosCallback @@ -1345,14 +1457,18 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * * @ingroup input */ -typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); +typedef void (* GLFWcursorposfun)(GLFWwindow* window, double xpos, double ypos); -/*! @brief The function signature for cursor enter/leave callbacks. +/*! @brief The function pointer type for cursor enter/leave callbacks. * - * This is the function signature for cursor enter/leave callback functions. + * This is the function pointer type for cursor enter/leave callbacks. + * A cursor enter/leave callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode * * @param[in] window The window that received the event. - * @param[in] entered `GLFW_TRUE` if the cursor entered the window's client + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content * area, or `GLFW_FALSE` if it left it. * * @sa @ref cursor_enter @@ -1362,11 +1478,15 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); * * @ingroup input */ -typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); +typedef void (* GLFWcursorenterfun)(GLFWwindow* window, int entered); -/*! @brief The function signature for scroll callbacks. +/*! @brief The function pointer type for scroll callbacks. * - * This is the function signature for scroll callback functions. + * This is the function pointer type for scroll callbacks. A scroll callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode * * @param[in] window The window that received the event. * @param[in] xoffset The scroll offset along the x-axis. @@ -1379,16 +1499,21 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); * * @ingroup input */ -typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); +typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffset); -/*! @brief The function signature for keyboard key callbacks. +/*! @brief The function pointer type for keyboard key callbacks. * - * This is the function signature for keyboard key callback functions. + * This is the function pointer type for keyboard key callbacks. A keyboard + * key callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] key The [keyboard key](@ref keys) that was pressed or released. * @param[in] scancode The system-specific scancode of the key. - * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. + * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future + * releases may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * @@ -1400,11 +1525,15 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * * @ingroup input */ -typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); +typedef void (* GLFWkeyfun)(GLFWwindow* window, int key, int scancode, int action, int mods); -/*! @brief The function signature for Unicode character callbacks. +/*! @brief The function pointer type for Unicode character callbacks. * - * This is the function signature for Unicode character callback functions. + * This is the function pointer type for Unicode character callbacks. + * A Unicode character callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode * * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. @@ -1417,14 +1546,18 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); * * @ingroup input */ -typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); +typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); -/*! @brief The function signature for Unicode character with modifiers +/*! @brief The function pointer type for Unicode character with modifiers * callbacks. * - * This is the function signature for Unicode character with modifiers callback - * functions. It is called for each input character, regardless of what - * modifier keys are held down. + * This is the function pointer type for Unicode character with modifiers + * callbacks. It is called for each input character, regardless of what + * modifier keys are held down. A Unicode character with modifiers callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. @@ -1440,16 +1573,23 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * * @ingroup input */ -typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); +typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); -/*! @brief The function signature for file drop callbacks. +/*! @brief The function pointer type for path drop callbacks. * - * This is the function signature for file drop callbacks. + * This is the function pointer type for path drop callbacks. A path drop + * callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode * * @param[in] window The window that received the event. - * @param[in] count The number of dropped files. + * @param[in] path_count The number of dropped paths. * @param[in] paths The UTF-8 encoded file and/or directory path names. * + * @pointer_lifetime The path array and its strings are valid until the + * callback function returns. + * * @sa @ref path_drop * @sa @ref glfwSetDropCallback * @@ -1457,15 +1597,19 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); * * @ingroup input */ -typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); +typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]); -/*! @brief The function signature for monitor configuration callbacks. +/*! @brief The function pointer type for monitor configuration callbacks. * - * This is the function signature for monitor configuration callback functions. + * This is the function pointer type for monitor configuration callbacks. + * A monitor callback function has the following signature: + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode * * @param[in] monitor The monitor that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining - * values reserved for future use. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. * * @sa @ref monitor_event * @sa @ref glfwSetMonitorCallback @@ -1474,16 +1618,19 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); * * @ingroup monitor */ -typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); +typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); -/*! @brief The function signature for joystick configuration callbacks. +/*! @brief The function pointer type for joystick configuration callbacks. * - * This is the function signature for joystick configuration callback - * functions. + * This is the function pointer type for joystick configuration callbacks. + * A joystick configuration callback function has the following signature: + * @code + * void function_name(int jid, int event) + * @endcode * * @param[in] jid The joystick that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining - * values reserved for future use. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. * * @sa @ref joystick_event * @sa @ref glfwSetJoystickCallback @@ -1492,7 +1639,7 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * * @ingroup input */ -typedef void (* GLFWjoystickfun)(int,int); +typedef void (* GLFWjoystickfun)(int jid, int event); /*! @brief Video mode type. * @@ -1567,6 +1714,8 @@ typedef struct GLFWgammaramp * * @since Added in version 2.1. * @glfw3 Removed format and bytes-per-pixel members. + * + * @ingroup window */ typedef struct GLFWimage { @@ -1589,6 +1738,8 @@ typedef struct GLFWimage * @sa @ref glfwGetGamepadState * * @since Added in version 3.3. + * + * @ingroup input */ typedef struct GLFWgamepadstate { @@ -1630,6 +1781,10 @@ typedef struct GLFWgamepadstate * bundle, if present. This can be disabled with the @ref * GLFW_COCOA_CHDIR_RESOURCES init hint. * + * @remark @x11 This function will set the `LC_CTYPE` category of the + * application locale according to the current environment if that category is + * still "C". This is because the "C" locale breaks Unicode text input. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init @@ -1653,6 +1808,8 @@ GLFWAPI int glfwInit(void); * call this function, as it is called by @ref glfwInit before it returns * failure. * + * This function has no effect if GLFW is not initialized. + * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * * @remark This function may be called before @ref glfwInit. @@ -1814,10 +1971,17 @@ GLFWAPI int glfwGetError(const char** description); * Once set, the error callback remains set even after the library has been * terminated. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set. * + * @callback_signature + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * For more information about the callback parameters, see the + * [callback pointer type](@ref GLFWerrorfun). + * * @errors None. * * @remark This function may be called before @ref glfwInit. @@ -1831,7 +1995,7 @@ GLFWAPI int glfwGetError(const char** description); * * @ingroup init */ -GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); /*! @brief Returns the currently connected monitors. * @@ -1911,6 +2075,37 @@ GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); */ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); +/*! @brief Retrieves the work area of the monitor. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the work area of the specified monitor along with the work area + * size in screen coordinates. The work area is defined as the area of the + * monitor not occluded by the operating system task bar where present. If no + * task bar exists then the work area is the monitor resolution in screen + * coordinates. + * + * Any or all of the position and size arguments may be `NULL`. If an error + * occurs, all non-`NULL` position and size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * @param[out] width Where to store the monitor width, or `NULL`. + * @param[out] height Where to store the monitor height, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_workarea + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); + /*! @brief Returns the physical size of the monitor. * * This function returns the size, in millimetres, of the display area of the @@ -1932,8 +2127,8 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @win32 calculates the returned physical size from the - * current resolution and system DPI instead of querying the monitor EDID data. + * @remark @win32 On Windows 8 and earlier the physical size is calculated from + * the current resolution and system DPI instead of querying the monitor EDID data. * * @thread_safety This function must only be called from the main thread. * @@ -1949,9 +2144,11 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* * * This function retrieves the content scale for the specified monitor. The * content scale is the ratio between the current DPI and the platform's - * default DPI. If you scale all pixel dimensions by this scale then your - * content should appear at an appropriate size. This is especially important - * for text and any UI elements. + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. * * The content scale may depend on both the monitor resolution and pixel * density and on user settings. It may be very different from the raw DPI @@ -2057,11 +2254,18 @@ GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); * currently set callback. This is called when a monitor is connected to or * disconnected from the system. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmonitorfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -2072,14 +2276,15 @@ GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); * * @ingroup monitor */ -GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback); /*! @brief Returns the available video modes for the specified monitor. * * This function returns an array of all video modes supported by the specified * monitor. The returned array is sorted in ascending order, first by color - * bit depth (the sum of all channel depths) and then by resolution area (the - * product of width and height). + * bit depth (the sum of all channel depths), then by resolution area (the + * product of width and height), then resolution width and finally by refresh + * rate. * * @param[in] monitor The monitor to query. * @param[out] count Where to store the number of video modes in the returned @@ -2137,9 +2342,9 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); /*! @brief Generates a gamma ramp and sets it for the specified monitor. * - * This function generates a 256-element gamma ramp from the specified exponent - * and then calls @ref glfwSetGammaRamp with it. The value must be a finite - * number greater than zero. + * This function generates an appropriately sized gamma ramp from the specified + * exponent and then calls @ref glfwSetGammaRamp with it. The value must be + * a finite number greater than zero. * * The software controlled gamma ramp is applied _in addition_ to the hardware * gamma correction, which today is usually an approximation of sRGB gamma. @@ -2155,7 +2360,7 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. @@ -2179,7 +2384,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while * returning `NULL`. * @@ -2218,12 +2423,12 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark Gamma ramp sizes other than 256 are not supported by all platforms - * or graphics hardware. + * @remark The size of the specified gamma ramp should match the size of the + * current ramp for that monitor. * * @remark @win32 The gamma ramp size must be 256. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified gamma ramp is copied before this function @@ -2442,7 +2647,7 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * * @remark @macos When activating frame autosaving with * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified - * window size and position may be overriden by previously saved values. + * window size and position may be overridden by previously saved values. * * @remark @x11 Some window managers will not respect the placement of * initially hidden windows. @@ -2455,15 +2660,18 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * @remark @x11 The class part of the `WM_CLASS` window property will by * default be set to the window title passed to this function. The instance * part will use the contents of the `RESOURCE_NAME` environment variable, if - * present and not empty, or fall back to the window title. Set the @ref - * GLFW_X11_CLASS_NAME and @ref GLFW_X11_INSTANCE_NAME window hints to override - * this. + * present and not empty, or fall back to the window title. Set the + * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and + * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to + * override this. * - * @remark @wayland The window frame is currently very simple, only allowing - * window resize or move. A compositor can still emit close, maximize or - * fullscreen events, using for example a keybind mechanism. Additionally, - * the wp_viewporter protocol is required for this feature, otherwise the - * window will not be decorated. + * @remark @wayland Compositors should implement the xdg-decoration protocol + * for GLFW to decorate the window properly. If this protocol isn't + * supported, or if the compositor prefers client-side decorations, a very + * simple fallback frame will be drawn using the wp_viewporter protocol. A + * compositor can still emit close, maximize or fullscreen events, using for + * instance a keybind mechanism. If neither of these protocols is supported, + * the window won't be decorated. * * @remark @wayland A full screen window will not attempt to change the mode, * no matter what the requested size or refresh rate. @@ -2599,8 +2807,8 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * @param[in] images The images to create the icon from. This is ignored if * count is zero. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -2625,19 +2833,19 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); */ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); -/*! @brief Retrieves the position of the client area of the specified window. +/*! @brief Retrieves the position of the content area of the specified window. * * This function retrieves the position, in screen coordinates, of the - * upper-left corner of the client area of the specified window. + * upper-left corner of the content area of the specified window. * * Any or all of the position arguments may be `NULL`. If an error occurs, all * non-`NULL` position arguments will be set to zero. * * @param[in] window The window to query. * @param[out] xpos Where to store the x-coordinate of the upper-left corner of - * the client area, or `NULL`. + * the content area, or `NULL`. * @param[out] ypos Where to store the y-coordinate of the upper-left corner of - * the client area, or `NULL`. + * the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2657,10 +2865,10 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i */ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); -/*! @brief Sets the position of the client area of the specified window. +/*! @brief Sets the position of the content area of the specified window. * * This function sets the position, in screen coordinates, of the upper-left - * corner of the client area of the specified windowed mode window. If the + * corner of the content area of the specified windowed mode window. If the * window is a full screen window, this function does nothing. * * __Do not use this function__ to move an already visible window unless you @@ -2670,8 +2878,8 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); * cannot and should not override these limits. * * @param[in] window The window to query. - * @param[in] xpos The x-coordinate of the upper-left corner of the client area. - * @param[in] ypos The y-coordinate of the upper-left corner of the client area. + * @param[in] xpos The x-coordinate of the upper-left corner of the content area. + * @param[in] ypos The y-coordinate of the upper-left corner of the content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2692,9 +2900,9 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); */ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); -/*! @brief Retrieves the size of the client area of the specified window. +/*! @brief Retrieves the size of the content area of the specified window. * - * This function retrieves the size, in screen coordinates, of the client area + * This function retrieves the size, in screen coordinates, of the content area * of the specified window. If you wish to retrieve the size of the * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. * @@ -2703,9 +2911,9 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); * * @param[in] window The window whose size to retrieve. * @param[out] width Where to store the width, in screen coordinates, of the - * client area, or `NULL`. + * content area, or `NULL`. * @param[out] height Where to store the height, in screen coordinates, of the - * client area, or `NULL`. + * content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2724,7 +2932,7 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); /*! @brief Sets the size limits of the specified window. * - * This function sets the size limits of the client area of the specified + * This function sets the size limits of the content area of the specified * window. If the window is full screen, the size limits only take effect * once it is made windowed. If the window is not resizable, this function * does nothing. @@ -2736,14 +2944,14 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * dimensions and all must be greater than or equal to zero. * * @param[in] window The window to set limits for. - * @param[in] minwidth The minimum width, in screen coordinates, of the client + * @param[in] minwidth The minimum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] minheight The minimum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. - * @param[in] maxwidth The maximum width, in screen coordinates, of the client + * content area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] maxheight The maximum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. + * content area, or `GLFW_DONT_CARE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. @@ -2767,7 +2975,7 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe /*! @brief Sets the aspect ratio of the specified window. * - * This function sets the required aspect ratio of the client area of the + * This function sets the required aspect ratio of the content area of the * specified window. If the window is full screen, the aspect ratio only takes * effect once it is made windowed. If the window is not resizable, this * function does nothing. @@ -2808,9 +3016,9 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe */ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); -/*! @brief Sets the size of the client area of the specified window. +/*! @brief Sets the size of the content area of the specified window. * - * This function sets the size, in screen coordinates, of the client area of + * This function sets the size, in screen coordinates, of the content area of * the specified window. * * For full screen windows, this function updates the resolution of its desired @@ -2826,9 +3034,9 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * * @param[in] window The window to resize. * @param[in] width The desired width, in screen coordinates, of the window - * client area. + * content area. * @param[in] height The desired height, in screen coordinates, of the window - * client area. + * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2919,9 +3127,11 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * * This function retrieves the content scale for the specified window. The * content scale is the ratio between the current DPI and the platform's - * default DPI. If you scale all pixel dimensions by this scale then your - * content should appear at an appropriate size. This is especially important - * for text and any UI elements. + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. * * On systems where each monitors can have its own content scale, the window * content scale will depend on which monitor the system considers the window @@ -3008,18 +3218,15 @@ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); * previously restored. If the window is already iconified, this function does * nothing. * - * If the specified window is a full screen window, the original monitor - * resolution is restored until the window is restored. + * If the specified window is a full screen window, GLFW restores the original + * video mode of the monitor. The window's desired video mode is set again + * when the window is restored. * * @param[in] window The window to iconify. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland There is no concept of iconification in wl_shell, this - * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated - * protocol. - * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify @@ -3039,8 +3246,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); * (minimized) or maximized. If the window is already restored, this function * does nothing. * - * If the specified window is a full screen window, the resolution chosen for - * the window is restored on the selected monitor. + * If the specified window is an iconified full screen window, its desired + * video mode is set again for its monitor when the window is restored. * * @param[in] window The window to restore. * @@ -3101,6 +3308,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Because Wayland wants every frame of the desktop to be + * complete, this function does not immediately make the window visible. + * Instead it will become visible the next time the window framebuffer is + * updated after this call. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide @@ -3232,7 +3444,7 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * The window position is ignored when setting a monitor. * * When the monitor is `NULL`, the position, width and height are used to - * place the window client area. The refresh rate is ignored when no monitor + * place the window content area. The refresh rate is ignored when no monitor * is specified. * * If you only wish to update the resolution of a full screen window or the @@ -3245,12 +3457,12 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @param[in] window The window whose monitor, size or video mode to set. * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. * @param[in] xpos The desired x-coordinate of the upper-left corner of the - * client area. + * content area. * @param[in] ypos The desired y-coordinate of the upper-left corner of the - * client area. - * @param[in] width The desired with, in screen coordinates, of the client area - * or video mode. - * @param[in] height The desired height, in screen coordinates, of the client + * content area. + * @param[in] width The desired with, in screen coordinates, of the content + * area or video mode. + * @param[in] height The desired height, in screen coordinates, of the content * area or video mode. * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, * or `GLFW_DONT_CARE`. @@ -3303,6 +3515,9 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int * errors. However, this function should not fail as long as it is passed * valid arguments and the library has been [initialized](@ref intro_init). * + * @remark @wayland The Wayland protocol provides no way to check whether a + * window is iconfied, so @ref GLFW_ICONIFIED always returns `GLFW_FALSE`. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs @@ -3400,15 +3615,22 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * This function sets the position callback of the specified window, which is * called when the window is moved. The callback is provided with the - * position, in screen coordinates, of the upper-left corner of the client area - * of the window. + * position, in screen coordinates, of the upper-left corner of the content + * area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowposfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland This callback will never be called, as there is no way for @@ -3422,20 +3644,27 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * @ingroup window */ -GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun cbfun); +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); /*! @brief Sets the size callback for the specified window. * * This function sets the size callback of the specified window, which is * called when the window is resized. The callback is provided with the size, - * in screen coordinates, of the client area of the window. + * in screen coordinates, of the content area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowsizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3447,7 +3676,7 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindow * * @ingroup window */ -GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun cbfun); +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback); /*! @brief Sets the close callback for the specified window. * @@ -3461,11 +3690,18 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * The close callback is not triggered by @ref glfwDestroyWindow. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowclosefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @macos Selecting Quit from the application menu will trigger the @@ -3480,12 +3716,12 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * * @ingroup window */ -GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun cbfun); +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback); /*! @brief Sets the refresh callback for the specified window. * * This function sets the refresh callback of the specified window, which is - * called when the client area of the window needs to be redrawn, for example + * called when the content area of the window needs to be redrawn, for example * if the window has been exposed after having been covered by another window. * * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where @@ -3493,11 +3729,18 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * very infrequently or never at all. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowrefreshfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3509,7 +3752,7 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * * @ingroup window */ -GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun cbfun); +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback); /*! @brief Sets the focus callback for the specified window. * @@ -3522,11 +3765,18 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GL * and @ref glfwSetMouseButtonCallback. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowfocusfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3537,7 +3787,7 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GL * * @ingroup window */ -GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun cbfun); +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback); /*! @brief Sets the iconify callback for the specified window. * @@ -3545,15 +3795,22 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * is called when the window is iconified or restored. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowiconifyfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @wayland The wl_shell protocol has no concept of iconification, - * this callback will never be called when using this deprecated protocol. + * @remark @wayland The XDG-shell protocol has no event for iconification, so + * this callback will never be called. * * @thread_safety This function must only be called from the main thread. * @@ -3563,7 +3820,7 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * * @ingroup window */ -GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun); +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback); /*! @brief Sets the maximize callback for the specified window. * @@ -3571,11 +3828,18 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL * is called when the window is maximized or restored. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowmaximizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3586,7 +3850,7 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL * * @ingroup window */ -GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun cbfun); +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); /*! @brief Sets the framebuffer resize callback for the specified window. * @@ -3594,11 +3858,18 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, * which is called when the framebuffer of the specified window is resized. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWframebuffersizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3609,7 +3880,7 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, * * @ingroup window */ -GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun); +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback); /*! @brief Sets the window content scale callback for the specified window. * @@ -3617,11 +3888,18 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * which is called when the content scale of the specified window changes. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowcontentscalefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3633,7 +3911,7 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * * @ingroup window */ -GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun cbfun); +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback); /*! @brief Processes all pending events. * @@ -3699,10 +3977,6 @@ GLFWAPI void glfwPollEvents(void); * GLFW will pass those events on to the application callbacks before * returning. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * Event processing is not required for joystick input to work. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref @@ -3750,14 +4024,13 @@ GLFWAPI void glfwWaitEvents(void); * GLFW will pass those events on to the application callbacks before * returning. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * Event processing is not required for joystick input to work. * * @param[in] timeout The maximum amount of time, in seconds, to wait. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. @@ -3777,10 +4050,6 @@ GLFWAPI void glfwWaitEventsTimeout(double timeout); * This function posts an empty event from the current thread to the event * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * @@ -3800,11 +4069,13 @@ GLFWAPI void glfwPostEmptyEvent(void); * * This function returns the value of an input option for the specified window. * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS. + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * @param[in] window The window to query. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. @@ -3823,13 +4094,14 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * * This function sets an input mode option for the specified window. The mode * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS. + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor * modes: * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. - * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the client - * area of the window but does not restrict the cursor from leaving. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the + * content area of the window but does not restrict the cursor from leaving. * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. @@ -3855,9 +4127,16 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. * + * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` + * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is + * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, + * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref + * glfwRawMouseMotionSupported to check for support. + * * @param[in] window The window whose input mode to set. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * @param[in] value The new value of the specified input mode. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -3873,6 +4152,35 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); +/*! @brief Returns whether raw mouse motion is supported. + * + * This function returns whether raw mouse motion is supported on the current + * system. This status does not change after GLFW has been initialized so you + * only need to check this once. If you attempt to enable raw motion on + * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. + * + * Raw mouse motion is closer to the actual motion of the mouse across + * a surface. It is not affected by the scaling and acceleration applied to + * the motion of the desktop cursor. That processing is suitable for a cursor + * while raw motion is better for controlling for example a 3D camera. Because + * of this, raw mouse motion is only provided when the cursor is disabled. + * + * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref raw_mouse_motion + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwRawMouseMotionSupported(void); + /*! @brief Returns the layout-specific name of the specified printable key. * * This function returns the name of the specified printable key, encoded as @@ -3925,9 +4233,11 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark The contents of the returned string may change when a keyboard + * layout change event is received. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the next call to @ref - * glfwGetKeyName, or until the library is terminated. + * should not free it yourself. It is valid until the library is terminated. * * @thread_safety This function must only be called from the main thread. * @@ -3968,8 +4278,7 @@ GLFWAPI int glfwGetKeyScancode(int key); * * This function returns the last state reported for the specified key to the * specified window. The returned state is one of `GLFW_PRESS` or - * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to - * the key callback. + * `GLFW_RELEASE`. The action `GLFW_REPEAT` is only reported to the key callback. * * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns * `GLFW_PRESS` the first time you call it for a key that was pressed, even if @@ -4011,8 +4320,8 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); * `GLFW_RELEASE`. * * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function - * `GLFW_PRESS` the first time you call it for a mouse button that was pressed, - * even if that mouse button has already been released. + * returns `GLFW_PRESS` the first time you call it for a mouse button that was + * pressed, even if that mouse button has already been released. * * @param[in] window The desired window. * @param[in] button The desired [mouse button](@ref buttons). @@ -4032,11 +4341,11 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); */ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); -/*! @brief Retrieves the position of the cursor relative to the client area of +/*! @brief Retrieves the position of the cursor relative to the content area of * the window. * * This function returns the position of the cursor, in screen coordinates, - * relative to the upper-left corner of the client area of the specified + * relative to the upper-left corner of the content area of the specified * window. * * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor @@ -4052,9 +4361,9 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); * * @param[in] window The desired window. * @param[out] xpos Where to store the cursor x-coordinate, relative to the - * left edge of the client area, or `NULL`. + * left edge of the content area, or `NULL`. * @param[out] ypos Where to store the cursor y-coordinate, relative to the to - * top edge of the client area, or `NULL`. + * top edge of the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -4070,11 +4379,11 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); */ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); -/*! @brief Sets the position of the cursor, relative to the client area of the +/*! @brief Sets the position of the cursor, relative to the content area of the * window. * * This function sets the position, in screen coordinates, of the cursor - * relative to the upper-left corner of the client area of the specified + * relative to the upper-left corner of the content area of the specified * window. The window must have input focus. If the window does not have * input focus when this function is called, it fails silently. * @@ -4089,9 +4398,9 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); * * @param[in] window The desired window. * @param[in] xpos The desired x-coordinate, relative to the left edge of the - * client area. + * content area. * @param[in] ypos The desired y-coordinate, relative to the top edge of the - * client area. + * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -4130,8 +4439,8 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @return The handle of the created cursor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -4201,7 +4510,7 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); /*! @brief Sets the cursor for the window. * * This function sets the cursor image to be used when the cursor is over the - * client area of the specified window. The set cursor will only be visible + * content area of the specified window. The set cursor will only be visible * when the [cursor mode](@ref cursor_mode) of the window is * `GLFW_CURSOR_NORMAL`. * @@ -4250,11 +4559,18 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * scancode may be zero. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new key callback, or `NULL` to remove the currently + * @param[in] callback The new key callback, or `NULL` to remove the currently * set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWkeyfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4266,7 +4582,7 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * * @ingroup input */ -GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback); /*! @brief Sets the Unicode character callback. * @@ -4283,16 +4599,21 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * The character callback behaves as system text input normally does and will * not be called if modifier keys are held down that would prevent normal text * input on that platform, for example a Super (Command) key on macOS or Alt key - * on Windows. There is a - * [character with modifiers callback](@ref glfwSetCharModsCallback) that - * receives these events. + * on Windows. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4304,7 +4625,7 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * * @ingroup input */ -GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback); /*! @brief Sets the Unicode character with modifiers callback. * @@ -4322,11 +4643,18 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * [key callback](@ref glfwSetKeyCallback) instead. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or an * [error](@ref error_handling) occurred. * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharmodsfun). + * * @deprecated Scheduled for removal in version 4.0. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. @@ -4339,7 +4667,7 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * * @ingroup input */ -GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun); +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback); /*! @brief Sets the mouse button callback. * @@ -4353,11 +4681,18 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmousebuttonfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4369,21 +4704,28 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * * @ingroup input */ -GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun cbfun); +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); /*! @brief Sets the cursor position callback. * * This function sets the cursor position callback of the specified window, * which is called when the cursor is moved. The callback is provided with the * position, in screen coordinates, relative to the upper-left corner of the - * client area of the window. + * content area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorposfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4394,20 +4736,27 @@ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmo * * @ingroup input */ -GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun cbfun); +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); -/*! @brief Sets the cursor enter/exit callback. +/*! @brief Sets the cursor enter/leave callback. * * This function sets the cursor boundary crossing callback of the specified - * window, which is called when the cursor enters or leaves the client area of + * window, which is called when the cursor enters or leaves the content area of * the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorenterfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4418,7 +4767,7 @@ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursor * * @ingroup input */ -GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun cbfun); +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback); /*! @brief Sets the scroll callback. * @@ -4430,11 +4779,18 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu * wheel or a touchpad scrolling area. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new scroll callback, or `NULL` to remove the currently - * set callback. + * @param[in] callback The new scroll callback, or `NULL` to remove the + * currently set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWscrollfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4445,12 +4801,12 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu * * @ingroup input */ -GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun); +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); -/*! @brief Sets the file drop callback. +/*! @brief Sets the path drop callback. * - * This function sets the file drop callback of the specified window, which is - * called when one or more dragged files are dropped on the window. + * This function sets the path drop callback of the specified window, which is + * called when one or more dragged paths are dropped on the window. * * Because the path array and its strings may have been generated specifically * for that event, they are not guaranteed to be valid after the callback has @@ -4458,11 +4814,18 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * make a deep copy. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new file drop callback, or `NULL` to remove the + * @param[in] callback The new file drop callback, or `NULL` to remove the * currently set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWdropfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland File drop is currently unimplemented. @@ -4475,7 +4838,7 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * * @ingroup input */ -GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); /*! @brief Returns whether the specified joystick is present. * @@ -4581,7 +4944,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); * Each element in the array is one of the following values: * * Name | Value - * --------------------- | -------------------------------- + * ---- | ----- * `GLFW_HAT_CENTERED` | 0 * `GLFW_HAT_UP` | 1 * `GLFW_HAT_RIGHT` | 2 @@ -4663,7 +5026,7 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); */ GLFWAPI const char* glfwGetJoystickName(int jid); -/*! @brief Returns the SDL comaptible GUID of the specified joystick. +/*! @brief Returns the SDL compatible GUID of the specified joystick. * * This function returns the SDL compatible GUID, as a UTF-8 encoded * hexadecimal string, of the specified joystick. The returned string is @@ -4794,11 +5157,18 @@ GLFWAPI int glfwJoystickIsGamepad(int jid); * called by joystick functions. The function will then return whatever it * returns if the joystick is not present. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(int jid, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWjoystickfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4809,7 +5179,7 @@ GLFWAPI int glfwJoystickIsGamepad(int jid); * * @ingroup input */ -GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); /*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. * @@ -4860,6 +5230,8 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string); * joystick is not present, does not have a mapping or an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref GLFW_INVALID_ENUM. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected, the gamepad mappings are updated or the library is terminated. @@ -4877,7 +5249,7 @@ GLFWAPI const char* glfwGetGamepadName(int jid); /*! @brief Retrieves the state of the specified joystick remapped as a gamepad. * - * This function retrives the state of the specified joystick remapped to + * This function retrieves the state of the specified joystick remapped to * an Xbox-like gamepad. * * If the specified joystick is not present or does not have a gamepad mapping @@ -4901,6 +5273,8 @@ GLFWAPI const char* glfwGetGamepadName(int jid); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * + * @thread_safety This function must only be called from the main thread. + * * @sa @ref gamepad * @sa @ref glfwUpdateGamepadMappings * @sa @ref glfwJoystickIsGamepad @@ -4922,8 +5296,6 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland Clipboard is currently unimplemented. - * * @pointer_lifetime The specified string is copied before this function * returns. * @@ -4949,10 +5321,8 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland Clipboard is currently unimplemented. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref @@ -4970,23 +5340,26 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); -/*! @brief Returns the value of the GLFW timer. +/*! @brief Returns the GLFW time. * - * This function returns the value of the GLFW timer. Unless the timer has - * been set using @ref glfwSetTime, the timer measures time elapsed since GLFW - * was initialized. + * This function returns the current GLFW time, in seconds. Unless the time + * has been set using @ref glfwSetTime it measures time elapsed since GLFW was + * initialized. + * + * This function and @ref glfwSetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. * * The resolution of the timer is system dependent, but is usually on the order * of a few micro- or nanoseconds. It uses the highest-resolution monotonic * time source on each supported platform. * - * @return The current value, in seconds, or zero if an + * @return The current time, in seconds, or zero if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Reading and - * writing of the internal timer offset is not atomic, so it needs to be + * writing of the internal base time is not atomic, so it needs to be * externally synchronized with calls to @ref glfwSetTime. * * @sa @ref time @@ -4997,23 +5370,26 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); */ GLFWAPI double glfwGetTime(void); -/*! @brief Sets the GLFW timer. +/*! @brief Sets the GLFW time. * - * This function sets the value of the GLFW timer. It then continues to count - * up from that value. The value must be a positive finite number less than - * or equal to 18446744073.0, which is approximately 584.5 years. + * This function sets the current GLFW time, in seconds. The value must be + * a positive finite number less than or equal to 18446744073.0, which is + * approximately 584.5 years. + * + * This function and @ref glfwGetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. * * @param[in] time The new value, in seconds. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_VALUE. * - * @remark The upper limit of the timer is calculated as + * @remark The upper limit of GLFW time is calculated as * floor((264 - 1) / 109) and is due to implementations * storing nanoseconds in 64 bits. The limit may be increased in the future. * * @thread_safety This function may be called from any thread. Reading and - * writing of the internal timer offset is not atomic, so it needs to be + * writing of the internal base time is not atomic, so it needs to be * externally synchronized with calls to @ref glfwGetTime. * * @sa @ref time @@ -5290,13 +5666,11 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); * This function returns whether the Vulkan loader and any minimally functional * ICD have been found. * - * The availability of a Vulkan loader and even an ICD does not by itself - * guarantee that surface creation or even instance creation is possible. - * For example, on Fermi systems Nvidia will install an ICD that provides no - * actual Vulkan support. Call @ref glfwGetRequiredInstanceExtensions to check - * whether the extensions necessary for Vulkan surface creation are available - * and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue - * family of a physical device supports image presentation. + * The availability of a Vulkan loader and even an ICD does not by itself guarantee that + * surface creation or even instance creation is possible. Call @ref + * glfwGetRequiredInstanceExtensions to check whether the extensions necessary for Vulkan + * surface creation are available and @ref glfwGetPhysicalDevicePresentationSupport to + * check whether a queue family of a physical device supports image presentation. * * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` * otherwise. @@ -5317,7 +5691,7 @@ GLFWAPI int glfwVulkanSupported(void); * * This function returns an array of names of Vulkan instance extensions required * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the - * list will always contains `VK_KHR_surface`, so if you don't require any + * list will always contain `VK_KHR_surface`, so if you don't require any * additional extensions you can pass this list directly to the * `VkInstanceCreateInfo` struct. * @@ -5342,9 +5716,6 @@ GLFWAPI int glfwVulkanSupported(void); * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * - * @remark @macos This function currently only supports the - * `VK_MVK_macos_surface` extension from MoltenVK. - * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * library is terminated. @@ -5426,7 +5797,7 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* p * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @remark @macos This function currently always returns `GLFW_TRUE`, as the - * `VK_MVK_macos_surface` extension does not provide + * `VK_MVK_macos_surface` and `VK_EXT_metal_surface` extensions do not provide * a `vkGetPhysicalDevice*PresentationSupport` type function. * * @thread_safety This function may be called from any thread. For @@ -5483,8 +5854,10 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. * - * @remark @macos This function currently only supports the - * `VK_MVK_macos_surface` extension from MoltenVK. + * @remark @macos GLFW prefers the `VK_EXT_metal_surface` extension, with the + * `VK_MVK_macos_surface` extension as a fallback. The name of the selected + * extension, if any, is included in the array returned by @ref + * glfwGetRequiredInstanceExtensions. * * @remark @macos This function creates and sets a `CAMetalLayer` instance for * the window content view, which is required for MoltenVK to function. @@ -5525,6 +5898,7 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window */ #ifndef GLAPIENTRY #define GLAPIENTRY APIENTRY + #define GLFW_GLAPIENTRY_DEFINED #endif /* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ diff --git a/examples/others/external/include/GLFW/glfw3native.h b/examples/others/external/include/GLFW/glfw3native.h index 4372cb766..7be0227d5 100644 --- a/examples/others/external/include/GLFW/glfw3native.h +++ b/examples/others/external/include/GLFW/glfw3native.h @@ -3,7 +3,7 @@ * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Löwy + * Copyright (c) 2006-2018 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -62,7 +62,6 @@ extern "C" { * * `GLFW_EXPOSE_NATIVE_COCOA` * * `GLFW_EXPOSE_NATIVE_X11` * * `GLFW_EXPOSE_NATIVE_WAYLAND` - * * `GLFW_EXPOSE_NATIVE_MIR` * * The available context API macros are: * * `GLFW_EXPOSE_NATIVE_WGL` @@ -75,6 +74,16 @@ extern "C" { * and which platform-specific headers to include. It is then up your (by * definition platform-specific) code to handle which of these should be * defined. + * + * If you do not want the platform-specific headers to be included, define + * `GLFW_NATIVE_INCLUDE_NONE` before including the @ref glfw3native.h header. + * + * @code + * #define GLFW_EXPOSE_NATIVE_WIN32 + * #define GLFW_EXPOSE_NATIVE_WGL + * #define GLFW_NATIVE_INCLUDE_NONE + * #include + * @endcode */ @@ -82,46 +91,65 @@ extern "C" { * System headers and types *************************************************************************/ -#if defined(GLFW_EXPOSE_NATIVE_WIN32) - // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for - // example to allow applications to correctly declare a GL_ARB_debug_output - // callback) but windows.h assumes no one will define APIENTRY before it does - #if defined(GLFW_APIENTRY_DEFINED) - #undef APIENTRY - #undef GLFW_APIENTRY_DEFINED - #endif - #include -#elif defined(GLFW_EXPOSE_NATIVE_COCOA) - #include - #if defined(__OBJC__) - #import - #else - typedef void* id; - #endif -#elif defined(GLFW_EXPOSE_NATIVE_X11) - #include - #include -#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) - #include -#elif defined(GLFW_EXPOSE_NATIVE_MIR) - #include -#endif +#if !defined(GLFW_NATIVE_INCLUDE_NONE) -#if defined(GLFW_EXPOSE_NATIVE_WGL) - /* WGL is declared by windows.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_NSGL) - /* NSGL is declared by Cocoa.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_GLX) - #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_EGL) - #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_OSMESA) - #include -#endif + #if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) + /* This is a workaround for the fact that glfw3.h needs to export APIENTRY (for + * example to allow applications to correctly declare a GL_KHR_debug callback) + * but windows.h assumes no one will define APIENTRY before it does + */ + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif + #include + #elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) + #if defined(__OBJC__) + #import + #else + #include + #include + #endif + #elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) + #include + #include + #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include + #endif + + #if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ + #endif + #if defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ + #endif + #if defined(GLFW_EXPOSE_NATIVE_GLX) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, glx.h will include gl.h, which will define it unconditionally + */ + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif + #include + #endif + #if defined(GLFW_EXPOSE_NATIVE_EGL) + #include + #endif + #if defined(GLFW_EXPOSE_NATIVE_OSMESA) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, osmesa.h will include gl.h, which will define it unconditionally + */ + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif + #include + #endif + +#endif /*GLFW_NATIVE_INCLUDE_NONE*/ /************************************************************************* @@ -135,6 +163,8 @@ extern "C" { * of the specified monitor, or `NULL` if an [error](@ref error_handling) * occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -150,6 +180,8 @@ GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -164,6 +196,16 @@ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); * @return The `HWND` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -180,6 +222,17 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); * @return The `HGLRC` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -196,6 +249,8 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); * @return The `CGDirectDisplayID` of the specified monitor, or * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -210,6 +265,8 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); * @return The `NSWindow` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -226,6 +283,9 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); * @return The `NSOpenGLContext` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -242,6 +302,8 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); * @return The `Display` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -256,6 +318,8 @@ GLFWAPI Display* glfwGetX11Display(void); * @return The `RRCrtc` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -270,6 +334,8 @@ GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); * @return The `RROutput` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -284,6 +350,8 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); * @return The `Window` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -350,6 +418,9 @@ GLFWAPI const char* glfwGetX11SelectionString(void); * @return The `GLXContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -364,6 +435,9 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); * @return The `GLXWindow` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -380,6 +454,8 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); * @return The `struct wl_display*` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -394,6 +470,8 @@ GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); * @return The `struct wl_output*` of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -408,6 +486,8 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); * @return The main `struct wl_surface*` of the specified window, or `NULL` if * an [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -418,56 +498,17 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); #endif -#if defined(GLFW_EXPOSE_NATIVE_MIR) -/*! @brief Returns the `MirConnection*` used by GLFW. - * - * @return The `MirConnection*` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirConnection* glfwGetMirDisplay(void); - -/*! @brief Returns the Mir output ID of the specified monitor. - * - * @return The Mir output ID of the specified monitor, or zero if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the `MirWindow*` of the specified window. - * - * @return The `MirWindow*` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* window); -#endif - #if defined(GLFW_EXPOSE_NATIVE_EGL) /*! @brief Returns the `EGLDisplay` used by GLFW. * * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark Because EGL is initialized on demand, this function will return + * `EGL_NO_DISPLAY` until the first context has been created via EGL. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -482,6 +523,9 @@ GLFWAPI EGLDisplay glfwGetEGLDisplay(void); * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -496,6 +540,9 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -519,6 +566,9 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -540,6 +590,9 @@ GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -554,6 +607,9 @@ GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height * @return The `OSMesaContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * diff --git a/examples/others/external/lib/libglfw3.a b/examples/others/external/lib/libglfw3.a index 90e7a4759d032cb44de7e0551b3e9ed81cb9ddff..60f9e59f89276e07aac687c19f59c41103ace104 100644 GIT binary patch literal 290550 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